passdb-pam.c revision 6c46568fae90840f82435be91d4364d23209cf70
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi Based on auth_pam.c from popa3d by Solar Designer <solar@openwall.com>.
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi You're allowed to do whatever you like with this software (including
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi re-distribution in source and/or binary form, with or without
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi modification), provided that credit is given where it is due and any
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi modified versions are marked as such. There's absolutely no warranty.
adf4b4083369a8bb7ed3c2ce3dd85dcaed7323c4KATOH Yasufumi#if !defined(_SECURITY_PAM_APPL_H) && !defined(LINUX_PAM) && !defined(_OPENPAM)
8c3a756ed4cf22f9fdab5f95d067321e6ddcc425KATOH Yasufumi/* Sun's PAM doesn't use const. we use a bit dirty hack to check it.
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi Originally it was just __sun__ check, but HP/UX also uses Sun's PAM
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi so I thought this might work better. */
8c3a756ed4cf22f9fdab5f95d067321e6ddcc425KATOH Yasufumi/* Linux-PAM prior to 0.74 */
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumistatic int pam_userpass_conv(int num_msg, linux_const struct pam_message **msg,
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi struct pam_response **resp, void *appdata_ptr)
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi /* @UNSAFE */
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi struct pam_userpass *userpass = (struct pam_userpass *) appdata_ptr;
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi if (num_msg != 1 || msg[0]->msg_style != PAM_BINARY_PROMPT)
92cbfdaf682dca9cf60243f23c2e911181bfc7abKATOH Yasufumi if (PAM_BP_RCONTROL(prompt) != PAM_BPC_SELECT ||
92cbfdaf682dca9cf60243f23c2e911181bfc7abKATOH Yasufumi strncmp(input, USERPASS_AGENT_ID "/", USERPASS_AGENT_ID_LENGTH + 1))
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi if ((flags & USERPASS_USER_MASK) == USERPASS_USER_FIXED &&
8adef7614d4340b4ee44a4441fadd530f48515edKATOH Yasufumi if (!(*resp = malloc(sizeof(struct pam_response))))
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi PAM_BP_RENEW(&prompt, PAM_BPC_DONE, userlen + 1 + passlen);
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi memcpy(output + userlen + 1, userpass->pass, passlen);
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi if (!(*resp = malloc(num_msg * sizeof(struct pam_response))))
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi for (i = 0; i < num_msg; i++) {
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi i_fatal_status(FATAL_OUTOFMEM, "Out of memory");
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi i_fatal_status(FATAL_OUTOFMEM, "Out of memory");
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi while (--i >= 0) {
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumistatic int pam_auth(struct auth_request *request,
0dc296145715c36795ad0ad561f115afdd758223KATOH Yasufumi struct passdb_module *_module = request->passdb->passdb;
a1e4c206d5373b8ecd7906bff37f2601d65f022cKATOH Yasufumi struct pam_passdb_module *module = (struct pam_passdb_module *)_module;
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi if ((status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi *error = t_strdup_printf("pam_authenticate() failed: %s",
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi if ((status = pam_setcred(pamh, PAM_ESTABLISH_CRED)) !=
41e8e807c8e85289bb717a365056b9437555e25aKATOH Yasufumi *error = t_strdup_printf("pam_setcred() failed: %s",
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi if ((status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi *error = t_strdup_printf("pam_acct_mgmt() failed: %s",
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi if ((status = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi "pam_open_session() failed: %s",
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi if ((status = pam_close_session(pamh, 0)) != PAM_SUCCESS) {
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi "pam_close_session() failed: %s",
7a49a081dd1d4f92e3c82df344709e3206457e46KATOH Yasufumi /* FIXME: this doesn't actually work since we're in the child
7a49a081dd1d4f92e3c82df344709e3206457e46KATOH Yasufumi status = pam_get_item(pamh, PAM_USER, (linux_const void **)&item);
7a49a081dd1d4f92e3c82df344709e3206457e46KATOH Yasufumi *error = t_strdup_printf("pam_get_item() failed: %s",
7a49a081dd1d4f92e3c82df344709e3206457e46KATOH Yasufumi auth_request_set_field(request, "user", item, NULL);
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumipam_verify_plain_child(struct auth_request *request, const char *service,
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi status = pam_start(service, request->user, &conv, &pamh);
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi str = t_strdup_printf("pam_start() failed: %s",
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi const char *host = net_ip2addr(&request->remote_ip);
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi /* Set some PAM items. They shouldn't fail, and we don't really
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi care if they do. */
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi /* TTY is needed by eg. pam_access module */
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi (void)pam_set_item(pamh, PAM_TTY, "dovecot");
420dfb599b22b8e39e6e51437d17213d4f778735Dwight Engen if ((status2 = pam_end(pamh, status)) == PAM_SUCCESS) {
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi str = t_strdup_printf("pam_end() failed: %s",
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi /* blocking=yes code path in auth worker */
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi buf = buffer_create_dynamic(pool_datastack_create(), 512);
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi /* Don't send larger writes than what would block. truncated error
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi message isn't that bad.. */
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi if ((ret = write(fd, buf->data, size)) != (int)size) {
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumistatic void pam_child_input(struct pam_auth_request *request)
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi struct auth_request *auth_request = request->request;
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi /* POSIX guarantees that writing PIPE_BUF bytes or less to pipes is
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi atomic. We rely on that. */
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi "read() from child process failed: %m");
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi } else if (ret == 0) {
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi /* it died */
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi "Child process died");
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi "Child process returned only %d bytes", (int)ret);
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi /* error message included */
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi if (result == PASSDB_RESULT_INTERNAL_FAILURE) {
cab79123082bef9ba265c9c8b9176b8b8afedd64KATOH Yasufumi "close(child input) failed: %m");
int status;
const char *service;
if (worker) {
if (pid == 0) {
_exit(0);
static struct passdb_module *
const char *const *t_args;
t_push();
t_args[i]);
t_pop();
NULL,