--- htpasswd.c 2008/01/25 22:02:27 1.1 +++ htpasswd.c 2008/01/25 22:05:58 @@ -46,6 +46,7 @@ #include "ap.h" #include "ap_md5.h" #include "ap_sha1.h" +#include #ifdef HAVE_CRYPT_H #include @@ -94,6 +95,8 @@ static char *tname_buf = NULL; #endif +FILE *FP = NULL; + /* * Get a line of input from the user, not including any terminating * newline. @@ -127,6 +130,67 @@ fputc('\n', f); } + +static void seed_prng() +{ + int randBits = 0; + int seedInt = 0; + int toAdd = 0; + int intSize = (8*(int)sizeof(int)); + int charSize = (8*(int)sizeof(char)); + if ( (!FP) && getenv("RANDOM_SEED") ) + { + /* use the device specified by the user */ + FP = fopen(getenv("RANDOM_SEED"), "r"); + } else if (!FP) { + /* use /dev/urandom for better (less predictable) seeding if available */ + FP = fopen("/dev/urandom", "r"); + } + if (FP) + { + while ( randBits < intSize ) { + /* how many bits to read (maximum == bytes in a char) */ + toAdd = (intSize - randBits) > charSize ? charSize : (intSize - randBits); + /* we could watch the fgetc() call for EOF and warn the user, + but the user should know better than to provide a small pool */ + seedInt += ((int)fgetc(FP) % (int)pow(2,toAdd)) * (int)pow(2,randBits); + randBits += toAdd; + } + (void) srand(seedInt); + /* deliberately leave FP open in case seed_prng() is called again and + FP behaves like a static file and always provides the same content; + with devices like /dev/urandom and /dev/random, there would be bo reason + not to fclose(FP) here */ + } else { + fprintf(stderr,"Warning: weak salt generation!\n"); + fprintf(stderr,"For better security, install /dev/urandom or provide the name of a file or device\n"); + fprintf(stderr,"that can provide enough random data in the environment variable RANDOM_SEED\n",(intSize/8)); + (void) srand((int) time((time_t *) NULL)); + } +} + +static void generate_salt(char *s, size_t size) +{ + static unsigned char tbl[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + size_t i; + int bitsUsed = 0; + for (i = 0; i < size; ++i) { + int idx = (int) (64.0 * rand() / (RAND_MAX + 1.0)); + bitsUsed += 6; + s[i] = tbl[idx]; + /* re-seed the PRNG if already used an integer's worth of bytes, or if the next + loop iteration would use more than an int, since the seed for rand() is an int. + This will only need to happen on platforms with integers less than 48 bits. */ + if ( (bitsUsed + 6) > (8*sizeof(int)) ) + { + /* re-seed */ + (void)seed_prng(); + /* reset our counter */ + bitsUsed = 0; + } + } +} + /* * Make a password record from the given information. A zero return * indicates success; failure means that the output buffer contains an @@ -171,8 +235,8 @@ break; case ALG_APMD5: - (void) srand((int) time((time_t *) NULL)); - ap_to64(&salt[0], rand(), 8); + (void)seed_prng(); + generate_salt(&salt[0], 8); salt[8] = '\0'; ap_MD5Encode((const unsigned char *)pw, (const unsigned char *)salt, @@ -186,7 +250,7 @@ case ALG_CRYPT: default: - (void) srand((int) time((time_t *) NULL)); + (void)seed_prng(); ap_to64(&salt[0], rand(), 8); salt[8] = '\0';