mech-cram-md5.c revision 244fcb971a4a38b476f733bfd5ed5d18b2c831f7
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen/* Copyright (C) 2002,2003 Timo Sirainen / Joshua Goodall */
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen/* CRAM-MD5 SASL authentication, see RFC-2195
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen Joshua Goodall <joshua@roughtrade.net> */
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#include "common.h"
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#include "ioloop.h"
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#include "buffer.h"
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#include "hex-binary.h"
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#include "md5.h"
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#include "randgen.h"
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#include "mech.h"
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#include "passdb.h"
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#include "hostpid.h"
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#include <stdlib.h>
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#include <time.h>
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainenstruct cram_auth_request {
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen struct auth_request auth_request;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen pool_t pool;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen /* requested: */
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen char *challenge;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen /* received: */
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen char *username;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen char *response;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen unsigned long maxbuf;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen};
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainenstatic const char *get_cram_challenge(void)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen{
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen char buf[17];
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen size_t i;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen hostpid_init();
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen random_fill(buf, sizeof(buf)-1);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen for (i = 0; i < sizeof(buf)-1; i++)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen buf[i] = (buf[i] % 10) + '0';
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen buf[sizeof(buf)-1] = '\0';
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen return t_strdup_printf("%s.%s@%s", buf, dec2str(ioloop_time),
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen my_hostname);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen}
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainenstatic int verify_credentials(struct cram_auth_request *auth,
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen const char *credentials)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen{
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen unsigned char digest[16], context_digest[32], *cdp;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen struct md5_context ctxo, ctxi;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen buffer_t *context_digest_buf;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen const char *response_hex;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen if (credentials == NULL)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen return FALSE;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen context_digest_buf =
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen buffer_create_data(pool_datastack_create(),
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen context_digest, sizeof(context_digest));
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen if (hex_to_binary(credentials, context_digest_buf) <= 0)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen return FALSE;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen#define CDGET(p, c) STMT_START { \
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen (c) = (*p++); \
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen (c) += (*p++ << 8); \
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen (c) += (*p++ << 16); \
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen (c) += (*p++ << 24); \
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen} STMT_END
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen cdp = context_digest;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen CDGET(cdp, ctxo.a);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen CDGET(cdp, ctxo.b);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen CDGET(cdp, ctxo.c);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen CDGET(cdp, ctxo.d);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen CDGET(cdp, ctxi.a);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen CDGET(cdp, ctxi.b);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen CDGET(cdp, ctxi.c);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen CDGET(cdp, ctxi.d);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen ctxo.lo = ctxi.lo = 64;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen ctxo.hi = ctxi.hi = 0;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen md5_update(&ctxi, auth->challenge, strlen(auth->challenge));
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen md5_final(&ctxi, digest);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen md5_update(&ctxo, digest, 16);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen md5_final(&ctxo, digest);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen response_hex = binary_to_hex(digest, 16);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen if (memcmp(response_hex, auth->response, 32) != 0) {
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen if (verbose) {
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen i_info("cram-md5(%s): password mismatch",
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen auth->username);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen }
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen return FALSE;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen }
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen return TRUE;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen}
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainenstatic int parse_cram_response(struct cram_auth_request *auth,
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen const unsigned char *data, size_t size,
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen const char **error_r)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen{
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen size_t i;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen *error_r = NULL;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen for (i = 0; i < size; i++) {
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen if (data[i] == ' ')
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen break;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen }
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen if (i == size) {
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen *error_r = "missing digest";
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen return FALSE;
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen }
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen auth->username = p_strndup(auth->pool, data, i);
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen i++;
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen auth->response = p_strndup(auth->pool, data + i, size - i);
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen return TRUE;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen}
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainenstatic void credentials_callback(const char *result,
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen struct auth_request *request)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen{
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen struct cram_auth_request *auth =
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen (struct cram_auth_request *) request;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen if (verify_credentials(auth, result)) {
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen if (verbose) {
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen i_info("cram-md5(%s): authenticated",
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen auth->username == NULL ? "" : auth->username);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen }
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen mech_auth_finish(request, NULL, 0, TRUE);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen } else {
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen if (verbose) {
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen i_info("cram-md5(%s): authentication failed",
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen auth->username == NULL ? "" : auth->username);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen }
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen mech_auth_finish(request, NULL, 0, FALSE);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen }
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen}
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainenstatic int
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainenmech_cram_md5_auth_continue(struct auth_request *auth_request,
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen struct auth_client_request_continue *request __attr_unused__,
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen const unsigned char *data,
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen mech_callback_t *callback)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen{
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen struct cram_auth_request *auth =
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen (struct cram_auth_request *)auth_request;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen const char *error;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen if (parse_cram_response(auth, data, request->data_size, &error)) {
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen auth_request->callback = callback;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen auth_request->user =
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen p_strdup(auth_request->pool, auth->username);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen if (mech_is_valid_username(auth_request->user)) {
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen passdb->lookup_credentials(&auth->auth_request,
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen PASSDB_CREDENTIALS_CRAM_MD5,
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen credentials_callback);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen return TRUE;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen }
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen error = "invalid username";
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen }
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen if (error == NULL)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen error = "authentication failed";
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen if (verbose) {
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen i_info("cram-md5(%s): %s",
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen auth->username == NULL ? "" : auth->username, error);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen }
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen /* failed */
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen mech_auth_finish(auth_request, NULL, 0, FALSE);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen return FALSE;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen}
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainenstatic void mech_cram_md5_auth_free(struct auth_request *auth_request)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen{
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen pool_unref(auth_request->pool);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen}
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainenstatic struct auth_request *
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainenmech_cram_md5_auth_new(struct auth_client_connection *conn,
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen unsigned int id, mech_callback_t *callback)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen{
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen struct auth_client_request_reply reply;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen struct cram_auth_request *auth;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen pool_t pool;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen pool = pool_alloconly_create("cram_md5_auth_request", 2048);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen auth = p_new(pool, struct cram_auth_request, 1);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen auth->pool = pool;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen auth->auth_request.refcount = 1;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen auth->auth_request.pool = pool;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen auth->auth_request.auth_continue = mech_cram_md5_auth_continue;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen auth->auth_request.auth_free = mech_cram_md5_auth_free;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen auth->challenge = p_strdup(auth->pool, get_cram_challenge());
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen /* initialize reply */
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen mech_init_auth_client_reply(&reply);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen reply.id = id;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen reply.result = AUTH_CLIENT_RESULT_CONTINUE;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen /* send the initial challenge */
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen reply.reply_idx = 0;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen reply.data_size = strlen(auth->challenge);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen callback(&reply, auth->challenge, conn);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen return &auth->auth_request;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen}
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainenstruct mech_module mech_cram_md5 = {
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen AUTH_MECH_CRAM_MD5,
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen mech_cram_md5_auth_new
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen};