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
to64(char * s,uint64_t v,int n)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 *
crypt_genhash_impl(char * ctbuffer,size_t ctbufflen,const char * plaintext,const char * switchsalt,const char ** params)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 *
crypt_gensalt_impl(char * gsbuffer,size_t gsbufflen,const char * oldsalt,const struct passwd * userinfo,const char ** params)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