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