[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