changeset 9288:77d6b5eb0963 HEAD

lib-auth: Added support for iterating through all users.
author Timo Sirainen <tss@iki.fi>
date Fri, 15 May 2009 15:09:13 -0400
parents 6557ed6fc4e1
children 0e02c867b15a
files src/lib-auth/auth-master.c src/lib-auth/auth-master.h
diffstat 2 files changed, 191 insertions(+), 61 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-auth/auth-master.c	Fri May 15 15:08:37 2009 -0400
+++ b/src/lib-auth/auth-master.c	Fri May 15 15:09:13 2009 -0400
@@ -21,6 +21,8 @@
 #define MAX_INBUF_SIZE 8192
 #define MAX_OUTBUF_SIZE 1024
 
+#define DEFAULT_USERDB_LOOKUP_PREFIX "userdb lookup"
+
 struct auth_master_connection {
 	char *auth_socket_path;
 
@@ -30,12 +32,13 @@
 	struct istream *input;
 	struct ostream *output;
 	struct timeout *to;
+	const char *prefix;
 
 	unsigned int request_counter;
-	pool_t pool;
-	const char *user;
-	struct auth_user_reply *user_reply;
-	int return_value;
+
+	bool (*reply_callback)(const char *cmd, const char *const *args,
+			       void *context);
+	void *reply_context;
 
 	unsigned int debug:1;
 	unsigned int sent_handshake:1;
@@ -43,6 +46,23 @@
 	unsigned int aborted:1;
 };
 
+struct auth_master_user_lookup_ctx {
+	struct auth_master_connection *conn;
+	pool_t pool;
+	const char *user;
+	struct auth_user_reply *user_reply;
+	int return_value;
+};
+
+struct auth_master_user_list_ctx {
+	struct auth_master_connection *conn;
+	pool_t pool;
+	ARRAY_TYPE(const_string) users;
+	const char *const *user_strings;
+	unsigned int idx, user_count;
+	bool failed;
+};
+
 static void auth_input(struct auth_master_connection *conn);
 
 struct auth_master_connection *
@@ -54,6 +74,7 @@
 	conn->auth_socket_path = i_strdup(auth_socket_path);
 	conn->fd = -1;
 	conn->debug = debug;
+	conn->prefix = DEFAULT_USERDB_LOOKUP_PREFIX;
 	return conn;
 }
 
@@ -87,19 +108,19 @@
 	conn->aborted = TRUE;
 }
 
-static void auth_parse_input(struct auth_master_connection *conn,
+static void auth_parse_input(struct auth_master_user_lookup_ctx *ctx,
 			     const char *const *args)
 {
-	struct auth_user_reply *reply = conn->user_reply;
+	struct auth_user_reply *reply = ctx->user_reply;
 
 	memset(reply, 0, sizeof(*reply));
 	reply->uid = (uid_t)-1;
 	reply->gid = (gid_t)-1;
-	p_array_init(&reply->extra_fields, conn->pool, 64);
+	p_array_init(&reply->extra_fields, ctx->pool, 64);
 
-	reply->user = p_strdup(conn->pool, *args);
+	reply->user = p_strdup(ctx->pool, *args);
 	for (args++; *args != NULL; args++) {
-		if (conn->debug)
+		if (ctx->conn->debug)
 			i_info("auth input: %s", *args);
 
 		if (strncmp(*args, "uid=", 4) == 0)
@@ -107,11 +128,11 @@
 		else if (strncmp(*args, "gid=", 4) == 0)
 			reply->gid = strtoul(*args + 4, NULL, 10);
 		else if (strncmp(*args, "home=", 5) == 0)
-			reply->home = p_strdup(conn->pool, *args + 5);
+			reply->home = p_strdup(ctx->pool, *args + 5);
 		else if (strncmp(*args, "chroot=", 7) == 0)
-			reply->chroot = p_strdup(conn->pool, *args + 7);
+			reply->chroot = p_strdup(ctx->pool, *args + 7);
 		else {
-			const char *field = p_strdup(conn->pool, *args);
+			const char *field = p_strdup(ctx->pool, *args);
 			array_append(&reply->extra_fields, &field, 1);
 		}
 	}
@@ -126,9 +147,9 @@
 		if (strcmp(tmp[0], "VERSION") == 0 &&
 		    tmp[1] != NULL && tmp[2] != NULL) {
 			if (strcmp(tmp[1], dec2str(AUTH_PROTOCOL_MAJOR)) != 0) {
-				i_error("userdb lookup(%s): "
+				i_error("userdb lookup: "
 					"Auth protocol version mismatch "
-					"(%s vs %d)", conn->user, tmp[1],
+					"(%s vs %d)", tmp[1],
 					AUTH_PROTOCOL_MAJOR);
 				auth_request_lookup_abort(conn);
 				return -1;
@@ -141,6 +162,29 @@
 	return 0;
 }
 
+static bool auth_user_reply_callback(const char *cmd, const char *const *args,
+				     void *context)
+{
+	struct auth_master_user_lookup_ctx *ctx = context;
+
+	io_loop_stop(ctx->conn->ioloop);
+	if (strcmp(cmd, "USER") == 0) {
+		auth_parse_input(ctx, args);
+		ctx->return_value = 1;
+		return TRUE;
+	}
+	if (strcmp(cmd, "NOTFOUND") == 0) {
+		ctx->return_value = 0;
+		return TRUE;
+	}
+	if (strcmp(cmd, "FAIL") == 0) {
+		i_error("userdb lookup(%s) failed: %s", ctx->user,
+			*args != NULL ? *args : "Internal failure");
+		return TRUE;
+	}
+	return FALSE;
+}
+
 static void auth_input(struct auth_master_connection *conn)
 {
 	const char *line, *cmd, *const *args, *id, *wanted_id;
@@ -150,14 +194,14 @@
 		return;
 	case -1:
 		/* disconnected */
-		i_error("userdb lookup(%s): Disconnected unexpectedly",
-			conn->user);
+		i_error("%s: Disconnected unexpectedly",
+			conn->prefix);
 		auth_request_lookup_abort(conn);
 		return;
 	case -2:
 		/* buffer full */
-		i_error("userdb lookup(%s): BUG: Received more than %d bytes",
-			conn->user, MAX_INBUF_SIZE);
+		i_error("%s: BUG: Received more than %d bytes",
+			conn->prefix, MAX_INBUF_SIZE);
 		auth_request_lookup_abort(conn);
 		return;
 	}
@@ -182,31 +226,16 @@
 
 	wanted_id = dec2str(conn->request_counter);
 	if (strcmp(id, wanted_id) == 0) {
-		io_loop_stop(conn->ioloop);
-		if (strcmp(cmd, "USER") == 0) {
-			auth_parse_input(conn, args);
-			conn->return_value = 1;
+		if (conn->reply_callback(cmd, args, conn->reply_context))
 			return;
-		}
-		if (strcmp(cmd, "NOTFOUND") == 0) {
-			conn->return_value = 0;
-			return;
-		}
-		if (strcmp(cmd, "FAIL") == 0) {
-			i_error("userdb lookup(%s) failed: %s",
-				conn->user, *args != NULL ? *args :
-				"Internal failure");
-			return;
-		}
 	}
 	
 	if (strcmp(cmd, "CUID") == 0) {
-		i_error("userdb lookup(%s): %s is an auth client socket. "
+		i_error("%s: %s is an auth client socket. "
 			"It should be a master socket.",
-			conn->user, conn->auth_socket_path);
+			conn->prefix, conn->auth_socket_path);
 	} else {
-		i_error("userdb lookup(%s): BUG: Unexpected input: %s",
-			conn->user, line);
+		i_error("%s: BUG: Unexpected input: %s", conn->prefix, line);
 	}
 	auth_request_lookup_abort(conn);
 }
@@ -238,9 +267,9 @@
 static void auth_request_timeout(struct auth_master_connection *conn)
 {
 	if (!conn->handshaked)
-		i_error("userdb lookup(%s): Connecting timed out", conn->user);
+		i_error("%s: Connecting timed out", conn->prefix);
 	else
-		i_error("userdb lookup(%s): Request timed out", conn->user);
+		i_error("%s: Request timed out", conn->prefix);
 	auth_request_lookup_abort(conn);
 }
 
@@ -293,17 +322,12 @@
 	return TRUE;
 }
 
-int auth_master_user_lookup(struct auth_master_connection *conn,
-			    const char *user, const char *service,
-			    pool_t pool, struct auth_user_reply *reply_r)
+static int auth_master_run_cmd(struct auth_master_connection *conn,
+			       const char *cmd)
 {
 	struct ioloop *prev_ioloop;
 	const char *str;
 
-	if (!is_valid_string(user) || !is_valid_string(service)) {
-		/* non-allowed characters, the user can't exist */
-		return 0;
-	}
 	if (conn->fd == -1) {
 		if (auth_master_connect(conn) < 0)
 			return -1;
@@ -311,14 +335,6 @@
 
 	prev_ioloop = current_ioloop;
 	auth_master_set_io(conn);
-	conn->return_value = -1;
-	conn->pool = pool;
-	conn->user = user;
-	conn->user_reply = reply_r;
-	if (++conn->request_counter == 0) {
-		/* avoid zero */
-		conn->request_counter++;
-	}
 
 	o_stream_cork(conn->output);
 	if (!conn->sent_handshake) {
@@ -328,9 +344,7 @@
 		conn->sent_handshake = TRUE;
 	}
 
-	str = t_strdup_printf("USER\t%u\t%s\tservice=%s\n",
-			      conn->request_counter, user, service);
-	o_stream_send_str(conn->output, str);
+	o_stream_send_str(conn->output, cmd);
 	o_stream_uncork(conn->output);
 
 	if (conn->output->stream_errno != 0) {
@@ -344,9 +358,118 @@
 	if (conn->aborted) {
 		conn->aborted = FALSE;
 		auth_connection_close(conn);
+		return -1;
 	}
-	conn->user = NULL;
-	conn->pool = NULL;
-	conn->user_reply = NULL;
-	return conn->return_value;
+	return 0;
+}
+
+int auth_master_user_lookup(struct auth_master_connection *conn,
+			    const char *user, const char *service,
+			    pool_t pool, struct auth_user_reply *reply_r)
+{
+	struct auth_master_user_lookup_ctx ctx;
+	const char *str;
+
+	if (!is_valid_string(user) || !is_valid_string(service)) {
+		/* non-allowed characters, the user can't exist */
+		return 0;
+	}
+
+	memset(&ctx, 0, sizeof(ctx));
+	ctx.conn = conn;
+	ctx.return_value = -1;
+	ctx.pool = pool;
+	ctx.user = user;
+	ctx.user_reply = reply_r;
+
+	if (++conn->request_counter == 0) {
+		/* avoid zero */
+		conn->request_counter++;
+	}
+
+	conn->reply_callback = auth_user_reply_callback;
+	conn->reply_context = &ctx;
+
+	conn->prefix = t_strdup_printf("userdb lookup(%s)", user);
+	str = t_strdup_printf("USER\t%u\t%s\tservice=%s\n",
+			      conn->request_counter, user, service);
+	(void)auth_master_run_cmd(conn, str);
+	conn->prefix = DEFAULT_USERDB_LOOKUP_PREFIX;
+
+	return ctx.return_value;
 }
+
+static bool
+auth_user_list_reply_callback(const char *cmd, const char *const *args,
+			      void *context)
+{
+	struct auth_master_user_list_ctx *ctx = context;
+	const char *user;
+
+	if (strcmp(cmd, "DONE") == 0) {
+		io_loop_stop(ctx->conn->ioloop);
+		if (args[0] != NULL && strcmp(args[0], "fail") == 0) {
+			i_error("User listing returned failure");
+			ctx->failed = TRUE;
+		}
+		return TRUE;
+	}
+	if (strcmp(cmd, "LIST") == 0 && args[0] != NULL) {
+		/* we'll just read all the users into memory. otherwise we'd
+		   have to use a separate connection for listing and there's
+		   a higher chance of a failure since the connection could be
+		   open to dovecot-auth for a long time. */
+		user = p_strdup(ctx->pool, args[0]);
+		array_append(&ctx->users, &user, 1);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+struct auth_master_user_list_ctx *
+auth_master_user_list_init(struct auth_master_connection *conn)
+{
+	struct auth_master_user_list_ctx *ctx;
+	const char *str;
+	pool_t pool;
+
+	pool = pool_alloconly_create("auth master user list", 10240);
+	ctx = p_new(pool, struct auth_master_user_list_ctx, 1);
+	ctx->pool = pool;
+	ctx->conn = conn;
+	i_array_init(&ctx->users, 128);
+
+	if (++conn->request_counter == 0) {
+		/* avoid zero */
+		conn->request_counter++;
+	}
+
+	conn->reply_callback = auth_user_list_reply_callback;
+	conn->reply_context = ctx;
+
+	str = t_strdup_printf("LIST\t%u\n", conn->request_counter);
+	conn->prefix = "userdb list";
+	if (auth_master_run_cmd(conn, str) < 0)
+		ctx->failed = TRUE;
+	ctx->user_strings = array_get(&ctx->users, &ctx->user_count);
+	conn->prefix = DEFAULT_USERDB_LOOKUP_PREFIX;
+	return ctx;
+}
+
+const char *auth_master_user_list_next(struct auth_master_user_list_ctx *ctx)
+{
+	if (ctx->idx == ctx->user_count)
+		return NULL;
+	return ctx->user_strings[ctx->idx++];
+}
+
+int auth_master_user_list_deinit(struct auth_master_user_list_ctx **_ctx)
+{
+	struct auth_master_user_list_ctx *ctx = *_ctx;
+	int ret = ctx->failed ? -1 : 0;
+
+	*_ctx = NULL;
+	array_free(&ctx->users);
+	pool_unref(&ctx->pool);
+	return ret;
+}
--- a/src/lib-auth/auth-master.h	Fri May 15 15:08:37 2009 -0400
+++ b/src/lib-auth/auth-master.h	Fri May 15 15:09:13 2009 -0400
@@ -17,4 +17,11 @@
 			    const char *user, const char *service,
 			    pool_t pool, struct auth_user_reply *reply_r);
 
+/* Iterate through all users. */
+struct auth_master_user_list_ctx *
+auth_master_user_list_init(struct auth_master_connection *conn);
+const char *auth_master_user_list_next(struct auth_master_user_list_ctx *ctx);
+/* Returns -1 if anything failed, 0 if ok */
+int auth_master_user_list_deinit(struct auth_master_user_list_ctx **ctx);
+
 #endif