Mercurial > dovecot > original-hg > dovecot-1.2
view src/auth/mech-cram-md5.c @ 2708:f1e9f3ec8135 HEAD
Buffer API change: we no longer support limited sized buffers where
writes past limit wouldn't kill the process. They weren't used hardly
anywhere, they could have hidden bugs and the code for handling them was too
complex.
This also changed base64 and hex-binary APIs.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 08 Oct 2004 20:51:47 +0300 |
parents | cf94f1dc1ec5 |
children | 0f31778d3c34 |
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 "hmac-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) { unsigned 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>", (const char *) 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]; struct hmac_md5_context ctx; 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; hmac_md5_set_cram_context(&ctx, context_digest); hmac_md5_update(&ctx, auth->challenge, strlen(auth->challenge)); hmac_md5_final(&ctx, 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", get_log_prefix(&auth->auth_request)); } return FALSE; } return TRUE; } static int parse_cram_response(struct cram_auth_request *auth, const unsigned char *data, size_t size, const char **error_r) { size_t i, space; *error_r = NULL; /* <username> SPACE <response>. Username may contain spaces, so assume the rightmost space is the response separator. */ for (i = space = 0; i < size; i++) { if (data[i] == ' ') space = i; } if (space == 0) { *error_r = "missing digest"; return FALSE; } auth->username = p_strndup(auth->pool, data, space); space++; auth->response = p_strndup(auth->pool, data + space, size - space); return TRUE; } 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)) mech_auth_finish(request, NULL, 0, TRUE); else { if (verbose) { i_info("cram-md5(%s): authentication failed", get_log_prefix(&auth->auth_request)); } mech_auth_finish(request, NULL, 0, FALSE); } } static int mech_cram_md5_auth_continue(struct auth_request *auth_request, const unsigned char *data, size_t data_size, mech_callback_t *callback) { struct cram_auth_request *auth = (struct cram_auth_request *)auth_request; const char *error; if (parse_cram_response(auth, data, data_size, &error)) { auth_request->callback = callback; auth_request->user = p_strdup(auth_request->pool, auth->username); if (mech_fix_username(auth_request->user, &error)) { passdb->lookup_credentials(&auth->auth_request, PASSDB_CREDENTIALS_CRAM_MD5, credentials_callback); return TRUE; } } if (error == NULL) error = "authentication failed"; if (verbose) { i_info("cram-md5(%s): %s", get_log_prefix(&auth->auth_request), error); } /* failed */ mech_auth_finish(auth_request, NULL, 0, FALSE); return FALSE; } static int mech_cram_md5_auth_initial(struct auth_request *auth_request, struct auth_client_request_new *request, const unsigned char *data __attr_unused__, mech_callback_t *callback) { struct cram_auth_request *auth = (struct cram_auth_request *)auth_request; struct auth_client_request_reply reply; if (AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request)) { /* No initial response in CRAM-MD5 */ return FALSE; } auth->challenge = p_strdup(auth->pool, get_cram_challenge()); /* initialize reply */ mech_init_auth_client_reply(&reply); reply.id = request->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, auth_request->conn); return TRUE; } 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(void) { 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_initial = mech_cram_md5_auth_initial; auth->auth_request.auth_continue = mech_cram_md5_auth_continue; auth->auth_request.auth_free = mech_cram_md5_auth_free; return &auth->auth_request; } struct mech_module mech_cram_md5 = { "CRAM-MD5", MEMBER(plaintext) FALSE, MEMBER(advertise) TRUE, MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) TRUE, mech_cram_md5_auth_new };