mech-cram-md5.c revision 08d6658a4e2ec8104cd1307f6baa75fdb07a24f8
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen/* CRAM-MD5 SASL authentication, see RFC-2195
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen Joshua Goodall <joshua@roughtrade.net> */
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "auth-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 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
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenstatic bool verify_credentials(struct cram_auth_request *request,
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen const unsigned char *credentials, size_t size)
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen{
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
a7790b4f44c743a821fa065084fd6286767bfa8aTimo Sirainen unsigned char digest[MD5_RESULTLEN];
edf7428147facc11ddb43b9a874a99b96486d42dTimo Sirainen struct hmac_md5_context ctx;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen const char *response_hex;
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen if (size != CRAM_MD5_CONTEXTLEN) {
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen auth_request_log_error(&request->auth_request, "cram-md5",
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen "invalid credentials length");
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen return FALSE;
517d1e7142d57299c733b30423e35e7e1f8d01d6Timo Sirainen }
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen hmac_md5_set_cram_context(&ctx, credentials);
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen hmac_md5_update(&ctx, request->challenge, strlen(request->challenge));
edf7428147facc11ddb43b9a874a99b96486d42dTimo Sirainen hmac_md5_final(&ctx, digest);
edf7428147facc11ddb43b9a874a99b96486d42dTimo Sirainen
a7790b4f44c743a821fa065084fd6286767bfa8aTimo Sirainen response_hex = binary_to_hex(digest, sizeof(digest));
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen
a7790b4f44c743a821fa065084fd6286767bfa8aTimo Sirainen if (memcmp(response_hex, request->response, sizeof(digest)*2) != 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
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenstatic bool parse_cram_response(struct cram_auth_request *request,
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen const unsigned char *data, size_t size,
6ef7e31619edfaa17ed044b45861d106a86191efTimo 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
f968e62caa52a8924bd05ebf76ff515b5c18e17bTimo Sirainenstatic void credentials_callback(enum passdb_result result,
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen const unsigned char *credentials, size_t size,
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:
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen if (verify_credentials(request, credentials, size))
f968e62caa52a8924bd05ebf76ff515b5c18e17bTimo Sirainen auth_request_success(auth_request, NULL, 0);
f968e62caa52a8924bd05ebf76ff515b5c18e17bTimo Sirainen else
f968e62caa52a8924bd05ebf76ff515b5c18e17bTimo 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,
a8e132559a7ebe54c8269d79ce29fa3338c76199Timo Sirainen "CRAM-MD5", 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,
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainen const unsigned char *data ATTR_UNUSED,
43d32cbe60fdaef2699d99f1ca259053e9350411Timo 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
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
16f46efe0e090fe6975acf012a61a160f4787985Andrey Paninconst struct mech_module mech_cram_md5 = {
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen "CRAM-MD5",
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen MEMBER(flags) MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE,
ba8566b02903a4b00a39a611d19f421739a09456Timo Sirainen MEMBER(passdb_need) MECH_PASSDB_NEED_VERIFY_RESPONSE,
d1414c09cf0d58ac983054e2f4e1a1f329272dcfTimo Sirainen
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen mech_cram_md5_auth_new,
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen mech_cram_md5_auth_initial,
73bfdbe28c2ce6d143eadf0bab8ccfbe4cab0faeTimo Sirainen mech_cram_md5_auth_continue,
e70d5895795732b8247ab9abb045b438e954bc46Timo Sirainen mech_generic_auth_free
94a78eb438622fa53abef1e1726714dacad4b61cTimo Sirainen};