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 from crypt_bsdmd5.so (bsdmd5.c) :
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 /*
40  * Implements the specification from:
41  *
42  * From http://people.redhat.com/drepper/SHA-crypt.txt
43  *
44  * Portions of the code taken from inspired by or verified against the
45  * source in the above document which is licensed as:
46  *
47  * "Released into the Public Domain by Ulrich Drepper <drepper@redhat.com>."
48  */
49 
50 
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <sys/sysmacros.h>
54 #include <fcntl.h>
55 #include <unistd.h>
56 #include <string.h>
57 #include <stdio.h>
58 #include <errno.h>
59 #include <stdlib.h>
60 #include <alloca.h>
61 
62 #include <sha2.h>
63 #include <crypt.h>
64 
65 #define	MAX_SALT_LEN	16
66 #define	ROUNDS_DEFAULT	5000
67 #define	ROUNDS_MIN	1000
68 #define	ROUNDS_MAX	999999999
69 
70 #ifdef CRYPT_SHA256
71 
72 #define	DIGEST_CTX	SHA256_CTX
73 #define	DIGESTInit	SHA256Init
74 #define	DIGESTUpdate	SHA256Update
75 #define	DIGESTFinal	SHA256Final
76 #define	DIGEST_LEN	SHA256_DIGEST_LENGTH
77 #define	MIXCHARS	32
78 static const char crypt_alg_magic[] = "$5";
79 
80 #elif CRYPT_SHA512
81 
82 #define	DIGEST_CTX	SHA512_CTX
83 #define	DIGESTInit	SHA512Init
84 #define	DIGESTUpdate	SHA512Update
85 #define	DIGESTFinal	SHA512Final
86 #define	DIGEST_LEN	SHA512_DIGEST_LENGTH
87 #define	MIXCHARS	64
88 static const char crypt_alg_magic[] = "$6";
89 
90 #else
91 #error	"One of CRYPT_256 or CRYPT_512 must be defined"
92 #endif
93 
94 static const int crypt_alg_magic_len = sizeof (crypt_alg_magic) - 1;
95 
96 
97 static uchar_t b64t[] =		/* 0 ... 63 => ascii - 64 */
98 	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
99 
100 #define	b64_from_24bit(B2, B1, B0, N) \
101 { \
102 	uint_t w = ((B2) << 16) | ((B1) << 8) | (B0); \
103 	int n = (N); \
104 	while (--n >= 0 && ctbufflen > 0) { \
105 		*p++ = b64t[w & 0x3f]; \
106 		w >>= 6; \
107 		ctbufflen--; \
108 	} \
109 }
110 
111 static void
to64(char * s,uint64_t v,int n)112 to64(char *s, uint64_t v, int n)
113 {
114 	while (--n >= 0) {
115 		*s++ = b64t[v & 0x3f];
116 		v >>= 6;
117 	}
118 }
119 
120 #define	ROUNDS		"rounds="
121 #define	ROUNDSLEN	(sizeof (ROUNDS) - 1)
122 
123 /*
124  * get the integer value after rounds= where ever it occurs in the string.
125  * if the last char after the int is a , or $ that is fine anything else is an
126  * error.
127  */
128 static uint32_t
getrounds(const char * s)129 getrounds(const char *s)
130 {
131 	char *r, *p, *e;
132 	long val;
133 
134 	if (s == NULL)
135 		return (0);
136 
137 	if ((r = strstr(s, ROUNDS)) == NULL) {
138 		return (0);
139 	}
140 
141 	if (strncmp(r, ROUNDS, ROUNDSLEN) != 0) {
142 		return (0);
143 	}
144 
145 	p = r + ROUNDSLEN;
146 	errno = 0;
147 	val = strtol(p, &e, 10);
148 	/*
149 	 * An error occurred or there is non-numeric stuff at the end
150 	 * which isn't one of the crypt(3c) special chars ',' or '$'
151 	 */
152 	if (errno != 0 || val < 0 ||
153 	    !(*e == '\0' || *e == ',' || *e == '$')) {
154 		return (0);
155 	}
156 
157 	return ((uint32_t)val);
158 }
159 
160 
161 /* ARGSUSED4 */
162 char *
crypt_genhash_impl(char * ctbuffer,size_t ctbufflen,const char * plaintext,const char * switchsalt,const char ** params)163 crypt_genhash_impl(char *ctbuffer,
164 	    size_t ctbufflen,
165 	    const char *plaintext,
166 	    const char *switchsalt,
167 	    const char **params)
168 {
169 	int salt_len, plaintext_len, i;
170 	char *salt;
171 	uchar_t A[DIGEST_LEN];
172 	uchar_t B[DIGEST_LEN];
173 	uchar_t DP[DIGEST_LEN];
174 	uchar_t DS[DIGEST_LEN];
175 	DIGEST_CTX ctxA, ctxB, ctxC, ctxDP, ctxDS;
176 	int rounds = ROUNDS_DEFAULT;
177 	int srounds = 0;
178 	boolean_t custom_rounds = B_FALSE;
179 	char *p;
180 	char *P, *Pp;
181 	char *S, *Sp;
182 
183 	/* Refine the salt */
184 	salt = (char *)switchsalt;
185 
186 	/* skip our magic string */
187 	if (strncmp((char *)salt, crypt_alg_magic, crypt_alg_magic_len) == 0) {
188 		salt += crypt_alg_magic_len + 1;
189 	}
190 
191 	srounds = getrounds(salt);
192 	if (srounds != 0) {
193 		rounds = MAX(ROUNDS_MIN, MIN(srounds, ROUNDS_MAX));
194 		custom_rounds = B_TRUE;
195 		p = strchr(salt, '$');
196 		if (p != NULL)
197 			salt = p + 1;
198 	}
199 
200 	salt_len = MIN(strcspn(salt, "$"), MAX_SALT_LEN);
201 	plaintext_len = strlen(plaintext);
202 
203 	/* 1. */
204 	DIGESTInit(&ctxA);
205 
206 	/* 2. The password first, since that is what is most unknown */
207 	DIGESTUpdate(&ctxA, plaintext, plaintext_len);
208 
209 	/* 3. Then the raw salt */
210 	DIGESTUpdate(&ctxA, salt, salt_len);
211 
212 	/* 4. - 8. */
213 	DIGESTInit(&ctxB);
214 	DIGESTUpdate(&ctxB, plaintext, plaintext_len);
215 	DIGESTUpdate(&ctxB, salt, salt_len);
216 	DIGESTUpdate(&ctxB, plaintext, plaintext_len);
217 	DIGESTFinal(B, &ctxB);
218 
219 	/* 9. - 10. */
220 	for (i = plaintext_len; i > MIXCHARS; i -= MIXCHARS)
221 		DIGESTUpdate(&ctxA, B, MIXCHARS);
222 	DIGESTUpdate(&ctxA, B, i);
223 
224 	/* 11. */
225 	for (i = plaintext_len; i > 0; i >>= 1) {
226 		if ((i & 1) != 0) {
227 			DIGESTUpdate(&ctxA, B, MIXCHARS);
228 		} else {
229 			DIGESTUpdate(&ctxA, plaintext, plaintext_len);
230 		}
231 	}
232 
233 	/* 12. */
234 	DIGESTFinal(A, &ctxA);
235 
236 	/* 13. - 15. */
237 	DIGESTInit(&ctxDP);
238 	for (i = 0; i < plaintext_len; i++)
239 		DIGESTUpdate(&ctxDP, plaintext, plaintext_len);
240 	DIGESTFinal(DP, &ctxDP);
241 
242 	/* 16. */
243 	Pp = P = alloca(plaintext_len);
244 	for (i = plaintext_len; i >= MIXCHARS; i -= MIXCHARS) {
245 		Pp = (char *)(memcpy(Pp, DP, MIXCHARS)) + MIXCHARS;
246 	}
247 	(void) memcpy(Pp, DP, i);
248 
249 	/* 17. - 19. */
250 	DIGESTInit(&ctxDS);
251 	for (i = 0; i < 16 + (uint8_t)A[0]; i++)
252 		DIGESTUpdate(&ctxDS, salt, salt_len);
253 	DIGESTFinal(DS, &ctxDS);
254 
255 	/* 20. */
256 	Sp = S = alloca(salt_len);
257 	for (i = salt_len; i >= MIXCHARS; i -= MIXCHARS) {
258 		Sp = (char *)(memcpy(Sp, DS, MIXCHARS)) + MIXCHARS;
259 	}
260 	(void) memcpy(Sp, DS, i);
261 
262 	/*  21. */
263 	for (i = 0; i < rounds; i++) {
264 		DIGESTInit(&ctxC);
265 
266 		if ((i & 1) != 0) {
267 			DIGESTUpdate(&ctxC, P, plaintext_len);
268 		} else {
269 			if (i == 0)
270 				DIGESTUpdate(&ctxC, A, MIXCHARS);
271 			else
272 				DIGESTUpdate(&ctxC, DP, MIXCHARS);
273 		}
274 
275 		if (i % 3 != 0) {
276 			DIGESTUpdate(&ctxC, S, salt_len);
277 		}
278 
279 		if (i % 7 != 0) {
280 			DIGESTUpdate(&ctxC, P, plaintext_len);
281 		}
282 
283 		if ((i & 1) != 0) {
284 			if (i == 0)
285 				DIGESTUpdate(&ctxC, A, MIXCHARS);
286 			else
287 				DIGESTUpdate(&ctxC, DP, MIXCHARS);
288 		} else {
289 			DIGESTUpdate(&ctxC, P, plaintext_len);
290 		}
291 		DIGESTFinal(DP, &ctxC);
292 	}
293 
294 	/* 22. Now make the output string */
295 	if (custom_rounds) {
296 		(void) snprintf(ctbuffer, ctbufflen,
297 		    "%s$rounds=%zu$", crypt_alg_magic, rounds);
298 	} else {
299 		(void) snprintf(ctbuffer, ctbufflen,
300 		    "%s$", crypt_alg_magic);
301 	}
302 	(void) strncat(ctbuffer, (const char *)salt, salt_len);
303 	(void) strlcat(ctbuffer, "$", ctbufflen);
304 
305 	p = ctbuffer + strlen(ctbuffer);
306 	ctbufflen -= strlen(ctbuffer);
307 
308 #ifdef CRYPT_SHA256
309 	b64_from_24bit(DP[ 0], DP[10], DP[20], 4);
310 	b64_from_24bit(DP[21], DP[ 1], DP[11], 4);
311 	b64_from_24bit(DP[12], DP[22], DP[ 2], 4);
312 	b64_from_24bit(DP[ 3], DP[13], DP[23], 4);
313 	b64_from_24bit(DP[24], DP[ 4], DP[14], 4);
314 	b64_from_24bit(DP[15], DP[25], DP[ 5], 4);
315 	b64_from_24bit(DP[ 6], DP[16], DP[26], 4);
316 	b64_from_24bit(DP[27], DP[ 7], DP[17], 4);
317 	b64_from_24bit(DP[18], DP[28], DP[ 8], 4);
318 	b64_from_24bit(DP[ 9], DP[19], DP[29], 4);
319 	b64_from_24bit(0, DP[31], DP[30], 3);
320 #elif CRYPT_SHA512
321 	b64_from_24bit(DP[ 0], DP[21], DP[42], 4);
322 	b64_from_24bit(DP[22], DP[43], DP[ 1], 4);
323 	b64_from_24bit(DP[44], DP[ 2], DP[23], 4);
324 	b64_from_24bit(DP[ 3], DP[24], DP[45], 4);
325 	b64_from_24bit(DP[25], DP[46], DP[ 4], 4);
326 	b64_from_24bit(DP[47], DP[ 5], DP[26], 4);
327 	b64_from_24bit(DP[ 6], DP[27], DP[48], 4);
328 	b64_from_24bit(DP[28], DP[49], DP[ 7], 4);
329 	b64_from_24bit(DP[50], DP[ 8], DP[29], 4);
330 	b64_from_24bit(DP[ 9], DP[30], DP[51], 4);
331 	b64_from_24bit(DP[31], DP[52], DP[10], 4);
332 	b64_from_24bit(DP[53], DP[11], DP[32], 4);
333 	b64_from_24bit(DP[12], DP[33], DP[54], 4);
334 	b64_from_24bit(DP[34], DP[55], DP[13], 4);
335 	b64_from_24bit(DP[56], DP[14], DP[35], 4);
336 	b64_from_24bit(DP[15], DP[36], DP[57], 4);
337 	b64_from_24bit(DP[37], DP[58], DP[16], 4);
338 	b64_from_24bit(DP[59], DP[17], DP[38], 4);
339 	b64_from_24bit(DP[18], DP[39], DP[60], 4);
340 	b64_from_24bit(DP[40], DP[61], DP[19], 4);
341 	b64_from_24bit(DP[62], DP[20], DP[41], 4);
342 	b64_from_24bit(0, 0, DP[63], 2);
343 #endif
344 	*p = '\0';
345 
346 	(void) memset(A, 0, sizeof (A));
347 	(void) memset(B, 0, sizeof (B));
348 	(void) memset(DP, 0, sizeof (DP));
349 	(void) memset(DS, 0, sizeof (DS));
350 
351 	return (ctbuffer);
352 }
353 
354 
355 /* ARGSUSED3 */
356 char *
crypt_gensalt_impl(char * gsbuffer,size_t gsbufflen,const char * oldsalt,const struct passwd * userinfo,const char ** params)357 crypt_gensalt_impl(char *gsbuffer,
358 	    size_t gsbufflen,
359 	    const char *oldsalt,
360 	    const struct passwd *userinfo,
361 	    const char **params)
362 {
363 	int fd;
364 	int err;
365 	ssize_t got;
366 	uint64_t rndval;
367 	uint32_t confrounds = 0;
368 	uint32_t saltrounds;
369 	char rndstr[sizeof (rndval) + 1];
370 	int i;
371 
372 	for (i = 0; params != NULL && params[i] != NULL; i++) {
373 		if (strncmp(params[i], ROUNDS, ROUNDSLEN) == 0) {
374 			confrounds = getrounds(params[i]);
375 		} else {
376 			errno = EINVAL;
377 			return (NULL);
378 		}
379 	}
380 
381 	/*
382 	 * If the config file has a higher value for rounds= than what
383 	 * was in the old salt use that, otherwise keep what was in the
384 	 * old salt.
385 	 */
386 	saltrounds = getrounds(oldsalt);
387 	if (confrounds > saltrounds) {
388 		saltrounds = confrounds;
389 	}
390 
391 	if ((fd = open("/dev/urandom", O_RDONLY)) == -1) {
392 		return (NULL);
393 	}
394 
395 	got = read(fd, &rndval, sizeof (rndval));
396 	if (got < sizeof (rndval)) {
397 		err = errno;
398 		(void) close(fd);
399 		errno = err;
400 		return (NULL);
401 	}
402 	(void) close(fd);
403 
404 	to64((char *)&rndstr, rndval, sizeof (rndval));
405 	rndstr[sizeof (rndstr) - 1] = 0;
406 
407 	if (saltrounds > 0) {
408 		if (snprintf(gsbuffer, gsbufflen,
409 		    "%s$rounds=%d$",
410 		    crypt_alg_magic, saltrounds) >= gsbufflen)
411 			goto fail;
412 	} else {
413 		if (snprintf(gsbuffer, gsbufflen,
414 		    "%s$", crypt_alg_magic) >= gsbufflen)
415 			goto fail;
416 	}
417 	if (strlcat(gsbuffer, rndstr, gsbufflen) >= gsbufflen)
418 		goto fail;
419 	if (strlcat(gsbuffer, "$", gsbufflen) >= gsbufflen)
420 		goto fail;
421 
422 	return (gsbuffer);
423 
424 fail:
425 	(void) memset(gsbuffer, 0, gsbufflen);
426 	return (gsbuffer);
427 }
428