--- dovecot-2.2.10/src/auth/passdb-pam.c.orig 2014-12-11 22:48:47.861478049 +0000 +++ dovecot-2.2.10/src/auth/passdb-pam.c 2014-12-12 11:25:23.304742138 +0000 @@ -41,12 +41,13 @@ struct pam_passdb_module { struct passdb_module module; - const char *service_name, *pam_cache_key; + const char *service_name, *pam_cache_key, *username_format; unsigned int requests_left; unsigned int pam_setcred:1; unsigned int pam_session:1; unsigned int failure_show_msg:1; + unsigned int pam_allow_transform:1; }; struct pam_conv_context { @@ -67,6 +68,13 @@ char *string; int i; + const struct var_expand_table *table; + string_t *username; + + username = t_str_new(256); + table = auth_request_get_var_expand_table(ctx->request, auth_request_str_escape); + var_expand(username, passdb->username_format, table); + *resp_r = NULL; resp = calloc(num_msg, sizeof(struct pam_response)); @@ -82,7 +90,7 @@ 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(str_c(username)); if (string == NULL) i_fatal_status(FATAL_OUTOFMEM, "Out of memory"); break; @@ -108,12 +116,14 @@ } free(resp); + str_free(&username); return PAM_CONV_ERR; } resp[i].resp_retcode = PAM_SUCCESS; resp[i].resp = string; } + str_free(&username); *resp_r = resp; return PAM_SUCCESS; @@ -231,7 +241,10 @@ pam_strerror(pamh, status)); return status; } - auth_request_set_field(request, "user", item, NULL); + if (module->pam_allow_transform) + { + auth_request_set_field(request, "user", item, NULL); + } return PAM_SUCCESS; } @@ -257,6 +270,11 @@ struct pam_conv conv; enum passdb_result result; int status, status2; + const struct var_expand_table *table; + string_t *username; + + struct passdb_module *_module = request->passdb->passdb; + struct pam_passdb_module *module = (struct pam_passdb_module *)_module; conv.conv = pam_userpass_conv; conv.appdata_ptr = &ctx; @@ -265,10 +283,15 @@ ctx.request = request; ctx.pass = password; - status = pam_start(service, request->user, &conv, &pamh); + username = t_str_new(256); + table = auth_request_get_var_expand_table(request, auth_request_str_escape); + var_expand(username, module->username_format, table); + + status = pam_start(service, str_c(username), &conv, &pamh); if (status != PAM_SUCCESS) { auth_request_log_error(request, "pam", "pam_start() failed: %s", pam_strerror(pamh, status)); + str_free(&username); return PASSDB_RESULT_INTERNAL_FAILURE; } @@ -277,6 +300,7 @@ if ((status2 = pam_end(pamh, status)) != PAM_SUCCESS) { auth_request_log_error(request, "pam", "pam_end() failed: %s", pam_strerror(pamh, status2)); + str_free(&username); return PASSDB_RESULT_INTERNAL_FAILURE; } @@ -300,6 +324,7 @@ auth_request_set_field(request, "reason", ctx.failure_msg, NULL); } + str_free(&username); return result; } @@ -319,6 +344,7 @@ } expanded_service = t_str_new(64); + var_expand(expanded_service, module->service_name, auth_request_get_var_expand_table(request, NULL)); service = str_c(expanded_service); @@ -338,6 +364,8 @@ module = p_new(pool, struct pam_passdb_module, 1); module->service_name = "dovecot"; + module->username_format = "%u"; + module->pam_allow_transform = 1; /* we're caching the password by using directly the plaintext password given by the auth mechanism */ module->module.default_pass_scheme = "PLAIN"; @@ -370,6 +398,12 @@ } } 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) { + module->username_format = t_args[i] + 16; + } else if (strcmp(t_args[i], "allow_pam_transform=no") == 0 || + strcmp(t_args[i], "-allow_pam_transform") == 0) + { + module->pam_allow_transform = 0; } else { i_fatal("pam: Unknown setting: %s", t_args[i]); }