Hi there,
Other auth modules (eg passwd-file) allow a username_format to be
specified, but not the PAM module.
The use-case, is where I want a static userdb configuration which takes the
domain into account but still want to use PAM for authentication, eg:
userdb {
driver = static
args = uid=8 gid=12 home=/mnt/storage/mail/vhosts/%d/%n
}
passdb {
driver = pam
args = username_format=%n allow_pam_transform=no dovecot
}
The global "auth_username_format" setting ends up changing the
username, so
looses the ability to have different mailboxes based on domain.
There is also a new setting, allow_pam_transform which stops the username
being changed after a successful authentication. Normally, if PAM changes
the username, then dovecot must update its record of the username for
further processing - but in this use case we must disable this function.
--
Brad.
-------------- next part --------------
--- 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]);
}