dynamically. Added support for SASL initial response.
--HG--
branch : HEAD
auth-client-interface.h \
auth-master-connection.h \
auth-master-interface.h \
- auth-mech-desc.h \
auth-module.h \
db-ldap.h \
db-mysql.h \
structures, as it may not be aligned properly. */
memcpy(&type, data, sizeof(type));
- if (type == AUTH_CLIENT_REQUEST_NEW) {
+ conn->refcount++;
+ switch (type) {
+ case AUTH_CLIENT_REQUEST_NEW: {
struct auth_client_request_new request;
if (size < sizeof(request))
return;
memcpy(&request, data, sizeof(request));
- i_stream_skip(conn->input, sizeof(request));
+ if (size < sizeof(request) + request.data_size)
+ return;
/* we have a full init request */
conn->refcount++;
- mech_request_new(conn, &request, request_callback);
- } else if (type == AUTH_CLIENT_REQUEST_CONTINUE) {
+ mech_request_new(conn, &request, data + sizeof(request),
+ request_callback);
+ i_stream_skip(conn->input, sizeof(request) + request.data_size);
+ break;
+ }
+ case AUTH_CLIENT_REQUEST_CONTINUE: {
struct auth_client_request_continue request;
if (size < sizeof(request))
if (size < sizeof(request) + request.data_size)
return;
- i_stream_skip(conn->input, sizeof(request) + request.data_size);
-
/* we have a full continued request */
conn->refcount++;
mech_request_continue(conn, &request, data + sizeof(request),
/* clear any sensitive data from memory */
safe_memset(data + sizeof(request), 0, request.data_size);
- } else {
+ i_stream_skip(conn->input, sizeof(request) + request.data_size);
+ break;
+ }
+ default:
/* unknown request */
- i_error("BUG: Auth client %u sent us unknown request %u",
+ i_error("BUG: Auth client %u sent us unknown request type %u",
conn->pid, type);
auth_client_connection_destroy(conn);
}
+ auth_client_connection_unref(conn);
}
static void auth_client_input(void *context)
conn->next = master->clients;
master->clients = conn;
- if (o_stream_send(conn->output, &master->handshake_reply,
- sizeof(master->handshake_reply)) < 0) {
+ if (o_stream_send(conn->output, master->handshake_reply,
+ sizeof(*master->handshake_reply) +
+ master->handshake_reply->data_size) < 0) {
auth_client_connection_destroy(conn);
conn = NULL;
}
void auth_client_connections_init(struct auth_master_connection *master)
{
- master->handshake_reply.server_pid = master->pid;
- master->handshake_reply.auth_mechanisms = auth_mechanisms;
-
master->to_clients = timeout_add(5000, request_timeout, master);
}
/* max. size for auth_client_request_continue.data[] */
#define AUTH_CLIENT_MAX_REQUEST_DATA_SIZE 4096
-/* sizeof(struct auth_client_request_new->protocol) */
-#define AUTH_CLIENT_PROTOCOL_BUF_SIZE 12
-
/* Client process must finish with single authentication requests in this time,
or the whole connection will be killed. */
#define AUTH_REQUEST_TIMEOUT 120
-enum auth_mech {
- AUTH_MECH_PLAIN = 0x01,
- AUTH_MECH_DIGEST_MD5 = 0x02,
- AUTH_MECH_ANONYMOUS = 0x04,
- AUTH_MECH_CRAM_MD5 = 0x08,
-
- AUTH_MECH_COUNT
-};
-
enum auth_client_request_new_flags {
AUTH_CLIENT_FLAG_SSL_VALID_CLIENT_CERT = 0x01
};
unsigned int client_pid; /* unique identifier for client process */
};
+struct auth_client_handshake_mech_desc {
+ uint32_t name_idx;
+ unsigned int plaintext:1;
+ unsigned int advertise:1;
+};
+
/* Server -> Client */
struct auth_client_handshake_reply {
unsigned int server_pid; /* unique auth process identifier */
- enum auth_mech auth_mechanisms; /* valid authentication mechanisms */
+
+ uint32_t mech_count;
+ uint32_t data_size;
+ /* struct auth_client_handshake_mech_desc mech_desc[auth_mech_count]; */
};
/* New authentication request */
enum auth_client_request_type type; /* AUTH_CLIENT_REQUEST_NEW */
unsigned int id; /* unique ID for the request */
- enum auth_mech mech;
enum auth_client_request_new_flags flags;
- char protocol[AUTH_CLIENT_PROTOCOL_BUF_SIZE];
+
+ uint32_t protocol_idx;
+ uint32_t mech_idx;
+ uint32_t initial_resp_idx;
+
+ uint32_t data_size;
+ /* unsigned char data[]; */
};
+#define AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request) \
+ ((request)->initial_resp_idx != (request)->data_size)
/* Continue authentication request */
struct auth_client_request_continue {
enum auth_client_request_type type; /* AUTH_CLIENT_REQUEST_CONTINUE */
unsigned int id;
- size_t data_size;
+ uint32_t data_size;
/* unsigned char data[]; */
};
/* variable width data, indexes into data[].
Ignore if it points outside data_size. */
- size_t username_idx; /* NUL-terminated */
- size_t reply_idx; /* last, non-NUL terminated */
+ uint32_t username_idx; /* NUL-terminated */
+ uint32_t reply_idx; /* last, non-NUL terminated */
- size_t data_size;
+ uint32_t data_size;
/* unsigned char data[]; */
};
}
}
+static void master_get_handshake_reply(struct auth_master_connection *master)
+{
+ struct mech_module_list *list;
+ buffer_t *buf;
+ struct auth_client_handshake_reply reply;
+ struct auth_client_handshake_mech_desc mech_desc;
+ uint32_t mech_desc_offset;
+
+ memset(&reply, 0, sizeof(reply));
+ memset(&mech_desc, 0, sizeof(mech_desc));
+
+ reply.server_pid = master->pid;
+
+ buf = buffer_create_dynamic(default_pool, 128, (size_t)-1);
+
+ for (list = mech_modules; list != NULL; list = list->next)
+ reply.mech_count++;
+ buffer_set_used_size(buf, sizeof(reply) +
+ sizeof(mech_desc) * reply.mech_count);
+
+ mech_desc_offset = sizeof(reply);
+ for (list = mech_modules; list != NULL; list = list->next) {
+ mech_desc.name_idx = buffer_get_used_size(buf) - sizeof(reply);
+ mech_desc.plaintext = list->module.plaintext;
+ mech_desc.advertise = list->module.advertise;
+
+ memcpy(buffer_get_space_unsafe(buf, mech_desc_offset,
+ sizeof(mech_desc)),
+ &mech_desc, sizeof(mech_desc));
+ buffer_append(buf, list->module.mech_name,
+ strlen(list->module.mech_name) + 1);
+
+ mech_desc_offset += sizeof(mech_desc);
+ }
+
+ reply.data_size = buffer_get_used_size(buf);
+ memcpy(buffer_get_space_unsafe(buf, 0, sizeof(reply)),
+ &reply, sizeof(reply));
+
+ master->handshake_reply = buffer_free_without_data(buf);
+}
+
struct auth_master_connection *
auth_master_connection_new(int fd, unsigned int pid)
{
MAX_OUTBUF_SIZE, FALSE);
conn->io = io_add(fd, IO_READ, master_input, conn);
}
+ master_get_handshake_reply(conn);
return conn;
}
i_free(l[i]);
}
buffer_free(conn->listeners_buf);
+ conn->listeners_buf = NULL;
auth_master_connection_unref(conn);
}
if (conn->output != NULL)
o_stream_unref(conn->output);
+ i_free(conn->handshake_reply);
i_free(conn);
return FALSE;
}
l->master = conn;
l->fd = fd;
l->path = i_strdup(path);
- l->io = io_add(fd, IO_READ, auth_accept, &l);
+ l->io = io_add(fd, IO_READ, auth_accept, l);
buffer_append(conn->listeners_buf, &l, sizeof(l));
}
unsigned int request_pos;
unsigned char request_buf[sizeof(struct auth_master_request)];
- struct auth_client_handshake_reply handshake_reply;
+ struct auth_client_handshake_reply *handshake_reply;
struct auth_client_connection *clients;
struct timeout *to_clients;
/* variable width fields are packed into data[]. These variables
contain indexes to the data, they're all NUL-terminated.
Ignore if it points outside data_size. */
- size_t system_user_idx;
- size_t virtual_user_idx;
- size_t home_idx, mail_idx, chroot_idx;
+ uint32_t system_user_idx;
+ uint32_t virtual_user_idx;
+ uint32_t home_idx, mail_idx, chroot_idx;
- size_t data_size;
+ uint32_t data_size;
/* unsigned char data[]; */
};
+++ /dev/null
-#ifndef __AUTH_MECH_DESC_H
-#define __AUTH_MECH_DESC_H
-
-struct auth_mech_desc {
- enum auth_mech mech;
- const char *name;
- int plaintext;
- int advertise;
-};
-
-static struct auth_mech_desc auth_mech_desc[AUTH_MECH_COUNT] = {
- { AUTH_MECH_PLAIN, "PLAIN", TRUE, FALSE },
- { AUTH_MECH_CRAM_MD5, "CRAM-MD5", FALSE, TRUE },
- { AUTH_MECH_DIGEST_MD5, "DIGEST-MD5", FALSE, TRUE },
- { AUTH_MECH_ANONYMOUS, "ANONYMOUS", FALSE, TRUE }
-};
-
-#endif
env = getenv("AUTH_SOCKETS");
}
- if (env != NULL) {
+ if (env != NULL && *env != '\0') {
master = auth_master_connection_new(-1, 0);
master_add_unix_listeners(master, env);
auth_client_connections_init(master);
static int
mech_anonymous_auth_continue(struct auth_request *auth_request,
- struct auth_client_request_continue *request,
- const unsigned char *data,
+ const unsigned char *data, size_t data_size,
mech_callback_t *callback)
{
i_assert(anonymous_username != NULL);
if (verbose) {
i_info("mech-anonymous: login by %s",
- t_strndup(data, request->data_size));
+ t_strndup(data, data_size));
}
auth_request->callback = callback;
return TRUE;
}
+static int
+mech_anonymous_auth_initial(struct auth_request *auth_request,
+ struct auth_client_request_new *request,
+ const unsigned char *data,
+ mech_callback_t *callback)
+{
+ struct auth_client_request_reply reply;
+ size_t data_size;
+
+ if (AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request)) {
+ data += request->initial_resp_idx;
+ data_size = request->data_size - request->initial_resp_idx;
+
+ return auth_request->auth_continue(auth_request, data,
+ data_size, callback);
+ }
+
+ /* initialize reply */
+ memset(&reply, 0, sizeof(reply));
+ reply.id = auth_request->id;
+ reply.result = AUTH_CLIENT_RESULT_CONTINUE;
+
+ callback(&reply, NULL, auth_request->conn);
+ return TRUE;
+}
+
static void
mech_anonymous_auth_free(struct auth_request *auth_request)
{
pool_unref(auth_request->pool);
}
-static struct auth_request *
-mech_anonymous_auth_new(struct auth_client_connection *conn, unsigned int id,
- mech_callback_t *callback)
+static struct auth_request *mech_anonymous_auth_new(void)
{
struct auth_request *auth_request;
- struct auth_client_request_reply reply;
pool_t pool;
pool = pool_alloconly_create("anonymous_auth_request", 256);
auth_request = p_new(pool, struct auth_request, 1);
auth_request->refcount = 1;
auth_request->pool = pool;
+ auth_request->auth_initial = mech_anonymous_auth_initial;
auth_request->auth_continue = mech_anonymous_auth_continue;
auth_request->auth_free = mech_anonymous_auth_free;
- /* initialize reply */
- memset(&reply, 0, sizeof(reply));
- reply.id = id;
- reply.result = AUTH_CLIENT_RESULT_CONTINUE;
-
- callback(&reply, NULL, conn);
return auth_request;
}
struct mech_module mech_anonymous = {
- AUTH_MECH_ANONYMOUS,
+ "ANONYMOUS",
+
+ MEMBER(plaintext) FALSE,
+ MEMBER(advertise) TRUE,
+
+ MEMBER(passdb_need_plain) FALSE,
+ MEMBER(passdb_need_credentials) FALSE,
+
mech_anonymous_auth_new
};
static int
mech_cram_md5_auth_continue(struct auth_request *auth_request,
- struct auth_client_request_continue *request __attr_unused__,
- const unsigned char *data,
- mech_callback_t *callback)
+ const unsigned char *data, size_t data_size,
+ mech_callback_t *callback)
{
struct cram_auth_request *auth =
(struct cram_auth_request *)auth_request;
const char *error;
- if (parse_cram_response(auth, data, request->data_size, &error)) {
+ if (parse_cram_response(auth, data, data_size, &error)) {
auth_request->callback = callback;
auth_request->user =
return FALSE;
}
+static int
+mech_cram_md5_auth_initial(struct auth_request *auth_request,
+ struct auth_client_request_new *request,
+ const unsigned char *data __attr_unused__,
+ mech_callback_t *callback)
+{
+ struct cram_auth_request *auth =
+ (struct cram_auth_request *)auth_request;
+
+ struct auth_client_request_reply reply;
+
+ if (AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request)) {
+ /* No initial response in CRAM-MD5 */
+ return FALSE;
+ }
+
+ auth->challenge = p_strdup(auth->pool, get_cram_challenge());
+
+ /* initialize reply */
+ mech_init_auth_client_reply(&reply);
+ reply.id = request->id;
+ reply.result = AUTH_CLIENT_RESULT_CONTINUE;
+
+ /* send the initial challenge */
+ reply.reply_idx = 0;
+ reply.data_size = strlen(auth->challenge);
+ callback(&reply, auth->challenge, auth_request->conn);
+ return TRUE;
+}
+
static void mech_cram_md5_auth_free(struct auth_request *auth_request)
{
pool_unref(auth_request->pool);
}
-static struct auth_request *
-mech_cram_md5_auth_new(struct auth_client_connection *conn,
- unsigned int id, mech_callback_t *callback)
+static struct auth_request *mech_cram_md5_auth_new(void)
{
- struct auth_client_request_reply reply;
struct cram_auth_request *auth;
pool_t pool;
auth->auth_request.refcount = 1;
auth->auth_request.pool = pool;
+ auth->auth_request.auth_initial = mech_cram_md5_auth_initial;
auth->auth_request.auth_continue = mech_cram_md5_auth_continue;
auth->auth_request.auth_free = mech_cram_md5_auth_free;
- auth->challenge = p_strdup(auth->pool, get_cram_challenge());
-
- /* initialize reply */
- mech_init_auth_client_reply(&reply);
- reply.id = id;
- reply.result = AUTH_CLIENT_RESULT_CONTINUE;
-
- /* send the initial challenge */
- reply.reply_idx = 0;
- reply.data_size = strlen(auth->challenge);
- callback(&reply, auth->challenge, conn);
-
return &auth->auth_request;
}
struct mech_module mech_cram_md5 = {
- AUTH_MECH_CRAM_MD5,
+ "CRAM-MD5",
+
+ MEMBER(plaintext) FALSE,
+ MEMBER(advertise) TRUE,
+
+ MEMBER(passdb_need_plain) FALSE,
+ MEMBER(passdb_need_credentials) TRUE,
+
mech_cram_md5_auth_new
};
static int
mech_digest_md5_auth_continue(struct auth_request *auth_request,
- struct auth_client_request_continue *request,
- const unsigned char *data,
+ const unsigned char *data, size_t data_size,
mech_callback_t *callback)
{
struct digest_auth_request *auth =
/* initialize reply */
mech_init_auth_client_reply(&reply);
- reply.id = request->id;
+ reply.id = auth_request->id;
if (auth->authenticated) {
/* authentication is done, we were just waiting the last
return TRUE;
}
- if (parse_digest_response(auth, data, request->data_size, &error)) {
+ if (parse_digest_response(auth, data, data_size, &error)) {
auth_request->callback = callback;
realm = auth->realm != NULL ? auth->realm : default_realm;
return FALSE;
}
+static int
+mech_digest_md5_auth_initial(struct auth_request *auth_request,
+ struct auth_client_request_new *request,
+ const unsigned char *data __attr_unused__,
+ mech_callback_t *callback)
+{
+ struct digest_auth_request *auth =
+ (struct digest_auth_request *)auth_request;
+ struct auth_client_request_reply reply;
+ string_t *challenge;
+ size_t data_size;
+
+ if (AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request)) {
+ /* FIXME: support subsequent authentication? */
+ data += request->initial_resp_idx;
+ data_size = request->data_size - request->initial_resp_idx;
+
+ return auth_request->auth_continue(auth_request, data,
+ data_size, callback);
+ }
+
+ /* initialize reply */
+ mech_init_auth_client_reply(&reply);
+ reply.id = request->id;
+ reply.result = AUTH_CLIENT_RESULT_CONTINUE;
+
+ /* send the initial challenge */
+ reply.reply_idx = 0;
+ challenge = get_digest_challenge(auth);
+ reply.data_size = str_len(challenge);
+ callback(&reply, str_data(challenge), auth_request->conn);
+ return TRUE;
+}
+
static void mech_digest_md5_auth_free(struct auth_request *auth_request)
{
pool_unref(auth_request->pool);
}
static struct auth_request *
-mech_digest_md5_auth_new(struct auth_client_connection *conn,
- unsigned int id, mech_callback_t *callback)
+mech_digest_md5_auth_new(void)
{
- struct auth_client_request_reply reply;
struct digest_auth_request *auth;
pool_t pool;
- string_t *challenge;
pool = pool_alloconly_create("digest_md5_auth_request", 2048);
auth = p_new(pool, struct digest_auth_request, 1);
auth->auth_request.refcount = 1;
auth->auth_request.pool = pool;
+ auth->auth_request.auth_initial = mech_digest_md5_auth_initial;
auth->auth_request.auth_continue = mech_digest_md5_auth_continue;
auth->auth_request.auth_free = mech_digest_md5_auth_free;
auth->qop = QOP_AUTH;
-
- /* initialize reply */
- mech_init_auth_client_reply(&reply);
- reply.id = id;
- reply.result = AUTH_CLIENT_RESULT_CONTINUE;
-
- /* send the initial challenge */
- reply.reply_idx = 0;
- challenge = get_digest_challenge(auth);
- reply.data_size = str_len(challenge);
- callback(&reply, str_data(challenge), conn);
-
return &auth->auth_request;
}
struct mech_module mech_digest_md5 = {
- AUTH_MECH_DIGEST_MD5,
+ "DIGEST-MD5",
+
+ MEMBER(plaintext) FALSE,
+ MEMBER(advertise) TRUE,
+
+ MEMBER(passdb_need_plain) FALSE,
+ MEMBER(passdb_need_credentials) TRUE,
+
mech_digest_md5_auth_new
};
static int
mech_plain_auth_continue(struct auth_request *auth_request,
- struct auth_client_request_continue *request,
- const unsigned char *data, mech_callback_t *callback)
+ const unsigned char *data, size_t data_size,
+ mech_callback_t *callback)
{
const char *authid, *authenid;
char *pass;
authenid = NULL; pass = "";
count = 0;
- for (i = 0; i < request->data_size; i++) {
+ for (i = 0; i < data_size; i++) {
if (data[i] == '\0') {
if (++count == 1)
authenid = (const char *) data + i+1;
else {
i++;
- len = request->data_size - i;
+ len = data_size - i;
pass = p_strndup(unsafe_data_stack_pool,
data+i, len);
break;
return TRUE;
}
+static int
+mech_plain_auth_initial(struct auth_request *auth_request,
+ struct auth_client_request_new *request,
+ const unsigned char *data,
+ mech_callback_t *callback)
+{
+ struct auth_client_request_reply reply;
+ size_t data_size;
+
+ if (AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request)) {
+ data += request->initial_resp_idx;
+ data_size = request->data_size - request->initial_resp_idx;
+
+ return auth_request->auth_continue(auth_request, data,
+ data_size, callback);
+ }
+
+ /* initialize reply */
+ memset(&reply, 0, sizeof(reply));
+ reply.id = request->id;
+ reply.result = AUTH_CLIENT_RESULT_CONTINUE;
+
+ callback(&reply, NULL, auth_request->conn);
+ return TRUE;
+}
+
static void
mech_plain_auth_free(struct auth_request *auth_request)
{
pool_unref(auth_request->pool);
}
-static struct auth_request *
-mech_plain_auth_new(struct auth_client_connection *conn, unsigned int id,
- mech_callback_t *callback)
+static struct auth_request *mech_plain_auth_new(void)
{
struct auth_request *auth_request;
- struct auth_client_request_reply reply;
pool_t pool;
pool = pool_alloconly_create("plain_auth_request", 256);
auth_request = p_new(pool, struct auth_request, 1);
auth_request->refcount = 1;
auth_request->pool = pool;
+ auth_request->auth_initial = mech_plain_auth_initial;
auth_request->auth_continue = mech_plain_auth_continue;
auth_request->auth_free = mech_plain_auth_free;
-
- /* initialize reply */
- memset(&reply, 0, sizeof(reply));
- reply.id = id;
- reply.result = AUTH_CLIENT_RESULT_CONTINUE;
-
- callback(&reply, NULL, conn);
return auth_request;
}
struct mech_module mech_plain = {
- AUTH_MECH_PLAIN,
+ "PLAIN",
+
+ MEMBER(plaintext) TRUE,
+ MEMBER(advertise) FALSE,
+
+ MEMBER(passdb_need_plain) TRUE,
+ MEMBER(passdb_need_credentials) FALSE,
+
mech_plain_auth_new
};
#include "buffer.h"
#include "hash.h"
#include "mech.h"
+#include "str.h"
#include "var-expand.h"
#include "auth-client-connection.h"
#include "auth-master-connection.h"
#include <stdlib.h>
-struct mech_module_list {
- struct mech_module_list *next;
-
- struct mech_module module;
-};
-
-enum auth_mech auth_mechanisms;
+struct mech_module_list *mech_modules;
const char *const *auth_realms;
const char *default_realm;
const char *anonymous_username;
static int set_use_cyrus_sasl;
static int ssl_require_client_cert;
-static struct mech_module_list *mech_modules;
static struct auth_client_request_reply failure_reply;
void mech_register_module(struct mech_module *module)
{
struct mech_module_list *list;
- i_assert((auth_mechanisms & module->mech) == 0);
-
- auth_mechanisms |= module->mech;
-
list = i_new(struct mech_module_list, 1);
list->module = *module;
{
struct mech_module_list **pos, *list;
- if ((auth_mechanisms & module->mech) == 0)
- return; /* not registered */
-
- auth_mechanisms &= ~module->mech;
-
for (pos = &mech_modules; *pos != NULL; pos = &(*pos)->next) {
- if ((*pos)->module.mech == module->mech) {
+ if (strcmp((*pos)->module.mech_name, module->mech_name) == 0) {
list = *pos;
*pos = (*pos)->next;
i_free(list);
}
}
+const string_t *auth_mechanisms_get_list(void)
+{
+ struct mech_module_list *list;
+ string_t *str;
+
+ str = t_str_new(128);
+ for (list = mech_modules; list != NULL; list = list->next)
+ str_append(str, list->module.mech_name);
+
+ return str;
+}
+
+static struct mech_module *mech_module_find(const char *name)
+{
+ struct mech_module_list *list;
+
+ for (list = mech_modules; list != NULL; list = list->next) {
+ if (strcmp(list->module.mech_name, name) == 0)
+ return &list->module;
+ }
+ return NULL;
+}
+
void mech_request_new(struct auth_client_connection *conn,
struct auth_client_request_new *request,
+ const unsigned char *data,
mech_callback_t *callback)
{
- struct mech_module_list *list;
+ struct mech_module *mech;
struct auth_request *auth_request;
- if ((auth_mechanisms & request->mech) == 0) {
+ /* make sure data is NUL-terminated */
+ if (request->data_size == 0 || request->initial_resp_idx == 0 ||
+ request->mech_idx >= request->data_size ||
+ request->protocol_idx >= request->data_size ||
+ request->initial_resp_idx > request->data_size ||
+ data[request->initial_resp_idx-1] != '\0') {
+ i_error("BUG: Auth client %u sent corrupted request",
+ conn->pid);
+ failure_reply.id = request->id;
+ callback(&failure_reply, NULL, conn);
+ return;
+ }
+
+ mech = mech_module_find((const char *)data + request->mech_idx);
+ if (mech == NULL) {
/* unsupported mechanism */
i_error("BUG: Auth client %u requested unsupported "
- "auth mechanism %d", conn->pid, request->mech);
+ "auth mechanism %s", conn->pid,
+ (const char *)data + request->mech_idx);
failure_reply.id = request->id;
callback(&failure_reply, NULL, conn);
return;
}
#ifdef USE_CYRUS_SASL2
- if (set_use_cyrus_sasl) {
+ if (set_use_cyrus_sasl)
auth_request = mech_cyrus_sasl_new(conn, request, callback);
- } else
+ else
#endif
- {
- auth_request = NULL;
-
- for (list = mech_modules; list != NULL; list = list->next) {
- if (list->module.mech == request->mech) {
- auth_request =
- list->module.auth_new(conn, request->id,
- callback);
- break;
- }
- }
- }
+ auth_request = mech->auth_new();
if (auth_request != NULL) {
auth_request->created = ioloop_time;
auth_request->conn = conn;
auth_request->id = request->id;
- strocpy(auth_request->protocol, request->protocol,
- sizeof(auth_request->protocol));
+ auth_request->protocol =
+ p_strdup(auth_request->pool,
+ (const char *)data + request->protocol_idx);
hash_insert(conn->auth_requests, POINTER_CAST(request->id),
auth_request);
+
+ if (!auth_request->auth_initial(auth_request, request, data,
+ callback))
+ mech_request_free(auth_request, request->id);
}
}
callback(&failure_reply, NULL, conn);
} else {
if (!auth_request->auth_continue(auth_request,
- request, data, callback))
+ data, request->data_size,
+ callback))
mech_request_free(auth_request, request->id);
}
}
const char *env;
mech_modules = NULL;
- auth_mechanisms = 0;
memset(&failure_reply, 0, sizeof(failure_reply));
failure_reply.result = AUTH_CLIENT_RESULT_FAILURE;
mechanisms++;
}
- if (auth_mechanisms == 0)
+ if (mech_modules == NULL)
i_fatal("No authentication mechanisms configured");
/* get our realm - note that we allocate from data stack so
unsigned int id;
time_t created;
- char protocol[AUTH_CLIENT_PROTOCOL_BUF_SIZE];
+ char *protocol;
mech_callback_t *callback;
+ int (*auth_initial)(struct auth_request *auth_request,
+ struct auth_client_request_new *request,
+ const unsigned char *data,
+ mech_callback_t *callback);
int (*auth_continue)(struct auth_request *auth_request,
- struct auth_client_request_continue *request,
- const unsigned char *data,
+ const unsigned char *data, size_t data_size,
mech_callback_t *callback);
void (*auth_free)(struct auth_request *auth_request);
/* ... mechanism specific data ... */
};
struct mech_module {
- enum auth_mech mech;
+ const char *mech_name;
- struct auth_request *(*auth_new)(struct auth_client_connection *conn,
- unsigned int id,
- mech_callback_t *callback);
+ unsigned int plaintext:1;
+ unsigned int advertise:1;
+ unsigned int passdb_need_plain:1;
+ unsigned int passdb_need_credentials:1;
+
+ struct auth_request *(*auth_new)(void);
};
-extern enum auth_mech auth_mechanisms;
+struct mech_module_list {
+ struct mech_module_list *next;
+
+ struct mech_module module;
+};
+
+extern struct mech_module_list *mech_modules;
extern const char *const *auth_realms;
extern const char *default_realm;
extern const char *anonymous_username;
void mech_register_module(struct mech_module *module);
void mech_unregister_module(struct mech_module *module);
+const string_t *auth_mechanisms_get_list(void);
+
void mech_request_new(struct auth_client_connection *conn,
struct auth_client_request_new *request,
+ const unsigned char *data,
mech_callback_t *callback);
void mech_request_continue(struct auth_client_connection *conn,
struct auth_client_request_continue *request,
struct auth_request *
mech_cyrus_sasl_new(struct auth_client_connection *conn,
struct auth_client_request_new *request,
+ const unsigned char *data,
mech_callback_t *callback);
void auth_request_ref(struct auth_request *request);
callback(password, auth_request);
}
+static void
+mech_list_verify_passdb(struct passdb_module *passdb, const char *name)
+{
+ struct mech_module_list *list;
+
+ for (list = mech_modules; list != NULL; list = list->next) {
+ if (list->module.passdb_need_plain &&
+ passdb->verify_plain == NULL)
+ break;
+ if (list->module.passdb_need_credentials &&
+ passdb->lookup_credentials == NULL)
+ break;
+ }
+
+ if (list != NULL) {
+ i_fatal("Passdb %s doesn't support %s method",
+ name, list->module.mech_name);
+ }
+}
+
void passdb_init(void)
{
const char *name, *args;
if (passdb->init != NULL)
passdb->init(args != NULL ? args+1 : "");
- if ((auth_mechanisms & AUTH_MECH_PLAIN) &&
- passdb->verify_plain == NULL)
- i_fatal("Passdb %s doesn't support PLAIN method", name);
-
- if ((auth_mechanisms & AUTH_MECH_CRAM_MD5) &&
- passdb->lookup_credentials == NULL)
- i_fatal("Passdb %s doesn't support CRAM-MD5 method", name);
-
- if ((auth_mechanisms & AUTH_MECH_DIGEST_MD5) &&
- passdb->lookup_credentials == NULL)
- i_fatal("Passdb %s doesn't support DIGEST-MD5 method", name);
+ mech_list_verify_passdb(passdb, name);
}
void passdb_deinit(void)
#include "str.h"
#include "imap-parser.h"
#include "auth-client.h"
-#include "../auth/auth-mech-desc.h"
#include "ssl-proxy.h"
#include "client.h"
#include "client-authenticate.h"
const char *client_authenticate_get_capabilities(int secured)
{
- static enum auth_mech cached_auth_mechs = 0;
- static char *cached_capability = NULL;
- enum auth_mech auth_mechs;
+ const struct auth_mech_desc *mech;
+ unsigned int i, count;
string_t *str;
- int i;
-
- auth_mechs = auth_client_get_available_mechs(auth_client);
- if (auth_mechs == cached_auth_mechs)
- return cached_capability;
-
- cached_auth_mechs = auth_mechs;
- i_free(cached_capability);
str = t_str_new(128);
-
- for (i = 0; i < AUTH_MECH_COUNT; i++) {
- if ((auth_mechs & auth_mech_desc[i].mech) == 0)
- continue; /* not available */
-
+ mech = auth_client_get_available_mechs(auth_client, &count);
+ for (i = 0; i < count; i++) {
/* a) transport is secured
b) auth mechanism isn't plaintext
c) we allow insecure authentication
- but don't advertise AUTH=PLAIN, as RFC 2595 requires
*/
- if (secured || !auth_mech_desc[i].plaintext ||
- (!disable_plaintext_auth &&
- auth_mech_desc[i].mech != AUTH_MECH_PLAIN)) {
+ if (mech[i].advertise &&
+ (secured || !mech[i].plaintext)) {
str_append_c(str, ' ');
str_append(str, "AUTH=");
- str_append(str, auth_mech_desc[i].name);
+ str_append(str, mech[i].name);
}
}
- cached_capability = i_strdup_empty(str_c(str));
- return cached_capability;
-}
-
-static struct auth_mech_desc *auth_mech_find(const char *name)
-{
- int i;
-
- for (i = 0; i < AUTH_MECH_COUNT; i++) {
- if (auth_mech_desc[i].name != NULL &&
- strcasecmp(auth_mech_desc[i].name, name) == 0)
- return &auth_mech_desc[i];
- }
-
- return NULL;
+ return str_c(str);
}
static void client_auth_abort(struct imap_client *client, const char *msg)
client_ref(client);
client->common.auth_request =
- auth_client_request_new(auth_client, AUTH_MECH_PLAIN, "IMAP",
+ auth_client_request_new(auth_client, "PLAIN", "IMAP",
client_get_auth_flags(client),
- login_callback, client, &error);
+ NULL, 0, login_callback,
+ client, &error);
if (client->common.auth_request == NULL) {
client_send_tagline(client, t_strconcat(
"NO Login failed: ", error, NULL));
int cmd_authenticate(struct imap_client *client, struct imap_arg *args)
{
- struct auth_mech_desc *mech;
+ const struct auth_mech_desc *mech;
const char *mech_name, *error;
/* we want only one argument: authentication mechanism name */
if (*mech_name == '\0')
return FALSE;
- mech = auth_mech_find(mech_name);
+ mech = auth_client_find_mech(auth_client, mech_name);
if (mech == NULL) {
client_send_tagline(client,
"NO Unsupported authentication mechanism.");
client_ref(client);
client->common.auth_request =
- auth_client_request_new(auth_client, mech->mech, "IMAP",
+ auth_client_request_new(auth_client, mech->name, "IMAP",
client_get_auth_flags(client),
- authenticate_callback,
+ NULL, 0, authenticate_callback,
client, &error);
if (client->common.auth_request != NULL) {
/* following input data will go to authentication */
/* Copyright (C) 2003 Timo Sirainen */
#include "lib.h"
+#include "buffer.h"
#include "ioloop.h"
#include "hash.h"
#include "auth-client.h"
client = i_new(struct auth_client, 1);
client->pid = client_pid;
+ client->available_auth_mechs =
+ buffer_create_dynamic(default_pool, 128, (size_t)-1);
auth_client_connect_missing_servers(client);
return client;
void auth_client_free(struct auth_client *client)
{
struct auth_server_connection *next;
+ struct auth_mech_desc *mech;
+ size_t i, size;
+
+ mech = buffer_get_modifyable_data(client->available_auth_mechs, &size);
+ size /= sizeof(*mech);
+ for (i = 0; i < size; i++)
+ i_free(mech[i].name);
+ buffer_free(client->available_auth_mechs);
while (client->connections != NULL) {
next = client->connections->next;
i_free(client);
}
-enum auth_mech auth_client_get_available_mechs(struct auth_client *client)
+const struct auth_mech_desc *
+auth_client_get_available_mechs(struct auth_client *client,
+ unsigned int *mech_count)
{
- return client->available_auth_mechs;
+ const struct auth_mech_desc *mechs;
+ size_t size;
+
+ mechs = buffer_get_data(client->available_auth_mechs, &size);
+ *mech_count = size / sizeof(*mechs);
+ return mechs;
+}
+
+const struct auth_mech_desc *
+auth_client_find_mech(struct auth_client *client, const char *name)
+{
+ const struct auth_mech_desc *mech;
+ size_t i, size;
+
+ mech = buffer_get_data(client->available_auth_mechs, &size);
+ size /= sizeof(*mech);
+ for (i = 0; i < size; i++) {
+ if (strcasecmp(mech[i].name, name) == 0)
+ return &mech[i];
+ }
+
+ return NULL;
}
int auth_client_is_connected(struct auth_client *client)
struct auth_client;
struct auth_request;
+struct auth_mech_desc {
+ char *name;
+ unsigned int plaintext:1;
+ unsigned int advertise:1;
+};
+
/* reply is NULL if auth connection died */
typedef void auth_request_callback_t(struct auth_request *request,
struct auth_client_request_reply *reply,
void auth_client_set_connect_notify(struct auth_client *client,
auth_connect_notify_callback_t *callback,
void *context);
-enum auth_mech auth_client_get_available_mechs(struct auth_client *client);
+const struct auth_mech_desc *
+auth_client_get_available_mechs(struct auth_client *client,
+ unsigned int *mech_count);
+const struct auth_mech_desc *
+auth_client_find_mech(struct auth_client *client, const char *name);
void auth_client_connect_missing_servers(struct auth_client *client);
happens for the request. */
struct auth_request *
auth_client_request_new(struct auth_client *client,
- enum auth_mech mech, const char *protocol,
+ const char *mech, const char *protocol,
enum auth_client_request_new_flags flags,
+ const unsigned char *initial_resp_data,
+ size_t initial_resp_size,
auth_request_callback_t *callback, void *context,
const char **error_r);
/* Copyright (C) 2003 Timo Sirainen */
#include "lib.h"
+#include "buffer.h"
#include "hash.h"
#include "ioloop.h"
#include "istream.h"
static void auth_server_connection_unref(struct auth_server_connection *conn);
-static void update_available_auth_mechs(struct auth_client *client)
+static void update_available_auth_mechs(struct auth_server_connection *conn)
{
- struct auth_server_connection *conn;
-
- client->available_auth_mechs = 0;
- for (conn = client->connections; conn != NULL; conn = conn->next)
- client->available_auth_mechs |= conn->available_auth_mechs;
+ struct auth_client *client = conn->client;
+ const struct auth_mech_desc *mech;
+ struct auth_mech_desc *new_mech;
+ unsigned int i;
+
+ mech = conn->available_auth_mechs;
+ for (i = 0; i < conn->available_auth_mechs_count; i++) {
+ if (auth_client_find_mech(client, mech[i].name) == NULL) {
+ new_mech = buffer_append_space_unsafe(
+ client->available_auth_mechs, sizeof(*mech));
+ *new_mech = mech[i];
+ new_mech->name = i_strdup(mech[i].name);
+ }
+ }
}
static void auth_handle_handshake(struct auth_server_connection *conn,
- struct auth_client_handshake_reply *handshake)
+ struct auth_client_handshake_reply *handshake,
+ const unsigned char *data)
{
+ struct auth_client_handshake_mech_desc handshake_mech_desc;
+ struct auth_mech_desc mech_desc;
+ buffer_t *buf;
+ unsigned int i;
+
if (handshake->server_pid == 0) {
i_error("BUG: Auth server said it's PID 0");
auth_server_connection_destroy(conn, FALSE);
return;
}
+ if (handshake->data_size == 0 || data[handshake->data_size-1] != '\0' ||
+ handshake->mech_count * sizeof(handshake_mech_desc) >=
+ handshake->data_size) {
+ i_error("BUG: Auth server sent corrupted handshake");
+ auth_server_connection_destroy(conn, FALSE);
+ return;
+ }
+
+ buf = buffer_create_dynamic(conn->pool, sizeof(mech_desc) *
+ handshake->mech_count, (size_t)-1);
+ for (i = 0; i < handshake->mech_count; i++) {
+ memcpy(&handshake_mech_desc,
+ data + sizeof(handshake_mech_desc) * i,
+ sizeof(handshake_mech_desc));
+
+ if (handshake_mech_desc.name_idx >= handshake->data_size) {
+ i_error("BUG: Auth server sent corrupted handshake");
+ auth_server_connection_destroy(conn, FALSE);
+ return;
+ }
+
+ mech_desc.name = p_strdup(conn->pool, (const char *)data +
+ handshake_mech_desc.name_idx);
+ mech_desc.plaintext = handshake_mech_desc.plaintext;
+ mech_desc.advertise = handshake_mech_desc.advertise;
+ buffer_append(buf, &mech_desc, sizeof(mech_desc));
+
+ if (strcmp(mech_desc.name, "PLAIN") == 0)
+ conn->has_plain_mech = TRUE;
+ }
+
conn->pid = handshake->server_pid;
- conn->available_auth_mechs = handshake->auth_mechanisms;
+ conn->available_auth_mechs_count =
+ buffer_get_used_size(buf) / sizeof(mech_desc);
+ conn->available_auth_mechs = buffer_free_without_data(buf);
conn->handshake_received = TRUE;
conn->client->conn_waiting_handshake_count--;
- update_available_auth_mechs(conn->client);
+ update_available_auth_mechs(conn);
if (conn->client->connect_notify_callback != NULL &&
auth_client_is_connected(conn->client)) {
if (!conn->handshake_received) {
data = i_stream_get_data(conn->input, &size);
- if (size == sizeof(handshake)) {
- memcpy(&handshake, data, sizeof(handshake));
- i_stream_skip(conn->input, sizeof(handshake));
-
- auth_handle_handshake(conn, &handshake);
- } else if (size > sizeof(handshake)) {
- i_error("BUG: Auth server sent us too large handshake "
- "(%"PRIuSIZE_T " vs %"PRIuSIZE_T")", size,
- sizeof(handshake));
- auth_server_connection_destroy(conn, FALSE);
- }
+ if (size < sizeof(handshake))
+ return;
+
+ memcpy(&handshake, data, sizeof(handshake));
+ if (size < sizeof(handshake) + handshake.data_size)
+ return;
+
+ conn->refcount++;
+ auth_handle_handshake(conn, &handshake,
+ data + sizeof(handshake));
+ i_stream_skip(conn->input, sizeof(handshake) +
+ handshake.data_size);
+ auth_server_connection_unref(conn);
return;
}
{
if (--conn->refcount > 0)
return;
+ i_assert(conn->refcount == 0);
hash_destroy(conn->requests);
struct auth_server_connection *
auth_server_connection_find_mech(struct auth_client *client,
- enum auth_mech mech, const char **error_r)
+ const char *name, const char **error_r)
{
struct auth_server_connection *conn;
+ const struct auth_mech_desc *mech;
+ unsigned int i;
for (conn = client->connections; conn != NULL; conn = conn->next) {
- if ((conn->available_auth_mechs & mech))
- return conn;
+ mech = conn->available_auth_mechs;
+ for (i = 0; i < conn->available_auth_mechs_count; i++) {
+ if (strcmp(mech[i].name, name) == 0)
+ return conn;
+ }
}
- if ((client->available_auth_mechs & mech) == 0)
+ if (auth_client_find_mech(client, name) == NULL)
*error_r = "Unsupported authentication mechanism";
else {
*error_r = "Authentication server isn't connected, "
unsigned int conn_waiting_handshake_count;
- enum auth_mech available_auth_mechs;
+ buffer_t *available_auth_mechs;
unsigned int request_id_counter;
auth_connect_notify_callback_t *connect_notify_callback;
struct ostream *output;
unsigned int pid;
- enum auth_mech available_auth_mechs;
+ const struct auth_mech_desc *available_auth_mechs;
+ unsigned int available_auth_mechs_count;
struct auth_client_request_reply reply;
struct hash_table *requests;
unsigned int handshake_received:1;
unsigned int reply_received:1;
+ unsigned int has_plain_mech:1;
};
struct auth_server_connection *
struct auth_server_connection *
auth_server_connection_find_mech(struct auth_client *client,
- enum auth_mech mech, const char **error_r);
+ const char *name, const char **error_r);
#endif
/* Copyright (C) 2003 Timo Sirainen */
#include "lib.h"
+#include "buffer.h"
#include "hash.h"
#include "ostream.h"
#include "auth-client.h"
struct auth_request {
struct auth_server_connection *conn;
- enum auth_mech mech;
- char protocol[AUTH_CLIENT_PROTOCOL_BUF_SIZE];
+ char *mech, *protocol;
enum auth_client_request_new_flags flags;
unsigned int id;
+ unsigned char *initial_resp_data;
+ size_t initial_resp_size;
+
auth_request_callback_t *callback;
void *context;
struct auth_request *request)
{
struct auth_client_request_new auth_request;
+ buffer_t *buf;
+ int ret;
+ memset(&auth_request, 0, sizeof(auth_request));
auth_request.type = AUTH_CLIENT_REQUEST_NEW;
auth_request.id = request->id;
- strocpy(auth_request.protocol, request->protocol,
- sizeof(auth_request.protocol));
- auth_request.mech = request->mech;
auth_request.flags = request->flags;
- if (o_stream_send(conn->output, &auth_request,
- sizeof(auth_request)) < 0) {
+ t_push();
+ buf = buffer_create_dynamic(pool_datastack_create(), 256, (size_t)-1);
+ buffer_set_used_size(buf, sizeof(auth_request));
+
+ auth_request.mech_idx =
+ buffer_get_used_size(buf) - sizeof(auth_request);
+ buffer_append(buf, request->mech, strlen(request->mech)+1);
+
+ auth_request.protocol_idx =
+ buffer_get_used_size(buf) - sizeof(auth_request);
+ buffer_append(buf, request->protocol, strlen(request->protocol)+1);
+
+ auth_request.initial_resp_idx =
+ buffer_get_used_size(buf) - sizeof(auth_request);
+ buffer_append(buf, request->initial_resp_data,
+ request->initial_resp_size);
+
+ auth_request.data_size =
+ buffer_get_used_size(buf) - sizeof(auth_request);
+
+ memcpy(buffer_get_space_unsafe(buf, 0, sizeof(auth_request)),
+ &auth_request, sizeof(auth_request));
+
+ ret = o_stream_send(conn->output, buffer_get_data(buf, NULL),
+ buffer_get_used_size(buf));
+ t_pop();
+
+ if (ret < 0) {
errno = conn->output->stream_errno;
i_warning("Error sending request to auth server: %m");
auth_server_connection_destroy(conn, TRUE);
{
conn = conn->next;
while (conn != NULL) {
- if ((conn->available_auth_mechs & AUTH_MECH_PLAIN) != 0)
+ if (conn->has_plain_mech)
return conn;
conn = conn->next;
}
struct auth_request *
auth_client_request_new(struct auth_client *client,
- enum auth_mech mech, const char *protocol,
+ const char *mech, const char *protocol,
enum auth_client_request_new_flags flags,
+ const unsigned char *initial_resp_data,
+ size_t initial_resp_size,
auth_request_callback_t *callback, void *context,
const char **error_r)
{
request = i_new(struct auth_request, 1);
request->conn = conn;
- request->mech = mech;
- strocpy(request->protocol, protocol, sizeof(request->protocol));
+ request->mech = i_strdup(mech);
+ request->protocol = i_strdup(protocol);
request->flags = flags;
request->id = ++client->request_id_counter;
+
+ if (initial_resp_size != 0) {
+ request->initial_resp_size = initial_resp_size;
+ request->initial_resp_data = i_malloc(initial_resp_size);
+ memcpy(request->initial_resp_data, initial_resp_data,
+ initial_resp_size);
+ }
+
if (request->id == 0) {
/* wrapped - ID 0 not allowed */
request->id = ++client->request_id_counter;
{
auth_server_send_continue(request->conn, request, data, data_size);
- if (request->mech == AUTH_MECH_PLAIN &&
- request->plaintext_data == NULL) {
+ if (strcmp(request->mech, "PLAIN") == 0 &&
+ request->plaintext_data == NULL && request->conn != NULL) {
request->next_conn = get_next_plain_server(request->conn);
if (request->next_conn != NULL) {
/* plaintext authentication - save the data so we can
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);
}
return -1;
}
+ dotlock_r->dev = st.st_dev;
+ dotlock_r->ino = st.st_ino;
+
if (close(fd) < 0) {
i_error("fstat(%s) failed: %m", lock_path);
return -1;
}
- dotlock_r->dev = st.st_dev;
- dotlock_r->ino = st.st_ino;
+ /* some NFS implementations may have used cached mtime in previous
+ fstat() call. Check again to avoid "dotlock was modified" errors. */
+ if (stat(lock_path, &st) < 0) {
+ i_error("stat(%s) failed: %m", lock_path);
+ return -1;
+ }
+ /* extra sanity check won't hurt.. */
+ if (st.st_dev != dotlock_r->dev ||
+ st.st_ino != dotlock_r->ino) {
+ i_error("dotlock %s was immediately recreated under us",
+ lock_path);
+ return -1;
+ }
dotlock_r->mtime = st.st_mtime;
return 1;
}
#include "safe-memset.h"
#include "str.h"
#include "auth-client.h"
-#include "../auth/auth-mech-desc.h"
#include "../pop3/capability.h"
#include "ssl-proxy.h"
#include "master.h"
int cmd_capa(struct pop3_client *client, const char *args __attr_unused__)
{
- static enum auth_mech cached_auth_mechs = 0;
- static char *cached_capability = NULL;
- enum auth_mech auth_mechs;
+ const struct auth_mech_desc *mech;
+ unsigned int i, count;
string_t *str;
- int i;
-
- auth_mechs = auth_client_get_available_mechs(auth_client);
- if (cached_auth_mechs != auth_mechs) {
- cached_auth_mechs = auth_mechs;
- i_free(cached_capability);
-
- str = t_str_new(128);
-
- str_append(str, "SASL");
- for (i = 0; i < AUTH_MECH_COUNT; i++) {
- if ((auth_mechs & auth_mech_desc[i].mech) == 0)
- continue; /* not available */
-
- /* a) transport is secured
- b) auth mechanism isn't plaintext
- c) we allow insecure authentication
- - but don't advertise AUTH=PLAIN,
- as RFC 2595 requires
- */
- if (client->secured || !auth_mech_desc[i].plaintext ||
- (!disable_plaintext_auth &&
- auth_mech_desc[i].mech != AUTH_MECH_PLAIN)) {
- str_append_c(str, ' ');
- str_append(str, auth_mech_desc[i].name);
- }
- }
- cached_capability = i_strdup(str_c(str));
+ str = t_str_new(128);
+ str_append(str, "SASL");
+
+ mech = auth_client_get_available_mechs(auth_client, &count);
+ for (i = 0; i < count; i++) {
+ /* a) transport is secured
+ b) auth mechanism isn't plaintext
+ c) we allow insecure authentication
+ - but don't advertise AUTH=PLAIN, as RFC 2595 requires
+ */
+ if (mech[i].advertise &&
+ (client->secured || !mech[i].plaintext)) {
+ str_append_c(str, ' ');
+ str_append(str, "AUTH=");
+ str_append(str, mech[i].name);
+ }
}
client_send_line(client, t_strconcat("+OK\r\n" POP3_CAPABILITY_REPLY,
(ssl_initialized && !client->tls) ?
"STLS\r\n" : "",
- cached_capability,
+ str_c(str),
"\r\n.", NULL));
return TRUE;
}
-static struct auth_mech_desc *auth_mech_find(const char *name)
-{
- int i;
-
- for (i = 0; i < AUTH_MECH_COUNT; i++) {
- if (auth_mech_desc[i].name != NULL &&
- strcasecmp(auth_mech_desc[i].name, name) == 0)
- return &auth_mech_desc[i];
- }
-
- return NULL;
-}
-
static void client_auth_abort(struct pop3_client *client, const char *msg)
{
if (client->common.auth_request != NULL) {
{
struct pop3_client *client = context;
const char *error;
- const void *ptr;
- size_t size;
switch (auth_callback(request, reply, data, &client->common,
master_callback, &error)) {
case -1:
+ case 0:
/* login failed */
client_auth_abort(client, error);
break;
- case 0:
- ptr = buffer_get_data(client->plain_login, &size);
- auth_client_request_continue(request, ptr, size);
-
- buffer_set_used_size(client->plain_login, 0);
- break;
-
default:
/* success, we should be able to log in. if we fail, just
disconnect the client. */
client_ref(client);
client->common.auth_request =
- auth_client_request_new(auth_client, AUTH_MECH_PLAIN, "POP3",
+ auth_client_request_new(auth_client, "PLAIN", "POP3",
client_get_auth_flags(client),
+ str_data(client->plain_login),
+ str_len(client->plain_login),
login_callback, client, &error);
+ buffer_set_used_size(client->plain_login, 0);
+
if (client->common.auth_request != NULL) {
/* don't read any input from client until login is finished */
if (client->common.io != NULL) {
int cmd_auth(struct pop3_client *client, const char *args)
{
- struct auth_mech_desc *mech;
- const char *error;
+ const struct auth_mech_desc *mech;
+ const char *mech_name, *error, *p;
+ string_t *buf;
+ size_t argslen;
+
+ /* <mechanism name> <initial response> */
+ p = strchr(args, ' ');
+ if (p == NULL) {
+ mech_name = args;
+ args = "";
+ } else {
+ mech_name = t_strdup_until(args, p);
+ args = p+1;
+ }
- /* we have only one argument: authentication mechanism name */
- mech = auth_mech_find(args);
+ mech = auth_client_find_mech(auth_client, mech_name);
if (mech == NULL) {
client_send_line(client,
"-ERR Unsupported authentication mechanism.");
return TRUE;
}
+ argslen = strlen(args);
+ buf = buffer_create_static_hard(pool_datastack_create(), argslen);
+
+ if (base64_decode((const unsigned char *)args, argslen,
+ NULL, buf) <= 0) {
+ /* failed */
+ client_send_line(client, "-ERR Invalid base64 data.");
+ return TRUE;
+ }
+
client_ref(client);
client->common.auth_request =
- auth_client_request_new(auth_client, mech->mech, "POP3",
+ auth_client_request_new(auth_client, mech->name, "POP3",
client_get_auth_flags(client),
+ str_data(buf), str_len(buf),
authenticate_callback, client, &error);
if (client->common.auth_request != NULL) {
/* following input data will go to authentication */