"Fossies" - the Fresh Open Source Software Archive

Member "whois/mkpasswd.c" (16 Feb 2021, 15192 Bytes) of package /linux/privat/whois_5.5.9.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "mkpasswd.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 5.5.6_vs_5.5.8.

    1 /*
    2  * Copyright (C) 2001-2021 Marco d'Itri <md@linux.it>.
    3  *
    4  * This program is free software; you can redistribute it and/or modify
    5  * it under the terms of the GNU General Public License as published by
    6  * the Free Software Foundation; either version 2 of the License, or
    7  * (at your option) any later version.
    8  *
    9  * This program is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License along
   15  * with this program; if not, write to the Free Software Foundation, Inc.,
   16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   17  */
   18 
   19 /* for crypt, snprintf and strcasecmp */
   20 #define _XOPEN_SOURCE 500
   21 #define _BSD_SOURCE 1
   22 #define _DEFAULT_SOURCE 1
   23 #define __EXTENSIONS__ 1
   24 
   25 /* System library */
   26 #include <stdio.h>
   27 #include <stdlib.h>
   28 #include <unistd.h>
   29 #include "config.h"
   30 #ifdef HAVE_GETOPT_LONG
   31 #include <getopt.h>
   32 #endif
   33 #include <fcntl.h>
   34 #include <string.h>
   35 #include <strings.h>
   36 #include <time.h>
   37 #include <sys/types.h>
   38 #ifdef HAVE_XCRYPT_H
   39 #include <xcrypt.h>
   40 #include <sys/stat.h>
   41 #endif
   42 #ifdef HAVE_CRYPT_H
   43 #include <crypt.h>
   44 #endif
   45 #ifdef HAVE_GETTIMEOFDAY
   46 #include <sys/time.h>
   47 #endif
   48 
   49 /* Application-specific */
   50 #include "version.h"
   51 #include "utils.h"
   52 
   53 /* Global variables */
   54 #ifdef HAVE_GETOPT_LONG
   55 static const struct option longopts[] = {
   56     {"method",      optional_argument,  NULL, 'm'},
   57     /* for backward compatibility with versions < 4.7.25 (< 20080321): */
   58     {"hash",        optional_argument,  NULL, 'H'},
   59     {"help",        no_argument,        NULL, 'h'},
   60     {"password-fd", required_argument,  NULL, 'P'},
   61     {"stdin",       no_argument,        NULL, 's'},
   62     {"salt",        required_argument,  NULL, 'S'},
   63     {"rounds",      required_argument,  NULL, 'R'},
   64     {"version",     no_argument,        NULL, 'V'},
   65     {NULL,      0,          NULL, 0  }
   66 };
   67 #else
   68 extern char *optarg;
   69 extern int optind;
   70 #endif
   71 
   72 static const char valid_salts[] = "abcdefghijklmnopqrstuvwxyz"
   73 "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
   74 
   75 struct crypt_method {
   76     const char *method;     /* short name used by the command line option */
   77     const char *prefix;     /* salt prefix */
   78     const unsigned int minlen;  /* minimum salt length */
   79     const unsigned int maxlen;  /* maximum salt length */
   80     const unsigned int rounds;  /* supports a variable number of rounds */
   81     const char *desc;       /* long description for the methods list */
   82 };
   83 
   84 /* XCRYPT_VERSION_NUM is defined in crypt.h from libxcrypt */
   85 #if defined XCRYPT_VERSION_NUM
   86 # define HAVE_SHA_CRYPT
   87 # define HAVE_BCRYPT
   88 # define HAVE_BSDICRYPT
   89 #endif
   90 
   91 static const struct crypt_method methods[] = {
   92     /* method       prefix  minlen, maxlen  rounds description */
   93 #ifdef CRYPT_GENSALT_IMPLEMENTS_DEFAULT_PREFIX
   94     { "auto",       NULL,   0,  0,  0, NULL },
   95 #endif
   96     /* compatibility aliases for mkpasswd versions < 5.4.0 */
   97     { "des",        "", 2,  2,  0, NULL },
   98     { "md5",        "$1$",  8,  8,  0, NULL },
   99 #if defined XCRYPT_VERSION_NUM
  100     { "yescrypt",   "$y$",  0,  0,  1, "Yescrypt" },
  101 #if XCRYPT_VERSION_NUM >= ((4 << 16) | 4)
  102     { "gost-yescrypt",  "$gy$", 0,  0,  1, "GOST Yescrypt" },
  103 #endif
  104     { "scrypt",     "$7$",  0,  0,  1, "scrypt" },
  105 #endif
  106 #ifdef HAVE_BCRYPT_OBSOLETE
  107     /* http://marc.info/?l=openbsd-misc&m=139320023202696 */
  108     { "bf",     "$2a$", 22, 22, 2, "bcrypt" },
  109 #endif
  110 #ifdef HAVE_BCRYPT
  111     { "bcrypt",     "$2b$", 22, 22, 2, "bcrypt" },
  112     { "bcrypt-a",   "$2a$", 22, 22, 2, "bcrypt (obsolete $2a$ version)" },
  113 #endif
  114 #if defined HAVE_SHA_CRYPT
  115     /* http://people.redhat.com/drepper/SHA-crypt.txt */
  116     { "sha512crypt",    "$6$",  8,  16, 1, "SHA-512" },
  117     { "sha256crypt",    "$5$",  8,  16, 1, "SHA-256" },
  118     /* compatibility aliases for mkpasswd versions < 5.4.0 */
  119     { "sha-256",    "$5$",  8,  16, 1, NULL },
  120     { "sha-512",    "$6$",  8,  16, 1, NULL },
  121 #endif
  122 #if (defined __SVR4 && defined __sun) || defined XCRYPT_VERSION_NUM
  123     /* http://www.crypticide.com/dropsafe/article/1389 */
  124     /*
  125      * Actually the maximum salt length is arbitrary, but Solaris by default
  126      * always uses 8 characters:
  127      * http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/ \
  128      *   usr/src/lib/crypt_modules/sunmd5/sunmd5.c#crypt_gensalt_impl
  129      */
  130     { "sunmd5",     "$md5$", 8, 8,  1, "SunMD5" },
  131 #endif
  132     { "md5crypt",   "$1$",  8,  8,  0, "MD5" },
  133 #ifdef HAVE_BSDICRYPT
  134     { "bsdicrypt",      "_",    0,  0,  0,
  135     N_("BSDI extended DES-based crypt(3)") },
  136 #endif
  137     { "descrypt",   "", 2,  2,  0,
  138     N_("standard 56 bit DES-based crypt(3)") },
  139 #if defined FreeBSD || defined XCRYPT_VERSION_NUM
  140     { "nt",     "$3$",  0,  0,  0, "NT-Hash" },
  141 #endif
  142     { NULL,     NULL,   0,  0,  0, NULL }
  143 };
  144 
  145 void generate_salt(char *const buf, const unsigned int len);
  146 void *get_random_bytes(const unsigned int len);
  147 void NORETURN display_help(int error);
  148 void display_version(void);
  149 void display_methods(void);
  150 char *read_line(FILE *fp);
  151 
  152 int main(int argc, char *argv[])
  153 {
  154     int ch, i;
  155     int password_fd = -1;
  156     unsigned int salt_minlen = 0;
  157     unsigned int salt_maxlen = 0;
  158     unsigned int rounds_support = 0;
  159     const char *salt_prefix = NULL;
  160     const char *salt_arg = NULL;
  161     unsigned int rounds = 0;
  162     char *salt = NULL;
  163     char rounds_str[30];
  164     char *password = NULL;
  165 
  166 #ifdef ENABLE_NLS
  167     setlocale(LC_ALL, "");
  168     bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
  169     textdomain(NLS_CAT_NAME);
  170 #endif
  171 
  172     /* prepend options from environment */
  173     argv = merge_args(getenv("MKPASSWD_OPTIONS"), argv, &argc);
  174 
  175     while ((ch = GETOPT_LONGISH(argc, argv, "hH:m:5P:R:sS:V", longopts, NULL))
  176         > 0) {
  177     switch (ch) {
  178     case '5':
  179         optarg = (char *) "md5crypt";
  180         /* fall through */
  181     case 'm':
  182     case 'H':
  183         if (!optarg || strcaseeq("help", optarg)) {
  184         display_methods();
  185         exit(0);
  186         }
  187 #if defined HAVE_LINUX_CRYPT_GENSALT || defined HAVE_SOLARIS_CRYPT_GENSALT
  188         if (optarg[0] == '$'
  189             && strlen(optarg) > 2
  190             && *(optarg + strlen(optarg) - 1) == '$') {
  191         salt_prefix = NOFAIL(strdup(optarg));
  192         salt_minlen = 0;
  193         salt_maxlen = 0;
  194         rounds_support = 0;
  195         break;
  196         }
  197 #endif
  198         for (i = 0; methods[i].method != NULL; i++)
  199         if (strcaseeq(methods[i].method, optarg)) {
  200             salt_prefix = methods[i].prefix;
  201             salt_minlen = methods[i].minlen;
  202             salt_maxlen = methods[i].maxlen;
  203             rounds_support = methods[i].rounds;
  204             break;
  205         }
  206         if (!salt_prefix) {
  207         fprintf(stderr, _("Invalid method '%s'.\n"), optarg);
  208         exit(1);
  209         }
  210         break;
  211     case 'P':
  212         {
  213         char *p;
  214         password_fd = strtol(optarg, &p, 10);
  215         if (p == NULL || *p != '\0' || password_fd < 0) {
  216             fprintf(stderr, _("Invalid number '%s'.\n"), optarg);
  217             exit(1);
  218         }
  219         }
  220         break;
  221     case 'R':
  222         {
  223         char *p;
  224         long r;
  225 
  226         r = strtol(optarg, &p, 10);
  227         if (p == NULL || *p != '\0' || r < 0) {
  228             fprintf(stderr, _("Invalid number '%s'.\n"), optarg);
  229             exit(1);
  230         }
  231         rounds = r;
  232         }
  233         break;
  234     case 's':
  235         password_fd = 0;
  236         break;
  237     case 'S':
  238         salt_arg = optarg;
  239         break;
  240     case 'V':
  241         display_version();
  242         exit(0);
  243     case 'h':
  244         display_help(EXIT_SUCCESS);
  245     default:
  246         fprintf(stderr, _("Try '%s --help' for more information.\n"),
  247             argv[0]);
  248         exit(1);
  249     }
  250     }
  251     argc -= optind;
  252     argv += optind;
  253 
  254     if (argc == 2 && !salt_arg) {
  255     password = argv[0];
  256     salt_arg = argv[1];
  257     } else if (argc == 1) {
  258     password = argv[0];
  259     } else if (argc == 0) {
  260     } else {
  261     display_help(EXIT_FAILURE);
  262     }
  263 
  264     /* default: DES password, or else whatever crypt_gensalt chooses */
  265     if (!salt_prefix) {
  266     salt_minlen = methods[0].minlen;
  267     salt_maxlen = methods[0].maxlen;
  268     salt_prefix = methods[0].prefix;
  269     rounds_support = methods[0].rounds;
  270     }
  271 
  272     if (!salt_prefix) {
  273     /* NULL means that crypt_gensalt will choose one later */
  274     } else if (rounds_support == 2) {
  275     /* bcrypt strings always contain the rounds number */
  276     if (rounds <= 5)
  277         rounds = 5;
  278     /* actually it is the logarithm of the number of rounds */
  279     snprintf(rounds_str, sizeof(rounds_str), "%02u$", rounds);
  280     } else if (rounds_support && rounds)
  281     snprintf(rounds_str, sizeof(rounds_str), "rounds=%u$", rounds);
  282     else
  283     rounds_str[0] = '\0';
  284 
  285     if (salt_arg && salt_arg[0] == '$')
  286     salt = NOFAIL(strdup(salt_arg));
  287     else if (salt_arg && salt_arg[0] != '\0') {
  288     unsigned int c = strlen(salt_arg);
  289     if (c < salt_minlen || c > salt_maxlen) {
  290         if (salt_minlen == salt_maxlen)
  291         fprintf(stderr, ngettext(
  292             "Wrong salt length: %d byte when %d expected.\n",
  293             "Wrong salt length: %d bytes when %d expected.\n", c),
  294             c, salt_maxlen);
  295         else
  296         fprintf(stderr, ngettext(
  297             "Wrong salt length: %d byte when %d <= n <= %d"
  298             " expected.\n",
  299             "Wrong salt length: %d bytes when %d <= n <= %d"
  300             " expected.\n", c),
  301             c, salt_minlen, salt_maxlen);
  302         exit(1);
  303     }
  304     while (c-- > 0) {
  305         if (strchr(valid_salts, salt_arg[c]) == NULL) {
  306         fprintf(stderr, _("Illegal salt character '%c'.\n"),
  307             salt_arg[c]);
  308         exit(1);
  309         }
  310     }
  311 
  312     salt = NOFAIL(malloc(strlen(salt_prefix) + strlen(rounds_str)
  313         + strlen(salt_arg) + 1));
  314     *salt = '\0';
  315     strcat(salt, salt_prefix);
  316     strcat(salt, rounds_str);
  317     strcat(salt, salt_arg);
  318     } else {
  319 #ifdef HAVE_SOLARIS_CRYPT_GENSALT
  320     salt = crypt_gensalt(salt_prefix, NULL);
  321     if (!salt) {
  322         perror("crypt_gensalt");
  323         exit(2);
  324     }
  325 #elif defined HAVE_LINUX_CRYPT_GENSALT
  326     void *entropy = get_random_bytes(64);
  327 
  328     salt = crypt_gensalt(salt_prefix, rounds, entropy, 64);
  329     if (!salt) {
  330         perror("crypt_gensalt");
  331         exit(2);
  332     }
  333     if (entropy)
  334         free(entropy);
  335 #else
  336     unsigned int salt_len = salt_maxlen;
  337 
  338     if (salt_minlen != salt_maxlen) { /* salt length can vary */
  339         srand(time(NULL) + getpid());
  340         salt_len = rand() % (salt_maxlen - salt_minlen + 1) + salt_minlen;
  341     }
  342 
  343     salt = NOFAIL(malloc(strlen(salt_prefix) + strlen(rounds_str)
  344         + salt_len + 1));
  345     *salt = '\0';
  346     strcat(salt, salt_prefix);
  347     strcat(salt, rounds_str);
  348     generate_salt(salt + strlen(salt), salt_len);
  349 #endif
  350     }
  351 
  352     if (password) {
  353     } else if (password_fd != -1) {
  354     FILE *fp;
  355 
  356     if (isatty(password_fd))
  357         fprintf(stderr, _("Password: "));
  358     fp = fdopen(password_fd, "r");
  359     if (!fp) {
  360         perror("fdopen");
  361         exit(2);
  362     }
  363 
  364     password = read_line(fp);
  365     if (!password) {
  366         perror("fgetc");
  367         exit(2);
  368     }
  369     } else {
  370     password = getpass(_("Password: "));
  371     if (!password) {
  372         perror("getpass");
  373         exit(2);
  374     }
  375     }
  376 
  377     {
  378     const char *result;
  379     result = crypt(password, salt);
  380     /* xcrypt returns "*0" on errors */
  381     if (!result || result[0] == '*') {
  382         if (CRYPT_SETS_ERRNO)
  383         perror("crypt");
  384         else
  385         fprintf(stderr, "crypt failed.\n");
  386         exit(2);
  387     }
  388     if (!strneq(result, salt, strlen(salt))) {
  389         fprintf(stderr, _("Method not supported by crypt(3).\n"));
  390         exit(2);
  391     }
  392     printf("%s\n", result);
  393     }
  394 
  395     exit(0);
  396 }
  397 
  398 #ifdef CRYPT_GENSALT_IMPLEMENTS_AUTO_ENTROPY
  399 
  400 /*
  401  * If NULL is passed to the libxcrypt version of crypt_gensalt() instead of
  402  * the buffer of random bytes then the function will obtain by itself the
  403  * required randomness.
  404  */
  405 inline void *get_random_bytes(const unsigned int count)
  406 {
  407     return NULL;
  408 }
  409 
  410 #elif defined HAVE_SOLARIS_CRYPT_GENSALT
  411 
  412 /*
  413  * The Solaris version of crypt_gensalt() gathers the random data by itself.
  414  */
  415 
  416 #elif defined RANDOM_DEVICE || defined HAVE_ARC4RANDOM_BUF || defined HAVE_GETENTROPY
  417 
  418 void *get_random_bytes(const unsigned int count)
  419 {
  420     char *buf = NOFAIL(malloc(count));
  421 
  422 #if defined HAVE_ARC4RANDOM_BUF
  423     arc4random_buf(buf, count);
  424 #elif defined HAVE_GETENTROPY
  425     if (getentropy(buf, count) < 0)
  426     perror("getentropy");
  427 #else
  428     int fd;
  429     ssize_t bytes_read;
  430 
  431     fd = open(RANDOM_DEVICE, O_RDONLY);
  432     if (fd < 0) {
  433     perror("open(" RANDOM_DEVICE ")");
  434     exit(2);
  435     }
  436     bytes_read = read(fd, buf, count);
  437     if (bytes_read < 0) {
  438     perror("read(" RANDOM_DEVICE ")");
  439     exit(2);
  440     }
  441     if (bytes_read != count) {
  442     fprintf(stderr, "Short read of %s.\n", RANDOM_DEVICE);
  443     exit(2);
  444     }
  445     close(fd);
  446 #endif
  447 
  448     return buf;
  449 }
  450 
  451 void generate_salt(char *const buf, const unsigned int len)
  452 {
  453     unsigned int i;
  454     unsigned char *entropy;
  455 
  456     entropy = get_random_bytes(len);
  457 
  458     for (i = 0; i < len; i++)
  459     buf[i] = valid_salts[entropy[i] % (sizeof valid_salts - 1)];
  460     buf[i] = '\0';
  461     free(entropy);
  462 }
  463 
  464 #else /* RANDOM_DEVICE || HAVE_ARC4RANDOM_BUF || HAVE_GETENTROPY */
  465 
  466 void generate_salt(char *const buf, const unsigned int len)
  467 {
  468     unsigned int i;
  469 
  470 # ifdef HAVE_GETTIMEOFDAY
  471     struct timeval tv;
  472 
  473     gettimeofday(&tv, NULL);
  474     srand(tv.tv_sec ^ tv.tv_usec);
  475 
  476 # else /* HAVE_GETTIMEOFDAY */
  477 #  warning "This system lacks a strong enough random numbers generator!"
  478 
  479     /*
  480      * The possible values of time over one year are 31536000, which is
  481      * two orders of magnitude less than the allowed entropy range (2^32).
  482      */
  483     srand(time(NULL) + getpid());
  484 
  485 # endif /* HAVE_GETTIMEOFDAY */
  486 
  487     for (i = 0; i < len; i++)
  488     buf[i] = valid_salts[rand() % (sizeof valid_salts - 1)];
  489     buf[i] = '\0';
  490 }
  491 
  492 #endif /* RANDOM_DEVICE || HAVE_ARC4RANDOM_BUF || HAVE_GETENTROPY*/
  493 
  494 void NORETURN display_help(int error)
  495 {
  496     fprintf((EXIT_SUCCESS == error) ? stdout : stderr,
  497         _("Usage: mkpasswd [OPTIONS]... [PASSWORD [SALT]]\n"
  498         "Crypts the PASSWORD using crypt(3).\n\n"));
  499     fprintf((EXIT_SUCCESS == error) ? stdout : stderr, _(
  500 "      -m, --method=TYPE     select method TYPE\n"
  501 "      -5                    like --method=md5crypt\n"
  502 "      -S, --salt=SALT       use the specified SALT\n"
  503     ));
  504     fprintf((EXIT_SUCCESS == error) ? stdout : stderr, _(
  505 "      -R, --rounds=NUMBER   use the specified NUMBER of rounds\n"
  506 "      -P, --password-fd=NUM read the password from file descriptor NUM\n"
  507 "                            instead of /dev/tty\n"
  508 "      -s, --stdin           like --password-fd=0\n"
  509     ));
  510     fprintf((EXIT_SUCCESS == error) ? stdout : stderr, _(
  511 "      -h, --help            display this help and exit\n"
  512 "      -V, --version         output version information and exit\n"
  513 "\n"
  514 "If PASSWORD is missing then it is asked interactively.\n"
  515 "If no SALT is specified, a random one is generated.\n"
  516 "If TYPE is 'help', available methods are printed.\n"
  517 "\n"
  518 "Report bugs to %s.\n"), "<md+whois@linux.it>");
  519     exit(error);
  520 }
  521 
  522 void display_version(void)
  523 {
  524     printf("mkpasswd %s\n\n", VERSION);
  525     puts("Copyright (C) 2001-2021 Marco d'Itri\n"
  526 "This is free software; see the source for copying conditions.  There is NO\n"
  527 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.");
  528 }
  529 
  530 void display_methods(void)
  531 {
  532     unsigned int i;
  533 
  534     printf(_("Available methods:\n"));
  535     for (i = 0; methods[i].method != NULL; i++)
  536     if (methods[i].desc)
  537         printf("%-15s %s\n", methods[i].method, methods[i].desc);
  538 }
  539 
  540 char *read_line(FILE *fp) {
  541     int size = 128;
  542     int ch;
  543     size_t pos = 0;
  544     char *password;
  545 
  546     password = NOFAIL(malloc(size));
  547 
  548     while ((ch = fgetc(fp)) != EOF) {
  549     if (ch == '\n' || ch == '\r')
  550         break;
  551     password[pos++] = ch;
  552     if (pos == size) {
  553         size += 128;
  554         password = NOFAIL(realloc(password, size));
  555     }
  556     }
  557     password[pos] = '\0';
  558 
  559     if (ferror(fp)) {
  560     free(password);
  561     return NULL;
  562     }
  563     return password;
  564 }
  565