]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Removed hardcoded mechanism lists. It's now possible to add them
authorTimo Sirainen <tss@iki.fi>
Sat, 29 May 2004 21:40:30 +0000 (00:40 +0300)
committerTimo Sirainen <tss@iki.fi>
Sat, 29 May 2004 21:40:30 +0000 (00:40 +0300)
dynamically. Added support for SASL initial response.

--HG--
branch : HEAD

23 files changed:
src/auth/Makefile.am
src/auth/auth-client-connection.c
src/auth/auth-client-interface.h
src/auth/auth-master-connection.c
src/auth/auth-master-connection.h
src/auth/auth-master-interface.h
src/auth/auth-mech-desc.h [deleted file]
src/auth/main.c
src/auth/mech-anonymous.c
src/auth/mech-cram-md5.c
src/auth/mech-digest-md5.c
src/auth/mech-plain.c
src/auth/mech.c
src/auth/mech.h
src/auth/passdb.c
src/imap-login/client-authenticate.c
src/lib-auth/auth-client.c
src/lib-auth/auth-client.h
src/lib-auth/auth-server-connection.c
src/lib-auth/auth-server-connection.h
src/lib-auth/auth-server-request.c
src/lib/file-dotlock.c
src/pop3-login/client-authenticate.c

index 20f5d14f7e67f3da8ba19f6a68bfc350aac32e93..fbc91d162a6bff07390a91070cc47f0547d3b852 100644 (file)
@@ -58,7 +58,6 @@ noinst_HEADERS = \
        auth-client-interface.h \
        auth-master-connection.h \
        auth-master-interface.h \
-       auth-mech-desc.h \
        auth-module.h \
        db-ldap.h \
        db-mysql.h \
index f51179025f98ca04cf3b550de5ab32b649d4b912..ead211fc3a4e7ce5be21f479a58a1903fbd507a2 100644 (file)
@@ -109,19 +109,26 @@ static void auth_client_input_request(struct auth_client_connection *conn)
           structures, as it may not be aligned properly. */
        memcpy(&type, data, sizeof(type));
 
-       if (type == AUTH_CLIENT_REQUEST_NEW) {
+       conn->refcount++;
+       switch (type) {
+       case AUTH_CLIENT_REQUEST_NEW: {
                struct auth_client_request_new request;
 
                if (size < sizeof(request))
                        return;
 
                memcpy(&request, data, sizeof(request));
-               i_stream_skip(conn->input, sizeof(request));
+               if (size < sizeof(request) + request.data_size)
+                       return;
 
                /* we have a full init request */
                conn->refcount++;
-               mech_request_new(conn, &request, request_callback);
-       } else if (type == AUTH_CLIENT_REQUEST_CONTINUE) {
+               mech_request_new(conn, &request, data + sizeof(request),
+                                request_callback);
+               i_stream_skip(conn->input, sizeof(request) + request.data_size);
+               break;
+       }
+       case AUTH_CLIENT_REQUEST_CONTINUE: {
                 struct auth_client_request_continue request;
 
                if (size < sizeof(request))
@@ -131,8 +138,6 @@ static void auth_client_input_request(struct auth_client_connection *conn)
                if (size < sizeof(request) + request.data_size)
                        return;
 
-               i_stream_skip(conn->input, sizeof(request) + request.data_size);
-
                /* we have a full continued request */
                conn->refcount++;
                mech_request_continue(conn, &request, data + sizeof(request),
@@ -140,12 +145,16 @@ static void auth_client_input_request(struct auth_client_connection *conn)
 
                /* clear any sensitive data from memory */
                safe_memset(data + sizeof(request), 0, request.data_size);
-       } else {
+               i_stream_skip(conn->input, sizeof(request) + request.data_size);
+               break;
+       }
+       default:
                /* unknown request */
-               i_error("BUG: Auth client %u sent us unknown request %u",
+               i_error("BUG: Auth client %u sent us unknown request type %u",
                        conn->pid, type);
                auth_client_connection_destroy(conn);
        }
+       auth_client_connection_unref(conn);
 }
 
 static void auth_client_input(void *context)
@@ -198,8 +207,9 @@ auth_client_connection_create(struct auth_master_connection *master, int fd)
        conn->next = master->clients;
        master->clients = conn;
 
-       if (o_stream_send(conn->output, &master->handshake_reply,
-                         sizeof(master->handshake_reply)) < 0) {
+       if (o_stream_send(conn->output, master->handshake_reply,
+                         sizeof(*master->handshake_reply) +
+                         master->handshake_reply->data_size) < 0) {
                auth_client_connection_destroy(conn);
                conn = NULL;
        }
@@ -298,9 +308,6 @@ static void request_timeout(void *context __attr_unused__)
 
 void auth_client_connections_init(struct auth_master_connection *master)
 {
-       master->handshake_reply.server_pid = master->pid;
-       master->handshake_reply.auth_mechanisms = auth_mechanisms;
-
        master->to_clients = timeout_add(5000, request_timeout, master);
 }
 
index d7a44b4330e071fbabce5e97b14f1f37b91d58a1..ac5a9f7cff30030ab4ba00fe707746bc7a3a4fa5 100644 (file)
@@ -4,22 +4,10 @@
 /* max. size for auth_client_request_continue.data[] */
 #define AUTH_CLIENT_MAX_REQUEST_DATA_SIZE 4096
 
-/* sizeof(struct auth_client_request_new->protocol) */
-#define AUTH_CLIENT_PROTOCOL_BUF_SIZE 12
-
 /* Client process must finish with single authentication requests in this time,
    or the whole connection will be killed. */
 #define AUTH_REQUEST_TIMEOUT 120
 
-enum auth_mech {
-       AUTH_MECH_PLAIN         = 0x01,
-       AUTH_MECH_DIGEST_MD5    = 0x02,
-       AUTH_MECH_ANONYMOUS     = 0x04,
-       AUTH_MECH_CRAM_MD5      = 0x08,
-
-       AUTH_MECH_COUNT
-};
-
 enum auth_client_request_new_flags {
        AUTH_CLIENT_FLAG_SSL_VALID_CLIENT_CERT = 0x01
 };
@@ -40,10 +28,19 @@ struct auth_client_handshake_request {
        unsigned int client_pid; /* unique identifier for client process */
 };
 
+struct auth_client_handshake_mech_desc {
+       uint32_t name_idx;
+       unsigned int plaintext:1;
+       unsigned int advertise:1;
+};
+
 /* Server -> Client */
 struct auth_client_handshake_reply {
        unsigned int server_pid; /* unique auth process identifier */
-       enum auth_mech auth_mechanisms; /* valid authentication mechanisms */
+
+       uint32_t mech_count;
+       uint32_t data_size;
+       /* struct auth_client_handshake_mech_desc mech_desc[auth_mech_count]; */
 };
 
 /* New authentication request */
@@ -51,17 +48,24 @@ struct auth_client_request_new {
        enum auth_client_request_type type; /* AUTH_CLIENT_REQUEST_NEW */
        unsigned int id; /* unique ID for the request */
 
-       enum auth_mech mech;
        enum auth_client_request_new_flags flags;
-       char protocol[AUTH_CLIENT_PROTOCOL_BUF_SIZE];
+
+       uint32_t protocol_idx;
+       uint32_t mech_idx;
+       uint32_t initial_resp_idx;
+
+       uint32_t data_size;
+       /* unsigned char data[]; */
 };
+#define AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request) \
+        ((request)->initial_resp_idx != (request)->data_size)
 
 /* Continue authentication request */
 struct auth_client_request_continue {
        enum auth_client_request_type type; /* AUTH_CLIENT_REQUEST_CONTINUE */
        unsigned int id;
 
-       size_t data_size;
+       uint32_t data_size;
        /* unsigned char data[]; */
 };
 
@@ -73,10 +77,10 @@ struct auth_client_request_reply {
 
        /* variable width data, indexes into data[].
           Ignore if it points outside data_size. */
-       size_t username_idx; /* NUL-terminated */
-       size_t reply_idx; /* last, non-NUL terminated */
+       uint32_t username_idx; /* NUL-terminated */
+       uint32_t reply_idx; /* last, non-NUL terminated */
 
-       size_t data_size;
+       uint32_t data_size;
        /* unsigned char data[]; */
 };
 
index 1d23f4ef584bfc5868af094bc88bd6d541d24fc9..63f35864369a43d4bb3e0c18cc5f0b73f6c063df 100644 (file)
@@ -182,6 +182,48 @@ static void master_input(void *context)
        }
 }
 
+static void master_get_handshake_reply(struct auth_master_connection *master)
+{
+       struct mech_module_list *list;
+       buffer_t *buf;
+       struct auth_client_handshake_reply reply;
+       struct auth_client_handshake_mech_desc mech_desc;
+       uint32_t mech_desc_offset;
+
+       memset(&reply, 0, sizeof(reply));
+       memset(&mech_desc, 0, sizeof(mech_desc));
+
+       reply.server_pid = master->pid;
+
+       buf = buffer_create_dynamic(default_pool, 128, (size_t)-1);
+
+       for (list = mech_modules; list != NULL; list = list->next)
+               reply.mech_count++;
+       buffer_set_used_size(buf, sizeof(reply) +
+                            sizeof(mech_desc) * reply.mech_count);
+
+       mech_desc_offset = sizeof(reply);
+       for (list = mech_modules; list != NULL; list = list->next) {
+               mech_desc.name_idx = buffer_get_used_size(buf) - sizeof(reply);
+               mech_desc.plaintext = list->module.plaintext;
+               mech_desc.advertise = list->module.advertise;
+
+               memcpy(buffer_get_space_unsafe(buf, mech_desc_offset,
+                                              sizeof(mech_desc)),
+                      &mech_desc, sizeof(mech_desc));
+               buffer_append(buf, list->module.mech_name,
+                             strlen(list->module.mech_name) + 1);
+
+               mech_desc_offset += sizeof(mech_desc);
+       }
+
+       reply.data_size = buffer_get_used_size(buf);
+       memcpy(buffer_get_space_unsafe(buf, 0, sizeof(reply)),
+              &reply, sizeof(reply));
+
+       master->handshake_reply = buffer_free_without_data(buf);
+}
+
 struct auth_master_connection *
 auth_master_connection_new(int fd, unsigned int pid)
 {
@@ -198,6 +240,7 @@ auth_master_connection_new(int fd, unsigned int pid)
                                                    MAX_OUTBUF_SIZE, FALSE);
                conn->io = io_add(fd, IO_READ, master_input, conn);
        }
+       master_get_handshake_reply(conn);
        return conn;
 }
 
@@ -241,6 +284,7 @@ void auth_master_connection_free(struct auth_master_connection *conn)
                i_free(l[i]);
        }
        buffer_free(conn->listeners_buf);
+       conn->listeners_buf = NULL;
 
        auth_master_connection_unref(conn);
 }
@@ -252,6 +296,7 @@ static int auth_master_connection_unref(struct auth_master_connection *conn)
 
        if (conn->output != NULL)
                o_stream_unref(conn->output);
+       i_free(conn->handshake_reply);
        i_free(conn);
        return FALSE;
 }
@@ -280,7 +325,7 @@ void auth_master_connection_add_listener(struct auth_master_connection *conn,
        l->master = conn;
        l->fd = fd;
        l->path = i_strdup(path);
-       l->io = io_add(fd, IO_READ, auth_accept, &l);
+       l->io = io_add(fd, IO_READ, auth_accept, l);
 
        buffer_append(conn->listeners_buf, &l, sizeof(l));
 }
index 27c4f6c33c0b4848276051710c04f2315aeedd5d..9f06748b4275032dcc5a261bde6c034fbbbf7fd5 100644 (file)
@@ -15,7 +15,7 @@ struct auth_master_connection {
        unsigned int request_pos;
        unsigned char request_buf[sizeof(struct auth_master_request)];
 
-       struct auth_client_handshake_reply handshake_reply;
+       struct auth_client_handshake_reply *handshake_reply;
        struct auth_client_connection *clients;
        struct timeout *to_clients;
 
index 00e1db803ce8df5fc3bd55d3f18d4ec0c79be49b..9a247442245da300ebbfd7b1cf67ef13cf8eb245 100644 (file)
@@ -21,11 +21,11 @@ struct auth_master_reply {
        /* variable width fields are packed into data[]. These variables
           contain indexes to the data, they're all NUL-terminated.
           Ignore if it points outside data_size. */
-       size_t system_user_idx;
-       size_t virtual_user_idx;
-       size_t home_idx, mail_idx, chroot_idx;
+       uint32_t system_user_idx;
+       uint32_t virtual_user_idx;
+       uint32_t home_idx, mail_idx, chroot_idx;
 
-       size_t data_size;
+       uint32_t data_size;
        /* unsigned char data[]; */
 };
 
diff --git a/src/auth/auth-mech-desc.h b/src/auth/auth-mech-desc.h
deleted file mode 100644 (file)
index fc5e74d..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef __AUTH_MECH_DESC_H
-#define __AUTH_MECH_DESC_H
-
-struct auth_mech_desc {
-       enum auth_mech mech;
-       const char *name;
-       int plaintext;
-       int advertise;
-};
-
-static struct auth_mech_desc auth_mech_desc[AUTH_MECH_COUNT] = {
-       { AUTH_MECH_PLAIN,              "PLAIN",        TRUE, FALSE },
-       { AUTH_MECH_CRAM_MD5,           "CRAM-MD5",     FALSE, TRUE },
-       { AUTH_MECH_DIGEST_MD5,         "DIGEST-MD5",   FALSE, TRUE },
-       { AUTH_MECH_ANONYMOUS,          "ANONYMOUS",    FALSE, TRUE }
-};
-
-#endif
index efa7fe703ef3fb8bad8664fef4d64c2b296acf58..f03432f55a1e3dd0ebbd14f8c0430f717a0a6ac5 100644 (file)
@@ -132,7 +132,7 @@ static void main_init(void)
                env = getenv("AUTH_SOCKETS");
        }
 
-       if (env != NULL) {
+       if (env != NULL && *env != '\0') {
                master = auth_master_connection_new(-1, 0);
                master_add_unix_listeners(master, env);
                auth_client_connections_init(master);
index b9e5b3f50359b87e9389597379746e809926934e..8c9d4f1f8daccf4508edf5d0affaf4c67fb5fcd9 100644 (file)
@@ -5,15 +5,14 @@
 
 static int
 mech_anonymous_auth_continue(struct auth_request *auth_request,
-                            struct auth_client_request_continue *request,
-                            const unsigned char *data,
+                            const unsigned char *data, size_t data_size,
                             mech_callback_t *callback)
 {
        i_assert(anonymous_username != NULL);
 
        if (verbose) {
                i_info("mech-anonymous: login by %s",
-                      t_strndup(data, request->data_size));
+                      t_strndup(data, data_size));
        }
 
        auth_request->callback = callback;
@@ -22,37 +21,62 @@ mech_anonymous_auth_continue(struct auth_request *auth_request,
        return TRUE;
 }
 
+static int
+mech_anonymous_auth_initial(struct auth_request *auth_request,
+                           struct auth_client_request_new *request,
+                           const unsigned char *data,
+                           mech_callback_t *callback)
+{
+       struct auth_client_request_reply reply;
+       size_t data_size;
+
+       if (AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request)) {
+               data += request->initial_resp_idx;
+               data_size = request->data_size - request->initial_resp_idx;
+
+               return auth_request->auth_continue(auth_request, data,
+                                                  data_size, callback);
+       }
+
+       /* initialize reply */
+       memset(&reply, 0, sizeof(reply));
+       reply.id = auth_request->id;
+       reply.result = AUTH_CLIENT_RESULT_CONTINUE;
+
+       callback(&reply, NULL, auth_request->conn);
+       return TRUE;
+}
+
 static void
 mech_anonymous_auth_free(struct auth_request *auth_request)
 {
        pool_unref(auth_request->pool);
 }
 
-static struct auth_request *
-mech_anonymous_auth_new(struct auth_client_connection *conn, unsigned int id,
-                       mech_callback_t *callback)
+static struct auth_request *mech_anonymous_auth_new(void)
 {
         struct auth_request *auth_request;
-       struct auth_client_request_reply reply;
        pool_t pool;
 
        pool = pool_alloconly_create("anonymous_auth_request", 256);
        auth_request = p_new(pool, struct auth_request, 1);
        auth_request->refcount = 1;
        auth_request->pool = pool;
+       auth_request->auth_initial = mech_anonymous_auth_initial;
        auth_request->auth_continue = mech_anonymous_auth_continue;
         auth_request->auth_free = mech_anonymous_auth_free;
 
-       /* initialize reply */
-       memset(&reply, 0, sizeof(reply));
-       reply.id = id;
-       reply.result = AUTH_CLIENT_RESULT_CONTINUE;
-
-       callback(&reply, NULL, conn);
        return auth_request;
 }
 
 struct mech_module mech_anonymous = {
-       AUTH_MECH_ANONYMOUS,
+       "ANONYMOUS",
+
+       MEMBER(plaintext) FALSE,
+       MEMBER(advertise) TRUE,
+
+       MEMBER(passdb_need_plain) FALSE,
+       MEMBER(passdb_need_credentials) FALSE,
+
        mech_anonymous_auth_new
 };
index 9ad886e0568e5e8e2e4dbf6ddbbd71c9d14a7b30..a447347592fd2bb8cc0121b33dd1697d4edfa3f3 100644 (file)
@@ -149,15 +149,14 @@ static void credentials_callback(const char *result,
 
 static int
 mech_cram_md5_auth_continue(struct auth_request *auth_request,
-       struct auth_client_request_continue *request __attr_unused__,
-       const unsigned char *data,
-       mech_callback_t *callback)
+                           const unsigned char *data, size_t data_size,
+                           mech_callback_t *callback)
 {
        struct cram_auth_request *auth =
                (struct cram_auth_request *)auth_request;
        const char *error;
 
-       if (parse_cram_response(auth, data, request->data_size, &error)) {
+       if (parse_cram_response(auth, data, data_size, &error)) {
                auth_request->callback = callback;
 
                auth_request->user =
@@ -186,16 +185,43 @@ mech_cram_md5_auth_continue(struct auth_request *auth_request,
        return FALSE;
 }
 
+static int
+mech_cram_md5_auth_initial(struct auth_request *auth_request,
+                          struct auth_client_request_new *request,
+                          const unsigned char *data __attr_unused__,
+                          mech_callback_t *callback)
+{
+       struct cram_auth_request *auth =
+               (struct cram_auth_request *)auth_request;
+
+       struct auth_client_request_reply reply;
+
+       if (AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request)) {
+               /* No initial response in CRAM-MD5 */
+               return FALSE;
+       }
+
+       auth->challenge = p_strdup(auth->pool, get_cram_challenge());
+
+       /* initialize reply */
+       mech_init_auth_client_reply(&reply);
+       reply.id = request->id;
+       reply.result = AUTH_CLIENT_RESULT_CONTINUE;
+
+       /* send the initial challenge */
+       reply.reply_idx = 0;
+       reply.data_size = strlen(auth->challenge);
+       callback(&reply, auth->challenge, auth_request->conn);
+       return TRUE;
+}
+
 static void mech_cram_md5_auth_free(struct auth_request *auth_request)
 {
        pool_unref(auth_request->pool);
 }
 
-static struct auth_request *
-mech_cram_md5_auth_new(struct auth_client_connection *conn,
-                      unsigned int id, mech_callback_t *callback)
+static struct auth_request *mech_cram_md5_auth_new(void)
 {
-       struct auth_client_request_reply reply;
        struct cram_auth_request *auth;
        pool_t pool;
 
@@ -205,25 +231,21 @@ mech_cram_md5_auth_new(struct auth_client_connection *conn,
 
        auth->auth_request.refcount = 1;
        auth->auth_request.pool = pool;
+       auth->auth_request.auth_initial = mech_cram_md5_auth_initial;
        auth->auth_request.auth_continue = mech_cram_md5_auth_continue;
        auth->auth_request.auth_free = mech_cram_md5_auth_free;
 
-       auth->challenge = p_strdup(auth->pool, get_cram_challenge());
-
-       /* initialize reply */
-       mech_init_auth_client_reply(&reply);
-       reply.id = id;
-       reply.result = AUTH_CLIENT_RESULT_CONTINUE;
-
-       /* send the initial challenge */
-       reply.reply_idx = 0;
-       reply.data_size = strlen(auth->challenge);
-       callback(&reply, auth->challenge, conn);
-
        return &auth->auth_request;
 }
 
 struct mech_module mech_cram_md5 = {
-       AUTH_MECH_CRAM_MD5,
+       "CRAM-MD5",
+
+       MEMBER(plaintext) FALSE,
+       MEMBER(advertise) TRUE,
+
+       MEMBER(passdb_need_plain) FALSE,
+       MEMBER(passdb_need_credentials) TRUE,
+
        mech_cram_md5_auth_new
 };
index 99639f96ee365e66a64fea114f95facea1429229..48307459b07f722787e47d4c53772a0eda01c7da 100644 (file)
@@ -525,8 +525,7 @@ static void credentials_callback(const char *result,
 
 static int
 mech_digest_md5_auth_continue(struct auth_request *auth_request,
-                             struct auth_client_request_continue *request,
-                             const unsigned char *data,
+                             const unsigned char *data, size_t data_size,
                              mech_callback_t *callback)
 {
        struct digest_auth_request *auth =
@@ -536,7 +535,7 @@ mech_digest_md5_auth_continue(struct auth_request *auth_request,
 
        /* initialize reply */
        mech_init_auth_client_reply(&reply);
-       reply.id = request->id;
+       reply.id = auth_request->id;
 
        if (auth->authenticated) {
                /* authentication is done, we were just waiting the last
@@ -545,7 +544,7 @@ mech_digest_md5_auth_continue(struct auth_request *auth_request,
                return TRUE;
        }
 
-       if (parse_digest_response(auth, data, request->data_size, &error)) {
+       if (parse_digest_response(auth, data, data_size, &error)) {
                auth_request->callback = callback;
 
                realm = auth->realm != NULL ? auth->realm : default_realm;
@@ -582,19 +581,50 @@ mech_digest_md5_auth_continue(struct auth_request *auth_request,
        return FALSE;
 }
 
+static int
+mech_digest_md5_auth_initial(struct auth_request *auth_request,
+                            struct auth_client_request_new *request,
+                            const unsigned char *data __attr_unused__,
+                            mech_callback_t *callback)
+{
+       struct digest_auth_request *auth =
+               (struct digest_auth_request *)auth_request;
+       struct auth_client_request_reply reply;
+       string_t *challenge;
+       size_t data_size;
+
+       if (AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request)) {
+               /* FIXME: support subsequent authentication? */
+               data += request->initial_resp_idx;
+               data_size = request->data_size - request->initial_resp_idx;
+
+               return auth_request->auth_continue(auth_request, data,
+                                                  data_size, callback);
+       }
+
+       /* initialize reply */
+       mech_init_auth_client_reply(&reply);
+       reply.id = request->id;
+       reply.result = AUTH_CLIENT_RESULT_CONTINUE;
+
+       /* send the initial challenge */
+       reply.reply_idx = 0;
+       challenge = get_digest_challenge(auth);
+       reply.data_size = str_len(challenge);
+       callback(&reply, str_data(challenge), auth_request->conn);
+       return TRUE;
+}
+
 static void mech_digest_md5_auth_free(struct auth_request *auth_request)
 {
        pool_unref(auth_request->pool);
 }
 
 static struct auth_request *
-mech_digest_md5_auth_new(struct auth_client_connection *conn,
-                        unsigned int id, mech_callback_t *callback)
+mech_digest_md5_auth_new(void)
 {
-       struct auth_client_request_reply reply;
        struct digest_auth_request *auth;
        pool_t pool;
-       string_t *challenge;
 
        pool = pool_alloconly_create("digest_md5_auth_request", 2048);
        auth = p_new(pool, struct digest_auth_request, 1);
@@ -602,25 +632,21 @@ mech_digest_md5_auth_new(struct auth_client_connection *conn,
 
        auth->auth_request.refcount = 1;
        auth->auth_request.pool = pool;
+       auth->auth_request.auth_initial = mech_digest_md5_auth_initial;
        auth->auth_request.auth_continue = mech_digest_md5_auth_continue;
        auth->auth_request.auth_free = mech_digest_md5_auth_free;
        auth->qop = QOP_AUTH;
-
-       /* initialize reply */
-       mech_init_auth_client_reply(&reply);
-       reply.id = id;
-       reply.result = AUTH_CLIENT_RESULT_CONTINUE;
-
-       /* send the initial challenge */
-       reply.reply_idx = 0;
-       challenge = get_digest_challenge(auth);
-       reply.data_size = str_len(challenge);
-       callback(&reply, str_data(challenge), conn);
-
        return &auth->auth_request;
 }
 
 struct mech_module mech_digest_md5 = {
-       AUTH_MECH_DIGEST_MD5,
+       "DIGEST-MD5",
+
+       MEMBER(plaintext) FALSE,
+       MEMBER(advertise) TRUE,
+
+       MEMBER(passdb_need_plain) FALSE,
+       MEMBER(passdb_need_credentials) TRUE,
+
        mech_digest_md5_auth_new
 };
index cc38e54da2931a300d2335fa98d5251adb008a15..12fc0a010c5247abf39cc665fb3801c7ab61ad03 100644 (file)
@@ -13,8 +13,8 @@ static void verify_callback(enum passdb_result result,
 
 static int
 mech_plain_auth_continue(struct auth_request *auth_request,
-                        struct auth_client_request_continue *request,
-                        const unsigned char *data, mech_callback_t *callback)
+                        const unsigned char *data, size_t data_size,
+                        mech_callback_t *callback)
 {
        const char *authid, *authenid;
        char *pass;
@@ -28,13 +28,13 @@ mech_plain_auth_continue(struct auth_request *auth_request,
        authenid = NULL; pass = "";
 
        count = 0;
-       for (i = 0; i < request->data_size; i++) {
+       for (i = 0; i < data_size; i++) {
                if (data[i] == '\0') {
                        if (++count == 1)
                                authenid = (const char *) data + i+1;
                        else {
                                i++;
-                               len = request->data_size - i;
+                               len = data_size - i;
                                pass = p_strndup(unsafe_data_stack_pool,
                                                 data+i, len);
                                break;
@@ -76,37 +76,61 @@ mech_plain_auth_continue(struct auth_request *auth_request,
        return TRUE;
 }
 
+static int
+mech_plain_auth_initial(struct auth_request *auth_request,
+                       struct auth_client_request_new *request,
+                       const unsigned char *data,
+                       mech_callback_t *callback)
+{
+       struct auth_client_request_reply reply;
+       size_t data_size;
+
+       if (AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request)) {
+               data += request->initial_resp_idx;
+               data_size = request->data_size - request->initial_resp_idx;
+
+               return auth_request->auth_continue(auth_request, data,
+                                                  data_size, callback);
+       }
+
+       /* initialize reply */
+       memset(&reply, 0, sizeof(reply));
+       reply.id = request->id;
+       reply.result = AUTH_CLIENT_RESULT_CONTINUE;
+
+       callback(&reply, NULL, auth_request->conn);
+       return TRUE;
+}
+
 static void
 mech_plain_auth_free(struct auth_request *auth_request)
 {
        pool_unref(auth_request->pool);
 }
 
-static struct auth_request *
-mech_plain_auth_new(struct auth_client_connection *conn, unsigned int id,
-                   mech_callback_t *callback)
+static struct auth_request *mech_plain_auth_new(void)
 {
         struct auth_request *auth_request;
-       struct auth_client_request_reply reply;
        pool_t pool;
 
        pool = pool_alloconly_create("plain_auth_request", 256);
        auth_request = p_new(pool, struct auth_request, 1);
        auth_request->refcount = 1;
        auth_request->pool = pool;
+       auth_request->auth_initial = mech_plain_auth_initial;
        auth_request->auth_continue = mech_plain_auth_continue;
         auth_request->auth_free = mech_plain_auth_free;
-
-       /* initialize reply */
-       memset(&reply, 0, sizeof(reply));
-       reply.id = id;
-       reply.result = AUTH_CLIENT_RESULT_CONTINUE;
-
-       callback(&reply, NULL, conn);
        return auth_request;
 }
 
 struct mech_module mech_plain = {
-       AUTH_MECH_PLAIN,
+       "PLAIN",
+
+       MEMBER(plaintext) TRUE,
+       MEMBER(advertise) FALSE,
+
+       MEMBER(passdb_need_plain) TRUE,
+       MEMBER(passdb_need_credentials) FALSE,
+
        mech_plain_auth_new
 };
index 3656a55a147492dc71782e662fd06e1857fabf86..f8b11b23002c90b92a66ae593aae0c6feb4b804e 100644 (file)
@@ -5,19 +5,14 @@
 #include "buffer.h"
 #include "hash.h"
 #include "mech.h"
+#include "str.h"
 #include "var-expand.h"
 #include "auth-client-connection.h"
 #include "auth-master-connection.h"
 
 #include <stdlib.h>
 
-struct mech_module_list {
-       struct mech_module_list *next;
-
-       struct mech_module module;
-};
-
-enum auth_mech auth_mechanisms;
+struct mech_module_list *mech_modules;
 const char *const *auth_realms;
 const char *default_realm;
 const char *anonymous_username;
@@ -25,17 +20,12 @@ char username_chars[256];
 
 static int set_use_cyrus_sasl;
 static int ssl_require_client_cert;
-static struct mech_module_list *mech_modules;
 static struct auth_client_request_reply failure_reply;
 
 void mech_register_module(struct mech_module *module)
 {
        struct mech_module_list *list;
 
-       i_assert((auth_mechanisms & module->mech) == 0);
-
-       auth_mechanisms |= module->mech;
-
        list = i_new(struct mech_module_list, 1);
        list->module = *module;
 
@@ -47,13 +37,8 @@ void mech_unregister_module(struct mech_module *module)
 {
        struct mech_module_list **pos, *list;
 
-       if ((auth_mechanisms & module->mech) == 0)
-               return; /* not registered */
-
-        auth_mechanisms &= ~module->mech;
-
        for (pos = &mech_modules; *pos != NULL; pos = &(*pos)->next) {
-               if ((*pos)->module.mech == module->mech) {
+               if (strcmp((*pos)->module.mech_name, module->mech_name) == 0) {
                        list = *pos;
                        *pos = (*pos)->next;
                        i_free(list);
@@ -62,17 +47,56 @@ void mech_unregister_module(struct mech_module *module)
        }
 }
 
+const string_t *auth_mechanisms_get_list(void)
+{
+       struct mech_module_list *list;
+       string_t *str;
+
+       str = t_str_new(128);
+       for (list = mech_modules; list != NULL; list = list->next)
+               str_append(str, list->module.mech_name);
+
+       return str;
+}
+
+static struct mech_module *mech_module_find(const char *name)
+{
+       struct mech_module_list *list;
+
+       for (list = mech_modules; list != NULL; list = list->next) {
+               if (strcmp(list->module.mech_name, name) == 0)
+                       return &list->module;
+       }
+       return NULL;
+}
+
 void mech_request_new(struct auth_client_connection *conn,
                      struct auth_client_request_new *request,
+                     const unsigned char *data,
                      mech_callback_t *callback)
 {
-       struct mech_module_list *list;
+        struct mech_module *mech;
        struct auth_request *auth_request;
 
-       if ((auth_mechanisms & request->mech) == 0) {
+       /* make sure data is NUL-terminated */
+       if (request->data_size == 0 || request->initial_resp_idx == 0 ||
+           request->mech_idx >= request->data_size ||
+           request->protocol_idx >= request->data_size ||
+           request->initial_resp_idx > request->data_size ||
+           data[request->initial_resp_idx-1] != '\0') {
+               i_error("BUG: Auth client %u sent corrupted request",
+                       conn->pid);
+               failure_reply.id = request->id;
+               callback(&failure_reply, NULL, conn);
+               return;
+       }
+
+       mech = mech_module_find((const char *)data + request->mech_idx);
+       if (mech == NULL) {
                /* unsupported mechanism */
                i_error("BUG: Auth client %u requested unsupported "
-                       "auth mechanism %d", conn->pid, request->mech);
+                       "auth mechanism %s", conn->pid,
+                       (const char *)data + request->mech_idx);
                failure_reply.id = request->id;
                callback(&failure_reply, NULL, conn);
                return;
@@ -89,32 +113,26 @@ void mech_request_new(struct auth_client_connection *conn,
        }
 
 #ifdef USE_CYRUS_SASL2
-       if (set_use_cyrus_sasl) {
+       if (set_use_cyrus_sasl)
                auth_request = mech_cyrus_sasl_new(conn, request, callback);
-       else
+       else
 #endif
-       {
-               auth_request = NULL;
-
-               for (list = mech_modules; list != NULL; list = list->next) {
-                       if (list->module.mech == request->mech) {
-                               auth_request =
-                                       list->module.auth_new(conn, request->id,
-                                                             callback);
-                               break;
-                       }
-               }
-       }
+               auth_request = mech->auth_new();
 
        if (auth_request != NULL) {
                auth_request->created = ioloop_time;
                auth_request->conn = conn;
                auth_request->id = request->id;
-               strocpy(auth_request->protocol, request->protocol,
-                       sizeof(auth_request->protocol));
+               auth_request->protocol =
+                       p_strdup(auth_request->pool,
+                                (const char *)data + request->protocol_idx);
 
                hash_insert(conn->auth_requests, POINTER_CAST(request->id),
                            auth_request);
+
+               if (!auth_request->auth_initial(auth_request, request, data,
+                                               callback))
+                       mech_request_free(auth_request, request->id);
        }
 }
 
@@ -133,7 +151,8 @@ void mech_request_continue(struct auth_client_connection *conn,
                callback(&failure_reply, NULL, conn);
        } else {
                if (!auth_request->auth_continue(auth_request,
-                                                request, data, callback))
+                                                data, request->data_size,
+                                                callback))
                        mech_request_free(auth_request, request->id);
        }
 }
@@ -276,7 +295,6 @@ void mech_init(void)
        const char *env;
 
         mech_modules = NULL;
-       auth_mechanisms = 0;
 
        memset(&failure_reply, 0, sizeof(failure_reply));
        failure_reply.result = AUTH_CLIENT_RESULT_FAILURE;
@@ -312,7 +330,7 @@ void mech_init(void)
                mechanisms++;
        }
 
-       if (auth_mechanisms == 0)
+       if (mech_modules == NULL)
                i_fatal("No authentication mechanisms configured");
 
        /* get our realm - note that we allocate from data stack so
index c98d45a91af899c2f61653bf189b528f0ec1133c..981c933c20cf64f70e1a0704627e9980c5052c41 100644 (file)
@@ -19,26 +19,38 @@ struct auth_request {
        unsigned int id;
        time_t created;
 
-       char protocol[AUTH_CLIENT_PROTOCOL_BUF_SIZE];
+       char *protocol;
        mech_callback_t *callback;
 
+       int (*auth_initial)(struct auth_request *auth_request,
+                            struct auth_client_request_new *request,
+                           const unsigned char *data,
+                           mech_callback_t *callback);
        int (*auth_continue)(struct auth_request *auth_request,
-                            struct auth_client_request_continue *request,
-                            const unsigned char *data,
+                            const unsigned char *data, size_t data_size,
                             mech_callback_t *callback);
        void (*auth_free)(struct auth_request *auth_request);
        /* ... mechanism specific data ... */
 };
 
 struct mech_module {
-       enum auth_mech mech;
+       const char *mech_name;
 
-       struct auth_request *(*auth_new)(struct auth_client_connection *conn,
-                                        unsigned int id,
-                                        mech_callback_t *callback);
+       unsigned int plaintext:1;
+       unsigned int advertise:1;
+       unsigned int passdb_need_plain:1;
+       unsigned int passdb_need_credentials:1;
+
+       struct auth_request *(*auth_new)(void);
 };
 
-extern enum auth_mech auth_mechanisms;
+struct mech_module_list {
+       struct mech_module_list *next;
+
+       struct mech_module module;
+};
+
+extern struct mech_module_list *mech_modules;
 extern const char *const *auth_realms;
 extern const char *default_realm;
 extern const char *anonymous_username;
@@ -48,8 +60,11 @@ extern int ssl_require_client_cert;
 void mech_register_module(struct mech_module *module);
 void mech_unregister_module(struct mech_module *module);
 
+const string_t *auth_mechanisms_get_list(void);
+
 void mech_request_new(struct auth_client_connection *conn,
                      struct auth_client_request_new *request,
+                     const unsigned char *data,
                      mech_callback_t *callback);
 void mech_request_continue(struct auth_client_connection *conn,
                           struct auth_client_request_continue *request,
@@ -70,6 +85,7 @@ void mech_cyrus_sasl_init_lib(void);
 struct auth_request *
 mech_cyrus_sasl_new(struct auth_client_connection *conn,
                    struct auth_client_request_new *request,
+                   const unsigned char *data,
                    mech_callback_t *callback);
 
 void auth_request_ref(struct auth_request *request);
index 816187f806fb42e4f4a3c8bd979e53f25a8506d9..558c8f135cbe73e2e65c255708fcc8430d9948aa 100644 (file)
@@ -71,6 +71,26 @@ void passdb_handle_credentials(enum passdb_credentials credentials,
        callback(password, auth_request);
 }
 
+static void
+mech_list_verify_passdb(struct passdb_module *passdb, const char *name)
+{
+       struct mech_module_list *list;
+
+       for (list = mech_modules; list != NULL; list = list->next) {
+               if (list->module.passdb_need_plain &&
+                   passdb->verify_plain == NULL)
+                       break;
+               if (list->module.passdb_need_credentials &&
+                   passdb->lookup_credentials == NULL)
+                       break;
+       }
+
+       if (list != NULL) {
+               i_fatal("Passdb %s doesn't support %s method",
+                       name, list->module.mech_name);
+       }
+}
+
 void passdb_init(void)
 {
        const char *name, *args;
@@ -135,17 +155,7 @@ void passdb_init(void)
        if (passdb->init != NULL)
                passdb->init(args != NULL ? args+1 : "");
 
-       if ((auth_mechanisms & AUTH_MECH_PLAIN) &&
-           passdb->verify_plain == NULL)
-               i_fatal("Passdb %s doesn't support PLAIN method", name);
-
-       if ((auth_mechanisms & AUTH_MECH_CRAM_MD5) &&
-           passdb->lookup_credentials == NULL)
-               i_fatal("Passdb %s doesn't support CRAM-MD5 method", name);
-
-       if ((auth_mechanisms & AUTH_MECH_DIGEST_MD5) &&
-           passdb->lookup_credentials == NULL)
-               i_fatal("Passdb %s doesn't support DIGEST-MD5 method", name);
+       mech_list_verify_passdb(passdb, name);
 }
 
 void passdb_deinit(void)
index c9305d0a9276c6197f597d8927fdd9fa4da1df9a..ef7c11fed85b6a0221e8725e9432e712a8b43f6b 100644 (file)
@@ -10,7 +10,6 @@
 #include "str.h"
 #include "imap-parser.h"
 #include "auth-client.h"
-#include "../auth/auth-mech-desc.h"
 #include "ssl-proxy.h"
 #include "client.h"
 #include "client-authenticate.h"
 
 const char *client_authenticate_get_capabilities(int secured)
 {
-       static enum auth_mech cached_auth_mechs = 0;
-       static char *cached_capability = NULL;
-        enum auth_mech auth_mechs;
+       const struct auth_mech_desc *mech;
+       unsigned int i, count;
        string_t *str;
-       int i;
-
-       auth_mechs = auth_client_get_available_mechs(auth_client);
-       if (auth_mechs == cached_auth_mechs)
-               return cached_capability;
-
-       cached_auth_mechs = auth_mechs;
-       i_free(cached_capability);
 
        str = t_str_new(128);
-
-       for (i = 0; i < AUTH_MECH_COUNT; i++) {
-               if ((auth_mechs & auth_mech_desc[i].mech) == 0)
-                       continue; /* not available */
-
+       mech = auth_client_get_available_mechs(auth_client, &count);
+       for (i = 0; i < count; i++) {
                /* a) transport is secured
                   b) auth mechanism isn't plaintext
                   c) we allow insecure authentication
                        - but don't advertise AUTH=PLAIN, as RFC 2595 requires
                */
-               if (secured || !auth_mech_desc[i].plaintext ||
-                   (!disable_plaintext_auth &&
-                    auth_mech_desc[i].mech != AUTH_MECH_PLAIN)) {
+               if (mech[i].advertise &&
+                   (secured || !mech[i].plaintext)) {
                        str_append_c(str, ' ');
                        str_append(str, "AUTH=");
-                       str_append(str, auth_mech_desc[i].name);
+                       str_append(str, mech[i].name);
                }
        }
 
-       cached_capability = i_strdup_empty(str_c(str));
-       return cached_capability;
-}
-
-static struct auth_mech_desc *auth_mech_find(const char *name)
-{
-       int i;
-
-       for (i = 0; i < AUTH_MECH_COUNT; i++) {
-               if (auth_mech_desc[i].name != NULL &&
-                   strcasecmp(auth_mech_desc[i].name, name) == 0)
-                       return &auth_mech_desc[i];
-       }
-
-       return NULL;
+       return str_c(str);
 }
 
 static void client_auth_abort(struct imap_client *client, const char *msg)
@@ -207,9 +179,10 @@ int cmd_login(struct imap_client *client, struct imap_arg *args)
        client_ref(client);
 
        client->common.auth_request =
-               auth_client_request_new(auth_client, AUTH_MECH_PLAIN, "IMAP",
+               auth_client_request_new(auth_client, "PLAIN", "IMAP",
                                        client_get_auth_flags(client),
-                                       login_callback, client, &error);
+                                       NULL, 0, login_callback,
+                                       client, &error);
        if (client->common.auth_request == NULL) {
                client_send_tagline(client, t_strconcat(
                        "NO Login failed: ", error, NULL));
@@ -307,7 +280,7 @@ static void client_auth_input(void *context)
 
 int cmd_authenticate(struct imap_client *client, struct imap_arg *args)
 {
-       struct auth_mech_desc *mech;
+       const struct auth_mech_desc *mech;
        const char *mech_name, *error;
 
        /* we want only one argument: authentication mechanism name */
@@ -320,7 +293,7 @@ int cmd_authenticate(struct imap_client *client, struct imap_arg *args)
        if (*mech_name == '\0')
                return FALSE;
 
-       mech = auth_mech_find(mech_name);
+       mech = auth_client_find_mech(auth_client, mech_name);
        if (mech == NULL) {
                client_send_tagline(client,
                                    "NO Unsupported authentication mechanism.");
@@ -335,9 +308,9 @@ int cmd_authenticate(struct imap_client *client, struct imap_arg *args)
 
        client_ref(client);
        client->common.auth_request =
-               auth_client_request_new(auth_client, mech->mech, "IMAP",
+               auth_client_request_new(auth_client, mech->name, "IMAP",
                                        client_get_auth_flags(client),
-                                       authenticate_callback,
+                                       NULL, 0, authenticate_callback,
                                        client, &error);
        if (client->common.auth_request != NULL) {
                /* following input data will go to authentication */
index d9981f07bc3311f086b5c4effebe8105680b101b..cac6b43b535d07ca8031f1bd5d444d94467fa177 100644 (file)
@@ -1,6 +1,7 @@
 /* Copyright (C) 2003 Timo Sirainen */
 
 #include "lib.h"
+#include "buffer.h"
 #include "ioloop.h"
 #include "hash.h"
 #include "auth-client.h"
@@ -15,6 +16,8 @@ struct auth_client *auth_client_new(unsigned int client_pid)
 
        client = i_new(struct auth_client, 1);
        client->pid = client_pid;
+       client->available_auth_mechs =
+               buffer_create_dynamic(default_pool, 128, (size_t)-1);
 
        auth_client_connect_missing_servers(client);
        return client;
@@ -23,6 +26,14 @@ struct auth_client *auth_client_new(unsigned int client_pid)
 void auth_client_free(struct auth_client *client)
 {
        struct auth_server_connection *next;
+       struct auth_mech_desc *mech;
+       size_t i, size;
+
+       mech = buffer_get_modifyable_data(client->available_auth_mechs, &size);
+       size /= sizeof(*mech);
+       for (i = 0; i < size; i++)
+               i_free(mech[i].name);
+       buffer_free(client->available_auth_mechs);
 
        while (client->connections != NULL) {
                next = client->connections->next;
@@ -35,9 +46,32 @@ void auth_client_free(struct auth_client *client)
        i_free(client);
 }
 
-enum auth_mech auth_client_get_available_mechs(struct auth_client *client)
+const struct auth_mech_desc *
+auth_client_get_available_mechs(struct auth_client *client,
+                               unsigned int *mech_count)
 {
-       return client->available_auth_mechs;
+       const struct auth_mech_desc *mechs;
+       size_t size;
+
+       mechs = buffer_get_data(client->available_auth_mechs, &size);
+       *mech_count = size / sizeof(*mechs);
+       return mechs;
+}
+
+const struct auth_mech_desc *
+auth_client_find_mech(struct auth_client *client, const char *name)
+{
+       const struct auth_mech_desc *mech;
+       size_t i, size;
+
+       mech = buffer_get_data(client->available_auth_mechs, &size);
+       size /= sizeof(*mech);
+       for (i = 0; i < size; i++) {
+               if (strcasecmp(mech[i].name, name) == 0)
+                       return &mech[i];
+       }
+
+       return NULL;
 }
 
 int auth_client_is_connected(struct auth_client *client)
index 411db4554f9307092bca1115bd1598dd5cca46c4..73c7a097f3a1e61bb8b8cd641a2b1048a7dcbdb6 100644 (file)
@@ -6,6 +6,12 @@
 struct auth_client;
 struct auth_request;
 
+struct auth_mech_desc {
+       char *name;
+       unsigned int plaintext:1;
+       unsigned int advertise:1;
+};
+
 /* reply is NULL if auth connection died */
 typedef void auth_request_callback_t(struct auth_request *request,
                                     struct auth_client_request_reply *reply,
@@ -22,7 +28,11 @@ int auth_client_is_connected(struct auth_client *client);
 void auth_client_set_connect_notify(struct auth_client *client,
                                    auth_connect_notify_callback_t *callback,
                                    void *context);
-enum auth_mech auth_client_get_available_mechs(struct auth_client *client);
+const struct auth_mech_desc *
+auth_client_get_available_mechs(struct auth_client *client,
+                               unsigned int *mech_count);
+const struct auth_mech_desc *
+auth_client_find_mech(struct auth_client *client, const char *name);
 
 void auth_client_connect_missing_servers(struct auth_client *client);
 
@@ -30,8 +40,10 @@ void auth_client_connect_missing_servers(struct auth_client *client);
    happens for the request. */
 struct auth_request *
 auth_client_request_new(struct auth_client *client,
-                       enum auth_mech mech, const char *protocol,
+                       const char *mech, const char *protocol,
                        enum auth_client_request_new_flags flags,
+                       const unsigned char *initial_resp_data,
+                       size_t initial_resp_size,
                        auth_request_callback_t *callback, void *context,
                        const char **error_r);
 
index 1fa6ea4def825345f06a5ed689622761c303779b..d39357bf50452decaf14898d55508be58f7cbfcd 100644 (file)
@@ -1,6 +1,7 @@
 /* Copyright (C) 2003 Timo Sirainen */
 
 #include "lib.h"
+#include "buffer.h"
 #include "hash.h"
 #include "ioloop.h"
 #include "istream.h"
 
 static void auth_server_connection_unref(struct auth_server_connection *conn);
 
-static void update_available_auth_mechs(struct auth_client *client)
+static void update_available_auth_mechs(struct auth_server_connection *conn)
 {
-       struct auth_server_connection *conn;
-
-        client->available_auth_mechs = 0;
-       for (conn = client->connections; conn != NULL; conn = conn->next)
-                client->available_auth_mechs |= conn->available_auth_mechs;
+       struct auth_client *client = conn->client;
+       const struct auth_mech_desc *mech;
+       struct auth_mech_desc *new_mech;
+       unsigned int i;
+
+       mech = conn->available_auth_mechs;
+       for (i = 0; i < conn->available_auth_mechs_count; i++) {
+               if (auth_client_find_mech(client, mech[i].name) == NULL) {
+                       new_mech = buffer_append_space_unsafe(
+                               client->available_auth_mechs, sizeof(*mech));
+                       *new_mech = mech[i];
+                       new_mech->name = i_strdup(mech[i].name);
+               }
+       }
 }
 
 static void auth_handle_handshake(struct auth_server_connection *conn,
-                                 struct auth_client_handshake_reply *handshake)
+                                 struct auth_client_handshake_reply *handshake,
+                                 const unsigned char *data)
 {
+       struct auth_client_handshake_mech_desc handshake_mech_desc;
+       struct auth_mech_desc mech_desc;
+       buffer_t *buf;
+       unsigned int i;
+
        if (handshake->server_pid == 0) {
                i_error("BUG: Auth server said it's PID 0");
                auth_server_connection_destroy(conn, FALSE);
                return;
        }
 
+       if (handshake->data_size == 0 || data[handshake->data_size-1] != '\0' ||
+           handshake->mech_count * sizeof(handshake_mech_desc) >=
+           handshake->data_size)  {
+               i_error("BUG: Auth server sent corrupted handshake");
+               auth_server_connection_destroy(conn, FALSE);
+               return;
+       }
+
+       buf = buffer_create_dynamic(conn->pool, sizeof(mech_desc) *
+                                   handshake->mech_count, (size_t)-1);
+       for (i = 0; i < handshake->mech_count; i++) {
+               memcpy(&handshake_mech_desc,
+                      data + sizeof(handshake_mech_desc) * i,
+                      sizeof(handshake_mech_desc));
+
+               if (handshake_mech_desc.name_idx >= handshake->data_size) {
+                       i_error("BUG: Auth server sent corrupted handshake");
+                       auth_server_connection_destroy(conn, FALSE);
+                       return;
+               }
+
+               mech_desc.name = p_strdup(conn->pool, (const char *)data +
+                                         handshake_mech_desc.name_idx);
+               mech_desc.plaintext = handshake_mech_desc.plaintext;
+               mech_desc.advertise = handshake_mech_desc.advertise;
+               buffer_append(buf, &mech_desc, sizeof(mech_desc));
+
+               if (strcmp(mech_desc.name, "PLAIN") == 0)
+                       conn->has_plain_mech = TRUE;
+       }
+
        conn->pid = handshake->server_pid;
-       conn->available_auth_mechs = handshake->auth_mechanisms;
+       conn->available_auth_mechs_count =
+               buffer_get_used_size(buf) / sizeof(mech_desc);
+       conn->available_auth_mechs = buffer_free_without_data(buf);
        conn->handshake_received = TRUE;
 
         conn->client->conn_waiting_handshake_count--;
-       update_available_auth_mechs(conn->client);
+       update_available_auth_mechs(conn);
 
        if (conn->client->connect_notify_callback != NULL &&
            auth_client_is_connected(conn->client)) {
@@ -77,17 +126,19 @@ static void auth_client_input(void *context)
 
        if (!conn->handshake_received) {
                data = i_stream_get_data(conn->input, &size);
-               if (size == sizeof(handshake)) {
-                       memcpy(&handshake, data, sizeof(handshake));
-                       i_stream_skip(conn->input, sizeof(handshake));
-
-                       auth_handle_handshake(conn, &handshake);
-               } else if (size > sizeof(handshake)) {
-                       i_error("BUG: Auth server sent us too large handshake "
-                               "(%"PRIuSIZE_T " vs %"PRIuSIZE_T")", size,
-                               sizeof(handshake));
-                       auth_server_connection_destroy(conn, FALSE);
-               }
+               if (size < sizeof(handshake))
+                       return;
+
+               memcpy(&handshake, data, sizeof(handshake));
+               if (size < sizeof(handshake) + handshake.data_size)
+                       return;
+
+               conn->refcount++;
+               auth_handle_handshake(conn, &handshake,
+                                     data + sizeof(handshake));
+               i_stream_skip(conn->input, sizeof(handshake) +
+                             handshake.data_size);
+               auth_server_connection_unref(conn);
                return;
        }
 
@@ -210,6 +261,7 @@ static void auth_server_connection_unref(struct auth_server_connection *conn)
 {
        if (--conn->refcount > 0)
                return;
+       i_assert(conn->refcount == 0);
 
        hash_destroy(conn->requests);
 
@@ -233,16 +285,21 @@ auth_server_connection_find_path(struct auth_client *client, const char *path)
 
 struct auth_server_connection *
 auth_server_connection_find_mech(struct auth_client *client,
-                                enum auth_mech mech, const char **error_r)
+                                const char *name, const char **error_r)
 {
        struct auth_server_connection *conn;
+       const struct auth_mech_desc *mech;
+       unsigned int i;
 
        for (conn = client->connections; conn != NULL; conn = conn->next) {
-               if ((conn->available_auth_mechs & mech))
-                       return conn;
+               mech = conn->available_auth_mechs;
+               for (i = 0; i < conn->available_auth_mechs_count; i++) {
+                       if (strcmp(mech[i].name, name) == 0)
+                               return conn;
+               }
        }
 
-       if ((client->available_auth_mechs & mech) == 0)
+       if (auth_client_find_mech(client, name) == NULL)
                *error_r = "Unsupported authentication mechanism";
        else {
                *error_r = "Authentication server isn't connected, "
index 4e212552881b0990c8fa9fd0605ed6f479d260a5..5ba25b2c7f142806fa7b12a400d0d4480b89c95d 100644 (file)
@@ -9,7 +9,7 @@ struct auth_client {
 
        unsigned int conn_waiting_handshake_count;
 
-       enum auth_mech available_auth_mechs;
+       buffer_t *available_auth_mechs;
        unsigned int request_id_counter;
 
        auth_connect_notify_callback_t *connect_notify_callback;
@@ -31,13 +31,15 @@ struct auth_server_connection {
        struct ostream *output;
 
        unsigned int pid;
-       enum auth_mech available_auth_mechs;
+       const struct auth_mech_desc *available_auth_mechs;
+       unsigned int available_auth_mechs_count;
         struct auth_client_request_reply reply;
 
         struct hash_table *requests;
 
        unsigned int handshake_received:1;
        unsigned int reply_received:1;
+       unsigned int has_plain_mech:1;
 };
 
 struct auth_server_connection *
@@ -50,6 +52,6 @@ auth_server_connection_find_path(struct auth_client *client, const char *path);
 
 struct auth_server_connection *
 auth_server_connection_find_mech(struct auth_client *client,
-                                enum auth_mech mech, const char **error_r);
+                                const char *name, const char **error_r);
 
 #endif
index 48983dd1ecd5ea72c7d2316d0beebec47ee63754..f343bd3e500fbcf9a0716ee7c688014a988ddc0e 100644 (file)
@@ -1,6 +1,7 @@
 /* Copyright (C) 2003 Timo Sirainen */
 
 #include "lib.h"
+#include "buffer.h"
 #include "hash.h"
 #include "ostream.h"
 #include "auth-client.h"
 struct auth_request {
         struct auth_server_connection *conn;
 
-       enum auth_mech mech;
-       char protocol[AUTH_CLIENT_PROTOCOL_BUF_SIZE];
+       char *mech, *protocol;
        enum auth_client_request_new_flags flags;
 
        unsigned int id;
 
+       unsigned char *initial_resp_data;
+       size_t initial_resp_size;
+
        auth_request_callback_t *callback;
        void *context;
 
@@ -31,16 +34,42 @@ static int auth_server_send_new_request(struct auth_server_connection *conn,
                                        struct auth_request *request)
 {
        struct auth_client_request_new auth_request;
+       buffer_t *buf;
+       int ret;
 
+       memset(&auth_request, 0, sizeof(auth_request));
        auth_request.type = AUTH_CLIENT_REQUEST_NEW;
        auth_request.id = request->id;
-       strocpy(auth_request.protocol, request->protocol,
-               sizeof(auth_request.protocol));
-       auth_request.mech = request->mech;
        auth_request.flags = request->flags;
 
-       if (o_stream_send(conn->output, &auth_request,
-                         sizeof(auth_request)) < 0) {
+       t_push();
+       buf = buffer_create_dynamic(pool_datastack_create(), 256, (size_t)-1);
+       buffer_set_used_size(buf, sizeof(auth_request));
+
+       auth_request.mech_idx =
+               buffer_get_used_size(buf) - sizeof(auth_request);
+       buffer_append(buf, request->mech, strlen(request->mech)+1);
+
+       auth_request.protocol_idx =
+               buffer_get_used_size(buf) - sizeof(auth_request);
+       buffer_append(buf, request->protocol, strlen(request->protocol)+1);
+
+       auth_request.initial_resp_idx =
+               buffer_get_used_size(buf) - sizeof(auth_request);
+       buffer_append(buf, request->initial_resp_data,
+                     request->initial_resp_size);
+
+       auth_request.data_size =
+               buffer_get_used_size(buf) - sizeof(auth_request);
+
+       memcpy(buffer_get_space_unsafe(buf, 0, sizeof(auth_request)),
+              &auth_request, sizeof(auth_request));
+
+       ret = o_stream_send(conn->output, buffer_get_data(buf, NULL),
+                           buffer_get_used_size(buf));
+       t_pop();
+
+       if (ret < 0) {
                errno = conn->output->stream_errno;
                i_warning("Error sending request to auth server: %m");
                auth_server_connection_destroy(conn, TRUE);
@@ -75,7 +104,7 @@ get_next_plain_server(struct auth_server_connection *conn)
 {
        conn = conn->next;
        while (conn != NULL) {
-               if ((conn->available_auth_mechs & AUTH_MECH_PLAIN) != 0)
+               if (conn->has_plain_mech)
                        return conn;
                conn = conn->next;
        }
@@ -179,8 +208,10 @@ void auth_server_requests_remove_all(struct auth_server_connection *conn)
 
 struct auth_request *
 auth_client_request_new(struct auth_client *client,
-                       enum auth_mech mech, const char *protocol,
+                       const char *mech, const char *protocol,
                        enum auth_client_request_new_flags flags,
+                       const unsigned char *initial_resp_data,
+                       size_t initial_resp_size,
                        auth_request_callback_t *callback, void *context,
                        const char **error_r)
 {
@@ -193,10 +224,18 @@ auth_client_request_new(struct auth_client *client,
 
        request = i_new(struct auth_request, 1);
        request->conn = conn;
-       request->mech = mech;
-       strocpy(request->protocol, protocol, sizeof(request->protocol));
+       request->mech = i_strdup(mech);
+       request->protocol = i_strdup(protocol);
        request->flags = flags;
        request->id = ++client->request_id_counter;
+
+       if (initial_resp_size != 0) {
+               request->initial_resp_size = initial_resp_size;
+               request->initial_resp_data = i_malloc(initial_resp_size);
+               memcpy(request->initial_resp_data, initial_resp_data,
+                      initial_resp_size);
+       }
+       
        if (request->id == 0) {
                /* wrapped - ID 0 not allowed */
                request->id = ++client->request_id_counter;
@@ -216,8 +255,8 @@ void auth_client_request_continue(struct auth_request *request,
 {
        auth_server_send_continue(request->conn, request, data, data_size);
 
-       if (request->mech == AUTH_MECH_PLAIN &&
-           request->plaintext_data == NULL) {
+       if (strcmp(request->mech, "PLAIN") == 0 &&
+           request->plaintext_data == NULL && request->conn != NULL) {
                request->next_conn = get_next_plain_server(request->conn);
                if (request->next_conn != NULL) {
                        /* plaintext authentication - save the data so we can
@@ -245,7 +284,10 @@ void auth_client_request_abort(struct auth_request *request)
 
        request->callback(request, NULL, NULL, request->context);
 
+       i_free(request->initial_resp_data);
        i_free(request->plaintext_data);
+       i_free(request->mech);
+       i_free(request->protocol);
        i_free(request);
 }
 
index d2e6aac3092c9ec08b835b3e7820dc3a70011187..1ac4342a40656cd40c6732f2b7951db8b41267fd 100644 (file)
@@ -371,13 +371,27 @@ int file_lock_dotlock(const char *path, const char *temp_prefix, int checkonly,
                return -1;
        }
 
+       dotlock_r->dev = st.st_dev;
+       dotlock_r->ino = st.st_ino;
+
        if (close(fd) < 0) {
                i_error("fstat(%s) failed: %m", lock_path);
                return -1;
        }
 
-       dotlock_r->dev = st.st_dev;
-       dotlock_r->ino = st.st_ino;
+       /* some NFS implementations may have used cached mtime in previous
+          fstat() call. Check again to avoid "dotlock was modified" errors. */
+       if (stat(lock_path, &st) < 0) {
+               i_error("stat(%s) failed: %m", lock_path);
+               return -1;
+       }
+       /* extra sanity check won't hurt.. */
+       if (st.st_dev != dotlock_r->dev ||
+           st.st_ino != dotlock_r->ino) {
+               i_error("dotlock %s was immediately recreated under us",
+                       lock_path);
+               return -1;
+       }
        dotlock_r->mtime = st.st_mtime;
        return 1;
 }
index 6dfa6343a4386135ea6216d1c6dde877619ea00b..77d802e66d0758b27c1115ec58e0c02eabedadcb 100644 (file)
@@ -9,7 +9,6 @@
 #include "safe-memset.h"
 #include "str.h"
 #include "auth-client.h"
-#include "../auth/auth-mech-desc.h"
 #include "../pop3/capability.h"
 #include "ssl-proxy.h"
 #include "master.h"
 
 int cmd_capa(struct pop3_client *client, const char *args __attr_unused__)
 {
-       static enum auth_mech cached_auth_mechs = 0;
-       static char *cached_capability = NULL;
-        enum auth_mech auth_mechs;
+       const struct auth_mech_desc *mech;
+       unsigned int i, count;
        string_t *str;
-       int i;
-
-       auth_mechs = auth_client_get_available_mechs(auth_client);
-       if (cached_auth_mechs != auth_mechs) {
-               cached_auth_mechs = auth_mechs;
-               i_free(cached_capability);
-
-               str = t_str_new(128);
-
-               str_append(str, "SASL");
-               for (i = 0; i < AUTH_MECH_COUNT; i++) {
-                       if ((auth_mechs & auth_mech_desc[i].mech) == 0)
-                               continue; /* not available */
-
-                       /* a) transport is secured
-                          b) auth mechanism isn't plaintext
-                          c) we allow insecure authentication
-                              - but don't advertise AUTH=PLAIN,
-                                as RFC 2595 requires
-                       */
-                       if (client->secured || !auth_mech_desc[i].plaintext ||
-                           (!disable_plaintext_auth &&
-                            auth_mech_desc[i].mech != AUTH_MECH_PLAIN)) {
-                               str_append_c(str, ' ');
-                               str_append(str, auth_mech_desc[i].name);
-                       }
-               }
 
-               cached_capability = i_strdup(str_c(str));
+       str = t_str_new(128);
+       str_append(str, "SASL");
+
+       mech = auth_client_get_available_mechs(auth_client, &count);
+       for (i = 0; i < count; i++) {
+               /* a) transport is secured
+                  b) auth mechanism isn't plaintext
+                  c) we allow insecure authentication
+                       - but don't advertise AUTH=PLAIN, as RFC 2595 requires
+               */
+               if (mech[i].advertise &&
+                   (client->secured || !mech[i].plaintext)) {
+                       str_append_c(str, ' ');
+                       str_append(str, "AUTH=");
+                       str_append(str, mech[i].name);
+               }
        }
 
        client_send_line(client, t_strconcat("+OK\r\n" POP3_CAPABILITY_REPLY,
                                             (ssl_initialized && !client->tls) ?
                                             "STLS\r\n" : "",
-                                            cached_capability,
+                                            str_c(str),
                                             "\r\n.", NULL));
        return TRUE;
 }
 
-static struct auth_mech_desc *auth_mech_find(const char *name)
-{
-       int i;
-
-       for (i = 0; i < AUTH_MECH_COUNT; i++) {
-               if (auth_mech_desc[i].name != NULL &&
-                   strcasecmp(auth_mech_desc[i].name, name) == 0)
-                       return &auth_mech_desc[i];
-       }
-
-       return NULL;
-}
-
 static void client_auth_abort(struct pop3_client *client, const char *msg)
 {
        if (client->common.auth_request != NULL) {
@@ -150,23 +123,15 @@ static void login_callback(struct auth_request *request,
 {
        struct pop3_client *client = context;
        const char *error;
-       const void *ptr;
-       size_t size;
 
        switch (auth_callback(request, reply, data, &client->common,
                              master_callback, &error)) {
        case -1:
+       case 0:
                /* login failed */
                client_auth_abort(client, error);
                break;
 
-       case 0:
-               ptr = buffer_get_data(client->plain_login, &size);
-               auth_client_request_continue(request, ptr, size);
-
-               buffer_set_used_size(client->plain_login, 0);
-               break;
-
        default:
                /* success, we should be able to log in. if we fail, just
                   disconnect the client. */
@@ -206,9 +171,13 @@ int cmd_pass(struct pop3_client *client, const char *args)
 
        client_ref(client);
        client->common.auth_request =
-               auth_client_request_new(auth_client, AUTH_MECH_PLAIN, "POP3",
+               auth_client_request_new(auth_client, "PLAIN", "POP3",
                                         client_get_auth_flags(client),
+                                       str_data(client->plain_login),
+                                       str_len(client->plain_login),
                                        login_callback, client, &error);
+       buffer_set_used_size(client->plain_login, 0);
+
        if (client->common.auth_request != NULL) {
                /* don't read any input from client until login is finished */
                if (client->common.io != NULL) {
@@ -296,11 +265,22 @@ static void client_auth_input(void *context)
 
 int cmd_auth(struct pop3_client *client, const char *args)
 {
-       struct auth_mech_desc *mech;
-       const char *error;
+       const struct auth_mech_desc *mech;
+       const char *mech_name, *error, *p;
+       string_t *buf;
+       size_t argslen;
+
+       /* <mechanism name> <initial response> */
+       p = strchr(args, ' ');
+       if (p == NULL) {
+               mech_name = args;
+               args = "";
+       } else {
+               mech_name = t_strdup_until(args, p);
+               args = p+1;
+       }
 
-       /* we have only one argument: authentication mechanism name */
-       mech = auth_mech_find(args);
+       mech = auth_client_find_mech(auth_client, mech_name);
        if (mech == NULL) {
                client_send_line(client,
                                 "-ERR Unsupported authentication mechanism.");
@@ -313,10 +293,21 @@ int cmd_auth(struct pop3_client *client, const char *args)
                return TRUE;
        }
 
+       argslen = strlen(args);
+       buf = buffer_create_static_hard(pool_datastack_create(), argslen);
+
+       if (base64_decode((const unsigned char *)args, argslen,
+                         NULL, buf) <= 0) {
+               /* failed */
+               client_send_line(client, "-ERR Invalid base64 data.");
+               return TRUE;
+       }
+
        client_ref(client);
        client->common.auth_request =
-               auth_client_request_new(auth_client, mech->mech, "POP3",
+               auth_client_request_new(auth_client, mech->name, "POP3",
                                         client_get_auth_flags(client),
+                                       str_data(buf), str_len(buf),
                                        authenticate_callback, client, &error);
        if (client->common.auth_request != NULL) {
                /* following input data will go to authentication */