crypt_sha.c revision 9557:e292b8371405
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
78static 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
88static const char crypt_alg_magic[] = "$6";
89
90#else
91#error	"One of CRYPT_256 or CRYPT_512 must be defined"
92#endif
93
94static const int crypt_alg_magic_len = sizeof (crypt_alg_magic) - 1;
95
96
97static 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
111static void
112to64(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 */
128static uint32_t
129getrounds(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 occured 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
161char *
162crypt_genhash_impl(char *ctbuffer,
163	    size_t ctbufflen,
164	    const char *plaintext,
165	    const char *switchsalt,
166	    const char **params)
167{
168	int salt_len, plaintext_len, i;
169	char *salt;
170	uchar_t A[DIGEST_LEN];
171	uchar_t B[DIGEST_LEN];
172	uchar_t DP[DIGEST_LEN];
173	uchar_t DS[DIGEST_LEN];
174	DIGEST_CTX ctxA, ctxB, ctxC, ctxDP, ctxDS;
175	int rounds = ROUNDS_DEFAULT;
176	int srounds = 0;
177	boolean_t custom_rounds = B_FALSE;
178	char *p;
179	char *P, *Pp;
180	char *S, *Sp;
181
182	/* Refine the salt */
183	salt = (char *)switchsalt;
184
185	/* skip our magic string */
186	if (strncmp((char *)salt, crypt_alg_magic, crypt_alg_magic_len) == 0) {
187		salt += crypt_alg_magic_len + 1;
188	}
189
190	srounds = getrounds(salt);
191	if (srounds != 0) {
192		rounds = MAX(ROUNDS_MIN, MIN(srounds, ROUNDS_MAX));
193		custom_rounds = B_TRUE;
194		p = strchr(salt, '$');
195		if (p != NULL)
196			salt = p + 1;
197	}
198
199	salt_len = MIN(strcspn(salt, "$"), MAX_SALT_LEN);
200	plaintext_len = strlen(plaintext);
201
202	/* 1. */
203	DIGESTInit(&ctxA);
204
205	/* 2. The password first, since that is what is most unknown */
206	DIGESTUpdate(&ctxA, plaintext, plaintext_len);
207
208	/* 3. Then the raw salt */
209	DIGESTUpdate(&ctxA, salt, salt_len);
210
211	/* 4. - 8. */
212	DIGESTInit(&ctxB);
213	DIGESTUpdate(&ctxB, plaintext, plaintext_len);
214	DIGESTUpdate(&ctxB, salt, salt_len);
215	DIGESTUpdate(&ctxB, plaintext, plaintext_len);
216	DIGESTFinal(B, &ctxB);
217
218	/* 9. - 10. */
219	for (i = plaintext_len; i > MIXCHARS; i -= MIXCHARS)
220		DIGESTUpdate(&ctxA, B, MIXCHARS);
221	DIGESTUpdate(&ctxA, B, i);
222
223	/* 11. */
224	for (i = plaintext_len; i > 0; i >>= 1) {
225		if ((i & 1) != 0) {
226			DIGESTUpdate(&ctxA, B, MIXCHARS);
227		} else {
228			DIGESTUpdate(&ctxA, plaintext, plaintext_len);
229		}
230	}
231
232	/* 12. */
233	DIGESTFinal(A, &ctxA);
234
235	/* 13. - 15. */
236	DIGESTInit(&ctxDP);
237	for (i = 0; i < plaintext_len; i++)
238		DIGESTUpdate(&ctxDP, plaintext, plaintext_len);
239	DIGESTFinal(DP, &ctxDP);
240
241	/* 16. */
242	Pp = P = alloca(plaintext_len);
243	for (i = plaintext_len; i >= MIXCHARS; i -= MIXCHARS) {
244		Pp = (char *)(memcpy(Pp, DP, MIXCHARS)) + MIXCHARS;
245	}
246	memcpy(Pp, DP, i);
247
248	/* 17. - 19. */
249	DIGESTInit(&ctxDS);
250	for (i = 0; i < 16 + (uint8_t)A[0]; i++)
251		DIGESTUpdate(&ctxDS, salt, salt_len);
252	DIGESTFinal(DS, &ctxDS);
253
254	/* 20. */
255	Sp = S = alloca(salt_len);
256	for (i = salt_len; i >= MIXCHARS; i -= MIXCHARS) {
257		Sp = (char *)(memcpy(Sp, DS, MIXCHARS)) + MIXCHARS;
258	}
259	memcpy(Sp, DS, i);
260
261	/*  21. */
262	for (i = 0; i < rounds; i++) {
263		DIGESTInit(&ctxC);
264
265		if ((i & 1) != 0) {
266			DIGESTUpdate(&ctxC, P, plaintext_len);
267		} else {
268			if (i == 0)
269				DIGESTUpdate(&ctxC, A, MIXCHARS);
270			else
271				DIGESTUpdate(&ctxC, DP, MIXCHARS);
272		}
273
274		if (i % 3 != 0) {
275			DIGESTUpdate(&ctxC, S, salt_len);
276		}
277
278		if (i % 7 != 0) {
279			DIGESTUpdate(&ctxC, P, plaintext_len);
280		}
281
282		if ((i & 1) != 0) {
283			if (i == 0)
284				DIGESTUpdate(&ctxC, A, MIXCHARS);
285			else
286				DIGESTUpdate(&ctxC, DP, MIXCHARS);
287		} else {
288			DIGESTUpdate(&ctxC, P, plaintext_len);
289		}
290		DIGESTFinal(DP, &ctxC);
291	}
292
293	/* 22. Now make the output string */
294	if (custom_rounds) {
295		(void) snprintf(ctbuffer, ctbufflen,
296		    "%s$rounds=%zu$", crypt_alg_magic, rounds);
297	} else {
298		(void) snprintf(ctbuffer, ctbufflen,
299		    "%s$", crypt_alg_magic);
300	}
301	(void) strncat(ctbuffer, (const char *)salt, salt_len);
302	(void) strlcat(ctbuffer, "$", ctbufflen);
303
304	p = ctbuffer + strlen(ctbuffer);
305	ctbufflen -= strlen(ctbuffer);
306
307#ifdef CRYPT_SHA256
308	b64_from_24bit(DP[ 0], DP[10], DP[20], 4);
309	b64_from_24bit(DP[21], DP[ 1], DP[11], 4);
310	b64_from_24bit(DP[12], DP[22], DP[ 2], 4);
311	b64_from_24bit(DP[ 3], DP[13], DP[23], 4);
312	b64_from_24bit(DP[24], DP[ 4], DP[14], 4);
313	b64_from_24bit(DP[15], DP[25], DP[ 5], 4);
314	b64_from_24bit(DP[ 6], DP[16], DP[26], 4);
315	b64_from_24bit(DP[27], DP[ 7], DP[17], 4);
316	b64_from_24bit(DP[18], DP[28], DP[ 8], 4);
317	b64_from_24bit(DP[ 9], DP[19], DP[29], 4);
318	b64_from_24bit(0, DP[31], DP[30], 3);
319#elif CRYPT_SHA512
320	b64_from_24bit(DP[ 0], DP[21], DP[42], 4);
321	b64_from_24bit(DP[22], DP[43], DP[ 1], 4);
322	b64_from_24bit(DP[44], DP[ 2], DP[23], 4);
323	b64_from_24bit(DP[ 3], DP[24], DP[45], 4);
324	b64_from_24bit(DP[25], DP[46], DP[ 4], 4);
325	b64_from_24bit(DP[47], DP[ 5], DP[26], 4);
326	b64_from_24bit(DP[ 6], DP[27], DP[48], 4);
327	b64_from_24bit(DP[28], DP[49], DP[ 7], 4);
328	b64_from_24bit(DP[50], DP[ 8], DP[29], 4);
329	b64_from_24bit(DP[ 9], DP[30], DP[51], 4);
330	b64_from_24bit(DP[31], DP[52], DP[10], 4);
331	b64_from_24bit(DP[53], DP[11], DP[32], 4);
332	b64_from_24bit(DP[12], DP[33], DP[54], 4);
333	b64_from_24bit(DP[34], DP[55], DP[13], 4);
334	b64_from_24bit(DP[56], DP[14], DP[35], 4);
335	b64_from_24bit(DP[15], DP[36], DP[57], 4);
336	b64_from_24bit(DP[37], DP[58], DP[16], 4);
337	b64_from_24bit(DP[59], DP[17], DP[38], 4);
338	b64_from_24bit(DP[18], DP[39], DP[60], 4);
339	b64_from_24bit(DP[40], DP[61], DP[19], 4);
340	b64_from_24bit(DP[62], DP[20], DP[41], 4);
341	b64_from_24bit(0, 0, DP[63], 2);
342#endif
343	*p = '\0';
344
345	(void) memset(A, 0, sizeof (A));
346	(void) memset(B, 0, sizeof (B));
347	(void) memset(DP, 0, sizeof (DP));
348	(void) memset(DS, 0, sizeof (DS));
349
350	return (ctbuffer);
351}
352
353char *
354crypt_gensalt_impl(char *gsbuffer,
355	    size_t gsbufflen,
356	    const char *oldsalt,
357	    const struct passwd *userinfo,
358	    const char **params)
359{
360	int fd;
361	int err;
362	ssize_t got;
363	uint64_t rndval;
364	uint32_t confrounds = 0;
365	uint32_t saltrounds;
366	char rndstr[sizeof (rndval) + 1];
367	int i;
368
369	for (i = 0; params != NULL && params[i] != NULL; i++) {
370		if (strncmp(params[i], ROUNDS, ROUNDSLEN) == 0) {
371			confrounds = getrounds(params[i]);
372		} else {
373			errno = EINVAL;
374			return (NULL);
375		}
376	}
377
378	/*
379	 * If the config file has a higher value for rounds= than what
380	 * was in the old salt use that, otherwise keep what was in the
381	 * old salt.
382	 */
383	saltrounds = getrounds(oldsalt);
384	if (confrounds > saltrounds) {
385		saltrounds = confrounds;
386	}
387
388	if ((fd = open("/dev/urandom", O_RDONLY)) == -1) {
389		return (NULL);
390	}
391
392	got = read(fd, &rndval, sizeof (rndval));
393	if (got < sizeof (rndval)) {
394		err = errno;
395		(void) close(fd);
396		errno = err;
397		return (NULL);
398	}
399	(void) close(fd);
400
401	to64((char *)&rndstr, rndval, sizeof (rndval));
402	rndstr[sizeof (rndstr) - 1] = 0;
403
404	if (saltrounds > 0) {
405		if (snprintf(gsbuffer, gsbufflen,
406		    "%s$rounds=%d$",
407		    crypt_alg_magic, saltrounds) >= gsbufflen)
408			goto fail;
409	} else {
410		if (snprintf(gsbuffer, gsbufflen,
411		    "%s$", crypt_alg_magic) >= gsbufflen)
412			goto fail;
413	}
414	if (strlcat(gsbuffer, rndstr, gsbufflen) >= gsbufflen)
415		goto fail;
416	if (strlcat(gsbuffer, "$", gsbufflen) >= gsbufflen)
417		goto fail;
418
419	return (gsbuffer);
420
421fail:
422	(void) memset(gsbuffer, 0, gsbufflen);
423	return (gsbuffer);
424}
425