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