coar 99/01/25 14:55:41
Modified: . STATUS src CHANGES src/ap ap_md5c.c src/include ap_md5.h src/modules/standard mod_auth.c mod_auth_db.c mod_auth_dbm.c src/support htpasswd.1 htpasswd.c Log: Enhance the authentication password handling so that stored passwords can be encrypted with either DES or MD5. htpasswd can now generate either on systems that allow both, and MD5 on Win32. .htpasswd files can contain both types; usernames with passwords encrypted with MD5 and usernames with DES passwords can appear in the same file. The authentication modules (mod_auth, mod_auth_db, mod_auth_dbm) autosense the correct algorithm from the stored password. This gives us encrypted passwords on Win32 at last. This is only the first part of the patch; some changes to allow the Win32 side to build properly are being fixed and should be committed to-morrow. However, Unix systems can build with and use these immediately. Submitted by: Ryan Bloom <[EMAIL PROTECTED]> Reviewed by: Ken Coar Revision Changes Path 1.607 +1 -5 apache-1.3/STATUS Index: STATUS =================================================================== RCS file: /home/cvs/apache-1.3/STATUS,v retrieving revision 1.606 retrieving revision 1.607 diff -u -r1.606 -r1.607 --- STATUS 1999/01/20 03:38:18 1.606 +++ STATUS 1999/01/25 22:55:31 1.607 @@ -1,5 +1,5 @@ 1.3 STATUS: - Last modified at [$Date: 1999/01/20 03:38:18 $] + Last modified at [$Date: 1999/01/25 22:55:31 $] Release: @@ -385,10 +385,6 @@ That _really_ sucks. Can we recommend running Apache as some other user? - - * need a crypt() of some sort. - - sources are easy; problem is export restrictions on DES - - if we don't do DES, can do md5 * modules that need to be made to work on win32 - mod_example isn't multithreadreded 1.1223 +3 -0 apache-1.3/src/CHANGES Index: CHANGES =================================================================== RCS file: /home/cvs/apache-1.3/src/CHANGES,v retrieving revision 1.1222 retrieving revision 1.1223 diff -u -r1.1222 -r1.1223 --- CHANGES 1999/01/25 18:12:36 1.1222 +++ CHANGES 1999/01/25 22:55:33 1.1223 @@ -1,5 +1,8 @@ Changes with Apache 1.3.5 + *) Add ability to handle DES or MD5 authentication passwords. + [Ryan Bloom <[EMAIL PROTECTED]>] + *) Fix O(n^2) memory consumption in mod_speling. [Dean Gaudet] *) SECURITY: Avoid some buffer overflow problems when escaping 1.18 +45 -0 apache-1.3/src/ap/ap_md5c.c Index: ap_md5c.c =================================================================== RCS file: /home/cvs/apache-1.3/src/ap/ap_md5c.c,v retrieving revision 1.17 retrieving revision 1.18 diff -u -r1.17 -r1.18 --- ap_md5c.c 1999/01/01 19:04:53 1.17 +++ ap_md5c.c 1999/01/25 22:55:36 1.18 @@ -92,6 +92,7 @@ #include "ap_config.h" #include "ap_md5.h" +#include "ap.h" #ifdef CHARSET_EBCDIC #include "ebcdic.h" #endif /*CHARSET_EBCDIC*/ @@ -389,4 +390,48 @@ for (i = 0, j = 0; j < len; i++, j += 4) output[i] = ((UINT4) input[j]) | (((UINT4) input[j + 1]) << 8) | (((UINT4) input[j + 2]) << 16) | (((UINT4) input[j + 3]) << 24); +} + +API_EXPORT(char *) ap_MD5Encode(const char *password, const char * salt) { +/* salt has size 2, md5 hash size 22, plus 1 for trailing NUL, plus 4 for + '$' separators between md5 distinguisher, salt, and password.*/ + + static unsigned char ret[2+22+1+4]; + AP_MD5_CTX my_md5; + unsigned char hash[16], *cp; + register int i; + static const char *alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"; + + /* + * Take the MD5 hash of the string argument. + */ + + sprintf(ret, "$1$%s$", salt); + + /* If the salt is shorter than 2, pad with random characters */ + for (cp = &ret[strlen(ret)]; cp < &ret[2]; ++cp) { + *cp = alphabet[rand() & 0x3F]; + } + ap_MD5Init(&my_md5); + ap_MD5Update(&my_md5, salt, 2); + ap_MD5Update(&my_md5, password, strlen(password)); + ap_MD5Final(hash, &my_md5); + + /* Take 3*8 bits (3 bytes) and store them as 4 base64 bytes (of 6 bit each) */ + /* Copy first 15 bytes in loop (producing 20 result bytes) */ + for (i = 0, cp = &ret[6]; i < 15; i += 3, cp += 4) { + long l = hash[i] | (hash[i+1] << 8) | (hash[i+2] << 16); + + cp[0] = alphabet[l&0x3F]; + cp[1] = alphabet[(l>>6)&0x3F]; + cp[2] = alphabet[(l>>12)&0x3F]; + cp[3] = alphabet[(l>>18)&0x3F]; + } + cp[0] = alphabet[hash[i]&0x3F]; /* Use 16th byte as 21st result byte */ + cp[1] = alphabet[(hash[i]>>6)&0x3F]; /* Last 2 bits of 16th byte are 22nd result byte */ + cp[2] = '\0'; + + /*ap_assert(&cp[2] == &ret[(sizeof ret)-1]);*/ + + return (char *)ret; } 1.3 +1 -0 apache-1.3/src/include/ap_md5.h Index: ap_md5.h =================================================================== RCS file: /home/cvs/apache-1.3/src/include/ap_md5.h,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- ap_md5.h 1999/01/01 19:04:39 1.2 +++ ap_md5.h 1999/01/25 22:55:37 1.3 @@ -108,6 +108,7 @@ API_EXPORT(void) ap_MD5Update(AP_MD5_CTX * context, const unsigned char *input, unsigned int inputLen); API_EXPORT(void) ap_MD5Final(unsigned char digest[16], AP_MD5_CTX * context); +API_EXPORT(char *) ap_MD5Encode(const char *, const char *); #ifdef __cplusplus } 1.43 +12 -2 apache-1.3/src/modules/standard/mod_auth.c Index: mod_auth.c =================================================================== RCS file: /home/cvs/apache-1.3/src/modules/standard/mod_auth.c,v retrieving revision 1.42 retrieving revision 1.43 diff -u -r1.42 -r1.43 --- mod_auth.c 1999/01/01 19:05:07 1.42 +++ mod_auth.c 1999/01/25 22:55:38 1.43 @@ -74,6 +74,7 @@ #include "http_core.h" #include "http_log.h" #include "http_protocol.h" +#include "ap_md5.h" #if defined(HAVE_CRYPT_H) #include <crypt.h> #endif @@ -220,9 +221,18 @@ return AUTH_REQUIRED; } /* anyone know where the prototype for crypt is? */ - if (strcmp(real_pw, (char *) crypt(sent_pw, real_pw))) { + if (real_pw[0] == '$' && real_pw[1] == '1') { + const char *salt = real_pw + 3; + salt = ap_getword(r->pool, &salt, '$'); + res = strcmp(real_pw, ap_MD5Encode(sent_pw, salt)); + } + else { + res = strcmp(real_pw, crypt(sent_pw, real_pw)); + } + + if (res != 0) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, - "user %s: password mismatch: %s", c->user, r->uri); + "user %s: password mismatch: %s", c->user, r->uri); ap_note_basic_auth_failure(r); return AUTH_REQUIRED; } 1.37 +17 -3 apache-1.3/src/modules/standard/mod_auth_db.c Index: mod_auth_db.c =================================================================== RCS file: /home/cvs/apache-1.3/src/modules/standard/mod_auth_db.c,v retrieving revision 1.36 retrieving revision 1.37 diff -u -r1.36 -r1.37 --- mod_auth_db.c 1999/01/01 19:05:07 1.36 +++ mod_auth_db.c 1999/01/25 22:55:38 1.37 @@ -96,6 +96,10 @@ #include "http_log.h" #include "http_protocol.h" #include <db.h> +#include "ap_md5.h"; +#if defined(HAVE_CRYPT_H) +#include <crypt.h> +#endif #if defined(DB_VERSION_MAJOR) && (DB_VERSION_MAJOR == 2) #define DB2 @@ -244,12 +248,22 @@ } /* Password is up to first : if exists */ colon_pw = strchr(real_pw, ':'); - if (colon_pw) + if (colon_pw) { *colon_pw = '\0'; + } /* anyone know where the prototype for crypt is? */ - if (strcmp(real_pw, (char *) crypt(sent_pw, real_pw))) { + if (real_pw[0] == '$' && real_pw[1] == '1') { + char *salt = real_pw + 3; + salt = ap_getword(r->pool, &salt, '$'); + res = strcmp(real_pw, ap_MD5Encode(sent_pw, salt)); + } + else { + res = strcmp(real_pw, crypt(sent_pw, real_pw)); + } + + if (res != 0) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, - "DB user %s: password mismatch: %s", c->user, r->uri); + "DB user %s: password mismatch: %s", c->user, r->uri); ap_note_basic_auth_failure(r); return AUTH_REQUIRED; } 1.42 +15 -2 apache-1.3/src/modules/standard/mod_auth_dbm.c Index: mod_auth_dbm.c =================================================================== RCS file: /home/cvs/apache-1.3/src/modules/standard/mod_auth_dbm.c,v retrieving revision 1.41 retrieving revision 1.42 diff -u -r1.41 -r1.42 --- mod_auth_dbm.c 1999/01/01 19:05:07 1.41 +++ mod_auth_dbm.c 1999/01/25 22:55:39 1.42 @@ -75,6 +75,10 @@ #include "http_log.h" #include "http_protocol.h" #include <ndbm.h> +#include "ap_md5.h"; +#if defined(HAVE_CRYPT_H) +#include <crypt.h> +#endif /* * Module definition information - the part between the -START and -END @@ -229,9 +233,18 @@ if (colon_pw) *colon_pw = '\0'; /* anyone know where the prototype for crypt is? */ - if (strcmp(real_pw, (char *) crypt(sent_pw, real_pw))) { + if (real_pw[0] == '$' && real_pw[1] == '1') { + char *salt = real_pw + 3; + salt = ap_getword(r->pool, &salt, '$'); + res = strcmp(real_pw, ap_MD5Encode(sent_pw, salt)); + } + else { + res = strcmp(real_pw, crypt(sent_pw, real_pw)); + } + + if (res != 0) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, - "user %s: password mismatch: %s", c->user, r->uri); + "user %s: password mismatch: %s", c->user, r->uri); ap_note_basic_auth_failure(r); return AUTH_REQUIRED; } 1.8 +3 -0 apache-1.3/src/support/htpasswd.1 Index: htpasswd.1 =================================================================== RCS file: /home/cvs/apache-1.3/src/support/htpasswd.1,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- htpasswd.1 1999/01/01 19:05:34 1.7 +++ htpasswd.1 1999/01/25 22:55:40 1.8 @@ -85,6 +85,9 @@ .IP \-c Create the \fIpasswdfile\fP. If \fIpasswdfile\fP already exists, it is deleted first. +.IP \-m +Use MD5 encryption for passwords. On Windows, this is the only format +supported. .IP \fB\fIpasswdfile\fP Name of the file to contain the user name and password. If \-c is given, this file is created if it does not already exist, 1.19 +111 -29 apache-1.3/src/support/htpasswd.c Index: htpasswd.c =================================================================== RCS file: /home/cvs/apache-1.3/src/support/htpasswd.c,v retrieving revision 1.18 retrieving revision 1.19 diff -u -r1.18 -r1.19 --- htpasswd.c 1999/01/19 22:35:45 1.18 +++ htpasswd.c 1999/01/25 22:55:41 1.19 @@ -13,7 +13,14 @@ #include "ap_config.h" #include <sys/types.h> #include <signal.h> +#include "ap_md5.h" +#ifdef WIN32 +#include <conio.h> +#include "../os/win32/getopt.h" +#define unlink _unlink +#endif + #ifndef CHARSET_EBCDIC #define LF 10 #define CR 13 @@ -116,8 +123,42 @@ #endif -static void add_password(char *user, FILE *f) +#ifdef WIN32 +/* Windows lacks getpass(). So we'll re-implement it here. + */ + +static char *getpass(const char *prompt) { + static char password[81]; + int n = 0; + + fputs(prompt, stderr); + + while ((password[n] = _getch()) != '\r') { + if (password[n] >= ' ' && password[n] <= '~') { + n++; + printf("*"); + } + else { + printf("\n"); + fputs(prompt, stderr); + n = 0; + } + } + + password[n] = '\0'; + printf("\n"); + + if (n > 8) { + password[8] = '\0'; + } + + return (char *) &password; +} +#endif + +static void add_password(char *user, FILE *f, int use_md5) +{ char *pw, *cpw, salt[3]; pw = strd((char *) getpass("New password:")); @@ -131,15 +172,23 @@ (void) srand((int) time((time_t *) NULL)); to64(&salt[0], rand(), 2); salt[2] = '\0'; - cpw = (char *)crypt(pw, salt); + + if (use_md5) { + cpw = (char *)ap_MD5Encode(pw, salt); + } + else { + cpw = (char *)crypt(pw, salt); + } free(pw); fprintf(f, "%s:%s\n", user, cpw); } static void usage(void) { - fprintf(stderr, "Usage: htpasswd [-c] passwordfile username\n"); + fprintf(stderr, "Usage: htpasswd [-cm] passwordfile username\n"); fprintf(stderr, "The -c flag creates a new file.\n"); + fprintf(stderr, "The -m flag creates a md5 encrypted file.\n"); + fprintf(stderr, "On Windows systems the -m flag is used by default.\n"); exit(1); } @@ -160,27 +209,58 @@ char l[MAX_STRING_LEN]; char w[MAX_STRING_LEN]; char command[MAX_STRING_LEN]; + char filename[MAX_STRING_LEN]; int found; + int use_md5 = 0; + int newfile = 0; tn = NULL; - signal(SIGINT, (void (*)()) interrupted); - if (argc == 4) { - if (strcmp(argv[1], "-c")) { + signal(SIGINT, (void (*)(int)) interrupted); + + /* preliminary check to make sure they provided at least + * three arguments, we'll do better argument checking as + * we parse the command line. + */ + if (argc < 3) { usage(); - } - if (!(tfp = fopen(argv[2], "w+"))) { - fprintf(stderr, "Could not open passwd file %s for writing.\n", - argv[2]); + } + else { + strcpy(filename, argv[argc - 2]); + } + + /* I would rather use getopt, but Windows and UNIX seem to handle getopt + * differently, so I am doing the argument checking by hand. + */ + + if (!strcmp(argv[1],"-c") || !strcmp(argv[2],"-c")) { + newfile = 1; + } + if (!strcmp(argv[1],"-m") || !strcmp(argv[2],"-m")) { + use_md5 = 1; + } + + if (!strcmp(argv[1], "-cm") || !strcmp(argv[2], "-mc")) { + use_md5 = 1; + newfile = 1; + } + +#ifdef WIN32 + if (!use_md5) { + use_md5 = 1; + fprintf(stderr,"Automatically using md5 format on Windows.\n"); + } +#endif + if (newfile) { + if (!(tfp = fopen(filename, "w+"))) { + fprintf(stderr, "Could not open password file %s for writing.\n", + filename); perror("fopen"); exit(1); } - printf("Adding password for %s.\n", argv[3]); - add_password(argv[3], tfp); + printf("Adding password for %s.\n", argv[argc-1]); + add_password(argv[argc - 1], tfp, use_md5); fclose(tfp); - exit(0); - } - else if (argc != 3) { - usage(); + return(0); } tn = tmpnam(NULL); @@ -189,13 +269,15 @@ exit(1); } - if (!(f = fopen(argv[1], "r+"))) { - fprintf(stderr, - "Could not open passwd file %s for reading.\n", argv[1]); - fprintf(stderr, "Use -c option to create new one.\n"); + if (!(f = fopen(argv[argc - 2], "r+"))) { + fprintf(stderr, "Could not open password file %s for reading.\n", + argv[argc - 2]); + fprintf(stderr, "Use -c option to create a new one\n"); + fclose(tfp); + unlink(tn); exit(1); } - strcpy(user, argv[2]); + strcpy(user, argv[argc - 1]); found = 0; while (!(getline(line, MAX_STRING_LEN, f))) { @@ -208,28 +290,28 @@ if (strcmp(user, w)) { putline(tfp, line); continue; - } + } else { printf("Changing password for user %s\n", user); - add_password(user, tfp); + add_password(user, tfp, use_md5); found = 1; } } if (!found) { printf("Adding user %s\n", user); - add_password(user, tfp); + add_password(user, tfp, use_md5); } /* * make a copy from the tmp file to the actual file */ - rewind(f); + f = fopen(filename, "w+"); rewind(tfp); - while ( fgets(command,MAX_STRING_LEN,tfp) != NULL) { - fputs(command,f); - } + while (fgets(command, MAX_STRING_LEN, tfp) != NULL) { + fputs(command, f); + } fclose(f); fclose(tfp); unlink(tn); - exit(0); + return(0); }