? run.sh Index: auth-request.c =================================================================== RCS file: /var/lib/cvs/dovecot/src/auth/auth-request.c,v retrieving revision 1.93 diff -u -r1.93 auth-request.c --- auth-request.c 25 Mar 2007 18:31:37 -0000 1.93 +++ auth-request.c 29 Mar 2007 06:19:13 -0000 @@ -180,6 +180,14 @@ request->mech->auth_continue(request, data, data_size); } +void auth_request_reset_passdb_lookup(struct auth_request *request) +{ + request->passdb_password = NULL; + + if (request->extra_fields != NULL) + auth_stream_reply_reset(request->extra_fields); +} + static void auth_request_save_cache(struct auth_request *request, enum passdb_result result) { @@ -200,6 +208,7 @@ return success. */ return; case PASSDB_RESULT_INTERNAL_FAILURE: + case PASSDB_RESULT_END_OF_LIST: i_unreached(); } @@ -336,7 +345,7 @@ *result != PASSDB_RESULT_USER_DISABLED) { /* try next passdb. */ request->passdb = request->passdb->next; - request->passdb_password = NULL; + auth_request_reset_passdb_lookup(request); if (*result == PASSDB_RESULT_INTERNAL_FAILURE) { /* remember that we have had an internal failure. at @@ -344,9 +353,6 @@ successfully login. */ request->passdb_internal_failure = TRUE; } - if (request->extra_fields != NULL) - auth_stream_reply_reset(request->extra_fields); - return FALSE; } else if (request->passdb_internal_failure) { /* last passdb lookup returned internal failure. it may have @@ -445,27 +451,33 @@ } } -static void +static bool auth_request_lookup_credentials_callback_finish(enum passdb_result result, const char *password, struct auth_request *request) { if (!auth_request_handle_passdb_callback(&result, request)) { + if (result != PASSDB_RESULT_END_OF_LIST) { + /* see if we can get more credentials */ + return FALSE; + } + /* try next passdb */ auth_request_lookup_credentials(request, request->credentials, request->private_callback.lookup_credentials); + return TRUE; } else { if (request->auth->verbose_debug_passwords && result == PASSDB_RESULT_OK) { auth_request_log_debug(request, "password", "Credentials: %s", password); } - request->private_callback. + return request->private_callback. lookup_credentials(result, password, request); } } -void auth_request_lookup_credentials_callback(enum passdb_result result, +bool auth_request_lookup_credentials_callback(enum passdb_result result, const char *password, struct auth_request *request) { @@ -475,7 +487,9 @@ request->state = AUTH_REQUEST_STATE_MECH_CONTINUE; - if (result != PASSDB_RESULT_INTERNAL_FAILURE) + if (result == PASSDB_RESULT_END_OF_LIST) { + /* no more results */ + } else if (result != PASSDB_RESULT_INTERNAL_FAILURE) auth_request_save_cache(request, result); else { /* lookup failed. if we're looking here only because the @@ -494,8 +508,12 @@ } } - auth_request_lookup_credentials_callback_finish(result, password, - request); + if (!auth_request_lookup_credentials_callback_finish(result, password, + request)) { + request->state = AUTH_REQUEST_STATE_PASSDB; + return FALSE; + } + return TRUE; } void auth_request_lookup_credentials(struct auth_request *request, @@ -534,8 +552,9 @@ auth_request_lookup_credentials_callback); } else { /* this passdb doesn't support credentials */ - auth_request_lookup_credentials_callback( + bool ret = auth_request_lookup_credentials_callback( PASSDB_RESULT_SCHEME_NOT_AVAILABLE, NULL, request); + i_assert(ret); } } Index: auth-request.h =================================================================== RCS file: /var/lib/cvs/dovecot/src/auth/auth-request.h,v retrieving revision 1.34 diff -u -r1.34 auth-request.h --- auth-request.h 10 Mar 2007 14:04:34 -0000 1.34 +++ auth-request.h 29 Mar 2007 06:19:13 -0000 @@ -110,6 +110,7 @@ const unsigned char *data, size_t data_size); void auth_request_continue(struct auth_request *request, const unsigned char *data, size_t data_size); +void auth_request_reset_passdb_lookup(struct auth_request *request); void auth_request_verify_plain(struct auth_request *request, const char *password, @@ -156,9 +157,10 @@ void auth_request_verify_plain_callback(enum passdb_result result, struct auth_request *request); -void auth_request_lookup_credentials_callback(enum passdb_result result, +bool auth_request_lookup_credentials_callback(enum passdb_result result, const char *credentials, - struct auth_request *request); + struct auth_request *request) + __attr_warn_unused_result__; void auth_request_set_credentials(struct auth_request *request, enum passdb_credentials credentials, const char *data, Index: auth-worker-client.c =================================================================== RCS file: /var/lib/cvs/dovecot/src/auth/auth-worker-client.c,v retrieving revision 1.31 diff -u -r1.31 auth-worker-client.c --- auth-worker-client.c 16 Feb 2007 12:08:32 -0000 1.31 +++ auth-worker-client.c 29 Mar 2007 06:19:13 -0000 @@ -164,7 +164,7 @@ verify_plain(auth_request, password, verify_plain_callback); } -static void +static bool lookup_credentials_callback(enum passdb_result result, const char *credentials, struct auth_request *request) { @@ -195,6 +195,7 @@ auth_request_unref(&request); auth_worker_client_check_throttle(client); auth_worker_client_unref(&client); + return TRUE; } static void Index: db-sql.c =================================================================== RCS file: /var/lib/cvs/dovecot/src/auth/db-sql.c,v retrieving revision 1.11 diff -u -r1.11 db-sql.c --- db-sql.c 15 Dec 2006 18:38:08 -0000 1.11 +++ db-sql.c 29 Mar 2007 06:19:13 -0000 @@ -20,6 +20,7 @@ DEF(SET_STR, user_query), DEF(SET_STR, update_query), DEF(SET_STR, default_pass_scheme), + DEF(SET_BOOL, allow_multiple_rows), { 0, NULL, 0 } }; @@ -30,7 +31,8 @@ MEMBER(password_query) "SELECT password FROM users WHERE userid = '%u'", MEMBER(user_query) "SELECT home, uid, gid FROM users WHERE userid = '%u'", MEMBER(update_query) "UPDATE users SET password = '%w' WHERE userid = '%u'", - MEMBER(default_pass_scheme) "PLAIN-MD5" + MEMBER(default_pass_scheme) "PLAIN-MD5", + MEMBER(allow_multiple_rows) FALSE }; static struct sql_connection *connections = NULL; Index: db-sql.h =================================================================== RCS file: /var/lib/cvs/dovecot/src/auth/db-sql.h,v retrieving revision 1.4 diff -u -r1.4 db-sql.h --- db-sql.h 8 Nov 2006 20:22:08 -0000 1.4 +++ db-sql.h 29 Mar 2007 06:19:13 -0000 @@ -10,6 +10,8 @@ const char *user_query; const char *update_query; const char *default_pass_scheme; + + bool allow_multiple_rows; }; struct sql_connection { Index: mech-apop.c =================================================================== RCS file: /var/lib/cvs/dovecot/src/auth/mech-apop.c,v retrieving revision 1.24 diff -u -r1.24 mech-apop.c --- mech-apop.c 26 Jan 2007 13:40:51 -0000 1.24 +++ mech-apop.c 29 Mar 2007 06:19:13 -0000 @@ -43,7 +43,7 @@ return memcmp(digest, request->digest, 16) == 0; } -static void +static bool apop_credentials_callback(enum passdb_result result, const char *credentials, struct auth_request *auth_request) @@ -53,10 +53,9 @@ switch (result) { case PASSDB_RESULT_OK: - if (verify_credentials(request, credentials)) - auth_request_success(auth_request, NULL, 0); - else - auth_request_fail(auth_request); + if (!verify_credentials(request, credentials)) + return FALSE; + auth_request_success(auth_request, NULL, 0); break; case PASSDB_RESULT_INTERNAL_FAILURE: auth_request_internal_failure(auth_request); @@ -65,6 +64,7 @@ auth_request_fail(auth_request); break; } + return TRUE; } static void Index: mech-cram-md5.c =================================================================== RCS file: /var/lib/cvs/dovecot/src/auth/mech-cram-md5.c,v retrieving revision 1.25 diff -u -r1.25 mech-cram-md5.c --- mech-cram-md5.c 8 Nov 2006 20:22:08 -0000 1.25 +++ mech-cram-md5.c 29 Mar 2007 06:19:13 -0000 @@ -107,7 +107,7 @@ return TRUE; } -static void credentials_callback(enum passdb_result result, +static bool credentials_callback(enum passdb_result result, const char *credentials, struct auth_request *auth_request) { @@ -116,10 +116,11 @@ switch (result) { case PASSDB_RESULT_OK: - if (verify_credentials(request, credentials)) - auth_request_success(auth_request, NULL, 0); - else - auth_request_fail(auth_request); + if (!verify_credentials(request, credentials)) { + /* see if we have more credentials */ + return FALSE; + } + auth_request_success(auth_request, NULL, 0); break; case PASSDB_RESULT_INTERNAL_FAILURE: auth_request_internal_failure(auth_request); @@ -128,6 +129,7 @@ auth_request_fail(auth_request); break; } + return TRUE; } static void Index: mech-digest-md5.c =================================================================== RCS file: /var/lib/cvs/dovecot/src/auth/mech-digest-md5.c,v retrieving revision 1.43 diff -u -r1.43 mech-digest-md5.c --- mech-digest-md5.c 8 Nov 2006 20:22:08 -0000 1.43 +++ mech-digest-md5.c 29 Mar 2007 06:19:13 -0000 @@ -518,7 +518,7 @@ return !failed; } -static void credentials_callback(enum passdb_result result, +static bool credentials_callback(enum passdb_result result, const char *credentials, struct auth_request *auth_request) { @@ -527,10 +527,8 @@ switch (result) { case PASSDB_RESULT_OK: - if (!verify_credentials(request, credentials)) { - auth_request_fail(auth_request); - return; - } + if (!verify_credentials(request, credentials)) + return FALSE; request->authenticated = TRUE; auth_request->callback(auth_request, @@ -545,6 +543,7 @@ auth_request_fail(auth_request); break; } + return TRUE; } static void Index: mech-ntlm.c =================================================================== RCS file: /var/lib/cvs/dovecot/src/auth/mech-ntlm.c,v retrieving revision 1.27 diff -u -r1.27 mech-ntlm.c --- mech-ntlm.c 8 Nov 2006 20:22:08 -0000 1.27 +++ mech-ntlm.c 29 Mar 2007 06:19:13 -0000 @@ -61,7 +61,7 @@ return memcmp(lm_response, client_response, LM_RESPONSE_SIZE) == 0; } -static void +static bool lm_credentials_callback(enum passdb_result result, const char *credentials, struct auth_request *auth_request) @@ -71,10 +71,10 @@ switch (result) { case PASSDB_RESULT_OK: - if (lm_verify_credentials(request, credentials)) - auth_request_success(auth_request, NULL, 0); - else - auth_request_fail(auth_request); + if (!lm_verify_credentials(request, credentials)) + return FALSE; + + auth_request_success(auth_request, NULL, 0); break; case PASSDB_RESULT_INTERNAL_FAILURE: auth_request_internal_failure(auth_request); @@ -83,6 +83,7 @@ auth_request_fail(auth_request); break; } + return TRUE; } static int ntlm_verify_credentials(struct ntlm_auth_request *request, @@ -145,7 +146,7 @@ } } -static void +static bool ntlm_credentials_callback(enum passdb_result result, const char *credentials, struct auth_request *auth_request) @@ -159,16 +160,14 @@ ret = ntlm_verify_credentials(request, credentials); if (ret > 0) { auth_request_success(auth_request, NULL, 0); - return; - } - if (ret < 0) { - auth_request_fail(auth_request); - return; + return TRUE; } + if (ret < 0) + return FALSE; break; case PASSDB_RESULT_INTERNAL_FAILURE: auth_request_internal_failure(auth_request); - return; + return TRUE; default: break; } @@ -177,6 +176,7 @@ try with LM credentials */ auth_request_lookup_credentials(auth_request, PASSDB_CREDENTIALS_LANMAN, lm_credentials_callback); + return TRUE; } static void Index: mech-otp.c =================================================================== RCS file: /var/lib/cvs/dovecot/src/auth/mech-otp.c,v retrieving revision 1.1 diff -u -r1.1 mech-otp.c --- mech-otp.c 12 Nov 2006 19:36:41 -0000 1.1 +++ mech-otp.c 29 Mar 2007 06:19:13 -0000 @@ -54,7 +54,7 @@ answer, strlen(answer)); } -static void +static bool skey_credentials_callback(enum passdb_result result, const char *credentials, struct auth_request *auth_request) @@ -70,9 +70,10 @@ auth_request_fail(auth_request); break; } + return TRUE; } -static void +static bool otp_credentials_callback(enum passdb_result result, const char *credentials, struct auth_request *auth_request) @@ -91,6 +92,7 @@ skey_credentials_callback); break; } + return TRUE; } static void Index: mech-rpa.c =================================================================== RCS file: /var/lib/cvs/dovecot/src/auth/mech-rpa.c,v retrieving revision 1.28 diff -u -r1.28 mech-rpa.c --- mech-rpa.c 26 Jan 2007 13:58:48 -0000 1.28 +++ mech-rpa.c 29 Mar 2007 06:19:13 -0000 @@ -453,7 +453,7 @@ return memcmp(response, request->user_response, 16) == 0; } -static void +static bool rpa_credentials_callback(enum passdb_result result, const char *credentials, struct auth_request *auth_request) @@ -466,14 +466,13 @@ switch (result) { case PASSDB_RESULT_OK: if (!verify_credentials(request, credentials)) - auth_request_fail(auth_request); - else { - token4 = mech_rpa_build_token4(request, &token4_size); - auth_request->callback(auth_request, - AUTH_CLIENT_RESULT_CONTINUE, - token4, token4_size); - request->phase = 2; - } + return FALSE; + + token4 = mech_rpa_build_token4(request, &token4_size); + auth_request->callback(auth_request, + AUTH_CLIENT_RESULT_CONTINUE, + token4, token4_size); + request->phase = 2; break; case PASSDB_RESULT_INTERNAL_FAILURE: auth_request_internal_failure(auth_request); @@ -482,6 +481,7 @@ auth_request_fail(auth_request); break; } + return TRUE; } static void Index: mech-skey.c =================================================================== RCS file: /var/lib/cvs/dovecot/src/auth/mech-skey.c,v retrieving revision 1.1 diff -u -r1.1 mech-skey.c --- mech-skey.c 12 Nov 2006 19:36:41 -0000 1.1 +++ mech-skey.c 29 Mar 2007 06:19:13 -0000 @@ -60,7 +60,7 @@ answer, strlen(answer)); } -static void +static bool otp_credentials_callback(enum passdb_result result, const char *credentials, struct auth_request *auth_request) @@ -76,9 +76,10 @@ auth_request_fail(auth_request); break; } + return TRUE; } -static void +static bool skey_credentials_callback(enum passdb_result result, const char *credentials, struct auth_request *auth_request) @@ -97,6 +98,7 @@ otp_credentials_callback); break; } + return TRUE; } static void Index: passdb-sql.c =================================================================== RCS file: /var/lib/cvs/dovecot/src/auth/passdb-sql.c,v retrieving revision 1.31 diff -u -r1.31 passdb-sql.c --- passdb-sql.c 15 Dec 2006 18:38:08 -0000 1.31 +++ passdb-sql.c 29 Mar 2007 06:19:13 -0000 @@ -52,76 +52,131 @@ } } +static int sql_query_next_row(struct sql_result *result, + struct passdb_sql_request *sql_request) +{ + struct auth_request *auth_request = sql_request->auth_request; + struct passdb_module *_module = auth_request->passdb->passdb; + struct sql_passdb_module *module = (struct sql_passdb_module *)_module; + int ret; + + ret = sql_result_next_row(result); + if (ret <= 0) { + if (ret == 0) + return 0; + + auth_request_log_error(auth_request, "sql", + "Password query failed: %s", + sql_result_get_error(result)); + return -1; + } + + sql_query_save_results(result, sql_request); + + /* Note that we really want to check if the password field is + found. Just checking if password is set isn't enough, + because with proxies we might want to return NULL as + password. */ + if (sql_result_find_field(result, "password") < 0) { + auth_request_log_error(auth_request, "sql", + "Password query must return a field named 'password'"); + } + + if (!module->conn->set.allow_multiple_rows) { + if (sql_result_next_row(result) > 0) { + auth_request_log_error(auth_request, "sql", + "Password query returned multiple matches " + "and allow_multiple_rows=no"); + return -1; + } + } + return 1; +} + static void sql_query_callback(struct sql_result *result, struct passdb_sql_request *sql_request) { struct auth_request *auth_request = sql_request->auth_request; enum passdb_result passdb_result; const char *user, *password, *scheme; + unsigned int row; int ret; + bool send_last = FALSE; passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; + user = auth_request->user; - password = NULL; + for (row = 0;; row++) { + if (row > 0) + auth_request_reset_passdb_lookup(auth_request); + + ret = sql_query_next_row(result, sql_request); + if (row > 0 && ret == 0) { + /* no more rows. use the last result. */ + send_last = TRUE; + break; + } - ret = sql_result_next_row(result); - if (ret < 0) { - auth_request_log_error(auth_request, "sql", - "Password query failed: %s", - sql_result_get_error(result)); - } else if (ret == 0) { - auth_request_log_info(auth_request, "sql", "unknown user"); - passdb_result = PASSDB_RESULT_USER_UNKNOWN; - } else { - sql_query_save_results(result, sql_request); - - /* Note that we really want to check if the password field is - found. Just checking if password is set isn't enough, - because with proxies we might want to return NULL as - password. */ - if (sql_result_find_field(result, "password") < 0) { - auth_request_log_error(auth_request, "sql", - "Password query must return a field named " - "'password'"); - } else if (sql_result_next_row(result) > 0) { - auth_request_log_error(auth_request, "sql", - "Password query returned multiple matches"); - } else { + switch (ret) { + case 1: /* passdb_password may change on the way, so we'll need to strdup. */ password = t_strdup(auth_request->passdb_password); if (password == NULL) auth_request->no_password = TRUE; passdb_result = PASSDB_RESULT_OK; + break; + case 0: + auth_request_log_info(auth_request, "sql", + "unknown user"); + passdb_result = PASSDB_RESULT_USER_UNKNOWN; + password = NULL; + break; + default: + passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; + password = NULL; + break; } - } - scheme = password_get_scheme(&password); - /* auth_request_set_field() sets scheme */ - i_assert(password == NULL || scheme != NULL); - - if (auth_request->credentials != -1) { - passdb_handle_credentials(passdb_result, password, scheme, - sql_request->callback.lookup_credentials, - auth_request); - auth_request_unref(&auth_request); - return; + scheme = password_get_scheme(&password); + /* auth_request_set_field() sets scheme */ + i_assert(password == NULL || scheme != NULL); + + if (auth_request->credentials != -1) { + lookup_credentials_callback_t *callback = + sql_request->callback.lookup_credentials; + + if (passdb_handle_credentials(passdb_result, password, + scheme, callback, + auth_request)) + break; + } else { + /* verify plain */ + if (password == NULL) + break; + + if (auth_request_password_verify(auth_request, + auth_request->mech_password, + password, scheme, "sql") > 0) { + passdb_result = PASSDB_RESULT_OK; + break; + } + passdb_result = PASSDB_RESULT_PASSWORD_MISMATCH; + } + + /* see if there's another row */ } - /* verify plain */ - if (password == NULL) { + if (auth_request->credentials == -1) sql_request->callback.verify_plain(passdb_result, auth_request); - auth_request_unref(&auth_request); - return; + else if (send_last) { + lookup_credentials_callback_t *callback = + sql_request->callback.lookup_credentials; + + if (!passdb_handle_credentials(PASSDB_RESULT_END_OF_LIST, NULL, + NULL, callback, auth_request)) + i_unreached(); } - - ret = auth_request_password_verify(auth_request, - auth_request->mech_password, - password, scheme, "sql"); - - sql_request->callback.verify_plain(ret > 0 ? PASSDB_RESULT_OK : - PASSDB_RESULT_PASSWORD_MISMATCH, - auth_request); auth_request_unref(&auth_request); } Index: passdb.c =================================================================== RCS file: /var/lib/cvs/dovecot/src/auth/passdb.c,v retrieving revision 1.47 diff -u -r1.47 passdb.c --- passdb.c 15 Feb 2007 11:51:33 -0000 1.47 +++ passdb.c 29 Mar 2007 06:19:13 -0000 @@ -118,21 +118,19 @@ return password; } -void passdb_handle_credentials(enum passdb_result result, +bool passdb_handle_credentials(enum passdb_result result, const char *password, const char *scheme, lookup_credentials_callback_t *callback, struct auth_request *auth_request) { - if (result != PASSDB_RESULT_OK) { - callback(result, NULL, auth_request); - return; - } + if (result != PASSDB_RESULT_OK) + return callback(result, NULL, auth_request); password = password == NULL ? NULL : passdb_get_credentials(auth_request, password, scheme); if (password == NULL) result = PASSDB_RESULT_SCHEME_NOT_AVAILABLE; - callback(result, password, auth_request); + return callback(result, password, auth_request); } struct auth_passdb *passdb_preinit(struct auth *auth, const char *driver, Index: passdb.h =================================================================== RCS file: /var/lib/cvs/dovecot/src/auth/passdb.h,v retrieving revision 1.37 diff -u -r1.37 passdb.h --- passdb.h 16 Dec 2006 13:37:32 -0000 1.37 +++ passdb.h 29 Mar 2007 06:19:13 -0000 @@ -27,6 +27,7 @@ PASSDB_RESULT_USER_UNKNOWN = -3, PASSDB_RESULT_USER_DISABLED = -4, PASSDB_RESULT_PASS_EXPIRED = -5, + PASSDB_RESULT_END_OF_LIST = -6, PASSDB_RESULT_PASSWORD_MISMATCH = 0, PASSDB_RESULT_OK = 1 @@ -34,7 +35,11 @@ typedef void verify_plain_callback_t(enum passdb_result result, struct auth_request *request); -typedef void lookup_credentials_callback_t(enum passdb_result result, +/* Returns TRUE if successful, FALSE if more credentials are wanted + (ie. support for multiple passwords). If FALSE is returned, the caller must + call this function again. If there are no more results, + result=PASSDB_RESULT_END_OF_LIST */ +typedef bool lookup_credentials_callback_t(enum passdb_result result, const char *password, struct auth_request *request); typedef void set_credentials_callback_t(enum passdb_result result, @@ -80,10 +85,11 @@ passdb_get_credentials(struct auth_request *auth_request, const char *password, const char *scheme); -void passdb_handle_credentials(enum passdb_result result, +bool passdb_handle_credentials(enum passdb_result result, const char *password, const char *scheme, lookup_credentials_callback_t *callback, - struct auth_request *auth_request); + struct auth_request *auth_request) + __attr_warn_unused_result__; const char *passdb_credentials_to_str(enum passdb_credentials credentials, const char *wanted_scheme); Index: userdb-static.c =================================================================== RCS file: /var/lib/cvs/dovecot/src/auth/userdb-static.c,v retrieving revision 1.23 diff -u -r1.23 userdb-static.c --- userdb-static.c 21 Mar 2007 20:13:00 -0000 1.23 +++ userdb-static.c 29 Mar 2007 06:19:13 -0000 @@ -60,7 +60,7 @@ t_pop(); } -static void +static bool static_credentials_callback(enum passdb_result result, const char *password __attr_unused__, struct auth_request *auth_request) @@ -92,6 +92,7 @@ } i_free(ctx); + return TRUE; } static void static_lookup(struct auth_request *auth_request,