]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Dovecot can now connect to externally running dovecot-auth.
authorTimo Sirainen <tss@iki.fi>
Wed, 23 Jun 2004 17:50:43 +0000 (20:50 +0300)
committerTimo Sirainen <tss@iki.fi>
Wed, 23 Jun 2004 17:50:43 +0000 (20:50 +0300)
--HG--
branch : HEAD

dovecot-example.conf
src/auth/auth-master-connection.c
src/auth/auth-master-connection.h
src/auth/auth-master-interface.h
src/auth/common.h
src/auth/main.c
src/master/auth-process.c
src/master/master-settings.c
src/master/master-settings.h

index d0a56ee564cc508dc49d748f06b589019c4aa52d..a359fcf3b701753d0533529fdc7c278f0e042b89 100644 (file)
@@ -484,3 +484,43 @@ auth default {
 #  passdb = passwd-file /etc/passwd.imap
 #  user = dovecot-auth
 #}
+
+# It's possible to export the authentication interface to other programs,
+# for example SMTP server which supports talking to Dovecot. Client socket
+# handles the actual authentication - you give it a username and password
+# and it returns OK or failure. So it's pretty safe to allow anyone access to
+# it. Master socket is used to a) query if given client was successfully
+# authenticated, b) userdb lookups.
+
+# listener sockets will be created by Dovecot's master process using the
+# settings given inside the auth section
+#auth default_with_listener {
+#  mechanisms = plain
+#  passdb = passwd
+#  userdb = pam
+#  socket listen {
+#    master {
+#      path = /var/run/dovecot/auth-master
+#      #mode = 0600
+#      # Default user/group is the one who started dovecot-auth (root)
+#      #user = 
+#      #group = 
+#    }
+#    client {
+#      path = /var/run/dovecot-auth-client
+#      mode = 0660
+#    }
+#  }
+#}
+
+# connect sockets are assumed to be already running, Dovecot's master
+# process only tries to connect to them. They don't need any other settings
+# than path for the master socket, as the configuration is done elsewhere.
+# Note that the client sockets must exist in login_dir.
+#auth external {
+#  socket connect {
+#    master {
+#      path = /var/run/dovecot/auth-master
+#    }
+#  }
+#}
index 63f35864369a43d4bb3e0c18cc5f0b73f6c063df..f56b4d0f688ca4a9544c7a157e11843af1988e60 100644 (file)
 static struct auth_master_reply failure_reply =
 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
 
+struct auth_listener {
+       struct auth_master_connection *master;
+       int client_listener;
+       int fd;
+       char *path;
+       struct io *io;
+};
+
 struct master_userdb_request {
        struct auth_master_connection *conn;
        unsigned int tag;
 };
 
+static void auth_master_connection_close(struct auth_master_connection *conn);
 static int auth_master_connection_unref(struct auth_master_connection *conn);
 
 static size_t reply_add(buffer_t *buf, const char *str)
@@ -89,7 +98,7 @@ static void master_send_reply(struct auth_master_connection *conn,
                ret = o_stream_send(conn->output, reply, reply_size);
                if (ret < 0) {
                        /* master died, kill ourself too */
-                       io_loop_stop(ioloop);
+                       auth_master_connection_close(conn);
                        break;
                }
 
@@ -100,7 +109,7 @@ static void master_send_reply(struct auth_master_connection *conn,
                i_warning("Master transmit buffer full, blocking..");
                if (o_stream_flush(conn->output) < 0) {
                        /* transmit error, probably master died */
-                       io_loop_stop(ioloop);
+                       auth_master_connection_close(conn);
                        break;
                }
        }
@@ -169,7 +178,7 @@ static void master_input(void *context)
                          sizeof(conn->request_buf) - conn->request_pos);
        if (ret < 0) {
                /* master died, kill ourself too */
-               io_loop_stop(ioloop);
+                auth_master_connection_close(conn);
                return;
        }
 
@@ -224,8 +233,23 @@ static void master_get_handshake_reply(struct auth_master_connection *master)
        master->handshake_reply = buffer_free_without_data(buf);
 }
 
+static void
+auth_master_connection_set_fd(struct auth_master_connection *conn, int fd)
+{
+       if (conn->output != NULL)
+               o_stream_unref(conn->output);
+       if (conn->io != NULL)
+               io_remove(conn->io);
+
+       conn->output = o_stream_create_file(fd, default_pool,
+                                           MAX_OUTBUF_SIZE, FALSE);
+       conn->io = io_add(fd, IO_READ, master_input, conn);
+
+       conn->fd = fd;
+}
+
 struct auth_master_connection *
-auth_master_connection_new(int fd, unsigned int pid)
+auth_master_connection_create(int fd, unsigned int pid)
 {
        struct auth_master_connection *conn;
 
@@ -235,42 +259,54 @@ auth_master_connection_new(int fd, unsigned int pid)
        conn->fd = fd;
        conn->listeners_buf =
                buffer_create_dynamic(default_pool, 64, (size_t)-1);
-       if (fd != -1) {
-               conn->output = o_stream_create_file(fd, default_pool,
-                                                   MAX_OUTBUF_SIZE, FALSE);
-               conn->io = io_add(fd, IO_READ, master_input, conn);
-       }
+       if (fd != -1)
+                auth_master_connection_set_fd(conn, fd);
        master_get_handshake_reply(conn);
        return conn;
 }
 
 void auth_master_connection_send_handshake(struct auth_master_connection *conn)
 {
+       struct auth_master_handshake_reply reply;
+
        /* just a note to master that we're ok. if we die before,
           master should shutdown itself. */
-       if (conn->output != NULL)
-               o_stream_send(conn->output, "O", 1);
+       if (conn->output != NULL) {
+               memset(&reply, 0, sizeof(reply));
+               reply.server_pid = conn->pid;
+               o_stream_send(conn->output, &reply, sizeof(reply));
+       }
+}
+
+static void auth_master_connection_close(struct auth_master_connection *conn)
+{
+       if (!standalone)
+               io_loop_stop(ioloop);
+
+       if (close(conn->fd) < 0)
+               i_error("close(): %m");
+       conn->fd = -1;
+
+       o_stream_close(conn->output);
+       conn->output = NULL;
+
+       io_remove(conn->io);
+       conn->io = NULL;
 }
 
-void auth_master_connection_free(struct auth_master_connection *conn)
+void auth_master_connection_destroy(struct auth_master_connection *conn)
 {
-       struct auth_client_listener **l;
+       struct auth_listener **l;
        size_t i, size;
 
        if (conn->destroyed)
                return;
        conn->destroyed = TRUE;
 
-       if (conn->fd != -1) {
-               if (close(conn->fd) < 0)
-                       i_error("close(): %m");
-               conn->fd = -1;
+       auth_client_connections_deinit(conn);
 
-               o_stream_close(conn->output);
-
-               io_remove(conn->io);
-               conn->io = NULL;
-       }
+       if (conn->fd != -1)
+               auth_master_connection_close(conn);
 
        l = buffer_get_modifyable_data(conn->listeners_buf, &size);
        size /= sizeof(*l);
@@ -303,7 +339,7 @@ static int auth_master_connection_unref(struct auth_master_connection *conn)
 
 static void auth_accept(void *context)
 {
-       struct auth_client_listener *l = context;
+       struct auth_listener *l = context;
        int fd;
 
        fd = net_accept(l->fd, NULL, NULL);
@@ -312,17 +348,24 @@ static void auth_accept(void *context)
                        i_fatal("accept() failed: %m");
        } else {
                net_set_nonblock(fd, TRUE);
-               (void)auth_client_connection_create(l->master, fd);
+               if (l->client_listener)
+                       (void)auth_client_connection_create(l->master, fd);
+               else {
+                       /* we'll just replace the previous master.. */
+                       auth_master_connection_set_fd(l->master, fd);
+                        auth_master_connection_send_handshake(l->master);
+               }
        }
 }
 
 void auth_master_connection_add_listener(struct auth_master_connection *conn,
-                                        int fd, const char *path)
+                                        int fd, const char *path, int client)
 {
-       struct auth_client_listener *l;
+       struct auth_listener *l;
 
-       l = i_new(struct auth_client_listener, 1);
+       l = i_new(struct auth_listener, 1);
        l->master = conn;
+       l->client_listener = client;
        l->fd = fd;
        l->path = i_strdup(path);
        l->io = io_add(fd, IO_READ, auth_accept, l);
index 9f06748b4275032dcc5a261bde6c034fbbbf7fd5..240f835989536cb5f0bcb62372cf6bef3b387bc2 100644 (file)
@@ -22,21 +22,14 @@ struct auth_master_connection {
        unsigned int destroyed:1;
 };
 
-struct auth_client_listener {
-       struct auth_master_connection *master;
-       int fd;
-       char *path;
-       struct io *io;
-};
-
 #define AUTH_MASTER_IS_DUMMY(master) (master->fd == -1)
 
 struct auth_master_connection *
-auth_master_connection_new(int fd, unsigned int pid);
+auth_master_connection_create(int fd, unsigned int pid);
 void auth_master_connection_send_handshake(struct auth_master_connection *conn);
-void auth_master_connection_free(struct auth_master_connection *conn);
+void auth_master_connection_destroy(struct auth_master_connection *conn);
 
 void auth_master_connection_add_listener(struct auth_master_connection *conn,
-                                        int fd, const char *path);
+                                        int fd, const char *path, int client);
 
 #endif
index 9a247442245da300ebbfd7b1cf67ef13cf8eb245..13b61d7fab24a1897f1a8adea1a59119ac2502ca 100644 (file)
@@ -3,6 +3,11 @@
 
 #define AUTH_MASTER_MAX_REPLY_DATA_SIZE 4096
 
+/* Server -> Master */
+struct auth_master_handshake_reply {
+       unsigned int server_pid;
+};
+
 struct auth_master_request {
        unsigned int tag;
 
index 99b3ad5fecdbd5dffeb04a311eb550a726116b3b..ab119093754266125c1ec1f99295e7170407b3f2 100644 (file)
@@ -8,5 +8,6 @@
 
 extern struct ioloop *ioloop;
 extern int verbose, verbose_debug;
+extern int standalone;
 
 #endif
index 95edcf02bfb9e8bbdbd82fc53203b562513ca157..a4593bc1230fdc23aa030b88e046189870f5342e 100644 (file)
 #include "auth-master-connection.h"
 #include "auth-client-connection.h"
 
+#include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <syslog.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/stat.h>
 
 struct ioloop *ioloop;
 int verbose = FALSE, verbose_debug = FALSE;
+int standalone = FALSE;
 
 static buffer_t *masters_buf;
 
@@ -64,26 +69,120 @@ static void drop_privileges(void)
        restrict_access_by_env(FALSE);
 }
 
-static void master_add_unix_listeners(struct auth_master_connection *master,
-                                     const char *sockets_list)
+static uid_t get_uid(const char *user)
 {
-       const char *const *sockets;
-       int fd;
-
-       sockets = t_strsplit(sockets_list, ":");
-       while (*sockets != NULL) {
-               fd = net_listen_unix(*sockets);
-               if (fd == -1) {
-                       i_fatal("net_listen_unix(%s) failed: %m",
-                               *sockets);
+       struct passwd *pw;
+
+       if (user == NULL)
+               return (uid_t)-1;
+
+       if ((pw = getpwnam(user)) == NULL)
+               i_fatal("User doesn't exist: %s", user);
+       return pw->pw_uid;
+}
+
+static gid_t get_gid(const char *group)
+{
+       struct group *gr;
+
+       if (group == NULL)
+               return (gid_t)-1;
+
+       if ((gr = getgrnam(group)) == NULL)
+               i_fatal("Group doesn't exist: %s", group);
+       return gr->gr_gid;
+}
+
+static int create_unix_listener(const char *env)
+{
+       const char *path, *mode, *user, *group;
+       mode_t old_umask;
+       unsigned int mask;
+       uid_t uid;
+       gid_t gid;
+       int fd, i;
+
+       path = getenv(env);
+       if (path == NULL)
+               return -1;
+
+       mode = getenv(t_strdup_printf("%s_MODE", env));
+       if (mode == NULL)
+               mask = 0177; /* default to 0600 */
+       else {
+               if (sscanf(mode, "%o", &mask) != 1)
+                       i_fatal("%s: Invalid mode %s", env, mode);
+               mask = (mask ^ 0777) & 0777;
+       }
+
+       old_umask = umask(mask);
+       for (i = 0; i < 5; i++) {
+               fd = net_listen_unix(path);
+               if (fd != -1)
+                       break;
+
+               if (errno != EADDRINUSE)
+                       i_fatal("net_listen_unix(%s) failed: %m", path);
+
+               /* see if it really exists */
+               if (net_connect_unix(path) != -1 || errno != ECONNREFUSED)
+                       i_fatal("Socket already exists: %s", path);
+
+               /* delete and try again */
+               if (unlink(path) < 0)
+                       i_fatal("unlink(%s) failed: %m", path);
+       }
+       umask(old_umask);
+
+       user = getenv(t_strdup_printf("%s_USER", env));
+       group = getenv(t_strdup_printf("%s_GROUP", env));
+
+       uid = get_uid(user); gid = get_gid(group);
+       if (chown(path, uid, gid) < 0) {
+               i_fatal("chown(%s, %s, %s) failed: %m",
+                       path, dec2str(uid), dec2str(gid));
+       }
+
+       return fd;
+}
+
+static void add_extra_listeners(void)
+{
+       struct auth_master_connection *master;
+       const char *str, *client_path, *master_path;
+       int client_fd, master_fd;
+       unsigned int i;
+
+       for (i = 1;; i++) {
+               t_push();
+               client_path = getenv(t_strdup_printf("AUTH_%u", i));
+               master_path = getenv(t_strdup_printf("AUTH_%u_MASTER", i));
+               if (client_path == NULL && master_path == NULL) {
+                       t_pop();
+                       break;
                }
 
-               auth_master_connection_add_listener(master, fd, *sockets);
-               sockets++;
+               str = t_strdup_printf("AUTH_%u", i);
+               client_fd = create_unix_listener(str);
+               str = t_strdup_printf("AUTH_%u_MASTER", i);
+               master_fd = create_unix_listener(str);
+
+               master = auth_master_connection_create(-1, getpid());
+               if (master_fd != -1) {
+                       auth_master_connection_add_listener(master, master_fd,
+                                                           master_path, FALSE);
+               }
+               if (client_fd != -1) {
+                       auth_master_connection_add_listener(master, client_fd,
+                                                           client_path, TRUE);
+               }
+               auth_client_connections_init(master);
+               buffer_append(masters_buf, &master, sizeof(master));
+               t_pop();
        }
 }
 
-static void main_init(void)
+static void main_init(int nodaemon)
 {
        struct auth_master_connection *master, **master_p;
        size_t i, size;
@@ -103,47 +202,45 @@ static void main_init(void)
        masters_buf = buffer_create_dynamic(default_pool, 64, (size_t)-1);
 
        env = getenv("AUTH_PROCESS");
-       if (env == NULL) {
+       standalone = env == NULL;
+       if (standalone) {
                /* starting standalone */
-               env = getenv("AUTH_SOCKETS");
-               if (env == NULL)
-                       i_fatal("AUTH_SOCKETS environment not set");
-
-               switch (fork()) {
-               case -1:
-                       i_fatal("fork() failed: %m");
-               case 0:
-                       break;
-               default:
-                       exit(0);
+               if (getenv("AUTH_1") == NULL) {
+                       i_fatal("dovecot-auth is usually started through "
+                               "dovecot master process. If you wish to run "
+                               "it standalone, you'll need to set AUTH_* "
+                               "environment variables (AUTH_1 isn't set).");
                }
 
-               if (setsid() < 0)
-                       i_fatal("setsid() failed: %m");
-
-               if (chdir("/") < 0)
-                       i_fatal("chdir(/) failed: %m");
+               if (!nodaemon) {
+                       switch (fork()) {
+                       case -1:
+                               i_fatal("fork() failed: %m");
+                       case 0:
+                               break;
+                       default:
+                               exit(0);
+                       }
+
+                       if (setsid() < 0)
+                               i_fatal("setsid() failed: %m");
+
+                       if (chdir("/") < 0)
+                               i_fatal("chdir(/) failed: %m");
+               }
        } else {
                pid = atoi(env);
                if (pid == 0)
                        i_fatal("AUTH_PROCESS can't be 0");
 
-               master = auth_master_connection_new(MASTER_SOCKET_FD, pid);
+               master = auth_master_connection_create(MASTER_SOCKET_FD, pid);
                auth_master_connection_add_listener(master, LOGIN_LISTEN_FD,
-                                                   NULL);
+                                                   NULL, TRUE);
                auth_client_connections_init(master);
                buffer_append(masters_buf, &master, sizeof(master));
-
-               /* accept also alternative listeners under dummy master */
-               env = getenv("AUTH_SOCKETS");
        }
 
-       if (env != NULL && *env != '\0') {
-               master = auth_master_connection_new(-1, 0);
-               master_add_unix_listeners(master, env);
-               auth_client_connections_init(master);
-               buffer_append(masters_buf, &master, sizeof(master));
-       }
+       add_extra_listeners();
 
        /* everything initialized, notify masters that all is well */
        master_p = buffer_get_modifyable_data(masters_buf, &size);
@@ -164,10 +261,8 @@ static void main_deinit(void)
 
        master = buffer_get_modifyable_data(masters_buf, &size);
        size /= sizeof(*master);
-       for (i = 0; i < size; i++) {
-               auth_client_connections_deinit(master[i]);
-               auth_master_connection_free(master[i]);
-       }
+       for (i = 0; i < size; i++)
+               auth_master_connection_destroy(master[i]);
 
         password_schemes_deinit();
        passdb_deinit();
@@ -179,10 +274,11 @@ static void main_deinit(void)
        closelog();
 }
 
-int main(int argc __attr_unused__, char *argv[] __attr_unused__)
+int main(int argc, char *argv[])
 {
 #ifdef DEBUG
-        fd_debug_verify_leaks(4, 1024);
+       if (getenv("GDB") == NULL)
+               fd_debug_verify_leaks(4, 1024);
 #endif
        /* NOTE: we start rooted, so keep the code minimal until
           restrict_access_by_env() is called */
@@ -191,7 +287,7 @@ int main(int argc __attr_unused__, char *argv[] __attr_unused__)
 
        ioloop = io_loop_create(system_pool);
 
-       main_init();
+       main_init(argc > 1 && strcmp(argv[1], "-F") == 0);
         io_loop_run(ioloop);
        main_deinit();
 
index a8665781c4803a85732b8b26b8892faf0defc490..74a27ba04af4d591e7c035ba9f67260db4318b33 100644 (file)
@@ -45,6 +45,7 @@ struct auth_process {
 
        struct hash_table *requests;
 
+       unsigned int external:1;
        unsigned int initialized:1;
        unsigned int in_auth_reply:1;
 };
@@ -152,16 +153,21 @@ static void auth_process_input(void *context)
        }
 
        if (!p->initialized) {
+               struct auth_master_handshake_reply rec;
+
                data = i_stream_get_data(p->input, &size);
-               i_assert(size > 0);
+               if (size < sizeof(rec))
+                       return;
+
+               memcpy(&rec, data, sizeof(rec));
+               i_stream_skip(p->input, sizeof(rec));
 
-               if (data[0] != 'O') {
+               if (rec.server_pid == 0) {
                        i_fatal("Auth process sent invalid initialization "
                                "notification");
                }
 
-               i_stream_skip(p->input, 1);
-
+               p->pid = rec.server_pid;
                p->initialized = TRUE;
        }
 
@@ -197,7 +203,8 @@ auth_process_new(pid_t pid, int fd, struct auth_process_group *group)
 {
        struct auth_process *p;
 
-       PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_AUTH);
+       if (pid != 0)
+               PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_AUTH);
 
        p = i_new(struct auth_process, 1);
        p->group = group;
@@ -223,7 +230,7 @@ static void auth_process_destroy(struct auth_process *p)
        void *key, *value;
        struct auth_process **pos;
 
-       if (!p->initialized && io_loop_is_running(ioloop)) {
+       if (!p->initialized && io_loop_is_running(ioloop) && !p->external) {
                i_error("Auth process died too early - shutting down");
                io_loop_stop(ioloop);
        }
@@ -250,14 +257,54 @@ static void auth_process_destroy(struct auth_process *p)
        i_free(p);
 }
 
-static pid_t create_auth_process(struct auth_process_group *group)
+static void
+socket_settings_env_put(const char *env_base, struct socket_settings *set)
+{
+       if (env_base == NULL)
+               return;
+
+       env_put(t_strdup_printf("%s_PATH=%s", env_base, set->path));
+       if (set->mode != 0)
+               env_put(t_strdup_printf("%s_MODE=%u", env_base, set->mode));
+       if (set->user != NULL)
+               env_put(t_strdup_printf("%s_USER=%s", env_base, set->user));
+       if (set->group != NULL)
+               env_put(t_strdup_printf("%s_GROUP=%s", env_base, set->group));
+}
+
+static int connect_auth_socket(struct auth_process_group *group,
+                              const char *path)
+{
+       struct auth_process *auth;
+       int fd;
+
+       fd = net_connect_unix(path);
+       if (fd == -1) {
+               i_error("net_connect_unix(%s) failed: %m", path);
+               return -1;
+       }
+
+       net_set_nonblock(fd, TRUE);
+       fd_close_on_exec(fd, TRUE);
+       auth = auth_process_new(0, fd, group);
+       auth->external = TRUE;
+       return 0;
+}
+
+static int create_auth_process(struct auth_process_group *group)
 {
        static char *argv[] = { NULL, NULL };
-       const char *prefix;
+       struct auth_socket_settings *as;
+       const char *prefix, *str;
        struct log_io *log;
        pid_t pid;
        int fd[2], log_fd, i;
 
+       /* see if this is a connect socket */
+       as = group->set->sockets;
+       if (as != NULL && strcmp(as->type, "connect") == 0)
+               return connect_auth_socket(group, as->master.path);
+
        /* create communication to process with a socket pair */
        if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
                i_error("socketpair() failed: %m");
@@ -290,7 +337,7 @@ static pid_t create_auth_process(struct auth_process_group *group)
                auth_process_new(pid, fd[0], group);
                (void)close(fd[1]);
                (void)close(log_fd);
-               return pid;
+               return 0;
        }
 
        prefix = t_strdup_printf("master-auth(%s): ", group->set->name);
@@ -337,7 +384,16 @@ static pid_t create_auth_process(struct auth_process_group *group)
        env_put(t_strconcat("USERNAME_CHARS=", group->set->username_chars, NULL));
        env_put(t_strconcat("ANONYMOUS_USERNAME=",
                            group->set->anonymous_username, NULL));
-       env_put(t_strconcat("AUTH_SOCKETS=", group->set->extra_sockets));
+
+       for (as = group->set->sockets, i = 1; as != NULL; as = as->next, i++) {
+               if (strcmp(as->type, "listen") != 0)
+                       continue;
+
+               str = t_strdup_printf("AUTH_%u", i);
+               socket_settings_env_put(str, &as->client);
+               socket_settings_env_put(t_strconcat(str, "_MASTER", NULL),
+                                       &as->master);
+       }
 
        if (group->set->use_cyrus_sasl)
                env_put("USE_CYRUS_SASL=1");
@@ -390,6 +446,13 @@ static void auth_process_group_create(struct auth_settings *auth_set)
        group = i_new(struct auth_process_group, 1);
        group->set = auth_set;
 
+       group->next = process_groups;
+       process_groups = group;
+
+       if (auth_set->sockets != NULL &&
+           strcmp(auth_set->sockets->type, "connect") == 0)
+               return;
+
        /* create socket for listening auth requests from login */
        path = t_strconcat(auth_set->parent->defaults->login_dir, "/",
                           auth_set->name, NULL);
@@ -410,9 +473,6 @@ static void auth_process_group_create(struct auth_settings *auth_set)
                        path, dec2str(master_uid),
                        dec2str(auth_set->parent->login_gid));
        }
-
-       group->next = process_groups;
-       process_groups = group;
 }
 
 static void auth_process_group_destroy(struct auth_process_group *group)
index 88c2f9841ec236f96d319b5f58af1a8442f41d6f..47bc75e423f561e9360f68e39c633407a9c4f21c 100644 (file)
@@ -16,7 +16,9 @@ enum settings_type {
        SETTINGS_TYPE_ROOT,
        SETTINGS_TYPE_SERVER,
        SETTINGS_TYPE_AUTH,
-        SETTINGS_TYPE_NAMESPACE
+       SETTINGS_TYPE_AUTH_SOCKET,
+        SETTINGS_TYPE_NAMESPACE,
+       SETTINGS_TYPE_SOCKET
 };
 
 struct settings_parse_ctx {
@@ -25,6 +27,8 @@ struct settings_parse_ctx {
 
        struct server_settings *root, *server;
        struct auth_settings *auth;
+       struct socket_settings *socket;
+       struct auth_socket_settings *auth_socket;
         struct namespace_settings *namespace;
 
        int level;
@@ -142,7 +146,29 @@ static struct setting_def auth_setting_defs[] = {
 
        DEF(SET_INT, count),
        DEF(SET_INT, process_size),
-       DEF(SET_STR, extra_sockets),
+
+       { 0, NULL, 0 }
+};
+
+#undef DEF
+#define DEF(type, name) \
+       { type, #name, offsetof(struct socket_settings, name) }
+
+static struct setting_def socket_setting_defs[] = {
+       DEF(SET_STR, path),
+       DEF(SET_INT, mode),
+       DEF(SET_STR, user),
+       DEF(SET_STR, group),
+
+       { 0, NULL, 0 }
+};
+
+#undef DEF
+#define DEF(type, name) \
+       { type, #name, offsetof(struct auth_socket_settings, name) }
+
+static struct setting_def auth_socket_setting_defs[] = {
+       DEF(SET_STR, type),
 
        { 0, NULL, 0 }
 };
@@ -281,11 +307,11 @@ struct auth_settings default_auth_settings = {
 
        MEMBER(count) 1,
        MEMBER(process_size) 256,
-       MEMBER(extra_sockets) NULL,
 
        /* .. */
        MEMBER(uid) 0,
-       MEMBER(gid) 0
+       MEMBER(gid) 0,
+       MEMBER(sockets) NULL
 };
 
 static pool_t settings_pool, settings2_pool;
@@ -406,6 +432,22 @@ static int settings_is_active(struct settings *set)
        return TRUE;
 }
 
+static int settings_have_connect_sockets(struct settings *set)
+{
+       struct auth_settings *auth;
+       struct server_settings *server;
+
+       for (server = set->server; server != NULL; server = server->next) {
+               for (auth = server->auths; auth != NULL; auth = auth->next) {
+                       if (auth->sockets != NULL &&
+                           strcmp(auth->sockets->type, "connect") == 0)
+                               return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
 static int settings_verify(struct settings *set)
 {
        const char *dir;
@@ -484,10 +526,15 @@ static int settings_verify(struct settings *set)
                          PKG_RUNDIR);
        }
 
-       /* wipe out contents of login directory, if it exists */
-       if (unlink_directory(set->login_dir, FALSE) < 0) {
-               i_error("unlink_directory() failed for %s: %m", set->login_dir);
-               return FALSE;
+       /* wipe out contents of login directory, if it exists.
+          except if we're using external authentication - then we would
+          otherwise wipe existing auth sockets */
+       if (!settings_have_connect_sockets(set)) {
+               if (unlink_directory(set->login_dir, FALSE) < 0) {
+                       i_error("unlink_directory() failed for %s: %m",
+                               set->login_dir);
+                       return FALSE;
+               }
        }
 
        if (safe_mkdir(set->login_dir, 0750,
@@ -570,6 +617,44 @@ parse_new_auth(struct server_settings *server, const char *name,
        return auth_settings_new(server, name);
 }
 
+static struct auth_socket_settings *
+auth_socket_settings_new(struct auth_settings *auth, const char *type)
+{
+       struct auth_socket_settings *as, **as_p;
+
+       as = p_new(settings_pool, struct auth_socket_settings, 1);
+
+       as->parent = auth;
+       as->type = str_lcase(p_strdup(settings_pool, type));
+
+       as_p = &auth->sockets;
+       while (*as_p != NULL)
+               as_p = &(*as_p)->next;
+       *as_p = as;
+
+       return as;
+}
+
+static struct auth_socket_settings *
+parse_new_auth_socket(struct auth_settings *auth, const char *name,
+                     const char **errormsg)
+{
+       if (strcmp(name, "connect") != 0 && strcmp(name, "listen") != 0) {
+               *errormsg = "Unknown auth socket type";
+               return NULL;
+       }
+
+       if ((auth->sockets != NULL && strcmp(name, "connect") == 0) ||
+           (auth->sockets != NULL &&
+            strcmp(auth->sockets->type, "listen") == 0)) {
+               *errormsg = "With connect auth socket no other sockets "
+                       "can be used in same auth section";
+               return NULL;
+       }
+
+       return auth_socket_settings_new(auth, name);
+}
+
 static struct namespace_settings *
 namespace_settings_new(struct server_settings *server, const char *type)
 {
@@ -657,10 +742,18 @@ static const char *parse_setting(const char *key, const char *value,
                        key += 5;
                return parse_setting_from_defs(settings_pool, auth_setting_defs,
                                               ctx->auth, key, value);
+       case SETTINGS_TYPE_AUTH_SOCKET:
+               return parse_setting_from_defs(settings_pool,
+                                              auth_socket_setting_defs,
+                                              ctx->auth_socket, key, value);
        case SETTINGS_TYPE_NAMESPACE:
                return parse_setting_from_defs(settings_pool,
                                               namespace_setting_defs,
                                               ctx->namespace, key, value);
+       case SETTINGS_TYPE_SOCKET:
+               return parse_setting_from_defs(settings_pool,
+                                              socket_setting_defs,
+                                              ctx->socket, key, value);
        }
 
        i_unreached();
@@ -705,12 +798,20 @@ static int parse_section(const char *type, const char *name, void *context,
 
        if (type == NULL) {
                /* section closing */
-               if (ctx->level > 0) {
-                       ctx->level--;
+               if (--ctx->level > 0) {
+                       ctx->type = ctx->parent_type;
                        ctx->protocol = MAIL_PROTOCOL_ANY;
+
+                       switch (ctx->type) {
+                       case SETTINGS_TYPE_AUTH_SOCKET:
+                               ctx->parent_type = SETTINGS_TYPE_AUTH;
+                               break;
+                       default:
+                               ctx->parent_type = SETTINGS_TYPE_ROOT;
+                               break;
+                       }
                } else {
-                       ctx->type = ctx->parent_type;
-                       ctx->parent_type = SETTINGS_TYPE_ROOT;
+                       ctx->type = SETTINGS_TYPE_ROOT;
                        ctx->server = ctx->root;
                        ctx->auth = &ctx->root->auth_defaults;
                        ctx->namespace = NULL;
@@ -718,15 +819,16 @@ static int parse_section(const char *type, const char *name, void *context,
                return TRUE;
        }
 
+       ctx->level++;
+       ctx->parent_type = ctx->type;
+
        if (strcmp(type, "server") == 0) {
                if (ctx->type != SETTINGS_TYPE_ROOT) {
                        *errormsg = "Server section not allowed here";
                        return FALSE;
                }
 
-               ctx->parent_type = ctx->type;
                ctx->type = SETTINGS_TYPE_SERVER;
-
                ctx->server = create_new_server(name, ctx->server->imap,
                                                ctx->server->pop3);
                 server = ctx->root;
@@ -739,7 +841,7 @@ static int parse_section(const char *type, const char *name, void *context,
        if (strcmp(type, "protocol") == 0) {
                if ((ctx->type != SETTINGS_TYPE_ROOT &&
                     ctx->type != SETTINGS_TYPE_SERVER) ||
-                   ctx->level != 0) {
+                   ctx->level != 1) {
                        *errormsg = "Protocol section not allowed here";
                        return FALSE;
                }
@@ -752,7 +854,6 @@ static int parse_section(const char *type, const char *name, void *context,
                        *errormsg = "Unknown protocol name";
                        return FALSE;
                }
-               ctx->level++;
                return TRUE;
        }
 
@@ -768,6 +869,28 @@ static int parse_section(const char *type, const char *name, void *context,
                return ctx->auth != NULL;
        }
 
+       if (ctx->type == SETTINGS_TYPE_AUTH &&
+           strcmp(type, "socket") == 0) {
+               ctx->type = SETTINGS_TYPE_AUTH_SOCKET;
+               ctx->auth_socket = parse_new_auth_socket(ctx->auth,
+                                                        name, errormsg);
+               return ctx->auth_socket != NULL;
+       }
+
+       if (ctx->type == SETTINGS_TYPE_AUTH_SOCKET) {
+               ctx->type = SETTINGS_TYPE_SOCKET;
+
+               if (strcmp(type, "master") == 0) {
+                       ctx->socket = &ctx->auth_socket->master;
+                       return TRUE;
+               }
+
+               if (strcmp(type, "client") == 0) {
+                       ctx->socket = &ctx->auth_socket->client;
+                       return TRUE;
+               }
+       }
+
        if (strcmp(type, "namespace") == 0) {
                if (ctx->type != SETTINGS_TYPE_ROOT &&
                    ctx->type != SETTINGS_TYPE_SERVER) {
index 7d8199c72f8e44573dbb1454850714007df89ed3..6907e1613512df2614c4354b95ff502ff35c3916 100644 (file)
@@ -98,6 +98,22 @@ struct settings {
        int listen_fd, ssl_listen_fd;
 };
 
+struct socket_settings {
+       const char *path;
+       unsigned int mode;
+       const char *user;
+       const char *group;
+};
+
+struct auth_socket_settings {
+       struct auth_settings *parent;
+       struct auth_socket_settings *next;
+
+       const char *type;
+       struct socket_settings master;
+        struct socket_settings client;
+};
+
 struct auth_settings {
        struct server_settings *parent;
        struct auth_settings *next;
@@ -119,11 +135,11 @@ struct auth_settings {
 
        unsigned int count;
        unsigned int process_size;
-       const char *extra_sockets;
 
        /* .. */
        uid_t uid;
        gid_t gid;
+       struct auth_socket_settings *sockets;
 };
 
 struct namespace_settings {