Annotation of src/usr.bin/openssl/passwd.c, Revision 1.3
1.3 ! jsing 1: /* $OpenBSD: passwd.c,v 1.2 2014/10/22 13:54:03 jsing Exp $ */
1.1 jsing 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:
1.3 ! jsing 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: }
1.1 jsing 134:
135: int passwd_main(int, char **);
136:
137: int
138: passwd_main(int argc, char **argv)
139: {
1.3 ! jsing 140: char *passwd = NULL, **passwds = NULL;
1.1 jsing 141: char *salt_malloc = NULL, *passwd_malloc = NULL;
142: size_t passwd_malloc_size = 0;
143: BIO *in = NULL, *out = NULL;
1.3 ! jsing 144: int badopt = 0;
! 145: int passed_salt = 0;
1.1 jsing 146: size_t pw_maxlen = 0;
1.3 ! jsing 147: int argsused;
! 148: int ret = 1;
1.1 jsing 149:
1.3 ! jsing 150: memset(&passwd_config, 0, sizeof(passwd_config));
! 151:
! 152: if (options_parse(argc, argv, passwd_options, NULL, &argsused) != 0) {
! 153: passwd_usage();
1.1 jsing 154: goto err;
1.3 ! jsing 155: }
1.1 jsing 156:
1.3 ! jsing 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 */
1.1 jsing 168:
1.3 ! jsing 169: /* Reject unsupported algorithms */
1.1 jsing 170: #ifdef OPENSSL_NO_DES
1.3 ! jsing 171: if (passwd_config.usecrypt)
1.1 jsing 172: badopt = 1;
173: #endif
174: #ifdef NO_MD5CRYPT_1
1.3 ! jsing 175: if (passwd_config.use1 || passwd_config.useapr1)
1.1 jsing 176: badopt = 1;
177: #endif
178:
179: if (badopt) {
1.3 ! jsing 180: passwd_usage();
! 181: goto err;
! 182: }
1.1 jsing 183:
1.3 ! jsing 184: if ((out = BIO_new(BIO_s_file())) == NULL)
1.1 jsing 185: goto err;
1.3 ! jsing 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)
1.1 jsing 190: goto err;
1.3 ! jsing 191: if (passwd_config.infile != NULL) {
! 192: assert(passwd_config.in_stdin == 0);
! 193: if (BIO_read_filename(in, passwd_config.infile) <= 0)
1.1 jsing 194: goto err;
195: } else {
1.3 ! jsing 196: assert(passwd_config.in_stdin);
1.1 jsing 197: BIO_set_fp(in, stdin, BIO_NOCLOSE);
198: }
199: }
1.3 ! jsing 200: if (passwd_config.usecrypt)
1.1 jsing 201: pw_maxlen = 8;
1.3 ! jsing 202: else if (passwd_config.use1 || passwd_config.useapr1)
1.1 jsing 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: }
1.3 ! jsing 215: if (in == NULL && passwds == NULL) {
1.1 jsing 216: /* build a null-terminated list */
217: static char *passwds_static[2] = {NULL, NULL};
218:
219: passwds = passwds_static;
220: if (in == NULL)
1.3 ! jsing 221: if (EVP_read_pw_string(passwd_malloc,
! 222: passwd_malloc_size, "Password: ",
! 223: !(passed_salt || passwd_config.noverify)) != 0)
1.1 jsing 224: goto err;
225: passwds[0] = passwd_malloc;
226: }
227: if (in == NULL) {
228: assert(passwds != NULL);
229: assert(*passwds != NULL);
230:
1.3 ! jsing 231: do { /* loop over list of passwords */
1.1 jsing 232: passwd = *passwds++;
1.3 ! jsing 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))
1.1 jsing 238: goto err;
1.3 ! jsing 239: } while (*passwds != NULL);
! 240: } else {
1.1 jsing 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:
1.3 ! jsing 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))
1.1 jsing 264: goto err;
265: }
266: done = (r <= 0);
1.3 ! jsing 267: } while (!done);
1.1 jsing 268: }
269: ret = 0;
270:
271: err:
272: ERR_print_errors(bio_err);
1.3 ! jsing 273:
1.1 jsing 274: free(salt_malloc);
275: free(passwd_malloc);
1.3 ! jsing 276:
1.1 jsing 277: BIO_free(in);
1.3 ! jsing 278: BIO_free_all(out);
1.1 jsing 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: }
1.2 jsing 418: arc4random_buf(*salt_p, 2);
1.1 jsing 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: }
1.2 jsing 434: arc4random_buf(*salt_p, 8);
1.1 jsing 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