dovecot: PAM lookups are now always done in auth worker processes.

dovecot at dovecot.org dovecot at dovecot.org
Tue Aug 7 13:46:59 EEST 2007


details:   http://hg.dovecot.org/dovecot/rev/74df0c0743c4
changeset: 6218:74df0c0743c4
user:      Timo Sirainen <tss at iki.fi>
date:      Tue Aug 07 13:46:55 2007 +0300
description:
PAM lookups are now always done in auth worker processes.

diffstat:

2 files changed, 79 insertions(+), 318 deletions(-)
dovecot-example.conf  |    6 
src/auth/passdb-pam.c |  391 +++++++++----------------------------------------

diffs (truncated from 543 to 300 lines):

diff -r 06743e1e4c13 -r 74df0c0743c4 dovecot-example.conf
--- a/dovecot-example.conf	Tue Aug 07 13:34:49 2007 +0300
+++ b/dovecot-example.conf	Tue Aug 07 13:46:55 2007 +0300
@@ -803,12 +803,8 @@ auth default {
   # REMEMBER: You'll need /etc/pam.d/dovecot file created for PAM
   # authentication to actually work. <doc/wiki/PasswordDatabase.PAM.txt>
   passdb pam {
-    # [blocking=yes] [session=yes] [setcred=yes] [failure_show_msg=yes]
+    # [session=yes] [setcred=yes] [failure_show_msg=yes]
     # [cache_key=<key>] [<service name>]
-    #
-    # By default a new process is forked from dovecot-auth for each PAM lookup.
-    # Setting blocking=yes uses the alternative way: dovecot-auth worker
-    # processes do the PAM lookups.
     #
     # session=yes makes Dovecot open and immediately close PAM session. Some
     # PAM plugins need this to work, such as pam_mkhomedir.
diff -r 06743e1e4c13 -r 74df0c0743c4 src/auth/passdb-pam.c
--- a/src/auth/passdb-pam.c	Tue Aug 07 13:34:49 2007 +0300
+++ b/src/auth/passdb-pam.c	Tue Aug 07 13:46:55 2007 +0300
@@ -12,9 +12,6 @@
 #ifdef PASSDB_PAM
 
 #include "lib-signals.h"
-#include "buffer.h"
-#include "ioloop.h"
-#include "hash.h"
 #include "str.h"
 #include "var-expand.h"
 #include "network.h"
@@ -22,12 +19,6 @@
 #include "safe-memset.h"
 
 #include <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/wait.h>
-
-#define PAM_CHILD_TIMEOUT (60*2)
-#define PAM_CHILD_CHECK_TIMEOUT (10*1000)
 
 #ifdef HAVE_SECURITY_PAM_APPL_H
 #  include <security/pam_appl.h>
@@ -55,26 +46,11 @@ struct pam_passdb_module {
 	unsigned int failure_show_msg:1;
 };
 
-struct pam_auth_request {
-	int refcount;
-	int fd;
-	struct io *io;
-
-	time_t start_time;
-	pid_t pid;
-
-	struct auth_request *request;
-        verify_plain_callback_t *callback;
-};
-
 struct pam_conv_context {
 	struct auth_request *request;
 	const char *pass;
 	const char *failure_msg;
 };
-
-static struct hash_table *pam_requests;
-static struct timeout *to;
 
 static int
 pam_userpass_conv(int num_msg, linux_const struct pam_message **msg,
@@ -140,19 +116,17 @@ pam_userpass_conv(int num_msg, linux_con
 	return PAM_SUCCESS;
 }
 
-static int pam_auth(struct auth_request *request,
-		    pam_handle_t *pamh, const char **error)
+static int try_pam_auth(struct auth_request *request, pam_handle_t *pamh)
 {
         struct passdb_module *_module = request->passdb->passdb;
         struct pam_passdb_module *module = (struct pam_passdb_module *)_module;
 	pam_item_t item;
 	int status;
 
-	*error = NULL;
-
 	if ((status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
-		*error = t_strdup_printf("pam_authenticate() failed: %s",
-					 pam_strerror(pamh, status));
+		auth_request_log_error(request, "pam",
+				       "pam_authenticate() failed: %s",
+				       pam_strerror(pamh, status));
 		return status;
 	}
 
@@ -160,59 +134,70 @@ static int pam_auth(struct auth_request 
 	if (module->pam_setcred) {
 		if ((status = pam_setcred(pamh, PAM_ESTABLISH_CRED)) !=
 		    PAM_SUCCESS) {
-			*error = t_strdup_printf("pam_setcred() failed: %s",
-						 pam_strerror(pamh, status));
+			auth_request_log_error(request, "pam",
+					       "pam_setcred() failed: %s",
+					       pam_strerror(pamh, status));
 			return status;
 		}
 	}
 #endif
 
 	if ((status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
-		*error = t_strdup_printf("pam_acct_mgmt() failed: %s",
-					 pam_strerror(pamh, status));
+		auth_request_log_error(request, "pam",
+				       "pam_acct_mgmt() failed: %s",
+				       pam_strerror(pamh, status));
 		return status;
 	}
 
 	if (module->pam_session) {
 	        if ((status = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
-			*error = t_strdup_printf(
-					"pam_open_session() failed: %s",
-					pam_strerror(pamh, status));
+			auth_request_log_error(request, "pam",
+					       "pam_open_session() failed: %s",
+					       pam_strerror(pamh, status));
 	                return status;
 	        }
 
 	        if ((status = pam_close_session(pamh, 0)) != PAM_SUCCESS) {
-			*error = t_strdup_printf(
-					"pam_close_session() failed: %s",
-	                                pam_strerror(pamh, status));
-	                return status;
+			auth_request_log_error(request, "pam",
+					       "pam_close_session() failed: %s",
+					       pam_strerror(pamh, status));
+			return status;
 	        }
 	}
 
-	/* FIXME: this works only with blocking=yes */
 	status = pam_get_item(pamh, PAM_USER, &item);
 	if (status != PAM_SUCCESS) {
-		*error = t_strdup_printf("pam_get_item() failed: %s",
-					 pam_strerror(pamh, status));
+		auth_request_log_error(request, "pam",
+				       "pam_get_item(PAM_USER) failed: %s",
+				       pam_strerror(pamh, status));
 		return status;
 	}
-        auth_request_set_field(request, "user", item, NULL);
-
+	auth_request_set_field(request, "user", item, NULL);
 	return PAM_SUCCESS;
 }
 
+static void set_pam_items(struct auth_request *request, pam_handle_t *pamh)
+{
+	const char *host;
+
+	/* These shouldn't fail, and we don't really care if they do. */
+	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);
+	/* TTY is needed by eg. pam_access module */
+	(void)pam_set_item(pamh, PAM_TTY, "dovecot");
+}
+
 static enum passdb_result 
-pam_verify_plain_child(struct auth_request *request, const char *service,
-		       const char *password, int fd)
+pam_verify_plain_call(struct auth_request *request, const char *service,
+		      const char *password)
 {
 	pam_handle_t *pamh;
 	struct pam_conv_context ctx;
 	struct pam_conv conv;
 	enum passdb_result result;
-	int ret, status, status2;
-	const char *str;
-	size_t size;
-	buffer_t *buf;
+	int status, status2;
 
 	conv.conv = pam_userpass_conv;
 	conv.appdata_ptr = &ctx;
@@ -223,164 +208,40 @@ pam_verify_plain_child(struct auth_reque
 
 	status = pam_start(service, request->user, &conv, &pamh);
 	if (status != PAM_SUCCESS) {
-		result = PASSDB_RESULT_INTERNAL_FAILURE;
-		str = t_strdup_printf("pam_start() failed: %s",
-				      pam_strerror(pamh, status));
-	} else {
-		const char *host = net_ip2addr(&request->remote_ip);
-
-		/* Set some PAM items. They shouldn't fail, and we don't really
-		   care if they do. */
-		if (host != NULL)
-			(void)pam_set_item(pamh, PAM_RHOST, host);
-		(void)pam_set_item(pamh, PAM_RUSER, request->user);
-		/* TTY is needed by eg. pam_access module */
-		(void)pam_set_item(pamh, PAM_TTY, "dovecot");
-
-		status = pam_auth(request, pamh, &str);
-		if ((status2 = pam_end(pamh, status)) == PAM_SUCCESS) {
-			switch (status) {
-			case PAM_SUCCESS:
-				result = PASSDB_RESULT_OK;
-				break;
-			case PAM_USER_UNKNOWN:
-				result = PASSDB_RESULT_USER_UNKNOWN;
-				break;
-			case PAM_NEW_AUTHTOK_REQD:
-			case PAM_ACCT_EXPIRED:
-				result = PASSDB_RESULT_PASS_EXPIRED;
-				break;
-			default:
-				result = PASSDB_RESULT_PASSWORD_MISMATCH;
-				break;
-			}
-		} else {
-			result = PASSDB_RESULT_INTERNAL_FAILURE;
-			str = t_strdup_printf("pam_end() failed: %s",
-					      pam_strerror(pamh, status2));
-		}
-		if (result != PASSDB_RESULT_OK && ctx.failure_msg != NULL) {
-			auth_request_set_field(request, "reason",
-					       ctx.failure_msg, NULL);
-		}
-	}
-
-	if (worker) {
-		/* blocking=yes code path in auth worker */
-		return result;
-	}
-
-	buf = buffer_create_dynamic(pool_datastack_create(), 512);
-	buffer_append(buf, &result, sizeof(result));
-
-	if (str != NULL) 
-		buffer_append(buf, str, strlen(str));
-
-	/* Don't send larger writes than what would block. truncated error
-	   message isn't that bad.. */
-        size = I_MIN(buf->used, PIPE_BUF);
-	if ((ret = write(fd, buf->data, size)) != (int)size) {
-		if (ret < 0)
-			i_error("write() failed: %m");
-		else {
-			i_error("write() failed: %d != %"PRIuSIZE_T,
-				ret, buf->used);
-		}
+		auth_request_log_error(request, "pam", "pam_start() failed: %s",
+				       pam_strerror(pamh, status));
+		return PASSDB_RESULT_INTERNAL_FAILURE;
+	}
+
+	set_pam_items(request, pamh);
+	status = try_pam_auth(request, pamh);
+	if ((status2 = pam_end(pamh, status)) != PAM_SUCCESS) {
+		auth_request_log_error(request, "pam", "pam_end() failed: %s",
+				       pam_strerror(pamh, status2));
+		return PASSDB_RESULT_INTERNAL_FAILURE;
+	}
+
+	switch (status) {
+	case PAM_SUCCESS:
+		result = PASSDB_RESULT_OK;
+		break;
+	case PAM_USER_UNKNOWN:
+		result = PASSDB_RESULT_USER_UNKNOWN;
+		break;
+	case PAM_NEW_AUTHTOK_REQD:
+	case PAM_ACCT_EXPIRED:
+		result = PASSDB_RESULT_PASS_EXPIRED;
+		break;
+	default:
+		result = PASSDB_RESULT_PASSWORD_MISMATCH;
+		break;
+	}
+
+	if (result != PASSDB_RESULT_OK && ctx.failure_msg != NULL) {
+		auth_request_set_field(request, "reason",
+				       ctx.failure_msg, NULL);
 	}
 	return result;
-}
-
-static void pam_child_input(struct pam_auth_request *request)
-{
-	struct auth_request *auth_request = request->request;
-	enum passdb_result result;
-	char buf[PIPE_BUF + 1];
-	ssize_t ret;
-
-	/* POSIX guarantees that writing PIPE_BUF bytes or less to pipes is
-	   atomic. We rely on that. */
-	ret = read(request->fd, buf, sizeof(buf)-1);


More information about the dovecot-cvs mailing list