diff options
-rw-r--r-- | common/buffer.c | 257 | ||||
-rw-r--r-- | common/compat.c | 13 | ||||
-rw-r--r-- | common/compat.h | 3 | ||||
-rw-r--r-- | configure.in | 3 | ||||
-rw-r--r-- | daemon/digest.c | 35 | ||||
-rw-r--r-- | daemon/digest.h | 2 | ||||
-rw-r--r-- | daemon/httpauthd.c | 10 | ||||
-rw-r--r-- | daemon/httpauthd.h | 14 | ||||
-rw-r--r-- | daemon/ldap.c | 7 | ||||
-rw-r--r-- | daemon/misc.c | 3 | ||||
-rw-r--r-- | daemon/simple.c | 188 | ||||
-rw-r--r-- | sample/httpauthd.conf | 3 |
12 files changed, 366 insertions, 172 deletions
diff --git a/common/buffer.c b/common/buffer.c index 0bba3bf..db23d79 100644 --- a/common/buffer.c +++ b/common/buffer.c @@ -8,44 +8,106 @@ * Memory Buffer */ +#define BUF_IS_EMPTY(b) ((b)->_pp == (b)->_rp) +#define BUF_IN_JOIN(b) (!BUF_IS_EMPTY(b) && *((b)->_rp - 1) != 0) +#define BUF_NEW_BLOCK(b) ((b)->_pp = (b)->_rp) + +/* TODO: Increase this size to 2048 once we're done debugging */ +#define BUF_INITIAL 80 +#define BUF_DELTA 16 + +#define INTERNAL_DATA(intl) ((void*)(((unsigned char*)(intl)) + sizeof(*(intl)))) + +#define FILL_BETWEEN(b, e, x) memset((b), x, ((char*)(e)) - ((char*)(b))); + /* * LEGEND: - * _al: allocated - * _dt: data + * _ft: first buffer block + * _dt: current buffer block * _rp: read/write point * _pp: parse/begin point */ +typedef struct ha_buffer_internal +{ + char* end; + struct ha_buffer_internal* next; +} +internal_t; + void buffer_bump(ha_buffer_t* buf, int count) { + int allocated; + internal_t* intl; + ASSERT(buf && count); if(ha_buferr(buf)) return; - if(buf->_rp + count >= buf->_dt + buf->_al) + ASSERT(BUF_DELTA < BUF_INITIAL); + + /* Handle this common case first for efficiency */ + if(buf->_rp + (count + BUF_DELTA) < buf->_dt->end) + return; + + /* Now when in joins we let it go closer */ + if(BUF_IN_JOIN(buf) && (buf->_rp + count < buf->_dt->end)) + return; + + /* Okay now we know we have to extend */ + allocated = buf->_dt->end - (char*)(buf->_dt); + ASSERT(allocated > 0 && (allocated % BUF_INITIAL) == 0); + + /* Enlarge the size as much as needed */ + do + { + allocated *= 2; + } + while(allocated < count); + + ASSERT(sizeof(internal_t) < allocated); + intl = (internal_t*)malloc(allocated); + if(!intl) + { + ha_messagex(LOG_CRIT, "out of memory"); + buf->_dt = NULL; + buf->_pp = buf->_rp = NULL; + return; + } + else { - char* old = buf->_dt; - int alloc = buf->_al; - int diff; + void* beg = INTERNAL_DATA(intl); + intl->end = ((char*)(intl) + allocated); + intl->next = NULL; - while((buf->_al + count) > alloc) - alloc *= 2; +#ifdef _DEBUG + FILL_BETWEEN(beg, intl->end, 0xCD); +#endif - buf->_dt = (char*)reallocf(buf->_dt, alloc * sizeof(char)); - if(!buf->_dt) + if(BUF_IN_JOIN(buf)) { - buf->_al = 0; - buf->_rp = buf->_pp = NULL; - ha_messagex(LOG_CRIT, "out of memory"); - return; - } + int diff = buf->_rp - buf->_pp; + + /* Always true in a join */ + ASSERT(buf->_pp < buf->_rp); - buf->_al = alloc; + /* Copy the memory and blank out old */ + memcpy(beg, buf->_pp, diff); +#ifdef _DEBUG + FILL_BETWEEN(buf->_pp, buf->_rp, 0xDD); +#endif + + buf->_pp = beg; + buf->_rp = buf->_pp + diff; + } + else + { + buf->_rp = buf->_pp = INTERNAL_DATA(intl); + } - diff = buf->_dt - old; - buf->_rp += diff; - buf->_pp += diff; + buf->_dt->next = intl; + buf->_dt = intl; } } @@ -53,39 +115,57 @@ void ha_bufinit(ha_buffer_t* buf) { ASSERT(buf); memset(buf, 0, sizeof(*buf)); - ha_bufreset(buf); + + ASSERT(BUF_INITIAL > sizeof(internal_t)); + buf->_ft = (internal_t*)malloc(BUF_INITIAL); + if(buf->_ft) + { + buf->_ft->end = ((char*)(buf->_ft) + BUF_INITIAL); + buf->_ft->next = NULL; + +#ifdef _DEBUG + FILL_BETWEEN(INTERNAL_DATA(buf->_ft), buf->_ft->end, 0xCD); +#endif + + ha_bufreset(buf); + } } void ha_bufreset(ha_buffer_t* buf) { - ASSERT(buf); +#ifdef _DEBUG + internal_t* intl; +#endif - if(!buf->_dt || buf->_al == 0) - { - buf->_dt = (char*)reallocf(buf->_dt, 256 * sizeof(char)); - if(!buf->_dt) - { - buf->_al = 0; - buf->_rp = buf->_pp = NULL; - ha_messagex(LOG_CRIT, "out of memory"); - return; - } + ASSERT(buf); + ASSERT(buf->_ft); - buf->_al = 256; - } +#ifdef _DEBUG + /* Go through all the buffers and set them to 0xCD */ + for(intl = buf->_ft; intl; intl = intl->next) + FILL_BETWEEN(INTERNAL_DATA(intl), intl->end, 0xCD); +#endif - buf->_rp = buf->_dt; - buf->_pp = buf->_dt; + buf->_dt = buf->_ft; + buf->_rp = buf->_pp = (char*)INTERNAL_DATA(buf->_ft); } void ha_buffree(ha_buffer_t* buf) { + internal_t* intl; + internal_t* next; + ASSERT(buf); - if(buf->_dt) - free(buf->_dt); + /* Go through free all the buffers and set them to 0xDD */ + for(intl = buf->_ft; intl; intl = next) + { + next = intl->next; + FILL_BETWEEN(INTERNAL_DATA(intl), intl->end, 0xDD); + free(intl); + } - buf->_al = 0; + buf->_ft = buf->_dt = NULL; buf->_rp = buf->_pp = NULL; } @@ -258,9 +338,8 @@ char* ha_bufmcat(ha_buffer_t* buf, ...) if(ha_buferr(buf)) return NULL; - /* Move up the block pointer if we're not joining strings */ - if(ha_buflen(buf) > 0 && *(buf->_rp - 1) != 0) - buf->_pp = buf->_rp; + if(!BUF_IN_JOIN(buf)) + BUF_NEW_BLOCK(buf); while((str = va_arg(ap, char*)) != NULL) { @@ -298,16 +377,15 @@ char* ha_bufncpy(ha_buffer_t* buf, const char* src, size_t len) if(ha_buferr(buf)) return NULL; - /* Move up the block pointer if we're not joining strings */ - if(ha_buflen(buf) > 0 && *(buf->_rp - 1) != 0) - buf->_pp = buf->_rp; - /* Always add one for the terminating char */ buffer_bump(buf, len + 1); if(ha_buferr(buf)) return NULL; + if(!BUF_IN_JOIN(buf)) + BUF_NEW_BLOCK(buf); + memcpy(buf->_rp, src, len * sizeof(char)); buf->_rp += (len + 1); *(buf->_rp - 1) = 0; @@ -324,17 +402,16 @@ void* ha_bufmalloc(ha_buffer_t* buf, size_t sz) return NULL; /* TODO: Align memory on an appropriate boundary here */ - - /* We're not working with strings so always bump the pointer up */ - buf->_pp = buf->_rp; - buffer_bump(buf, sz); if(ha_buferr(buf)) return NULL; + BUF_NEW_BLOCK(buf); + ret = (void*)buf->_pp; buf->_rp += sz; - return (void*)buf->_pp; + buf->_pp = buf->_rp; + return ret; } void* ha_bufmemdup(ha_buffer_t* buf, const void* src, size_t bytes) @@ -390,6 +467,9 @@ char* ha_bufenc64(ha_buffer_t* buf, const void* source, size_t len) if(ha_buferr(buf)) return NULL; + if(!BUF_IN_JOIN(buf)) + BUF_NEW_BLOCK(buf); + while(2 < len) { input[0] = *src++; @@ -450,6 +530,7 @@ void* ha_bufdec64(ha_buffer_t* buf, const char* src, size_t bytes) int state = 0; int ch; char* pos; + void* ret; size_t done = 0; ASSERT(buf && src); @@ -457,6 +538,8 @@ void* ha_bufdec64(ha_buffer_t* buf, const char* src, size_t bytes) if(ha_buferr(buf)) return NULL; + BUF_NEW_BLOCK(buf); + if(bytes == 0) bytes = ~0; @@ -472,7 +555,7 @@ void* ha_bufdec64(ha_buffer_t* buf, const char* src, size_t bytes) if(pos == 0) /* A non-base64 character. */ break; - buffer_bump(buf, 3); + buffer_bump(buf, 4); if(ha_buferr(buf)) return; @@ -480,8 +563,7 @@ void* ha_bufdec64(ha_buffer_t* buf, const char* src, size_t bytes) switch(state) { case 0: - *(buf->_rp++) = (pos - BASE64C) << 2; - done++; + *(buf->_rp) = (pos - BASE64C) << 2; state = 1; break; @@ -505,18 +587,23 @@ void* ha_bufdec64(ha_buffer_t* buf, const char* src, size_t bytes) state = 0; break; }; - - /* TODO: Validate ending and return error if invalid somehow */ } + if(state != 3) + buf->_rp++; + + /* TODO: Validate ending and return error if invalid somehow */ + /* If we were asked for a specific amount of bytes, then return null */ if(bytes != ~0 && bytes != done) return NULL; - return buf->_pp; + ret = (void*)buf->_pp; + buf->_pp = buf->_rp; + return ret; } -static const char HEXC[] = "0123456789ABCDEF"; +static const char HEXC[] = "0123456789abcdef"; char* ha_bufenchex(ha_buffer_t* buf, const void* source, size_t len) { @@ -530,21 +617,18 @@ char* ha_bufenchex(ha_buffer_t* buf, const void* source, size_t len) if(ha_buferr(buf)) return NULL; + if(!BUF_IN_JOIN(buf)) + BUF_NEW_BLOCK(buf); + while(len > 0) { j = *(src) >> 4 & 0xf; - if(j <= 9) - *(buf->_rp++) = (j + '0'); - else - *(buf->_rp++) = (j + 'a' - 10); + *(buf->_rp++) = HEXC[j]; j = *(src++) & 0xf; - len--; + *(buf->_rp++) = HEXC[j]; - if(j <= 9) - *(buf->_rp++) = (j + '0'); - else - *(buf->_rp++) = (j + 'a' - 10); + len--; } *(buf->_rp++) = 0; @@ -553,10 +637,11 @@ char* ha_bufenchex(ha_buffer_t* buf, const void* source, size_t len) void* ha_bufdechex(ha_buffer_t* buf, const char* src, size_t bytes) { - unsigned short a; - unsigned short b; + unsigned short j; size_t done = 0; + int state = 0; char* pos; + void* ret; ASSERT(buf && src); @@ -573,29 +658,41 @@ void* ha_bufdechex(ha_buffer_t* buf, const char* src, size_t bytes) if(ha_buferr(buf)) return NULL; - while(src[0] && src[1] && done < bytes) + BUF_NEW_BLOCK(buf); + + while(src[0] && done < bytes) { /* Find the position */ - pos = strchr(HEXC, src[0]); + pos = strchr(HEXC, tolower(src[0])); if(pos == 0) break; - a = HEXC - pos; - - pos = strchr(HEXC, src[1]); - if(pos == 0); - break; + j = pos - HEXC; - b = HEXC - pos; + if(!state) + { + *(buf->_rp) = (j & 0xf) << 4; + state = 1; + } + else + { + *(buf->_rp++) |= (j & 0xf); + done++; + state = 0; + } - *(buf->_rp++) = ((a & 0xf) << 4) | (b & 0xf); - src += 2; - done++; + src++; } + /* All bytes have to come in pairs */ + if(state != 0) + return NULL; + /* If we were asked for a specific amount of bytes, then return null */ if(bytes != ~0 && bytes != done) return NULL; - return buf->_pp; + ret = (void*)buf->_pp; + buf->_pp = buf->_rp; + return ret; } diff --git a/common/compat.c b/common/compat.c index 6a4b914..5618ee5 100644 --- a/common/compat.c +++ b/common/compat.c @@ -28,3 +28,16 @@ char* strlwr(char* s) return s; } #endif + +#ifndef HAVE_STRUPR +char* strupr(char* s) +{ + char* t = s; + while(*t) + { + *t = toupper(*t); + t++; + } + return s; +} +#endif diff --git a/common/compat.h b/common/compat.h index c250db1..bb7311c 100644 --- a/common/compat.h +++ b/common/compat.h @@ -36,5 +36,8 @@ void* reallocf(void* p, size_t sz); char* strlwr(char* s); #endif +#ifndef HAVE_STRUPR +char* strupr(char* s); +#endif #endif /* _COMPAT_H_ */ diff --git a/configure.in b/configure.in index 2abbf10..6ab2633 100644 --- a/configure.in +++ b/configure.in @@ -57,7 +57,8 @@ AC_ARG_ENABLE(debug, [Compile binaries in debug mode])) if test "$enable_debug" = "yes"; then - CFLAGS="$CFLAGS -g -O0 -D_DEBUG=1" + CFLAGS="$CFLAGS -g -O0" + AC_DEFINE_UNQUOTED(_DEBUG, 1, [In debug mode]) echo "enabling debug compile mode" fi diff --git a/daemon/digest.c b/daemon/digest.c index 79b4ff3..099ca49 100644 --- a/daemon/digest.c +++ b/daemon/digest.c @@ -87,16 +87,13 @@ digest_record_t* digest_makerec(unsigned char* nonce, const char* user) return rec; } -const char* digest_challenge(ha_buffer_t* buf, unsigned char* nonce, +const char* digest_challenge(ha_buffer_t* buf, const char* nonce_str, const char* realm, const char* domains, int stale) { - ASSERT(buf && realm && nonce); + ASSERT(buf && realm && nonce_str); - ha_bufmcat(buf, HA_PREFIX_DIGEST, " realm=\"", realm, "\", nonce=\"", NULL); - ha_bufjoin(buf); - ha_bufenc64(buf, nonce, DIGEST_NONCE_LEN); - ha_bufjoin(buf); - ha_bufmcat(buf, "\", qop=\"auth\", algorithm=\"MD5\"", NULL); + ha_bufmcat(buf, HA_PREFIX_DIGEST, " realm=\"", realm, "\", nonce=\"", + nonce_str, "\", qop=\"auth\", algorithm=\"MD5\"", NULL); if(domains) { @@ -143,7 +140,7 @@ int digest_parse(char* header, ha_buffer_t* buf, digest_header_t* rec, if(!header) return HA_ERROR; - memset(rec, 0, sizeof(rec)); + memset(rec, 0, sizeof(*rec)); while(header[0]) { @@ -178,8 +175,6 @@ int digest_parse(char* header, ha_buffer_t* buf, digest_header_t* rec, if(next == '=') { - header++; - while(header[0] && isspace(header[0])) header++; @@ -191,6 +186,7 @@ int digest_parse(char* header, ha_buffer_t* buf, digest_header_t* rec, while(header[0] && header[0] != '\"') header++; + next = header[0]; header[0] = 0; header++; } @@ -202,15 +198,19 @@ int digest_parse(char* header, ha_buffer_t* buf, digest_header_t* rec, while(header[0] && header[0] != ',' && !isspace(header[0])) header++; + next = header[0]; header[0] = 0; header++; } - while(header[0] && header[0] != ',') - header++; + if(next != ',') + { + while(header[0] && header[0] != ',') + header++; - if(header[0]) - header++; + if(header[0]) + header++; + } if(!strcasecmp(key, "username")) rec->username = value; @@ -320,7 +320,7 @@ int digest_check(const char* realm, const char* method, const char* uri, char* e; long nc = strtol(dg->nc, &e, 10); - if(e != (dg->nc + strlen(e)) || nc != rec->nc) + if(*e || nc != rec->nc) { ha_messagex(LOG_WARNING, "digest response has invalid nc value: %s", dg->nc); @@ -455,6 +455,9 @@ const char* digest_respond(ha_buffer_t* buf, digest_header_t* dg, ASSERT(buf && dg && rec); + /* This makes a new buffer */ + ha_bufcpy(buf, ""); + if(next) { nextnonce = ha_bufenc64(buf, next, DIGEST_NONCE_LEN); @@ -464,7 +467,7 @@ const char* digest_respond(ha_buffer_t* buf, digest_header_t* dg, } /* For older clients RFC 2069 */ - if(dg->qop) + if(!dg->qop) { if(nextnonce) ha_bufmcat(buf, "nextnonce=\"", nextnonce, "\"", NULL); diff --git a/daemon/digest.h b/daemon/digest.h index 6bd7acc..0f5e6b2 100644 --- a/daemon/digest.h +++ b/daemon/digest.h @@ -48,7 +48,7 @@ int ha_digestcheck(const char* realm, const char* method, const char* uri, const char* digest_respond(ha_buffer_t* buf, digest_header_t* dg, digest_record_t* rec, unsigned char* next); -const char* digest_challenge(ha_buffer_t* buf, unsigned char* nonce, +const char* digest_challenge(ha_buffer_t* buf, const char* nonce_str, const char* realm, const char* domains, int stale); void digest_makeha1(unsigned char* digest, const char* user, diff --git a/daemon/httpauthd.c b/daemon/httpauthd.c index a2985a3..465ac68 100644 --- a/daemon/httpauthd.c +++ b/daemon/httpauthd.c @@ -68,7 +68,7 @@ const char* kAuthHeaders[] = /* The command definitions */ const httpauth_command_t kCommands[] = { - { "auth", REQTYPE_AUTH, 3, kAuthHeaders }, + { "auth", REQTYPE_AUTH, 4, kAuthHeaders }, { "quit", REQTYPE_QUIT, 0, 0 }, { NULL, -1, -1 } }; @@ -434,6 +434,12 @@ int httpauth_read(int ifd, ha_request_t* req, } } + else + { + req->type = REQTYPE_IGNORE; + return more; + } + /* Check for invalid command */ if(req->type == -1) return more; @@ -734,6 +740,8 @@ int httpauth_processor(int ifd, int ofd) result = 0; break; + case REQTYPE_IGNORE: + break; default: if(httpauth_respond(ofd, HA_SERVER_BADREQ, "Unknown command") == -1) diff --git a/daemon/httpauthd.h b/daemon/httpauthd.h index 55a4d90..7f51895 100644 --- a/daemon/httpauthd.h +++ b/daemon/httpauthd.h @@ -6,11 +6,13 @@ * Memory Buffers */ +struct ha_buffer_internal; + /* A buffer which owns memory */ typedef struct ha_buffer { - int _al; - char* _dt; + struct ha_buffer_internal* _ft; + struct ha_buffer_internal* _dt; char* _pp; char* _rp; } @@ -191,7 +193,7 @@ ha_context_t; * should be no need to change it unless we're * adding or removing commands */ -#define MAX_ARGS 6 +#define MAX_ARGS 4 /* * The maximum number of pertinent headers to read @@ -222,9 +224,9 @@ ha_header_t; #define REQTYPE_QUIT 1 #define REQTYPE_AUTH 2 -#define AUTH_ARG_CONN 0 -#define AUTH_ARG_METHOD 1 -#define AUTH_ARG_URI 2 +#define AUTH_ARG_CONN 1 +#define AUTH_ARG_METHOD 2 +#define AUTH_ARG_URI 3 /* A single request from client */ typedef struct ha_request diff --git a/daemon/ldap.c b/daemon/ldap.c index 2474d09..440c531 100644 --- a/daemon/ldap.c +++ b/daemon/ldap.c @@ -920,15 +920,20 @@ static int digest_ldap_challenge(ldap_context_t* ctx, ha_response_t* resp, ha_buffer_t* buf, int stale) { unsigned char nonce[DIGEST_NONCE_LEN]; + const char* nonce_str; const char* header; ASSERT(ctx && resp && buf); /* Generate an nonce */ digest_makenonce(nonce, g_ldap_secret, NULL); + nonce_str = ha_bufenchex(buf, nonce, DIGEST_NONCE_LEN); + + if(!nonce_str) + return HA_ERROR; /* Now generate a message to send */ - header = digest_challenge(buf, nonce, ctx->realm, ctx->domains, stale); + header = digest_challenge(buf, nonce_str, ctx->realm, ctx->domains, stale); if(!header) return HA_ERROR; diff --git a/daemon/misc.c b/daemon/misc.c index c3f5ff4..9dba389 100644 --- a/daemon/misc.c +++ b/daemon/misc.c @@ -208,8 +208,7 @@ int ha_confint(const char* name, const char* conf, int min, int max, int* value) errno = 0; *value = strtol(conf, &p, 10); - if(p != (name + strlen(name)) || errno == ERANGE || - (*value < min) || (*value > max)) + if(*p || errno == ERANGE || (*value < min) || (*value > max)) { ha_messagex(LOG_ERR, "invalid configuration value '%s': must be a number between %d and %d", name, min, max); return HA_ERROR; diff --git a/daemon/simple.c b/daemon/simple.c index d51e9d9..e33e833 100644 --- a/daemon/simple.c +++ b/daemon/simple.c @@ -33,20 +33,13 @@ typedef struct simple_context /* Context ----------------------------------------------------------- */ hash_t* cache; /* Some cached records or basic */ + +#ifdef _DEBUG + const char* debug_nonce; +#endif } simple_context_t; -/* The defaults for the context */ -static const simple_context_t simple_defaults = -{ - NULL, /* filename */ - NULL, /* realm */ - NULL, /* domains */ - 0, /* cache_max */ - 0, /* cache_timeout */ - NULL /* cache */ -}; - /* ------------------------------------------------------------------------------- * Internal Functions @@ -156,6 +149,7 @@ static int complete_digest_ha1(simple_context_t* ctx, digest_record_t* rec, FILE* f; int found = 0; char* t; + char* t2; char line[SIMPLE_MAXLINE]; ASSERT(ctx && rec && buf && user && user[0] && code); @@ -195,33 +189,39 @@ static int complete_digest_ha1(simple_context_t* ctx, digest_record_t* rec, /* Check the user */ if(strcmp(line, user) == 0) { - /* Now decode the rest of the line and see if it matches up */ - t = ha_bufdechex(buf, t, MD5_LEN); - - if(t != NULL) + /* Otherwise it might be a digest type ha1. */ + t2 = strchr(t, ':'); + if(t2) { - memcpy(rec->ha1, t, MD5_LEN); - found = 1; - break; + *t2 = 0; + t2++; + + /* Check the realm */ + if(strcmp(t, ctx->realm) == 0) + { + /* Now try antd decode the ha1 */ + t = ha_bufdechex(buf, t2, MD5_LEN); + if(t != NULL) + { + memcpy(rec->ha1, t, MD5_LEN); + found = 1; + break; + } + } } - else - { - if(ha_buferr(buf)) - break; - + if(!t2 || !found) ha_messagex(LOG_WARNING, "user '%s' found in file, but password not in digest format", user); - } } } - - fclose(f); } + fclose(f); + if(ha_buferr(buf)) return HA_ERROR; - return found ? HA_FALSE : HA_OK; + return found ? HA_OK : HA_FALSE; } static int validate_user_password(simple_context_t* ctx, ha_buffer_t* buf, @@ -264,6 +264,14 @@ static int validate_user_password(simple_context_t* ctx, ha_buffer_t* buf, break; } + /* Take white space off end of line */ + t = line + strlen(line); + while(t != line && isspace(*(t - 1))) + { + *(t - 1) = 0; + t--; + } + t = strchr(line, ':'); if(t) { @@ -274,40 +282,48 @@ static int validate_user_password(simple_context_t* ctx, ha_buffer_t* buf, /* Check the user */ if(strcmp(line, user) == 0) { - /* We can validate against an ha1, so check if it decodes as one */ - t2 = ha_bufdechex(buf, t, MD5_LEN); + /* Not sure if crypt is thread safe so we lock */ + ha_lock(NULL); - if(t2 && memcmp(ha1, t2, MD5_LEN) == 0) + /* Check the password */ + t2 = crypt(clearpw, t); + + ha_unlock(NULL); + + if(strcmp(crypt(clearpw, t), t) == 0) { - memcpy(ha1, t2, MD5_LEN); found = 1; break; } - /* Otherwise we try a nice crypt style password */ - else + /* Otherwise it might be a digest type ha1. */ + t2 = strchr(t, ':'); + if(t2) { - /* Not sure if crypt is thread safe so we lock */ - ha_lock(); - - /* Check the password */ - if(strcmp(crypt(clearpw, t), t) == 0) + *t2 = 0; + t2++; + + /* Check the realm */ + if(strcmp(t, ctx->realm) == 0) + { + /* Now try antd decode the ha1 */ + t = ha_bufdechex(buf, t2, MD5_LEN); + if(t && memcmp(ha1, t, MD5_LEN) == 0) + { found = 1; - - ha_unlock(); - - if(found) - break; + break; + } + } } if(ha_buferr(buf)) break; } } - - fclose(f); } + fclose(f); + if(ha_buferr(buf)) return HA_ERROR; @@ -347,8 +363,9 @@ static int simple_basic_response(simple_context_t* ctx, const char* header, finally: - if(resp->code == HA_SERVER_ACCEPT) + if(ret = HA_OK) { + resp->code = HA_SERVER_ACCEPT; resp->detail = basic.user; /* We put this connection into the successful connections */ @@ -361,16 +378,33 @@ finally: static int simple_digest_challenge(simple_context_t* ctx, ha_response_t* resp, ha_buffer_t* buf, int stale) { - unsigned char nonce[DIGEST_NONCE_LEN]; + const char* nonce_str; const char* header; ASSERT(ctx && resp && buf); /* Generate an nonce */ - digest_makenonce(nonce, g_simple_secret, NULL); + +#ifdef _DEBUG + if(ctx->debug_nonce) + { + nonce_str = ctx->debug_nonce; + ha_messagex(LOG_WARNING, "using debug nonce. security non-existant."); + } + else +#endif + { + unsigned char nonce[DIGEST_NONCE_LEN]; + digest_makenonce(nonce, g_simple_secret, NULL); + + nonce_str = ha_bufenchex(buf, nonce, DIGEST_NONCE_LEN); + if(!nonce_str) + return HA_ERROR; + } + /* Now generate a message to send */ - header = digest_challenge(buf, nonce, ctx->realm, ctx->domains, stale); + header = digest_challenge(buf, nonce_str, ctx->realm, ctx->domains, stale); if(!header) return HA_ERROR; @@ -403,14 +437,34 @@ static int simple_digest_response(simple_context_t* ctx, const char* header, if(digest_parse(header, buf, &dg, nonce) == HA_ERROR) return HA_ERROR; - r = digest_checknonce(nonce, g_simple_secret, &expiry); - if(r != HA_OK) +#ifdef _DEBUG + if(ctx->debug_nonce) { - if(r == HA_FALSE) + if(dg.nonce && strcmp(dg.nonce, ctx->debug_nonce) != 0) + { + ret = HA_FALSE; ha_messagex(LOG_WARNING, "digest response contains invalid nonce"); + goto finally; + } - ret = r; - goto finally; + /* Do a rough hash into the real nonce, for use as a key */ + md5_string(nonce, ctx->debug_nonce); + + /* Debug nonce's never expire */ + expiry = time(NULL); + } + else +#endif + { + r = digest_checknonce(nonce, g_simple_secret, &expiry); + if(r != HA_OK) + { + if(r == HA_FALSE) + ha_messagex(LOG_WARNING, "digest response contains invalid nonce"); + + ret = r; + goto finally; + } } rec = get_cached_digest(ctx, nonce); @@ -520,6 +574,14 @@ int simple_config(ha_context_t* context, const char* name, const char* value) return HA_OK; } +#ifdef _DEBUG + else if(strcmp(name, "digestdebugnonce") == 0) + { + ctx->debug_nonce = value; + return HA_OK; + } +#endif + return HA_FALSE; } @@ -600,7 +662,7 @@ int simple_process(ha_context_t* context, ha_request_t* req, ha_response_t* resp, ha_buffer_t* buf) { simple_context_t* ctx = (simple_context_t*)(context->data); - const char* header; + const char* header = NULL; int ret = HA_FALSE; int found = 0; basic_header_t basic; @@ -652,13 +714,6 @@ int simple_process(ha_context_t* context, ha_request_t* req, { resp->code = HA_SERVER_DECLINE; - if(context->types & HA_TYPE_DIGEST) - { - ret = simple_digest_challenge(ctx, resp, buf, 0); - if(ret == HA_ERROR) - return ret; - } - if(context->types & HA_TYPE_BASIC) { ha_bufmcat(buf, "BASIC realm=\"", ctx->realm , "\"", NULL); @@ -668,6 +723,13 @@ int simple_process(ha_context_t* context, ha_request_t* req, ha_addheader(resp, "WWW-Authenticate", ha_bufdata(buf)); } + + if(context->types & HA_TYPE_DIGEST) + { + ret = simple_digest_challenge(ctx, resp, buf, 0); + if(ret == HA_ERROR) + return ret; + } } return ret; @@ -685,7 +747,7 @@ ha_handler_t simple_handler = simple_destroy, /* Uninitialization routine */ simple_config, /* Config routine */ simple_process, /* Processing routine */ - &simple_defaults, /* A default context */ + NULL, /* A default context */ sizeof(simple_context_t) /* Size of the context */ }; diff --git a/sample/httpauthd.conf b/sample/httpauthd.conf index a296c36..4c97858 100644 --- a/sample/httpauthd.conf +++ b/sample/httpauthd.conf @@ -3,9 +3,10 @@ # and blank lines MaxThreads: 18 CacheTimeout: 300 -AuthTypes: NTLM +AuthTypes: Basic Digest [Simple] Realm: blah PasswordFile: /data/projects/httpauth/sample/passwd.file +DigestDebugNonce: AkCLQA==560f26e24db2d4cecbe5d6e24d958377ab73def9 |