Mercurial > dovecot > original-hg > dovecot-1.2
changeset 4030:faf83f3e19b5 HEAD
Added support for "master users" who can log in as other people. Currently works only with SASL PLAIN authentication by giving it authorization ID string.
author | Timo Sirainen <timo.sirainen@movial.fi> |
---|---|
date | Mon, 20 Feb 2006 15:14:58 +0200 |
parents | 9bdd186d7b7d |
children | 357175e274b4 |
files | dovecot-example.conf src/auth/auth-request.c src/auth/auth-request.h src/auth/auth-worker-client.c src/auth/auth.c src/auth/auth.h src/auth/mech-plain.c src/auth/passdb-blocking.c src/auth/passdb.c src/auth/passdb.h src/master/auth-process.c src/master/master-settings.c src/master/master-settings.h |
diffstat | 13 files changed, 186 insertions(+), 62 deletions(-) [+] |
line wrap: on
line diff
--- a/dovecot-example.conf Sat Feb 18 14:14:32 2006 +0200 +++ b/dovecot-example.conf Mon Feb 20 15:14:58 2006 +0200 @@ -625,6 +625,12 @@ # allow both system users (/etc/passwd) and virtual users to login without # duplicating the system users into virtual database. # + # By adding master=yes setting inside a passdb you make the passdb a list + # of "master users", who can log in as anyone else. The destination user + # is still looked up from passdb without password verification, but this + # doesn't work with eg. PAM, so you can also set master_no_passdb=yes to + # skip this step and rely on userdb catching the invalid usernames. + # # http://wiki.dovecot.org/Authentication #
--- a/src/auth/auth-request.c Sat Feb 18 14:14:32 2006 +0200 +++ b/src/auth/auth-request.c Mon Feb 20 15:14:58 2006 +0200 @@ -110,6 +110,11 @@ str_append(str, "\tservice="); str_append(str, request->service); + if (request->master_user != NULL) { + str_append(str, "master_user="); + str_append(str, request->master_user); + } + if (request->local_ip.family != 0) { str_append(str, "\tlip="); str_append(str, net_ip2addr(&request->local_ip)); @@ -125,6 +130,8 @@ { if (strcmp(key, "user") == 0) request->user = p_strdup(request->pool, value); + else if (strcmp(key, "master_user") == 0) + request->master_user = p_strdup(request->pool, value); else if (strcmp(key, "cert_username") == 0) { if (request->auth->ssl_username_from_cert) { /* get username from SSL certificate. it overrides @@ -244,6 +251,30 @@ auth_cache_insert(passdb_cache, request, passdb->cache_key, str_c(str)); } +static bool auth_request_master_lookup_finish(struct auth_request *request) +{ + /* master login successful. update user and master_user variables. */ + auth_request_log_info(request, "passdb", "Master user logging in as %s", + request->requested_login_user); + + request->master_user = request->user; + request->user = request->requested_login_user; + request->requested_login_user = NULL; + + request->skip_password_check = TRUE; + request->passdb_password = NULL; + + if (request->passdb->master_no_passdb) { + /* skip the passdb lookup, we're authenticated now. */ + return TRUE; + } + + /* the authentication continues with passdb lookup for the + requested_login_user. */ + request->passdb = request->auth->passdbs; + return FALSE; +} + static bool auth_request_handle_passdb_callback(enum passdb_result *result, struct auth_request *request) @@ -262,11 +293,19 @@ "User found from deny passdb"); *result = PASSDB_RESULT_USER_DISABLED; } - } else if (*result != PASSDB_RESULT_OK && - *result != PASSDB_RESULT_USER_DISABLED && - request->passdb->next != NULL) { + } else if (*result == PASSDB_RESULT_OK) { + /* success */ + if (request->requested_login_user != NULL) { + /* this was a master user lookup. */ + if (!auth_request_master_lookup_finish(request)) + return FALSE; + } + } else if (request->passdb->next != NULL && + *result != PASSDB_RESULT_USER_DISABLED) { /* try next passdb. */ - if (*result == PASSDB_RESULT_INTERNAL_FAILURE) { + request->passdb = request->passdb->next; + + if (*result == PASSDB_RESULT_INTERNAL_FAILURE) { /* remember that we have had an internal failure. at the end return internal failure if we couldn't successfully login. */ @@ -276,11 +315,10 @@ auth_stream_reply_reset(request->extra_fields); return FALSE; - } else if (request->passdb_internal_failure && - *result != PASSDB_RESULT_OK) { - /* one of the passdb lookups returned internal failure. - it may have had the correct password, so return internal - failure instead of plain failure. */ + } else if (request->passdb_internal_failure) { + /* last passdb lookup returned internal failure. it may have + had the correct password, so return internal failure + instead of plain failure. */ *result = PASSDB_RESULT_INTERNAL_FAILURE; } @@ -312,7 +350,6 @@ if (!auth_request_handle_passdb_callback(&result, request)) { /* try next passdb */ - request->passdb = request->passdb->next; auth_request_verify_plain(request, request->mech_password, request->private_callback.verify_plain); } else { @@ -328,12 +365,20 @@ const char *password, verify_plain_callback_t *callback) { - struct passdb_module *passdb = request->passdb->passdb; + struct passdb_module *passdb; enum passdb_result result; const char *cache_key; i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE); + if (request->passdb == NULL) { + /* no masterdbs, master logins not supported */ + i_assert(request->requested_login_user != NULL); + callback(PASSDB_RESULT_USER_UNKNOWN, request); + return; + } + + passdb = request->passdb->passdb; if (request->mech_password == NULL) request->mech_password = p_strdup(request->pool, password); else @@ -391,7 +436,6 @@ if (!auth_request_handle_passdb_callback(&result, request)) { /* try next passdb */ - request->passdb = request->passdb->next; auth_request_lookup_credentials(request, request->credentials, request->private_callback.lookup_credentials); } else { @@ -474,11 +518,35 @@ userdb->iface->lookup(request, auth_request_userdb_callback); } +static char * +auth_request_fix_username(struct auth_request *request, const char *username, + const char **error_r) +{ + unsigned char *p; + char *user; + + if (strchr(username, '@') == NULL && + request->auth->default_realm != NULL) { + user = p_strconcat(request->pool, username, "@", + request->auth->default_realm, NULL); + } else { + user = p_strdup(request->pool, username); + } + + for (p = (unsigned char *)user; *p != '\0'; p++) { + if (request->auth->username_translation[*p & 0xff] != 0) + *p = request->auth->username_translation[*p & 0xff]; + if (request->auth->username_chars[*p & 0xff] == 0) { + *error_r = "Username contains disallowed characters"; + return NULL; + } + } + return user; +} + bool auth_request_set_username(struct auth_request *request, const char *username, const char **error_r) { - unsigned char *p; - if (request->cert_username) { /* cert_username overrides the username given by authentication mechanism. */ @@ -491,24 +559,22 @@ return FALSE; } - if (strchr(username, '@') == NULL && - request->auth->default_realm != NULL) { - request->user = p_strconcat(request->pool, username, "@", - request->auth->default_realm, NULL); - } else { - request->user = p_strdup(request->pool, username); - } + request->user = auth_request_fix_username(request, username, error_r); + return request->user != NULL; +} - for (p = (unsigned char *)request->user; *p != '\0'; p++) { - if (request->auth->username_translation[*p & 0xff] != 0) - *p = request->auth->username_translation[*p & 0xff]; - if (request->auth->username_chars[*p & 0xff] == 0) { - *error_r = "Username contains disallowed characters"; - return FALSE; - } - } +bool auth_request_set_login_username(struct auth_request *request, + const char *username, + const char **error_r) +{ + i_assert(*username != '\0'); - return TRUE; + /* lookup request->user from masterdb first */ + request->passdb = request->auth->masterdbs; + + request->requested_login_user = + auth_request_fix_username(request, username, error_r); + return request->requested_login_user != NULL; } void auth_request_set_field(struct auth_request *request, @@ -585,6 +651,12 @@ { int ret; + if (request->skip_password_check) { + /* currently this can happen only with master logins */ + i_assert(request->master_user != NULL); + return 1; + } + ret = password_verify(plain_password, crypted_password, scheme, request->user); if (ret < 0) { @@ -673,6 +745,8 @@ str_append_c(str, ','); str_append(str, ip); } + if (auth_request->requested_login_user != NULL) + str_append(str, ",master"); str_append(str, "): "); str_vprintfa(str, format, va); return str_c(str);
--- a/src/auth/auth-request.h Sat Feb 18 14:14:32 2006 +0200 +++ b/src/auth/auth-request.h Mon Feb 20 15:14:58 2006 +0200 @@ -21,7 +21,13 @@ pool_t pool; enum auth_request_state state; - char *user; + /* user contains the user who is being authenticated. + When master user is logging in as someone else, it gets more + complicated. Initially user is set to master's username and the + requested_login_user is set to destination username. After masterdb + has validated user as a valid master user, master_user is set to + user and user is set to requested_login_user. */ + char *user, *requested_login_user, *master_user; char *mech_password; /* set if verify_plain() is called */ char *passdb_password; /* set after password lookup if successful */ struct auth_stream_reply *extra_fields; @@ -58,6 +64,7 @@ unsigned int no_failure_delay:1; unsigned int no_login:1; unsigned int no_password:1; + unsigned int skip_password_check:1; unsigned int proxy:1; unsigned int cert_username:1; @@ -96,6 +103,9 @@ bool auth_request_set_username(struct auth_request *request, const char *username, const char **error_r); +bool auth_request_set_login_username(struct auth_request *request, + const char *username, + const char **error_r); void auth_request_set_field(struct auth_request *request, const char *name, const char *value,
--- a/src/auth/auth-worker-client.c Sat Feb 18 14:14:32 2006 +0200 +++ b/src/auth/auth-worker-client.c Mon Feb 20 15:14:58 2006 +0200 @@ -111,9 +111,9 @@ /* verify plaintext password */ struct auth_request *auth_request; const char *password; - unsigned int num; + unsigned int passdb_id; - num = atoi(t_strcut(args, '\t')); + passdb_id = atoi(t_strcut(args, '\t')); args = strchr(args, '\t'); if (args == NULL) { i_error("BUG: Auth worker server sent us invalid PASSV"); @@ -135,10 +135,10 @@ return; } - for (; num > 0; num--) { + while (auth_request->passdb->id != passdb_id) { auth_request->passdb = auth_request->passdb->next; if (auth_request->passdb == NULL) { - i_error("BUG: PASSV had invalid passdb num"); + i_error("BUG: PASSV had invalid passdb ID"); auth_request_unref(&auth_request); return; }
--- a/src/auth/auth.c Sat Feb 18 14:14:32 2006 +0200 +++ b/src/auth/auth.c Mon Feb 20 15:14:58 2006 +0200 @@ -17,7 +17,7 @@ struct auth *auth_preinit(void) { struct auth *auth; - struct auth_passdb *auth_passdb; + struct auth_passdb *auth_passdb, **passdb_p, **masterdb_p; const char *driver, *args; pool_t pool; unsigned int i; @@ -32,17 +32,32 @@ getenv("VERBOSE_DEBUG_PASSWORDS") != NULL; t_push(); + passdb_p = &auth->passdbs; + masterdb_p = &auth->masterdbs; for (i = 1; ; i++) { driver = getenv(t_strdup_printf("PASSDB_%u_DRIVER", i)); if (driver == NULL) break; args = getenv(t_strdup_printf("PASSDB_%u_ARGS", i)); - auth_passdb = passdb_preinit(auth, driver, args); + auth_passdb = passdb_preinit(auth, driver, args, i); + + auth_passdb->deny = + getenv(t_strdup_printf("PASSDB_%u_DENY", i)) != NULL; + auth_passdb->master_no_passdb = + getenv(t_strdup_printf("PASSDB_%u_MASTER_NO_PASSDB", + i)) != NULL; - if (getenv(t_strdup_printf("PASSDB_%u_DENY", i)) != NULL) - auth_passdb->deny = TRUE; + if (getenv(t_strdup_printf("PASSDB_%u_MASTER", i)) == NULL) { + *passdb_p = auth_passdb; + passdb_p = &auth_passdb->next; + } else { + if (auth_passdb->deny) + i_fatal("Master passdb can't have deny=yes"); + *masterdb_p = auth_passdb; + masterdb_p = &auth_passdb->next; + } } t_pop(); @@ -157,6 +172,8 @@ const char *const *mechanisms; const char *env; + for (passdb = auth->masterdbs; passdb != NULL; passdb = passdb->next) + passdb_init(passdb); for (passdb = auth->passdbs; passdb != NULL; passdb = passdb->next) passdb_init(passdb); for (userdb = auth->userdbs; userdb != NULL; userdb = userdb->next) @@ -239,6 +256,8 @@ *_auth = NULL; passdb_cache_deinit(); + for (passdb = auth->masterdbs; passdb != NULL; passdb = passdb->next) + passdb_deinit(passdb); for (passdb = auth->passdbs; passdb != NULL; passdb = passdb->next) passdb_deinit(passdb); for (userdb = auth->userdbs; userdb != NULL; userdb = userdb->next)
--- a/src/auth/auth.h Sat Feb 18 14:14:32 2006 +0200 +++ b/src/auth/auth.h Mon Feb 20 15:14:58 2006 +0200 @@ -5,7 +5,8 @@ struct auth *auth; struct auth_passdb *next; - unsigned int num; + /* id is used by blocking passdb to identify the passdb */ + unsigned int id; const char *args; struct passdb_module *passdb; #ifdef HAVE_MODULES @@ -13,6 +14,8 @@ #endif /* if user is found from this passdb, deny authentication immediately */ unsigned int deny:1; + /* masterdb: no passdb lookup for user wanted */ + unsigned int master_no_passdb:1; }; struct auth_userdb { @@ -33,6 +36,7 @@ struct mech_module_list *mech_modules; buffer_t *mech_handshake; + struct auth_passdb *masterdbs; struct auth_passdb *passdbs; struct auth_userdb *userdbs;
--- a/src/auth/mech-plain.c Sat Feb 18 14:14:32 2006 +0200 +++ b/src/auth/mech-plain.c Mon Feb 20 15:14:58 2006 +0200 @@ -54,19 +54,23 @@ /* invalid input */ auth_request_log_info(request, "plain", "invalid input"); auth_request_fail(request); - } else { - if (!auth_request_set_username(request, authenid, &error)) { - /* invalid username */ - auth_request_log_info(request, "plain", "%s", error); - auth_request_fail(request); - } else { - auth_request_verify_plain(request, pass, - verify_callback); - } + } else if (!auth_request_set_username(request, authenid, &error)) { + /* invalid username */ + auth_request_log_info(request, "plain", "%s", error); + auth_request_fail(request); + } else if (*authid != '\0' && + !auth_request_set_login_username(request, authid, &error)) { + /* invalid login username */ + auth_request_log_info(request, "plain", + "login user: %s", error); + auth_request_fail(request); + } else { + auth_request_verify_plain(request, pass, + verify_callback); + } - /* make sure it's cleared */ - safe_memset(pass, 0, strlen(pass)); - } + /* make sure it's cleared */ + safe_memset(pass, 0, strlen(pass)); } static void
--- a/src/auth/passdb-blocking.c Sat Feb 18 14:14:32 2006 +0200 +++ b/src/auth/passdb-blocking.c Mon Feb 20 15:14:58 2006 +0200 @@ -119,7 +119,7 @@ i_assert(request->extra_fields == NULL); str = t_str_new(64); - str_printfa(str, "PASSV\t%u\t", request->passdb->num); + str_printfa(str, "PASSV\t%u\t", request->passdb->id); str_append(str, request->mech_password); str_append_c(str, '\t'); auth_request_export(request, str); @@ -152,7 +152,7 @@ str = t_str_new(64); str_printfa(str, "PASSL\t%u\t%d\t", - request->passdb->num, request->credentials); + request->passdb->id, request->credentials); auth_request_export(request, str); auth_worker_call(request, str_c(str), lookup_credentials_callback);
--- a/src/auth/passdb.c Sat Feb 18 14:14:32 2006 +0200 +++ b/src/auth/passdb.c Mon Feb 20 15:14:58 2006 +0200 @@ -122,20 +122,17 @@ } struct auth_passdb *passdb_preinit(struct auth *auth, const char *driver, - const char *args) + const char *args, unsigned int id) { struct passdb_module_interface **p, *iface; - struct auth_passdb *auth_passdb, **dest; + struct auth_passdb *auth_passdb; if (args == NULL) args = ""; auth_passdb = p_new(auth->pool, struct auth_passdb, 1); auth_passdb->auth = auth; - auth_passdb->args = p_strdup(auth->pool, args); - - for (dest = &auth->passdbs; *dest != NULL; dest = &(*dest)->next) - auth_passdb->num++; - *dest = auth_passdb; + auth_passdb->args = p_strdup(auth->pool, args); + auth_passdb->id = id; iface = NULL; for (p = passdb_interfaces; *p != NULL; p++) {
--- a/src/auth/passdb.h Sat Feb 18 14:14:32 2006 +0200 +++ b/src/auth/passdb.h Mon Feb 20 15:14:58 2006 +0200 @@ -77,7 +77,7 @@ const char *passdb_credentials_to_str(enum passdb_credentials credentials); struct auth_passdb *passdb_preinit(struct auth *auth, const char *driver, - const char *args); + const char *args, unsigned int id); void passdb_init(struct auth_passdb *passdb); void passdb_deinit(struct auth_passdb *passdb);
--- a/src/master/auth-process.c Sat Feb 18 14:14:32 2006 +0200 +++ b/src/master/auth-process.c Mon Feb 20 15:14:58 2006 +0200 @@ -431,6 +431,12 @@ } if (ap->deny) env_put(t_strdup_printf("PASSDB_%u_DENY=1", i)); + if (ap->master) + env_put(t_strdup_printf("PASSDB_%u_MASTER=1", i)); + if (ap->master_no_passdb) { + env_put(t_strdup_printf("PASSDB_%u_MASTER_NO_PASSDB=1", + i)); + } } for (au = set->userdbs, i = 1; au != NULL; au = au->next, i++) { env_put(t_strdup_printf("USERDB_%u_DRIVER=%s", i, au->driver));