(root)/
Linux-PAM-1.5.3/
modules/
pam_unix/
md5_crypt.c
       1  /*
       2   * $Id$
       3   *
       4   * ----------------------------------------------------------------------------
       5   * "THE BEER-WARE LICENSE" (Revision 42):
       6   * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
       7   * can do whatever you want with this stuff. If we meet some day, and you think
       8   * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
       9   * ----------------------------------------------------------------------------
      10   *
      11   * Origin: Id: crypt.c,v 1.3 1995/05/30 05:42:22 rgrimes Exp
      12   *
      13   */
      14  
      15  #include <string.h>
      16  #include <stdlib.h>
      17  #include "md5.h"
      18  #include "pam_inline.h"
      19  
      20  static unsigned char itoa64[] =	/* 0 ... 63 => ascii - 64 */
      21  "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
      22  
      23  static void to64(char *s, unsigned long v, int n)
      24  {
      25  	while (--n >= 0) {
      26  		*s++ = itoa64[v & 0x3f];
      27  		v >>= 6;
      28  	}
      29  }
      30  
      31  /*
      32   * UNIX password
      33   *
      34   * Use MD5 for what it is best at...
      35   */
      36  
      37  char *MD5Name(crypt_md5)(const char *pw, const char *salt)
      38  {
      39  	const char *magic = "$1$";
      40  	/* This string is magic for this algorithm.  Having
      41  	 * it this way, we can get get better later on */
      42  	char *passwd, *p;
      43  	const char *sp, *ep;
      44  	unsigned char final[16];
      45  	int sl, pl, i, j;
      46  	MD5_CTX ctx, ctx1;
      47  	unsigned long l;
      48  
      49  	/* Refine the Salt first */
      50  	sp = salt;
      51  
      52  	/* TODO: now that we're using malloc'ed memory, get rid of the
      53  	   strange constant buffer size. */
      54  	passwd = malloc(120);
      55  	if (passwd == NULL)
      56  		return NULL;
      57  
      58  	/* If it starts with the magic string, then skip that */
      59  	if ((ep = pam_str_skip_prefix_len(sp, magic, strlen(magic))) != NULL)
      60  		sp = ep;
      61  
      62  	/* It stops at the first '$', max 8 chars */
      63  	for (ep = sp; *ep && *ep != '$' && ep < (sp + 8); ep++)
      64  		continue;
      65  
      66  	/* get the length of the true salt */
      67  	sl = ep - sp;
      68  
      69  	MD5Name(MD5Init)(&ctx);
      70  
      71  	/* The password first, since that is what is most unknown */
      72  	MD5Name(MD5Update)(&ctx,(unsigned const char *)pw,strlen(pw));
      73  
      74  	/* Then our magic string */
      75  	MD5Name(MD5Update)(&ctx,(unsigned const char *)magic,strlen(magic));
      76  
      77  	/* Then the raw salt */
      78  	MD5Name(MD5Update)(&ctx,(unsigned const char *)sp,sl);
      79  
      80  	/* Then just as many characters of the MD5(pw,salt,pw) */
      81  	MD5Name(MD5Init)(&ctx1);
      82  	MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
      83  	MD5Name(MD5Update)(&ctx1,(unsigned const char *)sp,sl);
      84  	MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
      85  	MD5Name(MD5Final)(final,&ctx1);
      86  	for (pl = strlen(pw); pl > 0; pl -= 16)
      87  		MD5Name(MD5Update)(&ctx,(unsigned const char *)final,pl>16 ? 16 : pl);
      88  
      89  	/* Don't leave anything around in vm they could use. */
      90  	pam_overwrite_array(final);
      91  
      92  	/* Then something really weird... */
      93  	for (j = 0, i = strlen(pw); i; i >>= 1)
      94  		if (i & 1)
      95  			MD5Name(MD5Update)(&ctx, (unsigned const char *)final+j, 1);
      96  		else
      97  			MD5Name(MD5Update)(&ctx, (unsigned const char *)pw+j, 1);
      98  
      99  	/* Now make the output string */
     100  	strcpy(passwd, magic);
     101  	strncat(passwd, sp, sl);
     102  	strcat(passwd, "$");
     103  
     104  	MD5Name(MD5Final)(final,&ctx);
     105  
     106  	/*
     107  	 * and now, just to make sure things don't run too fast
     108  	 * On a 60 Mhz Pentium this takes 34 msec, so you would
     109  	 * need 30 seconds to build a 1000 entry dictionary...
     110  	 */
     111  	for (i = 0; i < 1000; i++) {
     112  		MD5Name(MD5Init)(&ctx1);
     113  		if (i & 1)
     114  			MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
     115  		else
     116  			MD5Name(MD5Update)(&ctx1,(unsigned const char *)final,16);
     117  
     118  		if (i % 3)
     119  			MD5Name(MD5Update)(&ctx1,(unsigned const char *)sp,sl);
     120  
     121  		if (i % 7)
     122  			MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
     123  
     124  		if (i & 1)
     125  			MD5Name(MD5Update)(&ctx1,(unsigned const char *)final,16);
     126  		else
     127  			MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw));
     128  		MD5Name(MD5Final)(final,&ctx1);
     129  	}
     130  
     131  	p = passwd + strlen(passwd);
     132  
     133  	l = (final[0] << 16) | (final[6] << 8) | final[12];
     134  	to64(p, l, 4);
     135  	p += 4;
     136  	l = (final[1] << 16) | (final[7] << 8) | final[13];
     137  	to64(p, l, 4);
     138  	p += 4;
     139  	l = (final[2] << 16) | (final[8] << 8) | final[14];
     140  	to64(p, l, 4);
     141  	p += 4;
     142  	l = (final[3] << 16) | (final[9] << 8) | final[15];
     143  	to64(p, l, 4);
     144  	p += 4;
     145  	l = (final[4] << 16) | (final[10] << 8) | final[5];
     146  	to64(p, l, 4);
     147  	p += 4;
     148  	l = final[11];
     149  	to64(p, l, 2);
     150  	p += 2;
     151  	*p = '\0';
     152  
     153  	/* Don't leave anything around in vm they could use. */
     154  	pam_overwrite_array(final);
     155  
     156  	return passwd;
     157  }