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) {

Reply via email to