changeset 5872:93bd157917ca HEAD

Changed userdb callback API. Don't require uid/gid to be returned by userdb.
author Timo Sirainen <tss@iki.fi>
date Tue, 03 Jul 2007 03:20:06 +0300
parents 4cf56bf92215
children 592a69a21b53
files src/auth/auth-master-connection.c src/auth/auth-request-handler.c src/auth/auth-request.c src/auth/auth-request.h src/auth/auth-worker-client.c src/auth/db-ldap.c src/auth/db-ldap.h src/auth/passdb-ldap.c src/auth/userdb-blocking.c src/auth/userdb-ldap.c src/auth/userdb-nss.c src/auth/userdb-passwd-file.c src/auth/userdb-passwd.c src/auth/userdb-prefetch.c src/auth/userdb-sql.c src/auth/userdb-static.c src/auth/userdb-vpopmail.c src/auth/userdb.h
diffstat 18 files changed, 198 insertions(+), 323 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth/auth-master-connection.c	Tue Jul 03 01:59:06 2007 +0300
+++ b/src/auth/auth-master-connection.c	Tue Jul 03 03:20:06 2007 +0300
@@ -78,12 +78,15 @@
 
 static void
 user_callback(enum userdb_result result,
-	      struct auth_stream_reply *reply,
 	      struct auth_request *auth_request)
 {
 	struct auth_master_connection *conn = auth_request->context;
+	struct auth_stream_reply *reply = auth_request->userdb_reply;
 	string_t *str;
 
+	if (auth_request->userdb_lookup_failed)
+		result = USERDB_RESULT_INTERNAL_FAILURE;
+
 	str = t_str_new(128);
 	switch (result) {
 	case USERDB_RESULT_INTERNAL_FAILURE:
--- a/src/auth/auth-request-handler.c	Tue Jul 03 01:59:06 2007 +0300
+++ b/src/auth/auth-request-handler.c	Tue Jul 03 03:20:06 2007 +0300
@@ -399,16 +399,19 @@
 }
 
 static void userdb_callback(enum userdb_result result,
-			    struct auth_stream_reply *reply,
 			    struct auth_request *request)
 {
         struct auth_request_handler *handler = request->context;
+	struct auth_stream_reply *reply = request->userdb_reply;
 	string_t *str;
 
 	i_assert(request->state == AUTH_REQUEST_STATE_USERDB);
 
 	request->state = AUTH_REQUEST_STATE_FINISHED;
 
+	if (request->userdb_lookup_failed)
+		result = USERDB_RESULT_INTERNAL_FAILURE;
+
 	str = t_str_new(256);
 	switch (result) {
 	case USERDB_RESULT_INTERNAL_FAILURE:
--- a/src/auth/auth-request.c	Tue Jul 03 01:59:06 2007 +0300
+++ b/src/auth/auth-request.c	Tue Jul 03 03:20:06 2007 +0300
@@ -580,7 +580,6 @@
 }
 
 static void auth_request_userdb_save_cache(struct auth_request *request,
-					   struct auth_stream_reply *reply,
 					   enum userdb_result result)
 {
 	struct userdb_module *userdb = request->userdb->userdb;
@@ -590,7 +589,7 @@
 		return;
 
 	str = result == USERDB_RESULT_USER_UNKNOWN ? "" :
-		auth_stream_reply_export(reply);
+		auth_stream_reply_export(request->userdb_reply);
 	/* last_success has no meaning with userdb */
 	auth_cache_insert(passdb_cache, request, userdb->cache_key, str, FALSE);
 }
@@ -624,7 +623,6 @@
 }
 
 void auth_request_userdb_callback(enum userdb_result result,
-				  struct auth_stream_reply *reply,
 				  struct auth_request *request)
 {
 	struct userdb_module *userdb = request->userdb->userdb;
@@ -653,20 +651,23 @@
 	}
 
 	if (result != USERDB_RESULT_INTERNAL_FAILURE)
-		auth_request_userdb_save_cache(request, reply, result);
+		auth_request_userdb_save_cache(request, result);
 	else if (passdb_cache != NULL && userdb->cache_key != NULL) {
 		/* lookup failed. if we're looking here only because the
 		   request was expired in cache, fallback to using cached
 		   expired record. */
 		const char *cache_key = userdb->cache_key;
+		struct auth_stream_reply *reply;
 
 		if (auth_request_lookup_user_cache(request, cache_key, &reply,
-						   &result, TRUE))
+						   &result, TRUE)) {
+			request->userdb_reply = reply;
 			auth_request_log_info(request, "userdb",
 				"Fallbacking to expired data from cache");
+		}
 	}
 
-        request->private_callback.userdb(result, reply, request);
+        request->private_callback.userdb(result, request);
 }
 
 void auth_request_lookup_user(struct auth_request *request,
@@ -686,8 +687,8 @@
 
 		if (auth_request_lookup_user_cache(request, cache_key, &reply,
 						   &result, FALSE)) {
-			request->private_callback.userdb(result, reply,
-							 request);
+			request->userdb_reply = reply;
+			request->private_callback.userdb(result, request);
 			return;
 		}
 	}
@@ -765,7 +766,8 @@
 		return TRUE;
 	}
 
-	if (request->auth->master_user_separator != '\0') {
+	if (request->auth->master_user_separator != '\0' &&
+	    !request->userdb_lookup) {
 		/* check if the username contains a master user */
 		p = strchr(username, request->auth->master_user_separator);
 		if (p != NULL) {
@@ -977,6 +979,11 @@
 		request->passdb_password = NULL;
 	} else if (strcmp(name, "allow_nets") == 0) {
 		auth_request_validate_networks(request, value);
+	} else if (strncmp(name, "userdb_", 7) == 0) {
+		/* for prefetch userdb */
+		if (request->userdb_reply == NULL)
+			auth_request_init_userdb_reply(request);
+		auth_request_set_userdb_field(request, name + 7, value);
 	} else {
 		if (strcmp(name, "nologin") == 0) {
 			/* user can't actually login - don't keep this
@@ -1032,6 +1039,95 @@
 	t_pop();
 }
 
+void auth_request_init_userdb_reply(struct auth_request *request)
+{
+	request->userdb_reply = auth_stream_reply_init(request);
+	auth_stream_reply_add(request->userdb_reply, NULL, request->user);
+}
+
+void auth_request_set_userdb_field(struct auth_request *request,
+				   const char *name, const char *value)
+{
+	const char *str;
+	uid_t uid;
+	gid_t gid;
+
+	if (strcmp(name, "uid") == 0) {
+		uid = userdb_parse_uid(request, value);
+		if (uid == (uid_t)-1) {
+			request->userdb_lookup_failed = TRUE;
+			return;
+		}
+		value = dec2str(uid);
+	} else if (strcmp(name, "gid") == 0) {
+		gid = userdb_parse_gid(request, value);
+		if (gid == (gid_t)-1) {
+			request->userdb_lookup_failed = TRUE;
+			return;
+		}
+		value = dec2str(gid);
+	} else if (strcmp(name, "user") == 0) {
+		/* replace the username if it changed */
+		if (strcmp(value, request->user) == 0)
+			return;
+
+		t_push();
+		str = t_strdup(auth_stream_reply_export(request->userdb_reply));
+
+		/* reset the reply and add the new username */
+		auth_request_set_field(request, "user", value, NULL);
+		auth_stream_reply_reset(request->userdb_reply);
+		auth_stream_reply_add(request->userdb_reply,
+				      NULL, request->user);
+
+		/* add the rest */
+		str = strchr(str, '\t');
+		i_assert(str != NULL);
+		auth_stream_reply_import(request->userdb_reply, str + 1);
+		t_pop();
+	}
+
+	auth_stream_reply_add(request->userdb_reply, name, value);
+}
+
+void auth_request_set_userdb_field_values(struct auth_request *request,
+					  const char *name,
+					  const char *const *values)
+{
+	if (*values == NULL)
+		return;
+
+	if (strcmp(name, "uid") == 0) {
+		/* there can be only one. use the first one. */
+		auth_request_set_userdb_field(request, name, *values);
+	} else if (strcmp(name, "gid") == 0) {
+		/* convert gids to comma separated list */
+		string_t *value;
+		gid_t gid;
+
+		t_push();
+		value = t_str_new(128);
+		for (; *values != NULL; values++) {
+			gid = userdb_parse_gid(request, *values);
+			if (gid == (gid_t)-1) {
+				request->userdb_lookup_failed = TRUE;
+				t_pop();
+				return;
+			}
+
+			if (str_len(value) > 0)
+				str_append_c(value, ',');
+			str_append(value, dec2str(gid));
+		}
+		auth_stream_reply_add(request->userdb_reply, name,
+				      str_c(value));
+		t_pop();
+	} else {
+		/* add only one */
+		auth_request_set_userdb_field(request, name, *values);
+	}
+}
+
 int auth_request_password_verify(struct auth_request *request,
 				 const char *plain_password,
 				 const char *crypted_password,
--- a/src/auth/auth-request.h	Tue Jul 03 01:59:06 2007 +0300
+++ b/src/auth/auth-request.h	Tue Jul 03 03:20:06 2007 +0300
@@ -45,6 +45,8 @@
 	/* extra_fields that aren't supposed to be sent to the client, but
 	   are supposed to be stored to auth cache. */
 	struct auth_stream_reply *extra_cache_fields;
+	/* the whole userdb result reply */
+	struct auth_stream_reply *userdb_reply;
 
 	const struct mech_module *mech;
 	struct auth *auth;
@@ -85,6 +87,7 @@
 	unsigned int proxy:1;
 	unsigned int cert_username:1;
 	unsigned int userdb_lookup:1;
+	unsigned int userdb_lookup_failed:1;
 	unsigned int secured:1;
 
 	/* ... mechanism specific data ... */
@@ -133,6 +136,13 @@
 			     const char *const *fields,
 			     const char *default_scheme);
 
+void auth_request_init_userdb_reply(struct auth_request *request);
+void auth_request_set_userdb_field(struct auth_request *request,
+				   const char *name, const char *value);
+void auth_request_set_userdb_field_values(struct auth_request *request,
+					  const char *name,
+					  const char *const *values);
+
 int auth_request_password_verify(struct auth_request *request,
 				 const char *plain_password,
 				 const char *crypted_password,
@@ -164,7 +174,6 @@
 				  const char *scheme, const char *data,
 				  set_credentials_callback_t *callback);
 void auth_request_userdb_callback(enum userdb_result result,
-				  struct auth_stream_reply *reply,
 				  struct auth_request *request);
 
 #endif
--- a/src/auth/auth-worker-client.c	Tue Jul 03 01:59:06 2007 +0300
+++ b/src/auth/auth-worker-client.c	Tue Jul 03 03:20:06 2007 +0300
@@ -309,12 +309,15 @@
 
 static void
 lookup_user_callback(enum userdb_result result,
-		     struct auth_stream_reply *reply,
 		     struct auth_request *auth_request)
 {
 	struct auth_worker_client *client = auth_request->context;
+	struct auth_stream_reply *reply = auth_request->userdb_reply;
 	string_t *str;
 
+	if (auth_request->userdb_lookup_failed)
+		result = USERDB_RESULT_INTERNAL_FAILURE;
+
 	str = t_str_new(128);
 	str_printfa(str, "%u\t", auth_request->id);
 	switch (result) {
--- a/src/auth/db-ldap.c	Tue Jul 03 01:59:06 2007 +0300
+++ b/src/auth/db-ldap.c	Tue Jul 03 03:20:06 2007 +0300
@@ -66,8 +66,6 @@
 	DEF_STR(pass_attrs),
 	DEF_STR(pass_filter),
 	DEF_STR(default_pass_scheme),
-	DEF_STR(user_global_uid),
-	DEF_STR(user_global_gid),
 
 	{ 0, NULL, 0 }
 };
@@ -92,9 +90,7 @@
 	MEMBER(user_filter) "(&(objectClass=posixAccount)(uid=%u))",
 	MEMBER(pass_attrs) "uid,userPassword",
 	MEMBER(pass_filter) "(&(objectClass=posixAccount)(uid=%u))",
-	MEMBER(default_pass_scheme) "crypt",
-	MEMBER(user_global_uid) "",
-	MEMBER(user_global_gid) ""
+	MEMBER(default_pass_scheme) "crypt"
 };
 
 static struct ldap_connection *ldap_connections = NULL;
@@ -737,28 +733,6 @@
         conn->set.ldap_deref = deref2str(conn->set.deref);
 	conn->set.ldap_scope = scope2str(conn->set.scope);
 
-	if (*conn->set.user_global_uid == '\0')
-		conn->set.uid = (uid_t)-1;
-	else {
-		conn->set.uid =
-			userdb_parse_uid(NULL, conn->set.user_global_uid);
-		if (conn->set.uid == (uid_t)-1) {
-			i_fatal("LDAP: Invalid user_global_uid: %s",
-				conn->set.user_global_uid);
-		}
-	}
-
-	if (*conn->set.user_global_gid == '\0')
-		conn->set.gid = (gid_t)-1;
-	else {
-		conn->set.gid =
-			userdb_parse_gid(NULL, conn->set.user_global_gid);
-		if (conn->set.gid == (gid_t)-1) {
-			i_fatal("LDAP: Invalid user_global_gid: %s",
-				conn->set.user_global_gid);
-		}
-	}
-
 	conn->next = ldap_connections;
         ldap_connections = conn;
 	return conn;
--- a/src/auth/db-ldap.h	Tue Jul 03 01:59:06 2007 +0300
+++ b/src/auth/db-ldap.h	Tue Jul 03 03:20:06 2007 +0300
@@ -40,8 +40,6 @@
 	const char *pass_filter;
 
 	const char *default_pass_scheme;
-	const char *user_global_uid;
-	const char *user_global_gid;
 
 	/* ... */
 	int ldap_deref, ldap_scope;
--- a/src/auth/passdb-ldap.c	Tue Jul 03 01:59:06 2007 +0300
+++ b/src/auth/passdb-ldap.c	Tue Jul 03 03:20:06 2007 +0300
@@ -40,9 +40,6 @@
 	LDAPMessage *entry;
 
 	string_t *debug;
-	unsigned int userdb_fields:1;
-	unsigned int add_userdb_uid:1;
-	unsigned int add_userdb_gid:1;
 };
 
 static void
@@ -68,19 +65,6 @@
 	if (name == NULL)
 		return;
 
-	if (strncmp(name, "userdb_", 7) == 0) {
-		/* in case we're trying to use prefetch userdb,
-		   see if we need to add global uid/gid */
-		if (!ctx->userdb_fields) {
-			ctx->add_userdb_uid = ctx->add_userdb_gid = TRUE;
-			ctx->userdb_fields = TRUE;
-		}
-		if (strcmp(name, "userdb_uid") == 0)
-			ctx->add_userdb_uid = FALSE;
-		else if (strcmp(name, "userdb_gid") == 0)
-			ctx->add_userdb_gid = FALSE;
-	}
-
 	vals = ldap_get_values(ctx->conn->ld, ctx->entry, attr);
 	if (vals != NULL && *name != '\0') {
 		for (i = 0; vals[i] != NULL; i++) {
@@ -125,15 +109,6 @@
 	}
 	ber_free(ber, 0);
 
-	if (ctx.add_userdb_uid && conn->set.uid != (uid_t)-1) {
-		auth_request_set_field(auth_request, "userdb_uid",
-				       dec2str(conn->set.uid), NULL);
-	}
-	if (ctx.add_userdb_gid && conn->set.gid != (gid_t)-1) {
-		auth_request_set_field(auth_request, "userdb_gid",
-				       dec2str(conn->set.gid), NULL);
-	}
-
 	if (ctx.debug != NULL) {
 		auth_request_log_debug(auth_request, "ldap",
 				       "result: %s", str_c(ctx.debug));
--- a/src/auth/userdb-blocking.c	Tue Jul 03 01:59:06 2007 +0300
+++ b/src/auth/userdb-blocking.c	Tue Jul 03 03:20:06 2007 +0300
@@ -10,7 +10,6 @@
 
 static void user_callback(struct auth_request *request, const char *reply)
 {
-	struct auth_stream_reply *stream_reply = NULL;
 	enum userdb_result result;
 
 	if (strncmp(reply, "FAIL\t", 5) == 0)
@@ -19,14 +18,14 @@
 		result = USERDB_RESULT_USER_UNKNOWN;
 	else if (strncmp(reply, "OK\t", 3) == 0) {
 		result = USERDB_RESULT_OK;
-		stream_reply = auth_stream_reply_init(request);
-		auth_stream_reply_import(stream_reply, reply + 3);
+		request->userdb_reply = auth_stream_reply_init(request);
+		auth_stream_reply_import(request->userdb_reply, reply + 3);
 	} else {
 		result = USERDB_RESULT_INTERNAL_FAILURE;
 		i_error("BUG: auth-worker sent invalid user reply");
 	}
 
-        auth_request_userdb_callback(result, stream_reply, request);
+        auth_request_userdb_callback(result, request);
 }
 
 void userdb_blocking_lookup(struct auth_request *request)
--- a/src/auth/userdb-ldap.c	Tue Jul 03 01:59:06 2007 +0300
+++ b/src/auth/userdb-ldap.c	Tue Jul 03 03:20:06 2007 +0300
@@ -30,76 +30,24 @@
 	"", "home", "mail", "system_user", "uid", "gid", NULL
 };
 
-static bool append_uid_list(struct auth_request *auth_request,
-			    struct auth_stream_reply *reply,
-			    const char *name, char **vals)
-{
-	uid_t uid;
-
-	for (; *vals != NULL; vals++) {
-		uid = userdb_parse_uid(auth_request, *vals);
-		if (uid == (uid_t)-1)
-			return FALSE;
-
-		auth_stream_reply_add(reply, name, dec2str(uid));
-	}
-
-	return TRUE;
-}
-
-static bool append_gid_list(struct auth_request *auth_request,
-			    struct auth_stream_reply *reply,
-			    const char *name, char **vals)
-{
-	gid_t gid;
-
-	for (; *vals != NULL; vals++) {
-		gid = userdb_parse_gid(auth_request, *vals);
-		if (gid == (gid_t)-1)
-			return FALSE;
-
-		auth_stream_reply_add(reply, name, dec2str(gid));
-	}
-
-	return TRUE;
-}
-
-static struct auth_stream_reply *
+static void
 ldap_query_get_result(struct ldap_connection *conn, LDAPMessage *entry,
 		      struct auth_request *auth_request)
 {
-	struct auth_stream_reply *reply;
 	BerElement *ber;
 	const char *name;
 	char *attr, **vals;
-	unsigned int i;
-	bool seen_uid = FALSE, seen_gid = FALSE;
 
-	reply = auth_stream_reply_init(auth_request);
-	auth_stream_reply_add(reply, NULL, auth_request->user);
+	auth_request_init_userdb_reply(auth_request);
 
 	attr = ldap_first_attribute(conn->ld, entry, &ber);
 	while (attr != NULL) {
 		name = hash_lookup(conn->user_attr_map, attr);
 		vals = ldap_get_values(conn->ld, entry, attr);
 
-		if (name != NULL && vals != NULL && vals[0] != NULL) {
-			if (strcmp(name, "uid") == 0) {
-				if (!append_uid_list(auth_request, reply,
-						     name, vals))
-					return NULL;
-				seen_uid = TRUE;
-			} else if (strcmp(name, "gid") == 0) {
-				if (!append_gid_list(auth_request, reply,
-						     name, vals)) 
-					return NULL;
-				seen_gid = TRUE;
-			} else if (*name != '\0') {
-				for (i = 0; vals[i] != NULL; i++) {
-					auth_stream_reply_add(reply, name,
-							      vals[i]);
-				}
-			}
+		if (name != NULL && *name != '\0' && vals != NULL) {
+			auth_request_set_userdb_field_values(auth_request,
+					name, (const char *const *)vals);
 		}
 		ldap_value_free(vals);
 		ldap_memfree(attr);
@@ -107,29 +55,6 @@
 		attr = ldap_next_attribute(conn->ld, entry, ber);
 	}
 	ber_free(ber, 0);
-
-	if (!seen_uid) {
-		if (conn->set.uid == (uid_t)-1) {
-			auth_request_log_error(auth_request, "ldap",
-				"uid not in user_attrs and no default given in "
-				"user_global_uid");
-			return NULL;
-		}
-
-		auth_stream_reply_add(reply, "uid", dec2str(conn->set.uid));
-	}
-	if (!seen_gid) {
-		if (conn->set.gid == (gid_t)-1) {
-			auth_request_log_error(auth_request, "ldap",
-				"gid not in user_attrs and no default given in "
-				"user_global_gid");
-			return NULL;
-		}
-
-		auth_stream_reply_add(reply, "gid", dec2str(conn->set.gid));
-	}
-
-	return reply;
 }
 
 static void handle_request(struct ldap_connection *conn,
@@ -139,7 +64,6 @@
 		(struct userdb_ldap_request *) request;
 	struct auth_request *auth_request = urequest->auth_request;
 	LDAPMessage *entry;
-	struct auth_stream_reply *reply = NULL;
 	enum userdb_result result = USERDB_RESULT_INTERNAL_FAILURE;
 	int ret;
 
@@ -148,7 +72,7 @@
 		if (ret != LDAP_SUCCESS) {
 			auth_request_log_error(auth_request, "ldap",
 					       "ldap_search() failed: %s", ldap_err2string(ret));
-			urequest->userdb_callback(result, NULL, auth_request);
+			urequest->userdb_callback(result, auth_request);
 			return;
 		}
 	}
@@ -161,17 +85,16 @@
 					       "Unknown user");
 		}
 	} else {
-		reply = ldap_query_get_result(conn, entry, auth_request);
+		ldap_query_get_result(conn, entry, auth_request);
 		if (ldap_next_entry(conn->ld, entry) == NULL)
 			result = USERDB_RESULT_OK;
 		else {
 			auth_request_log_error(auth_request, "ldap",
 				"Multiple replies found for user");
-			reply = NULL;
 		}
 	}
 
-	urequest->userdb_callback(result, reply, auth_request);
+	urequest->userdb_callback(result, auth_request);
 	auth_request_unref(&auth_request);
 }
 
--- a/src/auth/userdb-nss.c	Tue Jul 03 01:59:06 2007 +0300
+++ b/src/auth/userdb-nss.c	Tue Jul 03 03:20:06 2007 +0300
@@ -33,7 +33,6 @@
 {
 	struct userdb_module *_module = auth_request->userdb->userdb;
 	struct nss_userdb_module *module = (struct nss_userdb_module *)_module;
-	struct auth_stream_reply *reply;
 	struct passwd pw;
 	enum nss_status status;
 	enum userdb_result result = USERDB_RESULT_INTERNAL_FAILURE;
@@ -66,18 +65,19 @@
 	}
 
 	if (result != USERDB_RESULT_OK) {
-		callback(result, NULL, auth_request);
+		callback(result, auth_request);
 		return;
 	}
 
-	reply = auth_stream_reply_init(auth_request);
-	auth_stream_reply_add(reply, NULL, pw.pw_name);
-	auth_stream_reply_add(reply, "system_user", pw.pw_name);
-	auth_stream_reply_add(reply, "uid", dec2str(pw.pw_uid));
-	auth_stream_reply_add(reply, "gid", dec2str(pw.pw_gid));
-	auth_stream_reply_add(reply, "home", pw.pw_dir);
+	auth_request_set_field(auth_request, "user", pw.pw_name, NULL);
 
-	callback(USERDB_RESULT_OK, reply, auth_request);
+	auth_request_init_userdb_reply(auth_request);
+	auth_request_set_userdb_field(auth_request, "system_user", pw.pw_name);
+	auth_request_set_userdb_field(auth_request, "uid", dec2str(pw.pw_uid));
+	auth_request_set_userdb_field(auth_request, "gid", dec2str(pw.pw_gid));
+	auth_request_set_userdb_field(auth_request, "home", pw.pw_dir);
+
+	callback(USERDB_RESULT_OK, auth_request);
 }
 
 static void
--- a/src/auth/userdb-passwd-file.c	Tue Jul 03 01:59:06 2007 +0300
+++ b/src/auth/userdb-passwd-file.c	Tue Jul 03 03:20:06 2007 +0300
@@ -25,7 +25,6 @@
 	struct userdb_module *_module = auth_request->userdb->userdb;
 	struct passwd_file_userdb_module *module =
 		(struct passwd_file_userdb_module *)_module;
-	struct auth_stream_reply *reply;
 	struct passwd_user *pu;
         const struct var_expand_table *table;
 	string_t *str;
@@ -34,17 +33,16 @@
 
 	pu = db_passwd_file_lookup(module->pwf, auth_request);
 	if (pu == NULL) {
-		callback(USERDB_RESULT_USER_UNKNOWN, NULL, auth_request);
+		callback(USERDB_RESULT_USER_UNKNOWN, auth_request);
 		return;
 	}
 
-	reply = auth_stream_reply_init(auth_request);
-	auth_stream_reply_add(reply, NULL, auth_request->user);
-	auth_stream_reply_add(reply, "uid", dec2str(pu->uid));
-	auth_stream_reply_add(reply, "gid", dec2str(pu->gid));
+	auth_request_init_userdb_reply(auth_request);
+	auth_request_set_userdb_field(auth_request, "uid", dec2str(pu->uid));
+	auth_request_set_userdb_field(auth_request, "gid", dec2str(pu->gid));
 
 	if (pu->home != NULL)
-		auth_stream_reply_add(reply, "home", pu->home);
+		auth_request_set_userdb_field(auth_request, "home", pu->home);
 
 	if (pu->extra_fields != NULL) {
 		t_push();
@@ -63,12 +61,12 @@
 				var_expand(str, value + 1, table);
 				value = str_c(str);
 			}
-			auth_stream_reply_add(reply, key, value);
+			auth_request_set_userdb_field(auth_request, key, value);
 		}
 		t_pop();
 	}
 
-	callback(USERDB_RESULT_OK, reply, auth_request);
+	callback(USERDB_RESULT_OK, auth_request);
 }
 
 static struct userdb_module *
--- a/src/auth/userdb-passwd.c	Tue Jul 03 01:59:06 2007 +0300
+++ b/src/auth/userdb-passwd.c	Tue Jul 03 03:20:06 2007 +0300
@@ -14,14 +14,13 @@
 			  userdb_callback_t *callback)
 {
 	struct passwd *pw;
-	struct auth_stream_reply *reply;
 
 	auth_request_log_debug(auth_request, "passwd", "lookup");
 
 	pw = getpwnam(auth_request->user);
 	if (pw == NULL) {
 		auth_request_log_info(auth_request, "passwd", "unknown user");
-		callback(USERDB_RESULT_USER_UNKNOWN, NULL, auth_request);
+		callback(USERDB_RESULT_USER_UNKNOWN, auth_request);
 		return;
 	}
 
@@ -33,14 +32,15 @@
 			pw->pw_name, auth_request->user);
 	}
 
-	reply = auth_stream_reply_init(auth_request);
-	auth_stream_reply_add(reply, NULL, pw->pw_name);
-	auth_stream_reply_add(reply, "system_user", pw->pw_name);
-	auth_stream_reply_add(reply, "uid", dec2str(pw->pw_uid));
-	auth_stream_reply_add(reply, "gid", dec2str(pw->pw_gid));
-	auth_stream_reply_add(reply, "home", pw->pw_dir);
+	auth_request_set_field(auth_request, "user", pw->pw_name, NULL);
 
-	callback(USERDB_RESULT_OK, reply, auth_request);
+	auth_request_init_userdb_reply(auth_request);
+	auth_request_set_userdb_field(auth_request, "system_user", pw->pw_name);
+	auth_request_set_userdb_field(auth_request, "uid", dec2str(pw->pw_uid));
+	auth_request_set_userdb_field(auth_request, "gid", dec2str(pw->pw_gid));
+	auth_request_set_userdb_field(auth_request, "home", pw->pw_dir);
+
+	callback(USERDB_RESULT_OK, auth_request);
 }
 
 static void passwd_passwd_init(struct userdb_module *module,
--- a/src/auth/userdb-prefetch.c	Tue Jul 03 01:59:06 2007 +0300
+++ b/src/auth/userdb-prefetch.c	Tue Jul 03 03:20:06 2007 +0300
@@ -13,13 +13,9 @@
 static void prefetch_lookup(struct auth_request *auth_request,
 			    userdb_callback_t *callback)
 {
-	const char *const *args;
-	string_t *str;
-	uid_t uid;
-	gid_t gid;
-	bool uid_seen, gid_seen;
-
-	if (auth_stream_is_empty(auth_request->extra_fields)) {
+	/* auth_request_set_field() should have already placed the userdb_*
+	   values to userdb_reply. */
+	if (auth_request->userdb_reply == NULL) {
 		if (auth_request->auth->userdbs->next == NULL) {
 			/* no other userdbs */
 			auth_request_log_error(auth_request, "prefetch",
@@ -29,74 +25,12 @@
 			auth_request_log_info(auth_request, "prefetch",
 				"passdb didn't return userdb entries");
 		}
-		callback(USERDB_RESULT_USER_UNKNOWN, NULL, auth_request);
+		callback(USERDB_RESULT_USER_UNKNOWN, auth_request);
 		return;
 	}
 
-	t_push();
-
-	uid = (uid_t)-1; gid = (gid_t)-1;
-	uid_seen = gid_seen = FALSE;
-
-	str = t_str_new(256);
-	str_append(str, auth_request->user);
-
-	/* export the request. keep all keys starting with userdb_ but strip
-	   the userdb_ away. */
-	args = t_strsplit(auth_stream_reply_export(auth_request->extra_fields),
-		"\t");
-	for (; *args != NULL; args++) {
-		const char *arg = *args;
-
-		if (strncmp(arg, "userdb_", 7) != 0)
-			continue;
-		arg += 7;
-
-		str_append_c(str, '\t');
-		if (strncmp(arg, "uid=", 4) == 0) {
-			uid_seen = TRUE;
-			uid = userdb_parse_uid(auth_request, arg+4);
-			if (uid == (uid_t)-1)
-				break;
-
-			str_append(str, "uid=");
-			str_append(str, dec2str(uid));
-		} else if (strncmp(arg, "gid=", 4) == 0) {
-			gid_seen = TRUE;
-			gid = userdb_parse_gid(auth_request, arg+4);
-			if (gid == (gid_t)-1)
-				break;
-
-			str_append(str, "gid=");
-			str_append(str, dec2str(gid));
-		} else {
-			str_append(str, arg);
-		}
-	}
-
-	if (!uid_seen) {
-		auth_request_log_error(auth_request, "prefetch",
-				       "userdb_uid not returned");
-	}
-	if (!gid_seen) {
-		auth_request_log_error(auth_request, "prefetch",
-				       "userdb_gid not returned");
-	}
-
-	if (uid == (uid_t)-1 || gid == (gid_t)-1)
-		callback(USERDB_RESULT_USER_UNKNOWN, NULL, auth_request);
-	else {
-		struct auth_stream_reply *reply;
-
-		auth_request_log_debug(auth_request, "prefetch", "success");
-
-		/* import the string into request. since the values were
-		   exported they are already in escaped form in the string. */
-		reply = auth_stream_reply_init(auth_request);
-		auth_stream_reply_import(reply, str_c(str));
-		callback(USERDB_RESULT_OK, reply, auth_request);
-	}
-	t_pop();
+	auth_request_log_debug(auth_request, "prefetch", "success");
+	callback(USERDB_RESULT_OK, auth_request);
 }
 
 struct userdb_module_interface userdb_prefetch = {
--- a/src/auth/userdb-sql.c	Tue Jul 03 01:59:06 2007 +0300
+++ b/src/auth/userdb-sql.c	Tue Jul 03 03:20:06 2007 +0300
@@ -25,65 +25,31 @@
 	userdb_callback_t *callback;
 };
 
-static struct auth_stream_reply *
+static void
 sql_query_get_result(struct sql_result *result,
 		     struct auth_request *auth_request)
 {
-	struct auth_stream_reply *reply;
-	uid_t uid, gid;
 	const char *name, *value;
 	unsigned int i, fields_count;
 
-	uid = (uid_t)-1;
-	gid = (gid_t)-1;
-
-	reply = auth_stream_reply_init(auth_request);
-	auth_stream_reply_add(reply, NULL, auth_request->user);
+	auth_request_init_userdb_reply(auth_request);
 
 	fields_count = sql_result_get_fields_count(result);
 	for (i = 0; i < fields_count; i++) {
 		name = sql_result_get_field_name(result, i);
 		value = sql_result_get_field_value(result, i);
 
-		if (value == NULL)
-			continue;
-
-		/* some special handling for UID and GID. */
-		if (strcmp(name, "uid") == 0) {
-			uid = userdb_parse_uid(auth_request, value);
-			if (uid == (uid_t)-1)
-				return NULL;
-			value = dec2str(uid);
-		} else if (strcmp(name, "gid") == 0) {
-			gid = userdb_parse_gid(auth_request, value);
-			if (gid == (gid_t)-1)
-				return NULL;
-			value = dec2str(gid);
+		if (*name != '\0' && value != NULL) {
+			auth_request_set_userdb_field(auth_request,
+						      name, value);
 		}
-
-		if (*name != '\0')
-			auth_stream_reply_add(reply, name, value);
 	}
-
-	if (uid == (uid_t)-1) {
-		auth_request_log_error(auth_request, "sql",
-			"User query didn't return uid, or it was NULL");
-		return NULL;
-	}
-	if (gid == (gid_t)-1) {
-		auth_request_log_error(auth_request, "sql",
-			"User query didn't return gid, or it was NULL");
-		return NULL;
-	}
-
-	return reply;
 }
 
 static void sql_query_callback(struct sql_result *sql_result,
 			       struct userdb_sql_request *sql_request)
 {
 	struct auth_request *auth_request = sql_request->auth_request;
-	struct auth_stream_reply *reply = NULL;
 	enum userdb_result result = USERDB_RESULT_INTERNAL_FAILURE;
 	int ret;
 
@@ -96,12 +62,11 @@
 		result = USERDB_RESULT_USER_UNKNOWN;
 		auth_request_log_info(auth_request, "sql", "Unknown user");
 	} else {
-		reply = sql_query_get_result(sql_result, auth_request);
-		if (reply != NULL)
-			result = USERDB_RESULT_OK;
+		sql_query_get_result(sql_result, auth_request);
+		result = USERDB_RESULT_OK;
 	}
 
-	sql_request->callback(result, reply, auth_request);
+	sql_request->callback(result, auth_request);
 	auth_request_unref(&auth_request);
 	i_free(sql_request);
 }
--- a/src/auth/userdb-static.c	Tue Jul 03 01:59:06 2007 +0300
+++ b/src/auth/userdb-static.c	Tue Jul 03 03:20:06 2007 +0300
@@ -31,7 +31,6 @@
 	struct static_userdb_module *module =
 		(struct static_userdb_module *)_module;
         const struct var_expand_table *table;
-	struct auth_stream_reply *reply;
 	string_t *str;
 	const char *const *args, *value;
 	unsigned int i, count;
@@ -40,8 +39,7 @@
 	str = t_str_new(256);
 	table = auth_request_get_var_expand_table(auth_request, NULL);
 
-	reply = auth_stream_reply_init(auth_request);
-	auth_stream_reply_add(reply, NULL, auth_request->user);
+	auth_request_init_userdb_reply(auth_request);
 
 	args = array_get(&module->template, &count);
 	i_assert((count % 2) == 0);
@@ -53,10 +51,10 @@
 			var_expand(str, args[i+1], table);
 			value = str_c(str);
 		}
-		auth_stream_reply_add(reply, args[i], value);
+		auth_request_set_userdb_field(auth_request, args[i], value);
 	}
 
-	callback(USERDB_RESULT_OK, reply, auth_request);
+	callback(USERDB_RESULT_OK, auth_request);
 	t_pop();
 }
 
@@ -79,7 +77,7 @@
 	case PASSDB_RESULT_USER_UNKNOWN:
 	case PASSDB_RESULT_USER_DISABLED:
 	case PASSDB_RESULT_PASS_EXPIRED:
-		ctx->callback(USERDB_RESULT_USER_UNKNOWN, NULL, auth_request);
+		ctx->callback(USERDB_RESULT_USER_UNKNOWN, auth_request);
 		break;
 	case PASSDB_RESULT_SCHEME_NOT_AVAILABLE:
 		auth_request_log_error(auth_request, "static",
@@ -87,8 +85,7 @@
 			"can't verify user's existence");
 		/* fall through */
 	default:
-		ctx->callback(USERDB_RESULT_INTERNAL_FAILURE,
-			      NULL, auth_request);
+		ctx->callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request);
 		break;
 	}
 
--- a/src/auth/userdb-vpopmail.c	Tue Jul 03 01:59:06 2007 +0300
+++ b/src/auth/userdb-vpopmail.c	Tue Jul 03 03:20:06 2007 +0300
@@ -75,14 +75,13 @@
 		(struct vpopmail_userdb_module *)_module;
 	char vpop_user[VPOPMAIL_LIMIT], vpop_domain[VPOPMAIL_LIMIT];
 	struct vqpasswd *vpw;
-	struct auth_stream_reply *reply;
 	const char *quota;
 	uid_t uid;
 	gid_t gid;
 
 	vpw = vpopmail_lookup_vqp(auth_request, vpop_user, vpop_domain);
 	if (vpw == NULL) {
-		callback(USERDB_RESULT_USER_UNKNOWN, NULL, auth_request);
+		callback(USERDB_RESULT_USER_UNKNOWN, auth_request);
 		return;
 	}
 
@@ -91,7 +90,7 @@
 	if (vget_assign(vpop_domain, NULL, 0, &uid, &gid) == NULL) {
 		auth_request_log_info(auth_request, "vpopmail",
 				      "vget_assign(%s) failed", vpop_domain);
-		callback(USERDB_RESULT_INTERNAL_FAILURE, NULL, auth_request);
+		callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request);
 		return;
 	}
 
@@ -110,32 +109,32 @@
 			auth_request_log_error(auth_request, "vpopmail",
 					       "make_user_dir(%s, %s) failed",
 					       vpop_user, vpop_domain);
-			callback(USERDB_RESULT_INTERNAL_FAILURE,
-				 NULL, auth_request);
+			callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request);
 			return;
 		}
 
 		/* get the user again so pw_dir is visible */
 		vpw = vauth_getpw(vpop_user, vpop_domain);
 		if (vpw == NULL) {
-			callback(USERDB_RESULT_INTERNAL_FAILURE,
-				 NULL, auth_request);
+			callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request);
 			return;
 		}
 	}
 
-	reply = auth_stream_reply_init(auth_request);
-	auth_stream_reply_add(reply, NULL, auth_request->user);
-	auth_stream_reply_add(reply, "uid", dec2str(uid));
-	auth_stream_reply_add(reply, "gid", dec2str(gid));
-	auth_stream_reply_add(reply, "home", vpw->pw_dir);
+	auth_request_init_userdb_reply(auth_request);
+	auth_request_set_userdb_field(auth_request, NULL, auth_request->user);
+	auth_request_set_userdb_field(auth_request, "uid", dec2str(uid));
+	auth_request_set_userdb_field(auth_request, "gid", dec2str(gid));
+	auth_request_set_userdb_field(auth_request, "home", vpw->pw_dir);
 
 	quota = userdb_vpopmail_get_quota(module->quota_template_value,
 					  vpw->pw_shell);
-	if (*quota != '\0')
-		auth_stream_reply_add(reply, module->quota_template_key, quota);
-
-	callback(USERDB_RESULT_OK, reply, auth_request);
+	if (*quota != '\0') {
+		auth_request_set_userdb_field(auth_request,
+					      module->quota_template_key,
+					      quota);
+	}
+	callback(USERDB_RESULT_OK, auth_request);
 }
 
 static struct userdb_module *
--- a/src/auth/userdb.h	Tue Jul 03 01:59:06 2007 +0300
+++ b/src/auth/userdb.h	Tue Jul 03 03:20:06 2007 +0300
@@ -13,7 +13,6 @@
 };
 
 typedef void userdb_callback_t(enum userdb_result result,
-			       struct auth_stream_reply *reply,
 			       struct auth_request *request);
 
 struct userdb_module {