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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
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 * ----------------------------------------------------------------------------
35 * $FreeBSD: crypt.c,v 1.5 1996/10/14 08:34:02 phk Exp $
40 * Implements the specification from:
42 * From http://people.redhat.com/drepper/SHA-crypt.txt
44 * Portions of the code taken from inspired by or verified against the
45 * source in the above document which is licensed as:
47 * "Released into the Public Domain by Ulrich Drepper <drepper@redhat.com>."
51 #include <sys/types.h>
53 #include <sys/sysmacros.h>
65 #define MAX_SALT_LEN 16
66 #define ROUNDS_DEFAULT 5000
67 #define ROUNDS_MIN 1000
68 #define ROUNDS_MAX 999999999
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
78 static const char crypt_alg_magic
[] = "$5";
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
88 static const char crypt_alg_magic
[] = "$6";
91 #error "One of CRYPT_256 or CRYPT_512 must be defined"
94 static const int crypt_alg_magic_len
= sizeof (crypt_alg_magic
) - 1;
97 static uchar_t b64t
[] = /* 0 ... 63 => ascii - 64 */
98 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
100 #define b64_from_24bit(B2, B1, B0, N) \
102 uint_t w = ((B2) << 16) | ((B1) << 8) | (B0); \
104 while (--n >= 0 && ctbufflen > 0) { \
105 *p++ = b64t[w & 0x3f]; \
112 to64(char *s
, uint64_t v
, int n
)
115 *s
++ = b64t
[v
& 0x3f];
120 #define ROUNDS "rounds="
121 #define ROUNDSLEN (sizeof (ROUNDS) - 1)
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
129 getrounds(const char *s
)
137 if ((r
= strstr(s
, ROUNDS
)) == NULL
) {
141 if (strncmp(r
, ROUNDS
, ROUNDSLEN
) != 0) {
147 val
= strtol(p
, &e
, 10);
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 '$'
152 if (errno
!= 0 || val
< 0 ||
153 !(*e
== '\0' || *e
== ',' || *e
== '$')) {
157 return ((uint32_t)val
);
163 crypt_genhash_impl(char *ctbuffer
,
165 const char *plaintext
,
166 const char *switchsalt
,
169 int salt_len
, plaintext_len
, i
;
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
;
178 boolean_t custom_rounds
= B_FALSE
;
183 /* Refine the salt */
184 salt
= (char *)switchsalt
;
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;
191 srounds
= getrounds(salt
);
193 rounds
= MAX(ROUNDS_MIN
, MIN(srounds
, ROUNDS_MAX
));
194 custom_rounds
= B_TRUE
;
195 p
= strchr(salt
, '$');
200 salt_len
= MIN(strcspn(salt
, "$"), MAX_SALT_LEN
);
201 plaintext_len
= strlen(plaintext
);
206 /* 2. The password first, since that is what is most unknown */
207 DIGESTUpdate(&ctxA
, plaintext
, plaintext_len
);
209 /* 3. Then the raw salt */
210 DIGESTUpdate(&ctxA
, salt
, salt_len
);
214 DIGESTUpdate(&ctxB
, plaintext
, plaintext_len
);
215 DIGESTUpdate(&ctxB
, salt
, salt_len
);
216 DIGESTUpdate(&ctxB
, plaintext
, plaintext_len
);
217 DIGESTFinal(B
, &ctxB
);
220 for (i
= plaintext_len
; i
> MIXCHARS
; i
-= MIXCHARS
)
221 DIGESTUpdate(&ctxA
, B
, MIXCHARS
);
222 DIGESTUpdate(&ctxA
, B
, i
);
225 for (i
= plaintext_len
; i
> 0; i
>>= 1) {
227 DIGESTUpdate(&ctxA
, B
, MIXCHARS
);
229 DIGESTUpdate(&ctxA
, plaintext
, plaintext_len
);
234 DIGESTFinal(A
, &ctxA
);
238 for (i
= 0; i
< plaintext_len
; i
++)
239 DIGESTUpdate(&ctxDP
, plaintext
, plaintext_len
);
240 DIGESTFinal(DP
, &ctxDP
);
243 Pp
= P
= alloca(plaintext_len
);
244 for (i
= plaintext_len
; i
>= MIXCHARS
; i
-= MIXCHARS
) {
245 Pp
= (char *)(memcpy(Pp
, DP
, MIXCHARS
)) + MIXCHARS
;
247 (void) memcpy(Pp
, DP
, i
);
251 for (i
= 0; i
< 16 + (uint8_t)A
[0]; i
++)
252 DIGESTUpdate(&ctxDS
, salt
, salt_len
);
253 DIGESTFinal(DS
, &ctxDS
);
256 Sp
= S
= alloca(salt_len
);
257 for (i
= salt_len
; i
>= MIXCHARS
; i
-= MIXCHARS
) {
258 Sp
= (char *)(memcpy(Sp
, DS
, MIXCHARS
)) + MIXCHARS
;
260 (void) memcpy(Sp
, DS
, i
);
263 for (i
= 0; i
< rounds
; i
++) {
267 DIGESTUpdate(&ctxC
, P
, plaintext_len
);
270 DIGESTUpdate(&ctxC
, A
, MIXCHARS
);
272 DIGESTUpdate(&ctxC
, DP
, MIXCHARS
);
276 DIGESTUpdate(&ctxC
, S
, salt_len
);
280 DIGESTUpdate(&ctxC
, P
, plaintext_len
);
285 DIGESTUpdate(&ctxC
, A
, MIXCHARS
);
287 DIGESTUpdate(&ctxC
, DP
, MIXCHARS
);
289 DIGESTUpdate(&ctxC
, P
, plaintext_len
);
291 DIGESTFinal(DP
, &ctxC
);
294 /* 22. Now make the output string */
296 (void) snprintf(ctbuffer
, ctbufflen
,
297 "%s$rounds=%zu$", crypt_alg_magic
, rounds
);
299 (void) snprintf(ctbuffer
, ctbufflen
,
300 "%s$", crypt_alg_magic
);
302 (void) strncat(ctbuffer
, (const char *)salt
, salt_len
);
303 (void) strlcat(ctbuffer
, "$", ctbufflen
);
305 p
= ctbuffer
+ strlen(ctbuffer
);
306 ctbufflen
-= strlen(ctbuffer
);
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);
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);
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
));
357 crypt_gensalt_impl(char *gsbuffer
,
360 const struct passwd
*userinfo
,
367 uint32_t confrounds
= 0;
369 char rndstr
[sizeof (rndval
) + 1];
372 for (i
= 0; params
!= NULL
&& params
[i
] != NULL
; i
++) {
373 if (strncmp(params
[i
], ROUNDS
, ROUNDSLEN
) == 0) {
374 confrounds
= getrounds(params
[i
]);
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
386 saltrounds
= getrounds(oldsalt
);
387 if (confrounds
> saltrounds
) {
388 saltrounds
= confrounds
;
391 if ((fd
= open("/dev/urandom", O_RDONLY
)) == -1) {
395 got
= read(fd
, &rndval
, sizeof (rndval
));
396 if (got
< sizeof (rndval
)) {
404 to64((char *)&rndstr
, rndval
, sizeof (rndval
));
405 rndstr
[sizeof (rndstr
) - 1] = 0;
407 if (saltrounds
> 0) {
408 if (snprintf(gsbuffer
, gsbufflen
,
410 crypt_alg_magic
, saltrounds
) >= gsbufflen
)
413 if (snprintf(gsbuffer
, gsbufflen
,
414 "%s$", crypt_alg_magic
) >= gsbufflen
)
417 if (strlcat(gsbuffer
, rndstr
, gsbufflen
) >= gsbufflen
)
419 if (strlcat(gsbuffer
, "$", gsbufflen
) >= gsbufflen
)
425 (void) memset(gsbuffer
, 0, gsbufflen
);