client-authenticate.c revision 73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0fae
886N/A/* Copyright (C) 2002-2004 Timo Sirainen */
2362N/A
886N/A#include "common.h"
886N/A#include "base64.h"
886N/A#include "buffer.h"
886N/A#include "hex-binary.h"
886N/A#include "ioloop.h"
886N/A#include "istream.h"
886N/A#include "ostream.h"
886N/A#include "safe-memset.h"
886N/A#include "str.h"
886N/A#include "str-sanitize.h"
886N/A#include "auth-client.h"
886N/A#include "../pop3/capability.h"
886N/A#include "ssl-proxy.h"
886N/A#include "client.h"
886N/A#include "client-authenticate.h"
886N/A
2362N/Aint cmd_capa(struct pop3_client *client, const char *args __attr_unused__)
2362N/A{
2362N/A const struct auth_mech_desc *mech;
886N/A unsigned int i, count;
886N/A string_t *str;
886N/A
886N/A str = t_str_new(128);
886N/A str_append(str, "SASL");
886N/A
886N/A mech = auth_client_get_available_mechs(auth_client, &count);
886N/A for (i = 0; i < count; i++) {
886N/A /* a) transport is secured
886N/A b) auth mechanism isn't plaintext
886N/A c) we allow insecure authentication
886N/A */
886N/A if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 &&
886N/A (client->secured || disable_plaintext_auth ||
886N/A (mech[i].flags & MECH_SEC_PLAINTEXT) == 0)) {
886N/A str_append_c(str, ' ');
886N/A str_append(str, mech[i].name);
886N/A }
886N/A }
886N/A
886N/A client_send_line(client, t_strconcat("+OK\r\n" POP3_CAPABILITY_REPLY,
886N/A (ssl_initialized && !client->tls) ?
886N/A "STLS\r\n" : "",
886N/A str_c(str),
886N/A "\r\n.", NULL));
886N/A return TRUE;
886N/A}
886N/A
886N/Astatic void client_auth_input(void *context)
886N/A{
886N/A struct pop3_client *client = context;
886N/A char *line;
886N/A
886N/A if (!client_read(client))
886N/A return;
886N/A
886N/A /* @UNSAFE */
886N/A line = i_stream_next_line(client->input);
886N/A if (line == NULL)
886N/A return;
886N/A
886N/A if (strcmp(line, "*") == 0) {
886N/A sasl_server_auth_cancel(&client->common,
886N/A "Authentication aborted");
886N/A return;
886N/A }
886N/A
886N/A if (client->common.auth_request == NULL) {
886N/A sasl_server_auth_cancel(&client->common,
886N/A "Don't send unrequested data");
886N/A } else {
886N/A auth_client_request_continue(client->common.auth_request, line);
886N/A }
886N/A
886N/A /* clear sensitive data */
886N/A safe_memset(line, 0, strlen(line));
886N/A}
886N/A
886N/Astatic void sasl_callback(struct client *_client, enum sasl_server_reply reply,
886N/A const char *data)
886N/A{
886N/A struct pop3_client *client = (struct pop3_client *)_client;
886N/A struct const_iovec iov[3];
886N/A size_t data_len;
886N/A ssize_t ret;
886N/A
886N/A switch (reply) {
886N/A case SASL_SERVER_REPLY_SUCCESS:
886N/A client_send_line(client, "+OK Logged in.");
886N/A client_destroy(client, t_strconcat(
886N/A "Login: ", client->common.virtual_user, NULL));
886N/A break;
886N/A case SASL_SERVER_REPLY_AUTH_FAILED:
886N/A if (data == NULL)
886N/A client_send_line(client, "-ERR Authentication failed");
886N/A else {
886N/A client_send_line(client, t_strconcat(
886N/A "-ERR Authentication failed: ", data, NULL));
886N/A }
886N/A
886N/A /* get back to normal client input. */
886N/A if (client->io != NULL)
886N/A io_remove(client->io);
886N/A client->io = io_add(client->common.fd, IO_READ,
886N/A client_input, client);
886N/A break;
886N/A case SASL_SERVER_REPLY_MASTER_FAILED:
886N/A client_destroy(client, t_strconcat("Internal login failure: ",
886N/A client->common.virtual_user,
886N/A NULL));
886N/A break;
886N/A case SASL_SERVER_REPLY_CONTINUE:
886N/A data_len = strlen(data);
886N/A iov[0].iov_base = "+ ";
886N/A iov[0].iov_len = 2;
886N/A iov[1].iov_base = data;
886N/A iov[1].iov_len = data_len;
886N/A iov[2].iov_base = "\r\n";
886N/A iov[2].iov_len = 2;
886N/A
886N/A ret = o_stream_sendv(client->output, iov, 3);
886N/A if (ret < 0)
886N/A client_destroy(client, "Disconnected");
886N/A else if ((size_t)ret != 2 + data_len + 2)
886N/A client_destroy(client, "Transmit buffer full");
886N/A else {
886N/A /* continue */
886N/A return;
886N/A }
886N/A break;
886N/A }
886N/A
886N/A client_unref(client);
886N/A}
886N/A
886N/Aint cmd_auth(struct pop3_client *client, const char *args)
886N/A{
886N/A const struct auth_mech_desc *mech;
886N/A const char *mech_name, *p;
886N/A
886N/A if (*args == '\0') {
886N/A /* Old-style SASL discovery, used by MS Outlook */
886N/A int i, count;
886N/A client_send_line(client, "+OK");
886N/A mech = auth_client_get_available_mechs(auth_client, &count);
886N/A for (i = 0; i < count; i++) {
886N/A if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 &&
886N/A (client->secured || disable_plaintext_auth ||
886N/A (mech[i].flags & MECH_SEC_PLAINTEXT) == 0))
886N/A client_send_line(client, mech[i].name);
886N/A }
886N/A client_send_line(client, ".");
886N/A return TRUE;
886N/A }
886N/A
886N/A /* <mechanism name> <initial response> */
886N/A p = strchr(args, ' ');
886N/A if (p == NULL) {
886N/A mech_name = args;
886N/A args = "";
886N/A } else {
886N/A mech_name = t_strdup_until(args, p);
886N/A args = p+1;
886N/A }
886N/A
886N/A client_ref(client);
886N/A sasl_server_auth_begin(&client->common, "POP3", mech_name,
886N/A args, sasl_callback);
886N/A if (!client->common.authenticating)
886N/A return TRUE;
886N/A
886N/A /* following input data will go to authentication */
886N/A if (client->io != NULL)
886N/A io_remove(client->io);
886N/A client->io = io_add(client->common.fd, IO_READ,
886N/A client_auth_input, client);
886N/A return TRUE;
886N/A}
886N/A
886N/Aint cmd_user(struct pop3_client *client, const char *args)
886N/A{
886N/A if (!client->secured && disable_plaintext_auth) {
886N/A if (verbose_auth) {
886N/A client_syslog(&client->common, "Login failed: "
886N/A "Plaintext authentication disabled");
886N/A }
886N/A client_send_line(client,
886N/A "-ERR Plaintext authentication disabled.");
886N/A return TRUE;
886N/A }
886N/A
886N/A i_free(client->last_user);
886N/A client->last_user = i_strdup(args);
886N/A
886N/A client_send_line(client, "+OK");
886N/A return TRUE;
886N/A}
886N/A
886N/Aint cmd_pass(struct pop3_client *client, const char *args)
886N/A{
886N/A string_t *plain_login, *base64;
886N/A
886N/A if (client->last_user == NULL) {
886N/A client_send_line(client, "-ERR No username given.");
886N/A return TRUE;
886N/A }
886N/A
886N/A /* authorization ID \0 authentication ID \0 pass */
886N/A plain_login = t_str_new(128);
886N/A str_append_c(plain_login, '\0');
886N/A str_append(plain_login, client->last_user);
886N/A str_append_c(plain_login, '\0');
886N/A str_append(plain_login, args);
886N/A
886N/A base64 = buffer_create_dynamic(pool_datastack_create(),
886N/A MAX_BASE64_ENCODED_SIZE(plain_login->used));
886N/A base64_encode(plain_login->data, plain_login->used, base64);
886N/A
886N/A client_ref(client);
886N/A sasl_server_auth_begin(&client->common, "POP3", "PLAIN",
886N/A str_c(base64), sasl_callback);
886N/A if (!client->common.authenticating)
886N/A return TRUE;
886N/A
886N/A /* don't read any input from client until login is finished */
886N/A if (client->io != NULL) {
886N/A io_remove(client->io);
886N/A client->io = NULL;
886N/A }
886N/A return TRUE;
886N/A}
886N/A
886N/Aint cmd_apop(struct pop3_client *client, const char *args)
886N/A{
886N/A buffer_t *apop_data, *base64;
886N/A const char *p;
886N/A
886N/A if (client->apop_challenge == NULL) {
886N/A if (verbose_auth) {
886N/A client_syslog(&client->common,
886N/A "APOP failed: APOP not enabled");
886N/A }
886N/A client_send_line(client, "-ERR APOP not enabled.");
886N/A return TRUE;
886N/A }
886N/A
886N/A /* <username> <md5 sum in hex> */
886N/A p = strchr(args, ' ');
886N/A if (p == NULL || strlen(p+1) != 32) {
886N/A if (verbose_auth) {
886N/A client_syslog(&client->common,
886N/A "APOP failed: Invalid parameters");
886N/A }
886N/A client_send_line(client, "-ERR Invalid parameters.");
886N/A return TRUE;
886N/A }
886N/A
886N/A /* APOP challenge \0 username \0 APOP response */
886N/A apop_data = buffer_create_dynamic(pool_datastack_create(), 128);
886N/A buffer_append(apop_data, client->apop_challenge,
886N/A strlen(client->apop_challenge)+1);
886N/A buffer_append(apop_data, args, (size_t)(p-args));
886N/A buffer_append_c(apop_data, '\0');
886N/A
886N/A if (hex_to_binary(p+1, apop_data) < 0) {
886N/A if (verbose_auth) {
886N/A client_syslog(&client->common, "APOP failed: "
886N/A "Invalid characters in MD5 response");
886N/A }
886N/A client_send_line(client,
886N/A "-ERR Invalid characters in MD5 response.");
886N/A return TRUE;
886N/A }
886N/A
886N/A base64 = buffer_create_dynamic(pool_datastack_create(),
886N/A MAX_BASE64_ENCODED_SIZE(apop_data->used));
886N/A base64_encode(apop_data->data, apop_data->used, base64);
886N/A
886N/A client_ref(client);
886N/A sasl_server_auth_begin(&client->common, "POP3", "APOP",
886N/A str_c(base64), sasl_callback);
886N/A if (!client->common.authenticating)
886N/A return TRUE;
886N/A
886N/A /* don't read any input from client until login is finished */
886N/A if (client->io != NULL) {
886N/A io_remove(client->io);
886N/A client->io = NULL;
886N/A }
886N/A return TRUE;
886N/A}
886N/A