xref: /openbsd/usr.bin/openssl/passwd.c (revision cecf84d4)
1 /* $OpenBSD: passwd.c,v 1.3 2015/01/05 15:25:39 jsing Exp $ */
2 
3 #if defined OPENSSL_NO_MD5
4 #define NO_MD5CRYPT_1
5 #endif
6 
7 #if !defined(OPENSSL_NO_DES) || !defined(NO_MD5CRYPT_1)
8 
9 #include <assert.h>
10 #include <string.h>
11 
12 #include "apps.h"
13 
14 #include <openssl/bio.h>
15 #include <openssl/err.h>
16 #include <openssl/evp.h>
17 
18 #ifndef OPENSSL_NO_DES
19 #include <openssl/des.h>
20 #endif
21 
22 #ifndef NO_MD5CRYPT_1
23 #include <openssl/md5.h>
24 #endif
25 
26 static unsigned const char cov_2char[64] = {
27 	/* from crypto/des/fcrypt.c */
28 	0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
29 	0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44,
30 	0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C,
31 	0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54,
32 	0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62,
33 	0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A,
34 	0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72,
35 	0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A
36 };
37 
38 static int
39 do_passwd(int passed_salt, char **salt_p, char **salt_malloc_p,
40     char *passwd, BIO * out, int quiet, int table, int reverse,
41     size_t pw_maxlen, int usecrypt, int use1, int useapr1);
42 
43 static struct {
44 	char *infile;
45 	int in_stdin;
46 	int noverify;
47 	int quiet;
48 	int reverse;
49 	char *salt;
50 	int table;
51 	int use1;
52 	int useapr1;
53 	int usecrypt;
54 } passwd_config;
55 
56 static struct option passwd_options[] = {
57 #ifndef NO_MD5CRYPT_1
58 	{
59 		.name = "1",
60 		.desc = "Use MD5 based BSD password algorithm 1",
61 		.type = OPTION_FLAG,
62 		.opt.flag = &passwd_config.use1,
63 	},
64 	{
65 		.name = "apr1",
66 		.desc = "Use apr1 algorithm (Apache variant of BSD algorithm)",
67 		.type = OPTION_FLAG,
68 		.opt.flag = &passwd_config.useapr1,
69 	},
70 #endif
71 #ifndef OPENSSL_NO_DES
72 	{
73 		.name = "crypt",
74 		.desc = "Use crypt algorithm (default)",
75 		.type = OPTION_FLAG,
76 		.opt.flag = &passwd_config.usecrypt,
77 	},
78 #endif
79 	{
80 		.name = "in",
81 		.argname = "file",
82 		.desc = "Read passwords from specified file",
83 		.type = OPTION_ARG,
84 		.opt.arg = &passwd_config.infile,
85 	},
86 	{
87 		.name = "noverify",
88 		.desc = "Do not verify password",
89 		.type = OPTION_FLAG,
90 		.opt.flag = &passwd_config.noverify,
91 	},
92 	{
93 		.name = "quiet",
94 		.desc = "Do not output warnings",
95 		.type = OPTION_FLAG,
96 		.opt.flag = &passwd_config.quiet,
97 	},
98 	{
99 		.name = "reverse",
100 		.desc = "Reverse table columns (requires -table)",
101 		.type = OPTION_FLAG,
102 		.opt.flag = &passwd_config.reverse,
103 	},
104 	{
105 		.name = "salt",
106 		.argname = "string",
107 		.desc = "Use specified salt",
108 		.type = OPTION_ARG,
109 		.opt.arg = &passwd_config.salt,
110 	},
111 	{
112 		.name = "stdin",
113 		.desc = "Read passwords from stdin",
114 		.type = OPTION_FLAG,
115 		.opt.flag = &passwd_config.in_stdin,
116 	},
117 	{
118 		.name = "table",
119 		.desc = "Output cleartext and hashed passwords (tab separated)",
120 		.type = OPTION_FLAG,
121 		.opt.flag = &passwd_config.table,
122 	},
123 	{ NULL },
124 };
125 
126 static void
127 passwd_usage(void)
128 {
129         fprintf(stderr, "usage: passwd [-1 | -apr1 | -crypt] [-in file] "
130 	    "[-noverify] [-quiet]\n"
131 	    "    [-reverse] [-salt string] [-stdin] [-table] [password]\n\n");
132         options_usage(passwd_options);
133 }
134 
135 int passwd_main(int, char **);
136 
137 int
138 passwd_main(int argc, char **argv)
139 {
140 	char *passwd = NULL, **passwds = NULL;
141 	char *salt_malloc = NULL, *passwd_malloc = NULL;
142 	size_t passwd_malloc_size = 0;
143 	BIO *in = NULL, *out = NULL;
144 	int badopt = 0;
145 	int passed_salt = 0;
146 	size_t pw_maxlen = 0;
147 	int argsused;
148 	int ret = 1;
149 
150 	memset(&passwd_config, 0, sizeof(passwd_config));
151 
152 	if (options_parse(argc, argv, passwd_options, NULL, &argsused) != 0) {
153 		passwd_usage();
154 		goto err;
155 	}
156 
157 	if (argsused < argc)
158 		passwds = &argv[argsused];
159 	if (passwd_config.salt != NULL)
160 		passed_salt = 1;
161 
162 	if (!passwd_config.usecrypt && !passwd_config.use1 &&
163 	    !passwd_config.useapr1)
164 		passwd_config.usecrypt = 1;	/* use default */
165 	if (passwd_config.usecrypt + passwd_config.use1 +
166 	    passwd_config.useapr1 > 1)
167 		badopt = 1;	/* conflicting options */
168 
169 	/* Reject unsupported algorithms */
170 #ifdef OPENSSL_NO_DES
171 	if (passwd_config.usecrypt)
172 		badopt = 1;
173 #endif
174 #ifdef NO_MD5CRYPT_1
175 	if (passwd_config.use1 || passwd_config.useapr1)
176 		badopt = 1;
177 #endif
178 
179 	if (badopt) {
180 		passwd_usage();
181 		goto err;
182 	}
183 
184 	if ((out = BIO_new(BIO_s_file())) == NULL)
185 		goto err;
186 	BIO_set_fp(out, stdout, BIO_NOCLOSE | BIO_FP_TEXT);
187 
188 	if (passwd_config.infile != NULL || passwd_config.in_stdin) {
189 		if ((in = BIO_new(BIO_s_file())) == NULL)
190 			goto err;
191 		if (passwd_config.infile != NULL) {
192 			assert(passwd_config.in_stdin == 0);
193 			if (BIO_read_filename(in, passwd_config.infile) <= 0)
194 				goto err;
195 		} else {
196 			assert(passwd_config.in_stdin);
197 			BIO_set_fp(in, stdin, BIO_NOCLOSE);
198 		}
199 	}
200 	if (passwd_config.usecrypt)
201 		pw_maxlen = 8;
202 	else if (passwd_config.use1 || passwd_config.useapr1)
203 		pw_maxlen = 256;/* arbitrary limit, should be enough for most
204 				 * passwords */
205 
206 	if (passwds == NULL) {
207 		/* no passwords on the command line */
208 
209 		passwd_malloc_size = pw_maxlen + 2;
210 		/* longer than necessary so that we can warn about truncation */
211 		passwd = passwd_malloc = malloc(passwd_malloc_size);
212 		if (passwd_malloc == NULL)
213 			goto err;
214 	}
215 	if (in == NULL && passwds == NULL) {
216 		/* build a null-terminated list */
217 		static char *passwds_static[2] = {NULL, NULL};
218 
219 		passwds = passwds_static;
220 		if (in == NULL)
221 			if (EVP_read_pw_string(passwd_malloc,
222 			    passwd_malloc_size, "Password: ",
223 			    !(passed_salt || passwd_config.noverify)) != 0)
224 				goto err;
225 		passwds[0] = passwd_malloc;
226 	}
227 	if (in == NULL) {
228 		assert(passwds != NULL);
229 		assert(*passwds != NULL);
230 
231 		do {	/* loop over list of passwords */
232 			passwd = *passwds++;
233 			if (!do_passwd(passed_salt, &passwd_config.salt,
234 			    &salt_malloc, passwd, out, passwd_config.quiet,
235 			    passwd_config.table, passwd_config.reverse,
236 			    pw_maxlen, passwd_config.usecrypt,
237 			    passwd_config.use1, passwd_config.useapr1))
238 				goto err;
239 		} while (*passwds != NULL);
240 	} else {
241 		int done;
242 
243 		assert(passwd != NULL);
244 		do {
245 			int r = BIO_gets(in, passwd, pw_maxlen + 1);
246 			if (r > 0) {
247 				char *c = (strchr(passwd, '\n'));
248 				if (c != NULL)
249 					*c = 0;	/* truncate at newline */
250 				else {
251 					/* ignore rest of line */
252 					char trash[BUFSIZ];
253 					do
254 						r = BIO_gets(in, trash, sizeof trash);
255 					while ((r > 0) && (!strchr(trash, '\n')));
256 				}
257 
258 				if (!do_passwd(passed_salt, &passwd_config.salt,
259 				    &salt_malloc, passwd, out,
260 				    passwd_config.quiet, passwd_config.table,
261 				    passwd_config.reverse, pw_maxlen,
262 				    passwd_config.usecrypt, passwd_config.use1,
263 				    passwd_config.useapr1))
264 					goto err;
265 			}
266 			done = (r <= 0);
267 		} while (!done);
268 	}
269 	ret = 0;
270 
271 err:
272 	ERR_print_errors(bio_err);
273 
274 	free(salt_malloc);
275 	free(passwd_malloc);
276 
277 	BIO_free(in);
278 	BIO_free_all(out);
279 
280 	return (ret);
281 }
282 
283 
284 #ifndef NO_MD5CRYPT_1
285 /* MD5-based password algorithm (should probably be available as a library
286  * function; then the static buffer would not be acceptable).
287  * For magic string "1", this should be compatible to the MD5-based BSD
288  * password algorithm.
289  * For 'magic' string "apr1", this is compatible to the MD5-based Apache
290  * password algorithm.
291  * (Apparently, the Apache password algorithm is identical except that the
292  * 'magic' string was changed -- the laziest application of the NIH principle
293  * I've ever encountered.)
294  */
295 static char *
296 md5crypt(const char *passwd, const char *magic, const char *salt)
297 {
298 	static char out_buf[6 + 9 + 24 + 2];	/* "$apr1$..salt..$.......md5h
299 						 * ash..........\0" */
300 	unsigned char buf[MD5_DIGEST_LENGTH];
301 	char *salt_out;
302 	int n;
303 	unsigned int i;
304 	EVP_MD_CTX md, md2;
305 	size_t passwd_len, salt_len;
306 
307 	passwd_len = strlen(passwd);
308 	out_buf[0] = '$';
309 	out_buf[1] = 0;
310 	assert(strlen(magic) <= 4);	/* "1" or "apr1" */
311 	strlcat(out_buf, magic, sizeof(out_buf));
312 	strlcat(out_buf, "$", sizeof(out_buf));
313 	strlcat(out_buf, salt, sizeof(out_buf));
314 	assert(strlen(out_buf) <= 6 + 8);	/* "$apr1$..salt.." */
315 	salt_out = out_buf + 2 + strlen(magic);
316 	salt_len = strlen(salt_out);
317 	assert(salt_len <= 8);
318 
319 	EVP_MD_CTX_init(&md);
320 	EVP_DigestInit_ex(&md, EVP_md5(), NULL);
321 	EVP_DigestUpdate(&md, passwd, passwd_len);
322 	EVP_DigestUpdate(&md, "$", 1);
323 	EVP_DigestUpdate(&md, magic, strlen(magic));
324 	EVP_DigestUpdate(&md, "$", 1);
325 	EVP_DigestUpdate(&md, salt_out, salt_len);
326 
327 	EVP_MD_CTX_init(&md2);
328 	EVP_DigestInit_ex(&md2, EVP_md5(), NULL);
329 	EVP_DigestUpdate(&md2, passwd, passwd_len);
330 	EVP_DigestUpdate(&md2, salt_out, salt_len);
331 	EVP_DigestUpdate(&md2, passwd, passwd_len);
332 	EVP_DigestFinal_ex(&md2, buf, NULL);
333 
334 	for (i = passwd_len; i > sizeof buf; i -= sizeof buf)
335 		EVP_DigestUpdate(&md, buf, sizeof buf);
336 	EVP_DigestUpdate(&md, buf, i);
337 
338 	n = passwd_len;
339 	while (n) {
340 		EVP_DigestUpdate(&md, (n & 1) ? "\0" : passwd, 1);
341 		n >>= 1;
342 	}
343 	EVP_DigestFinal_ex(&md, buf, NULL);
344 
345 	for (i = 0; i < 1000; i++) {
346 		EVP_DigestInit_ex(&md2, EVP_md5(), NULL);
347 		EVP_DigestUpdate(&md2, (i & 1) ? (unsigned const char *) passwd : buf,
348 		    (i & 1) ? passwd_len : sizeof buf);
349 		if (i % 3)
350 			EVP_DigestUpdate(&md2, salt_out, salt_len);
351 		if (i % 7)
352 			EVP_DigestUpdate(&md2, passwd, passwd_len);
353 		EVP_DigestUpdate(&md2, (i & 1) ? buf : (unsigned const char *) passwd,
354 		    (i & 1) ? sizeof buf : passwd_len);
355 		EVP_DigestFinal_ex(&md2, buf, NULL);
356 	}
357 	EVP_MD_CTX_cleanup(&md2);
358 
359 	{
360 		/* transform buf into output string */
361 
362 		unsigned char buf_perm[sizeof buf];
363 		int dest, source;
364 		char *output;
365 
366 		/* silly output permutation */
367 		for (dest = 0, source = 0; dest < 14; dest++, source = (source + 6) % 17)
368 			buf_perm[dest] = buf[source];
369 		buf_perm[14] = buf[5];
370 		buf_perm[15] = buf[11];
371 		assert(16 == sizeof buf_perm);
372 
373 		output = salt_out + salt_len;
374 		assert(output == out_buf + strlen(out_buf));
375 
376 		*output++ = '$';
377 
378 		for (i = 0; i < 15; i += 3) {
379 			*output++ = cov_2char[buf_perm[i + 2] & 0x3f];
380 			*output++ = cov_2char[((buf_perm[i + 1] & 0xf) << 2) |
381 			    (buf_perm[i + 2] >> 6)];
382 			*output++ = cov_2char[((buf_perm[i] & 3) << 4) |
383 			    (buf_perm[i + 1] >> 4)];
384 			*output++ = cov_2char[buf_perm[i] >> 2];
385 		}
386 		assert(i == 15);
387 		*output++ = cov_2char[buf_perm[i] & 0x3f];
388 		*output++ = cov_2char[buf_perm[i] >> 6];
389 		*output = 0;
390 		assert(strlen(out_buf) < sizeof(out_buf));
391 	}
392 	EVP_MD_CTX_cleanup(&md);
393 
394 	return out_buf;
395 }
396 #endif
397 
398 
399 static int
400 do_passwd(int passed_salt, char **salt_p, char **salt_malloc_p,
401     char *passwd, BIO * out, int quiet, int table, int reverse,
402     size_t pw_maxlen, int usecrypt, int use1, int useapr1)
403 {
404 	char *hash = NULL;
405 
406 	assert(salt_p != NULL);
407 	assert(salt_malloc_p != NULL);
408 
409 	/* first make sure we have a salt */
410 	if (!passed_salt) {
411 #ifndef OPENSSL_NO_DES
412 		if (usecrypt) {
413 			if (*salt_malloc_p == NULL) {
414 				*salt_p = *salt_malloc_p = malloc(3);
415 				if (*salt_malloc_p == NULL)
416 					goto err;
417 			}
418 			arc4random_buf(*salt_p, 2);
419 			(*salt_p)[0] = cov_2char[(*salt_p)[0] & 0x3f];	/* 6 bits */
420 			(*salt_p)[1] = cov_2char[(*salt_p)[1] & 0x3f];	/* 6 bits */
421 			(*salt_p)[2] = 0;
422 		}
423 #endif				/* !OPENSSL_NO_DES */
424 
425 #ifndef NO_MD5CRYPT_1
426 		if (use1 || useapr1) {
427 			int i;
428 
429 			if (*salt_malloc_p == NULL) {
430 				*salt_p = *salt_malloc_p = malloc(9);
431 				if (*salt_malloc_p == NULL)
432 					goto err;
433 			}
434 			arc4random_buf(*salt_p, 8);
435 
436 			for (i = 0; i < 8; i++)
437 				(*salt_p)[i] = cov_2char[(*salt_p)[i] & 0x3f];	/* 6 bits */
438 			(*salt_p)[8] = 0;
439 		}
440 #endif				/* !NO_MD5CRYPT_1 */
441 	}
442 	assert(*salt_p != NULL);
443 
444 	/* truncate password if necessary */
445 	if ((strlen(passwd) > pw_maxlen)) {
446 		if (!quiet)
447 			/*
448 			 * XXX: really we should know how to print a size_t,
449 			 * not cast it
450 			 */
451 			BIO_printf(bio_err, "Warning: truncating password to %u characters\n", (unsigned) pw_maxlen);
452 		passwd[pw_maxlen] = 0;
453 	}
454 	assert(strlen(passwd) <= pw_maxlen);
455 
456 	/* now compute password hash */
457 #ifndef OPENSSL_NO_DES
458 	if (usecrypt)
459 		hash = DES_crypt(passwd, *salt_p);
460 #endif
461 #ifndef NO_MD5CRYPT_1
462 	if (use1 || useapr1)
463 		hash = md5crypt(passwd, (use1 ? "1" : "apr1"), *salt_p);
464 #endif
465 	assert(hash != NULL);
466 
467 	if (table && !reverse)
468 		BIO_printf(out, "%s\t%s\n", passwd, hash);
469 	else if (table && reverse)
470 		BIO_printf(out, "%s\t%s\n", hash, passwd);
471 	else
472 		BIO_printf(out, "%s\n", hash);
473 	return 1;
474 
475 err:
476 	return 0;
477 }
478 #else
479 
480 int
481 passwd_main(int argc, char **argv)
482 {
483 	fputs("Program not available.\n", stderr)
484 	return (1);
485 }
486 #endif
487