dovecot-2.2: auth: Added passdb { result_* and skip } settings.

dovecot at dovecot.org dovecot at dovecot.org
Wed Jan 30 22:45:01 EET 2013


details:   http://hg.dovecot.org/dovecot-2.2/rev/d60aa734c72d
changeset: 15691:d60aa734c72d
user:      Timo Sirainen <tss at iki.fi>
date:      Wed Jan 30 22:44:48 2013 +0200
description:
auth: Added passdb { result_* and skip } settings.

passdb { skip = never | authenticated | unauthenticated } can be used to
skip over a passdb lookup based on previous passdb lookups.

passdb { result_success, result_failure, result_internalfail } can be used
to specify what to do on those conditions. Choices as continue,
continue-ok, continue-fail, return, return-ok, return-fail. The -ok and
-fail variants update the current "success" flag, while continue/return uses
the current flag. The authentication succeeds only if the success flag is
set after the last lookup. The continue variants continue to the next
passdb, while return variants finish the lookup immediately.

diffstat:

 src/auth/auth-fields.c            |   62 +++++++++++-
 src/auth/auth-fields.h            |   13 ++-
 src/auth/auth-master-connection.c |    5 +-
 src/auth/auth-request-handler.c   |    6 +-
 src/auth/auth-request.c           |  181 +++++++++++++++++++++++++------------
 src/auth/auth-request.h           |    5 +
 src/auth/auth-settings.c          |   12 ++
 src/auth/auth-settings.h          |    4 +
 src/auth/auth-worker-client.c     |    6 +-
 src/auth/auth.c                   |   39 ++++++++
 src/auth/auth.h                   |   20 ++++
 src/auth/mech-gssapi.c            |    1 +
 src/auth/passdb-blocking.c        |   11 +--
 13 files changed, 281 insertions(+), 84 deletions(-)

diffs (truncated from 711 to 300 lines):

diff -r 9e93a5743c95 -r d60aa734c72d src/auth/auth-fields.c
--- a/src/auth/auth-fields.c	Wed Jan 30 22:23:09 2013 +0200
+++ b/src/auth/auth-fields.c	Wed Jan 30 22:44:48 2013 +0200
@@ -10,7 +10,9 @@
 
 struct auth_fields {
 	pool_t pool;
-	ARRAY_TYPE(auth_field) fields;
+	ARRAY_TYPE(auth_field) fields, snapshot_fields;
+	unsigned int snapshot_idx;
+	bool snapshotted;
 };
 
 struct auth_fields *auth_fields_init(pool_t pool)
@@ -22,6 +24,16 @@
 	return fields;
 }
 
+static void auth_fields_snapshot_preserve(struct auth_fields *fields)
+{
+	if (!fields->snapshotted || array_is_created(&fields->snapshot_fields))
+		return;
+
+	p_array_init(&fields->snapshot_fields, fields->pool,
+		     array_count(&fields->fields));
+	array_append_array(&fields->snapshot_fields, &fields->fields);
+}
+
 static bool
 auth_fields_find_idx(struct auth_fields *fields, const char *key,
 		     unsigned int *idx_r)
@@ -60,18 +72,21 @@
 		field = array_append_space(&fields->fields);
 		field->key = p_strdup(fields->pool, key);
 	} else {
+		auth_fields_snapshot_preserve(fields);
 		field = array_idx_modifiable(&fields->fields, idx);
 	}
 	field->value = p_strdup_empty(fields->pool, value);
-	field->flags = flags;
+	field->flags = flags | AUTH_FIELD_FLAG_CHANGED;
 }
 
 void auth_fields_remove(struct auth_fields *fields, const char *key)
 {
 	unsigned int idx;
 
-	if (auth_fields_find_idx(fields, key, &idx))
+	if (auth_fields_find_idx(fields, key, &idx)) {
+		auth_fields_snapshot_preserve(fields);
 		array_delete(&fields->fields, idx, 1);
+	}
 }
 
 const char *auth_fields_find(struct auth_fields *fields, const char *key)
@@ -93,8 +108,10 @@
 
 void auth_fields_reset(struct auth_fields *fields)
 {
-	if (array_is_created(&fields->fields))
+	if (array_is_created(&fields->fields)) {
+		auth_fields_snapshot_preserve(fields);
 		array_clear(&fields->fields);
+	}
 }
 
 void auth_fields_import(struct auth_fields *fields, const char *str,
@@ -125,7 +142,8 @@
 }
 
 void auth_fields_append(struct auth_fields *fields, string_t *dest,
-			bool include_hidden)
+			enum auth_field_flags flags_mask,
+			enum auth_field_flags flags_result)
 {
 	const struct auth_field *f;
 	unsigned int i, count;
@@ -136,8 +154,7 @@
 
 	f = array_get(&fields->fields, &count);
 	for (i = 0; i < count; i++) {
-		if (!include_hidden &&
-		    (f[i].flags & AUTH_FIELD_FLAG_HIDDEN) != 0)
+		if ((f[i].flags & flags_mask) != flags_result)
 			continue;
 
 		if (first)
@@ -157,3 +174,34 @@
 	return fields == NULL || !array_is_created(&fields->fields) ||
 		array_count(&fields->fields) == 0;
 }
+
+void auth_fields_snapshot(struct auth_fields *fields)
+{
+	struct auth_field *field;
+
+	fields->snapshotted = TRUE;
+	if (!array_is_created(&fields->fields))
+		return;
+
+	if (!array_is_created(&fields->snapshot_fields)) {
+		/* try to avoid creating this array */
+		fields->snapshot_idx = array_count(&fields->fields);
+	} else {
+		array_clear(&fields->snapshot_fields);
+		array_append_array(&fields->snapshot_fields, &fields->fields);
+	}
+	array_foreach_modifiable(&fields->fields, field)
+		field->flags &= ~AUTH_FIELD_FLAG_CHANGED;
+}
+
+void auth_fields_rollback(struct auth_fields *fields)
+{
+	if (array_is_created(&fields->snapshot_fields)) {
+		array_clear(&fields->fields);
+		array_append_array(&fields->fields, &fields->snapshot_fields);
+	} else if (array_is_created(&fields->fields)) {
+		array_delete(&fields->fields, fields->snapshot_idx,
+			     array_count(&fields->fields) -
+			     fields->snapshot_idx);
+	}
+}
diff -r 9e93a5743c95 -r d60aa734c72d src/auth/auth-fields.h
--- a/src/auth/auth-fields.h	Wed Jan 30 22:23:09 2013 +0200
+++ b/src/auth/auth-fields.h	Wed Jan 30 22:44:48 2013 +0200
@@ -5,7 +5,9 @@
 
 enum auth_field_flags {
 	/* This field is internal to auth process and won't be sent to client */
-	AUTH_FIELD_FLAG_HIDDEN	= 0x01
+	AUTH_FIELD_FLAG_HIDDEN	= 0x01,
+	/* Changed since last snapshot. Set/cleared automatically. */
+	AUTH_FIELD_FLAG_CHANGED	= 0x02
 };
 
 struct auth_field {
@@ -27,8 +29,15 @@
 void auth_fields_import(struct auth_fields *fields, const char *str,
 			enum auth_field_flags flags);
 const ARRAY_TYPE(auth_field) *auth_fields_export(struct auth_fields *fields);
+/* Append fields where (flag & flags_mask) == flags_result. */
 void auth_fields_append(struct auth_fields *fields, string_t *dest,
-			bool include_hidden);
+			enum auth_field_flags flags_mask,
+			enum auth_field_flags flags_result);
 bool auth_fields_is_empty(struct auth_fields *fields);
 
+/* Remember the current fields. */
+void auth_fields_snapshot(struct auth_fields *fields);
+/* Rollback to previous snapshot, or clear the fields if there isn't any. */
+void auth_fields_rollback(struct auth_fields *fields);
+
 #endif
diff -r 9e93a5743c95 -r d60aa734c72d src/auth/auth-master-connection.c
--- a/src/auth/auth-master-connection.c	Wed Jan 30 22:23:09 2013 +0200
+++ b/src/auth/auth-master-connection.c	Wed Jan 30 22:44:48 2013 +0200
@@ -275,7 +275,8 @@
 		str_printfa(str, "USER\t%u\t", auth_request->id);
 		str_append_tabescaped(str, auth_request->user);
 		str_append_c(str, '\t');
-		auth_fields_append(auth_request->userdb_reply, str, FALSE);
+		auth_fields_append(auth_request->userdb_reply, str,
+				   AUTH_FIELD_FLAG_HIDDEN, 0);
 		break;
 	}
 
@@ -326,7 +327,7 @@
 		if (!auth_fields_is_empty(auth_request->extra_fields)) {
 			str_append_c(str, '\t');
 			auth_fields_append(auth_request->extra_fields,
-					   str, FALSE);
+					   str, AUTH_FIELD_FLAG_HIDDEN, 0);
 		}
 		break;
 	case PASSDB_RESULT_USER_UNKNOWN:
diff -r 9e93a5743c95 -r d60aa734c72d src/auth/auth-request-handler.c
--- a/src/auth/auth-request-handler.c	Wed Jan 30 22:23:09 2013 +0200
+++ b/src/auth/auth-request-handler.c	Wed Jan 30 22:44:48 2013 +0200
@@ -170,7 +170,8 @@
 		return;
 
 	str_append_c(dest, '\t');
-	auth_fields_append(request->extra_fields, dest, FALSE);
+	auth_fields_append(request->extra_fields, dest,
+			   AUTH_FIELD_FLAG_HIDDEN, 0);
 
 	if (!request->auth_only &&
 	    auth_fields_exists(request->extra_fields, "proxy")) {
@@ -641,7 +642,8 @@
 		str_printfa(str, "USER\t%u\t", request->id);
 		str_append_tabescaped(str, request->user);
 		str_append_c(str, '\t');
-		auth_fields_append(request->userdb_reply, str, FALSE);
+		auth_fields_append(request->userdb_reply, str,
+				   AUTH_FIELD_FLAG_HIDDEN, 0);
 
 		if (request->master_user != NULL &&
 		    !auth_fields_exists(request->userdb_reply, "master_user")) {
diff -r 9e93a5743c95 -r d60aa734c72d src/auth/auth-request.c
--- a/src/auth/auth-request.c	Wed Jan 30 22:23:09 2013 +0200
+++ b/src/auth/auth-request.c	Wed Jan 30 22:44:48 2013 +0200
@@ -81,6 +81,7 @@
 	request->last_access = ioloop_time;
 	request->session_pid = (pid_t)-1;
 	request->set = global_auth_settings;
+	request->extra_fields = auth_fields_init(request->pool);
 	return request;
 }
 
@@ -118,7 +119,7 @@
 {
 	i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE);
 
-	if (request->failed) {
+	if (request->failed || !request->passdb_success) {
 		/* password was valid, but some other check failed. */
 		auth_request_fail(request);
 		return;
@@ -193,8 +194,10 @@
 {
 	str_append_c(dest, '\t');
 	str_append(dest, key);
-	str_append_c(dest, '=');
-	str_append_tabescaped(dest, value);
+	if (value != NULL) {
+		str_append_c(dest, '=');
+		str_append_tabescaped(dest, value);
+	}
 }
 
 void auth_request_export(struct auth_request *request, string_t *dest)
@@ -321,10 +324,9 @@
 		request->requested_login_user = p_strdup(request->pool, value);
 	else if (strcmp(key, "successful") == 0)
 		request->successful = TRUE;
-	else if (strcmp(key, "skip-password-check") == 0) {
-		i_assert(request->master_user !=  NULL);
+	else if (strcmp(key, "skip-password-check") == 0)
 		request->skip_password_check = TRUE;
-	} else if (strcmp(key, "mech") == 0)
+	else if (strcmp(key, "mech") == 0)
 		request->mech_name = p_strdup(request->pool, value);
 	else
 		return FALSE;
@@ -429,18 +431,22 @@
 
 	if (!auth_fields_is_empty(request->extra_fields)) {
 		str_append_c(str, '\t');
-		auth_fields_append(request->extra_fields, str, TRUE);
+		/* add only those extra fields to cache that were set by this
+		   passdb lookup. the CHANGED flag does this, because we
+		   snapshotted the extra_fields before the current passdb
+		   lookup. */
+		auth_fields_append(request->extra_fields, str,
+				   AUTH_FIELD_FLAG_CHANGED,
+				   AUTH_FIELD_FLAG_CHANGED);
 	}
 	auth_cache_insert(passdb_cache, request, passdb->cache_key, str_c(str),
 			  result == PASSDB_RESULT_OK);
 }
 
-static bool auth_request_master_lookup_finish(struct auth_request *request)
+static void auth_request_master_lookup_finish(struct auth_request *request)
 {
-	struct auth_passdb *passdb;
-
 	if (request->failed)
-		return TRUE;
+		return;
 
 	/* master login successful. update user and master_user variables. */
 	auth_request_log_info(request, "passdb", "Master user logging in as %s",
@@ -449,35 +455,35 @@
 	request->master_user = request->user;
 	request->user = request->requested_login_user;
 	request->requested_login_user = NULL;
+}
 
-	request->skip_password_check = TRUE;
-	request->passdb_password = NULL;
+static bool
+auth_request_want_skip_passdb(struct auth_request *request,
+			      struct auth_passdb *passdb)
+{
+	/* skip_password_check basically specifies if authentication is
+	   finished */
+	bool authenticated = request->skip_password_check;
 
-	if (!request->passdb->set->pass) {
-		/* skip the passdb lookup, we're authenticated now. */
-		return TRUE;
+	switch (passdb->skip) {
+	case AUTH_PASSDB_SKIP_NEVER:
+		return FALSE;
+	case AUTH_PASSDB_SKIP_AUTHENTICATED:
+		return authenticated;
+	case AUTH_PASSDB_SKIP_UNAUTHENTICATED:
+		return !authenticated;
 	}
-


More information about the dovecot-cvs mailing list