client-authenticate.c revision cd94aeaa294f7cc507206b4b2075852f00e14d61
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "common.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "base64.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "buffer.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "hex-binary.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "ioloop.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "istream.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "ostream.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "safe-memset.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "str.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "str-sanitize.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "auth-client.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "../pop3/capability.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "ssl-proxy.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "client.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "client-authenticate.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "pop3-proxy.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include <stdlib.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#define POP3_SERVICE_NAME "pop3"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenconst char *capability_string = POP3_CAPABILITY_REPLY;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenbool cmd_capa(struct pop3_client *client, const char *args ATTR_UNUSED)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const struct auth_mech_desc *mech;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int i, count;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen string_t *str;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str = t_str_new(128);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(str, "+OK\r\n");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(str, capability_string);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ssl_initialized && !client->common.tls)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(str, "STLS\r\n");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!disable_plaintext_auth || client->common.secured)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(str, "USER\r\n");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(str, "SASL");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mech = auth_client_get_available_mechs(auth_client, &count);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for (i = 0; i < count; i++) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* a) transport is secured
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen b) auth mechanism isn't plaintext
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen c) we allow insecure authentication
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (client->common.secured || !disable_plaintext_auth ||
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (mech[i].flags & MECH_SEC_PLAINTEXT) == 0)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append_c(str, ' ');
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(str, mech[i].name);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(str, "\r\n.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
9a02317c852face76737763fa6ec43b444688de5Timo Sirainen client_send_line(client, str_c(str));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void client_auth_input(struct pop3_client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen char *line;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!client_read(client))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* @UNSAFE */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen line = i_stream_next_line(client->common.input);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (line == NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (strcmp(line, "*") == 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen sasl_server_auth_client_error(&client->common,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Authentication aborted");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen auth_client_request_continue(client->common.auth_request, line);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen io_remove(&client->io);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* clear sensitive data */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen safe_memset(line, 0, strlen(line));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic bool client_handle_args(struct pop3_client *client,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *const *args, bool success)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *reason = NULL, *host = NULL, *destuser = NULL, *pass = NULL;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen string_t *reply;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int port = 110;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen bool proxy = FALSE, temp = FALSE, nologin = !success;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for (; *args != NULL; args++) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (strcmp(*args, "nologin") == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen nologin = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else if (strcmp(*args, "proxy") == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen proxy = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else if (strcmp(*args, "temp") == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen temp = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else if (strncmp(*args, "reason=", 7) == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen reason = *args + 7;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else if (strncmp(*args, "host=", 5) == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen host = *args + 5;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else if (strncmp(*args, "port=", 5) == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen port = atoi(*args + 5);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else if (strncmp(*args, "destuser=", 9) == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen destuser = *args + 9;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else if (strncmp(*args, "pass=", 5) == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen pass = *args + 5;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (destuser == NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen destuser = client->common.virtual_user;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (proxy &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen !login_proxy_is_ourself(&client->common, host, port, destuser)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* we want to proxy the connection to another server.
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen don't do this unless authentication succeeded. with
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen master user proxying we can get FAIL with proxy still set.
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen proxy host=.. [port=..] [destuser=..] pass=.. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!success)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (pop3_proxy_new(client, host, port, destuser, pass) < 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy_internal_failure(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!nologin)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen reply = t_str_new(128);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(reply, "-ERR ");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (reason != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(reply, reason);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else if (temp)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(reply, AUTH_TEMP_FAILED_MSG);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(reply, AUTH_FAILED_MSG);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, str_c(reply));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!client->destroyed) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* get back to normal client input. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->io != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen io_remove(&client->io);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->io = io_add(client->common.fd, IO_READ,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_input, client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void sasl_callback(struct client *_client, enum sasl_server_reply reply,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *data, const char *const *args)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct pop3_client *client = (struct pop3_client *)_client;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct const_iovec iov[3];
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *msg;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen size_t data_len;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(!client->destroyed ||
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen reply == SASL_SERVER_REPLY_CLIENT_ERROR ||
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen reply == SASL_SERVER_REPLY_MASTER_FAILED);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen switch (reply) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case SASL_SERVER_REPLY_SUCCESS:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (args != NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client_handle_args(client, args, TRUE))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen break;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, "+OK Logged in.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy_success(client, "Login");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen break;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case SASL_SERVER_REPLY_AUTH_FAILED:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case SASL_SERVER_REPLY_CLIENT_ERROR:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (args != NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client_handle_args(client, args, FALSE))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen break;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen msg = t_strconcat("-ERR ", data != NULL ?
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen data : AUTH_FAILED_MSG, NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, msg);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!client->destroyed) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* get back to normal client input. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->io != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen io_remove(&client->io);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->io = io_add(client->common.fd, IO_READ,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_input, client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen break;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case SASL_SERVER_REPLY_MASTER_FAILED:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (data == NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy_internal_failure(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen t_strconcat("-ERR [IN-USE] ", data, NULL));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy_success(client, data);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen break;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case SASL_SERVER_REPLY_CONTINUE:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen data_len = strlen(data);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen iov[0].iov_base = "+ ";
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen iov[0].iov_len = 2;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen iov[1].iov_base = data;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen iov[1].iov_len = data_len;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen iov[2].iov_base = "\r\n";
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen iov[2].iov_len = 2;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* don't check return value here. it gets tricky if we try
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen to call client_destroy() in here. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (void)o_stream_sendv(client->output, iov, 3);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(client->io == NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->io = io_add(client->common.fd, IO_READ,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_auth_input, client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_auth_input(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_unref(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenbool cmd_auth(struct pop3_client *client, const char *args)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const struct auth_mech_desc *mech;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *mech_name, *p;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (*args == '\0') {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* Old-style SASL discovery, used by MS Outlook */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int i, count;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, "+OK");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mech = auth_client_get_available_mechs(auth_client, &count);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for (i = 0; i < count; i++) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (client->common.secured || disable_plaintext_auth ||
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (mech[i].flags & MECH_SEC_PLAINTEXT) == 0))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, mech[i].name);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, ".");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* <mechanism name> <initial response> */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen p = strchr(args, ' ');
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (p == NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mech_name = args;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen args = "";
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mech_name = t_strdup_until(args, p);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen args = p+1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_ref(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen sasl_server_auth_begin(&client->common, POP3_SERVICE_NAME, mech_name,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen args, sasl_callback);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!client->common.authenticating)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* don't handle input until we get the initial auth reply */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->io != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen io_remove(&client->io);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainenstatic bool check_plaintext_auth(struct pop3_client *client)
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->common.secured || !disable_plaintext_auth)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (verbose_auth) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_syslog(&client->common, "Login failed: "
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen "Plaintext authentication disabled");
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen }
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen client_send_line(client, "-ERR "AUTH_PLAINTEXT_DISABLED_MSG);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->common.auth_tried_disabled_plaintext = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenbool cmd_user(struct pop3_client *client, const char *args)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!check_plaintext_auth(client))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_free(client->last_user);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->last_user = i_strdup(args);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, "+OK");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenbool cmd_pass(struct pop3_client *client, const char *args)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen string_t *plain_login, *base64;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->last_user == NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* client may ignore the USER reply and only display the error
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen message from PASS */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!check_plaintext_auth(client))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, "-ERR No username given.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* authorization ID \0 authentication ID \0 pass */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen plain_login = t_str_new(128);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append_c(plain_login, '\0');
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(plain_login, client->last_user);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append_c(plain_login, '\0');
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append(plain_login, args);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_free(client->last_user);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->last_user = NULL;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen base64 = buffer_create_dynamic(pool_datastack_create(),
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen MAX_BASE64_ENCODED_SIZE(plain_login->used));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen base64_encode(plain_login->data, plain_login->used, base64);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_ref(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen sasl_server_auth_begin(&client->common, POP3_SERVICE_NAME, "PLAIN",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_c(base64), sasl_callback);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!client->common.authenticating)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* don't read any input from client until login is finished */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->io != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen io_remove(&client->io);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenbool cmd_apop(struct pop3_client *client, const char *args)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen buffer_t *apop_data, *base64;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *p;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->apop_challenge == NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (verbose_auth) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_syslog(&client->common,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "APOP failed: APOP not enabled");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, "-ERR APOP not enabled.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* <username> <md5 sum in hex> */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen p = strchr(args, ' ');
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (p == NULL || strlen(p+1) != 32) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (verbose_auth) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_syslog(&client->common,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "APOP failed: Invalid parameters");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, "-ERR Invalid parameters.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
}
/* APOP challenge \0 username \0 APOP response */
apop_data = buffer_create_dynamic(pool_datastack_create(), 128);
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) {
if (verbose_auth) {
client_syslog(&client->common, "APOP failed: "
"Invalid characters in MD5 response");
}
client_send_line(client,
"-ERR Invalid characters in MD5 response.");
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_SERVICE_NAME, "APOP",
str_c(base64), sasl_callback);
if (!client->common.authenticating)
return TRUE;
/* don't read any input from client until login is finished */
if (client->io != NULL)
io_remove(&client->io);
return TRUE;
}