This required some changes in auth APIs.
--HG--
branch : HEAD
Solar Designer <solar@openwall.com> (src/auth/userinfo-passwd|shadow|pam.c)
-Damian Ivereigh <damian@cisco.com> (src/lib-index/mail-tree-redblack.c)
-
Alex Howansky <alex@wankwood.com> (src/auth/*-pgsql.c)
Matthew Reimer <mreimer@vpop.net> (src/auth/*-mysql.c)
+Andrey Panin <pazke@donpac.ru> (src/auth/mech-apop.c)
+
This product includes software developed by Computing Services
at Carnegie Mellon University (http://www.cmu.edu/computing/).
(src/lib/base64.c, src/lib/mkgmtime.c)
auth default {
# Space separated list of wanted authentication mechanisms:
- # plain digest-md5 cram-md5 anonymous
+ # plain digest-md5 cram-md5 apop anonymous
mechanisms = plain
# Where user database is kept:
mech-plain.c \
mech-cram-md5.c \
mech-digest-md5.c \
+ mech-apop.c \
mycrypt.c \
passdb.c \
passdb-bsdauth.c \
struct auth_client_connection *
auth_client_connection_create(struct auth_master_connection *master, int fd)
{
+ static unsigned int connect_uid_counter = 0;
struct auth_client_connection *conn;
+ struct auth_client_handshake_reply handshake_reply;
+
pool_t pool;
pool = pool_alloconly_create("Auth client", 4096);
conn->pool = pool;
conn->master = master;
conn->refcount = 1;
+ conn->connect_uid = ++connect_uid_counter;
conn->fd = fd;
conn->input = i_stream_create_file(fd, default_pool, MAX_INBUF_SIZE,
conn->next = master->clients;
master->clients = conn;
- if (o_stream_send(conn->output, master->handshake_reply,
- sizeof(*master->handshake_reply) +
- master->handshake_reply->data_size) < 0) {
+ handshake_reply = *master->handshake_reply;
+ handshake_reply.connect_uid = conn->connect_uid;
+
+ if (o_stream_send(conn->output, &handshake_reply,
+ sizeof(handshake_reply)) < 0 ||
+ o_stream_send(conn->output, master->handshake_reply + 1,
+ handshake_reply.data_size) < 0) {
auth_client_connection_destroy(conn);
conn = NULL;
}
struct hash_table *auth_requests;
unsigned int pid;
+ unsigned int connect_uid;
};
struct auth_client_connection *
/* 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;
mech_desc_offset += sizeof(mech_desc);
}
- reply.data_size = buffer_get_used_size(buf);
+ reply.data_size = buffer_get_used_size(buf) - sizeof(reply);
memcpy(buffer_get_space_unsafe(buf, 0, sizeof(reply)),
&reply, sizeof(reply));
}
extern struct mech_module mech_plain;
+extern struct mech_module mech_apop;
extern struct mech_module mech_cram_md5;
extern struct mech_module mech_digest_md5;
extern struct mech_module mech_anonymous;
while (*mechanisms != NULL) {
if (strcasecmp(*mechanisms, "PLAIN") == 0)
mech_register_module(&mech_plain);
+ else if (strcasecmp(*mechanisms, "APOP") == 0)
+ mech_register_module(&mech_apop);
else if (strcasecmp(*mechanisms, "CRAM-MD5") == 0)
mech_register_module(&mech_cram_md5);
else if (strcasecmp(*mechanisms, "DIGEST-MD5") == 0)
timeout_remove(to_auth_failures);
mech_unregister_module(&mech_plain);
+ mech_unregister_module(&mech_apop);
mech_unregister_module(&mech_cram_md5);
mech_unregister_module(&mech_digest_md5);
mech_unregister_module(&mech_anonymous);
client_ref(client);
client->common.auth_request =
- auth_client_request_new(auth_client, &info,
+ auth_client_request_new(auth_client, NULL, &info,
login_callback, client, &error);
if (client->common.auth_request == NULL) {
client_send_tagline(client, t_strconcat(
client_ref(client);
client->common.auth_request =
- auth_client_request_new(auth_client, &info,
+ auth_client_request_new(auth_client, NULL, &info,
authenticate_callback, client, &error);
if (client->common.auth_request != NULL) {
/* following input data will go to authentication */
return NULL;
}
+int auth_client_reserve_connection(struct auth_client *client, const char *mech,
+ struct auth_connect_id *id_r)
+{
+ struct auth_server_connection *conn;
+ const char *error;
+
+ conn = auth_server_connection_find_mech(client, mech, &error);
+ if (conn == NULL)
+ return FALSE;
+
+ id_r->server_pid = conn->server_pid;
+ id_r->connect_uid = conn->connect_uid;
+
+ return TRUE;
+}
+
int auth_client_is_connected(struct auth_client *client)
{
return !client->reconnect &&
unsigned int advertise:1;
};
+struct auth_connect_id {
+ unsigned int server_pid;
+ unsigned int connect_uid;
+};
+
struct auth_request_info {
const char *mech;
const char *protocol;
const struct auth_mech_desc *
auth_client_find_mech(struct auth_client *client, const char *name);
+/* Reserve connection for specific mechanism. The id can be given to
+ auth_client_request_new() to force it to use the same connection, or fail.
+ This is currently useful only for APOP authentication. Returns TRUE if
+ successfull. */
+int auth_client_reserve_connection(struct auth_client *client, const char *mech,
+ struct auth_connect_id *id_r);
+
/* Create a new authentication request. callback is called whenever something
- happens for the request. */
+ happens for the request. id can be NULL. */
struct auth_request *
-auth_client_request_new(struct auth_client *client,
+auth_client_request_new(struct auth_client *client, struct auth_connect_id *id,
const struct auth_request_info *request_info,
auth_request_callback_t *callback, void *context,
const char **error_r);
conn->has_plain_mech = TRUE;
}
- conn->pid = handshake->server_pid;
+ conn->server_pid = handshake->server_pid;
+ conn->connect_uid = handshake->connect_uid;
conn->available_auth_mechs_count =
buffer_get_used_size(buf) / sizeof(mech_desc);
conn->available_auth_mechs = buffer_free_without_data(buf);
struct istream *input;
struct ostream *output;
- unsigned int pid;
+ unsigned int server_pid;
+ unsigned int connect_uid;
+
const struct auth_mech_desc *available_auth_mechs;
unsigned int available_auth_mechs_count;
struct auth_client_request_reply reply;
}
struct auth_request *
-auth_client_request_new(struct auth_client *client,
+auth_client_request_new(struct auth_client *client, struct auth_connect_id *id,
const struct auth_request_info *request_info,
auth_request_callback_t *callback, void *context,
const char **error_r)
struct auth_server_connection *conn;
struct auth_request *request;
- conn = auth_server_connection_find_mech(client, request_info->mech,
- error_r);
+ if (id == NULL) {
+ conn = auth_server_connection_find_mech(client,
+ request_info->mech,
+ error_r);
+ } else {
+ *error_r = NULL;
+ conn = client->connections;
+ for (; conn != NULL; conn = conn->next) {
+ if (conn->connect_uid == id->connect_uid &&
+ conn->server_pid == id->server_pid)
+ break;
+ }
+ }
+
if (conn == NULL)
return NULL;
unsigned int auth_client_request_get_server_pid(struct auth_request *request)
{
- return request->conn->pid;
+ return request->conn->server_pid;
}
#include "common.h"
#include "base64.h"
#include "buffer.h"
+#include "hex-binary.h"
#include "ioloop.h"
#include "istream.h"
#include "ostream.h"
client_ref(client);
client->common.auth_request =
- auth_client_request_new(auth_client, &info,
+ auth_client_request_new(auth_client, NULL, &info,
login_callback, client, &error);
if (client->common.auth_request != NULL) {
client_ref(client);
client->common.auth_request =
- auth_client_request_new(auth_client, &info,
+ auth_client_request_new(auth_client, NULL, &info,
authenticate_callback, client, &error);
if (client->common.auth_request != NULL) {
/* following input data will go to authentication */
return TRUE;
}
+
+int cmd_apop(struct pop3_client *client, const char *args)
+{
+ struct auth_request_info info;
+ const char *error, *p;
+ buffer_t *apop_data;
+
+ if (client->apop_challenge == NULL) {
+ client_send_line(client, "-ERR APOP not enabled.");
+ return TRUE;
+ }
+
+ /* <username> <md5 sum in hex> */
+ p = strchr(args, ' ');
+ if (p == NULL || strlen(p+1) != 32) {
+ client_send_line(client, "-ERR Invalid parameters.");
+ return TRUE;
+ }
+
+ /* APOP challenge \0 username \0 APOP response */
+ apop_data = buffer_create_dynamic(pool_datastack_create(),
+ 128, (size_t)-1);
+ buffer_append(apop_data, client->apop_challenge,
+ strlen(client->apop_challenge)+1);
+ buffer_append(apop_data, args, (size_t)(p-args));
+ buffer_append_c(apop_data, '\0');
+
+ if (hex_to_binary(p+1, apop_data) <= 0) {
+ client_send_line(client,
+ "-ERR Invalid characters in MD5 response.");
+ return TRUE;
+ }
+
+ memset(&info, 0, sizeof(info));
+ info.mech = "APOP";
+ info.protocol = "POP3";
+ info.flags = client_get_auth_flags(client);
+ info.local_ip = client->common.local_ip;
+ info.remote_ip = client->common.ip;
+ info.initial_resp_data =
+ buffer_get_data(apop_data, &info.initial_resp_size);
+
+ client_ref(client);
+ client->common.auth_request =
+ auth_client_request_new(auth_client, &client->auth_id, &info,
+ login_callback, client, &error);
+
+ if (client->common.auth_request != NULL) {
+ /* don't read any input from client until login is finished */
+ if (client->common.io != NULL) {
+ io_remove(client->common.io);
+ client->common.io = NULL;
+ }
+ } else if (error == NULL) {
+ /* the auth connection was lost. we have no choice
+ but to fail the APOP logins completely since the
+ challenge is auth connection-specific. disconnect. */
+ client_destroy(client, "APOP auth connection lost");
+ client_unref(client);
+ } else {
+ client_send_line(client,
+ t_strconcat("-ERR Login failed: ", error, NULL));
+ client_unref(client);
+ }
+ return TRUE;
+}
int cmd_user(struct pop3_client *client, const char *args);
int cmd_pass(struct pop3_client *client, const char *args);
int cmd_auth(struct pop3_client *client, const char *args);
+int cmd_apop(struct pop3_client *client, const char *args);
#endif
#include "client-authenticate.h"
#include "auth-client.h"
#include "ssl-proxy.h"
+#include "hostpid.h"
+#include "imem.h"
/* max. length of input command line (spec says 512) */
#define MAX_INBUF_SIZE 2048
return cmd_pass(client, args);
if (strcmp(cmd, "AUTH") == 0)
return cmd_auth(client, args);
+ if (strcmp(cmd, "APOP") == 0)
+ return cmd_apop(client, args);
if (strcmp(cmd, "STLS") == 0)
return cmd_stls(client);
if (strcmp(cmd, "QUIT") == 0)
}
}
+static char *get_apop_challenge(void)
+{
+ struct auth_connect_id id;
+
+ /* FIXME: breaks if we're not connected! */
+
+ if (!auth_client_reserve_connection(auth_client, "APOP", &id))
+ return NULL;
+
+ return i_strdup_printf("<%x.%x.%s@%s>", id.server_pid, id.connect_uid,
+ dec2str(ioloop_time), my_hostname);
+}
+
struct client *client_create(int fd, int ssl, const struct ip_addr *local_ip,
const struct ip_addr *ip)
{
main_ref();
- client_send_line(client, "+OK " PACKAGE " ready.");
+ client->apop_challenge = get_apop_challenge();
+ client_send_line(client, t_strconcat("+OK " PACKAGE " ready.",
+ client->apop_challenge, NULL));
client_set_title(client);
return &client->common;
}
i_stream_unref(client->input);
o_stream_unref(client->output);
+ i_free(client->apop_challenge);
i_free(client->common.virtual_user);
i_free(client);
#include "network.h"
#include "master.h"
#include "client-common.h"
+#include "auth-client.h"
struct pop3_client {
struct client common;
char *last_user;
+ char *apop_challenge;
+ struct auth_connect_id auth_id;
+
unsigned int tls:1;
unsigned int secured:1;
unsigned int input_blocked:1;