The Topics addressed in this patch are: * As a debugging aid, the salt can be preseeded by specifying the base64-string to use: $ htpasswd -S ........ -nb Apache Apache Apache:..TowpWtYvRfQ which allows regression testing of the various salted password algorithms crypt() and md5. It is debatable whether the switch should be called -S<salt> (there is a lower-case -s switch already).
* The salt for md5, when generated automatically, now uses 8 bytes of pseudo-random data instead of 4. Not that they contain more entropy, but at least they make brute-force attacks *much* more difficult. Before, the upper 32 bits were zero in the 48 bit number used for initialization of the salt (8 bytes output, each one shifting the random number right by 6 bits). In a present version of htpasswd, you can see the missing initialization as a run of 3 or 4 '.' characters in the salt string (before the 3rd '$ char): $ htpasswd -nbm Apache Apache Apache:$apr1$mV/um...$Nz1nYy20Cd3TywjHU74I6. * The usage() function now takes an optional errstr, explaining why htpasswd decided to exit. This may improve the user interface in situations where certain switch combinations caused an exit via usage(). WDYT? Martin -- <[EMAIL PROTECTED]> | Fujitsu Siemens http://www.fujitsu-siemens.com/imprint.html | 81730 Munich, Germany
--- httpd-2.2.x/support/htpasswd.c Wed Jul 12 05:38:44 2006 +++ /tmp/htpasswd.c Mon Sep 3 13:22:05 2007 @@ -101,6 +101,7 @@ apr_file_t *errfile; apr_file_t *ftemp = NULL; +static char salt[8+1]; #define NL APR_EOL_STR @@ -132,7 +133,6 @@ static int mkrecord(char *user, char *re char cpw[120]; char pwin[MAX_STRING_LEN]; char pwv[MAX_STRING_LEN]; - char salt[9]; apr_size_t bufsize; if (passwd != NULL) { @@ -162,9 +162,12 @@ static int mkrecord(char *user, char *re break; case ALG_APMD5: + if (salt[0] == '\0') { (void) srand((int) time((time_t *) NULL)); - to64(&salt[0], rand(), 8); + to64(&salt[0], rand(), 4); + to64(&salt[4], rand(), 4); salt[8] = '\0'; + } apr_md5_encode((const char *)pw, (const char *)salt, cpw, sizeof(cpw)); @@ -178,9 +181,13 @@ static int mkrecord(char *user, char *re #if !(defined(WIN32) || defined(NETWARE)) case ALG_CRYPT: default: + if (salt[0] == '\0') { (void) srand((int) time((time_t *) NULL)); - to64(&salt[0], rand(), 8); + /* 8 bytes of salt is overkill, as crypt() usually takes only 2 chars. */ + to64(&salt[0], rand(), 4); + to64(&salt[4], rand(), 4); salt[8] = '\0'; + } apr_cpystrn(cpw, (char *)crypt(pw, salt), sizeof(cpw) - 1); break; @@ -203,7 +210,7 @@ static int mkrecord(char *user, char *re return 0; } -static void usage(void) +static void usage(const char *errstr) { apr_file_printf(errfile, "Usage:" NL); apr_file_printf(errfile, "\thtpasswd [-cmdpsD] passwordfile username" NL); @@ -229,11 +236,15 @@ static void usage(void) apr_file_printf(errfile, " -b Use the password from the command line " "rather than prompting for it." NL); apr_file_printf(errfile, " -D Delete the specified user." NL); + apr_file_printf(errfile, " -S<salt> Use given \"salt\" value instead of randomized one." NL); apr_file_printf(errfile, "On Windows, NetWare and TPF systems the '-m' flag is used by " "default." NL); apr_file_printf(errfile, "On all other systems, the '-p' flag will probably not work." NL); + if (errstr != NULL) + apr_file_printf(errfile, + "\nFatal: %s" NL, errstr); exit(ERR_SYNTAX); } @@ -286,7 +297,7 @@ static void check_args(apr_pool_t *pool, * we parse the command line. */ if (argc < 3) { - usage(); + usage("Too few arguments given."); } /* @@ -309,6 +320,32 @@ static void check_args(apr_pool_t *pool, else if (*arg == 'm') { *alg = ALG_APMD5; } +#if 1 + else if (*arg == 'S') { + if (*++arg == '\0') + arg = argv[++i]; + if (arg == NULL || *arg == '\0') + usage("Need non-empty argument for -S option."); + apr_snprintf(salt, sizeof(salt), arg); + if (strcmp(salt, arg)) { + apr_file_printf(errfile, + "%s: salt too long (>%" APR_SIZE_T_FMT ")" NL, + argv[0], sizeof(salt) - 1); + exit(ERR_OVERFLOW); + } + for ( ; *arg; ++arg) { + if (NULL == strchr("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", *arg)) { + apr_file_printf(errfile, + "%s: salt must consist of alphanumeric " + "or '.' or '/' characters only." NL, + argv[0]); + exit(ERR_SYNTAX); + } + } + --arg; + /* *arg now points to the end-of-string, thus enforcing using a new argv[] */ + } +#endif else if (*arg == 's') { *alg = ALG_APSHA; } @@ -326,7 +363,7 @@ static void check_args(apr_pool_t *pool, *mask |= APHTP_DELUSER; } else { - usage(); + usage("Invalid option."); } } } @@ -349,7 +386,7 @@ static void check_args(apr_pool_t *pool, * specified). */ if ((argc - i) != args_left) { - usage(); + usage("Incorrect number of arguments"); } if (*mask & APHTP_NOFILE) {