Mercurial > dovecot > core-2.2
changeset 2077:d5b20d679b8a HEAD
Removed hardcoded mechanism lists. It's now possible to add them
dynamically. Added support for SASL initial response.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 30 May 2004 00:40:30 +0300 |
parents | 6a72075e3543 |
children | b3daf55df932 |
files | 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 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 |
diffstat | 23 files changed, 638 insertions(+), 336 deletions(-) [+] |
line wrap: on
line diff
--- a/src/auth/Makefile.am Sat May 29 20:06:49 2004 +0300 +++ b/src/auth/Makefile.am Sun May 30 00:40:30 2004 +0300 @@ -58,7 +58,6 @@ 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 \
--- a/src/auth/auth-client-connection.c Sat May 29 20:06:49 2004 +0300 +++ b/src/auth/auth-client-connection.c Sun May 30 00:40:30 2004 +0300 @@ -109,19 +109,26 @@ 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 @@ 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 @@ /* 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 @@ 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 @@ 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); }
--- a/src/auth/auth-client-interface.h Sat May 29 20:06:49 2004 +0300 +++ b/src/auth/auth-client-interface.h Sun May 30 00:40:30 2004 +0300 @@ -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 @@ 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 @@ 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 @@ /* 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[]; */ };
--- a/src/auth/auth-master-connection.c Sat May 29 20:06:49 2004 +0300 +++ b/src/auth/auth-master-connection.c Sun May 30 00:40:30 2004 +0300 @@ -182,6 +182,48 @@ } } +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 @@ 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 @@ i_free(l[i]); } buffer_free(conn->listeners_buf); + conn->listeners_buf = NULL; auth_master_connection_unref(conn); } @@ -252,6 +296,7 @@ if (conn->output != NULL) o_stream_unref(conn->output); + i_free(conn->handshake_reply); i_free(conn); return FALSE; } @@ -280,7 +325,7 @@ 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)); }
--- a/src/auth/auth-master-connection.h Sat May 29 20:06:49 2004 +0300 +++ b/src/auth/auth-master-connection.h Sun May 30 00:40:30 2004 +0300 @@ -15,7 +15,7 @@ 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;
--- a/src/auth/auth-master-interface.h Sat May 29 20:06:49 2004 +0300 +++ b/src/auth/auth-master-interface.h Sun May 30 00:40:30 2004 +0300 @@ -21,11 +21,11 @@ /* 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[]; */ };
--- a/src/auth/auth-mech-desc.h Sat May 29 20:06:49 2004 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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
--- a/src/auth/main.c Sat May 29 20:06:49 2004 +0300 +++ b/src/auth/main.c Sun May 30 00:40:30 2004 +0300 @@ -132,7 +132,7 @@ 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);
--- a/src/auth/mech-anonymous.c Sat May 29 20:06:49 2004 +0300 +++ b/src/auth/mech-anonymous.c Sun May 30 00:40:30 2004 +0300 @@ -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 @@ 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 };
--- a/src/auth/mech-cram-md5.c Sat May 29 20:06:49 2004 +0300 +++ b/src/auth/mech-cram-md5.c Sun May 30 00:40:30 2004 +0300 @@ -149,15 +149,14 @@ 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 @@ 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 @@ 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 };
--- a/src/auth/mech-digest-md5.c Sat May 29 20:06:49 2004 +0300 +++ b/src/auth/mech-digest-md5.c Sun May 30 00:40:30 2004 +0300 @@ -525,8 +525,7 @@ 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 @@ /* 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 @@ 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 @@ 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 @@ 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 };
--- a/src/auth/mech-plain.c Sat May 29 20:06:49 2004 +0300 +++ b/src/auth/mech-plain.c Sun May 30 00:40:30 2004 +0300 @@ -13,8 +13,8 @@ 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 @@ 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 @@ 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 };
--- a/src/auth/mech.c Sat May 29 20:06:49 2004 +0300 +++ b/src/auth/mech.c Sun May 30 00:40:30 2004 +0300 @@ -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 @@ 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 @@ { 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_request_new(struct auth_client_connection *conn, - struct auth_client_request_new *request, - mech_callback_t *callback) +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 *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 @@ } #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 @@ 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 @@ 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 @@ 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
--- a/src/auth/mech.h Sat May 29 20:06:49 2004 +0300 +++ b/src/auth/mech.h Sun May 30 00:40:30 2004 +0300 @@ -19,26 +19,38 @@ 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 @@ 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 @@ 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);
--- a/src/auth/passdb.c Sat May 29 20:06:49 2004 +0300 +++ b/src/auth/passdb.c Sun May 30 00:40:30 2004 +0300 @@ -71,6 +71,26 @@ 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 @@ 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)
--- a/src/imap-login/client-authenticate.c Sat May 29 20:06:49 2004 +0300 +++ b/src/imap-login/client-authenticate.c Sun May 30 00:40:30 2004 +0300 @@ -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" @@ -19,54 +18,27 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 */
--- a/src/lib-auth/auth-client.c Sat May 29 20:06:49 2004 +0300 +++ b/src/lib-auth/auth-client.c Sun May 30 00:40:30 2004 +0300 @@ -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 @@ 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 @@ 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 @@ 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)
--- a/src/lib-auth/auth-client.h Sat May 29 20:06:49 2004 +0300 +++ b/src/lib-auth/auth-client.h Sun May 30 00:40:30 2004 +0300 @@ -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 @@ 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 @@ 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);
--- a/src/lib-auth/auth-server-connection.c Sat May 29 20:06:49 2004 +0300 +++ b/src/lib-auth/auth-server-connection.c Sun May 30 00:40:30 2004 +0300 @@ -1,6 +1,7 @@ /* Copyright (C) 2003 Timo Sirainen */ #include "lib.h" +#include "buffer.h" #include "hash.h" #include "ioloop.h" #include "istream.h" @@ -21,30 +22,78 @@ 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; + struct auth_client *client = conn->client; + const struct auth_mech_desc *mech; + struct auth_mech_desc *new_mech; + unsigned int i; - client->available_auth_mechs = 0; - for (conn = client->connections; conn != NULL; conn = conn->next) - client->available_auth_mechs |= conn->available_auth_mechs; + 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 @@ 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)); + if (size < sizeof(handshake)) + return; + + memcpy(&handshake, data, sizeof(handshake)); + if (size < sizeof(handshake) + handshake.data_size) + return; - 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); - } + 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 @@ { if (--conn->refcount > 0) return; + i_assert(conn->refcount == 0); hash_destroy(conn->requests); @@ -233,16 +285,21 @@ 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, "
--- a/src/lib-auth/auth-server-connection.h Sat May 29 20:06:49 2004 +0300 +++ b/src/lib-auth/auth-server-connection.h Sun May 30 00:40:30 2004 +0300 @@ -9,7 +9,7 @@ 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 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 @@ 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
--- a/src/lib-auth/auth-server-request.c Sat May 29 20:06:49 2004 +0300 +++ b/src/lib-auth/auth-server-request.c Sun May 30 00:40:30 2004 +0300 @@ -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" @@ -10,12 +11,14 @@ 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 @@ 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 @@ { 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 @@ 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 @@ 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 @@ { 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 @@ 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); }
--- a/src/lib/file-dotlock.c Sat May 29 20:06:49 2004 +0300 +++ b/src/lib/file-dotlock.c Sun May 30 00:40:30 2004 +0300 @@ -371,13 +371,27 @@ 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; }
--- a/src/pop3-login/client-authenticate.c Sat May 29 20:06:49 2004 +0300 +++ b/src/pop3-login/client-authenticate.c Sun May 30 00:40:30 2004 +0300 @@ -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" @@ -20,62 +19,36 @@ 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 = t_str_new(128); + str_append(str, "SASL"); - 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); - } + 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); } - - cached_capability = i_strdup(str_c(str)); } 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 @@ { 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 @@ 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 @@ 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; - /* we have only one argument: authentication mechanism name */ - mech = auth_mech_find(args); + /* <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; + } + + mech = auth_client_find_mech(auth_client, mech_name); if (mech == NULL) { client_send_line(client, "-ERR Unsupported authentication mechanism."); @@ -313,10 +293,21 @@ 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 */