From a617fe15eac8db4fadc0799e9f9dc5a50c94b99d Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Wed, 19 Feb 2003 13:28:56 +0200 Subject: [PATCH] crypt-password checking was broken. added support for md5crypt passwords. --HG-- branch : HEAD --- src/auth/Makefile.am | 2 + src/auth/md5crypt.c | 147 +++++++++++++++++++++++++++++++++++++ src/auth/md5crypt.h | 6 ++ src/auth/password-scheme.c | 32 ++++++-- 4 files changed, 182 insertions(+), 5 deletions(-) create mode 100644 src/auth/md5crypt.c create mode 100644 src/auth/md5crypt.h diff --git a/src/auth/Makefile.am b/src/auth/Makefile.am index e28c326f6b..4b2a7658b4 100644 --- a/src/auth/Makefile.am +++ b/src/auth/Makefile.am @@ -26,6 +26,7 @@ dovecot_auth_SOURCES = \ login-connection.c \ main.c \ master-connection.c \ + md5crypt.c \ mech.c \ mech-cyrus-sasl2.c \ mech-plain.c \ @@ -56,6 +57,7 @@ noinst_HEADERS = \ common.h \ login-connection.h \ master-connection.h \ + md5crypt.h \ mech.h \ mycrypt.h \ passdb.h \ diff --git a/src/auth/md5crypt.c b/src/auth/md5crypt.c new file mode 100644 index 0000000000..9b85e9da10 --- /dev/null +++ b/src/auth/md5crypt.c @@ -0,0 +1,147 @@ +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * 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@jm.nu + */ + +#include "lib.h" +#include "md5.h" +#include "md5crypt.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(char *s, unsigned long v, int n) +{ + while (--n >= 0) { + *s++ = itoa64[v&0x3f]; + v >>= 6; + } +} + +/* + * UNIX password + * + * Use MD5 for what it is best at... + */ + +char * +md5_crypt(const char *pw, const char *salt) +{ + static char passwd[120], *p; + static const char *sp,*ep; + unsigned char final[16]; + int sl,pl,i,j; + struct md5_context ctx,ctx1; + unsigned long l; + + /* Refine the Salt first */ + sp = salt; + + /* If it starts with the magic string, then skip that */ + if(!strncmp(sp,magic,strlen(magic))) + sp += strlen(magic); + + /* 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,strlen(pw)); + + /* Then our magic string */ + md5_update(&ctx,magic,strlen(magic)); + + /* 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,strlen(pw)); + md5_update(&ctx1,sp,sl); + md5_update(&ctx1,pw,strlen(pw)); + md5_final(&ctx1,final); + for(pl = strlen(pw); pl > 0; pl -= 16) + md5_update(&ctx,final,pl>16 ? 16 : pl); + + /* Don't leave anything around in vm they could use. */ + memset(final,0,sizeof final); + + /* Then something really weird... */ + for (j=0,i = strlen(pw); i ; i >>= 1) + if(i&1) + md5_update(&ctx, final+j, 1); + else + md5_update(&ctx, pw+j, 1); + + /* Now make the output string */ + strcpy(passwd,magic); + strncat(passwd,sp,sl); + strcat(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,strlen(pw)); + else + md5_update(&ctx1,final,16); + + if(i % 3) + md5_update(&ctx1,sp,sl); + + if(i % 7) + md5_update(&ctx1,pw,strlen(pw)); + + if(i & 1) + md5_update(&ctx1,final,16); + else + md5_update(&ctx1,pw,strlen(pw)); + md5_final(&ctx1,final); + } + + p = passwd + strlen(passwd); + + l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4; + l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4; + l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4; + l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4; + l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4; + l = final[11] ; to64(p,l,2); p += 2; + *p = '\0'; + + /* Don't leave anything around in vm they could use. */ + memset(final,0,sizeof final); + + return passwd; +} diff --git a/src/auth/md5crypt.h b/src/auth/md5crypt.h new file mode 100644 index 0000000000..99983388b1 --- /dev/null +++ b/src/auth/md5crypt.h @@ -0,0 +1,6 @@ +#ifndef __MD5CRYPT_H +#define __MD5CRYPT_H + +char *md5_crypt(const char *pw, const char *salt); + +#endif diff --git a/src/auth/password-scheme.c b/src/auth/password-scheme.c index c9de2a5577..2f0f67a795 100644 --- a/src/auth/password-scheme.c +++ b/src/auth/password-scheme.c @@ -3,10 +3,14 @@ #include "lib.h" #include "hex-binary.h" #include "md5.h" +#include "md5crypt.h" #include "mycrypt.h" #include "randgen.h" #include "password-scheme.h" +static const char *salt_chars = + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + int password_verify(const char *plaintext, const char *password, const char *scheme, const char *user) { @@ -17,7 +21,10 @@ int password_verify(const char *plaintext, const char *password, return 0; if (strcasecmp(scheme, "CRYPT") == 0) - return strcmp(mycrypt(password, plaintext), plaintext) == 0; + return strcmp(mycrypt(plaintext, password), password) == 0; + + if (strcasecmp(scheme, "MD5") == 0) + return strcmp(md5_crypt(plaintext, password), password) == 0; if (strcasecmp(scheme, "PLAIN") == 0) return strcmp(password, plaintext) == 0; @@ -48,7 +55,15 @@ const char *password_get_scheme(const char **password) { const char *p, *scheme; - if (*password == NULL || **password != '{') + if (*password == NULL) + return NULL; + + if (strncmp(*password, "$1$", 3) == 0) { + *password = t_strcut(*password + 3, '$'); + return "MD5"; + } + + if (**password != '{') return NULL; p = strchr(*password, '}'); @@ -63,11 +78,10 @@ const char *password_get_scheme(const char **password) const char *password_generate(const char *plaintext, const char *user, const char *scheme) { - static const char *salt_chars = - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./"; const char *realm, *str; unsigned char digest[16]; - char salt[3]; + char salt[9]; + int i; if (strcasecmp(scheme, "CRYPT") == 0) { random_fill(salt, 2); @@ -77,6 +91,14 @@ const char *password_generate(const char *plaintext, const char *user, return t_strdup(mycrypt(plaintext, salt)); } + if (strcasecmp(scheme, "MD5") == 0) { + random_fill(salt, 8); + 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)); + } + if (strcasecmp(scheme, "PLAIN") == 0) return plaintext; -- 2.47.3