passdb-pam.c revision f153c0c2c4f52ecb93046bb33f5d951b6aac59fe
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen Based on auth_pam.c from popa3d by Solar Designer <solar@openwall.com>.
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen You're allowed to do whatever you like with this software (including
16f816d3f3c32ae3351834253f52ddd0212bcbf3Timo Sirainen re-distribution in source and/or binary form, with or without
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen modification), provided that credit is given where it is due and any
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen modified versions are marked as such. There's absolutely no warranty.
f81a4d2002da0db33d11ca694d3a91b3ee2a0fdbTimo Sirainen#if !defined(_SECURITY_PAM_APPL_H) && !defined(LINUX_PAM) && !defined(_OPENPAM)
f81a4d2002da0db33d11ca694d3a91b3ee2a0fdbTimo Sirainen/* Sun's PAM doesn't use const. we use a bit dirty hack to check it.
f81a4d2002da0db33d11ca694d3a91b3ee2a0fdbTimo Sirainen Originally it was just __sun__ check, but HP/UX also uses Sun's PAM
f81a4d2002da0db33d11ca694d3a91b3ee2a0fdbTimo Sirainen so I thought this might work better. */
f81a4d2002da0db33d11ca694d3a91b3ee2a0fdbTimo Sirainen/* Linux-PAM prior to 0.74 */
bf91bed88d4e294b4577ba2a3b14d87cf35ae135Timo Sirainenstatic int pam_userpass_conv(int num_msg, linux_const struct pam_message **msg,
bf91bed88d4e294b4577ba2a3b14d87cf35ae135Timo Sirainen struct pam_response **resp, void *appdata_ptr)
7de1c472fd23ddac6b4dc5cbeee6fa6a8418b071Timo Sirainen /* @UNSAFE */
b99f3f908d51f4d1f7628bdf2cc6100cd8587656Timo Sirainen struct pam_userpass *userpass = (struct pam_userpass *) appdata_ptr;
7de1c472fd23ddac6b4dc5cbeee6fa6a8418b071Timo Sirainen if (num_msg != 1 || msg[0]->msg_style != PAM_BINARY_PROMPT)
1cad0dd34667548ba39f794ddeb9fc486cf4c666Timo Sirainen if (PAM_BP_RCONTROL(prompt) != PAM_BPC_SELECT ||
1cad0dd34667548ba39f794ddeb9fc486cf4c666Timo Sirainen strncmp(input, USERPASS_AGENT_ID "/", USERPASS_AGENT_ID_LENGTH + 1))
1cad0dd34667548ba39f794ddeb9fc486cf4c666Timo Sirainen if ((flags & USERPASS_USER_MASK) == USERPASS_USER_FIXED &&
1cad0dd34667548ba39f794ddeb9fc486cf4c666Timo Sirainen if (!(*resp = malloc(sizeof(struct pam_response))))
1cad0dd34667548ba39f794ddeb9fc486cf4c666Timo Sirainen PAM_BP_RENEW(&prompt, PAM_BPC_DONE, userlen + 1 + passlen);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen memcpy(output + userlen + 1, userpass->pass, passlen);
1cad0dd34667548ba39f794ddeb9fc486cf4c666Timo Sirainen if (!(*resp = malloc(num_msg * sizeof(struct pam_response))))
1cad0dd34667548ba39f794ddeb9fc486cf4c666Timo Sirainen for (i = 0; i < num_msg; i++) {
1cad0dd34667548ba39f794ddeb9fc486cf4c666Timo Sirainen i_fatal_status(FATAL_OUTOFMEM, "Out of memory");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_fatal_status(FATAL_OUTOFMEM, "Out of memory");
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen while (--i >= 0) {
23079bf0a6e7489c5f542b0b897a71bdfd884a51Timo Sirainenstatic int pam_auth(struct auth_request *request,
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen struct passdb_module *_module = request->passdb->passdb;
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen struct pam_passdb_module *module = (struct pam_passdb_module *)_module;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen if ((status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen *error = t_strdup_printf("pam_authenticate() failed: %s",
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen if ((status = pam_setcred(pamh, PAM_ESTABLISH_CRED)) !=
3342badd8c69adff34db589fb0a221ace5996212Timo Sirainen *error = t_strdup_printf("pam_setcred() failed: %s",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if ((status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
31050c3df6cbe403e8ced8ef11b5c4e12124d354Timo Sirainen *error = t_strdup_printf("pam_acct_mgmt() failed: %s",
68b3667c9ee95951d7c3e03b19b2d37abbaa5736Timo Sirainen if ((status = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen "pam_open_session() failed: %s",
31050c3df6cbe403e8ced8ef11b5c4e12124d354Timo Sirainen if ((status = pam_close_session(pamh, 0)) != PAM_SUCCESS) {
68b3667c9ee95951d7c3e03b19b2d37abbaa5736Timo Sirainen "pam_close_session() failed: %s",
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen status = pam_get_item(pamh, PAM_USER, (linux_const void **)&item);
b32de04eb77234b25e2e411884a2503a1bf3c255Phil Carmody *error = t_strdup_printf("pam_get_item() failed: %s",
b32de04eb77234b25e2e411884a2503a1bf3c255Phil Carmody auth_request_set_field(request, "user", item, NULL);
b32de04eb77234b25e2e411884a2503a1bf3c255Phil Carmodypam_verify_plain_child(struct auth_request *request, const char *service,
dfdd228ab20092705da0e8812b3cd0a039319375Timo Sirainen const char *str;
74896b89e1d82819d710f9322cf7c9e72d5841adPhil Carmody status = pam_start(service, request->user, &conv, &pamh);
74896b89e1d82819d710f9322cf7c9e72d5841adPhil Carmody str = t_strdup_printf("pam_start() failed: %s",
74896b89e1d82819d710f9322cf7c9e72d5841adPhil Carmody const char *host = net_ip2addr(&request->remote_ip);
4e6629cb5d5e7f67d5023eda540105d32df5f2baPhil Carmody /* Set some PAM items. They shouldn't fail, and we don't really
53e0e6889bf659b86e4c7d2e83e27d69fd9d6bcbPhil Carmody care if they do. */
aba9cc9bf97576c0ca653d4e218567e617061029Phil Carmody /* TTY is needed by eg. pam_access module */
89b7d6ce9266288c156e3513f5798680f1e33572Phil Carmody if ((status2 = pam_end(pamh, status)) == PAM_SUCCESS) {
4e6629cb5d5e7f67d5023eda540105d32df5f2baPhil Carmody /* FIXME: check for PASSDB_RESULT_UNKNOWN_USER
89b7d6ce9266288c156e3513f5798680f1e33572Phil Carmody result = status == PAM_SUCCESS ? PASSDB_RESULT_OK :
53e0e6889bf659b86e4c7d2e83e27d69fd9d6bcbPhil Carmody buf = buffer_create_dynamic(pool_datastack_create(), 512);
89b7d6ce9266288c156e3513f5798680f1e33572Phil Carmody /* Don't send larger writes than what would block. truncated error
89b7d6ce9266288c156e3513f5798680f1e33572Phil Carmody message isn't that bad.. */
89b7d6ce9266288c156e3513f5798680f1e33572Phil Carmody if ((ret = write(fd, buf->data, size)) != (int)size) {
bddd52cb7f3e5a894c080f60750aa76b5aeaf103Timo Sirainen struct auth_request *auth_request = request->request;
bddd52cb7f3e5a894c080f60750aa76b5aeaf103Timo Sirainen /* POSIX guarantees that writing PIPE_BUF bytes or less to pipes is
bddd52cb7f3e5a894c080f60750aa76b5aeaf103Timo Sirainen atomic. We rely on that. */
bddd52cb7f3e5a894c080f60750aa76b5aeaf103Timo Sirainen "read() from child process failed: %m");
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen } else if (ret == 0) {
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen /* it died */
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen "Child process died");
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen "Child process returned only %d bytes", (int)ret);
1e2887ae909d9817cc43a7e40ecb50508419f7edTimo Sirainen /* error message included */
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen if (result == PASSDB_RESULT_INTERNAL_FAILURE) {
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainen "close(child input) failed: %m");
76959d3d6fed45d5f5e1397fcdcf09a5adb87f24Timo Sirainen /* FIXME: if we ever do some other kind of forking, this needs fixing */
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainen while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainenpam_verify_plain(struct auth_request *request, const char *password,
1032e5427bf10566098f3b3bb9110e2bc1227e85Timo Sirainen struct passdb_module *_module = request->passdb->passdb;
3342badd8c69adff34db589fb0a221ace5996212Timo Sirainen struct pam_passdb_module *module = (struct pam_passdb_module *)_module;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen auth_request_log_error(request, "pam", "pipe() failed: %m");
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen callback(PASSDB_RESULT_INTERNAL_FAILURE, request);
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen auth_request_log_error(request, "pam", "fork() failed: %m");
3342badd8c69adff34db589fb0a221ace5996212Timo Sirainen callback(PASSDB_RESULT_INTERNAL_FAILURE, request);
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen pam_verify_plain_child(request, service, password, fd[1]);
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen "close(fd[1]) failed: %m");
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen pam_auth_request = i_new(struct pam_auth_request, 1);
945565e0c9cf979b5feeba6fbd4efce3bf4484adTimo Sirainen io_add(fd[0], IO_READ, pam_child_input, pam_auth_request);
dd8de60250511cc729b67249e61dfc6b4debff11Timo Sirainen module->to_wait = timeout_add(1000, wait_timeout, module);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenpam_preinit(struct auth_passdb *auth_passdb, const char *args)
20b9283d4af31e45e588014da427fb2dbcd3227aTimo Sirainen const char *const *t_args;
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen module = p_new(auth_passdb->auth->pool, struct pam_passdb_module, 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* -session for backwards compatibility */
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen else if (strcmp(t_args[i], "setcred=yes") == 0)
41bb0aa8e357876bc9a1916a37c9e3e78e5f8185Timo Sirainen else if (strncmp(t_args[i], "cache_key=", 10) == 0) {
21ec6628c567eeff025af35d8027be01044b0b1aTimo Sirainen i_fatal("Unexpected PAM parameter: %s", t_args[i]);
3342badd8c69adff34db589fb0a221ace5996212Timo Sirainenstatic void pam_deinit(struct passdb_module *_module)
b9ec0443d7d8afebfe61c17a9d692d6fad30c276Timo Sirainen struct pam_passdb_module *module = (struct pam_passdb_module *)_module;