lee at trager.us
2014-Apr-24 06:50 UTC
[Dovecot] Help implementing username_format in auth PAM driver
While configuring my server with dovecot I noticed that the PAM authentication driver does not support the username_format option as does the password file driver. This didn't seem too hard to implement so I through together a patch. As you can see in the attached patch I only modify the username sent to PAM. Despit doing this I run into the domain lost issue(http://wiki2.dovecot.org/DomainLost). This prevents me from using the domain name in my mail_location config string. What I don't understand is why does changing the username string sent to PAM for authentication trigger this issue? Shouldn't dovecot continue to use the client supplied username as I am *not* changing it anywhere in my config? Thanks, Lee -------------- next part -------------- diff --git a/src/auth/passdb-pam.c b/src/auth/passdb-pam.c index cf0b3c9..5f42a5a 100644 --- a/src/auth/passdb-pam.c +++ b/src/auth/passdb-pam.c @@ -37,6 +37,7 @@ typedef pam_const void *pam_item_t; #define PASSDB_PAM_DEFAULT_MAX_REQUESTS 100 +#define PASSDB_PAM_DEFAULT_USERNAME_FORMAT "%u" struct pam_passdb_module { struct passdb_module module; @@ -47,6 +48,7 @@ struct pam_passdb_module { unsigned int pam_setcred:1; unsigned int pam_session:1; unsigned int failure_show_msg:1; + const char *username_format; }; struct pam_conv_context { @@ -55,6 +57,17 @@ struct pam_conv_context { const char *failure_msg; }; +inline const char* +pam_username_lookup(struct auth_request *request) +{ + struct passdb_module *_module = request->passdb->passdb; + struct pam_passdb_module *module = (struct pam_passdb_module *)_module; + string_t *username = t_str_new(256); + var_expand(username, module->username_format, + auth_request_get_var_expand_table(request, auth_request_str_escape)); + return str_c(username); +} + static int pam_userpass_conv(int num_msg, pam_const struct pam_message **msg, struct pam_response **resp_r, void *appdata_ptr) @@ -82,7 +95,7 @@ pam_userpass_conv(int num_msg, pam_const struct pam_message **msg, case PAM_PROMPT_ECHO_ON: /* Assume we're asking for user. We might not ever get here because PAM already knows the user. */ - string = strdup(ctx->request->user); + string = strdup(pam_username_lookup(ctx->request)); if (string == NULL) i_fatal_status(FATAL_OUTOFMEM, "Out of memory"); break; @@ -240,7 +253,7 @@ static void set_pam_items(struct auth_request *request, pam_handle_t *pamh) host = net_ip2addr(&request->remote_ip); if (host != NULL) (void)pam_set_item(pamh, PAM_RHOST, host); - (void)pam_set_item(pamh, PAM_RUSER, request->user); + (void)pam_set_item(pamh, PAM_RUSER, pam_username_lookup(request)); /* TTY is needed by eg. pam_access module */ (void)pam_set_item(pamh, PAM_TTY, "dovecot"); } @@ -262,7 +275,7 @@ pam_verify_plain_call(struct auth_request *request, const char *service, ctx.request = request; ctx.pass = password; - status = pam_start(service, request->user, &conv, &pamh); + status = pam_start(service, pam_username_lookup(request), &conv, &pamh); if (status != PAM_SUCCESS) { auth_request_log_error(request, "pam", "pam_start() failed: %s", pam_strerror(pamh, status)); @@ -331,6 +344,7 @@ pam_preinit(pool_t pool, const char *args) { struct pam_passdb_module *module; const char *const *t_args; + const char *format = PASSDB_PAM_DEFAULT_USERNAME_FORMAT; int i; module = p_new(pool, struct pam_passdb_module, 1); @@ -367,9 +381,14 @@ pam_preinit(pool_t pool, const char *args) } } else if (t_args[i+1] == NULL) { module->service_name = p_strdup(pool, t_args[i]); + } else if (strncmp(t_args[i], "username_format=", 16) == 0) { + format = auth_cache_parse_key(pool, t_args[i] + 16); } else { i_fatal("pam: Unknown setting: %s", t_args[i]); } } + + module->username_format = format; + return &module->module; }