mech-cram-md5.c revision 50782de8a9d5ebe11ee61496b4e695a1d3875230
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw/* CRAM-MD5 SASL authentication, see RFC-2195
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw Joshua Goodall <joshua@roughtrade.net> */
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#include "auth-common.h"
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#include "ioloop.h"
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#include "buffer.h"
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#include "hex-binary.h"
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#include "hmac-md5.h"
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#include "randgen.h"
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#include "mech.h"
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#include "passdb.h"
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#include "hostpid.h"
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#include <stdlib.h>
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw#include <time.h>
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstruct cram_auth_request {
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw struct auth_request auth_request;
148c5f43199ca0b43fc8e3b643aab11cd66ea327Alan Wright
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw pool_t pool;
c586600796766c83eb9485c446886fd9ed2359a9Keyur Desai
68b2bbf26c7040fea4281dcb58b81e7627e46f34Gordon Ross /* requested: */
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw char *challenge;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
bbf6f00c25b6a2bed23c35eac6d62998ecdb338cJordan Brown /* received: */
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw char *username;
3db3f65c6274eb042354801a308c8e9bc4994553amw char *response;
bbf6f00c25b6a2bed23c35eac6d62998ecdb338cJordan Brown unsigned long maxbuf;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw};
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
a90cf9f29973990687fa61de9f1f6ea22e924e40Gordon Rossstatic const char *get_cram_challenge(void)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw{
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw unsigned char buf[17];
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw size_t i;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw random_fill(buf, sizeof(buf)-1);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw for (i = 0; i < sizeof(buf)-1; i++)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw buf[i] = (buf[i] % 10) + '0';
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw buf[sizeof(buf)-1] = '\0';
b1352070d318187b41b088da3533692976f3f225Alan Wright
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return t_strdup_printf("<%s.%s@%s>", (const char *)buf,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw dec2str(ioloop_time), my_hostname);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw}
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic bool verify_credentials(struct cram_auth_request *request,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw const unsigned char *credentials, size_t size)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw{
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States unsigned char digest[MD5_RESULTLEN];
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States struct hmac_md5_context ctx;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw const char *response_hex;
7b59d02d2a384be9a08087b14defadd214b3c1ddjb
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb if (size != CRAM_MD5_CONTEXTLEN) {
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb auth_request_log_error(&request->auth_request, "cram-md5",
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb "invalid credentials length");
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb return FALSE;
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb }
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb hmac_md5_set_cram_context(&ctx, credentials);
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb hmac_md5_update(&ctx, request->challenge, strlen(request->challenge));
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb hmac_md5_final(&ctx, digest);
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb response_hex = binary_to_hex(digest, sizeof(digest));
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb if (memcmp(response_hex, request->response, sizeof(digest)*2) != 0) {
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb auth_request_log_info(&request->auth_request, "cram-md5",
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw "password mismatch");
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return FALSE;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw }
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return TRUE;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw}
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic bool parse_cram_response(struct cram_auth_request *request,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw const unsigned char *data, size_t size,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw const char **error_r)
a90cf9f29973990687fa61de9f1f6ea22e924e40Gordon Ross{
a90cf9f29973990687fa61de9f1f6ea22e924e40Gordon Ross size_t i, space;
a90cf9f29973990687fa61de9f1f6ea22e924e40Gordon Ross
a90cf9f29973990687fa61de9f1f6ea22e924e40Gordon Ross *error_r = NULL;
a90cf9f29973990687fa61de9f1f6ea22e924e40Gordon Ross
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw /* <username> SPACE <response>. Username may contain spaces, so assume
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw the rightmost space is the response separator. */
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw for (i = space = 0; i < size; i++) {
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (data[i] == '\0') {
7b59d02d2a384be9a08087b14defadd214b3c1ddjb *error_r = "NULs in response";
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb return FALSE;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw }
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (data[i] == ' ')
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw space = i;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw }
dc20a3024900c47dd2ee44b9707e6df38f7d62a5as
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb if (space == 0) {
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw *error_r = "missing digest";
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return FALSE;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw }
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw request->username = p_strndup(request->pool, data, space);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw space++;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw request->response =
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw p_strndup(request->pool, data + space, size - space);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw return TRUE;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw}
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic void credentials_callback(enum passdb_result result,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw const unsigned char *credentials, size_t size,
9fb67ea305c66b6a297583b9b0db6796b0dfe497afshin salek ardakani - Sun Microsystems - Irvine United States struct auth_request *auth_request)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw{
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw struct cram_auth_request *request =
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw (struct cram_auth_request *)auth_request;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw switch (result) {
7f3ef643e446c82e27a9386991b140b128baf22cGordon Ross case PASSDB_RESULT_OK:
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (verify_credentials(request, credentials, size))
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw auth_request_success(auth_request, NULL, 0);
dc20a3024900c47dd2ee44b9707e6df38f7d62a5as else
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb auth_request_fail(auth_request);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw break;
7f3ef643e446c82e27a9386991b140b128baf22cGordon Ross case PASSDB_RESULT_INTERNAL_FAILURE:
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw auth_request_internal_failure(auth_request);
dc20a3024900c47dd2ee44b9707e6df38f7d62a5as break;
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb default:
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw auth_request_fail(auth_request);
7f3ef643e446c82e27a9386991b140b128baf22cGordon Ross break;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw }
dc20a3024900c47dd2ee44b9707e6df38f7d62a5as}
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic void
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwmech_cram_md5_auth_continue(struct auth_request *auth_request,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw const unsigned char *data, size_t data_size)
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw{
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw struct cram_auth_request *request =
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw (struct cram_auth_request *)auth_request;
ccc71be50bb49efb4e31004c77fb3e065e9c0596Gordon Ross const char *error;
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (parse_cram_response(request, data, data_size, &error)) {
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (auth_request_set_username(auth_request, request->username,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw &error)) {
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw auth_request_lookup_credentials(auth_request,
7b59d02d2a384be9a08087b14defadd214b3c1ddjb "CRAM-MD5", credentials_callback);
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb return;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw }
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw }
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw if (error == NULL)
dc20a3024900c47dd2ee44b9707e6df38f7d62a5as error = "authentication failed";
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw auth_request_log_info(auth_request, "cram-md5", "%s", error);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw auth_request_fail(auth_request);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw}
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwstatic void
7b59d02d2a384be9a08087b14defadd214b3c1ddjbmech_cram_md5_auth_initial(struct auth_request *auth_request,
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb const unsigned char *data ATTR_UNUSED,
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb size_t data_size ATTR_UNUSED)
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb{
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb struct cram_auth_request *request =
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb (struct cram_auth_request *)auth_request;
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb request->challenge = p_strdup(request->pool, get_cram_challenge());
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb auth_request_handler_reply_continue(auth_request, request->challenge,
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb strlen(request->challenge));
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb}
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb
faa1795a28a5c712eed6d0a3f84d98c368a316c6jbstatic struct auth_request *mech_cram_md5_auth_new(void)
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb{
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb struct cram_auth_request *request;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw pool_t pool;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw pool = pool_alloconly_create("cram_md5_auth_request", 2048);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw request = p_new(pool, struct cram_auth_request, 1);
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw request->pool = pool;
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw request->auth_request.pool = pool;
dc20a3024900c47dd2ee44b9707e6df38f7d62a5as return &request->auth_request;
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb}
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amwconst struct mech_module mech_cram_md5 = {
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw "CRAM-MD5",
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw
dc20a3024900c47dd2ee44b9707e6df38f7d62a5as .flags = MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw .passdb_need = MECH_PASSDB_NEED_VERIFY_RESPONSE,
faa1795a28a5c712eed6d0a3f84d98c368a316c6jb
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw mech_cram_md5_auth_new,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw mech_cram_md5_auth_initial,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw mech_cram_md5_auth_continue,
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw mech_generic_auth_free
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw};
da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0amw