mech-cram-md5.c revision 4ac5448461b63de9637de839fbc611a3d503287c
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"
edf7428147facc11ddb43b9a874a99b96486d42dTimo Sirainen#include "hmac-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{
6c07b8ddc5e894feead4d422075b079451721241Timo Sirainen unsigned 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
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen return t_strdup_printf("<%s.%s@%s>", (const char *)buf,
6c07b8ddc5e894feead4d422075b079451721241Timo Sirainen dec2str(ioloop_time), my_hostname);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen}
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainenstatic int verify_credentials(struct cram_auth_request *request,
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen const char *credentials)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen{
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
edf7428147facc11ddb43b9a874a99b96486d42dTimo Sirainen unsigned char digest[16], context_digest[32];
edf7428147facc11ddb43b9a874a99b96486d42dTimo Sirainen struct hmac_md5_context ctx;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen buffer_t *context_digest_buf;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen const char *response_hex;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen context_digest_buf =
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen buffer_create_data(pool_datastack_create(),
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen context_digest, sizeof(context_digest));
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen if (hex_to_binary(credentials, context_digest_buf) < 0) {
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen auth_request_log_error(&request->auth_request, "cram-md5",
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen "passdb credentials are not in hex");
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen return FALSE;
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen }
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
edf7428147facc11ddb43b9a874a99b96486d42dTimo Sirainen hmac_md5_set_cram_context(&ctx, context_digest);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen hmac_md5_update(&ctx, request->challenge, strlen(request->challenge));
edf7428147facc11ddb43b9a874a99b96486d42dTimo Sirainen hmac_md5_final(&ctx, digest);
edf7428147facc11ddb43b9a874a99b96486d42dTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen response_hex = binary_to_hex(digest, 16);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen if (memcmp(response_hex, request->response, 32) != 0) {
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen auth_request_log_info(&request->auth_request, "cram-md5",
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen "password mismatch");
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen return FALSE;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen }
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen return TRUE;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen}
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainenstatic int parse_cram_response(struct cram_auth_request *request,
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen const unsigned char *data, size_t size,
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen const char **error_r)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen{
3a734c9c3efd87864d1ad860826fd4e787457bedTimo Sirainen size_t i, space;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen *error_r = NULL;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
3a734c9c3efd87864d1ad860826fd4e787457bedTimo Sirainen /* <username> SPACE <response>. Username may contain spaces, so assume
3a734c9c3efd87864d1ad860826fd4e787457bedTimo Sirainen the rightmost space is the response separator. */
3a734c9c3efd87864d1ad860826fd4e787457bedTimo Sirainen for (i = space = 0; i < size; i++) {
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen if (data[i] == ' ')
3a734c9c3efd87864d1ad860826fd4e787457bedTimo Sirainen space = i;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen }
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
3a734c9c3efd87864d1ad860826fd4e787457bedTimo Sirainen if (space == 0) {
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen *error_r = "missing digest";
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen return FALSE;
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen }
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request->username = p_strndup(request->pool, data, space);
3a734c9c3efd87864d1ad860826fd4e787457bedTimo Sirainen space++;
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request->response =
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen p_strndup(request->pool, data + space, size - space);
244fcb971a4a38b476f733bfd5ed5d18b2c831f7Timo Sirainen return TRUE;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen}
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainenstatic void credentials_callback(enum passdb_result result,
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen const char *credentials,
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen struct auth_request *auth_request)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen{
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen struct cram_auth_request *request =
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen (struct cram_auth_request *)auth_request;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen switch (result) {
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen case PASSDB_RESULT_OK:
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen if (verify_credentials(request, credentials))
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen auth_request_success(auth_request, NULL, 0);
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen else
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen auth_request_fail(auth_request);
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen break;
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen case PASSDB_RESULT_INTERNAL_FAILURE:
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen auth_request_internal_failure(auth_request);
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen break;
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen default:
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen auth_request_fail(auth_request);
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen break;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen }
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen}
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainenstatic void
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainenmech_cram_md5_auth_continue(struct auth_request *auth_request,
78ed6a99e980228a75fa59cff84327dc0ea82857Timo Sirainen const unsigned char *data, size_t data_size)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen{
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen struct cram_auth_request *request =
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen (struct cram_auth_request *)auth_request;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen const char *error;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen if (parse_cram_response(request, data, data_size, &error)) {
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen if (auth_request_set_username(auth_request, request->username,
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen &error)) {
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen auth_request_lookup_credentials(auth_request,
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen PASSDB_CREDENTIALS_CRAM_MD5,
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen credentials_callback);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen return;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen }
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen }
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen if (error == NULL)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen error = "authentication failed";
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen auth_request_log_info(auth_request, "cram-md5", "%s", error);
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen auth_request_fail(auth_request);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen}
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainenstatic void
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainenmech_cram_md5_auth_initial(struct auth_request *auth_request,
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen const unsigned char *data __attr_unused__,
78ed6a99e980228a75fa59cff84327dc0ea82857Timo Sirainen size_t data_size __attr_unused__)
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen{
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen struct cram_auth_request *request =
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen (struct cram_auth_request *)auth_request;
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request->challenge = p_strdup(request->pool, get_cram_challenge());
78ed6a99e980228a75fa59cff84327dc0ea82857Timo Sirainen auth_request->callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE,
78ed6a99e980228a75fa59cff84327dc0ea82857Timo Sirainen request->challenge, strlen(request->challenge));
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen}
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainenstatic void mech_cram_md5_auth_free(struct auth_request *request)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen{
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen pool_unref(request->pool);
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen}
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
4ac5448461b63de9637de839fbc611a3d503287cTimo Sirainenstatic struct auth_request *mech_cram_md5_auth_new(void)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen{
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen struct cram_auth_request *request;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen pool_t pool;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen pool = pool_alloconly_create("cram_md5_auth_request", 2048);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request = p_new(pool, struct cram_auth_request, 1);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request->pool = pool;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen request->auth_request.pool = pool;
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen return &request->auth_request;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen}
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainenstruct mech_module mech_cram_md5 = {
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen "CRAM-MD5",
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen MEMBER(flags) MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE,
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen MEMBER(passdb_need_plain) FALSE,
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen MEMBER(passdb_need_credentials) TRUE,
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen mech_cram_md5_auth_new,
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen mech_cram_md5_auth_initial,
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen mech_cram_md5_auth_continue,
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen mech_cram_md5_auth_free
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen};