Mercurial > dovecot > original-hg > dovecot-1.2
changeset 8251:26e7d4905d81 HEAD
PAM: Attempt to give better error messages.
Detect missing /etc/pam.d/service file and log it as an error the first time
after startup. Don't return "unknown user" and "password mismatch" as errors
but informational messages.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 06 Oct 2008 00:20:55 +0300 |
parents | 750ca24835c4 |
children | 533b43760eaa |
files | src/auth/passdb-pam.c |
diffstat | 1 files changed, 59 insertions(+), 5 deletions(-) [+] |
line wrap: on
line diff
--- a/src/auth/passdb-pam.c Sun Oct 05 23:27:33 2008 +0300 +++ b/src/auth/passdb-pam.c Mon Oct 06 00:20:55 2008 +0300 @@ -20,6 +20,7 @@ #include "auth-cache.h" #include <stdlib.h> +#include <sys/stat.h> #ifdef HAVE_SECURITY_PAM_APPL_H # include <security/pam_appl.h> @@ -31,6 +32,10 @@ /* Sun's PAM doesn't use const. we use a bit dirty hack to check it. Originally it was just __sun__ check, but HP/UX also uses Sun's PAM so I thought this might work better. */ +# define SUNPAM +#endif + +#ifdef SUNPAM # define linux_const #else # define linux_const const @@ -117,17 +122,66 @@ return PAM_SUCCESS; } -static int try_pam_auth(struct auth_request *request, pam_handle_t *pamh) +static const char * +pam_get_missing_service_file_path(const char *service ATTR_UNUSED) +{ +#ifdef SUNPAM + /* Uses /etc/pam.conf - we're not going to parse that */ + return NULL; +#else + static bool service_checked = FALSE; + const char *path; + struct stat st; + + if (service_checked) { + /* check and complain only once */ + return NULL; + } + service_checked = TRUE; + + path = t_strdup_printf("/etc/pam.d/%s", service); + if (stat(path, &st) < 0 && errno == ENOENT) { + /* looks like it's missing. but before assuming that the system + even uses /etc/pam.d, make sure that it exists. */ + if (stat("/etc/pam.d", &st) == 0) + return path; + } + /* exists or is unknown */ + return NULL; +} +#endif + +static int try_pam_auth(struct auth_request *request, pam_handle_t *pamh, + const char *service) { struct passdb_module *_module = request->passdb->passdb; struct pam_passdb_module *module = (struct pam_passdb_module *)_module; + const char *path, *str; pam_item_t item; int status; if ((status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) { - auth_request_log_error(request, "pam", - "pam_authenticate() failed: %s", - pam_strerror(pamh, status)); + path = pam_get_missing_service_file_path(service); + switch (status) { + case PAM_USER_UNKNOWN: + str = "unknown user"; + break; + default: + str = t_strconcat("pam_authenticate() failed: ", + pam_strerror(pamh, status), NULL); + break; + } + if (path != NULL) { + /* log this as error, since it probably is */ + str = t_strdup_printf("%s (%s missing?)", str, path); + auth_request_log_error(request, "pam", "%s", str); + } else { + if (status == PAM_AUTH_ERR) { + str = t_strconcat(str, " (password mismatch?)", + NULL); + } + auth_request_log_info(request, "pam", "%s", str); + } return status; } @@ -215,7 +269,7 @@ } set_pam_items(request, pamh); - status = try_pam_auth(request, pamh); + status = try_pam_auth(request, pamh, service); if ((status2 = pam_end(pamh, status)) != PAM_SUCCESS) { auth_request_log_error(request, "pam", "pam_end() failed: %s", pam_strerror(pamh, status2));