]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Added support to perform token-based service process authentication.
authorStephan Bosch <stephan@rename-it.nl>
Fri, 14 Sep 2012 18:48:45 +0000 (21:48 +0300)
committerStephan Bosch <stephan@rename-it.nl>
Fri, 14 Sep 2012 18:48:45 +0000 (21:48 +0300)
Creates hidden SASL method DOVECOT-TOKEN. This method is not available on
the normal auth login socket and thus never presented to clients.

Creates new auth socket type 'tokenlogin'. This otherwise normal login
socket only offers authentication using the DOVECOT-TOKEN mechanism.

Creates new token-login directory in base_dir to separate token logins from
normal logins. This directory is otherwise completely identical to the
normal login dir, i.e. it contains sockets for the service backends, used
to chroot login processes to, etc.

Makes default login socket configurable.

Performs some minor changes to src/login-common to build very sparse
protocols, e.g. avoid the need to implement methods that are not needed.

26 files changed:
src/auth/Makefile.am
src/auth/auth-client-connection.c
src/auth/auth-client-connection.h
src/auth/auth-master-connection.c
src/auth/auth-request-handler.c
src/auth/auth-request-handler.h
src/auth/auth-request.c
src/auth/auth-request.h
src/auth/auth-settings.c
src/auth/auth-token.c [new file with mode: 0644]
src/auth/auth-token.h [new file with mode: 0644]
src/auth/main.c
src/auth/mech-dovecot-token.c [new file with mode: 0644]
src/auth/mech.h
src/imap/main.c
src/lib-master/master-login-auth.c
src/lib-master/master-login-auth.h
src/lib-master/master-login.c
src/lib-master/master-login.h
src/lib-storage/mail-storage-service.c
src/lib-storage/mail-user.h
src/login-common/client-common-auth.c
src/login-common/client-common.c
src/login-common/login-common.h
src/login-common/main.c
src/master/master-settings.c

index 5e6efab95a912a09cd8046e662182cc815c423ed..e2e338ae4a663789732ea075384f6d8d4c8b2cbb 100644 (file)
@@ -71,6 +71,7 @@ auth_SOURCES = \
        auth-request-handler.c \
        auth-settings.c \
        auth-stream.c \
+       auth-token.c \
        auth-worker-client.c \
        auth-worker-server.c \
        db-checkpassword.c \
@@ -93,6 +94,7 @@ auth_SOURCES = \
        mech-rpa.c \
        mech-apop.c \
        mech-winbind.c \
+       mech-dovecot-token.c \
        passdb.c \
        passdb-blocking.c \
        passdb-bsdauth.c \
@@ -136,6 +138,7 @@ headers = \
        auth-request-handler.h \
        auth-settings.h \
        auth-stream.h \
+       auth-token.h \
        auth-worker-client.h \
        auth-worker-server.h \
        db-dict.h \
index 6a9b524077ea6fca2de42e8474a6cdd5be6b0cd2..138ae6f33b2476d2005c2156c4c3a53c958df313 100644 (file)
@@ -125,7 +125,7 @@ auth_client_input_cpid(struct auth_client_connection *conn, const char *args)
        /* handshake complete, we can now actually start serving requests */
         conn->refcount++;
        conn->request_handler =
-               auth_request_handler_create(auth_callback, conn,
+               auth_request_handler_create(conn->token_auth, auth_callback, conn,
                                            !conn->login_requests ? NULL :
                                            auth_master_request_callback);
        auth_request_handler_set(conn->request_handler, conn->connect_uid, pid);
@@ -294,10 +294,11 @@ static void auth_client_input(struct auth_client_connection *conn)
 }
 
 void auth_client_connection_create(struct auth *auth, int fd,
-                                  bool login_requests)
+                                  bool login_requests, bool token_auth)
 {
        static unsigned int connect_uid_counter = 0;
        struct auth_client_connection *conn;
+       const char *mechanisms;
        string_t *str;
 
        conn = i_new(struct auth_client_connection, 1);
@@ -305,6 +306,7 @@ void auth_client_connection_create(struct auth *auth, int fd,
        conn->refcount = 1;
        conn->connect_uid = ++connect_uid_counter;
        conn->login_requests = login_requests;
+       conn->token_auth = token_auth;
        random_fill(conn->cookie, sizeof(conn->cookie));
 
        conn->fd = fd;
@@ -317,11 +319,18 @@ void auth_client_connection_create(struct auth *auth, int fd,
 
        DLLIST_PREPEND(&auth_client_connections, conn);
 
+       if (token_auth) {
+               mechanisms = t_strconcat("MECH\t",
+                       mech_dovecot_token.mech_name, "\n", NULL);
+       } else {
+               mechanisms = str_c(auth->reg->handshake);
+       }
+
        str = t_str_new(128);
        str_printfa(str, "VERSION\t%u\t%u\n%sSPID\t%s\nCUID\t%u\nCOOKIE\t",
                     AUTH_CLIENT_PROTOCOL_MAJOR_VERSION,
                     AUTH_CLIENT_PROTOCOL_MINOR_VERSION,
-                   str_c(auth->reg->handshake), my_pid, conn->connect_uid);
+                   mechanisms, my_pid, conn->connect_uid);
        binary_to_hex_append(str, conn->cookie, sizeof(conn->cookie));
        str_append(str, "\nDONE\n");
 
index e9b66e5bd3df8fd4cde406dcee0d6551ba64f010..93a906d2cab994384b6676bcdfbc09dc6a133ea7 100644 (file)
@@ -20,10 +20,11 @@ struct auth_client_connection {
 
        unsigned int login_requests:1;
        unsigned int version_received:1;
+       unsigned int token_auth:1;
 };
 
 void auth_client_connection_create(struct auth *auth, int fd,
-                                  bool login_requests);
+                                  bool login_requests, bool token_auth);
 void auth_client_connection_destroy(struct auth_client_connection **conn);
 
 struct auth_client_connection *
index 8f3f100df76aea7f40b59f390bf1716d00ff6e01..88ef38efa4def6f17d5fa2fa3a1c1612af6210e9 100644 (file)
@@ -95,12 +95,12 @@ static bool
 master_input_request(struct auth_master_connection *conn, const char *args)
 {
        struct auth_client_connection *client_conn;
-       const char *const *list;
+       const char *const *list, *const *params;
        unsigned int id, client_pid, client_id;
        uint8_t cookie[MASTER_AUTH_COOKIE_SIZE];
        buffer_t buf;
 
-       /* <id> <client-pid> <client-id> <cookie> */
+       /* <id> <client-pid> <client-id> <cookie> [<parameters>] */
        list = t_strsplit_tab(args);
        if (str_array_length(list) < 4 ||
            str_to_uint(list[0], &id) < 0 ||
@@ -115,6 +115,7 @@ master_input_request(struct auth_master_connection *conn, const char *args)
                i_error("BUG: Master sent broken REQUEST cookie");
                return FALSE;
        }
+       params = list + 4;
 
        client_conn = auth_client_connection_lookup(client_pid);
        if (client_conn == NULL) {
@@ -128,7 +129,7 @@ master_input_request(struct auth_master_connection *conn, const char *args)
                o_stream_nsend_str(conn->output,
                                   t_strdup_printf("FAIL\t%u\n", id));
        } else if (!auth_request_handler_master_request(
-                       client_conn->request_handler, conn, id, client_id)) {
+                       client_conn->request_handler, conn, id, client_id, params)) {
                i_error("Master requested auth for non-login client %u",
                        client_pid);
                o_stream_nsend_str(conn->output,
index 0afb875537bcc57ed12dbc5b18f553fb6dd93df7..4e63e5ae62f5c8b35f4ba422faebf0a6b83a83c4 100644 (file)
@@ -6,11 +6,13 @@
 #include "aqueue.h"
 #include "base64.h"
 #include "hash.h"
+#include "network.h"
 #include "str.h"
 #include "str-sanitize.h"
 #include "master-interface.h"
 #include "auth-penalty.h"
 #include "auth-request.h"
+#include "auth-token.h"
 #include "auth-master-connection.h"
 #include "auth-request-handler.h"
 
@@ -31,6 +33,7 @@ struct auth_request_handler {
        auth_request_callback_t *master_callback;
 
        unsigned int destroyed:1;
+       unsigned int token_auth:1;
 };
 
 static ARRAY(struct auth_request *) auth_failures_arr;
@@ -41,8 +44,8 @@ static void auth_failure_timeout(void *context) ATTR_NULL(1);
 
 #undef auth_request_handler_create
 struct auth_request_handler *
-auth_request_handler_create(auth_request_callback_t *callback, void *context,
-                           auth_request_callback_t *master_callback)
+auth_request_handler_create(bool token_auth, auth_request_callback_t *callback,
+                           void *context, auth_request_callback_t *master_callback)
 {
        struct auth_request_handler *handler;
        pool_t pool;
@@ -56,6 +59,7 @@ auth_request_handler_create(auth_request_callback_t *callback, void *context,
        handler->callback = callback;
        handler->context = context;
        handler->master_callback = master_callback;
+       handler->token_auth = token_auth;
        return handler;
 }
 
@@ -461,13 +465,24 @@ bool auth_request_handler_auth_begin(struct auth_request_handler *handler,
                return FALSE;
        }
 
-       mech = mech_module_find(list[1]);
-       if (mech == NULL) {
-               /* unsupported mechanism */
-               i_error("BUG: Authentication client %u requested unsupported "
-                       "authentication mechanism %s", handler->client_pid,
-                       str_sanitize(list[1], MAX_MECH_NAME_LEN));
-               return FALSE;
+       if (handler->token_auth) {
+               mech = &mech_dovecot_token;
+               if (strcmp(list[1], mech->mech_name) != 0) {
+                       /* unsupported mechanism */
+                       i_error("BUG: Authentication client %u requested invalid "
+                               "authentication mechanism %s (DOVECOT-TOKEN required)",
+                               handler->client_pid, str_sanitize(list[1], MAX_MECH_NAME_LEN));
+                       return FALSE;
+               }
+       } else {                 
+               mech = mech_module_find(list[1]);
+               if (mech == NULL) {
+                       /* unsupported mechanism */
+                       i_error("BUG: Authentication client %u requested unsupported "
+                               "authentication mechanism %s", handler->client_pid,
+                               str_sanitize(list[1], MAX_MECH_NAME_LEN));
+                       return FALSE;
+               }
        }
 
        request = auth_request_new(mech);
@@ -664,8 +679,19 @@ static void userdb_callback(enum userdb_result result,
                        auth_stream_reply_add(request->userdb_reply,
                                              "anonymous", NULL);
                }
+
                auth_stream_reply_import(reply,
                        auth_stream_reply_export(request->userdb_reply));
+
+               /* generate auth_token when master service provided session_pid */
+               if (request->session_pid != (pid_t)-1) {
+                       const char *auth_token =
+                               auth_token_get(request->service,
+                                              dec2str(request->session_pid),
+                                              request->user,
+                                              request->session_id);
+                       auth_stream_reply_add(reply, "auth_token", auth_token);
+               }
                break;
        }
        handler->master_callback(reply, request->master);
@@ -675,13 +701,27 @@ static void userdb_callback(enum userdb_result result,
         auth_request_handler_unref(&handler);
 }
 
+static bool
+auth_master_request_failed(struct auth_request_handler *handler,
+                          struct auth_master_connection *master,
+                          struct auth_stream_reply *reply, unsigned int id)
+{
+       auth_stream_reply_add(reply, "FAIL", NULL);
+       auth_stream_reply_add(reply, NULL, dec2str(id));
+       if (handler->master_callback == NULL)
+               return FALSE;
+       handler->master_callback(reply, master);
+       return TRUE;
+}
+
 bool auth_request_handler_master_request(struct auth_request_handler *handler,
                                         struct auth_master_connection *master,
-                                        unsigned int id,
-                                        unsigned int client_id)
+                                        unsigned int id, unsigned int client_id,
+                                        const char *const *params)
 {
        struct auth_request *request;
        struct auth_stream_reply *reply;
+       struct net_unix_cred cred;
 
        reply = auth_stream_reply_init(pool_datastack_create());
 
@@ -689,17 +729,38 @@ bool auth_request_handler_master_request(struct auth_request_handler *handler,
        if (request == NULL) {
                i_error("Master request %u.%u not found",
                        handler->client_pid, client_id);
-               auth_stream_reply_add(reply, "FAIL", NULL);
-               auth_stream_reply_add(reply, NULL, dec2str(id));
-               if (handler->master_callback == NULL)
-                       return FALSE;
-               handler->master_callback(reply, master);
-               return TRUE;
+               return auth_master_request_failed(handler, master, reply, id);
        }
 
        auth_request_ref(request);
        auth_request_handler_remove(handler, request);
 
+       for (; *params != NULL; params++) {
+               const char *name, *param = strchr(*params, '=');
+
+               if (param == NULL) {
+                       name = *params;
+                       param = "";
+               } else {
+                       name = t_strdup_until(*params, param);
+                       param++;
+               }
+
+               (void)auth_request_import_master(request, name, param);
+       }
+
+       /* verify session pid if specified and possible */
+       if (request->session_pid != (pid_t)-1 &&
+           net_getunixcred(master->fd, &cred) == 0 &&
+           cred.pid != (pid_t)-1 && request->session_pid != cred.pid) {
+               i_error("Session pid %ld provided by master for request %u.%u "
+                       "did not match peer credentials (pid=%ld, uid=%ld)",
+                       (long)request->session_pid,
+                       handler->client_pid, client_id,
+                       (long)cred.pid, (long)cred.uid);
+               return auth_master_request_failed(handler, master, reply, id);
+       }
+
        if (request->state != AUTH_REQUEST_STATE_FINISHED ||
            !request->successful) {
                i_error("Master requested unfinished authentication request "
index 08e60f5d29ed80b6f69926248f3ce753d7f21259..7fcadb195584c9841d05d63dfeec910082a80aa4 100644 (file)
@@ -15,10 +15,10 @@ typedef void
 auth_request_callback_t(struct auth_stream_reply *reply, void *context);
 
 struct auth_request_handler *
-auth_request_handler_create(auth_request_callback_t *callback, void *context,
-                           auth_request_callback_t *master_callback);
-#define auth_request_handler_create(callback, context, master_callback)\
-         auth_request_handler_create( \
+auth_request_handler_create(bool token_auth, auth_request_callback_t *callback,
+                           void *context, auth_request_callback_t *master_callback);
+#define auth_request_handler_create(token_auth, callback, context, master_callback)\
+         auth_request_handler_create(token_auth, \
                (auth_request_callback_t *)callback, \
                (void *)((char*)context + \
                        CALLBACK_TYPECHECK(callback, void (*)( \
@@ -46,8 +46,8 @@ unsigned int
 auth_request_handler_get_request_count(struct auth_request_handler *handler);
 bool auth_request_handler_master_request(struct auth_request_handler *handler,
                                         struct auth_master_connection *master,
-                                        unsigned int id,
-                                        unsigned int client_id);
+                                        unsigned int id, unsigned int client_id,
+                                        const char *const *params);
 void auth_request_handler_cancel_request(struct auth_request_handler *handler,
                                         unsigned int client_id);
 
index 67f21fe98479039031e98bc34a7e70e61965878e..be605e89fa4663bc4af3d7ec08f16416a1d766e9 100644 (file)
@@ -56,6 +56,7 @@ auth_request_new(const struct mech_module *mech)
 
        request->refcount = 1;
        request->last_access = ioloop_time;
+       request->session_pid = (pid_t)-1;
 
        request->set = global_auth_settings;
        request->mech = mech;
@@ -77,6 +78,7 @@ struct auth_request *auth_request_new_dummy(void)
 
        request->refcount = 1;
        request->last_access = ioloop_time;
+       request->session_pid = (pid_t)-1;
        request->set = global_auth_settings;
        return request;
 }
@@ -279,6 +281,20 @@ bool auth_request_import_auth(struct auth_request *request,
        return TRUE;
 }
 
+bool auth_request_import_master(struct auth_request *request,
+                               const char *key, const char *value)
+{
+       pid_t pid;
+
+       /* master request lookups may set these */
+       if (strcmp(key, "session_pid") == 0) {
+               if (str_to_pid(value, &pid) == 0)
+                       request->session_pid = pid;
+       } else
+               return FALSE;
+       return TRUE;
+}
+
 bool auth_request_import(struct auth_request *request,
                         const char *key, const char *value)
 {
index e95eef1782f17c7f8a4411bea9f778a84998c7ce..dd74b059e3ba145789af8db5e9d77fc3e4bd337e 100644 (file)
@@ -73,6 +73,7 @@ struct auth_request {
        unsigned int client_pid;
        unsigned int id;
        time_t last_access;
+       pid_t session_pid;
 
        const char *service, *mech_name, *session_id;
        struct ip_addr local_ip, remote_ip;
@@ -157,6 +158,8 @@ bool auth_request_import_info(struct auth_request *request,
                              const char *key, const char *value);
 bool auth_request_import_auth(struct auth_request *request,
                              const char *key, const char *value);
+bool auth_request_import_master(struct auth_request *request,
+                               const char *key, const char *value);
 
 void auth_request_initial(struct auth_request *request);
 void auth_request_continue(struct auth_request *request,
index 1e753c9506e0fc14bcfc5e3f8488cbd09017d1b9..49d3b180e1dd261e83f344f8c5dd42f0dfc6b79e 100644 (file)
@@ -17,6 +17,7 @@ static bool auth_userdb_settings_check(void *_set, pool_t pool, const char **err
 /* <settings checks> */
 static struct file_listener_settings auth_unix_listeners_array[] = {
        { "login/login", 0666, "", "" },
+       { "token-login/token-login", 0666, "", "" },
        { "auth-login", 0600, "$default_internal_user", "" },
        { "auth-client", 0600, "", "" },
        { "auth-userdb", 0666, "$default_internal_user", "" },
@@ -27,7 +28,8 @@ static struct file_listener_settings *auth_unix_listeners[] = {
        &auth_unix_listeners_array[1],
        &auth_unix_listeners_array[2],
        &auth_unix_listeners_array[3],
-       &auth_unix_listeners_array[4]
+       &auth_unix_listeners_array[4],
+       &auth_unix_listeners_array[5]
 };
 static buffer_t auth_unix_listeners_buf = {
        auth_unix_listeners, sizeof(auth_unix_listeners), { 0, }
diff --git a/src/auth/auth-token.c b/src/auth/auth-token.c
new file mode 100644 (file)
index 0000000..45f252d
--- /dev/null
@@ -0,0 +1,183 @@
+/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */
+
+/* Auth process maintains a random secret. Once a user authenticates the
+   response to the REQUEST command from a master service is augmented with an
+   auth_token value. This token is the SHA1 hash of the secret, the service
+   name and the username of the user that just logged in. Using this token the
+   service (e.g. imap) can login to another service (e.g. imap-urlauth) to
+   gain access to resources that require additional privileges (e.g. another
+   user's e-mail).
+*/
+
+#include "auth-common.h"
+#include "hex-binary.h"
+#include "hmac-sha1.h"
+#include "randgen.h"
+#include "read-full.h"
+#include "write-full.h"
+#include "safe-memset.h"
+#include "auth-settings.h"
+#include "auth-token.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define AUTH_TOKEN_SECRET_LEN 32
+
+#define AUTH_TOKEN_SECRET_FNAME "auth-token-secret.dat"
+
+static unsigned char auth_token_secret[AUTH_TOKEN_SECRET_LEN];
+
+static int
+auth_token_read_secret(const char *path,
+                      unsigned char secret_r[AUTH_TOKEN_SECRET_LEN])
+{
+       struct stat st, lst;
+       int fd, ret;            
+
+       fd = open(path, O_RDONLY);
+       if (fd == -1) {
+               if (errno != ENOENT)
+                       i_error("open(%s) failed: %m", path);
+               return -1;
+       }
+
+       if (fstat(fd, &st) < 0) {
+               i_error("fstat(%s) failed: %m", path);
+               i_close_fd(&fd);
+               return -1;
+       }
+
+       /* check secret len and file type */
+       if (st.st_size != AUTH_TOKEN_SECRET_LEN || !S_ISREG(st.st_mode)) {
+               i_error("Corrupted token secret file: %s", path);
+               i_close_fd(&fd);
+               if (unlink(path) < 0)
+                       i_error("unlink(%s) failed: %m", path);
+               return -1;
+       }
+
+       /* verify that we're not dealing with a symbolic link */
+       if (lstat(path, &lst) < 0) {
+               i_error("lstat(%s) failed: %m", path);
+               i_close_fd(&fd);
+               return -1;              
+       }
+
+       /* check security parameters for compromise */
+       if ((st.st_mode & 07777) != 0600 || st.st_uid != 0 || st.st_nlink > 1 ||
+           !S_ISREG(lst.st_mode) || st.st_ino != lst.st_ino ||
+           !CMP_DEV_T(st.st_dev, lst.st_dev)) {
+               i_error("Compromised token secret file: %s", path);
+               i_close_fd(&fd);
+               if (unlink(path) < 0)
+                       i_error("unlink(%s) failed: %m", path);
+               return -1;
+       }
+
+       /* FIXME: fail here to generate new secret if stored one is too old */
+
+       ret = read_full(fd, secret_r, AUTH_TOKEN_SECRET_LEN);
+       if (ret < 0)
+               i_error("read(%s) failed: %m", path);
+       else if (ret == 0) {
+               i_error("Token secret file unexpectedly shrank: %s", path);
+               ret = -1;
+       }
+       if (close(fd) < 0)
+               i_error("close(%s) failed: %m", path);
+
+       if (global_auth_settings->debug)
+               i_debug("Read auth token secret from %s", path);
+       return ret;
+}
+
+static int
+auth_token_write_secret(const char *path,
+                       const unsigned char secret[AUTH_TOKEN_SECRET_LEN])
+{
+       const char *temp_path;
+       mode_t old_mask;
+       int fd, ret;
+
+       i_assert(getuid() == 0);
+
+       temp_path = t_strconcat(path, ".tmp", NULL);
+
+       old_mask = umask(0);
+       fd = open(temp_path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+       umask(old_mask);
+
+       if (fd == -1) {
+               i_error("open(%s) failed: %m", temp_path);
+               return -1;
+       }
+
+       ret = write_full(fd, secret, AUTH_TOKEN_SECRET_LEN);
+       if (ret < 0)
+               i_error("write(%s) failed: %m", temp_path);
+       if (close(fd) < 0) {
+               i_error("close(%s) failed: %m", temp_path);
+               ret = -1;
+       }
+
+       if (ret < 0) {
+               if (unlink(temp_path) < 0)
+                       i_error("unlink(%s) failed: %m", temp_path);
+               return -1;
+       }
+
+       if (rename(temp_path, path) < 0) {
+               i_error("rename(%s, %s) failed: %m", temp_path, path);
+               if (unlink(temp_path) < 0)
+                       i_error("unlink(%s) failed: %m", temp_path);
+               return -1;
+       }
+
+       if (global_auth_settings->debug)
+               i_debug("Wrote new auth token secret to %s", path);
+       return 0;
+}
+
+void auth_token_init(void)
+{
+       const char *secret_path =
+               t_strconcat(global_auth_settings->base_dir, "/",
+                           AUTH_TOKEN_SECRET_FNAME, NULL);
+
+       if (auth_token_read_secret(secret_path, auth_token_secret) < 0) {
+               random_fill(auth_token_secret, sizeof(auth_token_secret));
+
+               if (auth_token_write_secret(secret_path, auth_token_secret) < 0) {
+                       i_error("Failed to write auth token secret file; "
+                               "returned tokens will be invalid once auth restarts");
+               }
+       }
+}
+
+void auth_token_deinit(void)
+{
+       /* not very useful, but we do it anyway */
+       safe_memset(auth_token_secret, 0, sizeof(auth_token_secret));
+}
+
+const char *auth_token_get(const char *service, const char *session_pid,
+                          const char *username, const char *session_id)
+{
+       struct hmac_sha1_context ctx;
+       unsigned char result[SHA1_RESULTLEN];
+
+       hmac_sha1_init(&ctx, username, strlen(username));
+       hmac_sha1_update(&ctx, session_pid, strlen(session_pid));
+       if (session_id != NULL && *session_id != '\0')
+               hmac_sha1_update(&ctx, session_id, strlen(session_id));
+       hmac_sha1_update(&ctx, service, strlen(service));
+       hmac_sha1_update(&ctx, auth_token_secret, sizeof(auth_token_secret));
+       hmac_sha1_final(&ctx, result);
+
+       return binary_to_hex(result, sizeof(result));
+}
diff --git a/src/auth/auth-token.h b/src/auth/auth-token.h
new file mode 100644 (file)
index 0000000..3e427c0
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef AUTH_TOKEN_H
+#define AUTH_TOKEN_H
+
+void auth_token_init(void);
+void auth_token_deinit(void);
+
+const char *auth_token_get(const char *service, const char *session_pid,
+                          const char *username, const char *session_id);
+
+#endif
+
index fd693c6fef3bf9c62877c7c4170118b25dbc5d79..08e3a7d51620813214ec92f4ee2a133826c18516 100644 (file)
@@ -22,6 +22,7 @@
 #include "mech.h"
 #include "auth.h"
 #include "auth-penalty.h"
+#include "auth-token.h"
 #include "auth-request-handler.h"
 #include "auth-worker-server.h"
 #include "auth-worker-client.h"
@@ -40,7 +41,9 @@ enum auth_socket_type {
        AUTH_SOCKET_LOGIN_CLIENT,
        AUTH_SOCKET_MASTER,
        AUTH_SOCKET_USERDB,
-       AUTH_SOCKET_POSTFIX
+       AUTH_SOCKET_POSTFIX,
+       AUTH_SOCKET_TOKEN,
+       AUTH_SOCKET_TOKEN_LOGIN
 };
 
 struct auth_socket_listener {
@@ -118,6 +121,10 @@ auth_socket_type_get(const char *path)
                return AUTH_SOCKET_USERDB;
        else if (strcmp(suffix, "postmap") == 0)
                return AUTH_SOCKET_POSTFIX;
+       else if (strcmp(suffix, "token") == 0)
+               return AUTH_SOCKET_TOKEN;
+       else if (strcmp(suffix, "tokenlogin") == 0)
+               return AUTH_SOCKET_TOKEN_LOGIN;
        else
                return AUTH_SOCKET_CLIENT;
 }
@@ -198,6 +205,7 @@ static void main_preinit(void)
                      mech_reg, services);
 
        listeners_init();
+       auth_token_init();
 
        /* Password lookups etc. may require roots, allow it. */
        restrict_access_by_env(NULL, FALSE);
@@ -264,6 +272,8 @@ static void main_deinit(void)
        auths_free();
        dict_drivers_unregister_builtin();
 
+       auth_token_deinit();
+
        auth_client_connections_destroy_all();
        auth_master_connections_destroy_all();
        auth_postfix_connections_destroy_all();
@@ -329,10 +339,16 @@ static void client_connected(struct master_service_connection *conn)
                (void)auth_postfix_connection_create(auth, conn->fd);
                break;
        case AUTH_SOCKET_LOGIN_CLIENT:
-               auth_client_connection_create(auth, conn->fd, TRUE);
+               auth_client_connection_create(auth, conn->fd, TRUE, FALSE);
                break;
        case AUTH_SOCKET_CLIENT:
-               auth_client_connection_create(auth, conn->fd, FALSE);
+               auth_client_connection_create(auth, conn->fd, FALSE, FALSE);
+               break;
+       case AUTH_SOCKET_TOKEN_LOGIN:
+               auth_client_connection_create(auth, conn->fd, TRUE, TRUE);
+               break;
+       case AUTH_SOCKET_TOKEN:
+               auth_client_connection_create(auth, conn->fd, FALSE, TRUE);
                break;
        default:
                i_unreached();
diff --git a/src/auth/mech-dovecot-token.c b/src/auth/mech-dovecot-token.c
new file mode 100644 (file)
index 0000000..2934f8d
--- /dev/null
@@ -0,0 +1,86 @@
+/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */
+
+/* Used internally by Dovecot processes to authenticate against each others
+   (e.g. imap to imap-urlauth). See auth-token.c */
+
+#include "auth-common.h"
+#include "mech.h"
+#include "safe-memset.h"
+#include "auth-token.h"
+
+static void
+mech_dovecot_token_auth_continue(struct auth_request *request,
+                            const unsigned char *data, size_t data_size)
+{
+       const char *session_id, *username, *pid, *service, *error;
+       char *auth_token;
+       size_t i, len;
+       int count;
+
+       /* service \0 pid \0 username \0 session_id \0 auth_token */
+       service = (const char *) data;
+       session_id = username = pid = auth_token = NULL;
+       count = 0;
+       for (i = 0; i < data_size; i++) {
+               if (data[i] == '\0') {
+                       count++; i++;
+                       if (count == 1)
+                               pid = (const char *)data + i;
+                       else if (count == 2)
+                               username = (const char *)data + i;
+                       else if (count == 3)
+                               session_id = (const char *)data + i;
+                       else {
+                               len = data_size - i;
+                               auth_token = p_strndup(unsafe_data_stack_pool,
+                                                      data+i, len);
+                               break;
+                       }
+               }
+       }       
+
+       if (count != 4) {
+               /* invalid input */
+               auth_request_log_info(request, "dovecot-token", "invalid input");
+               auth_request_fail(request);
+       } else if (!auth_request_set_username(request, username, &error)) {
+               /* invalid username */
+               auth_request_log_info(request, "dovecot-token", "%s", error);
+               auth_request_fail(request);
+       } else {
+               const char *valid_token =
+                       auth_token_get(service, pid, request->user, session_id);
+
+               if (strcmp(auth_token, valid_token) == 0)
+                       auth_request_success(request, NULL, 0);
+               else
+                       auth_request_fail(request);
+       }
+
+       /* make sure it's cleared */
+       if (auth_token != NULL)
+               safe_memset(auth_token, 0, strlen(auth_token));
+}
+
+static struct auth_request *mech_dovecot_token_auth_new(void)
+{
+       struct auth_request *request;
+       pool_t pool;
+
+       pool = pool_alloconly_create("dovecot_token_auth_request", 512);
+       request = p_new(pool, struct auth_request, 1);
+       request->pool = pool;
+       return request;
+}
+
+const struct mech_module mech_dovecot_token = {
+       "DOVECOT-TOKEN",
+
+       .flags = MECH_SEC_PRIVATE,
+       .passdb_need = MECH_PASSDB_NEED_NOTHING,
+
+       mech_dovecot_token_auth_new,
+       mech_generic_auth_initial,
+       mech_dovecot_token_auth_continue,
+       mech_generic_auth_free
+};
index ac0bb7faf2e584f5778346bd6ac6fec793167174..74408caf5df2e1fcc72f492899c04ee7b6b3c8c6 100644 (file)
@@ -55,6 +55,8 @@ struct mechanisms_register {
        buffer_t *handshake;
 };
 
+extern const struct mech_module mech_dovecot_token;
+
 void mech_register_module(const struct mech_module *module);
 void mech_unregister_module(const struct mech_module *module);
 const struct mech_module *mech_module_find(const char *name);
index 259a062dd523206bc767339e5bba3b62ebb176ea..1e8fe1168db5bfb413f58a1b9d1df11f8af348d8 100644 (file)
@@ -321,6 +321,7 @@ int main(int argc, char *argv[])
 
        memset(&login_set, 0, sizeof(login_set));
        login_set.postlogin_timeout_secs = MASTER_POSTLOGIN_TIMEOUT_DEFAULT;
+       login_set.request_auth_token = TRUE;
 
        if (IS_STANDALONE() && getuid() == 0 &&
            net_getpeername(1, NULL, NULL) == 0) {
index a073ee53d2f321ea6a61fe362a02291611281443..4335efcaee0352e10f6db04be433de3e619614b6 100644 (file)
@@ -3,6 +3,7 @@
 #include "lib.h"
 #include "network.h"
 #include "ioloop.h"
+#include "hostpid.h"
 #include "istream.h"
 #include "ostream.h"
 #include "llist.h"
@@ -53,6 +54,7 @@ struct master_login_auth {
 
        pid_t auth_server_pid;
 
+       unsigned int request_auth_token:1;
        unsigned int version_received:1;
        unsigned int spid_received:1;
 };
@@ -60,7 +62,8 @@ struct master_login_auth {
 static void master_login_auth_set_timeout(struct master_login_auth *auth);
 static void master_login_auth_check_spids(struct master_login_auth *auth);
 
-struct master_login_auth *master_login_auth_init(const char *auth_socket_path)
+struct master_login_auth *
+master_login_auth_init(const char *auth_socket_path, bool request_auth_token)
 {
        struct master_login_auth *auth;
        pool_t pool;
@@ -69,6 +72,7 @@ struct master_login_auth *master_login_auth_init(const char *auth_socket_path)
        auth = p_new(pool, struct master_login_auth, 1);
        auth->pool = pool;
        auth->auth_socket_path = p_strdup(pool, auth_socket_path);
+       auth->request_auth_token = request_auth_token;
        auth->refcount = 1;
        auth->fd = -1;
        hash_table_create_direct(&auth->requests, pool, 0);
@@ -436,6 +440,8 @@ master_login_auth_send_request(struct master_login_auth *auth,
        str_printfa(str, "REQUEST\t%u\t%u\t%u\t", req->id,
                    req->client_pid, req->auth_id);
        binary_to_hex_append(str, req->cookie, sizeof(req->cookie));
+       if (auth->request_auth_token)
+               str_printfa(str, "\tsession_pid=%s", my_pid);
        str_append_c(str, '\n');
        o_stream_nsend(auth->output, str_data(str), str_len(str));
 }
index 96aba28cb391acdc804774d7eace33a82784b7c1..a823869529275dd498475610633a85e58be022bf 100644 (file)
@@ -7,7 +7,8 @@ typedef void
 master_login_auth_request_callback_t(const char *const *auth_args,
                                     const char *errormsg, void *context);
 
-struct master_login_auth *master_login_auth_init(const char *auth_socket_path);
+struct master_login_auth *
+master_login_auth_init(const char *auth_socket_path, bool request_auth_token);
 void master_login_auth_deinit(struct master_login_auth **auth);
 void master_login_auth_disconnect(struct master_login_auth *auth);
 
index 2eaaf35d33edfe5f583399b9ec4ad3feffef7108..feca9b7532370a26177e4baa0763e5edd16b8337 100644 (file)
@@ -70,7 +70,8 @@ master_login_init(struct master_service *service,
        login->service = service;
        login->callback = set->callback;
        login->failure_callback = set->failure_callback;
-       login->auth = master_login_auth_init(set->auth_socket_path);
+       login->auth = master_login_auth_init(set->auth_socket_path,
+                                            set->request_auth_token);
        login->postlogin_socket_path = i_strdup(set->postlogin_socket_path);
        login->postlogin_timeout_secs = set->postlogin_timeout_secs;
 
index 01e03f031a25d8819c5dc808dca4e83723613dc1..4bd667729f6ff7dce054d4201f616e0cf451e0a2 100644 (file)
@@ -30,6 +30,8 @@ struct master_login_settings {
 
        master_login_callback_t *callback;
        master_login_failure_callback_t *failure_callback;
+
+       unsigned int request_auth_token:1;
 };
 
 struct master_login *
index 0c13b40f5a8a3780a9dfd36e0f2ece1b62de0464..cd4096a72f8cb3941b5e0eb5b380d37e0d4ee371 100644 (file)
@@ -71,7 +71,7 @@ struct mail_storage_service_user {
        enum mail_storage_service_flags flags;
 
        struct ioloop_context *ioloop_ctx;
-       const char *log_prefix;
+       const char *log_prefix, *auth_token;
 
        const char *system_groups_user, *uid_source, *gid_source;
        const struct mail_user_settings *user_set;
@@ -271,6 +271,8 @@ user_reply_handle(struct mail_storage_service_ctx *ctx,
                                        i_error("setpriority(%d) failed: %m", n);
                        }
 #endif
+               } else if (strncmp(line, "auth_token=", 11) == 0) {
+                       user->auth_token = p_strdup(user->pool, line+11);
                } else T_BEGIN {
                        ret = set_line(ctx, user, line);
                } T_END;
@@ -605,6 +607,7 @@ mail_storage_service_init_post(struct mail_storage_service_ctx *ctx,
        mail_user->uid = priv->uid == (uid_t)-1 ? geteuid() : priv->uid;
        mail_user->gid = priv->gid == (gid_t)-1 ? getegid() : priv->gid;
        mail_user->anonymous = user->anonymous;
+       mail_user->auth_token = p_strdup(mail_user->pool, user->auth_token);
        
        mail_set = mail_user_set_get_storage_set(mail_user);
 
index 10018b365bf6550c458bcb7e49f533c5ffe01dc1..8d8a501bac9938680e087c335486c76443c750a7 100644 (file)
@@ -23,6 +23,8 @@ struct mail_user {
        gid_t gid;
        const char *service;
        struct ip_addr *local_ip, *remote_ip;
+       const char *auth_token;
+
        const struct var_expand_table *var_expand_table;
        /* If non-NULL, fail the user initialization with this error.
           This could be set by plugins that need to fail the initialization. */
index 1ec9a5b8d3d1f9f1a9f82e56f7fb59d17c80a976..6a1618f134ecec1e553d0817accfe4c1f33c87cc 100644 (file)
@@ -473,6 +473,7 @@ void client_auth_parse_response(struct client *client)
 
 static void client_auth_input(struct client *client)
 {
+       i_assert(client->v.auth_parse_response != NULL);
        client->v.auth_parse_response(client);
 }
 
@@ -559,6 +560,7 @@ sasl_callback(struct client *client, enum sasl_server_reply sasl_reply,
                        client_destroy_success(client, data);
                break;
        case SASL_SERVER_REPLY_CONTINUE:
+               i_assert(client->v.auth_send_challenge != NULL);
                client->v.auth_send_challenge(client, data);
 
                if (client->to_auth_waiting != NULL)
index 5a87d72558e88e2f83f2e5cd25193e717b218299..c423a974102564c29de97a31e9d2819ea48e6a7a 100644 (file)
@@ -211,7 +211,8 @@ void client_destroy(struct client *client, const char *reason)
 
        if (client->login_proxy != NULL)
                login_proxy_free(&client->login_proxy);
-       client->v.destroy(client);
+       if (client->v.destroy != NULL)
+               client->v.destroy(client);
        if (client_unref(&client) && initial_service_count == 1) {
                /* as soon as this connection is done with proxying
                   (or whatever), the process will die. there's no need for
@@ -672,7 +673,8 @@ void client_notify_disconnect(struct client *client,
                              const char *text)
 {
        if (!client->notified_disconnect) {
-               client->v.notify_disconnect(client, reason, text);
+               if (client->v.notify_disconnect != NULL)
+                       client->v.notify_disconnect(client, reason, text);
                client->notified_disconnect = TRUE;
        }
 }
@@ -680,7 +682,8 @@ void client_notify_disconnect(struct client *client,
 void client_notify_auth_ready(struct client *client)
 {
        if (!client->notified_auth_ready) {
-               client->v.notify_auth_ready(client);
+               if (client->v.notify_auth_ready != NULL)
+                       client->v.notify_auth_ready(client);
                client->notified_auth_ready = TRUE;
        }
 }
index 361d6cbdc0549370467b539685a977a83875d2cb..42e3d67edd32be03485ca9b73d6810541dd39d35 100644 (file)
@@ -12,6 +12,9 @@
 #define AUTH_PLAINTEXT_DISABLED_MSG \
        "Plaintext authentication disallowed on non-secure (SSL/TLS) connections."
 
+#define LOGIN_DEFAULT_SOCKET "login"
+#define LOGIN_TOKEN_DEFAULT_SOCKET "token-login"
+
 struct login_binary {
        /* e.g. imap, pop3 */
        const char *protocol;
@@ -23,6 +26,9 @@ struct login_binary {
        /* e.g. 993, 995. if there is no ssl port, use 0. */
        unsigned int default_ssl_port;
 
+       /* if value is NULL, LOGIN_DEFAULT_SOCKET is used as the default */
+       const char *default_login_socket;
+
        const struct client_vfuncs *client_vfuncs;
        void (*preinit)(void);
        void (*init)(void);
index 28172eddc10e5548a8d04701ef1cbb5a379007b2..8a842621ba5785687db7c8335015587cbeb5a2af 100644 (file)
@@ -21,7 +21,6 @@
 #include <unistd.h>
 #include <syslog.h>
 
-#define DEFAULT_LOGIN_SOCKET "login"
 #define AUTH_CLIENT_IDLE_TIMEOUT_MSECS (1000*60)
 
 struct login_access_lookup {
@@ -368,10 +367,12 @@ int login_binary_run(const struct login_binary *binary,
                MASTER_SERVICE_FLAG_TRACK_LOGIN_STATE;
        pool_t set_pool;
        bool allow_core_dumps = FALSE;
-       const char *login_socket = DEFAULT_LOGIN_SOCKET;
+       const char *login_socket;
        int c;
 
        login_binary = binary;
+       login_socket = binary->default_login_socket != NULL ?
+               binary->default_login_socket : LOGIN_DEFAULT_SOCKET;
 
        master_service = master_service_init(login_binary->process_name,
                                             service_flags, &argc, &argv,
index e503427f6f48cdfb8d9f1d2331edbbfd5ef5af01..5fe25325fa7d7ec277db8c11afc2226cc2db603a 100644 (file)
@@ -715,11 +715,36 @@ static void unlink_sockets(const char *path, const char *prefix)
        (void)closedir(dirp);
 }
 
+static void
+mkdir_login_dir(const struct master_settings *set, const char *login_dir)
+{
+       mode_t mode;
+       gid_t gid;
+
+       if (settings_have_auth_unix_listeners_in(set, login_dir)) {
+               /* we are not using external authentication, so make sure the
+                  login directory exists with correct permissions and it's
+                  empty. with external auth we wouldn't want to delete
+                  existing sockets or break the permissions required by the
+                  auth server. */
+               mode = login_want_core_dumps(set, &gid) ? 0770 : 0750;
+               if (safe_mkdir(login_dir, mode, master_uid, gid) == 0) {
+                       i_warning("Corrected permissions for login directory "
+                                 "%s", login_dir);
+               }
+
+               unlink_sockets(login_dir, "");
+       } else {
+               /* still make sure that login directory exists */
+               if (mkdir(login_dir, 0755) < 0 && errno != EEXIST)
+                       i_fatal("mkdir(%s) failed: %m", login_dir);
+       }
+}
+
 void master_settings_do_fixes(const struct master_settings *set)
 {
-       const char *login_dir, *empty_dir;
+       const char *empty_dir;
        struct stat st;
-       gid_t gid;
 
        /* since base dir is under /var/run by default, it may have been
           deleted. */
@@ -741,25 +766,8 @@ void master_settings_do_fixes(const struct master_settings *set)
        if (mkdir_parents(set->state_dir, 0755) < 0 && errno != EEXIST)
                i_fatal("mkdir(%s) failed: %m", set->state_dir);
 
-       login_dir = t_strconcat(set->base_dir, "/login", NULL);
-       if (settings_have_auth_unix_listeners_in(set, login_dir)) {
-               /* we are not using external authentication, so make sure the
-                  login directory exists with correct permissions and it's
-                  empty. with external auth we wouldn't want to delete
-                  existing sockets or break the permissions required by the
-                  auth server. */
-               mode_t mode = login_want_core_dumps(set, &gid) ? 0770 : 0750;
-               if (safe_mkdir(login_dir, mode, master_uid, gid) == 0) {
-                       i_warning("Corrected permissions for login directory "
-                                 "%s", login_dir);
-               }
-
-               unlink_sockets(login_dir, "");
-       } else {
-               /* still make sure that login directory exists */
-               if (mkdir(login_dir, 0755) < 0 && errno != EEXIST)
-                       i_fatal("mkdir(%s) failed: %m", login_dir);
-       }
+       mkdir_login_dir(set, t_strconcat(set->base_dir, "/login", NULL));
+       mkdir_login_dir(set, t_strconcat(set->base_dir, "/token-login", NULL));
 
        empty_dir = t_strconcat(set->base_dir, "/empty", NULL);
        if (safe_mkdir(empty_dir, 0755, master_uid, getegid()) == 0) {