passdb-checkpassword.c revision 51821162b1df9a8a9398b8b64ceca410b9cc3092
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2004-2010 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "auth-common.h"
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainen#include "execv-const.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "passdb.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
bdd7a96c363346f7c38f389791be1487ca08775bTimo Sirainen#ifdef PASSDB_CHECKPASSWORD
bdd7a96c363346f7c38f389791be1487ca08775bTimo Sirainen
cc833a7a4e2258afdc834ace4bfe6579820a1df3Timo Sirainen#include "db-checkpassword.h"
cc833a7a4e2258afdc834ace4bfe6579820a1df3Timo Sirainen
97cb20eb77d486ef67eac50567e3080faca025c1Timo Sirainenstruct checkpassword_passdb_module {
97cb20eb77d486ef67eac50567e3080faca025c1Timo Sirainen struct passdb_module module;
cc833a7a4e2258afdc834ace4bfe6579820a1df3Timo Sirainen
7e235b3a5f622813121cd18f351e036650aaf8f8Timo Sirainen const char *checkpassword_path, *checkpassword_reply_path;
366eb7178f2c90d97134e0c2d1958f93fcdaba12Timo Sirainen struct hash_table *clients;
7e235b3a5f622813121cd18f351e036650aaf8f8Timo Sirainen};
7e235b3a5f622813121cd18f351e036650aaf8f8Timo Sirainen
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainenstatic struct child_wait *checkpassword_passdb_children = NULL;
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen
863ea896fb31a16d1baec31e57650243b5547db6Timo Sirainenstatic void checkpassword_request_finish(struct chkpw_auth_request *request,
863ea896fb31a16d1baec31e57650243b5547db6Timo Sirainen enum passdb_result result)
863ea896fb31a16d1baec31e57650243b5547db6Timo Sirainen{
471e447023ab73a73f0f78da2afc0c55905330ddTimo Sirainen struct passdb_module *_module = request->request->passdb->passdb;
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen struct checkpassword_passdb_module *module =
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen (struct checkpassword_passdb_module *)_module;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen verify_plain_callback_t *callback =
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen (verify_plain_callback_t *)request->callback;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen hash_table_remove(module->clients, POINTER_CAST(request->pid));
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen if (result == PASSDB_RESULT_OK) {
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen if (strchr(str_c(request->input_buf), '\n') != NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen auth_request_log_error(request->request,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "checkpassword",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "LF characters in checkpassword reply");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen result = PASSDB_RESULT_INTERNAL_FAILURE;
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen } else {
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen auth_request_set_fields(request->request,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen t_strsplit(str_c(request->input_buf), "\t"),
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen callback(result, request->request);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
55a210942dc7da58b2fd0b11bed8da6b030af5c1Timo Sirainen auth_request_unref(&request->request);
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen checkpassword_request_free(request);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
52d2b356e3ddb4e59ee09c10d47add9d3280284bAki Tuomi
52d2b356e3ddb4e59ee09c10d47add9d3280284bAki Tuomistatic void
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainencheckpassword_request_half_finish(struct chkpw_auth_request *request)
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!request->exited || request->fd_in != -1)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen switch (request->exit_status) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* vpopmail exit codes: */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen case 3: /* password fail / vpopmail user not found */
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen case 12: /* null user name given */
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen case 13: /* null password given */
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen case 15: /* user has no password */
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen case 20: /* invalid user/domain characters */
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen case 21: /* system user not found */
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen case 22: /* system user shadow entry not found */
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen case 23: /* system password fail */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen /* standard checkpassword exit codes: */
88b8aea03a24ef7a9efc30399080487b7eb03537Timo Sirainen case 1:
88b8aea03a24ef7a9efc30399080487b7eb03537Timo Sirainen /* (1 is additionally defined in vpopmail for
88b8aea03a24ef7a9efc30399080487b7eb03537Timo Sirainen "pop/smtp/webmal/ imap/access denied") */
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen auth_request_log_info(request->request, "checkpassword",
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen "Login failed (status=%d)",
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen request->exit_status);
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen checkpassword_request_finish(request,
651fc0f1e43fef3e02e0e7b5f498973b05f641d7Timo Sirainen PASSDB_RESULT_PASSWORD_MISMATCH);
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen break;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen case 0:
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen if (request->input_buf != NULL) {
d9fda7e3a0fa5551547ac3e3054b837fc77f4bfbTimo Sirainen checkpassword_request_finish(request, PASSDB_RESULT_OK);
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen break;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen }
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen /* missing input - fall through */
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen case 2:
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* checkpassword is called with wrong
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen parameters? unlikely */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen auth_request_log_error(request->request, "checkpassword",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Child %s exited with status 2 (tried to use "
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "userdb-only checkpassword program for passdb?)",
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen dec2str(request->pid));
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen checkpassword_request_finish(request,
45e62043058738e294f89504c319d852e25943ccTimo Sirainen PASSDB_RESULT_INTERNAL_FAILURE);
45e62043058738e294f89504c319d852e25943ccTimo Sirainen break;
45e62043058738e294f89504c319d852e25943ccTimo Sirainen case 111:
45e62043058738e294f89504c319d852e25943ccTimo Sirainen /* temporary problem, treat as internal error */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen default:
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* whatever error.. */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen auth_request_log_error(request->request, "checkpassword",
d5960ce1c0adda5c9e259bc429123ebc29c60baeTimo Sirainen "Child %s exited with status %d",
d5960ce1c0adda5c9e259bc429123ebc29c60baeTimo Sirainen dec2str(request->pid), request->exit_status);
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen checkpassword_request_finish(request,
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen PASSDB_RESULT_INTERNAL_FAILURE);
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen break;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void sigchld_handler(const struct child_wait_status *status,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct checkpassword_passdb_module *module)
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen{
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainen struct chkpw_auth_request *request =
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen hash_table_lookup(module->clients, POINTER_CAST(status->pid));
bdd7a96c363346f7c38f389791be1487ca08775bTimo Sirainen
bdd7a96c363346f7c38f389791be1487ca08775bTimo Sirainen switch (checkpassword_sigchld_handler(status, request)) {
bdd7a96c363346f7c38f389791be1487ca08775bTimo Sirainen case SIGCHLD_RESULT_UNKNOWN_CHILD:
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen case SIGCHLD_RESULT_DEAD_CHILD:
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen break;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen case SIGCHLD_RESULT_UNKNOWN_ERROR:
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen checkpassword_request_finish(request,
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen PASSDB_RESULT_INTERNAL_FAILURE);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen break;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen case SIGCHLD_RESULT_OK:
c0d1bfc45e224251cb549de8d8804861e8acb517Timo Sirainen checkpassword_request_half_finish(request);
c0d1bfc45e224251cb549de8d8804861e8acb517Timo Sirainen request = NULL;
c0d1bfc45e224251cb549de8d8804861e8acb517Timo Sirainen break;
c0d1bfc45e224251cb549de8d8804861e8acb517Timo Sirainen }
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen}
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenstatic void ATTR_NORETURN
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainencheckpassword_verify_plain_child(struct auth_request *request,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen struct checkpassword_passdb_module *module,
f537e7efaec891d6b3320ca94331d09ca8c4a4dbTimo Sirainen int fd_in, int fd_out)
6321d9d33937c7fc13a8ff04c220a9e377efeeb8Timo Sirainen{
6321d9d33937c7fc13a8ff04c220a9e377efeeb8Timo Sirainen const char *cmd, *const *args;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
2e99f3f3bb35715ce5e0a75a2f2a9bac3ab4224bTimo Sirainen if (dup2(fd_out, 3) < 0 || dup2(fd_in, 4) < 0) {
2e99f3f3bb35715ce5e0a75a2f2a9bac3ab4224bTimo Sirainen auth_request_log_error(request, "checkpassword",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "dup2() failed: %m");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else {
902222fb0928d1701f20a384b73f327b1d9a15ddTimo Sirainen checkpassword_setup_env(request);
902222fb0928d1701f20a384b73f327b1d9a15ddTimo Sirainen cmd = checkpassword_get_cmd(request, module->checkpassword_path,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen module->checkpassword_reply_path);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen auth_request_log_debug(request, "checkpassword",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "execute: %s", cmd);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen /* very simple argument splitting. */
6ded8819b9002150a95a7615e4f64f091c250464Timo Sirainen args = t_strsplit(cmd, " ");
6ded8819b9002150a95a7615e4f64f091c250464Timo Sirainen execv_const(args[0], args);
2f8da04d700cc23fcd6630226a4866e828b761bdTimo Sirainen }
2f8da04d700cc23fcd6630226a4866e828b761bdTimo Sirainen exit(2);
b87a4156eca6dcf6b29c504eb0cb9be2fdb11b63Timo Sirainen}
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainen
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainenstatic void
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainencheckpassword_verify_plain(struct auth_request *request, const char *password,
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen verify_plain_callback_t *callback)
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen{
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen struct passdb_module *_module = request->passdb->passdb;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen struct checkpassword_passdb_module *module =
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen (struct checkpassword_passdb_module *)_module;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen struct chkpw_auth_request *chkpw_auth_request;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen int fd_in[2], fd_out[2];
pid_t pid;
fd_in[0] = -1;
if (pipe(fd_in) < 0 || pipe(fd_out) < 0) {
auth_request_log_error(request, "checkpassword",
"pipe() failed: %m");
callback(PASSDB_RESULT_INTERNAL_FAILURE, request);
if (fd_in[0] != -1) {
(void)close(fd_in[0]);
(void)close(fd_in[1]);
}
return;
}
pid = fork();
if (pid == -1) {
auth_request_log_error(request, "checkpassword",
"fork() failed: %m");
callback(PASSDB_RESULT_INTERNAL_FAILURE, request);
(void)close(fd_in[0]);
(void)close(fd_in[1]);
(void)close(fd_out[0]);
(void)close(fd_out[1]);
return;
}
if (pid == 0) {
(void)close(fd_in[0]);
(void)close(fd_out[1]);
checkpassword_verify_plain_child(request, module,
fd_in[1], fd_out[0]);
/* not reached */
}
if (close(fd_in[1]) < 0) {
auth_request_log_error(request, "checkpassword",
"close(fd_in[1]) failed: %m");
}
if (close(fd_out[0]) < 0) {
auth_request_log_error(request, "checkpassword",
"close(fd_out[0]) failed: %m");
}
auth_request_ref(request);
chkpw_auth_request = i_new(struct chkpw_auth_request, 1);
chkpw_auth_request->fd_in = fd_in[0];
chkpw_auth_request->fd_out = fd_out[1];
chkpw_auth_request->pid = pid;
chkpw_auth_request->password = i_strdup(password);
chkpw_auth_request->request = request;
chkpw_auth_request->callback = callback;
chkpw_auth_request->half_finish_callback =
checkpassword_request_half_finish;
chkpw_auth_request->finish_callback =
checkpassword_request_finish;
chkpw_auth_request->internal_failure_code =
PASSDB_RESULT_INTERNAL_FAILURE;
chkpw_auth_request->io_in =
io_add(fd_in[0], IO_READ, checkpassword_child_input,
chkpw_auth_request);
chkpw_auth_request->io_out =
io_add(fd_out[1], IO_WRITE, checkpassword_child_output,
chkpw_auth_request);
hash_table_insert(module->clients, POINTER_CAST(pid),
chkpw_auth_request);
if (checkpassword_passdb_children != NULL)
child_wait_add_pid(checkpassword_passdb_children, pid);
else {
checkpassword_passdb_children =
child_wait_new_with_pid(pid, sigchld_handler, module);
}
}
static struct passdb_module *
checkpassword_preinit(pool_t pool, const char *args)
{
struct checkpassword_passdb_module *module;
module = p_new(pool, struct checkpassword_passdb_module, 1);
module->checkpassword_path = p_strdup(pool, args);
module->checkpassword_reply_path =
PKG_LIBEXECDIR"/checkpassword-reply";
module->clients =
hash_table_create(default_pool, default_pool, 0, NULL, NULL);
return &module->module;
}
static void checkpassword_deinit(struct passdb_module *_module)
{
struct checkpassword_passdb_module *module =
(struct checkpassword_passdb_module *)_module;
struct hash_iterate_context *iter;
void *key, *value;
iter = hash_table_iterate_init(module->clients);
while (hash_table_iterate(iter, &key, &value)) {
checkpassword_request_finish(value,
PASSDB_RESULT_INTERNAL_FAILURE);
}
hash_table_iterate_deinit(&iter);
hash_table_destroy(&module->clients);
if (checkpassword_passdb_children != NULL)
child_wait_free(&checkpassword_passdb_children);
}
struct passdb_module_interface passdb_checkpassword = {
"checkpassword",
checkpassword_preinit,
NULL,
checkpassword_deinit,
checkpassword_verify_plain,
NULL,
NULL
};
#else
struct passdb_module_interface passdb_checkpassword = {
.name = "checkpassword"
};
#endif