passdb-pam.c revision 67b2c958f6de410bc86b68edc669b28b02c933f4
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen Based on auth_pam.c from popa3d by Solar Designer <solar@openwall.com>.
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.
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/* Linux-PAM prior to 0.74 */
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 /* @UNSAFE */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen struct pam_userpass *userpass = (struct pam_userpass *) appdata_ptr;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (num_msg != 1 || msg[0]->msg_style != PAM_BINARY_PROMPT)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (PAM_BP_RCONTROL(prompt) != PAM_BPC_SELECT ||
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen strncmp(input, USERPASS_AGENT_ID "/", USERPASS_AGENT_ID_LENGTH + 1))
d868a04630bd7bfe9c1543a7c3f68703b3e276e4Timo Sirainen if ((flags & USERPASS_USER_MASK) == USERPASS_USER_FIXED &&
131b0d222ad89b2c5d2b03b865b45cae9e290d68Timo Sirainen if (!(*resp = malloc(sizeof(struct pam_response))))
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen PAM_BP_RENEW(&prompt, PAM_BPC_DONE, userlen + 1 + passlen);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen memcpy(output + userlen + 1, userpass->pass, passlen);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if (!(*resp = malloc(num_msg * sizeof(struct pam_response))))
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen for (i = 0; i < num_msg; i++) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen while (--i >= 0) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstatic int pam_auth(pam_handle_t *pamh, const char *user, const char **error)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if ((status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen *error = t_strdup_printf("pam_authenticate(%s) failed: %s",
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 if ((status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen *error = t_strdup_printf("pam_acct_mgmt(%s) failed: %s",
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen status = pam_get_item(pamh, PAM_USER, (linux_const void **)&item);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen *error = t_strdup_printf("pam_get_item(%s) failed: %s",
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenpam_verify_plain_child(const char *service, const char *user,
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen const char *str;
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen status = pam_start(service, user, &conv, &pamh);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen str = t_strdup_printf("pam_start(%s) failed: %s",
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen if ((status2 = pam_end(pamh, status)) == PAM_SUCCESS) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen /* FIXME: check for PASSDB_RESULT_UNKNOWN_USER
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen result = status == PAM_SUCCESS ? PASSDB_RESULT_OK :
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen str = t_strdup_printf("pam_end(%s) failed: %s", user,
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen buf = buffer_create_data(data_stack_pool, buf_data, sizeof(buf_data));
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainen /* may truncate the error. tough luck. */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen write(fd, buf_data, buffer_get_used_size(buf));
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen /* POSIX guarantees that writing 512 bytes or less to pipes is atomic.
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen We rely on that. */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_error("PAM: read() from child process failed: %m");
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen } else if (ret == 0) {
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen /* it died */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_error("PAM: Child process returned only %d bytes", ret);
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen /* error message included */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen i_error("PAM: close(child input) failed: %m");
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainenstatic void wait_timeout(void *context __attr_unused__)
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen /* FIXME: if we ever do some other kind of forking, this needs fixing */
a49d1c2ca3c134c0c62b37a94936c78e9849e044Timo Sirainen while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
const char *service;
if (pid == 0) {
_exit(0);
static void pam_deinit(void)