passdb-pam.c revision c483a995bd24a3d37146d180b9f85213d103b6c7
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose/*
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose Based on auth_pam.c from popa3d by Solar Designer <solar@openwall.com>.
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose You're allowed to do whatever you like with this software (including
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose re-distribution in source and/or binary form, with or without
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose modification), provided that credit is given where it is due and any
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose modified versions are marked as such. There's absolutely no warranty.
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose*/
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#include "common.h"
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#ifdef PASSDB_PAM
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#include "lib-signals.h"
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#include "buffer.h"
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#include "ioloop.h"
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#include "hash.h"
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#include "network.h"
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#include "passdb.h"
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#include "mycrypt.h"
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#include "safe-memset.h"
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#include <stdlib.h>
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#include <fcntl.h>
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#include <unistd.h>
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#include <sys/wait.h>
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#define PAM_CHILD_TIMEOUT (60*2)
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#define PAM_CHILD_CHECK_TIMEOUT 10
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#ifdef HAVE_SECURITY_PAM_APPL_H
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose# include <security/pam_appl.h>
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#elif defined(HAVE_PAM_PAM_APPL_H)
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose# include <pam/pam_appl.h>
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#endif
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#if !defined(_SECURITY_PAM_APPL_H) && !defined(LINUX_PAM) && !defined(_OPENPAM)
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose/* Sun's PAM doesn't use const. we use a bit dirty hack to check it.
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose Originally it was just __sun__ check, but HP/UX also uses Sun's PAM
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose so I thought this might work better. */
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose# define linux_const
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#else
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose# define linux_const const
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#endif
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bosetypedef linux_const void *pam_item_t;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#ifdef AUTH_PAM_USERPASS
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose# include <security/pam_client.h>
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose# ifndef PAM_BP_RCONTROL
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose/* Linux-PAM prior to 0.74 */
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose# define PAM_BP_RCONTROL PAM_BP_CONTROL
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose# define PAM_BP_WDATA PAM_BP_DATA
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose# define PAM_BP_RDATA PAM_BP_DATA
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose# endif
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose# define USERPASS_AGENT_ID "userpass"
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose# define USERPASS_AGENT_ID_LENGTH 8
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose# define USERPASS_USER_MASK 0x03
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose# define USERPASS_USER_REQUIRED 1
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose# define USERPASS_USER_KNOWN 2
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose# define USERPASS_USER_FIXED 3
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#endif
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bosestruct pam_passdb_module {
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose struct passdb_module module;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose bool pam_setcred, pam_session;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose const char *service_name, *pam_cache_key;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose};
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bosestruct pam_auth_request {
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose int refcount;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose int fd;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose struct io *io;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose time_t start_time;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose pid_t pid;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose struct auth_request *request;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose verify_plain_callback_t *callback;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose};
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bosestruct pam_userpass {
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose const char *user;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose const char *pass;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose};
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bosestatic struct hash_table *pam_requests;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bosestatic struct timeout *to;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bosestatic int pam_userpass_conv(int num_msg, linux_const struct pam_message **msg,
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose struct pam_response **resp, void *appdata_ptr)
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose{
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose /* @UNSAFE */
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose struct pam_userpass *userpass = (struct pam_userpass *) appdata_ptr;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#ifdef AUTH_PAM_USERPASS
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose pamc_bp_t prompt;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose const char *input;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose char *output;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose char flags;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose size_t userlen, passlen;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose if (num_msg != 1 || msg[0]->msg_style != PAM_BINARY_PROMPT)
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose return PAM_CONV_ERR;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose prompt = (pamc_bp_t)msg[0]->msg;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose input = PAM_BP_RDATA(prompt);
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose if (PAM_BP_RCONTROL(prompt) != PAM_BPC_SELECT ||
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose strncmp(input, USERPASS_AGENT_ID "/", USERPASS_AGENT_ID_LENGTH + 1))
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose return PAM_CONV_ERR;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose flags = input[USERPASS_AGENT_ID_LENGTH + 1];
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose input += USERPASS_AGENT_ID_LENGTH + 1 + 1;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose if ((flags & USERPASS_USER_MASK) == USERPASS_USER_FIXED &&
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose strcmp(input, userpass->user))
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose return PAM_CONV_AGAIN;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose if (!(*resp = malloc(sizeof(struct pam_response))))
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose return PAM_CONV_ERR;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose userlen = strlen(userpass->user);
cd5033e86bb4065d75188e2b6ef287a4421344c8Sumit Bose passlen = strlen(userpass->pass);
cd5033e86bb4065d75188e2b6ef287a4421344c8Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose prompt = NULL;
cd5033e86bb4065d75188e2b6ef287a4421344c8Sumit Bose PAM_BP_RENEW(&prompt, PAM_BPC_DONE, userlen + 1 + passlen);
cd5033e86bb4065d75188e2b6ef287a4421344c8Sumit Bose output = PAM_BP_WDATA(prompt);
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose memcpy(output, userpass->user, userlen + 1);
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose memcpy(output + userlen + 1, userpass->pass, passlen);
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose (*resp)[0].resp_retcode = 0;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose (*resp)[0].resp = (char *)prompt;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#else
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose char *string;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose int i;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose if (!(*resp = malloc(num_msg * sizeof(struct pam_response))))
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose return PAM_CONV_ERR;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose for (i = 0; i < num_msg; i++) {
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose switch (msg[i]->msg_style) {
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose case PAM_PROMPT_ECHO_ON:
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose string = strdup(userpass->user);
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose if (string == NULL)
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose i_fatal_status(FATAL_OUTOFMEM, "Out of memory");
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose break;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose case PAM_PROMPT_ECHO_OFF:
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose string = strdup(userpass->pass);
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose if (string == NULL)
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose i_fatal_status(FATAL_OUTOFMEM, "Out of memory");
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose break;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose case PAM_ERROR_MSG:
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose case PAM_TEXT_INFO:
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose string = NULL;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose break;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose default:
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose while (--i >= 0) {
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose if ((*resp)[i].resp == NULL)
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose continue;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose safe_memset((*resp)[i].resp, 0,
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose strlen((*resp)[i].resp));
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose free((*resp)[i].resp);
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose (*resp)[i].resp = NULL;
04d138472cc086fb7961f0d378852b09961b1a33Lukas Slebodnik }
04d138472cc086fb7961f0d378852b09961b1a33Lukas Slebodnik
04d138472cc086fb7961f0d378852b09961b1a33Lukas Slebodnik free(*resp);
04d138472cc086fb7961f0d378852b09961b1a33Lukas Slebodnik *resp = NULL;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose return PAM_CONV_ERR;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose }
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose (*resp)[i].resp_retcode = PAM_SUCCESS;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose (*resp)[i].resp = string;
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose }
2ef62c64e7f07c8aced3f72850008ecb72860162Sumit Bose#endif
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose return PAM_SUCCESS;
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose}
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bosestatic int pam_auth(struct auth_request *request,
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose pam_handle_t *pamh, const char **error)
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose{
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose struct passdb_module *_module = request->passdb->passdb;
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose struct pam_passdb_module *module = (struct pam_passdb_module *)_module;
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose void *item;
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose int status;
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose *error = NULL;
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose if ((status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose *error = t_strdup_printf("pam_authenticate() failed: %s",
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose pam_strerror(pamh, status));
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose return status;
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose }
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose#ifdef HAVE_PAM_SETCRED
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose if (module->pam_setcred) {
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose if ((status = pam_setcred(pamh, PAM_ESTABLISH_CRED)) !=
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose PAM_SUCCESS) {
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose *error = t_strdup_printf("pam_setcred() failed: %s",
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose pam_strerror(pamh, status));
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose return status;
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose }
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose }
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose#endif
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose if ((status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose *error = t_strdup_printf("pam_acct_mgmt() failed: %s",
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose pam_strerror(pamh, status));
fe2ab0d67fe8c66fb6352e9d8f845bb46d1848cbSumit Bose return status;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose }
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose if (module->pam_session) {
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose if ((status = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose *error = t_strdup_printf(
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose "pam_open_session() failed: %s",
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose pam_strerror(pamh, status));
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose return status;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose }
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose if ((status = pam_close_session(pamh, 0)) != PAM_SUCCESS) {
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose *error = t_strdup_printf(
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose "pam_close_session() failed: %s",
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose pam_strerror(pamh, status));
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose return status;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose }
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose }
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose /* FIXME: this doesn't actually work since we're in the child
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose process.. */
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose status = pam_get_item(pamh, PAM_USER, (linux_const void **)&item);
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose if (status != PAM_SUCCESS) {
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose *error = t_strdup_printf("pam_get_item() failed: %s",
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose pam_strerror(pamh, status));
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose return status;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose }
87f8bee53ee1b4ca87b602ff8536bc5fd5b5b595Lukas Slebodnik auth_request_set_field(request, "user", item, NULL);
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose return PAM_SUCCESS;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose}
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bosestatic enum passdb_result
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bosepam_verify_plain_child(struct auth_request *request, const char *service,
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose const char *password, int fd)
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose{
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose pam_handle_t *pamh;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose struct pam_userpass userpass;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose struct pam_conv conv;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose enum passdb_result result;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose int ret, status, status2;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose const char *str;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose size_t size;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose buffer_t *buf;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose conv.conv = pam_userpass_conv;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose conv.appdata_ptr = &userpass;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose userpass.user = request->user;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose userpass.pass = password;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose status = pam_start(service, request->user, &conv, &pamh);
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose if (status != PAM_SUCCESS) {
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose result = PASSDB_RESULT_INTERNAL_FAILURE;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose str = t_strdup_printf("pam_start() failed: %s",
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose pam_strerror(pamh, status));
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose } else {
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose const char *host = net_ip2addr(&request->remote_ip);
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose /* Set some PAM items. They shouldn't fail, and we don't really
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose care if they do. */
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose if (host != NULL)
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose (void)pam_set_item(pamh, PAM_RHOST, host);
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose /* TTY is needed by eg. pam_access module */
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose (void)pam_set_item(pamh, PAM_TTY, "dovecot");
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose status = pam_auth(request, pamh, &str);
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose if ((status2 = pam_end(pamh, status)) == PAM_SUCCESS) {
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose switch (status) {
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose case PAM_SUCCESS:
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose result = PASSDB_RESULT_OK;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose break;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose case PAM_USER_UNKNOWN:
04d138472cc086fb7961f0d378852b09961b1a33Lukas Slebodnik result = PASSDB_RESULT_USER_UNKNOWN;
04d138472cc086fb7961f0d378852b09961b1a33Lukas Slebodnik break;
04d138472cc086fb7961f0d378852b09961b1a33Lukas Slebodnik case PAM_NEW_AUTHTOK_REQD:
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose case PAM_ACCT_EXPIRED:
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose result = PASSDB_RESULT_PASS_EXPIRED;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose break;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose default:
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose result = PASSDB_RESULT_PASSWORD_MISMATCH;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose break;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose }
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose } else {
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose result = PASSDB_RESULT_INTERNAL_FAILURE;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose str = t_strdup_printf("pam_end() failed: %s",
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose pam_strerror(pamh, status2));
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose }
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose }
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose if (worker) {
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose /* blocking=yes code path in auth worker */
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose return result;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose }
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose buf = buffer_create_dynamic(pool_datastack_create(), 512);
04d138472cc086fb7961f0d378852b09961b1a33Lukas Slebodnik buffer_append(buf, &result, sizeof(result));
04d138472cc086fb7961f0d378852b09961b1a33Lukas Slebodnik
04d138472cc086fb7961f0d378852b09961b1a33Lukas Slebodnik if (str != NULL)
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose buffer_append(buf, str, strlen(str));
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose /* Don't send larger writes than what would block. truncated error
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose message isn't that bad.. */
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose size = I_MIN(buf->used, PIPE_BUF);
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose if ((ret = write(fd, buf->data, size)) != (int)size) {
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose if (ret < 0)
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose i_error("write() failed: %m");
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose else {
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose i_error("write() failed: %d != %"PRIuSIZE_T,
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose ret, buf->used);
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose }
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose }
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose return result;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose}
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bosestatic void pam_child_input(struct pam_auth_request *request)
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose{
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose struct auth_request *auth_request = request->request;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose enum passdb_result result;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose char buf[PIPE_BUF + 1];
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose ssize_t ret;
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose /* POSIX guarantees that writing PIPE_BUF bytes or less to pipes is
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose atomic. We rely on that. */
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose ret = read(request->fd, buf, sizeof(buf)-1);
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose if (ret < 0) {
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose auth_request_log_error(auth_request, "pam",
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose "read() from child process failed: %m");
2fe140d3a41e1ac66400069d35adc9379348c1e5Sumit Bose result = PASSDB_RESULT_INTERNAL_FAILURE;
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose } else if (ret == 0) {
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose /* it died */
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose auth_request_log_error(auth_request, "pam",
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose "Child process died");
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose result = PASSDB_RESULT_INTERNAL_FAILURE;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose } else if ((size_t)ret < sizeof(result)) {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose auth_request_log_error(auth_request, "pam",
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose "Child process returned only %d bytes", (int)ret);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose result = PASSDB_RESULT_INTERNAL_FAILURE;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose } else {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose memcpy(&result, buf, sizeof(result));
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose if ((size_t)ret > sizeof(result)) {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose /* error message included */
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose buf[ret] = '\0';
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose if (result == PASSDB_RESULT_INTERNAL_FAILURE) {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose auth_request_log_error(auth_request, "pam",
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose "%s", buf + sizeof(result));
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose } else {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose auth_request_log_info(auth_request, "pam",
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose "%s", buf + sizeof(result));
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose }
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose }
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose }
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose io_remove(&request->io);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose if (close(request->fd) < 0) {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose auth_request_log_error(auth_request, "pam",
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose "close(child input) failed: %m");
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose }
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose request->callback(result, auth_request);
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose auth_request_unref(&auth_request);
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose if (--request->refcount == 0)
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose i_free(request);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose}
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bosestatic void sigchld_handler(int signo __attr_unused__,
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose void *context __attr_unused__)
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose{
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose struct pam_auth_request *request;
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose int status;
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose pid_t pid;
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose /* FIXME: if we ever do some other kind of forking, this needs fixing */
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose if (pid == -1) {
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose if (errno != ECHILD && errno != EINTR)
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose i_error("waitpid() failed: %m");
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose return;
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose }
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose request = hash_lookup(pam_requests, POINTER_CAST(pid));
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose if (request == NULL) {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose i_error("PAM: Unknown child %s exited with status %d",
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose dec2str(pid), status);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose continue;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose }
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose if (WIFSIGNALED(status)) {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose i_error("PAM: Child %s died with signal %d",
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose dec2str(pid), WTERMSIG(status));
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose } else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose i_error("PAM: Child %s exited unexpectedly with "
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose "exit code %d", dec2str(pid),
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose WEXITSTATUS(status));
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose }
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose hash_remove(pam_requests, POINTER_CAST(request->pid));
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose if (--request->refcount == 0)
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose i_free(request);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose }
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose}
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bosestatic void
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bosepam_verify_plain(struct auth_request *request, const char *password,
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose verify_plain_callback_t *callback)
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose{
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose struct passdb_module *_module = request->passdb->passdb;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose struct pam_passdb_module *module = (struct pam_passdb_module *)_module;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose struct pam_auth_request *pam_auth_request;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose enum passdb_result result;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose const char *service;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose int fd[2];
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose pid_t pid;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose service = module->service_name != NULL ?
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose module->service_name : request->service;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose if (worker) {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose /* blocking=yes code path in auth worker */
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose result = pam_verify_plain_child(request, service, password, -1);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose callback(result, request);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose return;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose }
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose if (pipe(fd) < 0) {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose auth_request_log_error(request, "pam", "pipe() failed: %m");
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose callback(PASSDB_RESULT_INTERNAL_FAILURE, request);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose return;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose }
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose pid = fork();
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose if (pid == -1) {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose auth_request_log_error(request, "pam", "fork() failed: %m");
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose callback(PASSDB_RESULT_INTERNAL_FAILURE, request);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose (void)close(fd[0]);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose (void)close(fd[1]);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose return;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose }
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose if (pid == 0) {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose (void)close(fd[0]);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose pam_verify_plain_child(request, service, password, fd[1]);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose _exit(0);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose }
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose if (close(fd[1]) < 0) {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose auth_request_log_error(request, "pam",
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose "close(fd[1]) failed: %m");
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose }
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose auth_request_ref(request);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose pam_auth_request = i_new(struct pam_auth_request, 1);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose pam_auth_request->refcount = 2;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose pam_auth_request->fd = fd[0];
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose pam_auth_request->request = request;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose pam_auth_request->callback = callback;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose pam_auth_request->pid = pid;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose pam_auth_request->start_time = ioloop_time;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose pam_auth_request->io =
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose io_add(fd[0], IO_READ, pam_child_input, pam_auth_request);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose hash_insert(pam_requests, POINTER_CAST(pid), pam_auth_request);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose}
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bosestatic struct passdb_module *
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bosepam_preinit(struct auth_passdb *auth_passdb, const char *args)
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose{
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose struct pam_passdb_module *module;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose const char *const *t_args;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose int i;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose module = p_new(auth_passdb->auth->pool, struct pam_passdb_module, 1);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose module->service_name = "dovecot";
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose t_push();
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose t_args = t_strsplit(args, " ");
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose for(i = 0; t_args[i] != NULL; i++) {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose /* -session for backwards compatibility */
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose if (strcmp(t_args[i], "-session") == 0 ||
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose strcmp(t_args[i], "session=yes") == 0)
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose module->pam_session = TRUE;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose else if (strcmp(t_args[i], "setcred=yes") == 0)
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose module->pam_setcred = TRUE;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose else if (strncmp(t_args[i], "cache_key=", 10) == 0) {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose module->module.cache_key =
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose p_strdup(auth_passdb->auth->pool,
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose t_args[i] + 10);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose } else if (strcmp(t_args[i], "blocking=yes") == 0) {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose module->module.blocking = TRUE;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose } else if (strcmp(t_args[i], "*") == 0) {
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose module->service_name = NULL;
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose } else if (t_args[i+1] == NULL) {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose if (*t_args[i] != '\0') {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose module->service_name =
9da27cbc7532f775afc411d809735760dd5294a7Sumit Bose p_strdup(auth_passdb->auth->pool,
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose t_args[i]);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose }
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose } else {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose i_fatal("Unexpected PAM parameter: %s", t_args[i]);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose }
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose }
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose t_pop();
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose lib_signals_set_handler(SIGCHLD, TRUE, sigchld_handler, NULL);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose return &module->module;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose}
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bosestatic void pam_child_timeout(void *context __attr_unused__)
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose{
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose struct hash_iterate_context *iter;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose void *key, *value;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose time_t timeout = ioloop_time - PAM_CHILD_TIMEOUT;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose iter = hash_iterate_init(pam_requests);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose while (hash_iterate(iter, &key, &value)) {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose struct pam_auth_request *request = value;
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose if (request->start_time > timeout)
b52b26176c92f3b06dba5598428c70c0cde13fd1Sumit Bose continue;
b52b26176c92f3b06dba5598428c70c0cde13fd1Sumit Bose
b52b26176c92f3b06dba5598428c70c0cde13fd1Sumit Bose auth_request_log_error(request->request, "pam",
b52b26176c92f3b06dba5598428c70c0cde13fd1Sumit Bose "PAM child process %s timed out, killing it",
b52b26176c92f3b06dba5598428c70c0cde13fd1Sumit Bose dec2str(request->pid));
b52b26176c92f3b06dba5598428c70c0cde13fd1Sumit Bose if (kill(request->pid, SIGKILL) < 0) {
b52b26176c92f3b06dba5598428c70c0cde13fd1Sumit Bose i_error("PAM: kill(%s) failed: %m",
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose dec2str(request->pid));
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose }
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose }
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose hash_iterate_deinit(iter);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose}
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bosestatic void pam_init(struct passdb_module *_module __attr_unused__,
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose const char *args __attr_unused__)
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose{
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose if (pam_requests != NULL)
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose i_fatal("Can't support more than one PAM passdb");
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose /* we're caching the password by using directly the plaintext password
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose given by the auth mechanism */
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose _module->default_pass_scheme = "PLAIN";
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose if (!_module->blocking) {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose pam_requests = hash_create(default_pool, default_pool, 0,
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose NULL, NULL);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose to = timeout_add(PAM_CHILD_CHECK_TIMEOUT,
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose pam_child_timeout, NULL);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose lib_signals_set_handler(SIGCHLD, TRUE, sigchld_handler, NULL);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose }
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose}
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bosestatic void pam_deinit(struct passdb_module *_module __attr_unused__)
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose{
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose if (!_module->blocking) {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose lib_signals_unset_handler(SIGCHLD, sigchld_handler, NULL);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose hash_destroy(pam_requests);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose timeout_remove(&to);
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose }
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose}
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bosestruct passdb_module_interface passdb_pam = {
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose "pam",
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose pam_preinit,
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose pam_init,
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose pam_deinit,
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose pam_verify_plain,
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose NULL,
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose NULL
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose};
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose#endif
ca49ae1eee321751681e99f3ebe2547211db3bf6Sumit Bose