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