Diff for /embedaddon/lighttpd/src/http_auth.c between versions 1.1.1.2 and 1.1.1.3

version 1.1.1.2, 2014/06/15 20:20:06 version 1.1.1.3, 2016/11/02 10:35:00
Line 1 Line 1
   #include "first.h"
   
 #include "server.h"  #include "server.h"
 #include "log.h"  #include "log.h"
 #include "http_auth.h"  #include "http_auth.h"
 #include "inet_ntop_cache.h"  #include "inet_ntop_cache.h"
 #include "stream.h"  #include "stream.h"
   #include "base64.h"
   
 #ifdef HAVE_CRYPT_H  #ifdef HAVE_CRYPT_H
 # include <crypt.h>  # include <crypt.h>
Line 11 Line 14
 # define _XOPEN_SOURCE  # define _XOPEN_SOURCE
 #endif  #endif
   
#ifdef HAVE_LIBCRYPT#if defined(HAVE_LIBCRYPT) && !defined(HAVE_CRYPT)
 /* always assume crypt() is present if we have -lcrypt */
 # define HAVE_CRYPT  # define HAVE_CRYPT
 #endif  #endif
   
Line 26 Line 30
 #include <errno.h>  #include <errno.h>
 #include <unistd.h>  #include <unistd.h>
 #include <ctype.h>  #include <ctype.h>
   #include <mysql/mysql.h>
   
 #include "md5.h"  #include "md5.h"
   
Line 33 Line 38
 #include <openssl/sha.h>  #include <openssl/sha.h>
 #endif  #endif
   
   #include "safe_memclear.h"
   
 #define HASHLEN 16  #define HASHLEN 16
 #define HASHHEXLEN 32  #define HASHHEXLEN 32
 typedef unsigned char HASH[HASHLEN];  typedef unsigned char HASH[HASHLEN];
 typedef char HASHHEX[HASHHEXLEN+1];  typedef char HASHHEX[HASHHEXLEN+1];
   
static void CvtHex(const HASH Bin, char Hex[33]) {static void CvtHex(const HASH Bin, char (*Hex)[33]) {
        unsigned short i;        li_tohex(*Hex, sizeof(*Hex), (const char*) Bin, 16);
 
        for (i = 0; i < 16; i++) { 
                Hex[i*2] = int2hex((Bin[i] >> 4) & 0xf); 
                Hex[i*2+1] = int2hex(Bin[i] & 0xf); 
        } 
        Hex[32] = '\0'; 
 }  }
   
   
 /**  /**
  * the $apr1$ handling is taken from apache 1.3.x   * the $apr1$ handling is taken from apache 1.3.x
  */   */
Line 65  static void CvtHex(const HASH Bin, char Hex[33]) { Line 67  static void CvtHex(const HASH Bin, char Hex[33]) {
   
 handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s);  handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s);
   
 static const char base64_pad = '=';  
   
 /* "A-Z a-z 0-9 + /" maps to 0-63 */  
 static const short base64_reverse_table[256] = {  
 /*       0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F */  
         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 - 0x0F */  
         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 - 0x1F */  
         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 - 0x2F */  
         52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 - 0x3F */  
         -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, /* 0x40 - 0x4F */  
         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 - 0x5F */  
         -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 - 0x6F */  
         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 0x70 - 0x7F */  
         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80 - 0x8F */  
         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x90 - 0x9F */  
         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xA0 - 0xAF */  
         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xB0 - 0xBF */  
         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC0 - 0xCF */  
         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xD0 - 0xDF */  
         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xE0 - 0xEF */  
         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xF0 - 0xFF */  
 };  
   
   
 static unsigned char * base64_decode(buffer *out, const char *in) {  
         unsigned char *result;  
         unsigned int j = 0; /* current output character (position) that is decoded. can contain partial result */  
         unsigned int group = 0; /* how many base64 digits in the current group were decoded already. each group has up to 4 digits */  
         size_t i;  
   
         size_t in_len = strlen(in);  
   
         buffer_prepare_copy(out, in_len);  
   
         result = (unsigned char *)out->ptr;  
   
         /* run through the whole string, converting as we go */  
         for (i = 0; i < in_len; i++) {  
                 unsigned char c = (unsigned char) in[i];  
                 short ch;  
   
                 if (c == '\0') break;  
   
                 if (c == base64_pad) {  
                         /* pad character can only come after 2 base64 digits in a group */  
                         if (group < 2) return NULL;  
                         break;  
                 }  
   
                 ch = base64_reverse_table[c];  
                 if (ch < 0) continue; /* skip invalid characters */  
   
                 switch(group) {  
                 case 0:  
                         result[j] = ch << 2;  
                         group = 1;  
                         break;  
                 case 1:  
                         result[j++] |= ch >> 4;  
                         result[j] = (ch & 0x0f) << 4;  
                         group = 2;  
                         break;  
                 case 2:  
                         result[j++] |= ch >>2;  
                         result[j] = (ch & 0x03) << 6;  
                         group = 3;  
                         break;  
                 case 3:  
                         result[j++] |= ch;  
                         group = 0;  
                         break;  
                 }  
         }  
   
         switch(group) {  
         case 0:  
                 /* ended on boundary */  
                 break;  
         case 1:  
                 /* need at least 2 base64 digits per group */  
                 return NULL;  
         case 2:  
                 /* have 2 base64 digits in last group => one real octect, two zeroes padded */  
         case 3:  
                 /* have 3 base64 digits in last group => two real octects, one zero padded */  
   
                 /* for both cases the current index already is on the first zero padded octet  
                  * - check it really is zero (overlapping bits) */  
                 if (0 != result[j]) return NULL;  
                 break;  
         }  
   
         result[j] = '\0';  
         out->used = j;  
   
         return result;  
 }  
   
 static int http_auth_get_password(server *srv, mod_auth_plugin_data *p, buffer *username, buffer *realm, buffer *password) {  static int http_auth_get_password(server *srv, mod_auth_plugin_data *p, buffer *username, buffer *realm, buffer *password) {
        int ret = -1;        if (buffer_is_empty(username) || buffer_is_empty(realm)) return -1;
   
         if (!username->used|| !realm->used) return -1;  
   
         if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) {          if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) {
                stream f;                FILE *fp;
                char * f_line;                char f_user[1024];
   
                if (buffer_is_empty(p->conf.auth_htdigest_userfile)) return -1;                if (buffer_string_is_empty(p->conf.auth_htdigest_userfile)) return -1;
   
                if (0 != stream_open(&f, p->conf.auth_htdigest_userfile)) {                fp = fopen(p->conf.auth_htdigest_userfile->ptr, "r");
                 if (NULL == fp) {
                         log_error_write(srv, __FILE__, __LINE__, "sbss", "opening digest-userfile", p->conf.auth_htdigest_userfile, "failed:", strerror(errno));                          log_error_write(srv, __FILE__, __LINE__, "sbss", "opening digest-userfile", p->conf.auth_htdigest_userfile, "failed:", strerror(errno));
   
                         return -1;                          return -1;
                 }                  }
   
                f_line = f.start;                while (NULL != fgets(f_user, sizeof(f_user), fp)) {
                         char *f_pwd, *f_realm;
                         size_t u_len, r_len;
   
                while (f_line - f.start != f.size) {                        /* skip blank lines and comment lines (beginning '#') */
                        char *f_user, *f_pwd, *e, *f_realm;                        if (f_user[0] == '#' || f_user[0] == '\n' || f_user[0] == '\0') continue;
                        size_t u_len, pwd_len, r_len; 
   
                         f_user = f_line;  
   
                         /*                          /*
                          * htdigest format                           * htdigest format
                          *                           *
                          * user:realm:md5(user:realm:password)                           * user:realm:md5(user:realm:password)
                          */                           */
   
                        if (NULL == (f_realm = memchr(f_user, ':', f.size - (f_user - f.start) ))) {                        if (NULL == (f_realm = strchr(f_user, ':'))) {
                                 log_error_write(srv, __FILE__, __LINE__, "sbs",                                  log_error_write(srv, __FILE__, __LINE__, "sbs",
                                                 "parsed error in", p->conf.auth_htdigest_userfile,                                                  "parsed error in", p->conf.auth_htdigest_userfile,
                                                 "expected 'username:realm:hashed password'");                                                  "expected 'username:realm:hashed password'");
   
                                stream_close(&f);                                continue; /* skip bad lines */
 
                                return -1; 
                         }                          }
   
                        if (NULL == (f_pwd = memchr(f_realm + 1, ':', f.size - (f_realm + 1 - f.start)))) {                        if (NULL == (f_pwd = strchr(f_realm + 1, ':'))) {
                                 log_error_write(srv, __FILE__, __LINE__, "sbs",                                  log_error_write(srv, __FILE__, __LINE__, "sbs",
                                                 "parsed error in", p->conf.auth_plain_userfile,                                                  "parsed error in", p->conf.auth_plain_userfile,
                                                 "expected 'username:realm:hashed password'");                                                  "expected 'username:realm:hashed password'");
   
                                stream_close(&f);                                continue; /* skip bad lines */
 
                                return -1; 
                         }                          }
   
                         /* get pointers to the fields */                          /* get pointers to the fields */
Line 220  static int http_auth_get_password(server *srv, mod_aut Line 118  static int http_auth_get_password(server *srv, mod_aut
                         r_len = f_pwd - f_realm;                          r_len = f_pwd - f_realm;
                         f_pwd++;                          f_pwd++;
   
                        if (NULL != (e = memchr(f_pwd, '\n', f.size - (f_pwd - f.start)))) {                        if (buffer_string_length(username) == u_len &&
                                pwd_len = e - f_pwd;                            (buffer_string_length(realm) == r_len) &&
                        } else { 
                                pwd_len = f.size - (f_pwd - f.start); 
                        } 
 
                        if (username->used - 1 == u_len && 
                            (realm->used - 1 == r_len) && 
                             (0 == strncmp(username->ptr, f_user, u_len)) &&                              (0 == strncmp(username->ptr, f_user, u_len)) &&
                             (0 == strncmp(realm->ptr, f_realm, r_len))) {                              (0 == strncmp(realm->ptr, f_realm, r_len))) {
                                 /* found */                                  /* found */
   
                                   size_t pwd_len = strlen(f_pwd);
                                   if (f_pwd[pwd_len-1] == '\n') --pwd_len;
   
                                 buffer_copy_string_len(password, f_pwd, pwd_len);                                  buffer_copy_string_len(password, f_pwd, pwd_len);
   
                                ret = 0;                                fclose(fp);
                                break;                                return 0;
                         }                          }
   
                         /* EOL */  
                         if (!e) break;  
   
                         f_line = e + 1;  
                 }                  }
   
                stream_close(&f);                fclose(fp);
         } else if (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD ||          } else if (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD ||
                    p->conf.auth_backend == AUTH_BACKEND_PLAIN) {                     p->conf.auth_backend == AUTH_BACKEND_PLAIN) {
                stream f;                FILE *fp;
                char * f_line;                char f_user[1024];
                 buffer *auth_fn;                  buffer *auth_fn;
   
                 auth_fn = (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD) ? p->conf.auth_htpasswd_userfile : p->conf.auth_plain_userfile;                  auth_fn = (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD) ? p->conf.auth_htpasswd_userfile : p->conf.auth_plain_userfile;
   
                if (buffer_is_empty(auth_fn)) return -1;                if (buffer_string_is_empty(auth_fn)) return -1;
   
                if (0 != stream_open(&f, auth_fn)) {                fp = fopen(auth_fn->ptr, "r");
                 if (NULL == fp) {
                         log_error_write(srv, __FILE__, __LINE__, "sbss",                          log_error_write(srv, __FILE__, __LINE__, "sbss",
                                         "opening plain-userfile", auth_fn, "failed:", strerror(errno));                                          "opening plain-userfile", auth_fn, "failed:", strerror(errno));
   
                         return -1;                          return -1;
                 }                  }
   
                f_line = f.start;                while (NULL != fgets(f_user, sizeof(f_user), fp)) {
                         char *f_pwd;
                         size_t u_len;
   
                while (f_line - f.start != f.size) {                        /* skip blank lines and comment lines (beginning '#') */
                        char *f_user, *f_pwd, *e;                        if (f_user[0] == '#' || f_user[0] == '\n' || f_user[0] == '\0') continue;
                        size_t u_len, pwd_len; 
   
                         f_user = f_line;  
   
                         /*                          /*
                          * htpasswd format                           * htpasswd format
                          *                           *
                          * user:crypted passwd                           * user:crypted passwd
                          */                           */
   
                        if (NULL == (f_pwd = memchr(f_user, ':', f.size - (f_user - f.start) ))) {                        if (NULL == (f_pwd = strchr(f_user, ':'))) {
                                 log_error_write(srv, __FILE__, __LINE__, "sbs",                                  log_error_write(srv, __FILE__, __LINE__, "sbs",
                                                 "parsed error in", auth_fn,                                                  "parsed error in", auth_fn,
                                                 "expected 'username:hashed password'");                                                  "expected 'username:hashed password'");
   
                                stream_close(&f);                                continue; /* skip bad lines */
 
                                return -1; 
                         }                          }
   
                         /* get pointers to the fields */                          /* get pointers to the fields */
                         u_len = f_pwd - f_user;                          u_len = f_pwd - f_user;
                         f_pwd++;                          f_pwd++;
   
                        if (NULL != (e = memchr(f_pwd, '\n', f.size - (f_pwd - f.start)))) {                        if (buffer_string_length(username) == u_len &&
                                pwd_len = e - f_pwd; 
                        } else { 
                                pwd_len = f.size - (f_pwd - f.start); 
                        } 
 
                        if (username->used - 1 == u_len && 
                             (0 == strncmp(username->ptr, f_user, u_len))) {                              (0 == strncmp(username->ptr, f_user, u_len))) {
                                 /* found */                                  /* found */
   
                                   size_t pwd_len = strlen(f_pwd);
                                   if (f_pwd[pwd_len-1] == '\n') --pwd_len;
   
                                 buffer_copy_string_len(password, f_pwd, pwd_len);                                  buffer_copy_string_len(password, f_pwd, pwd_len);
   
                                ret = 0;                                fclose(fp);
                                break;                                return 0;
                         }                          }
   
                         /* EOL */  
                         if (!e) break;  
   
                         f_line = e + 1;  
                 }                  }
   
                stream_close(&f);                fclose(fp);
         } else if (p->conf.auth_backend == AUTH_BACKEND_LDAP) {          } else if (p->conf.auth_backend == AUTH_BACKEND_LDAP) {
                ret = 0;                return 0;
        } else {        } else if (p->conf.auth_backend == AUTH_BACKEND_MYSQL) {
                return -1;                MYSQL_RES *result;
        }                MYSQL_ROW row;
                 int port = atoi(p->conf.auth_mysql_port->ptr);
                 char q[255];
   
        return ret;                if (p->conf.auth_mysql_socket->ptr != NULL)
                         if (0 == strcmp(p->conf.auth_mysql_socket->ptr, "")) p->conf.auth_mysql_socket->ptr = NULL;
 
                 p->conf.mysql_conn = mysql_init(NULL);
 
                 if (mysql_real_connect(p->conf.mysql_conn, p->conf.auth_mysql_host->ptr, p->conf.auth_mysql_user->ptr, p->conf.auth_mysql_pass->ptr, p->conf.auth_mysql_db->ptr, port, p->conf.auth_mysql_socket->ptr, 0))
                 {
 //#define MY_HOSTING
 
 #ifdef MY_HOSTING
                         char my_full_realm[255];
                         char *my_realm = NULL;
                         char *my_domain = NULL;
 
                         char *uname;
                         size_t unamelen;
 
                         unamelen = strlen(username->ptr);
                         uname = malloc(unamelen*2+1);
 
                         mysql_real_escape_string(p->conf.mysql_conn,
                                         uname, username->ptr,
                                         (unsigned long)unamelen);
 
                         strcpy(my_full_realm, realm->ptr);
                         my_realm = strtok(my_full_realm, "@");
 
                         if (my_realm != NULL)
                                 my_domain = strtok(NULL, "@");
 
                         sprintf(q, "SELECT %s FROM %s, %s WHERE %s='%s' AND %s='%s' AND %s='%s' AND %s=%s",
                                 p->conf.auth_mysql_col_pass->ptr,
 
                                 p->conf.auth_mysql_users_table->ptr,
                                 p->conf.auth_mysql_domains_table->ptr,
 
                                 p->conf.auth_mysql_col_user->ptr,
                                 uname,
 
                                 p->conf.auth_mysql_col_realm->ptr,
                                 my_realm,
 
                                 p->conf.auth_mysql_col_domain->ptr,
                                 my_domain,
 
                                 p->conf.auth_mysql_domains_table_col_domain_id->ptr,
                                 p->conf.auth_mysql_users_table_col_domain_id->ptr
                                 );
 
                         free(uname);
 #else
                         // sanitize username & realm by taguchi@ff.iij4u.or.jp
                         char *uname, *urealm;
                         size_t unamelen, urealmlen;
 
                         unamelen = strlen(username->ptr);
                         urealmlen = strlen(realm->ptr);
                         uname = malloc(unamelen*2+1);
                         urealm = malloc(urealmlen*2+1);
 
                         mysql_real_escape_string(p->conf.mysql_conn,
                                 uname, username->ptr,
                                 (unsigned long)unamelen);
 
                         mysql_real_escape_string(p->conf.mysql_conn,
                                 urealm, realm->ptr,
                                 (unsigned long)unamelen);
 
                         mysql_real_escape_string(p->conf.mysql_conn,
                                 urealm, realm->ptr,
                                 (unsigned long)urealmlen);
 
                         sprintf(q, "SELECT %s FROM %s WHERE %s='%s' AND %s='%s'",
                                 p->conf.auth_mysql_col_pass->ptr,
                                 p->conf.auth_mysql_users_table->ptr,
                                 p->conf.auth_mysql_col_user->ptr,
                                 uname,
                                 p->conf.auth_mysql_col_realm->ptr,
                                 urealm
                         );
 
                         free(uname);
                         free(urealm);
 #endif
 
                         mysql_query(p->conf.mysql_conn, q);
                         result = mysql_store_result(p->conf.mysql_conn);
                         if (mysql_num_rows(result) == 1)
                         {
                                 /* found */
                                 row = mysql_fetch_row(result);
                                 buffer_copy_string_len(password, row[0], strlen(row[0]));
 
                                 return 0;
                         } else
                         {
                                 /* not found */
                                 return -1;
                         }
 
                         mysql_free_result(result);
                         mysql_close(p->conf.mysql_conn);
 
                         p->conf.mysql_conn = NULL;
                 } else
                         return -1;
         }
 }  }
   
 int http_auth_match_rules(server *srv, array *req, const char *username, const char *group, const char *host) {  int http_auth_match_rules(server *srv, array *req, const char *username, const char *group, const char *host) {
Line 331  int http_auth_match_rules(server *srv, array *req, con Line 319  int http_auth_match_rules(server *srv, array *req, con
         UNUSED(host);          UNUSED(host);
   
         require = (data_string *)array_get_element(req, "require");          require = (data_string *)array_get_element(req, "require");
           if (!require) return -1; /*(should not happen; config is validated at startup)*/
   
         /* if we get here, the user we got a authed user */          /* if we get here, the user we got a authed user */
         if (0 == strcmp(require->value->ptr, "valid-user")) {          if (0 == strcmp(require->value->ptr, "valid-user")) {
Line 439  int http_auth_match_rules(server *srv, array *req, con Line 428  int http_auth_match_rules(server *srv, array *req, con
   
 static void to64(char *s, unsigned long v, int n)  static void to64(char *s, unsigned long v, int n)
 {  {
    static const unsigned char itoa64[] =         /* 0 ... 63 => ASCII - 64 */        static const unsigned char itoa64[] =         /* 0 ... 63 => ASCII - 64 */
        "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";                "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
   
    while (--n >= 0) {        while (--n >= 0) {
        *s++ = itoa64[v&0x3f];                *s++ = itoa64[v&0x3f];
        v >>= 6;                v >>= 6;
    }        }
 }  }
   
 static void apr_md5_encode(const char *pw, const char *salt, char *result, size_t nbytes) {  static void apr_md5_encode(const char *pw, const char *salt, char *result, size_t nbytes) {
    /*        /*
     * Minimum size is 8 bytes for salt, plus 1 for the trailing NUL,         * Minimum size is 8 bytes for salt, plus 1 for the trailing NUL,
     * plus 4 for the '$' separators, plus the password hash itself.         * plus 4 for the '$' separators, plus the password hash itself.
     * Let's leave a goodly amount of leeway.         * Let's leave a goodly amount of leeway.
     */         */
   
    char passwd[120], *p;        char passwd[120], *p;
    const char *sp, *ep;        const char *sp, *ep;
    unsigned char final[APR_MD5_DIGESTSIZE];        unsigned char final[APR_MD5_DIGESTSIZE];
    ssize_t sl, pl, i;        ssize_t sl, pl, i;
    li_MD5_CTX ctx, ctx1;        li_MD5_CTX ctx, ctx1;
    unsigned long l;        unsigned long l;
   
    /*        /*
     * Refine the salt first.  It's possible we were given an already-hashed         * Refine the salt first.  It's possible we were given an already-hashed
     * string as the salt argument, so extract the actual salt value from it         * string as the salt argument, so extract the actual salt value from it
     * if so.  Otherwise just use the string up to the first '$' as the salt.         * if so.  Otherwise just use the string up to the first '$' as the salt.
     */         */
    sp = salt;        sp = salt;
   
    /*        /*
     * If it starts with the magic string, then skip that.         * If it starts with the magic string, then skip that.
     */         */
    if (!strncmp(sp, APR1_ID, strlen(APR1_ID))) {        if (!strncmp(sp, APR1_ID, strlen(APR1_ID))) {
        sp += strlen(APR1_ID);                sp += strlen(APR1_ID);
    }        }
   
    /*        /*
     * It stops at the first '$' or 8 chars, whichever comes first         * It stops at the first '$' or 8 chars, whichever comes first
     */         */
    for (ep = sp; (*ep != '\0') && (*ep != '$') && (ep < (sp + 8)); ep++) {        for (ep = sp; (*ep != '\0') && (*ep != '$') && (ep < (sp + 8)); ep++) {
        continue;                continue;
    }        }
   
    /*        /*
     * Get the length of the true salt         * Get the length of the true salt
     */         */
    sl = ep - sp;        sl = ep - sp;
   
    /*        /*
     * 'Time to make the doughnuts..'         * 'Time to make the doughnuts..'
     */         */
    li_MD5_Init(&ctx);        li_MD5_Init(&ctx);
   
    /*        /*
     * The password first, since that is what is most unknown         * The password first, since that is what is most unknown
     */         */
    li_MD5_Update(&ctx, pw, strlen(pw));        li_MD5_Update(&ctx, pw, strlen(pw));
   
    /*        /*
     * Then our magic string         * Then our magic string
     */         */
    li_MD5_Update(&ctx, APR1_ID, strlen(APR1_ID));        li_MD5_Update(&ctx, APR1_ID, strlen(APR1_ID));
   
    /*        /*
     * Then the raw salt         * Then the raw salt
     */         */
    li_MD5_Update(&ctx, sp, sl);        li_MD5_Update(&ctx, sp, sl);
   
    /*        /*
     * Then just as many characters of the MD5(pw, salt, pw)         * Then just as many characters of the MD5(pw, salt, pw)
     */         */
    li_MD5_Init(&ctx1);        li_MD5_Init(&ctx1);
    li_MD5_Update(&ctx1, pw, strlen(pw));        li_MD5_Update(&ctx1, pw, strlen(pw));
    li_MD5_Update(&ctx1, sp, sl);        li_MD5_Update(&ctx1, sp, sl);
    li_MD5_Update(&ctx1, pw, strlen(pw));        li_MD5_Update(&ctx1, pw, strlen(pw));
    li_MD5_Final(final, &ctx1);        li_MD5_Final(final, &ctx1);
    for (pl = strlen(pw); pl > 0; pl -= APR_MD5_DIGESTSIZE) {        for (pl = strlen(pw); pl > 0; pl -= APR_MD5_DIGESTSIZE) {
        li_MD5_Update(&ctx, final,                li_MD5_Update(
                      (pl > APR_MD5_DIGESTSIZE) ? APR_MD5_DIGESTSIZE : pl);                        &ctx, final,
    }                        (pl > APR_MD5_DIGESTSIZE) ? APR_MD5_DIGESTSIZE : pl);
         }
   
    /*        /*
     * Don't leave anything around in vm they could use.         * Don't leave anything around in vm they could use.
     */         */
    memset(final, 0, sizeof(final));        memset(final, 0, sizeof(final));
   
    /*        /*
     * Then something really weird...         * Then something really weird...
     */         */
    for (i = strlen(pw); i != 0; i >>= 1) {        for (i = strlen(pw); i != 0; i >>= 1) {
        if (i & 1) {                if (i & 1) {
            li_MD5_Update(&ctx, final, 1);                        li_MD5_Update(&ctx, final, 1);
        }                }
        else {                else {
            li_MD5_Update(&ctx, pw, 1);                        li_MD5_Update(&ctx, pw, 1);
        }                }
    }        }
   
    /*        /*
     * Now make the output string.  We know our limitations, so we         * Now make the output string.  We know our limitations, so we
     * can use the string routines without bounds checking.         * can use the string routines without bounds checking.
     */         */
    strcpy(passwd, APR1_ID);        strcpy(passwd, APR1_ID);
    strncat(passwd, sp, sl);        strncat(passwd, sp, sl);
    strcat(passwd, "$");        strcat(passwd, "$");
   
    li_MD5_Final(final, &ctx);        li_MD5_Final(final, &ctx);
   
    /*        /*
     * And now, just to make sure things don't run too fast..         * And now, just to make sure things don't run too fast..
     * On a 60 Mhz Pentium this takes 34 msec, so you would         * On a 60 Mhz Pentium this takes 34 msec, so you would
     * need 30 seconds to build a 1000 entry dictionary...         * need 30 seconds to build a 1000 entry dictionary...
     */         */
    for (i = 0; i < 1000; i++) {        for (i = 0; i < 1000; i++) {
        li_MD5_Init(&ctx1);                li_MD5_Init(&ctx1);
        if (i & 1) {                if (i & 1) {
            li_MD5_Update(&ctx1, pw, strlen(pw));                        li_MD5_Update(&ctx1, pw, strlen(pw));
        }                }
        else {                else {
            li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE);                        li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE);
        }                }
        if (i % 3) {                if (i % 3) {
            li_MD5_Update(&ctx1, sp, sl);                        li_MD5_Update(&ctx1, sp, sl);
        }                }
   
        if (i % 7) {                if (i % 7) {
            li_MD5_Update(&ctx1, pw, strlen(pw));                        li_MD5_Update(&ctx1, pw, strlen(pw));
        }                }
   
        if (i & 1) {                if (i & 1) {
            li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE);                        li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE);
        }                }
        else {                else {
            li_MD5_Update(&ctx1, pw, strlen(pw));                        li_MD5_Update(&ctx1, pw, strlen(pw));
        }                }
        li_MD5_Final(final,&ctx1);                li_MD5_Final(final,&ctx1);
    }        }
   
    p = passwd + strlen(passwd);        p = passwd + strlen(passwd);
   
    l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p, l, 4); p += 4;        l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p, l, 4); p += 4;
    l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p, l, 4); p += 4;        l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p, l, 4); p += 4;
    l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p, l, 4); p += 4;        l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p, l, 4); p += 4;
    l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p, l, 4); p += 4;        l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p, l, 4); p += 4;
    l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p, l, 4); p += 4;        l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p, l, 4); p += 4;
    l =                    final[11]                ; to64(p, l, 2); p += 2;        l =                    final[11]                ; to64(p, l, 2); p += 2;
    *p = '\0';        *p = '\0';
   
    /*        /*
     * Don't leave anything around in vm they could use.         * Don't leave anything around in vm they could use.
     */         */
    memset(final, 0, sizeof(final));        safe_memclear(final, sizeof(final));
   
         /* FIXME          /* FIXME
          */           */
 #define apr_cpystrn strncpy  #define apr_cpystrn strncpy
    apr_cpystrn(result, passwd, nbytes - 1);        apr_cpystrn(result, passwd, nbytes - 1);
 }  }
   
 #ifdef USE_OPENSSL  #ifdef USE_OPENSSL
 static void apr_sha_encode(const char *pw, char *result, size_t nbytes) {  static void apr_sha_encode(const char *pw, char *result, size_t nbytes) {
        static const unsigned char base64_data[65] =        unsigned char digest[20];
                "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";        size_t base64_written;
        unsigned char digest[21]; /* multiple of 3 for base64 encoding */ 
        int i; 
   
           SHA1((const unsigned char*) pw, strlen(pw), digest);
   
         memset(result, 0, nbytes);          memset(result, 0, nbytes);
   
         /* need 5 bytes for "{SHA}", 28 for base64 (3 bytes -> 4 bytes) of SHA1 (20 bytes), 1 terminating */          /* need 5 bytes for "{SHA}", 28 for base64 (3 bytes -> 4 bytes) of SHA1 (20 bytes), 1 terminating */
         if (nbytes < 5 + 28 + 1) return;          if (nbytes < 5 + 28 + 1) return;
   
        SHA1((const unsigned char*) pw, strlen(pw), digest);        memcpy(result, "{SHA}", 5);
        digest[20] = 0;        base64_written = li_to_base64(result + 5, nbytes - 5, digest, 20, BASE64_STANDARD);
        force_assert(base64_written == 28);
        strcpy(result, "{SHA}");        result[5 + base64_written] = '\0'; /* terminate string */
        result = result + 5; 
        for (i = 0; i < 21; i += 3) { 
                unsigned int v = (digest[i] << 16) | (digest[i+1] << 8) | digest[i+2]; 
                result[3] = base64_data[v & 0x3f]; v >>= 6; 
                result[2] = base64_data[v & 0x3f]; v >>= 6; 
                result[1] = base64_data[v & 0x3f]; v >>= 6; 
                result[0] = base64_data[v & 0x3f]; 
                result += 4; 
        } 
        result[-1] = '='; /* last digest character was already end of string, pad it */ 
        *result = '\0'; 
 }  }
 #endif  #endif
   
Line 649  static int http_auth_basic_password_compare(server *sr Line 628  static int http_auth_basic_password_compare(server *sr
   
                 li_MD5_CTX Md5Ctx;                  li_MD5_CTX Md5Ctx;
                 HASH HA1;                  HASH HA1;
                char a1[256];                char a1[33];
   
                 li_MD5_Init(&Md5Ctx);                  li_MD5_Init(&Md5Ctx);
                li_MD5_Update(&Md5Ctx, (unsigned char *)username->ptr, username->used - 1);                li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(username));
                li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);                li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
                li_MD5_Update(&Md5Ctx, (unsigned char *)realm->ptr, realm->used - 1);                li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(realm));
                li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);                li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
                 li_MD5_Update(&Md5Ctx, (unsigned char *)pw, strlen(pw));                  li_MD5_Update(&Md5Ctx, (unsigned char *)pw, strlen(pw));
                 li_MD5_Final(HA1, &Md5Ctx);                  li_MD5_Final(HA1, &Md5Ctx);
   
                CvtHex(HA1, a1);                CvtHex(HA1, &a1);
   
                 if (0 == strcmp(password->ptr, a1)) {                  if (0 == strcmp(password->ptr, a1)) {
                         return 0;                          return 0;
Line 678  static int http_auth_basic_password_compare(server *sr Line 657  static int http_auth_basic_password_compare(server *sr
                         return (strcmp(sample, password->ptr) == 0) ? 0 : 1;                          return (strcmp(sample, password->ptr) == 0) ? 0 : 1;
 #endif  #endif
                 } else {                  } else {
#ifdef HAVE_CRYPT#if defined(HAVE_CRYPT_R) || defined(HAVE_CRYPT)
                         char *crypted;                          char *crypted;
   #if defined(HAVE_CRYPT_R)
                           struct crypt_data crypt_tmp_data;
                           crypt_tmp_data.initialized = 0;
   #endif
   
                         /* a simple DES password is 2 + 11 characters. everything else should be longer. */                          /* a simple DES password is 2 + 11 characters. everything else should be longer. */
                        if (password->used < 13 + 1) {                        if (buffer_string_length(password) < 13) {
                                 return -1;                                  return -1;
                         }                          }
   
   #if defined(HAVE_CRYPT_R)
                           if (0 == (crypted = crypt_r(pw, password->ptr, &crypt_tmp_data))) {
   #else
                         if (0 == (crypted = crypt(pw, password->ptr))) {                          if (0 == (crypted = crypt(pw, password->ptr))) {
   #endif
                                 /* crypt failed. */                                  /* crypt failed. */
                                 return -1;                                  return -1;
                         }                          }
Line 707  static int http_auth_basic_password_compare(server *sr Line 694  static int http_auth_basic_password_compare(server *sr
                 char *dn;                  char *dn;
                 int ret;                  int ret;
                 char *attrs[] = { LDAP_NO_ATTRS, NULL };                  char *attrs[] = { LDAP_NO_ATTRS, NULL };
                size_t i;                size_t i, len;
   
                 /* for now we stay synchronous */                  /* for now we stay synchronous */
   
Line 726  static int http_auth_basic_password_compare(server *sr Line 713  static int http_auth_basic_password_compare(server *sr
                  * a unpleasant way                   * a unpleasant way
                  */                   */
   
                for (i = 0; i < username->used - 1; i++) {                len = buffer_string_length(username);
                 for (i = 0; i < len; i++) {
                         char c = username->ptr[i];                          char c = username->ptr[i];
   
                         if (!isalpha(c) &&                          if (!isalpha(c) &&
Line 748  static int http_auth_basic_password_compare(server *sr Line 736  static int http_auth_basic_password_compare(server *sr
                         return -1;                          return -1;
   
                 /* build filter */                  /* build filter */
                buffer_copy_string_buffer(p->ldap_filter, p->conf.ldap_filter_pre);                buffer_copy_buffer(p->ldap_filter, p->conf.ldap_filter_pre);
                 buffer_append_string_buffer(p->ldap_filter, username);                  buffer_append_string_buffer(p->ldap_filter, username);
                 buffer_append_string_buffer(p->ldap_filter, p->conf.ldap_filter_post);                  buffer_append_string_buffer(p->ldap_filter, p->conf.ldap_filter_post);
   
Line 834  static int http_auth_basic_password_compare(server *sr Line 822  static int http_auth_basic_password_compare(server *sr
   
                 return 0;                  return 0;
 #endif  #endif
           } else if (p->conf.auth_backend == AUTH_BACKEND_MYSQL) {
                   /*
                           we check for md5 crypt() now
                           request by Nicola Tiling <nti@w4w.net>
                   */
                   if (password->ptr[0] == '$' && password->ptr[2] == '$')
                   {
                           char salt[32];
                           char *crypted;
                           size_t salt_len = 0;
                           char *dollar = NULL;
   
                           if (NULL == (dollar = strchr(password->ptr + 3, '$'))) {
                                   fprintf(stderr, "%s.%d\n", __FILE__, __LINE__);
                                   return -1;
                           }
   
                           salt_len = dollar - password->ptr;
   
                           if (salt_len > sizeof(salt) - 1)
                           {
                                   fprintf(stderr, "%s.%d\n", __FILE__, __LINE__);
                                   return -1;
                           }
   
                           strncpy(salt, password->ptr, salt_len);
   
                           salt[salt_len] = '\0';
   
                           crypted = crypt(pw, salt);
   
                           if (0 == strcmp(password->ptr, crypted))
                           {
                                   return 0;
                           } else {
                                   fprintf(stderr, "%s.%d\n", __FILE__, __LINE__);
                           }
                   } else
                   /* plain md5 check now */
                   {
                           li_MD5_CTX Md5Ctx;
                           HASH HA1;
                           char a1[256];
   
                           li_MD5_Init(&Md5Ctx);
                           li_MD5_Update(&Md5Ctx, (unsigned char *)pw, strlen(pw));
                           li_MD5_Final(HA1, &Md5Ctx);
   
                           CvtHex(HA1, a1);
   
                           if (0 == strcmp(password->ptr, a1)) {
                                   return 0;
                           }
                   }
         }          }
         return -1;          return -1;
 }  }
Line 845  int http_auth_basic_check(server *srv, connection *con Line 887  int http_auth_basic_check(server *srv, connection *con
         data_string *realm;          data_string *realm;
   
         realm = (data_string *)array_get_element(req, "realm");          realm = (data_string *)array_get_element(req, "realm");
           if (!realm) return 0; /*(should not happen; config is validated at startup)*/
   
         username = buffer_init();          username = buffer_init();
   
        if (!base64_decode(username, realm_str)) {        if (!buffer_append_base64_decode(username, realm_str, strlen(realm_str), BASE64_STANDARD)) {
                 log_error_write(srv, __FILE__, __LINE__, "sb", "decodeing base64-string failed", username);                  log_error_write(srv, __FILE__, __LINE__, "sb", "decodeing base64-string failed", username);
   
                 buffer_free(username);                  buffer_free(username);
Line 863  int http_auth_basic_check(server *srv, connection *con Line 906  int http_auth_basic_check(server *srv, connection *con
                 return 0;                  return 0;
         }          }
   
        *pw++ = '\0';        buffer_string_set_length(username, pw - username->ptr);
         pw++;
   
         username->used = pw - username->ptr;  
   
         password = buffer_init();          password = buffer_init();
         /* copy password to r1 */          /* copy password to r1 */
         if (http_auth_get_password(srv, p, username, realm->value, password)) {          if (http_auth_get_password(srv, p, username, realm->value, password)) {
Line 903  int http_auth_basic_check(server *srv, connection *con Line 945  int http_auth_basic_check(server *srv, connection *con
         }          }
   
         /* remember the username */          /* remember the username */
        buffer_copy_string_buffer(p->auth_user, username);        buffer_copy_buffer(p->auth_user, username);
   
         buffer_free(username);          buffer_free(username);
         buffer_free(password);          buffer_free(password);
Line 919  typedef struct { Line 961  typedef struct {
   
 /* return values: -1: error/bad request, 0: failed, 1: success */  /* return values: -1: error/bad request, 0: failed, 1: success */
 int http_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, const char *realm_str) {  int http_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, const char *realm_str) {
        char a1[256];        char a1[33];
        char a2[256];        char a2[33];
   
         char *username = NULL;          char *username = NULL;
         char *realm = NULL;          char *realm = NULL;
Line 1010  int http_auth_digest_check(server *srv, connection *co Line 1052  int http_auth_digest_check(server *srv, connection *co
                                         *(dkv[i].ptr) = c + dkv[i].key_len;                                          *(dkv[i].ptr) = c + dkv[i].key_len;
                                         c += strlen(c) - 1;                                          c += strlen(c) - 1;
                                 }                                  }
                                   break;
                         }                          }
                 }                  }
         }          }
Line 1064  int http_auth_digest_check(server *srv, connection *co Line 1107  int http_auth_digest_check(server *srv, connection *co
         }          }
   
         m = get_http_method_name(con->request.http_method);          m = get_http_method_name(con->request.http_method);
           force_assert(m);
   
         /* password-string == HA1 */          /* password-string == HA1 */
         password = buffer_init();          password = buffer_init();
Line 1084  int http_auth_digest_check(server *srv, connection *co Line 1128  int http_auth_digest_check(server *srv, connection *co
                 /* generate password from plain-text */                  /* generate password from plain-text */
                 li_MD5_Init(&Md5Ctx);                  li_MD5_Init(&Md5Ctx);
                 li_MD5_Update(&Md5Ctx, (unsigned char *)username, strlen(username));                  li_MD5_Update(&Md5Ctx, (unsigned char *)username, strlen(username));
                li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);                li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
                 li_MD5_Update(&Md5Ctx, (unsigned char *)realm, strlen(realm));                  li_MD5_Update(&Md5Ctx, (unsigned char *)realm, strlen(realm));
                li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);                li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
                li_MD5_Update(&Md5Ctx, (unsigned char *)password->ptr, password->used - 1);                li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(password));
                 li_MD5_Final(HA1, &Md5Ctx);                  li_MD5_Final(HA1, &Md5Ctx);
         } else if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) {          } else if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) {
                 /* HA1 */                  /* HA1 */
Line 1103  int http_auth_digest_check(server *srv, connection *co Line 1147  int http_auth_digest_check(server *srv, connection *co
   
         buffer_free(password);          buffer_free(password);
   
           /* detect if attacker is attempting to reuse valid digest for one uri
            * on a different request uri.  Might also happen if intermediate proxy
            * altered client request line.  (Altered request would not result in
            * the same digest as that calculated by the client.) */
           {
                   const size_t ulen = strlen(uri);
                   const size_t rlen = buffer_string_length(con->request.uri);
                   if (!buffer_is_equal_string(con->request.uri, uri, ulen)
                       && !(rlen < ulen && 0 == memcmp(con->request.uri->ptr, uri, rlen) && uri[rlen] == '?')) {
                           log_error_write(srv, __FILE__, __LINE__, "sbssss",
                                           "digest: auth failed: uri mismatch (", con->request.uri, "!=", uri, "), IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
                           buffer_free(b);
                           return -1;
                   }
           }
   
         if (algorithm &&          if (algorithm &&
             strcasecmp(algorithm, "md5-sess") == 0) {              strcasecmp(algorithm, "md5-sess") == 0) {
                 li_MD5_Init(&Md5Ctx);                  li_MD5_Init(&Md5Ctx);
                 /* Errata ID 1649: http://www.rfc-editor.org/errata_search.php?rfc=2617 */                  /* Errata ID 1649: http://www.rfc-editor.org/errata_search.php?rfc=2617 */
                CvtHex(HA1, a1);                CvtHex(HA1, &a1);
                li_MD5_Update(&Md5Ctx, (unsigned char *)a1, 32);                li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN);
                li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);                li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
                 li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce));                  li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce));
                li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);                li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
                 li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce));                  li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce));
                 li_MD5_Final(HA1, &Md5Ctx);                  li_MD5_Final(HA1, &Md5Ctx);
         }          }
   
        CvtHex(HA1, a1);        CvtHex(HA1, &a1);
   
         /* calculate H(A2) */          /* calculate H(A2) */
         li_MD5_Init(&Md5Ctx);          li_MD5_Init(&Md5Ctx);
         li_MD5_Update(&Md5Ctx, (unsigned char *)m, strlen(m));          li_MD5_Update(&Md5Ctx, (unsigned char *)m, strlen(m));
        li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);        li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
         li_MD5_Update(&Md5Ctx, (unsigned char *)uri, strlen(uri));          li_MD5_Update(&Md5Ctx, (unsigned char *)uri, strlen(uri));
         /* qop=auth-int not supported, already checked above */          /* qop=auth-int not supported, already checked above */
 /*  /*
         if (qop && strcasecmp(qop, "auth-int") == 0) {          if (qop && strcasecmp(qop, "auth-int") == 0) {
                li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);                li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
                 li_MD5_Update(&Md5Ctx, (unsigned char *) [body checksum], HASHHEXLEN);                  li_MD5_Update(&Md5Ctx, (unsigned char *) [body checksum], HASHHEXLEN);
         }          }
 */  */
         li_MD5_Final(HA2, &Md5Ctx);          li_MD5_Final(HA2, &Md5Ctx);
        CvtHex(HA2, HA2Hex);        CvtHex(HA2, &HA2Hex);
   
         /* calculate response */          /* calculate response */
         li_MD5_Init(&Md5Ctx);          li_MD5_Init(&Md5Ctx);
         li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN);          li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN);
        li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);        li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
         li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce));          li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce));
        li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);        li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
         if (qop && *qop) {          if (qop && *qop) {
                 li_MD5_Update(&Md5Ctx, (unsigned char *)nc, strlen(nc));                  li_MD5_Update(&Md5Ctx, (unsigned char *)nc, strlen(nc));
                li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);                li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
                 li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce));                  li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce));
                li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);                li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
                 li_MD5_Update(&Md5Ctx, (unsigned char *)qop, strlen(qop));                  li_MD5_Update(&Md5Ctx, (unsigned char *)qop, strlen(qop));
                li_MD5_Update(&Md5Ctx, (unsigned char *)":", 1);                li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
         };          };
         li_MD5_Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN);          li_MD5_Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN);
         li_MD5_Final(RespHash, &Md5Ctx);          li_MD5_Final(RespHash, &Md5Ctx);
        CvtHex(RespHash, a2);        CvtHex(RespHash, &a2);
   
         if (0 != strcmp(a2, respons)) {          if (0 != strcmp(a2, respons)) {
                 /* digest not ok */                  /* digest not ok */
Line 1176  int http_auth_digest_check(server *srv, connection *co Line 1236  int http_auth_digest_check(server *srv, connection *co
                 return 0;                  return 0;
         }          }
   
           /* check age of nonce.  Note that rand() is used in nonce generation
            * in http_auth_digest_generate_nonce().  If that were replaced
            * with nanosecond time, then nonce secret would remain unique enough
            * for the purposes of Digest auth, and would be reproducible (and
            * verifiable) if nanoseconds were inclued with seconds as part of the
            * nonce "timestamp:secret".  Since that is not done, timestamp in
            * nonce could theoretically be modified and still produce same md5sum,
            * but that is highly unlikely within a 10 min (moving) window of valid
            * time relative to current time (now) */
           {
                   time_t ts = 0;
                   const unsigned char * const nonce_uns = (unsigned char *)nonce;
                   for (i = 0; i < 8 && light_isxdigit(nonce_uns[i]); ++i) {
                           ts = (ts << 4) + hex2int(nonce_uns[i]);
                   }
                   if (i != 8 || nonce[8] != ':'
                       || ts > srv->cur_ts || srv->cur_ts - ts > 600) { /*(10 mins)*/
                           buffer_free(b);
                           return -2; /* nonce is stale; have client regenerate digest */
                   } /*(future: might send nextnonce when expiration is imminent)*/
           }
   
         /* remember the username */          /* remember the username */
         buffer_copy_string(p->auth_user, username);          buffer_copy_string(p->auth_user, username);
   
Line 1189  int http_auth_digest_check(server *srv, connection *co Line 1271  int http_auth_digest_check(server *srv, connection *co
 }  }
   
   
int http_auth_digest_generate_nonce(server *srv, mod_auth_plugin_data *p, buffer *fn, char out[33]) {int http_auth_digest_generate_nonce(server *srv, mod_auth_plugin_data *p, buffer *fn, char (*out)[33]) {
         HASH h;          HASH h;
         li_MD5_CTX Md5Ctx;          li_MD5_CTX Md5Ctx;
        char hh[32];        char hh[LI_ITOSTRING_LENGTH];
   
         UNUSED(p);          UNUSED(p);
   
         /* generate shared-secret */          /* generate shared-secret */
         li_MD5_Init(&Md5Ctx);          li_MD5_Init(&Md5Ctx);
        li_MD5_Update(&Md5Ctx, (unsigned char *)fn->ptr, fn->used - 1);        li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(fn));
        li_MD5_Update(&Md5Ctx, (unsigned char *)"+", 1);        li_MD5_Update(&Md5Ctx, CONST_STR_LEN("+"));
   
         /* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */          /* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */
        LI_ltostr(hh, srv->cur_ts);        li_itostrn(hh, sizeof(hh), srv->cur_ts);
         li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));          li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
         li_MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy));          li_MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy));
        LI_ltostr(hh, rand());        li_itostrn(hh, sizeof(hh), rand());
         li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));          li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
   
         li_MD5_Final(h, &Md5Ctx);          li_MD5_Final(h, &Md5Ctx);

Removed from v.1.1.1.2  
changed lines
  Added in v.1.1.1.3


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>