Mercurial > dovecot > original-hg > dovecot-1.2
changeset 2736:0f31778d3c34 HEAD
Changed dovecot-auth protocol to ASCII based. Should be easier now to write
replacement server if needed by someone. Also cleaned up/made more
consistent auth code. The new code could still use some cleaning though..
line wrap: on
line diff
--- a/src/auth/Makefile.am Wed Oct 13 15:32:54 2004 +0300 +++ b/src/auth/Makefile.am Wed Oct 13 19:38:32 2004 +0300 @@ -71,7 +71,6 @@ auth-client-connection.h \ auth-client-interface.h \ auth-master-connection.h \ - auth-master-interface.h \ auth-module.h \ db-ldap.h \ db-mysql.h \
--- a/src/auth/auth-client-connection.c Wed Oct 13 15:32:54 2004 +0300 +++ b/src/auth/auth-client-connection.c Wed Oct 13 19:38:32 2004 +0300 @@ -5,48 +5,99 @@ #include "istream.h" #include "ostream.h" #include "network.h" +#include "base64.h" +#include "buffer.h" #include "hash.h" +#include "str.h" +#include "str-sanitize.h" #include "safe-memset.h" #include "mech.h" #include "auth-client-connection.h" #include "auth-master-connection.h" #include <stdlib.h> -#include <syslog.h> -#define MAX_INBUF_SIZE \ - (sizeof(struct auth_client_request_continue) + \ - AUTH_CLIENT_MAX_REQUEST_DATA_SIZE) +/* Used only for string sanitization. */ +#define MAX_MECH_NAME_LEN 64 + #define MAX_OUTBUF_SIZE (1024*50) static void auth_client_connection_unref(struct auth_client_connection *conn); -static void request_callback(struct auth_client_request_reply *reply, - const void *data, - struct auth_client_connection *conn) +static void auth_client_send(struct auth_client_connection *conn, + const char *fmt, ...) __attr_format__(2, 3); +static void auth_client_send(struct auth_client_connection *conn, + const char *fmt, ...) { - struct const_iovec iov[2]; + va_list args; + string_t *str; + ssize_t ret; + + i_assert(conn->refcount > 1); + + t_push(); + va_start(args, fmt); + str = t_str_new(256); + str_vprintfa(str, fmt, args); + str_append_c(str, '\n'); + ret = o_stream_send(conn->output, str_data(str), str_len(str)); + if (ret != (ssize_t)str->used) { + i_warning("Authentication client %u: " + "Transmit buffer full, killing it", conn->pid); + auth_client_connection_destroy(conn); + } + va_end(args); + t_pop(); +} + +static void auth_callback(struct auth_request *request, + enum auth_client_result result, + const void *reply, size_t reply_size) +{ + string_t *str = NULL; ssize_t ret; - iov[0].iov_base = reply; - iov[0].iov_len = sizeof(*reply); - iov[1].iov_base = data; - iov[1].iov_len = reply->data_size; + t_push(); - ret = o_stream_sendv(conn->output, iov, 2); - if (ret == (ssize_t)(iov[0].iov_len + iov[1].iov_len)) { - /* all sent */ - auth_client_connection_unref(conn); - return; + switch (result) { + case AUTH_CLIENT_RESULT_CONTINUE: + str = t_str_new(32 + MAX_BASE64_ENCODED_SIZE(reply_size)); + str_printfa(str, "CONT\t%u\t", request->id); + base64_encode(reply, reply_size, str); + request->accept_input = TRUE; + break; + case AUTH_CLIENT_RESULT_SUCCESS: + str = t_str_new(128 + MAX_BASE64_ENCODED_SIZE(reply_size)); + str_printfa(str, "OK\t%u\t%s", request->id, request->user); + if (reply_size > 0) { + str_append(str, "\tresp="); + base64_encode(reply, reply_size, str); + } + break; + case AUTH_CLIENT_RESULT_FAILURE: + str = t_str_new(128); + str_printfa(str, "FAIL\t%u", request->id); + if (reply != NULL) { + str_append_c(str, '\t'); + str_append(str, reply); + } + break; } - if (ret >= 0) { - i_warning("Auth client %u: Transmit buffer full, killing it", - conn->pid); + str_append_c(str, '\n'); + + ret = o_stream_send(request->conn->output, str->data, str->used); + if (ret < 0) + auth_client_connection_destroy(request->conn); + else if ((size_t)ret != str->used) { + i_warning("Authentication client %u: " + "Transmit buffer full, killing it", + request->conn->pid); + auth_client_connection_destroy(request->conn); } + t_pop(); - auth_client_connection_destroy(conn); - auth_client_connection_unref(conn); + auth_client_connection_unref(request->conn); } struct auth_client_connection * @@ -63,106 +114,229 @@ return NULL; } -static int auth_client_input_handshake(struct auth_client_connection *conn) +static int +auth_client_input_proto(struct auth_client_connection *conn, const char *args) { - struct auth_client_handshake_request rec; - unsigned char *data; - size_t size; + if (conn->default_protocol == NULL) + conn->default_protocol = p_strdup(conn->pool, args); + return TRUE; +} - data = i_stream_get_modifyable_data(conn->input, &size); - if (size < sizeof(rec)) - return FALSE; +static int +auth_client_input_cpid(struct auth_client_connection *conn, const char *args) +{ + struct auth_client_connection *old; + unsigned int pid; - /* Don't just cast because of alignment issues. */ - memcpy(&rec, data, sizeof(rec)); - i_stream_skip(conn->input, sizeof(rec)); - - if (rec.client_pid == 0) { - i_error("BUG: Auth client said it's PID 0"); - auth_client_connection_destroy(conn); + if (conn->pid != 0) { + i_error("BUG: Authentication client re-handshaking"); return FALSE; } - if (auth_client_connection_lookup(conn->master, - rec.client_pid) != NULL) { - /* well, it might have just reconnected very fast .. although - there's not much reason for it. */ - i_error("BUG: Auth client gave a PID %u of existing connection", - rec.client_pid); - auth_client_connection_destroy(conn); + pid = (unsigned int)strtoul(args, NULL, 10); + if (pid == 0) { + i_error("BUG: Authentication client said it's PID 0"); return FALSE; } - conn->pid = rec.client_pid; + old = auth_client_connection_lookup(conn->master, pid); + if (old != NULL) { + /* already exists. it's possible that it just reconnected, + see if the old connection is still there. */ + if (i_stream_read(old->input) == -1) { + auth_client_connection_destroy(old); + old = NULL; + } + } + + if (old != NULL) { + i_error("BUG: Authentication client gave a PID " + "%u of existing connection", pid); + return FALSE; + } + + conn->pid = pid; return TRUE; } -static int auth_client_input_request(struct auth_client_connection *conn) +static int +auth_client_input_auth(struct auth_client_connection *conn, const char *args) { - enum auth_client_request_type type; - unsigned char *data; - size_t size; + struct mech_module *mech; + struct auth_request *request; + const char *const *list, *name, *arg, *initial_resp; + const void *initial_resp_data; + size_t initial_resp_len; + unsigned int id; + buffer_t *buf; + int valid_client_cert; + + if (conn->pid == 0) { + i_error("BUG: Authentication client %u didn't send handshake", + conn->pid); + return FALSE; + } + + /* <id> <mechanism> [...] */ + list = t_strsplit(args, "\t"); + if (list[0] == NULL || list[1] == NULL) { + i_error("BUG: Authentication client %u " + "sent broken AUTH request", conn->pid); + return FALSE; + } + + id = (unsigned int)strtoul(list[0], NULL, 10); + + mech = mech_module_find(list[1]); + if (mech == NULL) { + /* unsupported mechanism */ + i_error("BUG: Authentication client %u requested unsupported " + "authentication mechanism %s", conn->pid, + str_sanitize(list[1], MAX_MECH_NAME_LEN)); + return FALSE; + } + + request = auth_request_new(mech); + if (request == NULL) + return TRUE; + + request->conn = conn; + request->id = id; + + /* parse optional parameters */ + initial_resp = NULL; + valid_client_cert = FALSE; + for (list += 2; *list != NULL; list++) { + arg = strchr(*list, '='); + if (arg == NULL) { + name = *list; + arg = ""; + } else { + name = t_strdup_until(*list, arg); + arg++; + } + + if (strcmp(name, "lip") == 0) + (void)net_addr2ip(arg, &request->local_ip); + else if (strcmp(name, "rip") == 0) + (void)net_addr2ip(arg, &request->remote_ip); + else if (strcmp(name, "proto") == 0) + request->protocol = p_strdup(request->pool, arg); + else if (strcmp(name, "resp") == 0) + initial_resp = arg; + else if (strcmp(name, "valid-client-cert") == 0) + valid_client_cert = TRUE; + } + + if (request->protocol == NULL) + request->protocol = conn->default_protocol; + if (request->protocol == NULL) { + i_error("BUG: Authentication client %u " + "didn't specify protocol in request", conn->pid); + auth_request_destroy(request); + return FALSE; + } - data = i_stream_get_modifyable_data(conn->input, &size); - if (size < sizeof(type)) - return FALSE; + if (ssl_require_client_cert && !valid_client_cert) { + /* we fail without valid certificate */ + if (verbose) { + i_info("ssl-cert-check(%s): " + "Client didn't present valid SSL certificate", + get_log_prefix(request)); + } + auth_request_destroy(request); + auth_client_send(conn, "FAIL\t%u", id); + return TRUE; + } + + if (initial_resp == NULL) { + initial_resp_data = NULL; + initial_resp_len = 0; + } else { + size_t len = strlen(initial_resp); + buf = buffer_create_dynamic(pool_datastack_create(), + MAX_BASE64_DECODED_SIZE(len)); + if (base64_decode(initial_resp, len, NULL, buf) < 0) { + if (verbose) { + i_info("%s(%s): Invalid base64 data in " + "initial response", mech->mech_name, + get_log_prefix(request)); + } + auth_request_destroy(request); + auth_client_send(conn, "FAIL\t%u\t" + "Invalid base64 data in initial response", id); + return TRUE; + } + initial_resp_data = buf->data; + initial_resp_len = buf->used; + } + hash_insert(conn->auth_requests, POINTER_CAST(id), request); + + /* connection is referenced only until auth_callback is called. */ + conn->refcount++; + mech->auth_initial(request, initial_resp_data, initial_resp_len, + auth_callback); + return TRUE; +} - /* note that we can't directly cast the received data pointer into - structures, as it may not be aligned properly. */ - memcpy(&type, data, sizeof(type)); +static int +auth_client_input_cont(struct auth_client_connection *conn, const char *args) +{ + struct auth_request *request; + const char *data; + size_t data_len; + buffer_t *buf; + unsigned int id; + + data = strchr(args, '\t'); + if (data++ == NULL) { + i_error("BUG: Authentication client %u " + "sent broken CONT request", conn->pid); + return FALSE; + } + + id = (unsigned int)strtoul(args, NULL, 10); + + request = hash_lookup(conn->auth_requests, POINTER_CAST(id)); + if (request == NULL) { + /* timeouted */ + auth_client_send(conn, "FAIL\t%u\tTimeouted", id); + return TRUE; + } + + if (!request->accept_input) { + auth_client_send(conn, "FAIL\t%u\tUnexpected continuation", id); + auth_request_destroy(request); + return TRUE; + } + request->accept_input = FALSE; + + data_len = strlen(data); + buf = buffer_create_dynamic(pool_datastack_create(), + MAX_BASE64_DECODED_SIZE(data_len)); + if (base64_decode(data, data_len, NULL, buf) < 0) { + if (verbose) { + i_info("%s(%s): Invalid base64 data in " + "continued response", request->mech->mech_name, + get_log_prefix(request)); + } + auth_client_send(conn, "FAIL\t%u\tInvalid base64 data in " + "continued response", id); + auth_request_destroy(request); + return TRUE; + } conn->refcount++; - switch (type) { - case AUTH_CLIENT_REQUEST_NEW: { - struct auth_client_request_new request; - - if (size < sizeof(request)) - return FALSE; - - memcpy(&request, data, sizeof(request)); - if (size < sizeof(request) + request.data_size) - return FALSE; - - /* we have a full init request */ - conn->refcount++; - 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)) - return FALSE; - - memcpy(&request, data, sizeof(request)); - if (size < sizeof(request) + request.data_size) - return FALSE; - - /* we have a full continued request */ - conn->refcount++; - mech_request_continue(conn, &request, data + sizeof(request), - request_callback); - - /* clear any sensitive data from memory */ - safe_memset(data + sizeof(request), 0, request.data_size); - i_stream_skip(conn->input, sizeof(request) + request.data_size); - break; - } - default: - /* unknown request */ - 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); + request->mech->auth_continue(request, buf->data, buf->used, + auth_callback); return TRUE; } static void auth_client_input(void *context) { - struct auth_client_connection *conn = context; + struct auth_client_connection *conn = context; + char *line; + int ret; switch (i_stream_read(conn->input)) { case 0: @@ -174,18 +348,35 @@ case -2: /* buffer full */ i_error("BUG: Auth client %u sent us more than %d bytes", - conn->pid, (int)MAX_INBUF_SIZE); + conn->pid, (int)AUTH_CLIENT_MAX_LINE_LENGTH); auth_client_connection_destroy(conn); return; } - if (conn->pid == 0) { - if (!auth_client_input_handshake(conn)) - return; + conn->refcount++; + while ((line = i_stream_next_line(conn->input)) != NULL) { + t_push(); + if (strncmp(line, "AUTH\t", 5) == 0) + ret = auth_client_input_auth(conn, line + 5); + else if (strncmp(line, "CONT\t", 5) == 0) + ret = auth_client_input_cont(conn, line + 5); + else if (strncmp(line, "CPID\t", 5) == 0) + ret = auth_client_input_cpid(conn, line + 5); + else if (strncmp(line, "PROTO\t", 6) == 0) + ret = auth_client_input_proto(conn, line + 6); + else { + /* ignore unknown command */ + ret = TRUE; + } + safe_memset(line, 0, strlen(line)); + t_pop(); + + if (!ret) { + auth_client_connection_destroy(conn); + break; + } } - - while (auth_client_input_request(conn)) - ; + auth_client_connection_unref(conn); } struct auth_client_connection * @@ -193,8 +384,8 @@ { static unsigned int connect_uid_counter = 0; struct auth_client_connection *conn; - struct auth_client_handshake_reply handshake_reply; struct const_iovec iov[2]; + string_t *str; pool_t pool; @@ -206,7 +397,8 @@ conn->connect_uid = ++connect_uid_counter; conn->fd = fd; - conn->input = i_stream_create_file(fd, default_pool, MAX_INBUF_SIZE, + conn->input = i_stream_create_file(fd, default_pool, + AUTH_CLIENT_MAX_LINE_LENGTH, FALSE); conn->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE, FALSE); @@ -218,13 +410,14 @@ conn->next = master->clients; master->clients = conn; - handshake_reply = *master->handshake_reply; - handshake_reply.connect_uid = conn->connect_uid; + str = t_str_new(128); + str_printfa(str, "SPID\t%u\nCUID\t%u\nDONE\n", + master->pid, conn->connect_uid); - iov[0].iov_base = &handshake_reply; - iov[0].iov_len = sizeof(handshake_reply); - iov[1].iov_base = master->handshake_reply + 1; - iov[1].iov_len = handshake_reply.data_size; + iov[0].iov_base = str_data(mech_handshake); + iov[0].iov_len = str_len(mech_handshake); + iov[1].iov_base = str_data(str); + iov[1].iov_len = str_len(str); if (o_stream_sendv(conn->output, iov, 2) < 0) { auth_client_connection_destroy(conn);
--- a/src/auth/auth-client-connection.h Wed Oct 13 15:32:54 2004 +0300 +++ b/src/auth/auth-client-connection.h Wed Oct 13 19:38:32 2004 +0300 @@ -1,8 +1,6 @@ #ifndef __AUTH_CLIENT_CONNECTION_H #define __AUTH_CLIENT_CONNECTION_H -#include "auth-client-interface.h" - struct auth_client_connection { struct auth_client_connection *next; @@ -16,6 +14,7 @@ pool_t pool; struct hash_table *auth_requests; + char *default_protocol; unsigned int pid; unsigned int connect_uid;
--- a/src/auth/auth-client-interface.h Wed Oct 13 15:32:54 2004 +0300 +++ b/src/auth/auth-client-interface.h Wed Oct 13 19:38:32 2004 +0300 @@ -1,91 +1,24 @@ #ifndef __AUTH_CLIENT_INTERFACE_H #define __AUTH_CLIENT_INTERFACE_H -/* max. size for auth_client_request_continue.data[] */ -#define AUTH_CLIENT_MAX_REQUEST_DATA_SIZE 4096 - -/* Client process must finish with single authentication requests in this time, - or the whole connection will be killed. */ +#define AUTH_CLIENT_MAX_LINE_LENGTH 8192 #define AUTH_REQUEST_TIMEOUT 120 -enum auth_client_request_new_flags { - AUTH_CLIENT_FLAG_SSL_ENABLED = 0x01, - AUTH_CLIENT_FLAG_SSL_VALID_CLIENT_CERT = 0x02 -}; - -enum auth_client_request_type { - AUTH_CLIENT_REQUEST_NEW = 1, - AUTH_CLIENT_REQUEST_CONTINUE -}; - -enum auth_client_result { - AUTH_CLIENT_RESULT_CONTINUE = 1, - AUTH_CLIENT_RESULT_SUCCESS, - AUTH_CLIENT_RESULT_FAILURE -}; - -/* Client -> Server */ -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 */ - unsigned int connect_uid; /* unique connection identifier */ - - uint32_t mech_count; - uint32_t data_size; - /* struct auth_client_handshake_mech_desc mech_desc[auth_mech_count]; */ -}; - -/* New authentication request */ -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_client_request_new_flags flags; - - uint32_t ip_family; /* if non-zero, data begins with local/remote IPs */ - - 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; - - uint32_t data_size; - /* unsigned char data[]; */ -}; - -/* Reply to authentication */ -struct auth_client_request_reply { - unsigned int id; - - enum auth_client_result result; - - /* variable width data, indexes into data[]. - Ignore if it points outside data_size. */ - uint32_t username_idx; /* NUL-terminated */ - uint32_t reply_idx; /* last, non-NUL terminated */ - - uint32_t data_size; - /* unsigned char data[]; */ +enum mech_security_flags { + /* Don't advertise this as available SASL mechanism (eg. APOP) */ + MECH_SEC_PRIVATE = 0x0001, + /* Anonymous authentication */ + MECH_SEC_ANONYMOUS = 0x0002, + /* Transfers plaintext passwords */ + MECH_SEC_PLAINTEXT = 0x0004, + /* Subject to passive (dictionary) attack */ + MECH_SEC_DICTIONARY = 0x0008, + /* Subject to active (non-dictionary) attack */ + MECH_SEC_ACTIVE = 0x0010, + /* Provides forward secrecy between sessions */ + MECH_SEC_FORWARD_SECRECY = 0x0020, + /* Provides mutual authentication */ + MECH_SEC_MUTUAL_AUTH = 0x0040, }; #endif
--- a/src/auth/auth-master-connection.c Wed Oct 13 15:32:54 2004 +0300 +++ b/src/auth/auth-master-connection.c Wed Oct 13 19:38:32 2004 +0300 @@ -3,7 +3,9 @@ #include "common.h" #include "buffer.h" #include "hash.h" +#include "str.h" #include "ioloop.h" +#include "istream.h" #include "ostream.h" #include "network.h" #include "mech.h" @@ -12,11 +14,10 @@ #include "auth-master-connection.h" #include <unistd.h> - -#define MAX_OUTBUF_SIZE (1024*50) +#include <stdlib.h> -static struct auth_master_reply failure_reply = -{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +#define MAX_INBUF_SIZE 1024 +#define MAX_OUTBUF_SIZE (1024*50) struct auth_listener { struct auth_master_connection *master; @@ -28,164 +29,162 @@ struct master_userdb_request { struct auth_master_connection *conn; - unsigned int tag; + unsigned int id; }; static void master_output(void *context); static void auth_master_connection_close(struct auth_master_connection *conn); static int auth_master_connection_unref(struct auth_master_connection *conn); -static size_t reply_add(buffer_t *buf, const char *str) +static void master_send(struct auth_master_connection *conn, + const char *fmt, ...) __attr_format__(2, 3); +static void master_send(struct auth_master_connection *conn, + const char *fmt, ...) { - size_t index; + va_list args; + string_t *str; - if (str == NULL || *str == '\0') - return (size_t)-1; - - index = buffer_get_used_size(buf) - sizeof(struct auth_master_reply); - buffer_append(buf, str, strlen(str)+1); - return index; + t_push(); + va_start(args, fmt); + str = t_str_new(256); + str_vprintfa(str, fmt, args); + str_append_c(str, '\n'); + (void)o_stream_send(conn->output, str_data(str), str_len(str)); + va_end(args); + t_pop(); } -static struct auth_master_reply * -fill_reply(const struct user_data *user, size_t *reply_size) +static void append_user_reply(string_t *str, const struct user_data *user) { - struct auth_master_reply reply, *reply_p; - buffer_t *buf; - char *p; + const char *p; - buf = buffer_create_dynamic(pool_datastack_create(), - sizeof(reply) + 256); - memset(&reply, 0, sizeof(reply)); - buffer_append(buf, &reply, sizeof(reply)); - - reply.success = TRUE; + str_append(str, user->virtual_user); + str_printfa(str, "%s\tuid=%s\tgid=%s", user->virtual_user, + dec2str(user->uid), dec2str(user->gid)); - reply.uid = user->uid; - reply.gid = user->gid; - - reply.system_user_idx = reply_add(buf, user->system_user); - reply.virtual_user_idx = reply_add(buf, user->virtual_user); - reply.mail_idx = reply_add(buf, user->mail); + if (user->system_user != NULL) + str_printfa(str, "\tsystem_user=%s", user->system_user); + if (user->mail != NULL) + str_printfa(str, "\tmail=%s", user->mail); p = user->home != NULL ? strstr(user->home, "/./") : NULL; if (p == NULL) { - reply.home_idx = reply_add(buf, user->home); - reply.chroot_idx = reply_add(buf, NULL); + if (user->home != NULL) + str_printfa(str, "\thome=%s", user->home); } else { /* wu-ftpd like <chroot>/./<home> */ - reply.chroot_idx = - reply_add(buf, t_strdup_until(user->home, p)); - reply.home_idx = reply_add(buf, p + 3); - } - - *reply_size = buffer_get_used_size(buf); - reply.data_size = *reply_size - sizeof(reply); - - reply_p = buffer_get_space_unsafe(buf, 0, sizeof(reply)); - *reply_p = reply; - - return reply_p; -} - -static void master_send_reply(struct auth_master_connection *conn, - struct auth_master_reply *reply, - size_t reply_size, unsigned int tag) -{ - ssize_t ret; - - reply->tag = tag; - - ret = o_stream_send(conn->output, reply, reply_size); - if (ret < 0) { - /* master died, kill ourself too */ - auth_master_connection_close(conn); - return; - } - i_assert((size_t)ret == reply_size); - - if (o_stream_get_buffer_used_size(conn->output) >= MAX_OUTBUF_SIZE) { - /* buffer full, stop accepting more input */ - if (conn->io != NULL) { - io_remove(conn->io); - conn->io = NULL; - } + str_printfa(str, "\thome=%s\tchroot=%s", + p + 3, t_strdup_until(user->home, p)); } } -static void userdb_callback(struct user_data *user, void *context) +static void userdb_callback(const struct user_data *user, void *context) { struct master_userdb_request *master_request = context; - struct auth_master_reply *reply; - size_t reply_size; + string_t *str; if (auth_master_connection_unref(master_request->conn)) { if (user == NULL) { - master_send_reply(master_request->conn, &failure_reply, - sizeof(failure_reply), - master_request->tag); + master_send(master_request->conn, "NOTFOUND\t%u", + master_request->id); } else { - reply = fill_reply(user, &reply_size); - master_send_reply(master_request->conn, reply, - reply_size, master_request->tag); + str = t_str_new(256); + str_printfa(str, "USER\t%u\t", master_request->id); + append_user_reply(str, user); + master_send(master_request->conn, "%s", str_c(str)); } } i_free(master_request); } -static void master_handle_request(struct auth_master_connection *conn, - struct auth_master_request *request) +static int +master_input_request(struct auth_master_connection *conn, const char *args) { struct auth_client_connection *client_conn; - struct auth_request *auth_request; struct master_userdb_request *master_request; + struct auth_request *request; + const char *const *list; + unsigned int id, client_pid, client_id; - client_conn = auth_client_connection_lookup(conn, request->client_pid); - auth_request = client_conn == NULL ? NULL : + /* <id> <client-pid> <client-id> */ + list = t_strsplit(args, "\t"); + if (list[0] == NULL || list[1] == NULL || list[2] == NULL) { + i_error("BUG: Master sent broken REQUEST"); + return FALSE; + } + + id = (unsigned int)strtoul(list[0], NULL, 10); + client_pid = (unsigned int)strtoul(list[1], NULL, 10); + client_id = (unsigned int)strtoul(list[2], NULL, 10); + + client_conn = auth_client_connection_lookup(conn, client_pid); + request = client_conn == NULL ? NULL : hash_lookup(client_conn->auth_requests, - POINTER_CAST(request->id)); + POINTER_CAST(client_id)); - if (auth_request == NULL) { + if (request == NULL) { if (verbose) { i_info("Master request %u.%u not found", - request->client_pid, request->id); + client_pid, client_id); } - master_send_reply(conn, &failure_reply, sizeof(failure_reply), - request->tag); + master_send(conn, "NOTFOUND\t%u", id); } else { master_request = i_new(struct master_userdb_request, 1); master_request->conn = conn; - master_request->tag = request->tag; + master_request->id = id; conn->refcount++; - userdb->lookup(auth_request, userdb_callback, - master_request); + userdb->lookup(request, userdb_callback, master_request); /* the auth request is finished, we don't need it anymore */ - mech_request_free(auth_request, request->id); + auth_request_destroy(request); } + return TRUE; +} + +static int +master_input_die(struct auth_master_connection *conn) +{ + return TRUE; } static void master_input(void *context) { struct auth_master_connection *conn = context; + char *line; int ret; - ret = net_receive(conn->fd, - conn->request_buf + conn->request_pos, - sizeof(conn->request_buf) - conn->request_pos); - if (ret < 0) { - /* master died, kill ourself too */ + switch (i_stream_read(conn->input)) { + case 0: + return; + case -1: + /* disconnected */ + auth_master_connection_close(conn); + return; + case -2: + /* buffer full */ + i_error("BUG: Master sent us more than %d bytes", + (int)MAX_INBUF_SIZE); auth_master_connection_close(conn); return; } - conn->request_pos += ret; - if (conn->request_pos >= sizeof(conn->request_buf)) { - /* reply is now read */ - master_handle_request(conn, (struct auth_master_request *) - conn->request_buf); - conn->request_pos = 0; + while ((line = i_stream_next_line(conn->input)) != NULL) { + t_push(); + if (strncmp(line, "REQUEST\t", 8) == 0) + ret = master_input_request(conn, line + 8); + else if (strcmp(line, "DIE") == 0) + ret = master_input_die(conn); + else { + /* ignore unknown command */ + ret = TRUE; + } + t_pop(); + + if (!ret) { + auth_master_connection_close(conn); + return; + } } } @@ -206,56 +205,18 @@ } } -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); - - 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) - sizeof(reply); - memcpy(buffer_get_space_unsafe(buf, 0, sizeof(reply)), - &reply, sizeof(reply)); - - master->handshake_reply = buffer_free_without_data(buf); -} - static void auth_master_connection_set_fd(struct auth_master_connection *conn, int fd) { + if (conn->input != NULL) + i_stream_unref(conn->input); if (conn->output != NULL) o_stream_unref(conn->output); if (conn->io != NULL) io_remove(conn->io); + conn->input = i_stream_create_file(fd, default_pool, + MAX_INBUF_SIZE, FALSE); conn->output = o_stream_create_file(fd, default_pool, (size_t)-1, FALSE); o_stream_set_flush_callback(conn->output, master_output, conn); @@ -276,22 +237,15 @@ conn->listeners_buf = buffer_create_dynamic(default_pool, 64); if (fd != -1) auth_master_connection_set_fd(conn, fd); - master_get_handshake_reply(conn); return conn; } void auth_master_connection_send_handshake(struct auth_master_connection *conn) { - struct auth_master_handshake_reply reply; - /* just a note to master that we're ok. if we die before, it means we're broken and a simple restart most likely won't help. */ - if (conn->output != NULL) { - memset(&reply, 0, sizeof(reply)); - reply.server_pid = conn->pid; - if (o_stream_send(conn->output, &reply, sizeof(reply)) < 0) - auth_master_connection_close(conn); - } + if (conn->output != NULL) + master_send(conn, "SPID\t%u", conn->pid); } static void auth_master_connection_close(struct auth_master_connection *conn) @@ -350,7 +304,6 @@ if (conn->output != NULL) o_stream_unref(conn->output); - i_free(conn->handshake_reply); i_free(conn); return FALSE; }
--- a/src/auth/auth-master-connection.h Wed Oct 13 15:32:54 2004 +0300 +++ b/src/auth/auth-master-connection.h Wed Oct 13 19:38:32 2004 +0300 @@ -1,21 +1,16 @@ #ifndef __AUTH_MASTER_CONNECTION_H #define __AUTH_MASTER_CONNECTION_H -#include "auth-master-interface.h" - struct auth_master_connection { unsigned int pid; int refcount; int fd; + struct istream *input; struct ostream *output; struct io *io; buffer_t *listeners_buf; - unsigned int request_pos; - unsigned char request_buf[sizeof(struct auth_master_request)]; - - struct auth_client_handshake_reply *handshake_reply; struct auth_client_connection *clients; struct timeout *to_clients;
--- a/src/auth/auth-master-interface.h Wed Oct 13 15:32:54 2004 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -#ifndef __AUTH_MASTER_INTERFACE_H -#define __AUTH_MASTER_INTERFACE_H - -#define AUTH_MASTER_MAX_REPLY_DATA_SIZE 4096 - -/* Server -> Master */ -struct auth_master_handshake_reply { - unsigned int server_pid; -}; - -struct auth_master_request { - unsigned int tag; - - unsigned int id; - unsigned int client_pid; -}; - -struct auth_master_reply { - unsigned int tag; - - unsigned int success:1; - - uid_t uid; - gid_t gid; - - /* 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. */ - uint32_t system_user_idx; - uint32_t virtual_user_idx; - uint32_t home_idx, mail_idx, chroot_idx; - - uint32_t data_size; - /* unsigned char data[]; */ -}; - -#endif
--- a/src/auth/mech-anonymous.c Wed Oct 13 15:32:54 2004 +0300 +++ b/src/auth/mech-anonymous.c Wed Oct 13 19:38:32 2004 +0300 @@ -1,85 +1,72 @@ -/* Copyright (C) 2002 Timo Sirainen */ +/* Copyright (C) 2002-2004 Timo Sirainen */ #include "common.h" #include "mech.h" -static int -mech_anonymous_auth_continue(struct auth_request *auth_request, +static void +mech_anonymous_auth_continue(struct auth_request *request, const unsigned char *data, size_t data_size, mech_callback_t *callback) { i_assert(anonymous_username != NULL); if (verbose) { - auth_request->user = + /* temporarily set the user to the one that was given, + so that the log message goes right */ + request->user = p_strndup(pool_datastack_create(), data, data_size); i_info("anonymous(%s): login", - get_log_prefix(auth_request)); + get_log_prefix(request)); } - auth_request->callback = callback; - auth_request->user = p_strdup(auth_request->pool, anonymous_username); - - mech_auth_finish(auth_request, NULL, 0, TRUE); - 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; + request->callback = callback; + request->user = p_strdup(request->pool, anonymous_username); - 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; + mech_auth_finish(request, NULL, 0, TRUE); } static void -mech_anonymous_auth_free(struct auth_request *auth_request) +mech_anonymous_auth_initial(struct auth_request *request, + const unsigned char *data, size_t data_size, + mech_callback_t *callback) { - pool_unref(auth_request->pool); + if (data_size == 0) + callback(request, AUTH_CLIENT_RESULT_CONTINUE, NULL, 0); + else { + mech_anonymous_auth_continue(request, data, data_size, + callback); + } +} + +static void +mech_anonymous_auth_free(struct auth_request *request) +{ + pool_unref(request->pool); } static struct auth_request *mech_anonymous_auth_new(void) { - struct auth_request *auth_request; + struct auth_request *request; 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; + request = p_new(pool, struct auth_request, 1); + request->refcount = 1; + request->pool = pool; - return auth_request; + return request; } struct mech_module mech_anonymous = { "ANONYMOUS", - MEMBER(plaintext) FALSE, - MEMBER(advertise) TRUE, + MEMBER(flags) MECH_SEC_ANONYMOUS, MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) FALSE, - mech_anonymous_auth_new + mech_anonymous_auth_new, + mech_anonymous_auth_initial, + mech_anonymous_auth_continue, + mech_anonymous_auth_free };
--- a/src/auth/mech-apop.c Wed Oct 13 15:32:54 2004 +0300 +++ b/src/auth/mech-apop.c Wed Oct 13 19:38:32 2004 +0300 @@ -35,7 +35,7 @@ apop_credentials_callback(const char *credentials, struct auth_request *auth_request) { - struct apop_auth_request *auth = + struct apop_auth_request *request = (struct apop_auth_request *)auth_request; unsigned char digest[16]; struct md5_context ctx; @@ -43,41 +43,41 @@ if (credentials != NULL) { md5_init(&ctx); - md5_update(&ctx, auth->challenge, strlen(auth->challenge)); + md5_update(&ctx, request->challenge, + strlen(request->challenge)); md5_update(&ctx, credentials, strlen(credentials)); md5_final(&ctx, digest); - ret = memcmp(digest, auth->digest, 16) == 0; + ret = memcmp(digest, request->digest, 16) == 0; } mech_auth_finish(auth_request, NULL, 0, ret); } -static int +static void mech_apop_auth_initial(struct auth_request *auth_request, - struct auth_client_request_new *request, - const unsigned char *data, + const unsigned char *data, size_t data_size, mech_callback_t *callback) { - struct apop_auth_request *auth = + struct apop_auth_request *request = (struct apop_auth_request *)auth_request; const unsigned char *tmp, *end, *username = NULL; const char *str, *error; auth_request->callback = callback; - if (!AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request)) { + if (data_size == 0) { /* Should never happen */ if (verbose) { i_info("apop(%s): no initial respone", get_log_prefix(auth_request)); } mech_auth_finish(auth_request, NULL, 0, FALSE); - return TRUE; + return; } - tmp = data = data + request->initial_resp_idx; - end = data + request->data_size - request->initial_resp_idx; + tmp = data; + end = data + data_size; while (tmp != end && *tmp != '\0') tmp++; @@ -93,9 +93,9 @@ get_log_prefix(auth_request)); } mech_auth_finish(auth_request, NULL, 0, FALSE); - return TRUE; + return; } - auth->challenge = p_strdup(auth->pool, (const char *)data); + request->challenge = p_strdup(request->pool, (const char *)data); if (tmp != end) { username = ++tmp; @@ -110,58 +110,55 @@ get_log_prefix(auth_request)); } mech_auth_finish(auth_request, NULL, 0, FALSE); - return TRUE; + return; } tmp++; - auth_request->user = p_strdup(auth->pool, (const char *)username); + auth_request->user = p_strdup(request->pool, (const char *)username); if (!mech_fix_username(auth_request->user, &error)) { if (verbose) { i_info("apop(%s): %s", get_log_prefix(auth_request), error); } mech_auth_finish(auth_request, NULL, 0, FALSE); - return TRUE; + return; } - memcpy(auth->digest, tmp, sizeof(auth->digest)); + memcpy(request->digest, tmp, sizeof(request->digest)); passdb->lookup_credentials(auth_request, PASSDB_CREDENTIALS_PLAINTEXT, apop_credentials_callback); - return TRUE; } -static void mech_apop_auth_free(struct auth_request *auth_request) +static void mech_apop_auth_free(struct auth_request *request) { - pool_unref(auth_request->pool); + pool_unref(request->pool); } static struct auth_request *mech_apop_auth_new(void) { - struct apop_auth_request *auth; + struct apop_auth_request *request; pool_t pool; pool = pool_alloconly_create("apop_auth_request", 256); - auth = p_new(pool, struct apop_auth_request, 1); - auth->pool = pool; + request = p_new(pool, struct apop_auth_request, 1); + request->pool = pool; - auth->auth_request.refcount = 1; - auth->auth_request.pool = pool; - auth->auth_request.auth_initial = mech_apop_auth_initial; - auth->auth_request.auth_continue = NULL; - auth->auth_request.auth_free = mech_apop_auth_free; - - return &auth->auth_request; + request->auth_request.refcount = 1; + request->auth_request.pool = pool; + return &request->auth_request; } const struct mech_module mech_apop = { "APOP", - MEMBER(plaintext) FALSE, - MEMBER(advertise) FALSE, + MEMBER(flags) MECH_SEC_PRIVATE | MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE, MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) TRUE, mech_apop_auth_new, + mech_apop_auth_initial, + NULL, + mech_apop_auth_free };
--- a/src/auth/mech-cram-md5.c Wed Oct 13 15:32:54 2004 +0300 +++ b/src/auth/mech-cram-md5.c Wed Oct 13 19:38:32 2004 +0300 @@ -42,11 +42,11 @@ buf[i] = (buf[i] % 10) + '0'; buf[sizeof(buf)-1] = '\0'; - return t_strdup_printf("<%s.%s@%s>", (const char *) buf, + return t_strdup_printf("<%s.%s@%s>", (const char *)buf, dec2str(ioloop_time), my_hostname); } -static int verify_credentials(struct cram_auth_request *auth, +static int verify_credentials(struct cram_auth_request *request, const char *credentials) { @@ -66,15 +66,15 @@ return FALSE; hmac_md5_set_cram_context(&ctx, context_digest); - hmac_md5_update(&ctx, auth->challenge, strlen(auth->challenge)); + hmac_md5_update(&ctx, request->challenge, strlen(request->challenge)); hmac_md5_final(&ctx, digest); response_hex = binary_to_hex(digest, 16); - if (memcmp(response_hex, auth->response, 32) != 0) { + if (memcmp(response_hex, request->response, 32) != 0) { if (verbose) { i_info("cram-md5(%s): password mismatch", - get_log_prefix(&auth->auth_request)); + get_log_prefix(&request->auth_request)); } return FALSE; } @@ -82,7 +82,7 @@ return TRUE; } -static int parse_cram_response(struct cram_auth_request *auth, +static int parse_cram_response(struct cram_auth_request *request, const unsigned char *data, size_t size, const char **error_r) { @@ -102,126 +102,106 @@ return FALSE; } - auth->username = p_strndup(auth->pool, data, space); + request->username = p_strndup(request->pool, data, space); space++; - auth->response = p_strndup(auth->pool, data + space, size - space); + request->response = + p_strndup(request->pool, data + space, size - space); return TRUE; } static void credentials_callback(const char *result, - struct auth_request *request) + struct auth_request *auth_request) { - struct cram_auth_request *auth = - (struct cram_auth_request *) request; + struct cram_auth_request *request = + (struct cram_auth_request *)auth_request; - if (verify_credentials(auth, result)) - mech_auth_finish(request, NULL, 0, TRUE); + if (verify_credentials(request, result)) + mech_auth_finish(auth_request, NULL, 0, TRUE); else { if (verbose) { i_info("cram-md5(%s): authentication failed", - get_log_prefix(&auth->auth_request)); + get_log_prefix(auth_request)); } - mech_auth_finish(request, NULL, 0, FALSE); + mech_auth_finish(auth_request, NULL, 0, FALSE); } } -static int +static void mech_cram_md5_auth_continue(struct auth_request *auth_request, const unsigned char *data, size_t data_size, mech_callback_t *callback) { - struct cram_auth_request *auth = + struct cram_auth_request *request = (struct cram_auth_request *)auth_request; const char *error; - if (parse_cram_response(auth, data, data_size, &error)) { + if (parse_cram_response(request, data, data_size, &error)) { auth_request->callback = callback; auth_request->user = - p_strdup(auth_request->pool, auth->username); + p_strdup(auth_request->pool, request->username); if (mech_fix_username(auth_request->user, &error)) { - passdb->lookup_credentials(&auth->auth_request, + passdb->lookup_credentials(auth_request, PASSDB_CREDENTIALS_CRAM_MD5, credentials_callback); - return TRUE; + return; } } if (error == NULL) error = "authentication failed"; - if (verbose) { - i_info("cram-md5(%s): %s", - get_log_prefix(&auth->auth_request), error); - } + if (verbose) + i_info("cram-md5(%s): %s", get_log_prefix(auth_request), error); /* failed */ mech_auth_finish(auth_request, NULL, 0, FALSE); - return FALSE; } -static int +static void mech_cram_md5_auth_initial(struct auth_request *auth_request, - struct auth_client_request_new *request, const unsigned char *data __attr_unused__, + size_t data_size __attr_unused__, mech_callback_t *callback) { - struct cram_auth_request *auth = + struct cram_auth_request *request = (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; + request->challenge = p_strdup(request->pool, get_cram_challenge()); + callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE, + request->challenge, strlen(request->challenge)); } -static void mech_cram_md5_auth_free(struct auth_request *auth_request) +static void mech_cram_md5_auth_free(struct auth_request *request) { - pool_unref(auth_request->pool); + pool_unref(request->pool); } static struct auth_request *mech_cram_md5_auth_new(void) { - struct cram_auth_request *auth; + struct cram_auth_request *request; pool_t pool; pool = pool_alloconly_create("cram_md5_auth_request", 2048); - auth = p_new(pool, struct cram_auth_request, 1); - auth->pool = pool; + request = p_new(pool, struct cram_auth_request, 1); + request->pool = pool; - 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; - - return &auth->auth_request; + request->auth_request.refcount = 1; + request->auth_request.pool = pool; + return &request->auth_request; } struct mech_module mech_cram_md5 = { "CRAM-MD5", - MEMBER(plaintext) FALSE, - MEMBER(advertise) TRUE, + MEMBER(flags) MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE, MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) TRUE, - mech_cram_md5_auth_new + mech_cram_md5_auth_new, + mech_cram_md5_auth_initial, + mech_cram_md5_auth_continue, + mech_cram_md5_auth_free };
--- a/src/auth/mech-digest-md5.c Wed Oct 13 15:32:54 2004 +0300 +++ b/src/auth/mech-digest-md5.c Wed Oct 13 19:38:32 2004 +0300 @@ -52,7 +52,7 @@ char *rspauth; }; -static string_t *get_digest_challenge(struct digest_auth_request *auth) +static string_t *get_digest_challenge(struct digest_auth_request *request) { buffer_t *buf; string_t *str; @@ -79,7 +79,7 @@ base64_encode(nonce, sizeof(nonce), buf); buffer_append_c(buf, '\0'); - auth->nonce = p_strdup(auth->pool, buffer_get_data(buf, NULL)); + request->nonce = p_strdup(request->pool, buffer_get_data(buf, NULL)); t_pop(); str = t_str_new(256); @@ -89,11 +89,11 @@ str_append_c(str, ','); } - str_printfa(str, "nonce=\"%s\",", auth->nonce); + str_printfa(str, "nonce=\"%s\",", request->nonce); str_append(str, "qop=\""); first_qop = TRUE; for (i = 0; i < QOP_COUNT; i++) { - if (auth->qop & (1 << i)) { + if (request->qop & (1 << i)) { if (first_qop) first_qop = FALSE; else @@ -108,7 +108,7 @@ return str; } -static int verify_credentials(struct digest_auth_request *auth, +static int verify_credentials(struct digest_auth_request *request, const char *credentials) { struct md5_context ctx; @@ -151,9 +151,9 @@ md5_init(&ctx); md5_update(&ctx, digest, 16); md5_update(&ctx, ":", 1); - md5_update(&ctx, auth->nonce, strlen(auth->nonce)); + md5_update(&ctx, request->nonce, strlen(request->nonce)); md5_update(&ctx, ":", 1); - md5_update(&ctx, auth->cnonce, strlen(auth->cnonce)); + md5_update(&ctx, request->cnonce, strlen(request->cnonce)); md5_final(&ctx, digest); a1_hex = binary_to_hex(digest, 16); @@ -167,11 +167,12 @@ else md5_update(&ctx, ":", 1); - if (auth->digest_uri != NULL) { - md5_update(&ctx, auth->digest_uri, - strlen(auth->digest_uri)); + if (request->digest_uri != NULL) { + md5_update(&ctx, request->digest_uri, + strlen(request->digest_uri)); } - if (auth->qop == QOP_AUTH_INT || auth->qop == QOP_AUTH_CONF) { + if (request->qop == QOP_AUTH_INT || + request->qop == QOP_AUTH_CONF) { md5_update(&ctx, ":00000000000000000000000000000000", 33); } @@ -182,13 +183,15 @@ md5_init(&ctx); md5_update(&ctx, a1_hex, 32); md5_update(&ctx, ":", 1); - md5_update(&ctx, auth->nonce, strlen(auth->nonce)); + md5_update(&ctx, request->nonce, strlen(request->nonce)); md5_update(&ctx, ":", 1); - md5_update(&ctx, auth->nonce_count, strlen(auth->nonce_count)); + md5_update(&ctx, request->nonce_count, + strlen(request->nonce_count)); md5_update(&ctx, ":", 1); - md5_update(&ctx, auth->cnonce, strlen(auth->cnonce)); + md5_update(&ctx, request->cnonce, strlen(request->cnonce)); md5_update(&ctx, ":", 1); - md5_update(&ctx, auth->qop_value, strlen(auth->qop_value)); + md5_update(&ctx, request->qop_value, + strlen(request->qop_value)); md5_update(&ctx, ":", 1); md5_update(&ctx, a2_hex, 32); md5_final(&ctx, digest); @@ -196,10 +199,10 @@ if (i == 0) { /* verify response */ - if (memcmp(response_hex, auth->response, 32) != 0) { + if (memcmp(response_hex, request->response, 32) != 0) { if (verbose) { struct auth_request *auth_request = - &auth->auth_request; + &request->auth_request; i_info("digest-md5(%s): " "password mismatch", get_log_prefix(auth_request)); @@ -207,8 +210,9 @@ return FALSE; } } else { - auth->rspauth = p_strconcat(auth->pool, "rspauth=", - response_hex, NULL); + request->rspauth = + p_strconcat(request->pool, "rspauth=", + response_hex, NULL); } } @@ -283,7 +287,7 @@ return TRUE; } -static int auth_handle_response(struct digest_auth_request *auth, +static int auth_handle_response(struct digest_auth_request *request, char *key, char *value, const char **error) { int i; @@ -295,13 +299,13 @@ *error = "Invalid realm"; return FALSE; } - if (auth->realm == NULL && *value != '\0') - auth->realm = p_strdup(auth->pool, value); + if (request->realm == NULL && *value != '\0') + request->realm = p_strdup(request->pool, value); return TRUE; } if (strcmp(key, "username") == 0) { - if (auth->username != NULL) { + if (request->username != NULL) { *error = "username must not exist more than once"; return FALSE; } @@ -311,23 +315,23 @@ return FALSE; } - auth->username = p_strdup(auth->pool, value); + request->username = p_strdup(request->pool, value); return TRUE; } if (strcmp(key, "nonce") == 0) { /* nonce must be same */ - if (strcmp(value, auth->nonce) != 0) { + if (strcmp(value, request->nonce) != 0) { *error = "Invalid nonce"; return FALSE; } - auth->nonce_found = TRUE; + request->nonce_found = TRUE; return TRUE; } if (strcmp(key, "cnonce") == 0) { - if (auth->cnonce != NULL) { + if (request->cnonce != NULL) { *error = "cnonce must not exist more than once"; return FALSE; } @@ -337,12 +341,12 @@ return FALSE; } - auth->cnonce = p_strdup(auth->pool, value); + request->cnonce = p_strdup(request->pool, value); return TRUE; } if (strcmp(key, "nonce-count") == 0) { - if (auth->nonce_count != NULL) { + if (request->nonce_count != NULL) { *error = "nonce-count must not exist more than once"; return FALSE; } @@ -352,7 +356,7 @@ return FALSE; } - auth->nonce_count = p_strdup(auth->pool, value); + request->nonce_count = p_strdup(request->pool, value); return TRUE; } @@ -367,13 +371,13 @@ return FALSE; } - auth->qop &= (1 << i); - if (auth->qop == 0) { + request->qop &= (1 << i); + if (request->qop == 0) { *error = "Nonallowed QoP requested"; return FALSE; } - auth->qop_value = p_strdup(auth->pool, value); + request->qop_value = p_strdup(request->pool, value); return TRUE; } @@ -390,18 +394,18 @@ But isn't the realm enough already? That'd be just extra configuration.. Maybe optionally list valid hosts in config file? */ - auth->digest_uri = p_strdup(auth->pool, value); + request->digest_uri = p_strdup(request->pool, value); return TRUE; } if (strcmp(key, "maxbuf") == 0) { - if (auth->maxbuf != 0) { + if (request->maxbuf != 0) { *error = "maxbuf must not exist more than once"; return FALSE; } - auth->maxbuf = strtoul(value, NULL, 10); - if (auth->maxbuf == 0) { + request->maxbuf = strtoul(value, NULL, 10); + if (request->maxbuf == 0) { *error = "Invalid maxbuf value"; return FALSE; } @@ -423,7 +427,7 @@ return FALSE; } - memcpy(auth->response, value, 32); + memcpy(request->response, value, 32); return TRUE; } @@ -441,7 +445,7 @@ return TRUE; } -static int parse_digest_response(struct digest_auth_request *auth, +static int parse_digest_response(struct digest_auth_request *request, const unsigned char *data, size_t size, const char **error) { @@ -471,7 +475,7 @@ copy = t_strdup_noconst(t_strndup(data, size)); while (*copy != '\0') { if (parse_next(©, &key, &value)) { - if (!auth_handle_response(auth, key, value, error)) { + if (!auth_handle_response(request, key, value, error)) { failed = TRUE; break; } @@ -482,22 +486,22 @@ } if (!failed) { - if (!auth->nonce_found) { + if (!request->nonce_found) { *error = "Missing nonce parameter"; failed = TRUE; - } else if (auth->cnonce == NULL) { + } else if (request->cnonce == NULL) { *error = "Missing cnonce parameter"; failed = TRUE; - } else if (auth->username == NULL) { + } else if (request->username == NULL) { *error = "Missing username parameter"; failed = TRUE; } } - if (auth->nonce_count == NULL) - auth->nonce_count = p_strdup(auth->pool, "00000001"); - if (auth->qop_value == NULL) - auth->qop_value = p_strdup(auth->pool, "auth"); + if (request->nonce_count == NULL) + request->nonce_count = p_strdup(request->pool, "00000001"); + if (request->qop_value == NULL) + request->qop_value = p_strdup(request->pool, "auth"); t_pop(); @@ -505,148 +509,118 @@ } static void credentials_callback(const char *result, - struct auth_request *request) + struct auth_request *auth_request) { - struct digest_auth_request *auth = - (struct digest_auth_request *) request; - struct auth_client_request_reply reply; + struct digest_auth_request *request = + (struct digest_auth_request *)auth_request; - if (!verify_credentials(auth, result)) { - mech_auth_finish(request, NULL, 0, FALSE); + if (!verify_credentials(request, result)) { + mech_auth_finish(auth_request, NULL, 0, FALSE); return; } - mech_init_auth_client_reply(&reply); - reply.id = request->id; - reply.result = AUTH_CLIENT_RESULT_CONTINUE; - reply.data_size = strlen(auth->rspauth); - auth->authenticated = TRUE; - - request->callback(&reply, auth->rspauth, request->conn); + auth_request->callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE, + request->rspauth, strlen(request->rspauth)); } -static int +static void mech_digest_md5_auth_continue(struct auth_request *auth_request, const unsigned char *data, size_t data_size, mech_callback_t *callback) { - struct digest_auth_request *auth = + struct digest_auth_request *request = (struct digest_auth_request *)auth_request; - struct auth_client_request_reply reply; const char *error, *realm; - /* initialize reply */ - mech_init_auth_client_reply(&reply); - reply.id = auth_request->id; - - if (auth->authenticated) { + if (request->authenticated) { /* authentication is done, we were just waiting the last word from client */ mech_auth_finish(auth_request, NULL, 0, TRUE); - return TRUE; + return; } - if (parse_digest_response(auth, data, data_size, &error)) { + if (parse_digest_response(request, data, data_size, &error)) { auth_request->callback = callback; - realm = auth->realm != NULL ? auth->realm : default_realm; + realm = request->realm != NULL ? request->realm : default_realm; if (realm == NULL) { auth_request->user = p_strdup(auth_request->pool, - auth->username); + request->username); } else { auth_request->user = p_strconcat(auth_request->pool, - auth->username, "@", + request->username, "@", realm, NULL); } if (mech_fix_username(auth_request->user, &error)) { - passdb->lookup_credentials(&auth->auth_request, + passdb->lookup_credentials(auth_request, PASSDB_CREDENTIALS_DIGEST_MD5, credentials_callback); - return TRUE; + return; } } - if (error == NULL) - error = "Authentication failed"; - else if (verbose) { + if (verbose && error != NULL) { i_info("digest-md5(%s): %s", get_log_prefix(auth_request), error); } - /* failed */ - reply.result = AUTH_CLIENT_RESULT_FAILURE; - reply.data_size = strlen(error)+1; - callback(&reply, error, auth_request->conn); - return FALSE; + mech_auth_finish(auth_request, NULL, 0, FALSE); } -static int +static void mech_digest_md5_auth_initial(struct auth_request *auth_request, - struct auth_client_request_new *request, - const unsigned char *data __attr_unused__, + const unsigned char *data, size_t data_size, mech_callback_t *callback) { - struct digest_auth_request *auth = + struct digest_auth_request *request = (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)) { + if (data_size > 0) { /* 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); + mech_digest_md5_auth_continue(auth_request, data, data_size, + callback); + return; } - /* 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; + challenge = get_digest_challenge(request); + callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE, + str_data(challenge), str_len(challenge)); } -static void mech_digest_md5_auth_free(struct auth_request *auth_request) +static void mech_digest_md5_auth_free(struct auth_request *request) { - pool_unref(auth_request->pool); + pool_unref(request->pool); } static struct auth_request * mech_digest_md5_auth_new(void) { - struct digest_auth_request *auth; + struct digest_auth_request *request; pool_t pool; pool = pool_alloconly_create("digest_md5_auth_request", 2048); - auth = p_new(pool, struct digest_auth_request, 1); - auth->pool = pool; + request = p_new(pool, struct digest_auth_request, 1); + request->pool = pool; - 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; - return &auth->auth_request; + request->auth_request.refcount = 1; + request->auth_request.pool = pool; + request->qop = QOP_AUTH; + return &request->auth_request; } struct mech_module mech_digest_md5 = { "DIGEST-MD5", - MEMBER(plaintext) FALSE, - MEMBER(advertise) TRUE, + MEMBER(flags) MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE | + MECH_SEC_MUTUAL_AUTH, MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) TRUE, - mech_digest_md5_auth_new + mech_digest_md5_auth_new, + mech_digest_md5_auth_initial, + mech_digest_md5_auth_continue, + mech_digest_md5_auth_free };
--- a/src/auth/mech-login.c Wed Oct 13 15:32:54 2004 +0300 +++ b/src/auth/mech-login.c Wed Oct 13 19:38:32 2004 +0300 @@ -20,98 +20,77 @@ mech_auth_finish(request, NULL, 0, result == PASSDB_RESULT_OK); } -static int -mech_login_auth_continue(struct auth_request *auth_request, +static void +mech_login_auth_continue(struct auth_request *request, const unsigned char *data, size_t data_size, mech_callback_t *callback) { - struct auth_client_request_reply reply; static const char prompt2[] = "Password:"; const char *error; - auth_request->callback = callback; + request->callback = callback; - if (!auth_request->user) { - auth_request->user = - p_strndup(auth_request->pool, data, data_size); + if (request->user == NULL) { + request->user = p_strndup(request->pool, data, data_size); - if (!mech_fix_username(auth_request->user, &error)) { + if (!mech_fix_username(request->user, &error)) { if (verbose) { i_info("login(%s): %s", - get_log_prefix(auth_request), error); + get_log_prefix(request), error); } - mech_auth_finish(auth_request, NULL, 0, FALSE); - return TRUE; + mech_auth_finish(request, NULL, 0, FALSE); + return; } - mech_init_auth_client_reply(&reply); - reply.id = auth_request->id; - reply.result = AUTH_CLIENT_RESULT_CONTINUE; - - reply.reply_idx = 0; - reply.data_size = strlen(prompt2); - callback(&reply, prompt2, auth_request->conn); + callback(request, AUTH_CLIENT_RESULT_CONTINUE, + prompt2, strlen(prompt2)); } else { char *pass = p_strndup(unsafe_data_stack_pool, data, data_size); - - passdb->verify_plain(auth_request, pass, verify_callback); - + passdb->verify_plain(request, pass, verify_callback); safe_memset(pass, 0, strlen(pass)); } - - return TRUE; } -static int -mech_login_auth_initial(struct auth_request *auth_request, - struct auth_client_request_new *request, - const unsigned char *data __attr_unused__, - mech_callback_t *callback) +static void +mech_login_auth_initial(struct auth_request *request, + const unsigned char *data __attr_unused__, + size_t data_size __attr_unused__, + mech_callback_t *callback) { - struct auth_client_request_reply reply; static const char prompt1[] = "Username:"; - mech_init_auth_client_reply(&reply); - reply.id = request->id; - reply.result = AUTH_CLIENT_RESULT_CONTINUE; - - reply.reply_idx = 0; - reply.data_size = strlen(prompt1); - callback(&reply, prompt1, auth_request->conn); - - return TRUE; + callback(request, AUTH_CLIENT_RESULT_CONTINUE, + prompt1, strlen(prompt1)); } -static void mech_login_auth_free(struct auth_request *auth_request) +static void mech_login_auth_free(struct auth_request *request) { - pool_unref(auth_request->pool); + pool_unref(request->pool); } static struct auth_request *mech_login_auth_new(void) { - struct auth_request *auth; + struct auth_request *request; pool_t pool; pool = pool_alloconly_create("login_auth_request", 256); - auth = p_new(pool, struct auth_request, 1); + request = p_new(pool, struct auth_request, 1); - auth->refcount = 1; - auth->pool = pool; - auth->auth_initial = mech_login_auth_initial; - auth->auth_continue = mech_login_auth_continue; - auth->auth_free = mech_login_auth_free; - - return auth; + request->refcount = 1; + request->pool = pool; + return request; } const struct mech_module mech_login = { "LOGIN", - MEMBER(plaintext) TRUE, - MEMBER(advertise) TRUE, + MEMBER(flags) MECH_SEC_PLAINTEXT, MEMBER(passdb_need_plain) TRUE, MEMBER(passdb_need_credentials) FALSE, mech_login_auth_new, + mech_login_auth_initial, + mech_login_auth_continue, + mech_login_auth_free };
--- a/src/auth/mech-ntlm.c Wed Oct 13 15:32:54 2004 +0300 +++ b/src/auth/mech-ntlm.c Wed Oct 13 19:38:32 2004 +0300 @@ -36,7 +36,7 @@ lm_credentials_callback(const char *credentials, struct auth_request *auth_request) { - struct ntlm_auth_request *auth = + struct ntlm_auth_request *request = (struct ntlm_auth_request *)auth_request; const unsigned char *client_response; unsigned char lm_response[LM_RESPONSE_SIZE]; @@ -53,9 +53,9 @@ hash, sizeof(hash)); hex_to_binary(credentials, hash_buffer); - client_response = ntlmssp_buffer_data(auth->response, lm_response); + client_response = ntlmssp_buffer_data(request->response, lm_response); - ntlmssp_v1_response(hash, auth->challenge, lm_response); + ntlmssp_v1_response(hash, request->challenge, lm_response); ret = memcmp(lm_response, client_response, LM_RESPONSE_SIZE) == 0; @@ -66,7 +66,7 @@ ntlm_credentials_callback(const char *credentials, struct auth_request *auth_request) { - struct ntlm_auth_request *auth = + struct ntlm_auth_request *request = (struct ntlm_auth_request *)auth_request; const unsigned char *client_response; unsigned char hash[NTLMSSP_HASH_SIZE]; @@ -74,7 +74,7 @@ buffer_t *hash_buffer; int ret; - if (credentials == NULL && !auth->ntlm2_negotiated) { + if (credentials == NULL && !request->ntlm2_negotiated) { passdb->lookup_credentials(auth_request, PASSDB_CREDENTIALS_LANMAN, lm_credentials_callback); @@ -85,8 +85,9 @@ hash, sizeof(hash)); hex_to_binary(credentials, hash_buffer); - response_length = ntlmssp_buffer_length(auth->response, ntlm_response); - client_response = ntlmssp_buffer_data(auth->response, ntlm_response); + response_length = + ntlmssp_buffer_length(request->response, ntlm_response); + client_response = ntlmssp_buffer_data(request->response, ntlm_response); if (response_length > NTLMSSP_RESPONSE_SIZE) { unsigned char ntlm_v2_response[NTLMSSP_V2_RESPONSE_SIZE]; @@ -98,7 +99,7 @@ * as a standalone server, not as NT domain member. */ ntlmssp_v2_response(auth_request->user, NULL, - hash, auth->challenge, blob, + hash, request->challenge, blob, response_length - NTLMSSP_V2_RESPONSE_SIZE, ntlm_v2_response); @@ -107,14 +108,14 @@ } else { unsigned char ntlm_response[NTLMSSP_RESPONSE_SIZE]; const unsigned char *client_lm_response = - ntlmssp_buffer_data(auth->response, lm_response); + ntlmssp_buffer_data(request->response, lm_response); - if (auth->ntlm2_negotiated) - ntlmssp2_response(hash, auth->challenge, + if (request->ntlm2_negotiated) + ntlmssp2_response(hash, request->challenge, client_lm_response, ntlm_response); else - ntlmssp_v1_response(hash, auth->challenge, + ntlmssp_v1_response(hash, request->challenge, ntlm_response); ret = memcmp(ntlm_response, client_response, @@ -124,47 +125,42 @@ mech_auth_finish(auth_request, NULL, 0, ret); } -static int +static void mech_ntlm_auth_continue(struct auth_request *auth_request, const unsigned char *data, size_t data_size, mech_callback_t *callback) { - struct ntlm_auth_request *auth = + struct ntlm_auth_request *request = (struct ntlm_auth_request *)auth_request; - struct auth_client_request_reply reply; const char *error; auth_request->callback = callback; - if (!auth->challenge) { - const struct ntlmssp_request *request = + if (!request->challenge) { + const struct ntlmssp_request *ntlm_request = (struct ntlmssp_request *)data; const struct ntlmssp_challenge *message; size_t message_size; - if (!ntlmssp_check_request(request, data_size, &error)) { + if (!ntlmssp_check_request(ntlm_request, data_size, &error)) { if (verbose) { i_info("ntlm(%s): invalid NTLM request, %s", get_log_prefix(auth_request), error); } mech_auth_finish(auth_request, NULL, 0, FALSE); - return TRUE; + return; } - message = ntlmssp_create_challenge(auth->pool, request, + message = ntlmssp_create_challenge(request->pool, ntlm_request, &message_size); - auth->ntlm2_negotiated = + request->ntlm2_negotiated = read_le32(&message->flags) & NTLMSSP_NEGOTIATE_NTLM2; - auth->challenge = message->challenge; + request->challenge = message->challenge; - mech_init_auth_client_reply(&reply); - reply.id = auth_request->id; - reply.result = AUTH_CLIENT_RESULT_CONTINUE; - - reply.reply_idx = 0; - reply.data_size = message_size; - callback(&reply, message, auth_request->conn); + auth_request->callback(auth_request, + AUTH_CLIENT_RESULT_CONTINUE, + message, message_size); } else { const struct ntlmssp_response *response = (struct ntlmssp_response *)data; @@ -177,14 +173,14 @@ error); } mech_auth_finish(auth_request, NULL, 0, FALSE); - return TRUE; + return; } - auth->response = p_malloc(auth->pool, data_size); - memcpy(auth->response, response, data_size); + request->response = p_malloc(request->pool, data_size); + memcpy(request->response, response, data_size); username = p_strdup(auth_request->pool, - ntlmssp_t_str(auth->response, user)); + ntlmssp_t_str(request->response, user)); if (!mech_fix_username(username, &error)) { if (verbose) { @@ -192,70 +188,55 @@ get_log_prefix(auth_request), error); } mech_auth_finish(auth_request, NULL, 0, FALSE); - return TRUE; + return; } auth_request->user = username; - passdb->lookup_credentials(auth_request, PASSDB_CREDENTIALS_NTLM, ntlm_credentials_callback); } - - return TRUE; -} - -static int -mech_ntlm_auth_initial(struct auth_request *auth_request, - struct auth_client_request_new *request, - const unsigned char *data __attr_unused__, - mech_callback_t *callback) -{ - struct auth_client_request_reply reply; - - mech_init_auth_client_reply(&reply); - reply.id = request->id; - reply.result = AUTH_CLIENT_RESULT_CONTINUE; - - reply.reply_idx = 0; - reply.data_size = 0; - callback(&reply, "", auth_request->conn); - - return TRUE; } static void -mech_ntlm_auth_free(struct auth_request *auth_request) +mech_ntlm_auth_initial(struct auth_request *auth_request, + const unsigned char *data __attr_unused__, + size_t data_size __attr_unused__, + mech_callback_t *callback) { - pool_unref(auth_request->pool); + callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE, NULL, 0); +} + +static void +mech_ntlm_auth_free(struct auth_request *request) +{ + pool_unref(request->pool); } static struct auth_request *mech_ntlm_auth_new(void) { - struct ntlm_auth_request *auth; + struct ntlm_auth_request *request; pool_t pool; pool = pool_alloconly_create("ntlm_auth_request", 256); - auth = p_new(pool, struct ntlm_auth_request, 1); - auth->pool = pool; + request = p_new(pool, struct ntlm_auth_request, 1); + request->pool = pool; - auth->auth_request.refcount = 1; - auth->auth_request.pool = pool; - auth->auth_request.auth_initial = mech_ntlm_auth_initial; - auth->auth_request.auth_continue = mech_ntlm_auth_continue; - auth->auth_request.auth_free = mech_ntlm_auth_free; - - return &auth->auth_request; + request->auth_request.refcount = 1; + request->auth_request.pool = pool; + return &request->auth_request; } const struct mech_module mech_ntlm = { "NTLM", - MEMBER(plaintext) FALSE, - MEMBER(advertise) TRUE, + MEMBER(flags) MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE, MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) TRUE, mech_ntlm_auth_new, + mech_ntlm_auth_initial, + mech_ntlm_auth_continue, + mech_ntlm_auth_free };
--- a/src/auth/mech-plain.c Wed Oct 13 15:32:54 2004 +0300 +++ b/src/auth/mech-plain.c Wed Oct 13 19:38:32 2004 +0300 @@ -11,8 +11,8 @@ mech_auth_finish(request, NULL, 0, result == PASSDB_RESULT_OK); } -static int -mech_plain_auth_continue(struct auth_request *auth_request, +static void +mech_plain_auth_continue(struct auth_request *request, const unsigned char *data, size_t data_size, mech_callback_t *callback) { @@ -20,7 +20,7 @@ char *pass; size_t i, count, len; - auth_request->callback = callback; + request->callback = callback; /* authorization ID \0 authentication ID \0 pass. we'll ignore authorization ID for now. */ @@ -46,93 +46,74 @@ /* invalid input */ if (verbose) { i_info("plain(%s): invalid input", - get_log_prefix(auth_request)); + get_log_prefix(request)); } - mech_auth_finish(auth_request, NULL, 0, FALSE); + mech_auth_finish(request, NULL, 0, FALSE); } else { /* split and save user/realm */ if (strchr(authenid, '@') == NULL && default_realm != NULL) { - auth_request->user = p_strconcat(auth_request->pool, - authenid, "@", - default_realm, NULL); + request->user = p_strconcat(request->pool, + authenid, "@", + default_realm, NULL); } else { - auth_request->user = p_strdup(auth_request->pool, - authenid); + request->user = p_strdup(request->pool, authenid); } - if (!mech_fix_username(auth_request->user, &error)) { + if (!mech_fix_username(request->user, &error)) { /* invalid username */ if (verbose) { i_info("plain(%s): %s", - get_log_prefix(auth_request), error); + get_log_prefix(request), error); } - mech_auth_finish(auth_request, NULL, 0, FALSE); + mech_auth_finish(request, NULL, 0, FALSE); } else { - passdb->verify_plain(auth_request, pass, - verify_callback); + passdb->verify_plain(request, pass, verify_callback); } /* make sure it's cleared */ safe_memset(pass, 0, strlen(pass)); } - 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) +mech_plain_auth_initial(struct auth_request *request, + const unsigned char *data, size_t data_size, + mech_callback_t *callback) { - pool_unref(auth_request->pool); + if (data_size == 0) + callback(request, AUTH_CLIENT_RESULT_CONTINUE, NULL, 0); + else + mech_plain_auth_continue(request, data, data_size, callback); +} + +static void +mech_plain_auth_free(struct auth_request *request) +{ + pool_unref(request->pool); } static struct auth_request *mech_plain_auth_new(void) { - struct auth_request *auth_request; + struct auth_request *request; 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; - return auth_request; + request = p_new(pool, struct auth_request, 1); + request->refcount = 1; + request->pool = pool; + return request; } struct mech_module mech_plain = { "PLAIN", - MEMBER(plaintext) TRUE, - MEMBER(advertise) FALSE, + MEMBER(flags) MECH_SEC_PLAINTEXT, MEMBER(passdb_need_plain) TRUE, MEMBER(passdb_need_credentials) FALSE, - mech_plain_auth_new + mech_plain_auth_new, + mech_plain_auth_initial, + mech_plain_auth_continue, + mech_plain_auth_free };
--- a/src/auth/mech-rpa.c Wed Oct 13 15:32:54 2004 +0300 +++ b/src/auth/mech-rpa.c Wed Oct 13 19:38:32 2004 +0300 @@ -64,7 +64,7 @@ /* * Compute client -> server authentication response. */ -static void rpa_user_response(struct rpa_auth_request *auth, +static void rpa_user_response(struct rpa_auth_request *request, unsigned char *digest) { struct md5_context ctx; @@ -73,22 +73,22 @@ memset(z, 0, sizeof(z)); md5_init(&ctx); - md5_update(&ctx, auth->pwd_md5, 16); + md5_update(&ctx, request->pwd_md5, 16); md5_update(&ctx, z, sizeof(z)); - md5_update(&ctx, auth->username_ucs2be, auth->username_len); - md5_update(&ctx, auth->service_ucs2be, auth->service_len); - md5_update(&ctx, auth->realm_ucs2be, auth->realm_len); - md5_update(&ctx, auth->user_challenge, auth->user_challenge_len); - md5_update(&ctx, auth->service_challenge, RPA_SCHALLENGE_LEN); - md5_update(&ctx, auth->service_timestamp, RPA_TIMESTAMP_LEN); - md5_update(&ctx, auth->pwd_md5, 16); + md5_update(&ctx, request->username_ucs2be, request->username_len); + md5_update(&ctx, request->service_ucs2be, request->service_len); + md5_update(&ctx, request->realm_ucs2be, request->realm_len); + md5_update(&ctx, request->user_challenge, request->user_challenge_len); + md5_update(&ctx, request->service_challenge, RPA_SCHALLENGE_LEN); + md5_update(&ctx, request->service_timestamp, RPA_TIMESTAMP_LEN); + md5_update(&ctx, request->pwd_md5, 16); md5_final(&ctx, digest); } /* * Compute server -> client authentication response. */ -static void rpa_server_response(struct rpa_auth_request *auth, +static void rpa_server_response(struct rpa_auth_request *request, unsigned char *digest) { struct md5_context ctx; @@ -99,32 +99,32 @@ memset(z, 0, sizeof(z)); md5_init(&ctx); - md5_update(&ctx, auth->pwd_md5, 16); + md5_update(&ctx, request->pwd_md5, 16); md5_update(&ctx, z, sizeof(z)); - md5_update(&ctx, auth->service_ucs2be, auth->service_len); - md5_update(&ctx, auth->username_ucs2be, auth->username_len); - md5_update(&ctx, auth->realm_ucs2be, auth->realm_len); - md5_update(&ctx, auth->service_challenge, RPA_SCHALLENGE_LEN); - md5_update(&ctx, auth->user_challenge, auth->user_challenge_len); - md5_update(&ctx, auth->service_timestamp, RPA_TIMESTAMP_LEN); - md5_update(&ctx, auth->pwd_md5, 16); + md5_update(&ctx, request->service_ucs2be, request->service_len); + md5_update(&ctx, request->username_ucs2be, request->username_len); + md5_update(&ctx, request->realm_ucs2be, request->realm_len); + md5_update(&ctx, request->service_challenge, RPA_SCHALLENGE_LEN); + md5_update(&ctx, request->user_challenge, request->user_challenge_len); + md5_update(&ctx, request->service_timestamp, RPA_TIMESTAMP_LEN); + md5_update(&ctx, request->pwd_md5, 16); md5_final(&ctx, tmp); for (i = 0; i < 16; i++) - tmp[i] = auth->session_key[i] ^ tmp[i]; + tmp[i] = request->session_key[i] ^ tmp[i]; md5_init(&ctx); - md5_update(&ctx, auth->pwd_md5, 16); + md5_update(&ctx, request->pwd_md5, 16); md5_update(&ctx, z, sizeof(z)); - md5_update(&ctx, auth->service_ucs2be, auth->service_len); - md5_update(&ctx, auth->username_ucs2be, auth->username_len); - md5_update(&ctx, auth->realm_ucs2be, auth->realm_len); - md5_update(&ctx, auth->session_key, 16); - md5_update(&ctx, auth->service_challenge, RPA_SCHALLENGE_LEN); - md5_update(&ctx, auth->user_challenge, auth->user_challenge_len); - md5_update(&ctx, auth->service_timestamp, RPA_TIMESTAMP_LEN); + md5_update(&ctx, request->service_ucs2be, request->service_len); + md5_update(&ctx, request->username_ucs2be, request->username_len); + md5_update(&ctx, request->realm_ucs2be, request->realm_len); + md5_update(&ctx, request->session_key, 16); + md5_update(&ctx, request->service_challenge, RPA_SCHALLENGE_LEN); + md5_update(&ctx, request->user_challenge, request->user_challenge_len); + md5_update(&ctx, request->service_timestamp, RPA_TIMESTAMP_LEN); md5_update(&ctx, tmp, 16); - md5_update(&ctx, auth->pwd_md5, 16); + md5_update(&ctx, request->pwd_md5, 16); md5_final(&ctx, digest); } @@ -248,10 +248,10 @@ } static int -rpa_parse_token3(struct rpa_auth_request *auth, const void *data, +rpa_parse_token3(struct rpa_auth_request *request, const void *data, size_t data_size, const char **error) { - struct auth_request *auth_request = (struct auth_request *)auth; + struct auth_request *auth_request = &request->auth_request; const unsigned char *end = ((unsigned char *)data) + data_size; const unsigned char *p; unsigned int len; @@ -277,21 +277,21 @@ user = t_strndup(p, len); p += len; - auth_request->user = rpa_parse_username(auth->pool, user); + auth_request->user = rpa_parse_username(request->pool, user); - auth->username_ucs2be = ucs2be_str(auth->pool, auth_request->user, - &auth->username_len); + request->username_ucs2be = ucs2be_str(request->pool, auth_request->user, + &request->username_len); /* Read user challenge */ - auth->user_challenge_len = rpa_read_buffer(auth->pool, &p, end, - &auth->user_challenge); - if (auth->user_challenge_len == 0) { + request->user_challenge_len = rpa_read_buffer(request->pool, &p, end, + &request->user_challenge); + if (request->user_challenge_len == 0) { *error = "invalid user challenge"; return FALSE; } /* Read user response */ - len = rpa_read_buffer(auth->pool, &p, end, &auth->user_response); + len = rpa_read_buffer(request->pool, &p, end, &request->user_response); if (len != RPA_UCHALLENGE_LEN) { *error = "invalid user response"; return FALSE; @@ -321,7 +321,7 @@ } static const unsigned char * -mech_rpa_build_token2(struct rpa_auth_request *auth, +mech_rpa_build_token2(struct rpa_auth_request *request, const char *realms, size_t *size) { unsigned int realms_len; @@ -333,7 +333,7 @@ length = sizeof(rpa_oid) + 3 + RPA_SCHALLENGE_LEN + RPA_TIMESTAMP_LEN + 2 + realms_len; - buf = buffer_create_dynamic(auth->pool, length + 4); + buf = buffer_create_dynamic(request->pool, length + 4); buffer_append_c(buf, ASN1_APPLICATION); buffer_append_asn1_length(buf, length); @@ -344,18 +344,19 @@ buffer_append_c(buf, 0); /* Service challenge */ - auth->service_challenge = p_malloc(auth->pool, RPA_SCHALLENGE_LEN); - random_fill(auth->service_challenge, RPA_SCHALLENGE_LEN); + request->service_challenge = + p_malloc(request->pool, RPA_SCHALLENGE_LEN); + random_fill(request->service_challenge, RPA_SCHALLENGE_LEN); buffer_append_c(buf, RPA_SCHALLENGE_LEN); - buffer_append(buf, auth->service_challenge, RPA_SCHALLENGE_LEN); + buffer_append(buf, request->service_challenge, RPA_SCHALLENGE_LEN); /* Timestamp, looks like clients accept anything we send */ random_fill(timestamp, sizeof(timestamp)); - auth->service_timestamp = p_malloc(auth->pool, RPA_TIMESTAMP_LEN); - memcpy(auth->service_timestamp, + request->service_timestamp = p_malloc(request->pool, RPA_TIMESTAMP_LEN); + memcpy(request->service_timestamp, binary_to_hex(timestamp, sizeof(timestamp)), RPA_TIMESTAMP_LEN); - buffer_append(buf, auth->service_timestamp, RPA_TIMESTAMP_LEN); + buffer_append(buf, request->service_timestamp, RPA_TIMESTAMP_LEN); /* Realm list */ buffer_append_c(buf, realms_len >> 8); @@ -367,29 +368,29 @@ } static const unsigned char * -mech_rpa_build_token4(struct rpa_auth_request *auth, size_t *size) +mech_rpa_build_token4(struct rpa_auth_request *request, size_t *size) { unsigned int length = sizeof(rpa_oid) + 17 + 17 + 1; buffer_t *buf; unsigned char server_response[16]; - buf = buffer_create_dynamic(auth->pool, length + 4); + buf = buffer_create_dynamic(request->pool, length + 4); buffer_append_c(buf, ASN1_APPLICATION); buffer_append_asn1_length(buf, length); buffer_append(buf, rpa_oid, sizeof(rpa_oid)); /* Generate random session key */ - auth->session_key = p_malloc(auth->pool, 16); - random_fill(auth->session_key, 16); + request->session_key = p_malloc(request->pool, 16); + random_fill(request->session_key, 16); /* Server authentication response */ - rpa_server_response(auth, server_response); + rpa_server_response(request, server_response); buffer_append_c(buf, 16); buffer_append(buf, server_response, 16); buffer_append_c(buf, 16); - buffer_append(buf, auth->session_key, 16); + buffer_append(buf, request->session_key, 16); /* Status, 0 - success */ buffer_append_c(buf, 0); @@ -402,28 +403,24 @@ rpa_credentials_callback(const char *credentials, struct auth_request *auth_request) { - struct rpa_auth_request *auth = + struct rpa_auth_request *request = (struct rpa_auth_request *)auth_request; buffer_t *hash_buffer; if (credentials == NULL) return; - auth->pwd_md5 = p_malloc(auth->pool, 16); - - hash_buffer = buffer_create_data(auth->pool, auth->pwd_md5, 16); - + request->pwd_md5 = p_malloc(request->pool, 16); + hash_buffer = buffer_create_data(request->pool, request->pwd_md5, 16); hex_to_binary(credentials, hash_buffer); } -static int +static void mech_rpa_auth_phase1(struct auth_request *auth_request, - const unsigned char *data, size_t data_size, - mech_callback_t *callback) + const unsigned char *data, size_t data_size) { - struct rpa_auth_request *auth = + struct rpa_auth_request *request = (struct rpa_auth_request *)auth_request; - struct auth_client_request_reply reply; const unsigned char *token2; size_t token2_size; const char *service, *error; @@ -434,52 +431,42 @@ get_log_prefix(auth_request), error); } mech_auth_finish(auth_request, NULL, 0, FALSE); - return TRUE; + return; } service = t_str_lcase(auth_request->protocol); - token2 = mech_rpa_build_token2(auth, t_strconcat(service, "@", + token2 = mech_rpa_build_token2(request, t_strconcat(service, "@", my_hostname, NULL), &token2_size); - auth->service_ucs2be = ucs2be_str(auth->pool, service, - &auth->service_len); - auth->realm_ucs2be = ucs2be_str(auth->pool, my_hostname, - &auth->realm_len); + request->service_ucs2be = ucs2be_str(request->pool, service, + &request->service_len); + request->realm_ucs2be = ucs2be_str(request->pool, my_hostname, + &request->realm_len); - mech_init_auth_client_reply(&reply); - reply.id = auth_request->id; - reply.result = AUTH_CLIENT_RESULT_CONTINUE; - - reply.reply_idx = 0; - reply.data_size = token2_size; - callback(&reply, token2, auth_request->conn); - - auth->phase = 1; - - return TRUE; + auth_request->callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE, + token2, token2_size); + request->phase = 1; } -static int +static void mech_rpa_auth_phase2(struct auth_request *auth_request, - const unsigned char *data, size_t data_size, - mech_callback_t *callback) + const unsigned char *data, size_t data_size) { - struct rpa_auth_request *auth = + struct rpa_auth_request *request = (struct rpa_auth_request *)auth_request; - struct auth_client_request_reply reply; unsigned char response[16]; const unsigned char *token4; const char *error; size_t token4_size; - if (!rpa_parse_token3(auth, data, data_size, &error)) { + if (!rpa_parse_token3(request, data, data_size, &error)) { if (verbose) { i_info("rpa(%s): invalid token 3, %s", get_log_prefix(auth_request), error); } mech_auth_finish(auth_request, NULL, 0, FALSE); - return TRUE; + return; } if (!mech_fix_username(auth_request->user, &error)) { @@ -488,41 +475,31 @@ get_log_prefix(auth_request), error); } mech_auth_finish(auth_request, NULL, 0, FALSE); - return TRUE; + return; } passdb->lookup_credentials(auth_request, PASSDB_CREDENTIALS_RPA, rpa_credentials_callback); - if (auth->pwd_md5 == NULL) { + if (request->pwd_md5 == NULL) { mech_auth_finish(auth_request, NULL, 0, FALSE); - return TRUE; - } - - rpa_user_response(auth, response); - if (memcmp(response, auth->user_response, 16) != 0) { - mech_auth_finish(auth_request, NULL, 0, FALSE); - return TRUE; + return; } - token4 = mech_rpa_build_token4(auth, &token4_size); - - mech_init_auth_client_reply(&reply); - reply.id = auth_request->id; - reply.result = AUTH_CLIENT_RESULT_CONTINUE; + rpa_user_response(request, response); + if (memcmp(response, request->user_response, 16) != 0) { + mech_auth_finish(auth_request, NULL, 0, FALSE); + return; + } - reply.reply_idx = 0; - reply.data_size = token4_size; - callback(&reply, token4, auth_request->conn); - - auth->phase = 2; - - return TRUE; + token4 = mech_rpa_build_token4(request, &token4_size); + auth_request->callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE, + token4, token4_size); + request->phase = 2; } -static int +static void mech_rpa_auth_phase3(struct auth_request *auth_request, - const unsigned char *data, size_t data_size, - mech_callback_t *callback __attr_unused__) + const unsigned char *data, size_t data_size) { static const unsigned char client_ack[3] = { 0x60, 0x01, 0x00 }; int ret = TRUE; @@ -537,90 +514,80 @@ } mech_auth_finish(auth_request, NULL, 0, ret); - return TRUE; } -static int +static void mech_rpa_auth_continue(struct auth_request *auth_request, - const unsigned char *data, size_t data_size, - mech_callback_t *callback) + const unsigned char *data, size_t data_size, + mech_callback_t *callback) { - struct rpa_auth_request *auth = + struct rpa_auth_request *request = (struct rpa_auth_request *)auth_request; auth_request->callback = callback; - switch (auth->phase) { - case 0: return mech_rpa_auth_phase1(auth_request, data, - data_size, callback); - case 1: return mech_rpa_auth_phase2(auth_request, data, - data_size, callback); - case 2: return mech_rpa_auth_phase3(auth_request, data, - data_size, callback); + switch (request->phase) { + case 0: + mech_rpa_auth_phase1(auth_request, data, data_size); + break; + case 1: + mech_rpa_auth_phase2(auth_request, data, data_size); + break; + case 2: + mech_rpa_auth_phase3(auth_request, data, data_size); + break; + default: + mech_auth_finish(auth_request, NULL, 0, FALSE); + break; } - - mech_auth_finish(auth_request, NULL, 0, FALSE); - return TRUE; } -static int +static void mech_rpa_auth_initial(struct auth_request *auth_request, - struct auth_client_request_new *request, const unsigned char *data __attr_unused__, + size_t data_size __attr_unused__, mech_callback_t *callback) { - struct auth_client_request_reply reply; - - mech_init_auth_client_reply(&reply); - reply.id = request->id; - reply.result = AUTH_CLIENT_RESULT_CONTINUE; - - reply.reply_idx = 0; - reply.data_size = 0; - callback(&reply, "", auth_request->conn); - - return TRUE; + callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE, NULL, 0); } static void mech_rpa_auth_free(struct auth_request *auth_request) { - struct rpa_auth_request *auth = + struct rpa_auth_request *request = (struct rpa_auth_request *)auth_request; - if (auth->pwd_md5 != NULL) - safe_memset(auth->pwd_md5, 0, 16); + if (request->pwd_md5 != NULL) + safe_memset(request->pwd_md5, 0, 16); pool_unref(auth_request->pool); } static struct auth_request *mech_rpa_auth_new(void) { - struct rpa_auth_request *auth; + struct rpa_auth_request *request; pool_t pool; pool = pool_alloconly_create("rpa_auth_request", 256); - auth = p_new(pool, struct rpa_auth_request, 1); - auth->pool = pool; - auth->phase = 0; + request = p_new(pool, struct rpa_auth_request, 1); + request->pool = pool; + request->phase = 0; - auth->auth_request.refcount = 1; - auth->auth_request.pool = pool; - auth->auth_request.auth_initial = mech_rpa_auth_initial; - auth->auth_request.auth_continue = mech_rpa_auth_continue; - auth->auth_request.auth_free = mech_rpa_auth_free; - - return &auth->auth_request; + request->auth_request.refcount = 1; + request->auth_request.pool = pool; + return &request->auth_request; } const struct mech_module mech_rpa = { "RPA", - MEMBER(plaintext) FALSE, - MEMBER(advertise) TRUE, + MEMBER(flags) MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE, MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) TRUE, mech_rpa_auth_new, + mech_rpa_auth_initial, + mech_rpa_auth_continue, + mech_rpa_auth_free };
--- a/src/auth/mech.c Wed Oct 13 15:32:54 2004 +0300 +++ b/src/auth/mech.c Wed Oct 13 19:38:32 2004 +0300 @@ -14,14 +14,14 @@ #include <stdlib.h> struct mech_module_list *mech_modules; +buffer_t *mech_handshake; + const char *const *auth_realms; const char *default_realm; const char *anonymous_username; char username_chars[256], username_translation[256]; int ssl_require_client_cert; -static struct auth_client_request_reply failure_reply; - static buffer_t *auth_failures_buf; static struct timeout *to_auth_failures; @@ -32,6 +32,23 @@ list = i_new(struct mech_module_list, 1); list->module = *module; + str_printfa(mech_handshake, "MECH\t%s", module->mech_name); + if ((module->flags & MECH_SEC_PRIVATE) != 0) + str_append(mech_handshake, "\tprivate"); + if ((module->flags & MECH_SEC_ANONYMOUS) != 0) + str_append(mech_handshake, "\tanonymous"); + if ((module->flags & MECH_SEC_PLAINTEXT) != 0) + str_append(mech_handshake, "\tplaintext"); + if ((module->flags & MECH_SEC_DICTIONARY) != 0) + str_append(mech_handshake, "\tdictionary"); + if ((module->flags & MECH_SEC_ACTIVE) != 0) + str_append(mech_handshake, "\tactive"); + if ((module->flags & MECH_SEC_FORWARD_SECRECY) != 0) + str_append(mech_handshake, "\tforward-secrecy"); + if ((module->flags & MECH_SEC_MUTUAL_AUTH) != 0) + str_append(mech_handshake, "\tmutual-auth"); + str_append_c(mech_handshake, '\n'); + list->next = mech_modules; mech_modules = list; } @@ -62,7 +79,7 @@ return str; } -static struct mech_module *mech_module_find(const char *name) +struct mech_module *mech_module_find(const char *name) { struct mech_module_list *list; @@ -73,183 +90,48 @@ 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 auth_request *auth_request_new(struct mech_module *mech) { - struct mech_module *mech; - struct auth_request *auth_request; - size_t ip_size = 1; + struct auth_request *request; - if (request->ip_family == AF_INET) - ip_size = 4; - else if (request->ip_family != 0) - ip_size = sizeof(auth_request->local_ip.ip); - else - ip_size = 0; + request = mech->auth_new(); + if (request == NULL) + return NULL; - /* make sure data is NUL-terminated */ - if (request->data_size <= ip_size*2 || 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; - } + request->mech = mech; + request->created = ioloop_time; + return request; +} - 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 %s", conn->pid, - (const char *)data + request->mech_idx); - failure_reply.id = request->id; - callback(&failure_reply, NULL, conn); - return; +void auth_request_destroy(struct auth_request *request) +{ + if (request->conn != NULL) { + hash_remove(request->conn->auth_requests, + POINTER_CAST(request->id)); } - - auth_request = mech->auth_new(); - if (auth_request == NULL) - return; + auth_request_unref(request); +} - auth_request->created = ioloop_time; - auth_request->conn = conn; - auth_request->id = request->id; - auth_request->protocol = - p_strdup(auth_request->pool, - (const char *)data + request->protocol_idx); - - if (request->ip_family != 0) { - auth_request->local_ip.family = request->ip_family; - auth_request->remote_ip.family = request->ip_family; - - memcpy(&auth_request->local_ip.ip, data, ip_size); - memcpy(&auth_request->remote_ip.ip, data + ip_size, ip_size); - } - - if (ssl_require_client_cert && - (request->flags & AUTH_CLIENT_FLAG_SSL_VALID_CLIENT_CERT) == 0) { - /* we fail without valid certificate */ - if (verbose) { - i_info("ssl-cert-check(%s): " - "Client didn't present valid SSL certificate", - get_log_prefix(auth_request)); - } - auth_request_unref(auth_request); - - failure_reply.id = request->id; - callback(&failure_reply, NULL, conn); +void mech_auth_finish(struct auth_request *request, + const void *data, size_t data_size, int success) +{ + if (!success) { + /* failure. don't announce it immediately to avoid + a) timing attacks, b) flooding */ + buffer_append(auth_failures_buf, &request, sizeof(request)); return; } - 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); -} - -void mech_request_continue(struct auth_client_connection *conn, - struct auth_client_request_continue *request, - const unsigned char *data, - mech_callback_t *callback) -{ - struct auth_request *auth_request; - - auth_request = hash_lookup(conn->auth_requests, - POINTER_CAST(request->id)); - if (auth_request == NULL) { - /* timeouted */ - failure_reply.id = request->id; - callback(&failure_reply, NULL, conn); - } else { - if (!auth_request->auth_continue(auth_request, - data, request->data_size, - callback)) - mech_request_free(auth_request, request->id); - } -} - -void mech_request_free(struct auth_request *auth_request, unsigned int id) -{ - if (auth_request->conn != NULL) { - hash_remove(auth_request->conn->auth_requests, - POINTER_CAST(id)); - } - auth_request_unref(auth_request); -} - -void mech_init_auth_client_reply(struct auth_client_request_reply *reply) -{ - memset(reply, 0, sizeof(*reply)); - - reply->username_idx = (uint32_t)-1; - reply->reply_idx = (uint32_t)-1; -} - -void *mech_auth_success(struct auth_client_request_reply *reply, - struct auth_request *auth_request, - const void *data, size_t data_size) -{ - buffer_t *buf; - - buf = buffer_create_dynamic(pool_datastack_create(), 256); - - reply->username_idx = 0; - buffer_append(buf, auth_request->user, strlen(auth_request->user)+1); - - if (data_size == 0) - reply->reply_idx = (uint32_t)-1; - else { - reply->reply_idx = buffer_get_used_size(buf); - buffer_append(buf, data, data_size); + if (request->conn != NULL) { + request->callback(request, AUTH_CLIENT_RESULT_SUCCESS, + data, data_size); } - reply->result = AUTH_CLIENT_RESULT_SUCCESS; - reply->data_size = buffer_get_used_size(buf); - return buffer_get_modifyable_data(buf, NULL); -} - -void mech_auth_finish(struct auth_request *auth_request, - const void *data, size_t data_size, int success) -{ - struct auth_client_request_reply reply; - void *reply_data; - int free_request; - - if (!success) { - /* failure. don't announce it immediately to avoid - a) timing attacks, b) flooding */ - buffer_append(auth_failures_buf, - &auth_request, sizeof(auth_request)); - return; - } - - memset(&reply, 0, sizeof(reply)); - reply.id = auth_request->id; - reply.result = AUTH_CLIENT_RESULT_SUCCESS; - - if (auth_request->conn == NULL) { - /* client is already gone */ - free_request = TRUE; - } else { - /* get this before callback because it can destroy connection */ - free_request = AUTH_MASTER_IS_DUMMY(auth_request->conn->master); - - reply_data = mech_auth_success(&reply, auth_request, - data, data_size); - auth_request->callback(&reply, reply_data, auth_request->conn); - } - - if (free_request) { + if (request->conn == NULL || + AUTH_MASTER_IS_DUMMY(request->conn->master)) { /* we don't have master process, the request is no longer needed */ - mech_request_free(auth_request, auth_request->id); + auth_request_destroy(request); } } @@ -285,7 +167,7 @@ if (--request->refcount > 0) return TRUE; - request->auth_free(request); + request->mech->auth_free(request); return FALSE; } @@ -358,22 +240,18 @@ void auth_failure_buf_flush(void) { struct auth_request **auth_request; - struct auth_client_request_reply reply; size_t i, size; auth_request = buffer_get_modifyable_data(auth_failures_buf, &size); size /= sizeof(*auth_request); - memset(&reply, 0, sizeof(reply)); - reply.result = AUTH_CLIENT_RESULT_FAILURE; - for (i = 0; i < size; i++) { - reply.id = auth_request[i]->id; if (auth_request[i]->conn != NULL) { - auth_request[i]->callback(&reply, NULL, - auth_request[i]->conn); + auth_request[i]->callback(auth_request[i], + AUTH_CLIENT_RESULT_FAILURE, + NULL, 0); } - mech_request_free(auth_request[i], reply.id); + auth_request_destroy(auth_request[i]); } buffer_set_used_size(auth_failures_buf, 0); } @@ -397,10 +275,8 @@ const char *const *mechanisms; const char *env; - mech_modules = NULL; - - memset(&failure_reply, 0, sizeof(failure_reply)); - failure_reply.result = AUTH_CLIENT_RESULT_FAILURE; + mech_modules = NULL; + mech_handshake = str_new(default_pool, 512); anonymous_username = getenv("ANONYMOUS_USERNAME"); if (anonymous_username != NULL && *anonymous_username == '\0') @@ -493,4 +369,6 @@ mech_unregister_module(&mech_ntlm); mech_unregister_module(&mech_rpa); mech_unregister_module(&mech_anonymous); + + str_free(mech_handshake); }
--- a/src/auth/mech.h Wed Oct 13 15:32:54 2004 +0300 +++ b/src/auth/mech.h Wed Oct 13 19:38:32 2004 +0300 @@ -4,11 +4,18 @@ #include "network.h" #include "auth-client-interface.h" +enum auth_client_result { + AUTH_CLIENT_RESULT_CONTINUE = 1, + AUTH_CLIENT_RESULT_SUCCESS, + AUTH_CLIENT_RESULT_FAILURE +}; + +struct auth_request; struct auth_client_connection; -typedef void mech_callback_t(struct auth_client_request_reply *reply, - const void *data, - struct auth_client_connection *conn); +typedef void mech_callback_t(struct auth_request *request, + enum auth_client_result result, + const void *reply, size_t reply_size); struct auth_request { int refcount; @@ -16,34 +23,35 @@ pool_t pool; char *user; + struct mech_module *mech; struct auth_client_connection *conn; + unsigned int id; time_t created; - char *protocol; + const char *protocol; struct ip_addr local_ip, remote_ip; 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, - const unsigned char *data, size_t data_size, - mech_callback_t *callback); - void (*auth_free)(struct auth_request *auth_request); + unsigned int accept_input:1; /* ... mechanism specific data ... */ }; struct mech_module { const char *mech_name; - unsigned int plaintext:1; - unsigned int advertise:1; + enum mech_security_flags flags; unsigned int passdb_need_plain:1; unsigned int passdb_need_credentials:1; struct auth_request *(*auth_new)(void); + void (*auth_initial)(struct auth_request *request, + const unsigned char *data, size_t data_size, + mech_callback_t *callback); + void (*auth_continue)(struct auth_request *request, + const unsigned char *data, size_t data_size, + mech_callback_t *callback); + void (*auth_free)(struct auth_request *request); }; struct mech_module_list { @@ -53,6 +61,8 @@ }; extern struct mech_module_list *mech_modules; +extern buffer_t *mech_handshake; + extern const char *const *auth_realms; extern const char *default_realm; extern const char *anonymous_username; @@ -61,28 +71,17 @@ void mech_register_module(struct mech_module *module); void mech_unregister_module(struct mech_module *module); +struct mech_module *mech_module_find(const char *name); 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, - const unsigned char *data, - mech_callback_t *callback); -void mech_request_free(struct auth_request *auth_request, unsigned int id); - -void mech_init_auth_client_reply(struct auth_client_request_reply *reply); -void *mech_auth_success(struct auth_client_request_reply *reply, - struct auth_request *auth_request, - const void *data, size_t data_size); -void mech_auth_finish(struct auth_request *auth_request, +void mech_auth_finish(struct auth_request *request, const void *data, size_t data_size, int success); int mech_fix_username(char *username, const char **error_r); +struct auth_request *auth_request_new(struct mech_module *mech); +void auth_request_destroy(struct auth_request *request); void auth_request_ref(struct auth_request *request); int auth_request_unref(struct auth_request *request);
--- a/src/auth/userdb.h Wed Oct 13 15:32:54 2004 +0300 +++ b/src/auth/userdb.h Wed Oct 13 19:38:32 2004 +0300 @@ -13,7 +13,7 @@ gid_t gid; }; -typedef void userdb_callback_t(struct user_data *user, void *context); +typedef void userdb_callback_t(const struct user_data *user, void *context); struct userdb_module { void (*preinit)(const char *args);
--- a/src/imap-login/client-authenticate.c Wed Oct 13 15:32:54 2004 +0300 +++ b/src/imap-login/client-authenticate.c Wed Oct 13 19:38:32 2004 +0300 @@ -26,10 +26,10 @@ /* 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 && - (secured || !mech[i].plaintext)) { + if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 && + (secured || !disable_plaintext_auth || + (mech[i].flags & MECH_SEC_PLAINTEXT) == 0)) { str_append_c(str, ' '); str_append(str, "AUTH="); str_append(str, mech[i].name); @@ -42,9 +42,7 @@ static void client_auth_input(void *context) { struct imap_client *client = context; - buffer_t *buf; char *line; - size_t linelen, bufsize; if (!client_read(client)) return; @@ -67,25 +65,15 @@ return; } - linelen = strlen(line); - buf = buffer_create_static_hard(pool_datastack_create(), linelen); - - if (base64_decode(line, linelen, NULL, buf) < 0) { - /* failed */ - sasl_server_auth_cancel(&client->common, "Invalid base64 data"); - } else if (client->common.auth_request == NULL) { + if (client->common.auth_request == NULL) { sasl_server_auth_cancel(&client->common, "Don't send unrequested data"); } else { - auth_client_request_continue(client->common.auth_request, - buf->data, buf->used); + auth_client_request_continue(client->common.auth_request, line); } /* clear sensitive data */ - safe_memset(line, 0, linelen); - - bufsize = buffer_get_used_size(buf); - safe_memset(buffer_free_without_data(buf), 0, bufsize); + safe_memset(line, 0, strlen(line)); } static void sasl_callback(struct client *_client, enum sasl_server_reply reply, @@ -162,7 +150,7 @@ return FALSE; client_ref(client); - sasl_server_auth_begin(&client->common, "IMAP", mech_name, NULL, 0, + sasl_server_auth_begin(&client->common, "IMAP", mech_name, NULL, sasl_callback); if (!client->common.authenticating) return 1; @@ -178,7 +166,7 @@ int cmd_login(struct imap_client *client, struct imap_arg *args) { const char *user, *pass; - string_t *plain_login; + string_t *plain_login, *base64; /* two arguments: username and password */ if (args[0].type != IMAP_ARG_ATOM && args[0].type != IMAP_ARG_STRING) @@ -212,10 +200,13 @@ buffer_append_c(plain_login, '\0'); buffer_append(plain_login, pass, strlen(pass)); + base64 = buffer_create_dynamic(pool_datastack_create(), + MAX_BASE64_ENCODED_SIZE(plain_login->used)); + base64_encode(plain_login->data, plain_login->used, base64); + client_ref(client); sasl_server_auth_begin(&client->common, "IMAP", "PLAIN", - plain_login->data, plain_login->used, - sasl_callback); + str_c(base64), sasl_callback); if (!client->common.authenticating) return 1;
--- a/src/lib-auth/auth-client.h Wed Oct 13 15:32:54 2004 +0300 +++ b/src/lib-auth/auth-client.h Wed Oct 13 19:38:32 2004 +0300 @@ -7,10 +7,14 @@ struct auth_client; struct auth_request; +enum auth_request_flags { + AUTH_REQUEST_FLAG_SECURED = 0x01, + AUTH_REQUEST_FLAG_VALID_CLIENT_CERT = 0x02, +}; + struct auth_mech_desc { char *name; - unsigned int plaintext:1; - unsigned int advertise:1; + enum mech_security_flags flags; }; struct auth_connect_id { @@ -21,18 +25,16 @@ struct auth_request_info { const char *mech; const char *protocol; - enum auth_client_request_new_flags flags; + enum auth_request_flags flags; struct ip_addr local_ip, remote_ip; - const unsigned char *initial_resp_data; - size_t initial_resp_size; + const char *initial_resp_base64; }; -/* reply is NULL if auth connection died */ -typedef void auth_request_callback_t(struct auth_request *request, - struct auth_client_request_reply *reply, - const unsigned char *data, void *context); +typedef void auth_request_callback_t(struct auth_request *request, int status, + const char *data_base64, + const char *const *args, void *context); typedef void auth_connect_notify_callback_t(struct auth_client *client, int connected, void *context); @@ -69,7 +71,7 @@ /* Continue authentication. Call when reply->result == AUTH_CLIENT_REQUEST_CONTINUE */ void auth_client_request_continue(struct auth_request *request, - const unsigned char *data, size_t data_size); + const char *data_base64); /* Abort ongoing authentication request. */ void auth_client_request_abort(struct auth_request *request);
--- a/src/lib-auth/auth-server-connection.c Wed Oct 13 15:32:54 2004 +0300 +++ b/src/lib-auth/auth-server-connection.c Wed Oct 13 19:38:32 2004 +0300 @@ -1,4 +1,4 @@ -/* Copyright (C) 2003 Timo Sirainen */ +/* Copyright (C) 2003-2004 Timo Sirainen */ #include "lib.h" #include "buffer.h" @@ -12,13 +12,7 @@ #include "auth-server-request.h" #include <unistd.h> - -/* Maximum size for an auth reply. 50kB should be more than enough. */ -#define MAX_INBUF_SIZE (1024*50) - -#define MAX_OUTBUF_SIZE \ - (sizeof(struct auth_client_request_continue) + \ - AUTH_CLIENT_MAX_REQUEST_DATA_SIZE) +#include <stdlib.h> static void auth_server_connection_unref(struct auth_server_connection *conn); @@ -40,54 +34,81 @@ } } -static void auth_handle_handshake(struct auth_server_connection *conn, - struct auth_client_handshake_reply *handshake, - const unsigned char *data) +static int +auth_client_input_mech(struct auth_server_connection *conn, const char *args) { - struct auth_client_handshake_mech_desc handshake_mech_desc; + const char *const *list; struct auth_mech_desc mech_desc; - buffer_t *buf; - unsigned int i; - 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; + if (conn->handshake_received) { + i_error("BUG: Authentication server already sent handshake"); + return FALSE; + } + + list = t_strsplit(args, "\t"); + if (list[0] == NULL) { + i_error("BUG: Authentication server sent broken MECH line"); + return FALSE; } - buf = buffer_create_dynamic(conn->pool, sizeof(mech_desc) * - handshake->mech_count); - for (i = 0; i < handshake->mech_count; i++) { - memcpy(&handshake_mech_desc, - data + sizeof(handshake_mech_desc) * i, - sizeof(handshake_mech_desc)); + memset(&mech_desc, 0, sizeof(mech_desc)); + mech_desc.name = p_strdup(conn->pool, list[0]); + + if (strcmp(mech_desc.name, "PLAIN") == 0) + conn->has_plain_mech = TRUE; - if (handshake_mech_desc.name_idx >= handshake->data_size) { - i_error("BUG: Auth server sent corrupted handshake"); - auth_server_connection_destroy(conn, FALSE); - return; - } + for (list++; *list != NULL; list++) { + if (strcmp(*list, "private") == 0) + mech_desc.flags |= MECH_SEC_PRIVATE; + else if (strcmp(*list, "anonymous") == 0) + mech_desc.flags |= MECH_SEC_ANONYMOUS; + else if (strcmp(*list, "plaintext") == 0) + mech_desc.flags |= MECH_SEC_PLAINTEXT; + else if (strcmp(*list, "dictionary") == 0) + mech_desc.flags |= MECH_SEC_DICTIONARY; + else if (strcmp(*list, "active") == 0) + mech_desc.flags |= MECH_SEC_ACTIVE; + else if (strcmp(*list, "forward-secrecy") == 0) + mech_desc.flags |= MECH_SEC_FORWARD_SECRECY; + else if (strcmp(*list, "mutual-auth") == 0) + mech_desc.flags |= MECH_SEC_MUTUAL_AUTH; + } + buffer_append(conn->auth_mechs_buf, &mech_desc, sizeof(mech_desc)); + return TRUE; +} - 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; +static int +auth_client_input_spid(struct auth_server_connection *conn, const char *args) +{ + if (conn->handshake_received) { + i_error("BUG: Authentication server already sent handshake"); + return FALSE; } - conn->server_pid = handshake->server_pid; - conn->connect_uid = handshake->connect_uid; + conn->server_pid = (unsigned int)strtoul(args, NULL, 10); + return TRUE; +} + +static int +auth_client_input_cuid(struct auth_server_connection *conn, const char *args) +{ + if (conn->handshake_received) { + i_error("BUG: Authentication server already sent handshake"); + return FALSE; + } + + conn->connect_uid = (unsigned int)strtoul(args, NULL, 10); + return TRUE; +} + +static int auth_client_input_done(struct auth_server_connection *conn) +{ + conn->available_auth_mechs = conn->auth_mechs_buf->data; conn->available_auth_mechs_count = - buffer_get_used_size(buf) / sizeof(mech_desc); - conn->available_auth_mechs = buffer_free_without_data(buf); + conn->auth_mechs_buf->used / sizeof(struct auth_mech_desc); + conn->handshake_received = TRUE; - - conn->client->conn_waiting_handshake_count--; + conn->client->conn_waiting_handshake_count--; update_available_auth_mechs(conn); if (conn->client->connect_notify_callback != NULL && @@ -95,14 +116,14 @@ conn->client->connect_notify_callback(conn->client, TRUE, conn->client->connect_notify_context); } + return TRUE; } static void auth_client_input(void *context) { struct auth_server_connection *conn = context; - struct auth_client_handshake_reply handshake; - const unsigned char *data; - size_t size; + const char *line; + int ret; switch (i_stream_read(conn->input)) { case 0: @@ -114,50 +135,37 @@ case -2: /* buffer full - can't happen unless auth is buggy */ i_error("BUG: Auth server sent us more than %d bytes of data", - MAX_INBUF_SIZE); + AUTH_CLIENT_MAX_LINE_LENGTH); auth_server_connection_destroy(conn, FALSE); return; } - if (!conn->handshake_received) { - data = i_stream_get_data(conn->input, &size); - 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; - } + conn->refcount++; + while ((line = i_stream_next_line(conn->input)) != NULL) { + if (strncmp(line, "OK\t", 3) == 0) + ret = auth_client_input_ok(conn, line + 3); + else if (strncmp(line, "CONT\t", 5) == 0) + ret = auth_client_input_cont(conn, line + 5); + else if (strncmp(line, "FAIL\t", 5) == 0) + ret = auth_client_input_fail(conn, line + 5); + else if (strncmp(line, "MECH\t", 5) == 0) + ret = auth_client_input_mech(conn, line + 5); + else if (strncmp(line, "SPID\t", 5) == 0) + ret = auth_client_input_spid(conn, line + 5); + else if (strncmp(line, "CUID\t", 5) == 0) + ret = auth_client_input_cuid(conn, line + 5); + else if (strcmp(line, "DONE") == 0) + ret = auth_client_input_done(conn); + else { + /* ignore unknown command */ + ret = TRUE; + } - if (!conn->reply_received) { - data = i_stream_get_data(conn->input, &size); - if (size < sizeof(conn->reply)) - return; - - memcpy(&conn->reply, data, sizeof(conn->reply)); - i_stream_skip(conn->input, sizeof(conn->reply)); - conn->reply_received = TRUE; + if (!ret) { + auth_server_connection_destroy(conn, FALSE); + break; + } } - - data = i_stream_get_data(conn->input, &size); - if (size < conn->reply.data_size) - return; - - /* we've got a full reply */ - conn->refcount++; - conn->reply_received = FALSE; - - auth_server_request_handle_reply(conn, &conn->reply, data); - i_stream_skip(conn->input, conn->reply.data_size); - auth_server_connection_unref(conn); } @@ -165,7 +173,6 @@ auth_server_connection_new(struct auth_client *client, const char *path) { struct auth_server_connection *conn; - struct auth_client_handshake_request handshake; pool_t pool; int fd; @@ -192,21 +199,19 @@ conn->ext_input_io = client->ext_input_add(fd, auth_client_input, conn); } - conn->input = i_stream_create_file(fd, default_pool, MAX_INBUF_SIZE, - FALSE); + conn->input = i_stream_create_file(fd, default_pool, + AUTH_CLIENT_MAX_LINE_LENGTH, FALSE); conn->output = o_stream_create_file(fd, default_pool, (size_t)-1, FALSE); conn->requests = hash_create(default_pool, pool, 100, NULL, NULL); + conn->auth_mechs_buf = buffer_create_dynamic(default_pool, 256); conn->next = client->connections; client->connections = conn; - /* send our handshake */ - memset(&handshake, 0, sizeof(handshake)); - handshake.client_pid = client->pid; - client->conn_waiting_handshake_count++; - if (o_stream_send(conn->output, &handshake, sizeof(handshake)) < 0) { + if (o_stream_send_str(conn->output, + t_strdup_printf("CPID\t%u\n", client->pid)) < 0) { errno = conn->output->stream_errno; i_warning("Error sending handshake to auth server: %m"); auth_server_connection_destroy(conn, TRUE); @@ -252,7 +257,7 @@ conn->fd = -1; auth_server_requests_remove_all(conn); - auth_server_connection_unref(conn); + auth_server_connection_unref(conn); if (reconnect) auth_client_connect_missing_servers(client); @@ -270,6 +275,7 @@ i_assert(conn->refcount == 0); hash_destroy(conn->requests); + buffer_free(conn->auth_mechs_buf); i_stream_unref(conn->input); o_stream_unref(conn->output);
--- a/src/lib-auth/auth-server-connection.h Wed Oct 13 15:32:54 2004 +0300 +++ b/src/lib-auth/auth-server-connection.h Wed Oct 13 19:38:32 2004 +0300 @@ -40,14 +40,13 @@ unsigned int server_pid; unsigned int connect_uid; + buffer_t *auth_mechs_buf; 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; };
--- a/src/lib-auth/auth-server-request.c Wed Oct 13 15:32:54 2004 +0300 +++ b/src/lib-auth/auth-server-request.c Wed Oct 13 19:38:32 2004 +0300 @@ -1,31 +1,31 @@ -/* Copyright (C) 2003 Timo Sirainen */ +/* Copyright (C) 2003-2004 Timo Sirainen */ #include "lib.h" -#include "buffer.h" +#include "str.h" #include "hash.h" #include "ostream.h" #include "auth-client.h" #include "auth-server-connection.h" #include "auth-server-request.h" +#include <stdlib.h> + struct auth_request { struct auth_server_connection *conn; unsigned int id; char *mech, *protocol; - enum auth_client_request_new_flags flags; + enum auth_request_flags flags; struct ip_addr local_ip, remote_ip; - unsigned char *initial_resp_data; - size_t initial_resp_size; + char *initial_resp_base64; auth_request_callback_t *callback; void *context; struct auth_server_connection *next_conn; - unsigned char *plaintext_data; /* for resending to other servers */ - size_t plaintext_data_size; + char *plaintext_data; /* for resending to other servers */ unsigned int init_sent:1; unsigned int retrying:1; @@ -33,6 +33,7 @@ static int auth_server_send_new_request(struct auth_server_connection *conn, struct auth_request *request); +static void auth_client_request_free(struct auth_request *request); static struct auth_server_connection * get_next_plain_server(struct auth_server_connection *conn) @@ -47,8 +48,7 @@ } static void -auth_server_request_check_retry(struct auth_request *request, - const unsigned char *data, size_t data_size) +auth_server_request_check_retry(struct auth_request *request, const char *data) { if (strcmp(request->mech, "PLAIN") == 0 && request->plaintext_data == NULL && request->conn != NULL) { @@ -56,9 +56,7 @@ if (request->next_conn != NULL) { /* plaintext authentication - save the data so we can try it for the next */ - request->plaintext_data = i_malloc(data_size); - memcpy(request->plaintext_data, data, data_size); - request->plaintext_data_size = data_size; + request->plaintext_data = i_strdup(data); hash_insert(request->next_conn->requests, POINTER_CAST(request->id), request); @@ -72,51 +70,28 @@ 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; - size_t size; + string_t *str; ssize_t ret; - memset(&auth_request, 0, sizeof(auth_request)); - auth_request.type = AUTH_CLIENT_REQUEST_NEW; - auth_request.id = request->id; - auth_request.flags = request->flags; - - if (request->local_ip.family == request->remote_ip.family) - auth_request.ip_family = request->local_ip.family; - t_push(); - buf = buffer_create_dynamic(pool_datastack_create(), 256); - buffer_set_used_size(buf, sizeof(auth_request)); + str = t_str_new(512); - if (auth_request.ip_family != 0) { - size = IPADDR_IS_V4(&request->local_ip) ? 4 : - sizeof(request->local_ip.ip); - buffer_append(buf, &request->local_ip.ip, size); - buffer_append(buf, &request->remote_ip.ip, size); - } - - auth_request.mech_idx = - buffer_get_used_size(buf) - sizeof(auth_request); - buffer_append(buf, request->mech, strlen(request->mech)+1); + str_printfa(str, "AUTH\t%u\t%s\tproto=%s", + request->id, request->mech, request->protocol); + if ((request->flags & AUTH_REQUEST_FLAG_SECURED) != 0) + str_append(str, "\tsecured"); + if ((request->flags & AUTH_REQUEST_FLAG_VALID_CLIENT_CERT) != 0) + str_append(str, "\tvalid-client-cert"); - 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); + if (request->local_ip.family != 0) + str_printfa(str, "\tlip=%s", net_ip2addr(&request->local_ip)); + if (request->remote_ip.family != 0) + str_printfa(str, "\trip=%s", net_ip2addr(&request->remote_ip)); + if (request->initial_resp_base64 != NULL) + str_printfa(str, "\tresp=%s", request->initial_resp_base64); + str_append_c(str, '\n'); - 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)); + ret = o_stream_send(conn->output, str_data(str), str_len(str)); t_pop(); if (ret < 0) { @@ -126,63 +101,123 @@ return FALSE; } - auth_server_request_check_retry(request, request->initial_resp_data, - request->initial_resp_size); + auth_server_request_check_retry(request, request->initial_resp_base64); return TRUE; } static void auth_server_send_continue(struct auth_server_connection *conn, struct auth_request *request, - const unsigned char *data, size_t size) + const char *data_base64) { - struct auth_client_request_continue auth_request; - struct const_iovec iov[2]; + struct const_iovec iov[3]; + const char *prefix; + + prefix = t_strdup_printf("CONT\t%u\t", request->id); - /* send continued request to auth */ - auth_request.type = AUTH_CLIENT_REQUEST_CONTINUE; - auth_request.id = request->id; - auth_request.data_size = size; + iov[0].iov_base = prefix; + iov[0].iov_len = strlen(prefix); + iov[1].iov_base = data_base64; + iov[1].iov_len = strlen(data_base64); + iov[2].iov_base = "\n"; + iov[2].iov_len = 1; - iov[0].iov_base = &auth_request; - iov[0].iov_len = sizeof(auth_request); - iov[1].iov_base = data; - iov[1].iov_len = size; - - if (o_stream_sendv(conn->output, iov, 2) < 0) { + if (o_stream_sendv(conn->output, iov, 3) < 0) { errno = conn->output->stream_errno; i_warning("Error sending continue request to auth server: %m"); auth_server_connection_destroy(conn, TRUE); } } -void auth_server_request_handle_reply(struct auth_server_connection *conn, - struct auth_client_request_reply *reply, - const unsigned char *data) +int auth_client_input_ok(struct auth_server_connection *conn, const char *args) +{ + const char *const *list, *const *args_list, *data_base64; + struct auth_request *request; + unsigned int id; + + list = t_strsplit(args, "\t"); + if (list[0] == NULL) { + i_error("BUG: Authentication server sent broken OK line"); + return FALSE; + } + + id = (unsigned int)strtoul(list[0], NULL, 10); + + request = hash_lookup(conn->requests, POINTER_CAST(id)); + if (request == NULL) { + /* We've already destroyed the request */ + return TRUE; + } + + hash_remove(request->conn->requests, POINTER_CAST(id)); + if (request->next_conn != NULL) + hash_remove(request->next_conn->requests, POINTER_CAST(id)); + request->conn = conn; + request->next_conn = NULL; + + data_base64 = NULL; + for (args_list = ++list; *list != NULL; list++) { + if (strncmp(*list, "resp=", 5) == 0) { + data_base64 = *list + 5; + break; + } + } + + request->callback(request, 1, data_base64, args_list, request->context); + auth_client_request_free(request); + return TRUE; +} + +int auth_client_input_cont(struct auth_server_connection *conn, + const char *args) +{ + struct auth_request *request; + const char *data; + unsigned int id; + + data = strchr(args, '\t'); + if (data++ == NULL) { + i_error("BUG: Authentication server sent broken CONT line"); + return FALSE; + } + + id = (unsigned int)strtoul(args, NULL, 10); + + request = hash_lookup(conn->requests, POINTER_CAST(id)); + if (request == NULL) { + /* We've already destroyed the request */ + return TRUE; + } + + if (request->retrying) { + auth_server_send_continue(conn, request, + request->plaintext_data); + } + request->callback(request, 0, data, NULL, request->context); + return TRUE; +} + +int auth_client_input_fail(struct auth_server_connection *conn, + const char *args) { struct auth_request *request; struct auth_server_connection *next; + const char *error; + unsigned int id; - request = hash_lookup(conn->requests, POINTER_CAST(reply->id)); + error = strchr(args, '\t'); + if (error != NULL) + error++; + + id = (unsigned int)strtoul(args, NULL, 10); + + request = hash_lookup(conn->requests, POINTER_CAST(id)); if (request == NULL) { /* We've already destroyed the request */ - return; + return TRUE; } - switch (reply->result) { - case AUTH_CLIENT_RESULT_SUCCESS: - hash_remove(request->conn->requests, POINTER_CAST(request->id)); - if (request->next_conn != NULL) { - hash_remove(request->next_conn->requests, - POINTER_CAST(request->id)); - } - request->conn = conn; - request->next_conn = NULL; - break; - case AUTH_CLIENT_RESULT_FAILURE: - hash_remove(conn->requests, POINTER_CAST(request->id)); - if (!request->retrying) - break; - + hash_remove(conn->requests, POINTER_CAST(request->id)); + if (request->retrying) { next = request->next_conn == NULL ? NULL : get_next_plain_server(request->next_conn); @@ -193,33 +228,22 @@ if (next == NULL) { if (request->conn != NULL) { /* the other one hasn't replied yet */ - return; + return TRUE; } request->conn = conn; - break; - } - - hash_insert(next->requests, POINTER_CAST(request->id), request); - request->next_conn = next; + } else { + hash_insert(next->requests, POINTER_CAST(request->id), + request); + request->next_conn = next; - auth_server_send_new_request(next, request); - return; - case AUTH_CLIENT_RESULT_CONTINUE: - if (!request->retrying) - break; - - auth_server_send_continue(conn, request, - request->plaintext_data, - request->plaintext_data_size); - return; + auth_server_send_new_request(next, request); + return TRUE; + } } - request->callback(request, reply, data, request->context); - - if (reply->result != AUTH_CLIENT_RESULT_CONTINUE) { - i_free(request->plaintext_data); - i_free(request); - } + request->callback(request, -1, error, NULL, request->context); + auth_client_request_free(request); + return TRUE; } static void request_hash_remove(struct auth_server_connection *conn, @@ -227,7 +251,7 @@ { if (request->conn == conn) { if (request->next_conn == NULL) { - request->callback(request, NULL, NULL, + request->callback(request, -1, NULL, NULL, request->context); request->conn = NULL; } else { @@ -285,13 +309,9 @@ request->remote_ip = request_info->remote_ip; request->id = ++client->request_id_counter; - if (request_info->initial_resp_size != 0) { - request->initial_resp_size = request_info->initial_resp_size; - request->initial_resp_data = - i_malloc(request_info->initial_resp_size); - memcpy(request->initial_resp_data, - request_info->initial_resp_data, - request_info->initial_resp_size); + if (request_info->initial_resp_base64 != NULL) { + request->initial_resp_base64 = + i_strdup(request_info->initial_resp_base64); } if (request->id == 0) { @@ -309,11 +329,19 @@ } void auth_client_request_continue(struct auth_request *request, - const unsigned char *data, size_t data_size) + const char *data_base64) { - auth_server_send_continue(request->conn, request, data, data_size); + auth_server_send_continue(request->conn, request, data_base64); + auth_server_request_check_retry(request, data_base64); +} - auth_server_request_check_retry(request, data, data_size); +static void auth_client_request_free(struct auth_request *request) +{ + i_free(request->initial_resp_base64); + i_free(request->plaintext_data); + i_free(request->mech); + i_free(request->protocol); + i_free(request); } void auth_client_request_abort(struct auth_request *request) @@ -324,13 +352,8 @@ if (request->next_conn != NULL) hash_remove(request->next_conn->requests, id); - 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); + request->callback(request, -1, NULL, NULL, request->context); + auth_client_request_free(request); } unsigned int auth_client_request_get_id(struct auth_request *request)
--- a/src/lib-auth/auth-server-request.h Wed Oct 13 15:32:54 2004 +0300 +++ b/src/lib-auth/auth-server-request.h Wed Oct 13 19:38:32 2004 +0300 @@ -1,9 +1,11 @@ #ifndef __AUTH_SERVER_REQUEST_H #define __AUTH_SERVER_REQUEST_H -void auth_server_request_handle_reply(struct auth_server_connection *conn, - struct auth_client_request_reply *reply, - const unsigned char *data); +int auth_client_input_ok(struct auth_server_connection *conn, const char *args); +int auth_client_input_cont(struct auth_server_connection *conn, + const char *args); +int auth_client_input_fail(struct auth_server_connection *conn, + const char *args); void auth_server_requests_remove_all(struct auth_server_connection *conn);
--- a/src/login-common/sasl-server.c Wed Oct 13 15:32:54 2004 +0300 +++ b/src/login-common/sasl-server.c Wed Oct 13 19:38:32 2004 +0300 @@ -12,16 +12,16 @@ /* Used only for string sanitization while verbose_auth is set. */ #define MAX_MECH_NAME 64 -static enum auth_client_request_new_flags +static enum auth_request_flags client_get_auth_flags(struct client *client) { - enum auth_client_request_new_flags auth_flags = 0; + enum auth_request_flags auth_flags = 0; if (client->proxy != NULL && ssl_proxy_has_valid_client_cert(client->proxy)) - auth_flags |= AUTH_CLIENT_FLAG_SSL_VALID_CLIENT_CERT; - if (client->tls) - auth_flags |= AUTH_CLIENT_FLAG_SSL_ENABLED; + auth_flags |= AUTH_REQUEST_FLAG_VALID_CLIENT_CERT; + if (client->secured) + auth_flags |= AUTH_REQUEST_FLAG_SECURED; return auth_flags; } @@ -35,43 +35,22 @@ SASL_SERVER_REPLY_MASTER_FAILED, NULL); } -static const char *auth_client_get_str(struct auth_client_request_reply *reply, - const unsigned char *data, size_t idx) -{ - size_t stop; - - if (idx >= reply->data_size || idx >= reply->reply_idx) - return NULL; - - stop = reply->reply_idx < reply->data_size ? - reply->reply_idx-1 : reply->data_size; - - return t_strndup(data + idx, stop); -} - -static void authenticate_callback(struct auth_request *request, - struct auth_client_request_reply *reply, - const unsigned char *data, void *context) +static void authenticate_callback(struct auth_request *request, int status, + const char *data_base64, + const char *const *args, void *context) { struct client *client = context; - buffer_t *buf; - const char *user, *error; + const char *error; if (!client->authenticating) { /* client aborted */ - i_assert(reply == NULL); + i_assert(status < 0); return; } - if (reply == NULL) { - /* failed */ - client->auth_request = NULL; - sasl_server_auth_cancel(client, "Authentication process died."); - return; - } - - switch (reply->result) { - case AUTH_CLIENT_RESULT_CONTINUE: + switch (status) { + case 0: + /* continue */ if (client->auth_request != NULL) { i_assert(client->auth_request == request); } else { @@ -80,34 +59,30 @@ client->auth_request = request; } - t_push(); - buf = buffer_create_dynamic(pool_datastack_create(), - MAX_BASE64_ENCODED_SIZE(reply->data_size)); - base64_encode(data, reply->data_size, buf); - buffer_append_c(buf, '\0'); - client->sasl_callback(client, SASL_SERVER_REPLY_CONTINUE, - buf->data); - t_pop(); + data_base64); break; - case AUTH_CLIENT_RESULT_SUCCESS: + case 1: client->auth_request = NULL; - user = auth_client_get_str(reply, data, reply->username_idx); - i_free(client->virtual_user); - client->virtual_user = i_strdup(user); + for (; *args != NULL; args++) { + if (strncmp(*args, "user=", 5) == 0) { + i_free(client->virtual_user); + client->virtual_user = i_strdup(*args + 5); + } + } master_request_login(client, master_callback, auth_client_request_get_server_pid(request), auth_client_request_get_id(request)); break; - case AUTH_CLIENT_RESULT_FAILURE: + case -1: client->auth_request = NULL; /* see if we have error message */ - if (reply->data_size > 0 && data[reply->data_size-1] == '\0') { + if (data_base64 != NULL) { error = t_strconcat("Authentication failed: ", - (const char *)data, NULL); + (const char *)data_base64, NULL); } else { error = NULL; } @@ -118,8 +93,7 @@ void sasl_server_auth_begin(struct client *client, const char *protocol, const char *mech_name, - const unsigned char *initial_resp, - size_t initial_resp_size, + const char *initial_resp_base64, sasl_server_callback_t *callback) { struct auth_request_info info; @@ -137,7 +111,8 @@ return; } - if (!client->secured && mech->plaintext && disable_plaintext_auth) { + if (!client->secured && disable_plaintext_auth && + (mech->flags & MECH_SEC_PLAINTEXT) != 0) { sasl_server_auth_cancel(client, "Plaintext authentication disabled."); return; @@ -149,8 +124,7 @@ info.flags = client_get_auth_flags(client); info.local_ip = client->local_ip; info.remote_ip = client->ip; - info.initial_resp_data = initial_resp; - info.initial_resp_size = initial_resp_size; + info.initial_resp_base64 = initial_resp_base64; client->auth_request = auth_client_request_new(auth_client, NULL, &info,
--- a/src/login-common/sasl-server.h Wed Oct 13 15:32:54 2004 +0300 +++ b/src/login-common/sasl-server.h Wed Oct 13 19:38:32 2004 +0300 @@ -14,8 +14,7 @@ void sasl_server_auth_begin(struct client *client, const char *protocol, const char *mech_name, - const unsigned char *initial_resp, - size_t initial_resp_size, + const char *initial_resp_base64, sasl_server_callback_t *callback); void sasl_server_auth_cancel(struct client *client, const char *reason);
--- a/src/master/auth-process.c Wed Oct 13 15:32:54 2004 +0300 +++ b/src/master/auth-process.c Wed Oct 13 19:38:32 2004 +0300 @@ -7,6 +7,7 @@ #include "network.h" #include "istream.h" #include "ostream.h" +#include "str.h" #include "restrict-access.h" #include "restrict-process-size.h" #include "auth-process.h" @@ -18,8 +19,8 @@ #include <syslog.h> #include <sys/stat.h> -#define MAX_INBUF_SIZE \ - (sizeof(struct auth_master_reply) + AUTH_MASTER_MAX_REPLY_DATA_SIZE) +#define MAX_INBUF_SIZE 8192 +#define MAX_OUTBUF_SIZE 65536 struct auth_process_group { struct auth_process_group *next; @@ -41,8 +42,6 @@ struct istream *input; struct ostream *output; - struct auth_master_reply auth_reply; - struct hash_table *requests; unsigned int external:1; @@ -56,64 +55,19 @@ static void auth_process_destroy(struct auth_process *p); -static int handle_reply(struct auth_process *process, - struct auth_master_reply *reply, - const unsigned char *data) -{ - size_t nul_pos; - void *context; - - context = hash_lookup(process->requests, POINTER_CAST(reply->tag)); - if (context == NULL) { - i_error("Auth process %s sent unrequested reply with tag %u", - dec2str(process->pid), reply->tag); - return TRUE; - } - - /* make sure the reply looks OK */ - if (reply->data_size == 0) { - nul_pos = 0; - data = (const unsigned char *) ""; - } else { - nul_pos = reply->data_size-1; - } - - if (data[nul_pos] != '\0') { - i_error("Auth process %s sent invalid reply", - dec2str(process->pid)); - return FALSE; - } - - /* fix the request so that all the values point to \0 terminated - strings */ - if (reply->system_user_idx >= reply->data_size) - reply->system_user_idx = nul_pos; - if (reply->virtual_user_idx >= reply->data_size) - reply->virtual_user_idx = nul_pos; - if (reply->home_idx >= reply->data_size) - reply->home_idx = nul_pos; - if (reply->chroot_idx >= reply->data_size) - reply->chroot_idx = nul_pos; - if (reply->mail_idx >= reply->data_size) - reply->mail_idx = nul_pos; - - auth_master_callback(reply, data, context); - hash_remove(process->requests, POINTER_CAST(reply->tag)); - return TRUE; -} - void auth_process_request(struct auth_process *process, unsigned int login_pid, unsigned int login_id, void *context) { - struct auth_master_request req; + string_t *str; ssize_t ret; - req.tag = ++auth_tag; - req.id = login_id; - req.client_pid = login_pid; + t_push(); + str = t_str_new(256); + str_printfa(str, "REQUEST\t%u\t%u\t%u\n", + ++auth_tag, login_pid, login_id); - ret = o_stream_send(process->output, &req, sizeof(req)); - if ((size_t)ret != sizeof(req)) { + ret = o_stream_send(process->output, str_data(str), str_len(str)); + if (ret != (ssize_t)str_len(str)) { if (ret >= 0) { /* FIXME: well .. I'm not sure if it'd be better to just block here. I don't think this condition should @@ -125,76 +79,147 @@ "killing..", dec2str(process->pid)); } auth_process_destroy(process); - return; + } else { + hash_insert(process->requests, POINTER_CAST(auth_tag), context); + } + t_pop(); +} + +static int +auth_process_input_user(struct auth_process *process, const char *args) +{ + void *context; + const char *const *list; + unsigned int id; + + /* <id> <userid> [..] */ + + list = t_strsplit(args, "\t"); + if (list[0] == NULL || list[1] == NULL) { + i_error("BUG: Auth process %s sent corrupted USER line", + dec2str(process->pid)); + return FALSE; + } + id = (unsigned int)strtoul(list[0], NULL, 10); + + context = hash_lookup(process->requests, POINTER_CAST(id)); + if (context == NULL) { + i_error("BUG: Auth process %s sent unrequested reply with ID " + "%u", dec2str(process->pid), id); + return FALSE; + } + + auth_master_callback(list[1], list + 2, context); + hash_remove(process->requests, POINTER_CAST(id)); + return TRUE; +} + +static int +auth_process_input_notfound(struct auth_process *process, const char *args) +{ + void *context; + unsigned int id; + + id = (unsigned int)strtoul(args, NULL, 10); + + context = hash_lookup(process->requests, POINTER_CAST(id)); + if (context == NULL) { + i_error("BUG: Auth process %s sent unrequested reply with ID " + "%u", dec2str(process->pid), id); + return FALSE; } - hash_insert(process->requests, POINTER_CAST(req.tag), context); + auth_master_callback(NULL, NULL, context); + hash_remove(process->requests, POINTER_CAST(id)); + return TRUE; +} + +static int +auth_process_input_spid(struct auth_process *process, const char *args) +{ + unsigned int pid; + + if (process->initialized) { + i_error("BUG: Authentication server re-handshaking"); + return FALSE; + } + + pid = (unsigned int)strtoul(args, NULL, 10); + if (pid == 0) { + i_error("BUG: Authentication server said it's PID 0"); + return FALSE; + } + + process->pid = pid; + process->initialized = TRUE; + return TRUE; +} + +static int +auth_process_input_fail(struct auth_process *process, const char *args) +{ + void *context; + const char *error; + unsigned int id; + + error = strchr(args, '\t'); + if (error != NULL) + error++; + + id = (unsigned int)strtoul(args, NULL, 10); + + context = hash_lookup(process->requests, POINTER_CAST(id)); + if (context == NULL) { + i_error("BUG: Auth process %s sent unrequested reply with ID " + "%u", dec2str(process->pid), id); + return FALSE; + } + + auth_master_callback(NULL, NULL, context); + hash_remove(process->requests, POINTER_CAST(id)); + return TRUE; } static void auth_process_input(void *context) { - struct auth_process *p = context; - const unsigned char *data; - size_t size; + struct auth_process *process = context; + const char *line; + int ret; - switch (i_stream_read(p->input)) { + switch (i_stream_read(process->input)) { case 0: return; case -1: /* disconnected */ - auth_process_destroy(p); + auth_process_destroy(process); return; case -2: /* buffer full */ i_error("BUG: Auth process %s sent us more than %d " - "bytes of data", dec2str(p->pid), (int)MAX_INBUF_SIZE); - auth_process_destroy(p); + "bytes of data", dec2str(process->pid), + (int)MAX_INBUF_SIZE); + auth_process_destroy(process); return; } - if (!p->initialized) { - struct auth_master_handshake_reply rec; - - data = i_stream_get_data(p->input, &size); - if (size < sizeof(rec)) - return; - - memcpy(&rec, data, sizeof(rec)); - i_stream_skip(p->input, sizeof(rec)); - - if (rec.server_pid == 0) { - i_fatal("Auth process sent invalid initialization " - "notification"); - } - - p->pid = rec.server_pid; - p->initialized = TRUE; - } + while ((line = i_stream_next_line(process->input)) != NULL) { + t_push(); + if (strncmp(line, "USER\t", 5) == 0) + ret = auth_process_input_user(process, line + 5); + else if (strncmp(line, "NOTFOUND\t", 9) == 0) + ret = auth_process_input_notfound(process, line + 9); + else if (strncmp(line, "FAIL\t", 5) == 0) + ret = auth_process_input_fail(process, line + 5); + else if (strncmp(line, "SPID\t", 5) == 0) + ret = auth_process_input_spid(process, line + 5); + else + ret = TRUE; - for (;;) { - if (!p->in_auth_reply) { - data = i_stream_get_data(p->input, &size); - if (size < sizeof(p->auth_reply)) - break; - - p->in_auth_reply = TRUE; - memcpy(&p->auth_reply, data, sizeof(p->auth_reply)); - - i_stream_skip(p->input, sizeof(p->auth_reply)); - } - - data = i_stream_get_data(p->input, &size); - if (size < p->auth_reply.data_size) - break; - - /* reply is now read */ - if (!handle_reply(p, &p->auth_reply, data)) { - auth_process_destroy(p); + if (!ret) { + auth_process_destroy(process); break; } - - p->in_auth_reply = FALSE; - i_stream_skip(p->input, p->auth_reply.data_size); + t_pop(); } } @@ -213,8 +238,7 @@ p->io = io_add(fd, IO_READ, auth_process_input, p); p->input = i_stream_create_file(fd, default_pool, MAX_INBUF_SIZE, FALSE); - p->output = o_stream_create_file(fd, default_pool, - sizeof(struct auth_master_request)*100, + p->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE, FALSE); p->requests = hash_create(default_pool, default_pool, 0, NULL, NULL);
--- a/src/master/auth-process.h Wed Oct 13 15:32:54 2004 +0300 +++ b/src/master/auth-process.h Wed Oct 13 19:38:32 2004 +0300 @@ -1,8 +1,8 @@ #ifndef __AUTH_PROCESS_H #define __AUTH_PROCESS_H -void auth_master_callback(struct auth_master_reply *reply, - const unsigned char *data, void *context); +void auth_master_callback(const char *user, const char *const *args, + void *context); /* Find process for given id */ struct auth_process *auth_process_find(unsigned int pid);
--- a/src/master/common.h Wed Oct 13 15:32:54 2004 +0300 +++ b/src/master/common.h Wed Oct 13 19:38:32 2004 +0300 @@ -7,8 +7,6 @@ #include "hash.h" #include "master-settings.h" -#include "../auth/auth-master-interface.h" - enum process_type { PROCESS_TYPE_UNKNOWN, PROCESS_TYPE_AUTH,
--- a/src/master/login-process.c Wed Oct 13 15:32:54 2004 +0300 +++ b/src/master/login-process.c Wed Oct 13 19:38:32 2004 +0300 @@ -71,14 +71,14 @@ i_free(group); } -void auth_master_callback(struct auth_master_reply *reply, - const unsigned char *data, void *context) +void auth_master_callback(const char *user, const char *const *args, + void *context) { struct login_auth_request *request = context; struct master_login_reply master_reply; ssize_t ret; - if (reply == NULL || !reply->success) + if (user == NULL) master_reply.success = FALSE; else { struct login_group *group = request->process->group; @@ -87,8 +87,7 @@ master_reply.success = create_mail_process(group, request->fd, &request->local_ip, - &request->remote_ip, - reply, (const char *) data); + &request->remote_ip, user, args); t_pop(); }
--- a/src/master/mail-process.c Wed Oct 13 15:32:54 2004 +0300 +++ b/src/master/mail-process.c Wed Oct 13 19:38:32 2004 +0300 @@ -312,15 +312,18 @@ int create_mail_process(struct login_group *group, int socket, const struct ip_addr *local_ip, const struct ip_addr *remote_ip, - struct auth_master_reply *reply, const char *data) + const char *user, const char *const *args) { struct settings *set = group->set; const struct var_expand_table *var_expand_table; - const char *addr, *mail, *user, *chroot_dir, *home_dir, *full_home_dir; + const char *addr, *mail, *chroot_dir, *home_dir, *full_home_dir; + const char *system_user; char title[1024]; struct log_io *log; string_t *str; pid_t pid; + uid_t uid; + gid_t gid; int i, err, ret, log_fd; // FIXME: per-group @@ -329,14 +332,25 @@ return FALSE; } - if (!validate_uid_gid(set, reply->uid, reply->gid, - data + reply->virtual_user_idx)) - return FALSE; + mail = home_dir = chroot_dir = system_user = ""; + uid = gid = 0; + for (; *args != NULL; args++) { + if (strncmp(*args, "home=", 5) == 0) + home_dir = *args + 5; + else if (strncmp(*args, "mail=", 5) == 0) + mail = *args + 5; + else if (strncmp(*args, "chroot=", 7) == 0) + chroot_dir = *args + 7; + else if (strncmp(*args, "system_user=", 12) == 0) + system_user = *args + 12; + else if (strncmp(*args, "uid=", 4) == 0) + uid = (uid_t)strtoul(*args + 4, NULL, 10); + else if (strncmp(*args, "gid=", 4) == 0) + gid = (gid_t)strtoul(*args + 4, NULL, 10); + } - user = data + reply->virtual_user_idx; - mail = data + reply->mail_idx; - home_dir = data + reply->home_idx; - chroot_dir = data + reply->chroot_idx; + if (!validate_uid_gid(set, uid, gid, user)) + return FALSE; if (*chroot_dir == '\0' && set->mail_chroot != NULL) chroot_dir = set->mail_chroot; @@ -395,8 +409,7 @@ /* setup environment - set the most important environment first (paranoia about filling up environment without noticing) */ - restrict_access_set_env(data + reply->system_user_idx, - reply->uid, reply->gid, chroot_dir, + restrict_access_set_env(system_user, uid, gid, chroot_dir, set->first_valid_gid, set->last_valid_gid, set->mail_extra_groups); @@ -410,10 +423,10 @@ /* NOTE: if home directory is NFS-mounted, we might not have access to it as root. Change the effective UID temporarily to make it work. */ - if (reply->uid != master_uid && seteuid(reply->uid) < 0) - i_fatal("seteuid(%s) failed: %m", dec2str(reply->uid)); + if (uid != master_uid && seteuid(uid) < 0) + i_fatal("seteuid(%s) failed: %m", dec2str(uid)); ret = chdir(full_home_dir); - if (reply->uid != master_uid && seteuid(master_uid) < 0) + if (uid != master_uid && seteuid(master_uid) < 0) i_fatal("seteuid(%s) failed: %m", dec2str(master_uid)); /* If user's home directory doesn't exist and we're not @@ -421,7 +434,7 @@ could be stored elsewhere. */ if (ret < 0 && (errno != ENOENT || *chroot_dir != '\0')) { i_fatal("chdir(%s) failed with uid %s: %m", - full_home_dir, dec2str(reply->uid)); + full_home_dir, dec2str(uid)); } } if (ret < 0) { @@ -435,7 +448,7 @@ env_put("LOGGED_IN=1"); env_put(t_strconcat("HOME=", home_dir, NULL)); - env_put(t_strconcat("USER=", data + reply->virtual_user_idx, NULL)); + env_put(t_strconcat("USER=", user, NULL)); addr = net_ip2addr(remote_ip); env_put(t_strconcat("IP=", addr, NULL)); @@ -446,8 +459,7 @@ if (addr == NULL) addr = "??"; - i_snprintf(title, sizeof(title), "[%s %s]", - data + reply->virtual_user_idx, addr); + i_snprintf(title, sizeof(title), "[%s %s]", user, addr); } /* make sure we don't leak syslog fd, but do it last so that
--- a/src/master/mail-process.h Wed Oct 13 15:32:54 2004 +0300 +++ b/src/master/mail-process.h Wed Oct 13 19:38:32 2004 +0300 @@ -9,7 +9,7 @@ int create_mail_process(struct login_group *group, int socket, const struct ip_addr *local_ip, const struct ip_addr *remote_ip, - struct auth_master_reply *reply, const char *data); + const char *user, const char *const *args); void mail_process_destroyed(pid_t pid);
--- a/src/pop3-login/client-authenticate.c Wed Oct 13 15:32:54 2004 +0300 +++ b/src/pop3-login/client-authenticate.c Wed Oct 13 19:38:32 2004 +0300 @@ -30,10 +30,10 @@ /* 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)) { + if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 && + (client->secured || disable_plaintext_auth || + (mech[i].flags & MECH_SEC_PLAINTEXT) == 0)) { str_append_c(str, ' '); str_append(str, mech[i].name); } @@ -50,9 +50,7 @@ static void client_auth_input(void *context) { struct pop3_client *client = context; - buffer_t *buf; char *line; - size_t linelen, bufsize; if (!client_read(client)) return; @@ -68,25 +66,15 @@ return; } - linelen = strlen(line); - buf = buffer_create_static_hard(pool_datastack_create(), linelen); - - if (base64_decode(line, linelen, NULL, buf) < 0) { - /* failed */ - sasl_server_auth_cancel(&client->common, "Invalid base64 data"); - } else if (client->common.auth_request == NULL) { + if (client->common.auth_request == NULL) { sasl_server_auth_cancel(&client->common, "Don't send unrequested data"); } else { - auth_client_request_continue(client->common.auth_request, - buf->data, buf->used); + auth_client_request_continue(client->common.auth_request, line); } /* clear sensitive data */ - safe_memset(line, 0, linelen); - - bufsize = buffer_get_used_size(buf); - safe_memset(buffer_free_without_data(buf), 0, bufsize); + safe_memset(line, 0, strlen(line)); } static void sasl_callback(struct client *_client, enum sasl_server_reply reply, @@ -112,7 +100,8 @@ } /* get back to normal client input. */ - io_remove(client->io); + if (client->io != NULL) + io_remove(client->io); client->io = io_add(client->common.fd, IO_READ, client_input, client); break; @@ -149,8 +138,6 @@ { const struct auth_mech_desc *mech; const char *mech_name, *p; - string_t *buf; - size_t argslen; if (*args == '\0') { /* Old-style SASL discovery, used by MS Outlook */ @@ -158,9 +145,10 @@ client_send_line(client, "+OK"); mech = auth_client_get_available_mechs(auth_client, &count); for (i = 0; i < count; i++) { - if (mech[i].advertise) { + if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 && + (client->secured || disable_plaintext_auth || + (mech[i].flags & MECH_SEC_PLAINTEXT) == 0)) client_send_line(client, mech[i].name); - } } client_send_line(client, "."); return TRUE; @@ -176,18 +164,9 @@ args = p+1; } - argslen = strlen(args); - buf = buffer_create_static_hard(pool_datastack_create(), argslen); - - if (base64_decode(args, argslen, NULL, buf) < 0) { - /* failed */ - client_send_line(client, "-ERR Invalid base64 data."); - return TRUE; - } - client_ref(client); sasl_server_auth_begin(&client->common, "POP3", mech_name, - buf->data, buf->used, sasl_callback); + args, sasl_callback); if (!client->common.authenticating) return TRUE; @@ -220,7 +199,7 @@ int cmd_pass(struct pop3_client *client, const char *args) { - string_t *plain_login; + string_t *plain_login, *base64; if (client->last_user == NULL) { client_send_line(client, "-ERR No username given."); @@ -234,10 +213,13 @@ str_append_c(plain_login, '\0'); str_append(plain_login, args); + base64 = buffer_create_dynamic(pool_datastack_create(), + MAX_BASE64_ENCODED_SIZE(plain_login->used)); + base64_encode(plain_login->data, plain_login->used, base64); + client_ref(client); sasl_server_auth_begin(&client->common, "POP3", "PLAIN", - plain_login->data, plain_login->used, - sasl_callback); + str_c(base64), sasl_callback); if (!client->common.authenticating) return TRUE; @@ -251,7 +233,7 @@ int cmd_apop(struct pop3_client *client, const char *args) { - buffer_t *apop_data; + buffer_t *apop_data, *base64; const char *p; if (client->apop_challenge == NULL) { @@ -291,9 +273,13 @@ return TRUE; } + base64 = buffer_create_dynamic(pool_datastack_create(), + MAX_BASE64_ENCODED_SIZE(apop_data->used)); + base64_encode(apop_data->data, apop_data->used, base64); + client_ref(client); sasl_server_auth_begin(&client->common, "POP3", "APOP", - apop_data->data, apop_data->used, sasl_callback); + str_c(base64), sasl_callback); if (!client->common.authenticating) return TRUE;