passdb-pam.c revision 22535a9e685e29214082878e37a267157044618e
970N/A/*
970N/A Based on auth_pam.c from popa3d by Solar Designer <solar@openwall.com>.
970N/A
970N/A You're allowed to do whatever you like with this software (including
1072N/A re-distribution in source and/or binary form, with or without
970N/A modification), provided that credit is given where it is due and any
970N/A modified versions are marked as such. There's absolutely no warranty.
970N/A*/
970N/A
970N/A#include "config.h"
970N/A#undef HAVE_CONFIG_H
970N/A
970N/A#ifdef PASSDB_PAM
970N/A
970N/A#include "common.h"
970N/A#include "passdb.h"
970N/A#include "mycrypt.h"
970N/A#include "safe-memset.h"
970N/A
970N/A#include <stdlib.h>
970N/A#ifdef HAVE_SECURITY_PAM_APPL_H
970N/A# include <security/pam_appl.h>
970N/A#elif defined(HAVE_PAM_PAM_APPL_H)
970N/A# include <pam/pam_appl.h>
970N/A#endif
970N/A
970N/A#if !defined(_SECURITY_PAM_APPL_H) && !defined(LINUX_PAM)
970N/A/* Sun's PAM doesn't use const. we use a bit dirty hack to check it.
970N/A Originally it was just __sun__ check, but HP/UX also uses Sun's PAM
970N/A so I thought this might work better. */
970N/A# define linux_const
970N/A#else
970N/A# define linux_const const
970N/A#endif
970N/Atypedef linux_const void *pam_item_t;
970N/A
970N/A#ifdef AUTH_PAM_USERPASS
970N/A# include <security/pam_client.h>
970N/A
970N/A# ifndef PAM_BP_RCONTROL
970N/A/* Linux-PAM prior to 0.74 */
970N/A# define PAM_BP_RCONTROL PAM_BP_CONTROL
970N/A# define PAM_BP_WDATA PAM_BP_DATA
1105N/A# define PAM_BP_RDATA PAM_BP_DATA
970N/A# endif
970N/A
970N/A# define USERPASS_AGENT_ID "userpass"
970N/A# define USERPASS_AGENT_ID_LENGTH 8
970N/A
970N/A# define USERPASS_USER_MASK 0x03
970N/A# define USERPASS_USER_REQUIRED 1
970N/A# define USERPASS_USER_KNOWN 2
970N/A# define USERPASS_USER_FIXED 3
970N/A#endif
970N/A
970N/Astruct pam_userpass {
970N/A const char *user;
970N/A const char *pass;
970N/A};
970N/A
970N/Astatic char *service_name;
970N/A
970N/Astatic int pam_userpass_conv(int num_msg, linux_const struct pam_message **msg,
970N/A struct pam_response **resp, void *appdata_ptr)
970N/A{
970N/A /* @UNSAFE */
970N/A struct pam_userpass *userpass = (struct pam_userpass *) appdata_ptr;
970N/A#ifdef AUTH_PAM_USERPASS
970N/A pamc_bp_t prompt;
1130N/A const char *input;
970N/A char *output;
970N/A char flags;
970N/A size_t userlen, passlen;
970N/A
970N/A if (num_msg != 1 || msg[0]->msg_style != PAM_BINARY_PROMPT)
970N/A return PAM_CONV_ERR;
970N/A
970N/A prompt = (pamc_bp_t)msg[0]->msg;
970N/A input = PAM_BP_RDATA(prompt);
970N/A
970N/A if (PAM_BP_RCONTROL(prompt) != PAM_BPC_SELECT ||
970N/A strncmp(input, USERPASS_AGENT_ID "/", USERPASS_AGENT_ID_LENGTH + 1))
970N/A return PAM_CONV_ERR;
970N/A
970N/A flags = input[USERPASS_AGENT_ID_LENGTH + 1];
970N/A input += USERPASS_AGENT_ID_LENGTH + 1 + 1;
970N/A
970N/A if ((flags & USERPASS_USER_MASK) == USERPASS_USER_FIXED &&
970N/A strcmp(input, userpass->user))
970N/A return PAM_CONV_AGAIN;
970N/A
970N/A if (!(*resp = malloc(sizeof(struct pam_response))))
970N/A return PAM_CONV_ERR;
970N/A
970N/A userlen = strlen(userpass->user);
970N/A passlen = strlen(userpass->pass);
970N/A
970N/A prompt = NULL;
1105N/A PAM_BP_RENEW(&prompt, PAM_BPC_DONE, userlen + 1 + passlen);
1105N/A output = PAM_BP_WDATA(prompt);
970N/A
1105N/A memcpy(output, userpass->user, userlen + 1);
970N/A memcpy(output + userlen + 1, userpass->pass, passlen);
970N/A
970N/A (*resp)[0].resp_retcode = 0;
970N/A (*resp)[0].resp = (char *)prompt;
970N/A#else
970N/A char *string;
977N/A int i;
1152N/A
970N/A if (!(*resp = malloc(num_msg * sizeof(struct pam_response))))
970N/A return PAM_CONV_ERR;
1105N/A
970N/A for (i = 0; i < num_msg; i++) {
970N/A switch (msg[i]->msg_style) {
1105N/A case PAM_PROMPT_ECHO_ON:
970N/A string = strdup(userpass->user);
970N/A if (string == NULL)
1120N/A i_fatal("Out of memory");
1120N/A break;
1120N/A case PAM_PROMPT_ECHO_OFF:
970N/A string = strdup(userpass->pass);
970N/A if (string == NULL)
970N/A i_fatal("Out of memory");
970N/A break;
1153N/A case PAM_ERROR_MSG:
1153N/A case PAM_TEXT_INFO:
1153N/A string = NULL;
1153N/A break;
970N/A default:
970N/A while (--i >= 0) {
970N/A if ((*resp)[i].resp == NULL)
970N/A continue;
1105N/A safe_memset((*resp)[i].resp, 0,
1105N/A strlen((*resp)[i].resp));
1105N/A free((*resp)[i].resp);
970N/A (*resp)[i].resp = NULL;
970N/A }
970N/A
1105N/A free(*resp);
970N/A *resp = NULL;
970N/A
970N/A return PAM_CONV_ERR;
970N/A }
970N/A
970N/A (*resp)[i].resp_retcode = PAM_SUCCESS;
970N/A (*resp)[i].resp = string;
970N/A }
970N/A#endif
970N/A
970N/A return PAM_SUCCESS;
970N/A}
970N/A
970N/Astatic int pam_auth(pam_handle_t *pamh, const char *user)
970N/A{
970N/A char *item;
970N/A int status;
970N/A
1057N/A if ((status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
970N/A if (verbose) {
970N/A i_info("PAM: pam_authenticate(%s) failed: %s",
970N/A user, pam_strerror(pamh, status));
970N/A }
970N/A return status;
970N/A }
970N/A
970N/A#ifdef HAVE_PAM_SETCRED
1132N/A if ((status = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
1132N/A if (verbose) {
1132N/A i_info("PAM: pam_setcred(%s) failed: %s",
970N/A user, pam_strerror(pamh, status));
970N/A }
970N/A return status;
970N/A }
970N/A#endif
1132N/A
1132N/A if ((status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
970N/A if (verbose) {
970N/A i_info("PAM: pam_acct_mgmt(%s) failed: %s",
1132N/A user, pam_strerror(pamh, status));
1132N/A }
1132N/A return status;
1132N/A }
1132N/A
970N/A status = pam_get_item(pamh, PAM_USER, (linux_const void **)&item);
1132N/A if (status != PAM_SUCCESS) {
1132N/A if (verbose) {
1132N/A i_info("PAM: pam_get_item(%s) failed: %s",
1132N/A user, pam_strerror(pamh, status));
1132N/A }
1147N/A return status;
1147N/A }
1147N/A
1147N/A return PAM_SUCCESS;
1132N/A}
1132N/A
1132N/Astatic void
1132N/Apam_verify_plain(const char *user, const char *realm, const char *password,
1132N/A verify_plain_callback_t *callback, void *context)
1132N/A{
1153N/A pam_handle_t *pamh;
1153N/A struct pam_userpass userpass;
1153N/A struct pam_conv conv;
1153N/A int status, status2;
970N/A
970N/A if (realm != NULL)
1003N/A user = t_strconcat(user, "@", realm, NULL);
1003N/A
1153N/A conv.conv = pam_userpass_conv;
970N/A conv.appdata_ptr = &userpass;
970N/A
1153N/A userpass.user = user;
1153N/A userpass.pass = password;
970N/A
970N/A status = pam_start(service_name, user, &conv, &pamh);
970N/A if (status != PAM_SUCCESS) {
970N/A if (verbose) {
970N/A i_info("PAM: pam_start(%s) failed: %s",
970N/A user, pam_strerror(pamh, status));
970N/A }
970N/A callback(PASSDB_RESULT_INTERNAL_FAILURE, context);
970N/A return;
970N/A }
970N/A
1105N/A status = pam_auth(pamh, user);
1105N/A if ((status2 = pam_end(pamh, status)) != PAM_SUCCESS) {
970N/A i_error("pam_end(%s) failed: %s",
1105N/A user, pam_strerror(pamh, status2));
1105N/A callback(PASSDB_RESULT_INTERNAL_FAILURE, context);
1153N/A return;
1153N/A }
970N/A
1153N/A /* FIXME: check for PASSDB_RESULT_UNKNOWN_USER somehow */
1153N/A callback(status == PAM_SUCCESS ? PASSDB_RESULT_OK :
1153N/A PASSDB_RESULT_PASSWORD_MISMATCH, context);
1153N/A}
970N/A
970N/Astatic void pam_init(const char *args)
970N/A{
1152N/A service_name = i_strdup(*args != '\0' ? args : "imap");
1152N/A}
1152N/A
970N/Astatic void pam_deinit(void)
970N/A{
970N/A i_free(service_name);
970N/A}
970N/A
970N/Astruct passdb_module passdb_pam = {
970N/A pam_init,
970N/A pam_deinit,
970N/A
970N/A pam_verify_plain,
970N/A NULL
970N/A};
970N/A
970N/A#endif
970N/A