userinfo-pam.c revision 395cfd9f83ada53f9d434943c062cbe1c80bae2a
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen/*
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen Based on auth_pam.c from popa3d by Solar Designer <solar@openwall.com>.
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen You're allowed to do whatever you like with this software (including
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen re-distribution in source and/or binary form, with or without
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen modification), provided that credit is given where it is due and any
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen modified versions are marked as such. There's absolutely no warranty.
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen*/
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
411d6baa37f31d90730e90c4a28c43e1974bbe58Timo Sirainen#include "config.h"
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen#undef HAVE_CONFIG_H
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#ifdef USERINFO_PAM
daf029d2a627daa39d05507140f385162828172eTimo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "userinfo-passwd.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen#include <stdlib.h>
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen#ifdef HAVE_SECURITY_PAM_APPL_H
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen# include <security/pam_appl.h>
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen#elif defined(HAVE_PAM_PAM_APPL_H)
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen# include <pam/pam_appl.h>
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen#endif
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#if !defined(_SECURITY_PAM_APPL_H) && !defined(LINUX_PAM)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen/* Sun's PAM doesn't use const. we use a bit dirty hack to check it.
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen Originally it was just __sun__ check, but HP/UX also uses Sun's PAM
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen so I thought this might work better. */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen# define linux_const
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen#else
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen# define linux_const const
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen#endif
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainentypedef linux_const void *pam_item_t;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen#ifdef AUTH_PAM_USERPASS
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen# include <security/pam_client.h>
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen# ifndef PAM_BP_RCONTROL
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen/* Linux-PAM prior to 0.74 */
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen# define PAM_BP_RCONTROL PAM_BP_CONTROL
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen# define PAM_BP_WDATA PAM_BP_DATA
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen# define PAM_BP_RDATA PAM_BP_DATA
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen# endif
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen# define USERPASS_AGENT_ID "userpass"
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen# define USERPASS_AGENT_ID_LENGTH 8
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen# define USERPASS_USER_MASK 0x03
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen# define USERPASS_USER_REQUIRED 1
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen# define USERPASS_USER_KNOWN 2
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen# define USERPASS_USER_FIXED 3
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#endif
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstruct pam_userpass {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen const char *user;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen const char *pass;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen};
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainenstatic char *service_name;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainenstatic int pam_userpass_conv(int num_msg, linux_const struct pam_message **msg,
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen struct pam_response **resp, void *appdata_ptr)
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen{
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen /* @UNSAFE */
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen struct pam_userpass *userpass = (struct pam_userpass *) appdata_ptr;
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen#ifdef AUTH_PAM_USERPASS
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen pamc_bp_t prompt;
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen const char *input;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen char *output;
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen char flags;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen size_t userlen, passlen;
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen if (num_msg != 1 || msg[0]->msg_style != PAM_BINARY_PROMPT)
08fb191d6148feb3ed14e2d6c625cd248dd1c1d4Timo Sirainen return PAM_CONV_ERR;
08fb191d6148feb3ed14e2d6c625cd248dd1c1d4Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen prompt = (pamc_bp_t)msg[0]->msg;
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen input = PAM_BP_RDATA(prompt);
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen if (PAM_BP_RCONTROL(prompt) != PAM_BPC_SELECT ||
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen strncmp(input, USERPASS_AGENT_ID "/", USERPASS_AGENT_ID_LENGTH + 1))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return PAM_CONV_ERR;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen flags = input[USERPASS_AGENT_ID_LENGTH + 1];
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen input += USERPASS_AGENT_ID_LENGTH + 1 + 1;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if ((flags & USERPASS_USER_MASK) == USERPASS_USER_FIXED &&
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen strcmp(input, userpass->user))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return PAM_CONV_AGAIN;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
65cb456a072219fa35b55695d476b0bf51e2d735Timo Sirainen if (!(*resp = malloc(sizeof(struct pam_response))))
65cb456a072219fa35b55695d476b0bf51e2d735Timo Sirainen return PAM_CONV_ERR;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen userlen = strlen(userpass->user);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen passlen = strlen(userpass->pass);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
prompt = NULL;
PAM_BP_RENEW(&prompt, PAM_BPC_DONE, userlen + 1 + passlen);
output = PAM_BP_WDATA(prompt);
memcpy(output, userpass->user, userlen + 1);
memcpy(output + userlen + 1, userpass->pass, passlen);
(*resp)[0].resp_retcode = 0;
(*resp)[0].resp = (char *)prompt;
#else
char *string;
int i;
if (!(*resp = malloc(num_msg * sizeof(struct pam_response))))
return PAM_CONV_ERR;
for (i = 0; i < num_msg; i++) {
switch (msg[i]->msg_style) {
case PAM_PROMPT_ECHO_ON:
string = strdup(userpass->user);
if (string == NULL)
i_fatal("Out of memory");
break;
case PAM_PROMPT_ECHO_OFF:
string = strdup(userpass->pass);
if (string == NULL)
i_fatal("Out of memory");
break;
case PAM_ERROR_MSG:
case PAM_TEXT_INFO:
string = NULL;
break;
default:
while (--i >= 0) {
if ((*resp)[i].resp == NULL)
continue;
safe_memset((*resp)[i].resp, 0,
strlen((*resp)[i].resp));
free((*resp)[i].resp);
(*resp)[i].resp = NULL;
}
free(*resp);
*resp = NULL;
return PAM_CONV_ERR;
}
(*resp)[i].resp_retcode = PAM_SUCCESS;
(*resp)[i].resp = string;
}
#endif
return PAM_SUCCESS;
}
static int pam_auth(pam_handle_t *pamh, const char *user)
{
char *item;
int status;
if ((status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
if (verbose) {
i_info("PAM: pam_authenticate(%s) failed: %s",
user, pam_strerror(pamh, status));
}
return status;
}
#ifdef HAVE_PAM_SETCRED
if ((status = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
if (verbose) {
i_info("PAM: pam_setcred(%s) failed: %s",
user, pam_strerror(pamh, status));
}
return status;
}
#endif
if ((status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
if (verbose) {
i_info("PAM: pam_acct_mgmt(%s) failed: %s",
user, pam_strerror(pamh, status));
}
return status;
}
status = pam_get_item(pamh, PAM_USER, (linux_const void **)&item);
if (status != PAM_SUCCESS) {
if (verbose) {
i_info("PAM: pam_get_item(%s) failed: %s",
user, pam_strerror(pamh, status));
}
return status;
}
return PAM_SUCCESS;
}
static int pam_verify_plain(const char *user, const char *password,
struct auth_cookie_reply_data *reply)
{
pam_handle_t *pamh;
struct pam_userpass userpass;
struct pam_conv conv;
struct passwd *pw;
int status, status2;
conv.conv = pam_userpass_conv;
conv.appdata_ptr = &userpass;
userpass.user = user;
userpass.pass = password;
status = pam_start(service_name, user, &conv, &pamh);
if (status != PAM_SUCCESS) {
if (verbose) {
i_info("PAM: pam_start(%s) failed: %s",
user, pam_strerror(pamh, status));
}
return FALSE;
}
status = pam_auth(pamh, user);
if ((status2 = pam_end(pamh, status)) != PAM_SUCCESS) {
i_error("pam_end(%s) failed: %s",
user, pam_strerror(pamh, status2));
return FALSE;
}
if (status != PAM_SUCCESS)
return FALSE;
/* password ok, save the user info */
pw = getpwnam(user);
if (pw == NULL) {
i_error("PAM: getpwnam(%s) failed: %m", user);
return FALSE;
}
safe_memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
passwd_fill_cookie_reply(pw, reply);
return TRUE;
}
static void pam_init(const char *args)
{
service_name = i_strdup(*args != '\0' ? args : "imap");
}
static void pam_deinit(void)
{
i_free(service_name);
}
struct user_info_module userinfo_pam = {
pam_init,
pam_deinit,
pam_verify_plain,
NULL
};
#endif