[dovecot-cvs] dovecot/src/auth mech-cram-md5.c,NONE,1.1 password-scheme-cram-md5.c,NONE,1.1 password-scheme-md5crypt.c,NONE,1.1 Makefile.am,1.22,1.23 auth-client-interface.h,1.1,1.2 auth-mech-desc.h,1.2,1.3 mech-digest-md5.c,1.16,1.17 mech.c,1.16,1.17 passdb.c,1.11,1.12 passdb.h,1.7,1.8 password-scheme.c,1.3,1.4 password-scheme.h,1.1,1.2 md5crypt.c,1.3,NONE md5crypt.h,1.2,NONE

cras at procontrol.fi cras at procontrol.fi
Mon Nov 10 22:36:05 EET 2003


Update of /home/cvs/dovecot/src/auth
In directory danu:/tmp/cvs-serv20150

Modified Files:
	Makefile.am auth-client-interface.h auth-mech-desc.h 
	mech-digest-md5.c mech.c passdb.c passdb.h password-scheme.c 
	password-scheme.h 
Added Files:
	mech-cram-md5.c password-scheme-cram-md5.c 
	password-scheme-md5crypt.c 
Removed Files:
	md5crypt.c md5crypt.h 
Log Message:
CRAM-MD5 mechanism by Joshua Goodall, plus some cleanups.



--- NEW FILE: mech-cram-md5.c ---
/* Copyright (C) 2002,2003 Timo Sirainen / Joshua Goodall */

/* CRAM-MD5 SASL authentication, see RFC-2195
   Joshua Goodall <joshua at 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
};

--- NEW FILE: password-scheme-cram-md5.c ---
/* Copyright (C) 2003 Timo Sirainen */

#include "lib.h"
#include "md5.h"
#include "hex-binary.h"
#include "password-scheme.h"

const char *password_generate_cram_md5(const char *plaintext)
{
	unsigned char digest[16], ipad[64], opad[64], context_digest[32], *cdp;
	struct md5_context ctxo, ctxi;
	size_t len;
	int i;

	memset(ipad, 0, sizeof(ipad));
	memset(opad, 0, sizeof(opad));

	/* Hash excessively long passwords */
	len = strlen(plaintext);
	if (len > 64) {
		md5_get_digest(plaintext, len, digest);
		memcpy(ipad, digest, 16);
		memcpy(opad, digest, 16);
	} else {
		memcpy(ipad, plaintext, len);
		memcpy(opad, plaintext, len);
	}

	/* ipad/opad operation */
	for (i = 0; i < 64; i++) {
		ipad[i] ^= 0x36;
		opad[i] ^= 0x5c;
	}

	md5_init(&ctxi);
	md5_init(&ctxo);
	md5_update(&ctxi, ipad, 64);
	md5_update(&ctxo, opad, 64);

	/* Make HMAC-MD5 hex digest */
#define CDPUT(p, c) STMT_START {   \
	*(p)++ = (c) & 0xff;       \
	*(p)++ = (c) >> 8 & 0xff;  \
	*(p)++ = (c) >> 16 & 0xff; \
	*(p)++ = (c) >> 24 & 0xff; \
} STMT_END
	cdp = context_digest;
	CDPUT(cdp, ctxo.a);
	CDPUT(cdp, ctxo.b);
	CDPUT(cdp, ctxo.c);
	CDPUT(cdp, ctxo.d);
	CDPUT(cdp, ctxi.a);
	CDPUT(cdp, ctxi.b);
	CDPUT(cdp, ctxi.c);
	CDPUT(cdp, ctxi.d);

	return binary_to_hex(context_digest, sizeof(context_digest));
}

--- NEW FILE: password-scheme-md5crypt.c ---
/*
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * <phk at login.dknet.dk> wrote this file.  As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
 * ----------------------------------------------------------------------------
 */

/*
 * Ported from FreeBSD to Linux, only minimal changes.  --marekm
 */

/*
 * Adapted from shadow-19990607 by Tudor Bosman, tudorb at jm.nu
 */

#include "lib.h"
#include "safe-memset.h"
#include "str.h"
#include "md5.h"
#include "password-scheme.h"

static unsigned char itoa64[] =		/* 0 ... 63 => ascii - 64 */
	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

static char	*magic = "$1$";	/*
				 * This string is magic for
				 * this algorithm.  Having
				 * it this way, we can get
				 * get better later on
				 */

static void
to64(string_t *str, unsigned long v, int n)
{
	while (--n >= 0) {
		str_append_c(str, itoa64[v&0x3f]);
		v >>= 6;
	}
}

/*
 * UNIX password
 *
 * Use MD5 for what it is best at...
 */

const char *password_generate_md5_crypt(const char *pw, const char *salt)
{
	const char *sp,*ep;
	unsigned char	final[16];
	int sl,pl,i,j;
	struct md5_context ctx,ctx1;
	unsigned long l;
	string_t *passwd;
	size_t pw_len = strlen(pw);

	/* Refine the Salt first */
	sp = salt;

	/* If it starts with the magic string, then skip that */
	if (strncmp(sp, magic, sizeof(magic)-1) == 0)
		sp += sizeof(magic)-1;

	/* It stops at the first '$', max 8 chars */
	for(ep=sp;*ep && *ep != '$' && ep < (sp+8);ep++)
		continue;

	/* get the length of the true salt */
	sl = ep - sp;

	md5_init(&ctx);

	/* The password first, since that is what is most unknown */
	md5_update(&ctx,pw,pw_len);

	/* Then our magic string */
	md5_update(&ctx,magic,sizeof(magic)-1);

	/* Then the raw salt */
	md5_update(&ctx,sp,sl);

	/* Then just as many characters of the MD5(pw,salt,pw) */
	md5_init(&ctx1);
	md5_update(&ctx1,pw,pw_len);
	md5_update(&ctx1,sp,sl);
	md5_update(&ctx1,pw,pw_len);
	md5_final(&ctx1,final);
	for(pl = pw_len; pl > 0; pl -= 16)
		md5_update(&ctx,final,pl>16 ? 16 : pl);

	/* Don't leave anything around in vm they could use. */
	safe_memset(final, 0, sizeof(final));

	/* Then something really weird... */
	for (j=0,i = pw_len; i ; i >>= 1)
		if(i&1)
		    md5_update(&ctx, final+j, 1);
		else
		    md5_update(&ctx, pw+j, 1);

	/* Now make the output string */
	passwd = t_str_new(sl + 64);
	str_append(passwd, magic);
	str_append_n(passwd, sp, sl);
	str_append_c(passwd, '$');

	md5_final(&ctx,final);

	/*
	 * and now, just to make sure things don't run too fast
	 * On a 60 Mhz Pentium this takes 34 msec, so you would
	 * need 30 seconds to build a 1000 entry dictionary...
	 */
	for(i=0;i<1000;i++) {
		md5_init(&ctx1);
		if(i & 1)
			md5_update(&ctx1,pw,pw_len);
		else
			md5_update(&ctx1,final,16);

		if(i % 3)
			md5_update(&ctx1,sp,sl);

		if(i % 7)
			md5_update(&ctx1,pw,pw_len);

		if(i & 1)
			md5_update(&ctx1,final,16);
		else
			md5_update(&ctx1,pw,pw_len);
		md5_final(&ctx1,final);
	}

	l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(passwd,l,4);
	l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(passwd,l,4);
	l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(passwd,l,4);
	l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(passwd,l,4);
	l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(passwd,l,4);
	l =                    final[11]                ; to64(passwd,l,2);

	/* Don't leave anything around in vm they could use. */
	safe_memset(final, 0, sizeof(final));

	return str_c(passwd);
}

Index: Makefile.am
===================================================================
RCS file: /home/cvs/dovecot/src/auth/Makefile.am,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -d -r1.22 -r1.23
--- Makefile.am	29 Oct 2003 14:10:20 -0000	1.22
+++ Makefile.am	10 Nov 2003 20:36:02 -0000	1.23
@@ -23,11 +23,11 @@
 	db-pgsql.c \
 	db-passwd-file.c \
 	main.c \
-	md5crypt.c \
 	mech.c \
 	mech-anonymous.c \
 	mech-cyrus-sasl2.c \
 	mech-plain.c \
+	mech-cram-md5.c \
 	mech-digest-md5.c \
 	mycrypt.c \
 	passdb.c \
@@ -40,6 +40,8 @@
 	passdb-vpopmail.c \
 	passdb-pgsql.c \
 	password-scheme.c \
+	password-scheme-md5crypt.c \
+	password-scheme-cram-md5.c \
 	userdb.c \
 	userdb-ldap.c \
 	userdb-passwd.c \
@@ -59,7 +61,6 @@
 	db-pgsql.h \
 	db-passwd-file.h \
 	common.h \
-	md5crypt.h \
 	mech.h \
 	mycrypt.h \
 	passdb.h \

Index: auth-client-interface.h
===================================================================
RCS file: /home/cvs/dovecot/src/auth/auth-client-interface.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- auth-client-interface.h	22 Aug 2003 02:42:13 -0000	1.1
+++ auth-client-interface.h	10 Nov 2003 20:36:02 -0000	1.2
@@ -12,6 +12,7 @@
 	AUTH_MECH_PLAIN		= 0x01,
 	AUTH_MECH_DIGEST_MD5	= 0x02,
 	AUTH_MECH_ANONYMOUS	= 0x04,
+	AUTH_MECH_CRAM_MD5	= 0x08,
 
 	AUTH_MECH_COUNT
 };

Index: auth-mech-desc.h
===================================================================
RCS file: /home/cvs/dovecot/src/auth/auth-mech-desc.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- auth-mech-desc.h	8 May 2003 03:24:57 -0000	1.2
+++ auth-mech-desc.h	10 Nov 2003 20:36:02 -0000	1.3
@@ -10,6 +10,7 @@
 
 static struct auth_mech_desc auth_mech_desc[AUTH_MECH_COUNT] = {
 	{ AUTH_MECH_PLAIN,		"PLAIN",	TRUE, FALSE },
+	{ AUTH_MECH_CRAM_MD5,		"CRAM-MD5",	FALSE, TRUE },
 	{ AUTH_MECH_DIGEST_MD5,		"DIGEST-MD5",	FALSE, TRUE },
 	{ AUTH_MECH_ANONYMOUS,		"ANONYMOUS",	FALSE, TRUE }
 };

Index: mech-digest-md5.c
===================================================================
RCS file: /home/cvs/dovecot/src/auth/mech-digest-md5.c,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -d -r1.16 -r1.17
--- mech-digest-md5.c	20 Oct 2003 07:33:21 -0000	1.16
+++ mech-digest-md5.c	10 Nov 2003 20:36:02 -0000	1.17
@@ -464,7 +464,7 @@
 }
 
 static int parse_digest_response(struct digest_auth_request *auth,
-				 const char *data, size_t size,
+				 const unsigned char *data, size_t size,
 				 const char **error)
 {
 	char *copy, *key, *value;
@@ -569,8 +569,7 @@
 		return TRUE;
 	}
 
-	if (parse_digest_response(auth, (const char *) data,
-				  request->data_size, &error)) {
+	if (parse_digest_response(auth, data, request->data_size, &error)) {
 		auth_request->callback = callback;
 
 		realm = auth->realm != NULL ? auth->realm : default_realm;

Index: mech.c
===================================================================
RCS file: /home/cvs/dovecot/src/auth/mech.c,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -d -r1.16 -r1.17
--- mech.c	20 Oct 2003 04:15:16 -0000	1.16
+++ mech.c	10 Nov 2003 20:36:02 -0000	1.17
@@ -215,6 +215,7 @@
 }
 
 extern struct mech_module mech_plain;
+extern struct mech_module mech_cram_md5;
 extern struct mech_module mech_digest_md5;
 extern struct mech_module mech_anonymous;
 
@@ -242,6 +243,8 @@
 	while (*mechanisms != NULL) {
 		if (strcasecmp(*mechanisms, "PLAIN") == 0)
 			mech_register_module(&mech_plain);
+		else if (strcasecmp(*mechanisms, "CRAM-MD5") == 0)
+			mech_register_module(&mech_cram_md5);
 		else if (strcasecmp(*mechanisms, "DIGEST-MD5") == 0)
 			mech_register_module(&mech_digest_md5);
 		else if (strcasecmp(*mechanisms, "ANONYMOUS") == 0) {
@@ -293,6 +296,7 @@
 void mech_deinit(void)
 {
 	mech_unregister_module(&mech_plain);
+	mech_unregister_module(&mech_cram_md5);
 	mech_unregister_module(&mech_digest_md5);
 	mech_unregister_module(&mech_anonymous);
 }

Index: passdb.c
===================================================================
RCS file: /home/cvs/dovecot/src/auth/passdb.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -d -r1.11 -r1.12
--- passdb.c	29 Oct 2003 14:10:20 -0000	1.11
+++ passdb.c	10 Nov 2003 20:36:02 -0000	1.12
@@ -24,6 +24,8 @@
 		return "PLAIN";
 	case PASSDB_CREDENTIALS_CRYPT:
 		return "CRYPT";
+	case PASSDB_CREDENTIALS_CRAM_MD5:
+		return "CRAM-MD5";
 	case PASSDB_CREDENTIALS_DIGEST_MD5:
 		return "DIGEST-MD5";
 	}
@@ -132,6 +134,10 @@
 	if ((auth_mechanisms & AUTH_MECH_PLAIN) &&
 	    passdb->verify_plain == NULL)
 		i_fatal("Passdb %s doesn't support PLAIN method", name);
+
+	if ((auth_mechanisms & AUTH_MECH_CRAM_MD5) &&
+	    passdb->lookup_credentials == NULL)
+		i_fatal("Passdb %s doesn't support CRAM-MD5 method", name);
 
 	if ((auth_mechanisms & AUTH_MECH_DIGEST_MD5) &&
 	    passdb->lookup_credentials == NULL)

Index: passdb.h
===================================================================
RCS file: /home/cvs/dovecot/src/auth/passdb.h,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- passdb.h	29 Oct 2003 14:10:20 -0000	1.7
+++ passdb.h	10 Nov 2003 20:36:02 -0000	1.8
@@ -11,6 +11,7 @@
 
 	PASSDB_CREDENTIALS_PLAINTEXT,
 	PASSDB_CREDENTIALS_CRYPT,
+	PASSDB_CREDENTIALS_CRAM_MD5,
 	PASSDB_CREDENTIALS_DIGEST_MD5
 };
 

Index: password-scheme.c
===================================================================
RCS file: /home/cvs/dovecot/src/auth/password-scheme.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- password-scheme.c	3 Apr 2003 23:42:54 -0000	1.3
+++ password-scheme.c	10 Nov 2003 20:36:02 -0000	1.4
@@ -3,7 +3,6 @@
 #include "lib.h"
 #include "hex-binary.h"
 #include "md5.h"
-#include "md5crypt.h"
 #include "mycrypt.h"
 #include "randgen.h"
 #include "password-scheme.h"
@@ -23,8 +22,10 @@
 	if (strcasecmp(scheme, "CRYPT") == 0)
 		return strcmp(mycrypt(plaintext, password), password) == 0;
 
-	if (strcasecmp(scheme, "MD5") == 0)
-		return strcmp(md5_crypt(plaintext, password), password) == 0;
+	if (strcasecmp(scheme, "MD5") == 0) {
+                str = password_generate_md5_crypt(plaintext, password);
+		return strcmp(str, password) == 0;
+	}
 
 	if (strcasecmp(scheme, "PLAIN") == 0)
 		return strcmp(password, plaintext) == 0;
@@ -103,11 +104,14 @@
 		for (i = 0; i < 8; i++)
 			salt[i] = salt_chars[salt[i] % (sizeof(salt_chars)-1)];
 		salt[8] = '\0';
-		return t_strdup(md5_crypt(plaintext, salt));
+		return password_generate_md5_crypt(plaintext, salt);
 	}
 
 	if (strcasecmp(scheme, "PLAIN") == 0)
 		return plaintext;
+
+	if (strcasecmp(scheme, "CRAM-MD5") == 0)
+		return password_generate_cram_md5(plaintext);
 
 	if (strcasecmp(scheme, "DIGEST-MD5") == 0) {
 		/* user:realm:passwd */

Index: password-scheme.h
===================================================================
RCS file: /home/cvs/dovecot/src/auth/password-scheme.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- password-scheme.h	18 Feb 2003 19:24:44 -0000	1.1
+++ password-scheme.h	10 Nov 2003 20:36:02 -0000	1.2
@@ -12,4 +12,8 @@
 const char *password_generate(const char *plaintext, const char *user,
 			      const char *scheme);
 
+/* INTERNAL: */
+const char *password_generate_md5_crypt(const char *pw, const char *salt);
+const char *password_generate_cram_md5(const char *pw);
+
 #endif

--- md5crypt.c DELETED ---

--- md5crypt.h DELETED ---



More information about the dovecot-cvs mailing list