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