menu: clean up and fix MD5 password code
authorH. Peter Anvin <hpa@zytor.com>
Tue, 29 Jan 2008 05:49:42 +0000 (21:49 -0800)
committerH. Peter Anvin <hpa@zytor.com>
Tue, 29 Jan 2008 05:49:42 +0000 (21:49 -0800)
MD5 password code would append the encrypted password to a previous
password, with the result that it would never match after picking a
wrong password.  Fix.

com32/libutil/crypt-md5.c
com32/libutil/md5.c

index c503443..751c3d2 100644 (file)
  * UNIX password
  */
 
-static char itoa64[] =         /* 0 ... 63 => ascii - 64 */
-       "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-
-static void
+static char *
 _crypt_to64(char *s, uint32_t v, int n)
 {
+       static const char itoa64[64] = "./0123456789"
+               "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
        while (--n >= 0) {
                *s++ = itoa64[v&0x3f];
                v >>= 6;
        }
+       return s;
 }
 
 char *
@@ -52,59 +53,65 @@ crypt_md5(const char *pw, const char *salt)
        int sl, pl;
        uint32_t i;
        uint8_t final[MD5_SIZE];
-       static const char *sp, *ep;
-       static char passwd[120], *p;
-       static const char *magic = "$1$";
+       const char *sp;
+       static char passwd[120]; /* Output buffer */
+       static const char magic[] = "$1$";
+       char *p;
+       const int magic_len = sizeof magic - 1;
+       int pwlen = strlen(pw);
 
        /* 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;
+       if (!strncmp(sp, magic, magic_len))
+               sp += magic_len;
 
-       /* get the length of the true salt */
-       sl = ep - sp;
+       /* Compute the salt length:
+          it stops at the first '$', max 8 chars */
+       for (sl = 0; sl < 8 && sp[sl] && sp[sl] != '$'; sl++)
+               ;
 
        MD5Init(&ctx);
 
        /* The password first, since that is what is most unknown */
-       MD5Update(&ctx, (const uint8_t *)pw, strlen(pw));
+       MD5Update(&ctx, pw, pwlen);
 
        /* Then our magic string */
-       MD5Update(&ctx, (const uint8_t *)magic, strlen(magic));
+       MD5Update(&ctx, magic, magic_len);
 
        /* Then the raw salt */
-       MD5Update(&ctx, (const uint8_t *)sp, (uint32_t)sl);
+       MD5Update(&ctx, sp, sl);
 
        /* Then just as many characters of the MD5(pw,salt,pw) */
        MD5Init(&ctx1);
-       MD5Update(&ctx1, (const uint8_t *)pw, strlen(pw));
-       MD5Update(&ctx1, (const uint8_t *)sp, (uint32_t)sl);
-       MD5Update(&ctx1, (const uint8_t *)pw, strlen(pw));
+       MD5Update(&ctx1, pw, pwlen);
+       MD5Update(&ctx1, sp, sl);
+       MD5Update(&ctx1, pw, pwlen);
        MD5Final(final, &ctx1);
-       for(pl = (int)strlen(pw); pl > 0; pl -= MD5_SIZE)
-               MD5Update(&ctx, (const uint8_t *)final,
-                   (uint32_t)(pl > MD5_SIZE ? MD5_SIZE : pl));
+       for (pl = pwlen; pl > 0; pl -= MD5_SIZE)
+               MD5Update(&ctx, final, pl > MD5_SIZE ? MD5_SIZE : pl);
 
        /* Don't leave anything around in vm they could use. */
-       memset(final, 0, sizeof(final));
+       memset(final, 0, sizeof final);
 
        /* Then something really weird... */
-       for (i = strlen(pw); i; i >>= 1)
-               if(i & 1)
-                   MD5Update(&ctx, (const uint8_t *)final, 1);
+       for (i = pwlen; i; i >>= 1)
+               if (i & 1)
+                   MD5Update(&ctx, final, 1);
                else
-                   MD5Update(&ctx, (const uint8_t *)pw, 1);
+                   MD5Update(&ctx, pw, 1);
 
        /* Now make the output string */
-       strcpy(passwd, magic);
-       strncat(passwd, sp, (uint32_t)sl);
-       strcat(passwd, "$");
+       p = passwd;
+
+       memcpy(p, magic, magic_len);
+       p += magic_len;
+
+       memcpy(p, sp, sl);
+       p += sl;
+
+       *p++ = '$';
 
        MD5Final(final, &ctx);
 
@@ -113,46 +120,44 @@ crypt_md5(const char *pw, const char *salt)
         * 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++) {
+       for (i = 0; i < 1000; i++) {
                MD5Init(&ctx1);
                if(i & 1)
-                       MD5Update(&ctx1, (const uint8_t *)pw, strlen(pw));
+                       MD5Update(&ctx1, pw, pwlen);
                else
-                       MD5Update(&ctx1, (const uint8_t *)final, MD5_SIZE);
+                       MD5Update(&ctx1, final, MD5_SIZE);
 
                if(i % 3)
-                       MD5Update(&ctx1, (const uint8_t *)sp, (uint32_t)sl);
+                       MD5Update(&ctx1, sp, sl);
 
                if(i % 7)
-                       MD5Update(&ctx1, (const uint8_t *)pw, strlen(pw));
+                       MD5Update(&ctx1, pw, pwlen);
 
                if(i & 1)
-                       MD5Update(&ctx1, (const uint8_t *)final, MD5_SIZE);
+                       MD5Update(&ctx1, final, MD5_SIZE);
                else
-                       MD5Update(&ctx1, (const uint8_t *)pw, strlen(pw));
+                       MD5Update(&ctx1, pw, pwlen);
                MD5Final(final, &ctx1);
        }
 
-       p = passwd + strlen(passwd);
-
        l = (final[ 0]<<16) | (final[ 6]<<8) | final[12];
-       _crypt_to64(p, l, 4); p += 4;
+       p = _crypt_to64(p, l, 4);
        l = (final[ 1]<<16) | (final[ 7]<<8) | final[13];
-       _crypt_to64(p, l, 4); p += 4;
+       p = _crypt_to64(p, l, 4);
        l = (final[ 2]<<16) | (final[ 8]<<8) | final[14];
-       _crypt_to64(p, l, 4); p += 4;
+       p = _crypt_to64(p, l, 4);
        l = (final[ 3]<<16) | (final[ 9]<<8) | final[15];
-       _crypt_to64(p, l, 4); p += 4;
+       p = _crypt_to64(p, l, 4);
        l = (final[ 4]<<16) | (final[10]<<8) | final[ 5];
-       _crypt_to64(p, l, 4); p += 4;
+       p = _crypt_to64(p, l, 4);
        l = final[11];
-       _crypt_to64(p, l, 2); p += 2;
+       p = _crypt_to64(p, l, 2);
        *p = '\0';
 
        /* Don't leave anything around in vm they could use. */
-       memset(final, 0, sizeof(final));
+       memset(final, 0, sizeof final);
 
-       return (passwd);
+       return passwd;
 }
 
 #ifdef TEST
@@ -160,8 +165,12 @@ crypt_md5(const char *pw, const char *salt)
 
 int main(int argc, char *argv[])
 {
-  puts(crypt_md5(argv[1], argv[2]));
-  return 0;
+       int i;
+
+       for (i = 2; i < argc; i += 2) {
+               puts(crypt_md5(argv[i], argv[i-1]));
+       }
+       return 0;
 }
 
 #endif
index 0e53db3..0a2c3dc 100644 (file)
@@ -80,7 +80,6 @@ static unsigned char PADDING[64] = {
 void
 MD5Init (MD5_CTX *context)
 {
-
        context->count[0] = context->count[1] = 0;
 
        /* Load magic initialization constants.  */
@@ -90,7 +89,7 @@ MD5Init (MD5_CTX *context)
        context->state[3] = 0x10325476;
 }
 
-/* 
+/*
  * MD5 block update operation. Continues an MD5 message-digest
  * operation, processing another message block, and updating the
  * context.