passdb-pam.c revision 67b2c958f6de410bc86b68edc669b28b02c933f4
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/*
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen Based on auth_pam.c from popa3d by Solar Designer <solar@openwall.com>.
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen You're allowed to do whatever you like with this software (including
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen re-distribution in source and/or binary form, with or without
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen modification), provided that credit is given where it is due and any
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen modified versions are marked as such. There's absolutely no warranty.
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen*/
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#include "config.h"
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#undef HAVE_CONFIG_H
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#ifdef PASSDB_PAM
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#include "common.h"
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#include "buffer.h"
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#include "ioloop.h"
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#include "passdb.h"
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#include "mycrypt.h"
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#include "safe-memset.h"
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#include <stdlib.h>
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#include <fcntl.h>
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#include <unistd.h>
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#include <sys/wait.h>
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#ifdef HAVE_SECURITY_PAM_APPL_H
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen# include <security/pam_appl.h>
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#elif defined(HAVE_PAM_PAM_APPL_H)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen# include <pam/pam_appl.h>
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#endif
f6a21dbf70e816c33bfb1ce91728d06bd7d5758fTimo Sirainen
f6a21dbf70e816c33bfb1ce91728d06bd7d5758fTimo Sirainen#if !defined(_SECURITY_PAM_APPL_H) && !defined(LINUX_PAM) && !defined(_OPENPAM)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen/* Sun's PAM doesn't use const. we use a bit dirty hack to check it.
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen Originally it was just __sun__ check, but HP/UX also uses Sun's PAM
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen so I thought this might work better. */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen# define linux_const
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#else
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen# define linux_const const
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#endif
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainentypedef linux_const void *pam_item_t;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
d28179fd78550a58be44dcb1e3e830ab7d33172dTimo Sirainen#ifdef AUTH_PAM_USERPASS
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen# include <security/pam_client.h>
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen# ifndef PAM_BP_RCONTROL
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen/* Linux-PAM prior to 0.74 */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen# define PAM_BP_RCONTROL PAM_BP_CONTROL
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen# define PAM_BP_WDATA PAM_BP_DATA
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen# define PAM_BP_RDATA PAM_BP_DATA
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen# endif
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen# define USERPASS_AGENT_ID "userpass"
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen# define USERPASS_AGENT_ID_LENGTH 8
573424407a2d3c1453638a643583a7cf10c129e1Phil Carmody
573424407a2d3c1453638a643583a7cf10c129e1Phil Carmody# define USERPASS_USER_MASK 0x03
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen# define USERPASS_USER_REQUIRED 1
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen# define USERPASS_USER_KNOWN 2
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen# define USERPASS_USER_FIXED 3
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#endif
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstruct pam_auth_request {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen int fd;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen struct io *io;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen struct auth_request *request;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen verify_plain_callback_t *callback;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen};
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstruct pam_userpass {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen const char *user;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen const char *pass;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen};
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstatic char *service_name;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstatic struct timeout *to_wait;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstatic int pam_userpass_conv(int num_msg, linux_const struct pam_message **msg,
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen struct pam_response **resp, void *appdata_ptr)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen{
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen /* @UNSAFE */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen struct pam_userpass *userpass = (struct pam_userpass *) appdata_ptr;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#ifdef AUTH_PAM_USERPASS
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen pamc_bp_t prompt;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen const char *input;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen char *output;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen char flags;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen size_t userlen, passlen;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (num_msg != 1 || msg[0]->msg_style != PAM_BINARY_PROMPT)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return PAM_CONV_ERR;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen prompt = (pamc_bp_t)msg[0]->msg;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen input = PAM_BP_RDATA(prompt);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (PAM_BP_RCONTROL(prompt) != PAM_BPC_SELECT ||
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen strncmp(input, USERPASS_AGENT_ID "/", USERPASS_AGENT_ID_LENGTH + 1))
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return PAM_CONV_ERR;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen flags = input[USERPASS_AGENT_ID_LENGTH + 1];
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen input += USERPASS_AGENT_ID_LENGTH + 1 + 1;
d868a04630bd7bfe9c1543a7c3f68703b3e276e4Timo Sirainen
d868a04630bd7bfe9c1543a7c3f68703b3e276e4Timo Sirainen if ((flags & USERPASS_USER_MASK) == USERPASS_USER_FIXED &&
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen strcmp(input, userpass->user))
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return PAM_CONV_AGAIN;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
131b0d222ad89b2c5d2b03b865b45cae9e290d68Timo Sirainen if (!(*resp = malloc(sizeof(struct pam_response))))
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return PAM_CONV_ERR;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen userlen = strlen(userpass->user);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen passlen = strlen(userpass->pass);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen prompt = NULL;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen PAM_BP_RENEW(&prompt, PAM_BPC_DONE, userlen + 1 + passlen);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen output = PAM_BP_WDATA(prompt);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen memcpy(output, userpass->user, userlen + 1);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen memcpy(output + userlen + 1, userpass->pass, passlen);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen (*resp)[0].resp_retcode = 0;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen (*resp)[0].resp = (char *)prompt;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#else
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen char *string;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen int i;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (!(*resp = malloc(num_msg * sizeof(struct pam_response))))
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return PAM_CONV_ERR;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen for (i = 0; i < num_msg; i++) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen switch (msg[i]->msg_style) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen case PAM_PROMPT_ECHO_ON:
9184983183ae28fb543695c54c85bc5396c07e42Phil Carmody string = strdup(userpass->user);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (string == NULL)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_fatal("Out of memory");
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen break;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen case PAM_PROMPT_ECHO_OFF:
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen string = strdup(userpass->pass);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (string == NULL)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_fatal("Out of memory");
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen break;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen case PAM_ERROR_MSG:
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen case PAM_TEXT_INFO:
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen string = NULL;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen break;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen default:
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen while (--i >= 0) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if ((*resp)[i].resp == NULL)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen continue;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen safe_memset((*resp)[i].resp, 0,
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen strlen((*resp)[i].resp));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen free((*resp)[i].resp);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen (*resp)[i].resp = NULL;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen free(*resp);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen *resp = NULL;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return PAM_CONV_ERR;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen (*resp)[i].resp_retcode = PAM_SUCCESS;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen (*resp)[i].resp = string;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#endif
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return PAM_SUCCESS;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen}
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstatic int pam_auth(pam_handle_t *pamh, const char *user, const char **error)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen{
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen char *item;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen int status;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen *error = NULL;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if ((status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen *error = t_strdup_printf("pam_authenticate(%s) failed: %s",
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen user, pam_strerror(pamh, status));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return status;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#ifdef HAVE_PAM_SETCRED
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if ((status = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen *error = t_strdup_printf("pam_setcred(%s) failed: %s",
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen user, pam_strerror(pamh, status));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return status;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen#endif
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if ((status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen *error = t_strdup_printf("pam_acct_mgmt(%s) failed: %s",
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen user, pam_strerror(pamh, status));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return status;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen status = pam_get_item(pamh, PAM_USER, (linux_const void **)&item);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (status != PAM_SUCCESS) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen *error = t_strdup_printf("pam_get_item(%s) failed: %s",
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen user, pam_strerror(pamh, status));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return status;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return PAM_SUCCESS;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen}
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstatic void
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenpam_verify_plain_child(const char *service, const char *user,
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen const char *password, int fd)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen{
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen pam_handle_t *pamh;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen struct pam_userpass userpass;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen struct pam_conv conv;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen enum passdb_result result;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen int status, status2;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen const char *str;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen char buf_data[512];
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen buffer_t *buf;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen conv.conv = pam_userpass_conv;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen conv.appdata_ptr = &userpass;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen userpass.user = user;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen userpass.pass = password;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen status = pam_start(service, user, &conv, &pamh);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (status != PAM_SUCCESS) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen result = PASSDB_RESULT_INTERNAL_FAILURE;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen str = t_strdup_printf("pam_start(%s) failed: %s",
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen user, pam_strerror(pamh, status));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen } else {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen status = pam_auth(pamh, user, &str);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if ((status2 = pam_end(pamh, status)) == PAM_SUCCESS) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen /* FIXME: check for PASSDB_RESULT_UNKNOWN_USER
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen somehow? */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (!verbose)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen str = NULL;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen result = status == PAM_SUCCESS ? PASSDB_RESULT_OK :
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen PASSDB_RESULT_PASSWORD_MISMATCH;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen } else {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen result = PASSDB_RESULT_INTERNAL_FAILURE;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen str = t_strdup_printf("pam_end(%s) failed: %s", user,
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen pam_strerror(pamh, status2));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen buf = buffer_create_data(data_stack_pool, buf_data, sizeof(buf_data));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen buffer_append(buf, &result, sizeof(result));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (str != NULL) {
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainen /* may truncate the error. tough luck. */
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainen buffer_append(buf, str, strlen(str));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen write(fd, buf_data, buffer_get_used_size(buf));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen}
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstatic void pam_child_input(void *context)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen{
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen struct pam_auth_request *request = context;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen enum passdb_result result;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen char buf[513];
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen ssize_t ret;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen /* POSIX guarantees that writing 512 bytes or less to pipes is atomic.
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen We rely on that. */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen ret = read(request->fd, buf, sizeof(buf)-1);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (ret < 0) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_error("PAM: read() from child process failed: %m");
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen result = PASSDB_RESULT_INTERNAL_FAILURE;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen } else if (ret == 0) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen /* it died */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_error("PAM: Child process died");
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen result = PASSDB_RESULT_INTERNAL_FAILURE;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen } else if ((size_t)ret < sizeof(result)) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_error("PAM: Child process returned only %d bytes", ret);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen result = PASSDB_RESULT_INTERNAL_FAILURE;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen } else {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen result = *((enum passdb_result *) buf);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if ((size_t)ret > sizeof(result)) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen /* error message included */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen buf[ret] = '\0';
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (result == PASSDB_RESULT_INTERNAL_FAILURE)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_error("PAM: %s", buf + sizeof(result));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen else
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_info("PAM: %s", buf + sizeof(result));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen request->callback(result, request->request);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (close(request->fd) < 0)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_error("PAM: close(child input) failed: %m");
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen io_remove(request->io);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_free(request);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen}
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstatic void wait_timeout(void *context __attr_unused__)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen{
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen int status;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen pid_t pid;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen /* FIXME: if we ever do some other kind of forking, this needs fixing */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (pid == -1) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (errno == ECHILD) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen timeout_remove(to_wait);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen to_wait = NULL;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen } else if (errno != EINTR)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_error("waitpid() failed: %m");
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen return;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (WIFSIGNALED(status)) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_error("PAM: Child %s died with signal %d",
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen dec2str(pid), WTERMSIG(status));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen }
}
static void
pam_verify_plain(struct auth_request *request, const char *password,
verify_plain_callback_t *callback)
{
struct pam_auth_request *pam_auth_request;
const char *service;
int fd[2];
pid_t pid;
service = service_name != NULL ? service_name :
request->protocol == AUTH_PROTOCOL_IMAP ? "imap" :
request->protocol == AUTH_PROTOCOL_POP3 ? "pop3" : NULL;
if (service == NULL) {
i_error("Unknown protocol %d in auth request",
request->protocol);
}
if (pipe(fd) < 0) {
i_error("PAM: pipe() failed: %m");
callback(PASSDB_RESULT_INTERNAL_FAILURE, request);
return;
}
pid = fork();
if (pid == -1) {
i_error("PAM: fork() failed: %m");
callback(PASSDB_RESULT_INTERNAL_FAILURE, request);
(void)close(fd[0]);
(void)close(fd[1]);
return;
}
if (pid == 0) {
(void)close(fd[0]);
pam_verify_plain_child(service, request->user, password, fd[1]);
_exit(0);
}
if (close(fd[1]) < 0)
i_error("PAM: close(fd[1]) failed: %m");
pam_auth_request = i_new(struct pam_auth_request, 1);
pam_auth_request->fd = fd[0];
pam_auth_request->request = request;
pam_auth_request->callback = callback;
pam_auth_request->io =
io_add(fd[0], IO_READ, pam_child_input, pam_auth_request);
if (to_wait == NULL)
to_wait = timeout_add(1000, wait_timeout, NULL);
}
static void pam_init(const char *args)
{
service_name = strcmp(args, "*") == 0 ? NULL :
i_strdup(*args != '\0' ? args : "dovecot");
to_wait = NULL;
}
static void pam_deinit(void)
{
if (to_wait != NULL)
timeout_remove(to_wait);
i_free(service_name);
}
struct passdb_module passdb_pam = {
pam_init,
pam_deinit,
pam_verify_plain,
NULL
};
#endif