Mercurial > dovecot > original-hg > dovecot-1.2
view src/auth/mech-cram-md5.c @ 1873:ed5e808d934f HEAD
CRAM-MD5 mechanism by Joshua Goodall, plus some cleanups.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 10 Nov 2003 22:36:02 +0200 |
parents | |
children | 445fc3dfecad |
line wrap: on
line source
/* Copyright (C) 2002,2003 Timo Sirainen / Joshua Goodall */ /* CRAM-MD5 SASL authentication, see RFC-2195 Joshua Goodall <joshua@roughtrade.net> */ #include "common.h" #include "ioloop.h" #include "buffer.h" #include "hex-binary.h" #include "md5.h" #include "randgen.h" #include "mech.h" #include "passdb.h" #include "hostpid.h" #include <stdlib.h> #include <time.h> struct cram_auth_request { struct auth_request auth_request; pool_t pool; /* requested: */ char *challenge; /* received: */ char *username; char *response; unsigned long maxbuf; }; static const char *get_cram_challenge(void) { char buf[17]; size_t i; hostpid_init(); random_fill(buf, sizeof(buf)-1); for (i = 0; i < sizeof(buf)-1; i++) buf[i] = (buf[i] % 10) + '0'; buf[sizeof(buf)-1] = '\0'; return t_strdup_printf("%s.%s@%s", buf, dec2str(ioloop_time), my_hostname); } static int verify_credentials(struct cram_auth_request *auth, const char *credentials) { unsigned char digest[16], context_digest[32], *cdp; struct md5_context ctxo, ctxi; buffer_t *context_digest_buf; const char *response_hex; if (credentials == NULL) return FALSE; context_digest_buf = buffer_create_data(pool_datastack_create(), context_digest, sizeof(context_digest)); if (hex_to_binary(credentials, context_digest_buf) <= 0) return FALSE; #define CDGET(p, c) STMT_START { \ (c) = (*p++); \ (c) += (*p++ << 8); \ (c) += (*p++ << 16); \ (c) += (*p++ << 24); \ } STMT_END cdp = context_digest; CDGET(cdp, ctxo.a); CDGET(cdp, ctxo.b); CDGET(cdp, ctxo.c); CDGET(cdp, ctxo.d); CDGET(cdp, ctxi.a); CDGET(cdp, ctxi.b); CDGET(cdp, ctxi.c); CDGET(cdp, ctxi.d); ctxo.lo = ctxi.lo = 64; ctxo.hi = ctxi.hi = 0; md5_update(&ctxi, auth->challenge, strlen(auth->challenge)); md5_final(&ctxi, digest); md5_update(&ctxo, digest, 16); md5_final(&ctxo, digest); response_hex = binary_to_hex(digest, 16); if (memcmp(response_hex, auth->response, 32) != 0) { if (verbose) { i_info("cram-md5(%s): password mismatch", auth->username); } return FALSE; } return TRUE; } static int parse_cram_response(struct cram_auth_request *auth, const char *data, const char **error) { char *digest; int failed; *error = NULL; failed = FALSE; digest = strchr(data, ' '); if (digest != NULL) { auth->username = p_strdup_until(auth->pool, data, digest); digest++; auth->response = p_strdup(auth->pool, digest); } else { *error = "missing digest"; failed = TRUE; } return !failed; } static void credentials_callback(const char *result, struct auth_request *request) { struct cram_auth_request *auth = (struct cram_auth_request *) request; if (verify_credentials(auth, result)) { if (verbose) { i_info("cram-md5(%s): authenticated", auth->username == NULL ? "" : auth->username); } mech_auth_finish(request, NULL, 0, TRUE); } else { if (verbose) { i_info("cram-md5(%s): authentication failed", auth->username == NULL ? "" : auth->username); } mech_auth_finish(request, NULL, 0, FALSE); } } static int mech_cram_md5_auth_continue(struct auth_request *auth_request, struct auth_client_request_continue *request, const unsigned char *data, mech_callback_t *callback) { struct cram_auth_request *auth = (struct cram_auth_request *)auth_request; const char *error; /* unused */ (void)request; if (parse_cram_response(auth, (const char *) data, &error)) { auth_request->callback = callback; auth_request->user = p_strdup(auth_request->pool, auth->username); if (mech_is_valid_username(auth_request->user)) { passdb->lookup_credentials(&auth->auth_request, PASSDB_CREDENTIALS_CRAM_MD5, credentials_callback); return TRUE; } error = "invalid username"; } if (error == NULL) error = "authentication failed"; if (verbose) { i_info("cram-md5(%s): %s", auth->username == NULL ? "" : auth->username, error); } /* failed */ mech_auth_finish(auth_request, NULL, 0, FALSE); return FALSE; } static void mech_cram_md5_auth_free(struct auth_request *auth_request) { pool_unref(auth_request->pool); } static struct auth_request * mech_cram_md5_auth_new(struct auth_client_connection *conn, unsigned int id, mech_callback_t *callback) { struct auth_client_request_reply reply; struct cram_auth_request *auth; pool_t pool; pool = pool_alloconly_create("cram_md5_auth_request", 2048); auth = p_new(pool, struct cram_auth_request, 1); auth->pool = pool; auth->auth_request.refcount = 1; auth->auth_request.pool = pool; auth->auth_request.auth_continue = mech_cram_md5_auth_continue; auth->auth_request.auth_free = mech_cram_md5_auth_free; auth->challenge = p_strdup(auth->pool, get_cram_challenge()); /* initialize reply */ mech_init_auth_client_reply(&reply); reply.id = id; reply.result = AUTH_CLIENT_RESULT_CONTINUE; /* send the initial challenge */ reply.reply_idx = 0; reply.data_size = strlen(auth->challenge); callback(&reply, auth->challenge, conn); return &auth->auth_request; } struct mech_module mech_cram_md5 = { AUTH_MECH_CRAM_MD5, mech_cram_md5_auth_new };