Mercurial > dovecot > original-hg > dovecot-1.2
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