changeset 8365:f97099eb4dee HEAD

New generic userdb lookup api `auth-master' in lib-auth.
author Sascha Wilde <wilde@intevation.de>
date Fri, 24 Oct 2008 16:06:07 +0200
parents 58afb62be4e0
children 2c111b572eee
files src/deliver/Makefile.am src/deliver/auth-client.c src/deliver/auth-client.h src/deliver/deliver.c src/lib-auth/Makefile.am src/lib-auth/auth-master.c src/lib-auth/auth-master.h
diffstat 7 files changed, 338 insertions(+), 229 deletions(-) [+]
line wrap: on
line diff
--- a/src/deliver/Makefile.am	Fri Oct 31 18:35:43 2008 +0200
+++ b/src/deliver/Makefile.am	Fri Oct 24 16:06:07 2008 +0200
@@ -20,7 +20,7 @@
 # get some functions included which only plugins use. liblib should probably
 # be a shared library so this wouldn't be needed..
 unused_objects = \
- 	../lib/mountpoint.o
+	../lib/mountpoint.o
 
 libs = \
 	../lib-storage/register/libstorage-register.a \
@@ -30,6 +30,7 @@
 	../lib-mail/libmail.a \
 	../lib-dict/libdict.a \
 	../lib-charset/libcharset.a \
+	../lib-auth/libauth.a \
 	../lib/liblib.a \
 	$(unused_objects)
 
--- a/src/deliver/auth-client.c	Fri Oct 31 18:35:43 2008 +0200
+++ b/src/deliver/auth-client.c	Fri Oct 24 16:06:07 2008 +0200
@@ -9,6 +9,7 @@
 #include "env-util.h"
 #include "restrict-access.h"
 #include "auth-client.h"
+#include "../lib-auth/auth-master.h"
 
 #include <stdlib.h>
 #include <unistd.h>
@@ -16,42 +17,8 @@
 #include <grp.h>
 #include <sysexits.h>
 
-#define AUTH_REQUEST_TIMEOUT 60
-#define MAX_INBUF_SIZE 8192
-#define MAX_OUTBUF_SIZE 512
-
 static int return_value;
 
-struct auth_connection {
-	int fd;
-	struct timeout *to;
-	struct io *io;
-	struct istream *input;
-	struct ostream *output;
-
-	struct ioloop *ioloop;
-	uid_t euid;
-	const char *auth_socket;
-	const char *user;
-	ARRAY_TYPE(string) *extra_fields;
-
-	unsigned int handshaked:1;
-};
-
-static void auth_connection_destroy(struct auth_connection *conn)
-{
-	io_loop_stop(conn->ioloop);
-
-	if (conn->to != NULL)
-		timeout_remove(&conn->to);
-	io_remove(&conn->io);
-	i_stream_unref(&conn->input);
-	o_stream_unref(&conn->output);
-	if (close(conn->fd) < 0)
-		i_error("close() failed: %m");
-	i_free(conn);
-}
-
 static bool parse_uid(const char *str, uid_t *uid_r)
 {
 	struct passwd *pw;
@@ -90,90 +57,59 @@
 	return TRUE;
 }
 
-static void auth_parse_input(struct auth_connection *conn, const char *args)
+static void set_env(struct auth_user_reply *reply, const char *user, uid_t euid)
 {
-	const char *const *tmp, *extra_groups;
-	uid_t uid = 0;
-	gid_t gid = 0;
-	const char *chroot_dir = getenv("MAIL_CHROOT");
-	const char *home_dir = NULL;
-	bool debug = getenv("DEBUG") != NULL;
+	const char *extra_groups;
 	unsigned int len;
 
-	for (tmp = t_strsplit(args, "\t"); *tmp != NULL; tmp++) {
-		if (debug)
-			i_info("auth input: %s", *tmp);
-
-		if (strncmp(*tmp, "uid=", 4) == 0) {
-			uid = strtoul(*tmp + 4, NULL, 10);
-
-			if (uid == 0) {
-				i_error("userdb(%s) returned 0 as uid",
-					conn->user);
-				return_value = EX_TEMPFAIL;
+	if (reply->uid == 0) {
+		i_error("userdb(%s) returned 0 as uid", user);
+		return;
+	} else if (reply->uid == (uid_t)-1) {
+		if (getenv("MAIL_UID") != NULL) {
+			if (!parse_uid(getenv("MAIL_UID"), &reply->uid) || reply->uid == 0) {
+				i_error("mail_uid setting is invalid");
+				return;
 			}
-		} else if (strncmp(*tmp, "gid=", 4) == 0) {
-			gid = strtoul(*tmp + 4, NULL, 10);
-
-			if (gid == 0) {
-				i_error("userdb(%s) returned 0 as gid",
-					conn->user);
-				return_value = EX_TEMPFAIL;
+		} else {
+			i_error("User %s is missing UID (set mail_uid)", user);
+			return;
+		}
+	}
+	if (reply->gid == 0) {
+		i_error("userdb(%s) returned 0 as gid", user);
+		return;
+	} else if (reply->gid == (gid_t)-1) {
+		if (getenv("MAIL_GID") != NULL) {
+			if (!parse_gid(getenv("MAIL_GID"), &reply->gid) || reply->gid == 0) {
+				i_error("mail_gid setting is invalid");
+				return;
 			}
-		} else if (strncmp(*tmp, "chroot=", 7) == 0) {
-			chroot_dir = *tmp + 7;
 		} else {
-			char *field = i_strdup(*tmp);
-
-			if (strncmp(field, "home=", 5) == 0)
-				home_dir = field + 5;
-
-			array_append(conn->extra_fields, &field, 1);
+			i_error("User %s is missing GID (set mail_gid)", user);
+			return;
 		}
 	}
 
-	if (uid == 0 && getenv("MAIL_UID") != NULL) {
-		if (!parse_uid(getenv("MAIL_UID"), &uid) || uid == 0) {
-			i_error("mail_uid setting is invalid");
-			return_value = EX_TEMPFAIL;
-			return;
+	if (euid != reply->uid)
+		env_put(t_strconcat("RESTRICT_SETUID=", dec2str(reply->uid), NULL));
+	if (euid == 0 || getegid() != reply->gid)
+		env_put(t_strconcat("RESTRICT_SETGID=", dec2str(reply->gid), NULL));
+
+	if (reply->chroot == NULL)
+		reply->chroot = getenv("MAIL_CHROOT");
+	if (reply->chroot != NULL) {
+		len = strlen(reply->chroot);
+		if (len > 2 && strcmp(reply->chroot + len - 2, "/.") == 0 &&
+		    reply->home != NULL &&
+		    strncmp(reply->home, reply->chroot, len - 2) == 0) {
+			/* strip chroot dir from home dir */
+			reply->home += len - 2;
 		}
-	}
-	if (uid == 0) {
-		i_error("User %s is missing UID (set mail_uid)", conn->user);
-		return_value = EX_TEMPFAIL;
-		return;
-	}
-	if (gid == 0 && getenv("MAIL_GID") != NULL) {
-		if (!parse_gid(getenv("MAIL_GID"), &gid) || gid == 0) {
-			i_error("mail_gid setting is invalid");
-			return_value = EX_TEMPFAIL;
-			return;
-		}
+		env_put(t_strconcat("RESTRICT_CHROOT=", reply->chroot, NULL));
 	}
-	if (gid == 0) {
-		i_error("User %s is missing GID (set mail_gid)", conn->user);
-		return_value = EX_TEMPFAIL;
-		return;
-	}
-
-	if (conn->euid != uid)
-		env_put(t_strconcat("RESTRICT_SETUID=", dec2str(uid), NULL));
-	if (conn->euid == 0 || getegid() != gid)
-		env_put(t_strconcat("RESTRICT_SETGID=", dec2str(gid), NULL));
-
-	if (chroot_dir != NULL) {
-		len = strlen(chroot_dir);
-		if (len > 2 && strcmp(chroot_dir + len - 2, "/.") == 0 &&
-		    home_dir != NULL &&
-		    strncmp(home_dir, chroot_dir, len - 2) == 0) {
-			/* strip chroot dir from home dir */
-			home_dir += len - 2;
-		}
-		env_put(t_strconcat("RESTRICT_CHROOT=", chroot_dir, NULL));
-	}
-	if (home_dir != NULL)
-		env_put(t_strconcat("HOME=", home_dir, NULL));
+	if (reply->home != NULL)
+		env_put(t_strconcat("HOME=", reply->home, NULL));
 
 	extra_groups = getenv("MAIL_EXTRA_GROUPS");
 	if (extra_groups != NULL) {
@@ -181,127 +117,39 @@
 				    extra_groups, NULL));
 	}
 
-	restrict_access_by_env(TRUE);
 	return_value = EX_OK;
 }
 
-static void auth_input(struct auth_connection *conn)
-{
-	const char *line;
-
-	switch (i_stream_read(conn->input)) {
-	case 0:
-		return;
-	case -1:
-		/* disconnected */
-		i_error("Auth lookup disconnected unexpectedly");
-		auth_connection_destroy(conn);
-		return;
-	case -2:
-		/* buffer full */
-		i_error("BUG: Auth master sent us more than %d bytes",
-			MAX_INBUF_SIZE);
-		auth_connection_destroy(conn);
-		return;
-	}
-
-	if (!conn->handshaked) {
-		while ((line = i_stream_next_line(conn->input)) != NULL) {
-			if (strncmp(line, "VERSION\t", 8) == 0) {
-				if (strncmp(line + 8, "1\t", 2) != 0) {
-					i_error("Auth master version mismatch");
-					auth_connection_destroy(conn);
-					return;
-				}
-			} else if (strncmp(line, "SPID\t", 5) == 0) {
-				conn->handshaked = TRUE;
-				break;
-			}
-		}
-	}
-
-	line = i_stream_next_line(conn->input);
-	if (line != NULL) {
-		if (strncmp(line, "USER\t1\t", 7) == 0) {
-			auth_parse_input(conn, line + 7);
-		} else if (strcmp(line, "NOTFOUND\t1") == 0)
-			return_value = EX_NOUSER;
-		else if (strncmp(line, "FAIL\t1", 6) == 0) {
-			i_error("Auth lookup returned failure");
-			return_value = EX_TEMPFAIL;
-		} else if (strncmp(line, "CUID\t", 5) == 0) {
-			i_error("%s is an auth client socket. "
-				"It should be a master socket.",
-				conn->auth_socket);
-		} else {
-			i_error("BUG: Unexpected input from auth master: %s",
-				line);
-		}
-		auth_connection_destroy(conn);
-	}
-}
-
-static struct auth_connection *auth_connection_new(const char *auth_socket)
-{
-	struct auth_connection *conn;
-	int fd, try;
-
-	/* max. 1 second wait here. */
-	for (try = 0; try < 10; try++) {
-		fd = net_connect_unix(auth_socket);
-		if (fd != -1 || (errno != EAGAIN && errno != ECONNREFUSED))
-			break;
-
-		/* busy. wait for a while. */
-		usleep(((rand() % 10) + 1) * 10000);
-	}
-	if (fd == -1) {
-		i_error("Can't connect to auth server at %s: %m", auth_socket);
-		return NULL;
-	}
-
-	conn = i_new(struct auth_connection, 1);
-	conn->fd = fd;
-	conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE);
-	conn->output = o_stream_create_fd(fd, MAX_OUTBUF_SIZE, FALSE);
-	conn->io = io_add(fd, IO_READ, auth_input, conn);
-	return conn;
-}
-
-static void auth_client_timeout(struct auth_connection *conn)
-{
-	if (!conn->handshaked)
-		i_error("Connecting to dovecot-auth timed out");
-	else
-		i_error("User request from dovecot-auth timed out");
-	auth_connection_destroy(conn);
-}
-
-int auth_client_lookup_and_restrict(struct ioloop *ioloop,
-				    const char *auth_socket,
+int auth_client_lookup_and_restrict(const char *auth_socket,
 				    const char *user, uid_t euid,
-				    ARRAY_TYPE(string) *extra_fields_r)
+				    pool_t pool,
+				    ARRAY_TYPE(string) **extra_fields_r)
 {
         struct auth_connection *conn;
-
-	conn = auth_connection_new(auth_socket);
-	if (conn == NULL)
-		return EX_TEMPFAIL;
+	struct auth_user_reply *reply;
+	bool debug = getenv("DEBUG") != NULL;
 
-	conn->ioloop = ioloop;
-	conn->euid = euid;
-	conn->user = user;
-	conn->auth_socket = auth_socket;
-	conn->to = timeout_add(1000*AUTH_REQUEST_TIMEOUT,
-			       auth_client_timeout, conn);
-	conn->extra_fields = extra_fields_r;
-
-	o_stream_send_str(conn->output,
-			  t_strconcat("VERSION\t1\t0\n"
-				      "USER\t1\t", user, "\t"
-				      "service=deliver\n", NULL));
+	conn = auth_master_init(auth_socket, debug);
+	reply = i_new(struct auth_user_reply, 1);
 
 	return_value = EX_TEMPFAIL;
-	io_loop_run(ioloop);
+
+	switch (auth_master_user_lookup(conn, user, "deliver", pool, reply)) {
+	case -1:
+		break;
+	case 0:
+		return_value = EX_NOUSER;
+		break;
+	case 1:
+		set_env(reply, user, euid);
+		if (return_value == EX_OK)
+			restrict_access_by_env(TRUE);
+		break;
+	}
+	
+	*extra_fields_r = reply->extra_fields;
+	i_free(reply);
+	auth_master_deinit(conn);
+
 	return return_value;
 }
--- a/src/deliver/auth-client.h	Fri Oct 31 18:35:43 2008 +0200
+++ b/src/deliver/auth-client.h	Fri Oct 24 16:06:07 2008 +0200
@@ -1,9 +1,9 @@
 #ifndef AUTH_CLIENT_H
 #define AUTH_CLIENT_H
 
-int auth_client_lookup_and_restrict(struct ioloop *ioloop,
-				    const char *auth_socket,
+int auth_client_lookup_and_restrict(const char *auth_socket,
 				    const char *user, uid_t euid,
-				    ARRAY_TYPE(string) *extra_fields_r);
+				    pool_t pool,
+				    ARRAY_TYPE(string) **extra_fields_r);
 
 #endif
--- a/src/deliver/deliver.c	Fri Oct 31 18:35:43 2008 +0200
+++ b/src/deliver/deliver.c	Fri Oct 24 16:06:07 2008 +0200
@@ -789,7 +789,6 @@
 			key = t_str_ucase(t_strdup_until(fields[i], p));
 			env_put(t_strconcat(key, p, NULL));
 		}
-		i_free(fields[i]);
 	}
 }
 
@@ -799,7 +798,7 @@
 	const char *mailbox = "INBOX";
 	const char *auth_socket;
 	const char *home, *destaddr, *user, *value, *errstr, *path;
-	ARRAY_TYPE(string) extra_fields;
+	ARRAY_TYPE(string) *extra_fields;
 	struct mail_user *mail_user, *raw_mail_user;
 	struct mail_namespace *raw_ns;
 	struct mail_storage *storage;
@@ -815,6 +814,7 @@
 	bool user_auth = FALSE;
 	time_t mtime;
 	int i, ret;
+	pool_t userdb_pool;
 
 	i_set_failure_exit_callback(failure_exit_callback);
 
@@ -945,7 +945,8 @@
 					  TRUE, version);
 	}
 
-	t_array_init(&extra_fields, 64);
+	userdb_pool = pool_alloconly_create("userdb lookup replys", 512);
+
 	if (user_auth) {
 		auth_socket = getenv("AUTH_SOCKET_PATH");
 		if (auth_socket == NULL) {
@@ -956,8 +957,9 @@
 						  NULL);
 		}
 
-		ret = auth_client_lookup_and_restrict(ioloop, auth_socket,
+		ret = auth_client_lookup_and_restrict(auth_socket,
 						      user, process_euid,
+						      userdb_pool,
 						      &extra_fields);
 		if (ret != 0)
 			return ret;
@@ -966,7 +968,9 @@
 		destaddr = user;
 
 	expand_envs(user);
-	putenv_extra_fields(&extra_fields);
+	putenv_extra_fields(extra_fields);
+
+	pool_unref(&userdb_pool);
 
 	/* Fix namespaces with empty locations */
 	for (i = 1;; i++) {
--- a/src/lib-auth/Makefile.am	Fri Oct 31 18:35:43 2008 +0200
+++ b/src/lib-auth/Makefile.am	Fri Oct 24 16:06:07 2008 +0200
@@ -5,11 +5,13 @@
 
 libauth_a_SOURCES = \
 	auth-client.c \
+	auth-master.c \
 	auth-server-connection.c \
 	auth-server-request.c
 
 headers = \
 	auth-client.h \
+	auth-master.h \
 	auth-server-connection.h \
 	auth-server-request.h
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-auth/auth-master.c	Fri Oct 24 16:06:07 2008 +0200
@@ -0,0 +1,233 @@
+/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "ioloop.h"
+#include "network.h"
+#include "istream.h"
+#include "ostream.h"
+#include "env-util.h"
+#include "restrict-access.h"
+#include "auth-master.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sysexits.h>
+
+#define AUTH_REQUEST_TIMEOUT 60
+#define MAX_INBUF_SIZE 8192
+#define MAX_OUTBUF_SIZE 512
+
+struct auth_connection {
+	int fd;
+	struct timeout *to;
+	struct io *io;
+	struct istream *input;
+	struct ostream *output;
+
+	struct ioloop *ioloop;
+	const char *auth_socket;
+	const char *user;
+	pool_t pool;
+	struct auth_user_reply *user_reply;
+	int return_value;
+
+	unsigned int handshaked:1;
+	bool debug;
+};
+
+static void auth_input(struct auth_connection *conn);
+
+struct auth_connection *auth_master_init(const char *auth_socket, bool debug)
+{
+	struct auth_connection *conn;
+	int fd, try;
+
+	/* max. 1 second wait here. */
+	for (try = 0; try < 10; try++) {
+		fd = net_connect_unix(auth_socket);
+		if (fd != -1 || (errno != EAGAIN && errno != ECONNREFUSED))
+			break;
+
+		/* busy. wait for a while. */
+		usleep(((rand() % 10) + 1) * 10000);
+	}
+	if (fd == -1) {
+		i_error("Can't connect to auth server at %s: %m", auth_socket);
+		return NULL;
+	}
+
+	conn = i_new(struct auth_connection, 1);
+	conn->auth_socket = auth_socket;
+	conn->fd = fd;
+	conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE);
+	conn->output = o_stream_create_fd(fd, MAX_OUTBUF_SIZE, FALSE);
+	conn->io = io_add(fd, IO_READ, auth_input, conn);
+	conn->ioloop = current_ioloop;
+	conn->debug = debug;
+	return conn;
+}
+
+static void auth_connection_close(struct auth_connection *conn)
+{
+	if (conn->fd == -1)
+		return;
+
+	io_loop_stop(conn->ioloop);
+
+	if (conn->to != NULL)
+		timeout_remove(&conn->to);
+	io_remove(&conn->io);
+	i_stream_unref(&conn->input);
+	o_stream_unref(&conn->output);
+	if (close(conn->fd) < 0)
+		i_error("close() failed: %m");
+
+	conn->fd = -1;
+}
+
+void auth_master_deinit(struct auth_connection *conn)
+{
+	auth_connection_close(conn);
+	i_free(conn);
+}
+
+static void auth_parse_input(struct auth_connection *conn, const char *args)
+{
+	struct auth_user_reply *reply = conn->user_reply;
+	const char *const *tmp;
+	uid_t uid = (uid_t)-1;
+	gid_t gid = (gid_t)-1;
+	const char *chroot_dir = NULL;
+	const char *home_dir = NULL;
+
+	reply->extra_fields = p_new(conn->pool, ARRAY_TYPE(string), 1);
+	p_array_init(reply->extra_fields, conn->pool, 64);
+
+	for (tmp = t_strsplit(args, "\t"); *tmp != NULL; tmp++) {
+		if (conn->debug)
+			i_info("auth input: %s", *tmp);
+
+		if (strncmp(*tmp, "uid=", 4) == 0)
+			uid = strtoul(*tmp + 4, NULL, 10);
+		else if (strncmp(*tmp, "gid=", 4) == 0) {
+			gid = strtoul(*tmp + 4, NULL, 10);
+
+		} else if (strncmp(*tmp, "chroot=", 7) == 0) {
+			chroot_dir = *tmp + 7;
+		} else {
+			char *field = p_strdup(conn->pool, *tmp);
+
+			if (strncmp(field, "home=", 5) == 0)
+				home_dir = field + 5;
+
+			if (reply->extra_fields != NULL)
+				array_append(reply->extra_fields, &field, 1);
+		}
+	}
+
+	reply->uid = uid;
+	reply->gid = gid;
+	if (home_dir != NULL)
+		reply->home = p_strdup(conn->pool, home_dir);
+	else
+		reply->home = NULL;
+	reply->chroot = p_strdup(conn->pool, chroot_dir);
+	
+	conn->return_value = 1;
+}
+
+static void auth_input(struct auth_connection *conn)
+{
+	const char *line;
+
+	switch (i_stream_read(conn->input)) {
+	case 0:
+		return;
+	case -1:
+		/* disconnected */
+		i_error("Auth lookup disconnected unexpectedly");
+		auth_connection_close(conn);
+		return;
+	case -2:
+		/* buffer full */
+		i_error("BUG: Auth master sent us more than %d bytes",
+			MAX_INBUF_SIZE);
+		auth_connection_close(conn);
+		return;
+	}
+
+	if (!conn->handshaked) {
+		while ((line = i_stream_next_line(conn->input)) != NULL) {
+			if (strncmp(line, "VERSION\t", 8) == 0) {
+				if (strncmp(line + 8, "1\t", 2) != 0) {
+					i_error("Auth master version mismatch");
+					auth_connection_close(conn);
+					return;
+				}
+			} else if (strncmp(line, "SPID\t", 5) == 0) {
+				conn->handshaked = TRUE;
+				break;
+			}
+		}
+	}
+
+	line = i_stream_next_line(conn->input);
+	if (line != NULL) {
+		if (strncmp(line, "USER\t1\t", 7) == 0) {
+			auth_parse_input(conn, line + 7);
+		} else if (strcmp(line, "NOTFOUND\t1") == 0)
+			conn->return_value = 0;
+		else if (strncmp(line, "FAIL\t1", 6) == 0) {
+			i_error("Auth lookup returned failure");
+			conn->return_value = -1;
+		} else if (strncmp(line, "CUID\t", 5) == 0) {
+			i_error("%s is an auth client socket. "
+				"It should be a master socket.",
+				conn->auth_socket);
+			conn->return_value = -1;
+		} else {
+			i_error("BUG: Unexpected input from auth master: %s",
+				line);
+		}
+		auth_connection_close(conn);
+	}
+}
+
+
+static void auth_client_timeout(struct auth_connection *conn)
+{
+	if (!conn->handshaked)
+		i_error("Connecting to dovecot-auth timed out");
+	else
+		i_error("User request from dovecot-auth timed out");
+	auth_connection_close(conn);
+}
+
+int auth_master_user_lookup(struct auth_connection *conn,
+			    const char *user,
+			    const char *service,
+			    pool_t pool,
+			    struct auth_user_reply *reply_r)
+{
+	if (conn == NULL)
+		return -1;
+
+	conn->user = user;
+	conn->return_value = -1;
+	conn->to = timeout_add(1000*AUTH_REQUEST_TIMEOUT,
+			       auth_client_timeout, conn);
+	conn->pool = pool;
+	conn->user_reply = reply_r;
+
+	o_stream_send_str(conn->output,
+			  t_strconcat("VERSION\t1\t0\n"
+				      "USER\t1\t", user, "\t"
+				      "service=", service, "\n",
+				      NULL));
+
+	io_loop_run(conn->ioloop);
+	return conn->return_value;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-auth/auth-master.h	Fri Oct 24 16:06:07 2008 +0200
@@ -0,0 +1,21 @@
+#ifndef AUTH_MASTER_H
+#define AUTH_MASTER_H
+
+struct auth_user_reply {
+	uid_t uid;
+	gid_t gid;
+	const char *home, *chroot;
+	ARRAY_TYPE(string) *extra_fields;
+};
+
+struct auth_connection *auth_master_init(const char *auth_socket, bool debug);
+void auth_master_deinit(struct auth_connection *conn);
+
+/* Returns -1 = error, 0 = user not found, 1 = ok */
+int auth_master_user_lookup(struct auth_connection *conn,
+			    const char *user,
+			    const char *service,
+			    pool_t pool,
+			    struct auth_user_reply *reply_r);
+
+#endif