xref: /illumos-gate/usr/src/lib/crypt_modules/bsdmd5/bsdmd5.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Portions of this code:
31  * ----------------------------------------------------------------------------
32  * "THE BEER-WARE LICENSE" (Revision 42):
33  * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
34  * can do whatever you want with this stuff. If we meet some day, and you think
35  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
36  * ----------------------------------------------------------------------------
37  *
38  * $FreeBSD: crypt.c,v 1.5 1996/10/14 08:34:02 phk Exp $
39  *
40  */
41 
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <string.h>
47 #include <strings.h>
48 #include <stdio.h>
49 #include <errno.h>
50 
51 #include <md5.h>
52 #include <crypt.h>
53 
54 static const char crypt_alg_magic[] = "$1$";
55 
56 #define	SALT_LEN	8
57 
58 static uchar_t itoa64[] =		/* 0 ... 63 => ascii - 64 */
59 	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
60 
61 static void
62 to64(char *s, uint64_t v, int n)
63 {
64 	while (--n >= 0) {
65 		*s++ = itoa64[v&0x3f];
66 		v >>= 6;
67 	}
68 }
69 
70 
71 char *
72 crypt_genhash_impl(char *ctbuffer,
73 	    size_t ctbufflen,
74 	    const char *plaintext,
75 	    const char *switchsalt,
76 	    const char **params)
77 {
78 	char *p;
79 	int sl, l, pl, i;
80 	uchar_t *sp, *ep;
81 	uchar_t final[16]; /* XXX: 16 is some number from the orig source */
82 	MD5_CTX ctx, ctx1;
83 	const int crypt_alg_magic_len = strlen(crypt_alg_magic);
84 
85 	/* Refine the salt */
86 	sp = (uchar_t *)switchsalt;
87 
88 	/* skip our magic string */
89 	if (strncmp((char *)sp, crypt_alg_magic, crypt_alg_magic_len) == 0) {
90 		sp += crypt_alg_magic_len;
91 	}
92 
93 	/* Salt stops at the first $, max SALT_LEN chars */
94 	for (ep = sp; *ep && *ep != '$' && ep < (sp + SALT_LEN); ep++)
95 		continue;
96 
97 	sl = ep - sp;
98 
99 	MD5Init(&ctx);
100 
101 	/* The password first, since that is what is most unknown */
102 	MD5Update(&ctx, (uchar_t *)plaintext, strlen(plaintext));
103 
104 	/* Then our magic string */
105 	MD5Update(&ctx, (uchar_t *)crypt_alg_magic, strlen(crypt_alg_magic));
106 
107 	/* Then the raw salt */
108 	MD5Update(&ctx, (uchar_t *)sp, sl);
109 
110 	/* Then just as many characters of the MD5(plaintext,salt,plaintext) */
111 	MD5Init(&ctx1);
112 	MD5Update(&ctx1, (uchar_t *)plaintext, strlen(plaintext));
113 	MD5Update(&ctx1, sp, sl);
114 	MD5Update(&ctx1, (uchar_t *)plaintext, strlen(plaintext));
115 	MD5Final(final, &ctx1);
116 	for (pl = strlen(plaintext); pl > 0; pl -= 16)
117 		MD5Update(&ctx, final, pl > 16 ? 16 : pl);
118 
119 	/* Don't leave anything around in vm they could use. */
120 	memset(final, 0, sizeof (final));
121 
122 	/* Then something really weird... */
123 	for (i = strlen(plaintext); i; i >>= 1) {
124 		if (i & 1) {
125 			MD5Update(&ctx, final, 1);
126 		} else {
127 			MD5Update(&ctx, (uchar_t *)plaintext, 1);
128 		}
129 	}
130 
131 	/* Now make the output string */
132 	(void) strlcpy(ctbuffer, crypt_alg_magic, ctbufflen);
133 	(void) strncat(ctbuffer, (const char *)sp, sl);
134 	(void) strlcat(ctbuffer, "$", ctbufflen);
135 
136 	MD5Final(final, &ctx);
137 
138 	/*
139 	 * and now, just to make sure things don't run too fast
140 	 * On a 60 Mhz Pentium this takes 34 msec, so you would
141 	 * need 30 seconds to build a 1000 entry dictionary...
142 	 */
143 	for (i = 0; i < 1000; i++) {
144 		MD5Init(&ctx1);
145 		if (i & 1)
146 			MD5Update(&ctx1, (uchar_t *)plaintext,
147 			    strlen(plaintext));
148 		else
149 			MD5Update(&ctx1, final, 16);
150 
151 		if (i % 3)
152 			MD5Update(&ctx1, sp, sl);
153 
154 		if (i % 7)
155 			MD5Update(&ctx1, (uchar_t *)plaintext,
156 			    strlen(plaintext));
157 
158 		if (i & 1)
159 			MD5Update(&ctx1, final, 16);
160 		else
161 			MD5Update(&ctx1, (uchar_t *)plaintext,
162 			    strlen(plaintext));
163 		MD5Final(final, &ctx1);
164 	}
165 
166 	p = ctbuffer + strlen(ctbuffer);
167 
168 	l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p, l, 4); p += 4;
169 	l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p, l, 4); p += 4;
170 	l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p, l, 4); p += 4;
171 	l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p, l, 4); p += 4;
172 	l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p, l, 4); p += 4;
173 	l = final[11]; to64(p, l, 2); p += 2;
174 	*p = '\0';
175 
176 	/* Don't leave anything around in vm they could use. */
177 	memset(final, 0, sizeof (final));
178 
179 	return (ctbuffer);
180 }
181 
182 
183 char *
184 crypt_gensalt_impl(char *gsbuffer,
185 	    size_t gsbufflen,
186 	    const char *oldsalt,
187 	    const struct passwd *userinfo,
188 	    const char **params)
189 {
190 	int fd;
191 	int err;
192 	ssize_t got;
193 	uint64_t rndval;
194 
195 	if ((fd = open("/dev/urandom", O_RDONLY)) == -1) {
196 		return (NULL);
197 	}
198 
199 	(void) strlcpy(gsbuffer, crypt_alg_magic, gsbufflen);
200 
201 	got = read(fd, &rndval, sizeof (rndval));
202 	if (got < sizeof (rndval)) {
203 		err = errno;
204 		(void) close(fd);
205 		errno = err;
206 		return (NULL);
207 	}
208 	to64(&gsbuffer[strlen(crypt_alg_magic)], rndval, sizeof (rndval));
209 
210 	(void) close(fd);
211 
212 	return (gsbuffer);
213 }
214