summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2004-04-24 22:38:50 +0000
committerStef Walter <stef@memberwebs.com>2004-04-24 22:38:50 +0000
commitcbbe71752d7f9c6204ab0f16600fe7f10490f203 (patch)
tree365e6e472d239d117b5f849c45f3c08fc6617c0a
parentff76efc3e5e1b0e4ca3b10b7402406f619509bba (diff)
Completed implementation of ldap/ntlm/simple handlers
-rw-r--r--common/buffer.c216
-rw-r--r--common/hash.c130
-rw-r--r--common/hash.h52
-rw-r--r--common/md5.c36
-rw-r--r--common/md5.h26
-rw-r--r--common/sha1.c164
-rw-r--r--common/sha1.h30
-rw-r--r--configure.in20
-rw-r--r--daemon/Makefile.am4
-rw-r--r--daemon/basic.c242
-rw-r--r--daemon/basic.h17
-rw-r--r--daemon/defaults.h2
-rw-r--r--daemon/digest.c533
-rw-r--r--daemon/digest.h57
-rw-r--r--daemon/httpauthd.c65
-rw-r--r--daemon/httpauthd.h182
-rw-r--r--daemon/ldap.c768
-rw-r--r--daemon/misc.c599
-rw-r--r--daemon/ntlm.c115
-rw-r--r--daemon/simple.c663
-rw-r--r--daemon/usuals.h7
21 files changed, 2814 insertions, 1114 deletions
diff --git a/common/buffer.c b/common/buffer.c
index 28cab24..a5b18a7 100644
--- a/common/buffer.c
+++ b/common/buffer.c
@@ -47,6 +47,12 @@ void buffer_bump(ha_buffer_t* buf, int count)
}
}
+void ha_bufinit(ha_buffer_t* buf)
+{
+ memset(buf, 0, sizeof(*buf));
+ ha_bufreset(buf);
+}
+
void ha_bufreset(ha_buffer_t* buf)
{
if(!buf->_dt || buf->_al == 0)
@@ -67,12 +73,6 @@ void ha_bufreset(ha_buffer_t* buf)
buf->_pp = buf->_dt;
}
-void ha_bufinit(ha_buffer_t* buf)
-{
- memset(buf, 0, sizeof(*buf));
- ha_bufreset(buf);
-}
-
void ha_buffree(ha_buffer_t* buf)
{
if(buf->_dt)
@@ -82,7 +82,8 @@ void ha_buffree(ha_buffer_t* buf)
buf->_rp = buf->_pp = NULL;
}
-int ha_readline(int fd, ha_buffer_t* buf)
+
+int ha_bufreadline(int fd, ha_buffer_t* buf)
{
int l;
@@ -137,7 +138,7 @@ int ha_readline(int fd, ha_buffer_t* buf)
return 1;
}
-char* ha_parseword(ha_buffer_t* buf, const char* delims)
+char* ha_bufparseword(ha_buffer_t* buf, const char* delims)
{
char* word = NULL;
@@ -185,7 +186,7 @@ char* ha_parseword(ha_buffer_t* buf, const char* delims)
return word;
}
-char* ha_parseline(ha_buffer_t* buf, int trim)
+char* ha_bufparseline(ha_buffer_t* buf, int trim)
{
char* t;
char* line = NULL;
@@ -231,55 +232,93 @@ char* ha_parseline(ha_buffer_t* buf, int trim)
return line;
}
-void ha_bufnext(ha_buffer_t* buf)
-{
- buffer_bump(buf, 1);
-
- if(!ha_buferr(buf))
- {
- buf->_rp++;
- buf->_pp = buf->_rp;
- *(buf->_rp) = 0;
- }
-}
-
-void ha_bufcat(ha_buffer_t* buf, ...)
+char* ha_bufmcat(ha_buffer_t* buf, ...)
{
const char* str;
va_list ap;
va_start(ap, 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;
+
while((str = va_arg(ap, char*)) != NULL)
{
int len = strlen(str);
- buffer_bump(buf, len);
+ /* Always add one for the terminating char */
+ buffer_bump(buf, len + 1);
if(ha_buferr(buf))
- return;
+ return NULL;
- /* _rpoint points to teh null */
+ /* _rp always points to the next write point */
strcpy(buf->_rp, str);
buf->_rp += len;
}
+
+ buf->_rp++;
+ return buf->_pp;
+}
+
+char* ha_bufcpy(ha_buffer_t* buf, const char* src)
+{
+ size_t len = strlen(src);
+ return ha_bufncpy(buf, src, len);
+}
+
+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;
+
+ memcpy(buf->_rp, src, len * sizeof(char));
+ buf->_rp += (len + 1);
+ *(buf->_rp - 1) = 0;
+ return buf->_pp;
}
void* ha_bufmalloc(ha_buffer_t* buf, size_t sz)
{
void* ret;
- ha_bufnext(buf);
+ if(ha_buferr(buf))
+ 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);
- buf->_rp += sz;
if(ha_buferr(buf))
return NULL;
- ret = (void*)ha_bufdata(buf);
- ha_bufnext(buf);
+ buf->_rp += sz;
+ return (void*)buf->_pp;
+}
- return ret;
+void* ha_bufmemdup(ha_buffer_t* buf, const void* src, size_t bytes)
+{
+ void* mem = ha_bufmalloc(buf, bytes);
+ if(mem)
+ memcpy(mem, src, bytes);
+ return mem;
}
/*
@@ -311,14 +350,15 @@ static const char BASE64C[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char PAD64C = '=';
-void ha_bufenc64(ha_buffer_t* buf, const char* src, int len)
+char* ha_bufenc64(ha_buffer_t* buf, const void* source, size_t len)
{
unsigned char input[3];
unsigned char output[4];
+ unsigned char* src = (unsigned char*)source;
size_t i;
- if(len == -1)
- len = strlen(src);
+ if(ha_buferr(buf))
+ return NULL;
while(2 < len)
{
@@ -332,10 +372,11 @@ void ha_bufenc64(ha_buffer_t* buf, const char* src, int len)
output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
output[3] = input[2] & 0x3f;
+ /* This also accounts for the null terminator */
buffer_bump(buf, 5);
if(ha_buferr(buf))
- return;
+ return NULL;
*(buf->_rp++) = BASE64C[output[0]];
*(buf->_rp++) = BASE64C[output[1]];
@@ -355,10 +396,11 @@ void ha_bufenc64(ha_buffer_t* buf, const char* src, int len)
output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+ /* This also accounts for the null terminator */
buffer_bump(buf, 5);
if(ha_buferr(buf))
- return;
+ return NULL;
*(buf->_rp++) = BASE64C[output[0]];
*(buf->_rp++) = BASE64C[output[1]];
@@ -370,17 +412,23 @@ void ha_bufenc64(ha_buffer_t* buf, const char* src, int len)
}
*(buf->_rp++) = '\0';
+ return buf->_pp;
}
-void ha_bufdec64(ha_buffer_t* buf, const char* src)
+void* ha_bufdec64(ha_buffer_t* buf, const char* src, size_t bytes)
{
- int state;
+ int state = 0;
int ch;
char* pos;
+ size_t done = 0;
- state = 0;
+ if(ha_buferr(buf))
+ return NULL;
- while((ch = *src++) != '\0')
+ if(bytes == 0)
+ bytes = ~0;
+
+ while((ch = *src++) != '\0' && done < bytes)
{
if(isspace(ch)) /* Skip whitespace anywhere. */
continue;
@@ -401,23 +449,27 @@ void ha_bufdec64(ha_buffer_t* buf, const char* src)
{
case 0:
*(buf->_rp++) = (pos - BASE64C) << 2;
+ done++;
state = 1;
break;
case 1:
*(buf->_rp++) |= (pos - BASE64C) >> 4;
+ done++;
*(buf->_rp) = ((pos - BASE64C) & 0x0f) << 4;
state = 2;
break;
case 2:
*(buf->_rp++) |= (pos - BASE64C) >> 2;
+ done++;
*(buf->_rp) = ((pos - BASE64C) & 0x03) << 6;
state = 3;
break;
case 3:
*(buf->_rp++) |= (pos - BASE64C);
+ done++;
state = 0;
break;
};
@@ -425,5 +477,89 @@ void ha_bufdec64(ha_buffer_t* buf, const char* src)
/* TODO: Validate ending and return error if invalid somehow */
}
- *(buf->_rp++) = '\0';
+ /* If we were asked for a specific amount of bytes, then return null */
+ if(bytes != ~0 && bytes != done)
+ return NULL;
+
+ return buf->_pp;
+}
+
+static const char HEXC[] = "0123456789ABCDEF";
+
+char* ha_bufenchex(ha_buffer_t* buf, const void* source, size_t len)
+{
+ unsigned char* src = (unsigned char*)source;
+ unsigned char j;
+
+ buffer_bump(buf, (len * 2) + 1);
+
+ if(ha_buferr(buf))
+ return NULL;
+
+ while(len > 0)
+ {
+ j = *(src) >> 4 & 0xf;
+ if(j <= 9)
+ *(buf->_rp++) = (j + '0');
+ else
+ *(buf->_rp++) = (j + 'a' - 10);
+
+ j = *(src++) & 0xf;
+ len--;
+
+ if(j <= 9)
+ *(buf->_rp++) = (j + '0');
+ else
+ *(buf->_rp++) = (j + 'a' - 10);
+ }
+
+ *(buf->_rp++) = 0;
+ return buf->_pp;
+}
+
+void* ha_bufdechex(ha_buffer_t* buf, const char* src, size_t bytes)
+{
+ unsigned short a;
+ unsigned short b;
+ size_t done = 0;
+ char* pos;
+
+ if(bytes != 0)
+ {
+ buffer_bump(buf, bytes + 1);
+ }
+ else
+ {
+ bytes = ~0;
+ buffer_bump(buf, (strlen(src) / 2) + 1);
+ }
+
+ if(ha_buferr(buf))
+ return NULL;
+
+ while(src[0] && src[1] && done < bytes)
+ {
+ /* Find the position */
+ pos = strchr(HEXC, src[0]);
+ if(pos == 0)
+ break;
+
+ a = HEXC - pos;
+
+ pos = strchr(HEXC, src[1]);
+ if(pos == 0);
+ break;
+
+ b = HEXC - pos;
+
+ *(buf->_rp++) = ((a & 0xf) << 4) | (b & 0xf);
+ src += 2;
+ done++;
+ }
+
+ /* If we were asked for a specific amount of bytes, then return null */
+ if(bytes != ~0 && bytes != done)
+ return NULL;
+
+ return buf->_pp;
}
diff --git a/common/hash.c b/common/hash.c
index 7d67e53..6471803 100644
--- a/common/hash.c
+++ b/common/hash.c
@@ -88,10 +88,14 @@ struct hash_t
#ifdef HASH_COPYKEYS
unsigned int klen;
#endif
+#ifdef HASH_CALLBACKS
+ hash_free_val_t f_free;
+ void* arg;
+#endif
};
-#define INITIAL_MAX 15 /* tunable == 2^n - 1 */
+#define INITIAL_MAX 15 /* tunable == 2^n - 1 */
/*
* Hash creation functions.
@@ -102,10 +106,18 @@ static hash_entry_t** alloc_array(hash_t* ht, unsigned int max)
return malloc(sizeof(*(ht->array)) * (max + 1));
}
-#ifdef HASH_COPYKEYS
-hash_t* hash_create(size_t klen)
+#ifdef HASH_CALLBACKS
+ #ifdef HASH_COPYKEYS
+hash_t* hash_create(size_t klen, hash_free_val_t f_free, void* arg)
+ #else
+hash_t* hash_create(hash_free_val_t f_free, void* arg)
+ #endif
#else
+ #ifdef HASH_COPYKEYS
+hash_t* hash_create(size_t klen)
+ #else
hash_t* hash_create()
+ #endif
#endif
{
hash_t* ht = malloc(sizeof(hash_t));
@@ -117,7 +129,10 @@ hash_t* hash_create()
#ifdef HASH_COPYKEYS
ht->klen = klen;
#endif
-
+#ifdef HASH_CALLBACKS
+ ht->f_free = f_free;
+ ht->arg = arg;
+#endif
if(!ht->array)
{
free(ht);
@@ -132,7 +147,13 @@ void hash_free(hash_t* ht)
hash_index_t* hi;
for(hi = hash_first(ht); hi; hi = hash_next(hi))
+ {
+#ifdef HASH_CALLBACKS
+ if(hi->ths->val && ht->f_free)
+ ht->f_free(ht->arg, (void*)hi->ths->val);
+#endif
free(hi->ths);
+ }
if(ht->array)
free(ht->array);
@@ -362,58 +383,64 @@ void* hash_get(hash_t* ht, const void *key, size_t klen)
}
#ifdef HASH_COPYKEYS
-int hash_set(hash_t* ht, const void* key, const void* val)
+int hash_set(hash_t* ht, const void* key, void* val)
{
hash_entry_t** hep = find_entry(ht, key, val);
#else
-int hash_set(hash_t* ht, const void* key, size_t klen, const void* val)
+int hash_set(hash_t* ht, const void* key, size_t klen, void* val)
{
hash_entry_t** hep = find_entry(ht, key, klen, val);
#endif
if(hep && *hep)
{
- if(val)
- {
- /* replace entry */
- (*hep)->val = val;
+#ifdef HASH_CALLBACKS
+ if((*hep)->val && (*hep)->val != val && ht->f_free)
+ ht->f_free(ht->arg, (void*)((*hep)->val));
+#endif
+
+ /* replace entry */
+ (*hep)->val = val;
#ifdef HASH_TIMESTAMP
- /* Update or set the timestamp */
- (*hep)->stamp = time(NULL);
+ /* Update or set the timestamp */
+ (*hep)->stamp = time(NULL);
#endif
- /* check that the collision rate isn't too high */
- if(ht->count > ht->max)
- {
- if(!expand_array(ht))
- return 0;
- }
-
- return 1;
+ /* check that the collision rate isn't too high */
+ if(ht->count > ht->max)
+ {
+ if(!expand_array(ht))
+ return 0;
}
+
+ return 1;
}
return 0;
}
#ifdef HASH_COPYKEYS
-void hash_rem(hash_t* ht, const void* key)
+void* hash_rem(hash_t* ht, const void* key)
{
hash_entry_t** hep = find_entry(ht, key, NULL);
#else
-void hash_rem(hash_t* ht, const void* key, size_t klen)
+void* hash_rem(hash_t* ht, const void* key, size_t klen)
{
hash_entry_t** hep = find_entry(ht, key, klen, NULL);
#endif
+ void* val = NULL;
if(hep && *hep)
{
hash_entry_t* old = *hep;
*hep = (*hep)->next;
--ht->count;
+ val = (void*)old->val;
free(old);
}
+
+ return val;
}
unsigned int hash_count(hash_t* ht)
@@ -426,6 +453,7 @@ int hash_purge(hash_t* ht, time_t stamp)
{
hash_index_t* hi;
int r = 0;
+ void* val;
for(hi = hash_first(ht); hi; hi = hash_next(hi))
{
@@ -433,9 +461,14 @@ int hash_purge(hash_t* ht, time_t stamp)
{
/* No need to check for errors as we're deleting */
#ifdef HASH_COPYKEYS
- hash_rem(ht, KEY_DATA(hi->ths));
+ val = hash_rem(ht, KEY_DATA(hi->ths));
#else
- hash_rem(ht, hi->ths->key, hi->ths->klen);
+ val = hash_rem(ht, hi->ths->key, hi->ths->klen);
+#endif
+
+#ifdef HASH_CALLBACKS
+ if(val && ht->f_free)
+ (ht->f_free)(ht->arg, val);
#endif
r++;
@@ -446,17 +479,58 @@ int hash_purge(hash_t* ht, time_t stamp)
}
#ifdef HASH_COPYKEYS
-void* hash_touch(hash_index_t* hi, const void** key);
+void hash_touch(hash_t* ht, const void* key)
{
hash_entry_t** hep = find_entry(ht, key, NULL);
#else
-void* hash_touch(hash_index_t* hi, const void** key, size_t* klen);
+void hash_touch(hash_t* ht, const void* key, size_t* klen)
{
hash_entry_t** hep = find_entry(ht, key, klen, NULL);
#endif
- if(he && *he)
- ((*he)->stamp) = time(NULL);
+ if(hep && *hep)
+ ((*hep)->stamp) = time(NULL);
+}
+
+int hash_bump(hash_t* ht)
+{
+ hash_index_t* hi;
+ void* key = NULL;
+ void* val = NULL;
+ time_t least = 0;
+#ifndef HASH_COPYKEYS
+ size_t klen = 0;
+#endif
+
+ for(hi = hash_first(ht); hi; hi = hash_next(hi))
+ {
+ if(least == 0 || hi->ths->stamp < least)
+ {
+ least = hi->ths->stamp;
+ key = KEY_DATA(hi->ths);
+#ifndef HASH_COPYKEYS
+ klen = hi->this->klen;
+#endif
+ }
+ }
+
+ if(key)
+ {
+#ifdef HASH_COPYKEYS
+ val = hash_rem(ht, key);
+#else
+ val = hash_rem(ht, key, klen);
+#endif
+
+#ifdef HASH_CALLBACKS
+ if(val && ht->f_free)
+ (ht->f_free)(ht->arg, (void*)val);
+#endif
+
+ return 1;
+ }
+
+ return 0;
}
#endif /* HASH_TIMESTAMP */
diff --git a/common/hash.h b/common/hash.h
index 9e8174c..2b22b64 100644
--- a/common/hash.h
+++ b/common/hash.h
@@ -22,7 +22,7 @@
#define __HASH_H__
/*
- * Options to define. You need to build both this file and
+ * Features to define. You need to build both this file and
* the corresponding hash.c file with whatever options you set here
*/
@@ -32,6 +32,9 @@
/* Keep key values internally */
#define HASH_COPYKEYS 1
+/* Hash callback functionality */
+#define HASH_CALLBACKS 1
+
#ifdef __cplusplus
extern "C" {
@@ -49,16 +52,31 @@ typedef struct hash_t hash_t;
/* Abstract type for scanning hash tables. */
typedef struct hash_index_t hash_index_t;
+/* A callback during remove operations */
+#ifdef HASH_CALLBACKS
+ typedef void (*hash_free_val_t)(void* arg, void* val);
+#endif
+
/* Create a hash table */
-#ifdef HASH_COPYKEYS
- hash_t* hash_create(size_t klen);
+#ifdef HASH_CALLBACKS
+ #ifdef HASH_COPYKEYS
+ hash_t* hash_create(size_t klen, hash_free_val_t f_free, void* arg);
+ #else
+ hash_t* hash_create(hash_free_val_t f_free, void* arg);
+ #endif
#else
- hash_t* hash_create();
-#endif
+ #ifdef HASH_COPYKEYS
+ hash_t* hash_create(size_t klen);
+ #else
+ hash_t* hash_create();
+ #endif
+#endif /* HASH_CALLBACKS */
+
/* To release all resources for a hash table */
void hash_free(hash_t* ht);
+
/**
* Associate a value with a key in a hash table.
*
@@ -70,11 +88,12 @@ void hash_free(hash_t* ht);
* val must not be null
*/
#ifdef HASH_COPYKEYS
- int hash_set(hash_t* ht, const void* key, const void* val);
+ int hash_set(hash_t* ht, const void* key, void* val);
#else
- int hash_set(hash_t* ht, const void* key, size_t klen, const void* val);
+ int hash_set(hash_t* ht, const void* key, size_t klen, void* val);
#endif
+
/**
* Remove a value and key form the hash table
*
@@ -83,11 +102,12 @@ void hash_free(hash_t* ht);
* klen Length of the key. Can be HASH_KEY_STRING to use the string length
*/
#ifdef HASH_COPYKEYS
- void hash_rem(hash_t* ht, const void* key);
+ void* hash_rem(hash_t* ht, const void* key);
#else
- void hash_rem(hash_t* ht, const void* key, size_t klen);
+ void* hash_rem(hash_t* ht, const void* key, size_t klen);
#endif
+
/**
* Look up the value associated with a key in a hash table.
*
@@ -103,6 +123,7 @@ void hash_free(hash_t* ht);
void* hash_get(hash_t* ht, const void* key, size_t klen);
#endif
+
/**
* Start iterating over the entries in a hash table.
*
@@ -114,6 +135,7 @@ void hash_free(hash_t* ht);
*/
hash_index_t* hash_first(hash_t* ht);
+
/**
* Continue iterating over the entries in a hash table.
*
@@ -124,6 +146,7 @@ hash_index_t* hash_first(hash_t* ht);
*/
hash_index_t* hash_next(hash_index_t* hi);
+
/**
* Get the current entry's details from the iteration state.
*
@@ -141,19 +164,26 @@ hash_index_t* hash_next(hash_index_t* hi);
void* hash_this(hash_index_t* hi, const void** key, size_t* klen);
#endif
+
/**
* Purge entries before a certain timestamp
*/
#ifdef HASH_TIMESTAMP
+
int hash_purge(hash_t* ht, time_t stamp);
#ifdef HASH_COPYKEYS
- void hash_touch(hash_index_t* hi, const void** key);
+ void hash_touch(hash_t* ht, const void* key);
#else
- void hash_touch(hash_index_t* hi, const void** key, size_t* klen);
+ void hash_touch(hash_t* ht, const void* key, size_t* klen);
#endif
+
+/* Bumps the oldest out */
+int hash_bump(hash_t* ht);
+
#endif
+
/**
* Get the number of key/value pairs in the hash table.
*
diff --git a/common/md5.c b/common/md5.c
index c909bb3..2f92bec 100644
--- a/common/md5.c
+++ b/common/md5.c
@@ -18,6 +18,8 @@
#include <string.h>
#include "md5.h"
+void md5_transform(unsigned int buf[4], unsigned int const in[16]);
+
#ifdef BIG_ENDIAN
void
byteSwap(unsigned int *buf, unsigned words)
@@ -39,7 +41,7 @@ byteSwap(unsigned int *buf, unsigned words)
* initialization constants.
*/
void
-MD5Init(struct MD5Context *ctx)
+md5_init(md5_ctx_t* ctx)
{
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
@@ -55,9 +57,10 @@ MD5Init(struct MD5Context *ctx)
* of bytes.
*/
void
-MD5Update(struct MD5Context *ctx, const unsigned char* buf, unsigned len)
+md5_update(md5_ctx_t* ctx, const void* b, unsigned len)
{
unsigned int t;
+ const unsigned char* buf = (const unsigned char*)b;
/* Update byte count */
@@ -73,7 +76,7 @@ MD5Update(struct MD5Context *ctx, const unsigned char* buf, unsigned len)
/* First chunk is an odd size */
memcpy((unsigned char *)ctx->in + 64 - t, buf, t);
byteSwap(ctx->in, 16);
- MD5Transform(ctx->buf, ctx->in);
+ md5_transform(ctx->buf, ctx->in);
buf += t;
len -= t;
@@ -81,7 +84,7 @@ MD5Update(struct MD5Context *ctx, const unsigned char* buf, unsigned len)
while (len >= 64) {
memcpy(ctx->in, buf, 64);
byteSwap(ctx->in, 16);
- MD5Transform(ctx->buf, ctx->in);
+ md5_transform(ctx->buf, ctx->in);
buf += 64;
len -= 64;
}
@@ -95,7 +98,7 @@ MD5Update(struct MD5Context *ctx, const unsigned char* buf, unsigned len)
* 1 0* (64-bit count of bits processed, MSB-first)
*/
void
-MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+md5_final(unsigned char digest[16], md5_ctx_t* ctx)
{
int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */
unsigned char *p = (unsigned char *)ctx->in + count;
@@ -109,7 +112,7 @@ MD5Final(unsigned char digest[16], struct MD5Context *ctx)
if (count < 0) { /* Padding forces an extra block */
memset(p, 0, (unsigned int)count + 8);
byteSwap(ctx->in, 16);
- MD5Transform(ctx->buf, ctx->in);
+ md5_transform(ctx->buf, ctx->in);
p = (unsigned char *)ctx->in;
count = 56;
}
@@ -119,13 +122,30 @@ MD5Final(unsigned char digest[16], struct MD5Context *ctx)
/* Append length in bits and transform */
ctx->in[14] = ctx->bytes[0] << 3;
ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
- MD5Transform(ctx->buf, ctx->in);
+ md5_transform(ctx->buf, ctx->in);
byteSwap(ctx->buf, 4);
memcpy(digest, ctx->buf, 16);
memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
}
+void
+md5_string(unsigned char digest[MD5_LEN], const char* str)
+{
+ md5_ctx_t md5;
+ md5_init(&md5);
+ md5_update(&md5, str, strlen(str));
+ md5_final(digest, &md5);
+}
+
+int
+md5_strcmp(unsigned char digest[MD5_LEN], const char* str)
+{
+ unsigned char other[MD5_LEN];
+ md5_string(other, str);
+ return memcmp(digest, other, MD5_LEN);
+}
+
/* The four core functions - F1 is optimized somewhat */
/* #define F1(x, y, z) (x & y | ~x & z) */
@@ -144,7 +164,7 @@ MD5Final(unsigned char digest[16], struct MD5Context *ctx)
* the data and converts bytes into longwords for this routine.
*/
void
-MD5Transform(unsigned int buf[4], unsigned int const in[16])
+md5_transform(unsigned int buf[4], unsigned int const in[16])
{
register unsigned int a, b, c, d;
diff --git a/common/md5.h b/common/md5.h
index 62cbf2b..de431da 100644
--- a/common/md5.h
+++ b/common/md5.h
@@ -15,7 +15,7 @@
* will fill a supplied 16-byte array with the digest.
*
* Changed so as no longer to depend on Colin Plumb's `usual.h'
- * header definitions; now uses stuff from dpkg's config.h
+ * header definitions
* - Ian Jackson <ijackson@nyx.cs.du.edu>.
* Still in the public domain.
*/
@@ -27,20 +27,22 @@ extern "C" {
#ifndef MD5_H
#define MD5_H
+#define MD5_LEN 16
#define md5byte unsigned char
-struct MD5Context {
- unsigned int buf[4];
- unsigned int bytes[2];
- unsigned int in[16];
-};
-
-typedef struct MD5Context MD5_CTX;
+typedef struct md5_ctx
+{
+ unsigned int buf[4];
+ unsigned int bytes[2];
+ unsigned int in[16];
+}
+md5_ctx_t;
-void MD5Init(struct MD5Context *context);
-void MD5Update(struct MD5Context *context, const md5byte* buf, unsigned len);
-void MD5Final(unsigned char digest[16], struct MD5Context *context);
-void MD5Transform(unsigned int buf[4], unsigned int const in[16]);
+void md5_init(md5_ctx_t* context);
+void md5_update(md5_ctx_t* context, const void* buf, unsigned len);
+void md5_final(unsigned char digest[MD5_LEN], md5_ctx_t* context);
+void md5_string(unsigned char digest[MD5_LEN], const char* str);
+int md5_strcmp(unsigned char digest[MD5_LEN], const char* str);
#ifdef __cplusplus
}
diff --git a/common/sha1.c b/common/sha1.c
new file mode 100644
index 0000000..a40732e
--- /dev/null
+++ b/common/sha1.c
@@ -0,0 +1,164 @@
+/*
+ * Fetched from ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c on May 4, 1999
+ * Modified by EPG May 6, 1999
+ */
+
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+*/
+
+#include <string.h>
+#include "sha1.h"
+
+typedef unsigned long ulong;
+typedef unsigned char uchar;
+
+void SHA1Transform(ulong state[5], const uchar buffer[64]);
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#ifdef LITTLE_ENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+ |(rol(block->l[i],8)&0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+ ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1Transform(ulong state[5], const uchar buffer[64])
+{
+ ulong a, b, c, d, e;
+ typedef union {
+ uchar c[64];
+ ulong l[16];
+ } CHAR64LONG16;
+ CHAR64LONG16* block;
+#ifdef SHA1HANDSOFF
+ static uchar workspace[64];
+ block = (CHAR64LONG16*)workspace;
+ memcpy(block, buffer, 64);
+#else
+ block = (CHAR64LONG16*)buffer;
+#endif
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ /* Wipe variables */
+ a = b = c = d = e = 0;
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void sha1_init(sha1_ctx_t* context)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void sha1_update(sha1_ctx_t* context, const void* d, unsigned int len)
+{
+ unsigned int i, j;
+ const uchar* data = (uchar*)d;
+
+ j = (context->count[0] >> 3) & 63;
+ if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
+ context->count[1] += (len >> 29);
+ if ((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64-j));
+ SHA1Transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ SHA1Transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+void sha1_final(uchar digest[20], sha1_ctx_t* context)
+{
+ ulong i;
+ uchar finalcount[8];
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (uchar)((context->count[(i >= 4 ? 0 : 1)]
+ >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
+ }
+ sha1_update(context, (uchar *)"\200", 1);
+ while ((context->count[0] & 504) != 448) {
+ sha1_update(context, (uchar *)"\0", 1);
+ }
+ sha1_update(context, finalcount, 8); /* Should cause a SHA1Transform() */
+ for (i = 0; i < 20; i++) {
+ digest[i] = (uchar)
+ ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+ }
+ /* Wipe variables */
+ i = 0;
+ memset(context->buffer, 0, 64);
+ memset(context->state, 0, 20);
+ memset(context->count, 0, 8);
+ memset(&finalcount, 0, 8);
+#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite it's own static vars */
+ SHA1Transform(context->state, context->buffer);
+#endif
+}
+
+
diff --git a/common/sha1.h b/common/sha1.h
new file mode 100644
index 0000000..37f734f
--- /dev/null
+++ b/common/sha1.h
@@ -0,0 +1,30 @@
+#ifndef __SHA1_H__
+#define __SHA1_H__
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SHA1_LEN 20
+
+typedef struct sha1_ctx
+{
+ unsigned long state[5];
+ unsigned long count[2];
+ unsigned char buffer[64];
+}
+sha1_ctx_t;
+
+void sha1_init(sha1_ctx_t* context);
+void sha1_update(sha1_ctx_t* context, const void* data, unsigned int len);
+void sha1_final(unsigned char digest[SHA1_LEN], sha1_ctx_t* context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __SHA1_H__
diff --git a/configure.in b/configure.in
index fbce603..2abbf10 100644
--- a/configure.in
+++ b/configure.in
@@ -51,14 +51,29 @@ AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
+# Debug mode
+AC_ARG_ENABLE(debug,
+ AC_HELP_STRING([--enable-debug],
+ [Compile binaries in debug mode]))
+
+if test "$enable_debug" = "yes"; then
+ CFLAGS="$CFLAGS -g -O0 -D_DEBUG=1"
+ echo "enabling debug compile mode"
+fi
+
+# TODO: Make SMB support an option
+# TODO: Make LDAP support an option
+
# Check for libraries
AC_CHECK_LIB([pthread], [pthread_create], ,
[ echo "ERROR: Pthread libraries required."; exit 1] )
-# Check for libraries
AC_CHECK_LIB([crypt], [crypt], ,
[ echo "ERROR: Crypt library required."; exit 1] )
-
+
+AC_CHECK_LIB([ldap], [ldap_init], ,
+ [ echo "ERROR: Open LDAP 2.x library required."; exit 1] )
+
# Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS([unistd.h stdio.h stddef.h fcntl.h stdlib.h assert.h errno.h stdarg.h err.h string.h], ,
@@ -73,7 +88,6 @@ AC_CHECK_DECL(PTHREAD_MUTEX_ERRORCHECK_NP, [AC_DEFINE(HAVE_ERR_MUTEX, 1, "Error
[echo "ERROR: Missing error checking mutex functionality in pthread.h"],
[ #include <pthread.h> ])], [ #include <pthread.h> ])
-
# Required Functions
AC_CHECK_FUNCS([memset strerror malloc realloc getopt strchr tolower], ,
[echo "ERROR: Required function missing"; exit 1])
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 4525edf..c93e89f 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -2,8 +2,8 @@
bin_PROGRAMS = httpauthd
httpauthd_SOURCES = httpauthd.c httpauthd.h usuals.h compat.h compat.c \
- buffer.c misc.c basic.c ntlm.c hash.c hash.h ntlmssp.h ntlmssp.c \
- md5.c md5.h \
+ buffer.c misc.c basic.h basic.c ntlm.c hash.c hash.h ntlmssp.h ntlmssp.c \
+ md5.c md5.h sha1.c sha1.h digest.h digest.c ldap.c defaults.h simple.c \
smblib/smblib.c smblib/smblib-util.c smblib/file.c smblib/smb-errors.c smblib/exper.c smblib/smblib-api.c \
rfcnb/rfcnb-io.c rfcnb/rfcnb-util.c rfcnb/session.c
diff --git a/daemon/basic.c b/daemon/basic.c
index a8325af..da73682 100644
--- a/daemon/basic.c
+++ b/daemon/basic.c
@@ -1,243 +1,45 @@
-/* On some linux this is required to get crypt to show itself */
-#define _XOPEN_SOURCE
-
#include "usuals.h"
#include "httpauthd.h"
-#include "defaults.h"
-
-#include <syslog.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#define BASIC_MAXLINE 128
-
-/* This needs to be the same as an MD5 hash length */
-#define BASIC_HASH_KEY_LEN 16
-#define BASIC_ESTABLISHED (void*)1
-
-/* -------------------------------------------------------------------------------
- * Structures
- */
-
-typedef struct basic_context
-{
- const char* filename; /* The file name with the user names */
- const char* realm; /* The realm for basic authentication */
-
- /* Context ----------------------------------------------------------- */
- hash_t* established; /* Established connections */
-}
-basic_context_t;
-
-/* -------------------------------------------------------------------------------
- * Handler Functions
- */
-
-int basic_config(ha_context_t* context, const char* name, const char* value)
-{
- basic_context_t* ctx = (basic_context_t*)(context.data);
-
- if(strcmp(name, "basicfile") == 0)
- {
- ctx->filename = value;
- return HA_OK;
- }
-
- else if(strcmp(name, "realm") == 0)
- {
- ctx->realm = value;
- return HA_OK;
- }
-
- return HA_FALSE;
-}
-
-int basic_init(ha_context_t* context)
-{
- /* Don't do global init */
- if(!context)
- return HA_OK;
-
- basic_context_t* ctx = (basic_context_t*)(context.data);
- int fd;
-
- /* Make sure there are some types of authentication we can do */
- if(!(context->types & HA_TYPE_BASIC))
- {
- ha_messagex(LOG_ERR, "Basic module configured, but does not implement any "
- "configured authentication type.");
- return HA_ERROR;
- }
-
- /* Check to make sure the file exists */
- if(!ctx->filename)
- {
- ha_messagex(LOG_ERR, "Basic configuration incomplete. "
- "Must have a BasicFile configured.");
- return HA_ERROR;
- }
-
- fd = open(ctx->filename, O_RDONLY);
- if(fd == -1)
- {
- ha_message(LOG_ERR, "can't open file for basic authentication: %s", ctx->filename);
- return HA_ERROR;
- }
-
- close(fd);
+#include "basic.h"
- /* Initialize our cache table */
- if(!(ctx->established = hash_create(BASIC_HASH_KEY_LEN)))
- {
- ha_messagex(LOG_CRIT, "out of memory");
- return HA_ERROR;
- }
-
- return HA_OK;
-}
-
-int basic_process(ha_context_t* context, ha_request_t* req,
- ha_response_t* resp, ha_buffer_t* buf)
+int basic_parse(const char* header, ha_buffer_t* buf, basic_header_t* rec)
{
- basic_context_t* ctx = (basic_context_t*)(context.data);
- const char* header;
char* t;
- int found = 0;
- ha_basic_header_t basic;
-
- memset(&basic, 0, sizeof(basic));
-
- ha_lock(NULL);
- /* Purge the cache */
- hash_purge(ctx->established, time(NULL) - context->timeout);
-
- ha_unlock(NULL);
+ memset(rec, 0, sizeof(*rec));
+ /* Trim the white space */
+ while(*header && isspace(*header))
+ header++;
/*
- * Init checked and made sure basic auth is enabled, so we
- * can take that for granted here.
+ * Authorization header is in this format:
+ *
+ * "Basic " B64(user ":" password)
*/
+ ha_bufdec64(buf, header, 0);
+ header = ha_bufdata(buf);
- header = ha_getheader(req, "Authorization", BASIC_PREFIX);
- if(header)
- {
- if(ha_parsebasic(header, buf, &basic) == HA_ERROR)
- return HA_ERROR;
-
- /* Check and see if this connection is in the cache */
- ha_lock(NULL);
-
- if(hash_get(ctx->established, basic.key) == BASIC_ESTABLISHED)
- {
- hash_touch(ctx->established, basic.key);
- found = 1;
- }
-
- ha_unlock(NULL);
- }
-
-
- /* If we have a user name and password that wasn't in the cache */
- if(!found && basic.user && basic.user[0] &&
- basic.password && basic.password[0])
- {
- FILE* f;
- char line[BASIC_MAXLINE];
-
- f = fopen(ctx->filename, "r");
- if(!f)
- {
- ha_message(LOG_ERR, "can't open file for basic auth: %s", ctx->filename);
- resp->code = HA_SERVER_ERROR;
- return HA_FALSE;
- }
-
- /*
- * Note: There should be no returns or jumps between
- * this point and the closing of the file below.
- */
-
- /* Now look through the whole file */
- while(!feof(f))
- {
- fgets(line, BASIC_MAXLINE, f);
-
- if(ferror(f))
- ha_message(LOG_ERR, "error reading basic password file");
-
- t = strchr(line, ':');
- if(t)
- {
- /* Split the line */
- *t = 0;
- t++;
-
- /* Check the user */
- if(strcmp(line, basic.user) == 0)
- {
- /* Not sure if crypt is thread safe so we lock */
- ha_lock();
-
- /* Check the password */
- if(strcmp(crypt(basic.password, t), t) == 0)
- found = 1;
-
- ha_unlock();
-
- if(found)
- break;
- }
- }
- }
+ if(!header)
+ return HA_ERROR;
- fclose(f);
- }
- /* If we found a user then tell them what it was */
- if(found)
- {
- resp->code = HA_SERVER_ACCEPT;
- resp->detail = basic.user;
+ /* We have a cache key at this point so hash it */
+ md5_string(rec->key, header);
- /* We put this connection into the successful connections */
- if(!hash_set(ctx->established, basic.key, BASIC_ESTABLISHED))
- {
- ha_messagex(LOG_CRIT, "out of memory");
- return HA_ERROR;
- }
- }
- /* Otherwise make the browser authenticate */
- else
+ /* Parse the user. We need it in any case */
+ t = strchr(header, ':');
+ if(t != NULL)
{
- resp->code = HA_SERVER_DECLINE;
-
- ha_bufnext(buf);
- ha_bufcat(buf, "BASIC realm=\"", ctx->realm ? ctx->realm : "",
- "\"", NULL);
+ /* Break the string in half */
+ *t = 0;
- ha_addheader(resp, "WWW-Authenticate", ha_bufdata(buf));
+ rec->user = header;
+ rec->password = t + 1;
}
return HA_OK;
}
-
-/* -------------------------------------------------------------------------------
- * Handler Definition
- */
-
-ha_handler_t basic_handler =
-{
- "Basic", /* The type */
- basic_init, /* Initialization function */
- basic_destroy, /* Uninitialization routine */
- basic_config, /* Config routine */
- basic_process, /* Processing routine */
- NULL, /* A default context */
- sizeof(basic_context_t) /* Size of the context */
-};
-
diff --git a/daemon/basic.h b/daemon/basic.h
new file mode 100644
index 0000000..c28dc1e
--- /dev/null
+++ b/daemon/basic.h
@@ -0,0 +1,17 @@
+
+#ifndef __BASIC_H__
+#define __BASIC_H__
+
+#include "md5.h"
+
+typedef struct basic_header
+{
+ const char* user;
+ const char* password;
+ unsigned char key[MD5_LEN];
+}
+basic_header_t;
+
+int basic_parse(const char* header, ha_buffer_t* buf, basic_header_t* rec);
+
+#endif /* __BASIC_H__ */
diff --git a/daemon/defaults.h b/daemon/defaults.h
index bfcd9f4..755d12c 100644
--- a/daemon/defaults.h
+++ b/daemon/defaults.h
@@ -6,4 +6,4 @@
#define DEFAULT_PENDING_TIMEOUT 60
#define DEFAULT_TIMEOUT 900
-#endif /* __DEFAULTS_H__ */ \ No newline at end of file
+#endif /* __DEFAULTS_H__ */
diff --git a/daemon/digest.c b/daemon/digest.c
new file mode 100644
index 0000000..51120a0
--- /dev/null
+++ b/daemon/digest.c
@@ -0,0 +1,533 @@
+
+#include "usuals.h"
+#include "md5.h"
+#include "httpauthd.h"
+#include "digest.h"
+
+#include <syslog.h>
+
+/* A globally unique counter used to guarantee uniqueness of nonces */
+static unsigned int g_digest_unique = 0;
+
+typedef struct internal_nonce
+{
+ unsigned char hash[MD5_LEN];
+ time_t tm;
+ unsigned int unique;
+}
+internal_nonce_t;
+
+void digest_makenonce(unsigned char* nonce, unsigned char* secret, unsigned char* old)
+{
+ internal_nonce_t in;
+ md5_ctx_t md5;
+
+ ASSERT(sizeof(internal_nonce_t) == DIGEST_NONCE_LEN);
+
+ if(old)
+ {
+ internal_nonce_t* nold = (internal_nonce_t*)old;
+ in.tm = nold->tm;
+ in.unique = nold->unique;
+ }
+ else
+ {
+ in.tm = time(NULL);
+ ha_lock(NULL);
+ in.unique = g_digest_unique++;
+ ha_unlock(NULL);
+ }
+
+ md5_init(&md5);
+ md5_update(&md5, secret, DIGEST_SECRET_LEN);
+ md5_update(&md5, &(in.tm), sizeof(in.tm));
+ md5_update(&md5, &(in.unique), sizeof(in.unique));
+ md5_final(in.hash, &md5);
+
+ memcpy(nonce, &in, DIGEST_NONCE_LEN);
+}
+
+int digest_checknonce(unsigned char* nonce, unsigned char* secret, time_t* tm)
+{
+ internal_nonce_t in;
+
+ ASSERT(sizeof(internal_nonce_t) == DIGEST_NONCE_LEN);
+
+ digest_makenonce((unsigned char*)&in, secret, nonce);
+
+ if(memcmp((unsigned char*)&in, nonce, DIGEST_SECRET_LEN) == 0)
+ {
+ if(tm)
+ *tm = in.tm;
+
+ return HA_OK;
+ }
+
+ return HA_FALSE;
+}
+
+digest_record_t* digest_makerec(unsigned char* nonce, const char* user)
+{
+ digest_record_t* rec = (digest_record_t*)malloc(sizeof(*rec));
+ if(!rec)
+ {
+ ha_messagex(LOG_CRIT, "out of memory");
+ return NULL;
+ }
+
+ memset(rec, 0, sizeof(*rec));
+ memcpy(rec->nonce, nonce, DIGEST_NONCE_LEN);
+
+ md5_string(rec->userhash, user);
+ return rec;
+}
+
+const char* digest_challenge(ha_buffer_t* buf, unsigned char* nonce,
+ const char* realm, const char* domains, int stale)
+{
+ ASSERT(realm);
+ ASSERT(nonce);
+
+ 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);
+
+ if(domains)
+ {
+ ha_bufjoin(buf);
+ ha_bufmcat(buf, ", domain=\"", domains, "\"", NULL);
+ }
+
+ if(stale)
+ {
+ ha_bufjoin(buf);
+ ha_bufcat(buf, ", stale=true");
+ }
+
+ return ha_bufdata(buf);
+}
+
+/*
+ * Copyright 1999-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+int digest_parse(char* header, ha_buffer_t* buf, digest_header_t* rec,
+ unsigned char* nonce)
+{
+ /*
+ * This function destroys the contents of header by
+ * terminating strings in it all over the place.
+ */
+
+ char next;
+ char* key;
+ char* value;
+
+ header = ha_bufcpy(buf, header);
+
+ if(!header)
+ return HA_ERROR;
+
+ memset(rec, 0, sizeof(rec));
+
+ while(header[0])
+ {
+ /* find key */
+
+ while(header[0] && isspace(header[0]))
+ header++;
+
+ key = header;
+
+ while(header[0] && header[0] != '=' && header[0] != ',' &&
+ !isspace(header[0]))
+ header++;
+
+ /* null terminate and move on */
+ next = header[0];
+ header[0] = '\0';
+ header++;
+
+ if(!next)
+ break;
+
+ if(isspace(header[0]))
+ {
+ while(header[0] && isspace(header[0]))
+ header++;
+
+ next = header[0];
+ }
+
+ /* find value */
+
+ if(next == '=')
+ {
+ header++;
+
+ while(header[0] && isspace(header[0]))
+ header++;
+
+ if(header[0] == '\"') /* quoted string */
+ {
+ header++;
+ value = header;
+
+ while(header[0] && header[0] != '\"')
+ header++;
+
+ header[0] = 0;
+ header++;
+ }
+
+ else /* token */
+ {
+ value = header;
+
+ while(header[0] && header[0] != ',' && !isspace(header[0]))
+ header++;
+
+ header[0] = 0;
+ header++;
+ }
+
+ while(header[0] && header[0] != ',')
+ header++;
+
+ if(header[0])
+ header++;
+
+ if(!strcasecmp(key, "username"))
+ rec->username = value;
+ else if(!strcasecmp(key, "realm"))
+ rec->realm = value;
+ else if (!strcasecmp(key, "nonce"))
+ rec->nonce = value;
+ else if (!strcasecmp(key, "uri"))
+ rec->uri = value;
+ else if (!strcasecmp(key, "response"))
+ rec->digest = value;
+ else if (!strcasecmp(key, "algorithm"))
+ rec->algorithm = value;
+ else if (!strcasecmp(key, "cnonce"))
+ rec->cnonce = value;
+ else if (!strcasecmp(key, "opaque"))
+ rec->opaque = value;
+ else if (!strcasecmp(key, "qop"))
+ rec->qop = value;
+ else if (!strcasecmp(key, "nc"))
+ rec->nc = value;
+ }
+ }
+
+ if(nonce)
+ {
+ memset(nonce, 0, DIGEST_NONCE_LEN);
+
+ if(rec->nonce)
+ {
+ void* d = ha_bufdec64(buf, rec->nonce, DIGEST_NONCE_LEN);
+
+ if(d != NULL)
+ memcpy(nonce, d, DIGEST_NONCE_LEN);
+ }
+ }
+
+ return HA_OK;
+}
+
+int digest_check(const char* realm, const char* method, const char* uri,
+ ha_buffer_t* buf, digest_header_t* dg, digest_record_t* rec)
+{
+ unsigned char hash[MD5_LEN];
+ md5_ctx_t md5;
+ const char* digest;
+ const char* t;
+
+ /* Check for digest */
+ if(!dg->digest || !dg->digest[0])
+ {
+ ha_messagex(LOG_WARNING, "digest response missing digest");
+ return HA_FALSE;
+ }
+
+ /* Username */
+ if(!dg->username || !dg->username[0] ||
+ md5_strcmp(rec->userhash, dg->username) != 0)
+ {
+ ha_messagex(LOG_WARNING, "digest response missing username");
+ return HA_FALSE;
+ }
+
+ /* The realm */
+ if(!dg->realm || strcmp(dg->realm, realm) != 0)
+ {
+ ha_messagex(LOG_WARNING, "digest response contains invalid realm: '%s'",
+ dg->realm ? dg->realm : "");
+ return HA_FALSE;
+ }
+
+ /* Components in the new RFC */
+ if(dg->qop)
+ {
+ /*
+ * We only support 'auth' qop. We don't have access to the data
+ * and wouldn't be able to support anything else.
+ */
+ if(strcmp(dg->qop, "auth") != 0)
+ {
+ ha_messagex(LOG_WARNING, "digest response contains unknown or unsupported qop: '%s'",
+ dg->qop ? dg->qop : "");
+ return HA_FALSE;
+ }
+
+ /* The cnonce */
+ if(!dg->cnonce || !dg->cnonce[0])
+ {
+ ha_messagex(LOG_WARNING, "digest response is missing cnonce value");
+ return HA_FALSE;
+ }
+
+ /* The nonce count */
+ if(!dg->nc || !dg->nc[0])
+ {
+ ha_messagex(LOG_WARNING, "digest response is missing nc value");
+ return HA_FALSE;
+ }
+
+ /* Validate the nc */
+ else
+ {
+ char* e;
+ long nc = strtol(dg->nc, &e, 10);
+
+ if(e != (dg->nc + strlen(e)) || nc != rec->nc)
+ {
+ ha_messagex(LOG_WARNING, "digest response has invalid nc value: %s",
+ dg->nc);
+ return HA_FALSE;
+ }
+ }
+ }
+
+ /* The algorithm */
+ if(dg->algorithm && strcasecmp(dg->algorithm, "MD5") != 0)
+ {
+ ha_messagex(LOG_WARNING, "digest response contains unknown or unsupported algorithm: '%s'",
+ dg->algorithm ? dg->algorithm : "");
+ return HA_FALSE;
+ }
+
+ /* Request URI */
+ if(!dg->uri)
+ {
+ ha_messagex(LOG_WARNING, "digest response is missing uri");
+ return HA_FALSE;
+ }
+
+ if(strcmp(dg->uri, uri) != 0)
+ {
+ ha_uri_t d_uri;
+ ha_uri_t s_uri;
+
+ if(ha_uriparse(buf, dg->uri, &d_uri) == HA_ERROR)
+ {
+ ha_messagex(LOG_WARNING, "digest response constains invalid uri: %s", dg->uri);
+ return HA_FALSE;
+ }
+
+ if(ha_uriparse(buf, uri, &s_uri) == HA_ERROR)
+ {
+ ha_messagex(LOG_ERR, "server sent us an invalid uri");
+ return HA_ERROR;
+ }
+
+ if(ha_uricmp(&d_uri, &s_uri) != 0)
+ {
+ ha_messagex(LOG_WARNING, "digest response contains wrong uri: %s "
+ "(should be %s)", dg->uri, uri);
+ return HA_ERROR;
+ }
+ }
+
+ /*
+ * nonce: should have already been validated by the caller who
+ * found this nice rec structure to pass us.
+ *
+ * opaque: We also don't use opaque. The caller should have validated it
+ * if it's used there.
+ */
+
+ /*
+ * Now we validate the digest response
+ */
+
+ /* Encode ha1 */
+ t = ha_bufenchex(buf, rec->ha1, MD5_LEN);
+
+ if(t == NULL)
+ return HA_ERROR;
+
+ /* Encode ha2 */
+ md5_init(&md5);
+ md5_update(&md5, method, strlen(method));
+ md5_update(&md5, ":", 1);
+ md5_update(&md5, dg->uri, strlen(dg->uri));
+ md5_final(hash, &md5);
+
+ ha_bufenchex(buf, hash, MD5_LEN);
+
+ if(!ha_bufdata(buf))
+ return HA_ERROR;
+
+
+ /* Old style digest (RFC 2069) */
+ if(!dg->qop)
+ {
+ md5_init(&md5);
+ md5_update(&md5, t, MD5_LEN * 2); /* ha1 */
+ md5_update(&md5, ":", 1);
+ md5_update(&md5, dg->nonce, strlen(dg->nonce)); /* nonce */
+ md5_update(&md5, ":", 1);
+ md5_update(&md5, ha_bufdata(buf), MD5_LEN * 2); /* ha1 */
+ md5_final(hash, &md5);
+ }
+
+ /* New style 'auth' digest (RFC 2617) */
+ else
+ {
+ md5_init(&md5);
+ md5_update(&md5, t, MD5_LEN * 2); /* ha1 */
+ md5_update(&md5, ":", 1);
+ md5_update(&md5, dg->nonce, strlen(dg->nonce)); /* nonce */
+ md5_update(&md5, ":", 1);
+ md5_update(&md5, dg->nc, strlen(dg->nc)); /* nc */
+ md5_update(&md5, ":", 1);
+ md5_update(&md5, dg->cnonce, strlen(dg->cnonce)); /* cnonce */
+ md5_update(&md5, ":", 1);
+ md5_update(&md5, dg->qop, strlen(dg->qop)); /* qop */
+ md5_update(&md5, ":", 1);
+ md5_update(&md5, ha_bufdata(buf), MD5_LEN * 2); /* ha2 */
+ md5_final(hash, &md5);
+ }
+
+ /* Encode the digest */
+ digest = ha_bufenchex(buf, hash, MD5_LEN);
+
+ if(digest == NULL)
+ return HA_ERROR;
+
+ if(strcasecmp(dg->digest, digest) != 0)
+ {
+ ha_messagex(LOG_WARNING, "digest authentication failed for user: %s", dg->username);
+ return HA_FALSE;
+ }
+
+ return HA_OK;
+}
+
+const char* digest_respond(ha_buffer_t* buf, digest_header_t* dg,
+ digest_record_t* rec, unsigned char* next)
+{
+ unsigned char hash[MD5_LEN];
+ md5_ctx_t md5;
+ const char* nextnonce = NULL;
+ const char* t;
+
+ if(next)
+ {
+ nextnonce = ha_bufenc64(buf, next, DIGEST_NONCE_LEN);
+
+ if(nextnonce == NULL)
+ return NULL;
+ }
+
+ /* For older clients RFC 2069 */
+ if(dg->qop)
+ {
+ if(nextnonce)
+ ha_bufmcat(buf, "nextnonce=\"", nextnonce, "\"", NULL);
+
+ return ha_bufdata(buf);
+ }
+
+ /* Otherwise we do the whole song and dance */
+
+ /* Encode ha1 */
+ t = ha_bufenchex(buf, rec->ha1, MD5_LEN);
+
+ if(t == NULL)
+ return NULL;
+
+ /* Encode ha2 */
+ md5_init(&md5);
+ md5_update(&md5, ":", 1);
+ md5_update(&md5, dg->uri, strlen(dg->uri));
+ md5_final(hash, &md5);
+
+ ha_bufenchex(buf, hash, MD5_LEN);
+
+ if(!ha_bufdata(buf))
+ return NULL;
+
+ /* New style 'auth' digest (RFC 2617) */
+ md5_init(&md5);
+ md5_update(&md5, t, MD5_LEN * 2); /* ha1 */
+ md5_update(&md5, ":", 1);
+ md5_update(&md5, dg->nonce, strlen(dg->nonce)); /* nonce */
+ md5_update(&md5, ":", 1);
+ md5_update(&md5, dg->nc, strlen(dg->nc)); /* nc */
+ md5_update(&md5, ":", 1);
+ md5_update(&md5, dg->cnonce, strlen(dg->cnonce)); /* cnonce */
+ md5_update(&md5, ":", 1);
+ md5_update(&md5, dg->qop, strlen(dg->qop)); /* qop */
+ md5_update(&md5, ":", 1);
+ md5_update(&md5, ha_bufdata(buf), MD5_LEN * 2); /* ha2 */
+ md5_final(hash, &md5);
+
+ /* Encode the digest */
+ t = ha_bufenchex(buf, hash, MD5_LEN);
+
+ if(t == NULL)
+ return NULL;
+
+ ha_bufmcat(buf, "rspauth=\"", t, "\"",
+ " qop=", dg->qop,
+ " nc=", dg->nc,
+ " cnonce=\"", dg->cnonce, "\"", NULL);
+
+ if(nextnonce)
+ {
+ ha_bufjoin(buf);
+ ha_bufmcat(buf, " nextnonce=\"", nextnonce, "\"", NULL);
+ }
+
+ return ha_bufdata(buf);
+}
+
+void digest_makeha1(unsigned char* digest, const char* user,
+ const char* realm, const char* password)
+{
+ md5_ctx_t md5;
+ md5_init(&md5);
+ md5_update(&md5, user, strlen(user));
+ md5_update(&md5, ":", 1);
+ md5_update(&md5, realm, strlen(realm));
+ md5_update(&md5, ":", 1);
+ md5_update(&md5, password, strlen(password));
+ md5_final(digest, &md5);
+} \ No newline at end of file
diff --git a/daemon/digest.h b/daemon/digest.h
new file mode 100644
index 0000000..6bd7acc
--- /dev/null
+++ b/daemon/digest.h
@@ -0,0 +1,57 @@
+
+#ifndef __DIGEST_H__
+#define __DIGEST_H__
+
+#include "md5.h"
+
+#define DIGEST_NONCE_LEN sizeof(time_t) + sizeof(unsigned int) + MD5_LEN
+#define DIGEST_SECRET_LEN 16
+
+/* Parsed Digest response from the client */
+typedef struct digest_header
+{
+ const char* scheme;
+ const char* realm;
+ const char* username;
+ const char* nonce;
+ const char* uri;
+ const char* method;
+ const char* digest;
+ const char* algorithm;
+ const char* cnonce;
+ const char* opaque;
+ const char* qop;
+ const char* nc;
+}
+digest_header_t;
+
+/* Kept by the server for validating the client */
+typedef struct digest_record
+{
+ unsigned char nonce[DIGEST_NONCE_LEN];
+ unsigned char userhash[MD5_LEN];
+ unsigned char ha1[MD5_LEN];
+ unsigned int nc;
+}
+digest_record_t;
+
+digest_record_t* digest_makerec(unsigned char* nonce, const char* user);
+
+int ha_digestparse(char* header, ha_buffer_t* buf, digest_header_t* rec,
+ unsigned char* nonce);
+
+int ha_digestnonce(time_t* tm, unsigned char* nonce);
+
+int ha_digestcheck(const char* realm, const char* method, const char* uri,
+ ha_buffer_t* buf, digest_header_t* header, digest_record_t* rec);
+
+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* realm, const char* domains, int stale);
+
+void digest_makeha1(unsigned char* digest, const char* user,
+ const char* realm, const char* password);
+
+#endif /* __DIGEST_H__ */
diff --git a/daemon/httpauthd.c b/daemon/httpauthd.c
index 8af5d7f..2b74586 100644
--- a/daemon/httpauthd.c
+++ b/daemon/httpauthd.c
@@ -15,17 +15,22 @@
#include "usuals.h"
#include "httpauthd.h"
+#include "defaults.h"
/* -----------------------------------------------------------------------
* Handlers Registered Here
*/
-extern ha_handler_t basic_handler;
+extern ha_handler_t simple_handler;
+extern ha_handler_t ldap_handler;
+extern ha_handler_t ntlm_handler;
/* This is the list of all available handlers */
-const ha_handler_t* g_handlerlist[] =
+ha_handler_t* g_handlerlist[] =
{
- &basic_handler
+ &simple_handler,
+ &ldap_handler,
+ &ntlm_handler
};
typedef struct httpauth_loaded
@@ -63,7 +68,7 @@ const char* kAuthHeaders[] =
/* The command definitions */
const httpauth_command_t kCommands[] =
{
- { "auth", REQTYPE_AUTH, 2, kAuthHeaders },
+ { "auth", REQTYPE_AUTH, 3, kAuthHeaders },
{ "quit", REQTYPE_QUIT, 0, 0 },
{ NULL, -1, -1 }
};
@@ -123,7 +128,7 @@ int main(int argc, char* argv[])
{
const char* conf = DEFAULT_CONFIG;
httpauth_thread_t* threads = NULL;
- const httpauth_loaded_t* h;
+ httpauth_loaded_t* h;
int daemonize = 1;
ha_buffer_t cbuf;
int r, i, sock;
@@ -392,7 +397,7 @@ int httpauth_read(int ifd, ha_request_t* req,
/* This guarantees a bit of memory allocated, and resets buffer */
ha_bufreset(buf);
- r = ha_readline(ifd, buf);
+ r = ha_bufreadline(ifd, buf);
if(r == -1)
return -1;
@@ -408,7 +413,7 @@ int httpauth_read(int ifd, ha_request_t* req,
}
/* Find the first space in the line */
- t = ha_parseword(buf, " \t");
+ t = ha_bufparseword(buf, " \t");
if(t)
{
@@ -429,7 +434,7 @@ int httpauth_read(int ifd, ha_request_t* req,
/* Now parse the arguments if any */
for(i = 0; i < cmd->args; i++)
- req->args[i] = ha_parseword(buf, " \t");
+ req->args[i] = ha_bufparseword(buf, " \t");
/* Now skip anything else we have in the buffer */
@@ -449,7 +454,7 @@ int httpauth_read(int ifd, ha_request_t* req,
if(!more)
break;
- r = ha_readline(ifd, buf);
+ r = ha_bufreadline(ifd, buf);
if(r == -1)
return -1;
@@ -477,7 +482,7 @@ int httpauth_read(int ifd, ha_request_t* req,
with a space continues the previous header */
if(valid && i > 0)
{
- t = ha_parseline(buf, 0);
+ t = ha_bufparseline(buf, 0);
if(t)
{
char* t2 = (char*)req->headers[i - 1].data + strlen(req->headers[i - 1].data);
@@ -492,7 +497,7 @@ int httpauth_read(int ifd, ha_request_t* req,
{
if(i < MAX_HEADERS)
{
- t = ha_parseword(buf, ":");
+ t = ha_bufparseword(buf, ":");
if(t)
{
@@ -512,7 +517,7 @@ int httpauth_read(int ifd, ha_request_t* req,
if(t)
{
req->headers[i].name = t;
- req->headers[i].data = ha_parseline(buf, 1);
+ req->headers[i].data = ha_bufparseline(buf, 1);
i++;
}
@@ -631,11 +636,15 @@ int httpauth_ready(int ofd, ha_buffer_t* buf)
httpauth_loaded_t* h;
/* We send a ready banner to our client */
- ha_bufnext(buf);
for(h = g_handlers; h; h = h->next)
- ha_bufcat(buf, (h == g_handlers) ? "" : " ",
+ {
+ if(h != g_handlers)
+ ha_bufjoin(buf);
+
+ ha_bufmcat(buf, (h != g_handlers) ? " " : "",
h->ctx.name, NULL);
+ }
if(ha_buferr(buf))
return httpauth_respond(ofd, HA_SERVER_ERROR, NULL);
@@ -731,7 +740,7 @@ finally:
int process_auth(ha_request_t* req, ha_response_t* resp,
ha_buffer_t* outb)
{
- const httpauth_loaded_t* h;
+ httpauth_loaded_t* h;
/* Clear out our response */
memset(resp, 0, sizeof(*resp));
@@ -769,7 +778,7 @@ int process_auth(ha_request_t* req, ha_response_t* resp,
*/
ha_context_t* config_addhandler(ha_buffer_t* buf, const char* alias,
- const ha_handler_t* handler, const ha_context_t* defaults)
+ ha_handler_t* handler, const ha_context_t* defaults)
{
httpauth_loaded_t* loaded;
int len = sizeof(httpauth_loaded_t) + handler->context_size;
@@ -834,7 +843,7 @@ int config_parse(const char* file, ha_buffer_t* buf)
int fd;
const char** t;
char* name;
- const char* value;
+ char* value;
int more = 1;
int recog;
int r, i;
@@ -856,7 +865,7 @@ int config_parse(const char* file, ha_buffer_t* buf)
{
ha_bufskip(buf);
- if((more = ha_readline(fd, buf)) == -1)
+ if((more = ha_bufreadline(fd, buf)) == -1)
return -1;
line++;
@@ -876,11 +885,11 @@ int config_parse(const char* file, ha_buffer_t* buf)
/* Check for a handler */
if(ha_bufchar(buf) == '[')
{
- const ha_handler_t* handler = NULL;
+ ha_handler_t* handler = NULL;
const char* x;
ha_bufeat(buf);
- name = ha_parseline(buf, 1);
+ name = ha_bufparseline(buf, 1);
if(!name || name[strlen(name) - 1] != ']')
errx(1, "configuration section invalid (line %d)", line);
@@ -905,7 +914,7 @@ int config_parse(const char* file, ha_buffer_t* buf)
errx(1, "unknown handler type '%s' (line %d)", name, line);
/* If we had a last handler then add it to handlers */
- loaded = config_addhandler(buf, name, handler, defaults);
+ ctx = config_addhandler(buf, name, handler, &defaults);
/* Rest doesn't apply to handler headers */
continue;
@@ -913,14 +922,14 @@ int config_parse(const char* file, ha_buffer_t* buf)
/* Parse out the name */
- name = ha_parseword(buf, ":");
+ name = ha_bufparseword(buf, ":");
if(!name || !name[0])
errx(1, "configuration file invalid (line %d)", line);
strlwr(name);
/* And get the rest of the line */
- value = ha_parseline(buf, 1);
+ value = ha_bufparseline(buf, 1);
if(value == NULL)
errx(1, "configuration missing value at (line %d)", line);
@@ -939,8 +948,8 @@ int config_parse(const char* file, ha_buffer_t* buf)
else if(strcmp("maxthreads", name) == 0)
{
- if(ha_confint(value, 1, 256, &g_maxthreads) == -1)
- errx(1, "invalid value for '%s'. must be a number between 1 and 256", name);
+ if(ha_confint(name, value, 1, 256, &g_maxthreads) == -1)
+ exit(1);
recog = 1;
}
@@ -951,13 +960,13 @@ int config_parse(const char* file, ha_buffer_t* buf)
{
if(strcmp("alias", name) == 0)
{
- loaded->alias = value;
+ ctx->name = value;
recog = 1;
}
- if(loaded->ctx.handler->f_config)
+ if(ctx->handler->f_config)
{
- r = (loaded->ctx.handler->f_config)(&(loaded->ctx), name, value);
+ r = (ctx->handler->f_config)(ctx, name, value);
if(r == -1)
return -1;
diff --git a/daemon/httpauthd.h b/daemon/httpauthd.h
index 536dfdc..b710444 100644
--- a/daemon/httpauthd.h
+++ b/daemon/httpauthd.h
@@ -16,31 +16,14 @@ typedef struct ha_buffer
}
ha_buffer_t;
+/* Initializes a buffer */
void ha_bufinit(ha_buffer_t* buf);
-void ha_buffree(ha_buffer_t* buf);
-void ha_bufreset(ha_buffer_t* buf);
-
-/* Buffer input functions */
-int ha_readline(int fd, ha_buffer_t* buf);
-char* ha_parseline(ha_buffer_t* buf, int trim);
-char* ha_parseword(ha_buffer_t* buf, const char* delims);
-
-/* Buffer output functions */
-void ha_bufnext(ha_buffer_t* buf);
-void ha_bufcat(ha_buffer_t* buf, ...);
-/* Buffer encoding functions */
-void ha_bufenc64(ha_buffer_t* buf, const const char* src, size_t len);
-void ha_bufdec64(ha_buffer_t* buf, const char* src, size_t len);
-
-void ha_bufenchex(ha_buffer_t* buf, const unsigned char* src, size_t len);
-void ha_bufdechex(ha_buffer_t* buf, const char* src, size_t len);
-
-/* Memory allocation functions */
-void* ha_bufmalloc(ha_buffer_t* buf, size_t sz);
+/* Frees all memory associated with a buffer */
+void ha_buffree(ha_buffer_t* buf);
-#define ha_bufskip(buf) \
- ((buf)->_pp = (buf)->_rp)
+/* Resets a buffer for later reuse */
+void ha_bufreset(ha_buffer_t* buf);
#define ha_buflen(buf) \
((buf)->_rp - (buf)->_pp)
@@ -51,32 +34,87 @@ void* ha_bufmalloc(ha_buffer_t* buf, size_t sz);
#define ha_bufdata(buf) \
((buf)->_pp)
+#define ha_buferr(buf) \
+ ((buf)->_dt == NULL)
+
+/* Buffer input functions ------------------------------------------------ */
+
+/* Read a line from an input handle */
+int ha_bufreadline(int fd, ha_buffer_t* buf);
+
+/* Parse the current line */
+char* ha_bufparseline(ha_buffer_t* buf, int trim);
+
+/* Parse a word from the current block */
+char* ha_bufparseword(ha_buffer_t* buf, const char* delims);
+
+#define ha_bufskip(buf) \
+ ((buf)->_pp = (buf)->_rp)
+
#define ha_bufeat(buf) \
((!ha_buferr(buf) && ha_buflen(buf) > 0) ? ++((buf)->_pp) : (buf)->_pp)
-#define ha_buferr(buf) \
- ((buf)->_dt == NULL)
+/* Buffer output functions ----------------------------------------------- */
+
+/* Adds multiple strings together */
+char* ha_bufmcat(ha_buffer_t* buf, ...);
+
+/* Copies a string to the buffer */
+char* ha_bufcpy(ha_buffer_t* buf, const char* src);
+
+/* Copies a portion of a string to the buffer */
+char* ha_bufncpy(ha_buffer_t* buf, const char* src, size_t len);
+
+/* Opens up the end of the current block so it can be joined by more data */
+#define ha_bufjoin(buf) \
+ ((buf)->_rp && ((buf)->_rp != (buf)->_pp) ? (buf)->_rp-- : (buf)->_rp)
+
+#define ha_bufcat ha_bufcpy
+
+/* Buffer allocation functions ------------------------------------------- */
+
+/* Memory allocation */
+void* ha_bufmalloc(ha_buffer_t* buf, size_t bytes);
+
+void* ha_bufmemdup(ha_buffer_t* buf, const void* src, size_t bytes);
+
+/* Buffer Encoding Functions --------------------------------------------- */
+
+/* Encode an array of bytes in base 64 */
+char* ha_bufenc64(ha_buffer_t* buf, const void* src, size_t bytes);
+
+/* Decode an array of bytes from base 64 */
+void* ha_bufdec64(ha_buffer_t* buf, const char* src, size_t bytes);
+
+/* Encode an array of bytes in hex */
+char* ha_bufenchex(ha_buffer_t* buf, const void* src, size_t bytes);
+
+/* Decode an array of bytes in hex */
+void* ha_bufdechex(ha_buffer_t* buf, const char* src, size_t bytes);
+
/* -----------------------------------------------------------------------
* HTTP Auth Handlers
*/
-typedef struct ha_context_t;
+struct ha_context;
+struct ha_request;
+struct ha_response;
/*
* This function initializes the handler. It gets called
* after the configuration gets loaded so if a config func
* is registered it'll get called before this.
*/
-typedef int (*auth_init_t)(ha_context_t* ctx);
+typedef int (*auth_init_t)(struct ha_context* ctx);
/*
* This function is called when the app exits. All threads
* should have completed at this point, so it's not necessary
* to be thread safe in here
*/
-typedef void (*auth_destroy_t)(ha_context_t* ctx);
+typedef void (*auth_destroy_t)(struct ha_context* ctx);
/*
* Called once for each configuration parameter. This is
@@ -84,15 +122,15 @@ typedef void (*auth_destroy_t)(ha_context_t* ctx);
* always be lower case. White space will always be trimmed
* from the value.
*/
-typedef int (*auth_config_t)(ha_context_t* ctx, const char* name, const char* value);
+typedef int (*auth_config_t)(struct ha_context* ctx, const char* name, const char* value);
/*
* Called for each authentication request that is designated
* for this handler. Note that all data access in this
* function must be thread-safe.
*/
-typedef int (*auth_process_t)(ha_context_t* ctx, ha_request_t* req,
- ha_response_t* resp, ha_buffer_t* mem);
+typedef int (*auth_process_t)(struct ha_context* ctx, struct ha_request* req,
+ struct ha_response* resp, ha_buffer_t* mem);
/* An authentication handler */
typedef struct ha_handler
@@ -131,7 +169,7 @@ ha_handler_t;
struct ha_options;
/* Context passed to the handler functions below */
-typdef struct ha_context
+typedef struct ha_context
{
const char* name; /* A name assigned by the configuration file */
ha_handler_t* handler; /* The original handler structure */
@@ -152,7 +190,7 @@ ha_context_t;
* should be no need to change it unless we're
* adding or removing commands
*/
-#define MAX_ARGS 2
+#define MAX_ARGS 6
/*
* The maximum number of pertinent headers to read
@@ -183,6 +221,10 @@ 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
+
/* A single request from client */
typedef struct ha_request
{
@@ -220,13 +262,6 @@ void ha_addheader(ha_response_t* resp, const char* name, const char* data);
int ha_confbool(const char* name, const char* conf, int* value);
int ha_confint(const char* name, const char* conf, int min, int max, int* value);
-/* A little hashing */
-#ifndef MD5_LEN
- #define MD5_LEN 16
-#endif
-
-void ha_md5string(const char* data, unsigned char* hash);
-
/* -----------------------------------------------------------------------
* Error Handling
*/
@@ -244,54 +279,8 @@ void ha_messagex(int level, const char* msg, ...);
#define HA_TYPE_BASIC 1 << 1
#define HA_PREFIX_BASIC "Basic "
-typedef struct ha_basic_header
-{
- const char* user;
- const char* password;
- unsigned char key[MD5_LEN];
-}
-ha_basic_header_t;
-
-int ha_parsebasic(char* header, ha_buffer_t* buf, ha_basic_header_t* rec);
-
-
#define HA_TYPE_DIGEST 1 << 2
#define HA_PREFIX_DIGEST "Digest "
-#define HA_DIGEST_NONCE_LEN MD5_LEN * 2
-
-/* Parsed Digest response from the client */
-typedef struct ha_digest_header
-{
- const char* scheme;
- const char* realm;
- const char* username;
- const char* nonce;
- const char* uri;
- const char* method;
- const char* digest;
- const char* algorithm;
- const char* cnonce;
- const char* opaque;
- const char* message_qop;
- const char* nc;
- unsigned char key[MD5_LEN];
-}
-ha_digest_header_t;
-
-/* Kept by the server for validating the client */
-typedef struct ha_digest_record
-{
- unsigned char nonce[HA_DIGEST_NONCE_LEN];
- unsigned char userhash[MD5_LEN];
- unsigned char ha1[MD5_LEN];
- unsigned int nc;
-}
-ha_digest_record_t;
-
-int ha_digestparse(char* header, ha_buffer_t* buf, ha_digest_header_t* rec);
-int ha_digestcheck(const char* realm, const char* method, const char* uri,
- ha_buffer_t* buf, ha_digest_header_t* header, ha_digest_record_t* rec);
-
#define HA_TYPE_NTLM 1 << 3
#define HA_PREFIX_NTLM "NTLM "
@@ -301,21 +290,22 @@ int ha_digestcheck(const char* realm, const char* method, const char* uri,
* URI Parse Support
*/
-struct ha_uri_t
+typedef struct ha_uri
{
- /* Note: We only support HTTP uris */
+ const char* scheme;
const char* user;
const char* pw;
const char* host;
unsigned short port;
const char* path;
const char* query;
- const char* bookmark;
-};
-
+ const char* fragment;
+}
+ha_uri_t;
-char* ha_uriformat(const ha_uri_t* uri, ha_buffer_t* buf);
-int ha_uriparse(const char* str, ha_uri_t* uri);
+char* ha_uriformat(ha_buffer_t* buf, const ha_uri_t* uri);
+int ha_uriparse(ha_buffer_t* buf, const char* suri, ha_uri_t* uri);
+int ha_uricmp(ha_uri_t* one, ha_uri_t* two);
/* -----------------------------------------------------------------------
@@ -325,4 +315,12 @@ int ha_uriparse(const char* str, ha_uri_t* uri);
void ha_lock();
void ha_unlock();
+
+/* -----------------------------------------------------------------------
+ * Miscellaneous
+ */
+
+int ha_genrandom(unsigned char* data, size_t len);
+
+
#endif /* __HTTPAUTHD_H__ */
diff --git a/daemon/ldap.c b/daemon/ldap.c
index 2927b1d..59af797 100644
--- a/daemon/ldap.c
+++ b/daemon/ldap.c
@@ -1,23 +1,31 @@
/* TODO: Include attribution for ideas, and code from mod_auth_digest */
+#define _XOPEN_SOURCE
+
#include "usuals.h"
#include "httpauthd.h"
#include "hash.h"
#include "defaults.h"
+#include "digest.h"
+#include "basic.h"
+#include "md5.h"
+#include "sha1.h"
+#include <sys/time.h>
+#include <unistd.h>
#include <syslog.h>
/* LDAP library */
#include <ldap.h>
+unsigned char g_ldap_secret[DIGEST_SECRET_LEN];
+
/* -------------------------------------------------------------------------------
* Defaults and Constants
*/
-/* This needs to be the same as an MD5 hash length */
-#define LDAP_HASH_KEY_LEN 16
-#define LDAP_ESTABLISHED (void*)1
+#define BASIC_ESTABLISHED (void*)1
/* TODO: We need to support more password types */
#define LDAP_PW_CLEAR 0
@@ -57,18 +65,21 @@ typedef struct ldap_context
const char* ha1_attr; /* Password for an encrypted Digest H(A1) */
const char* user; /* User to bind as */
const char* password; /* Password to bind with */
- const char* realm; /* The realm to use in authentication */
const char* dnmap; /* For mapping users to dns */
int port; /* Port to connect to LDAP server on */
int scope; /* Scope for filter */
+
+ const char* realm; /* The realm to use in authentication */
+ const char* domains; /* Domains for which digest auth is valid */
+
int dobind; /* Bind to do simple authentication */
- int pending_max; /* Maximum number of connections at once */
- int pending_timeout; /* Timeout for authentication (in seconds) */
- int ldap_timeout; /* Timeout for LDAP operations */
+ int cache_max; /* Maximum number of connections at once */
+ int ldap_max; /* Number of open connections allowed */
+ int ldap_timeout; /* Maximum amount of time to dedicate to an ldap query */
/* Context ----------------------------------------------------------- */
- hash_t* pending; /* Pending connections */
- hash_t* established; /* Established connections */
+ hash_t* cache; /* Some cached records or basic */
+
LDAP** pool; /* Pool of available connections */
int pool_mark; /* Amount of connections allocated */
}
@@ -76,11 +87,27 @@ ldap_context_t;
/* The defaults for the context */
-static const ldap_defaults =
-{XXXXX
- NULL, NULL, "", "userPassword", NULL, NULL, NULL, "", NULL
- LDAP_SCOPE_DEFAULT, 1, DEFAULT_PENDING_MAX, DEFAULT_PENDING_TIMEOUT,
- 30, NULL, NULL, NULL
+static const ldap_context_t ldap_defaults =
+{
+ NULL, /* servers */
+ NULL, /* filter */
+ "", /* base */
+ "userPassword", /* pw_attr */
+ NULL, /* ha1_attr */
+ NULL, /* user */
+ NULL, /* password */
+ NULL, /* dnmap */
+ 389, /* port */
+ LDAP_SCOPE_DEFAULT, /* scope */
+ "", /* realm */
+ NULL, /* domains */
+ 1, /* dobind */
+ 1000, /* cache_max */
+ 10, /* ldap_max */
+ 30, /* ldap_timeout */
+ NULL, /* cache */
+ NULL, /* pool */
+ 0 /* pool_mark */
};
@@ -88,45 +115,182 @@ static const ldap_defaults =
* Internal Functions
*/
-static void make_digest_ha1(unsigned char* digest, const char* user,
- const char* realm, const char* password)
+static void free_hash_object(void* arg, void* val)
+{
+ if(val && val != BASIC_ESTABLISHED)
+ free(val);
+}
+
+static int report_ldap(const char* msg, int code, ha_response_t* resp)
+{
+ if(!msg)
+ msg = "ldap error";
+
+ ha_messagex(LOG_ERR, "%s: %s", msg, ldap_err2string(code));
+
+ switch(code)
+ {
+ case LDAP_NO_MEMORY:
+ return HA_ERROR;
+
+ default:
+ if(resp)
+ resp->code = HA_SERVER_ERROR;
+
+ return HA_FALSE;
+ };
+}
+
+static digest_record_t* get_cached_digest(ldap_context_t* ctx, unsigned char* nonce)
+{
+ digest_record_t* rec;
+
+ if(ctx->cache_max == 0)
+ return NULL;
+
+ ha_lock(NULL);
+
+ rec = (digest_record_t*)hash_get(ctx->cache, nonce);
+
+ /* Just in case it's a basic :) */
+ if(rec && rec != BASIC_ESTABLISHED)
+ hash_rem(ctx->cache, nonce);
+
+ ha_unlock(NULL);
+
+ ASSERT(!rec || memcmp(nonce, rec->nonce, DIGEST_NONCE_LEN) == 0);
+ return rec;
+}
+
+static int have_cached_basic(ldap_context_t* ctx, unsigned char* key)
+{
+ int ret = 0;
+
+ ha_lock(NULL);
+
+ ret = (hash_get(ctx->cache, key) == BASIC_ESTABLISHED);
+
+ ha_unlock(NULL);
+
+ return ret;
+}
+
+static int save_cached_digest(ldap_context_t* ctx, digest_record_t* rec)
+{
+ int r;
+
+ if(ctx->cache_max == 0)
+ return HA_FALSE;
+
+ ha_lock(NULL);
+
+ while(hash_count(ctx->cache) >= ctx->cache_max)
+ hash_bump(ctx->cache);
+
+ r = hash_set(ctx->cache, rec->nonce, rec);
+
+ ha_unlock(NULL);
+
+ if(!r)
+ {
+ ha_messagex(LOG_CRIT, "out of memory");
+ return HA_ERROR;
+ }
+
+ return HA_OK;
+}
+
+static int add_cached_basic(ldap_context_t* ctx, unsigned char* key)
+{
+ int r;
+
+ if(ctx->cache_max == 0)
+ return HA_FALSE;
+
+ ha_lock(NULL);
+
+ while(hash_count(ctx->cache) >= ctx->cache_max)
+ hash_bump(ctx->cache);
+
+ r = hash_set(ctx->cache, key, BASIC_ESTABLISHED);
+
+ ha_unlock(NULL);
+
+ if(!r)
+ {
+ ha_messagex(LOG_CRIT, "out of memory");
+ return HA_ERROR;
+ }
+
+ return HA_OK;
+}
+
+static const char* substitute_params(ldap_context_t* ctx, ha_buffer_t* buf,
+ const char* user, const char* str)
{
- struct MD5Context md5;
- MD5_Init(&md5);
- MD5_Update(&md5, user, strlen(user));
- MD5_Update(&md5, ":", 1);
- MD5_Update(&md5, realm, strlen(realm));
- MD5_Update(&md5, ":", 1);
- MD5_Update(&md5, password, strlen(pasword));
- MD5_Final(digest, &md5);
+ const char* t;
+
+ /* This starts a new block to join */
+ ha_bufcpy(buf, "");
+
+ while(str[0])
+ {
+ t = strchr(str, '%');
+ if(!t)
+ {
+ ha_bufjoin(buf);
+ ha_bufcpy(buf, str);
+ break;
+ }
+
+ ha_bufjoin(buf);
+ ha_bufncpy(buf, str, t - str);
+
+ t++;
+
+ switch(t[0])
+ {
+ case 'u':
+ ha_bufjoin(buf);
+ ha_bufcpy(buf, user);
+ t++;
+ break;
+
+ case 'r':
+ ha_bufjoin(buf);
+ ha_bufcpy(buf, ctx->realm);
+ t++;
+ break;
+ };
+
+ str = t;
+ }
+
+ return ha_bufdata(buf);
}
static const char* make_password_md5(ha_buffer_t* buf, const char* clearpw)
{
- struct MD5Context md5;
+ md5_ctx_t md5;
unsigned char digest[MD5_LEN];
- MD5_Init(&md5);
- MD5_Update(&md5, clearpw, strlen(clearpw));
- MD5_Final(digest, &md5);
+ md5_init(&md5);
+ md5_update(&md5, clearpw, strlen(clearpw));
+ md5_final(digest, &md5);
- ha_bufnext(buf);
- ha_bufenc64(buf, digest, MD5_LEN);
- return ha_bufdata(buf);
+ return ha_bufenc64(buf, digest, MD5_LEN);
}
static const char* make_password_sha(ha_buffer_t* buf, const char* clearpw)
{
- struct SHA1Context sha;
+ sha1_ctx_t sha;
unsigned char digest[SHA1_LEN];
- SHA1_Init(&sha);
- SHA1_Update(&sha, clearpw, strlen(clearpw));
- SHA1_Final(digest, &sha);
+ sha1_init(&sha);
+ sha1_update(&sha, clearpw, strlen(clearpw));
+ sha1_final(digest, &sha);
- ha_bufnext(buf);
- ha_bufenc64(buf, digest, SHA1_LEN);
- return ha_bufdata(buf);
+ return ha_bufenc64(buf, digest, SHA1_LEN);
}
static int parse_ldap_password(const char** password)
@@ -154,7 +318,7 @@ static int parse_ldap_password(const char** password)
pw++;
/* scheme should end in a brace */
- if(pw != '}')
+ if(*pw != '}')
return LDAP_PW_CLEAR;
*password = pw + 1;
@@ -162,8 +326,8 @@ static int parse_ldap_password(const char** password)
/* find a scheme in our map */
for(i = 0; i < countof(kLDAPPWTypes); i++)
{
- if(strncasecmp(kLDAPSchemes[i].name, scheme, pw - scheme))
- return kLDAPSchemes[i].type;
+ if(strncasecmp(kLDAPPWTypes[i].name, scheme, pw - scheme))
+ return kLDAPPWTypes[i].type;
}
return LDAP_PW_UNKNOWN;
@@ -171,8 +335,6 @@ static int parse_ldap_password(const char** password)
static const char* find_cleartext_password(ha_buffer_t* buf, const char** pws)
{
- ha_bufnext(buf);
-
for(; pws && *pws; pws++)
{
const char* pw = *pws;
@@ -184,28 +346,23 @@ static const char* find_cleartext_password(ha_buffer_t* buf, const char** pws)
return NULL;
}
-
static int parse_ldap_ha1(ha_buffer_t* buf, struct berval* bv, unsigned char* ha1)
{
/* Raw binary */
if(bv->bv_len == MD5_LEN)
{
- memcpy(ha1, bv->bv_len, MD5_LEN);
+ memcpy(ha1, bv->bv_val, MD5_LEN);
return HA_OK;
}
/* Hex encoded */
else if(bv->bv_len == (MD5_LEN * 2))
{
- ha_bufnext(buf);
- ha_bufdechex(buf, bv->bv_val, MD5_LEN * 2);
+ void* d = ha_bufdechex(buf, bv->bv_val, MD5_LEN);
- if(!ha_bufdata(buf))
- return HA_ERROR;
-
- if(ha_buflen(buf) == MD5_LEN)
+ if(d)
{
- memcpy(rec->ha1, ha_bufdata(buf), MD5_LEN);
+ memcpy(ha1, d, MD5_LEN);
return HA_OK;
}
}
@@ -213,26 +370,22 @@ static int parse_ldap_ha1(ha_buffer_t* buf, struct berval* bv, unsigned char* ha
/* B64 Encoded */
else
{
- ha_bufnext(buf);
- ha_bufdec64(buf, (*pws)->bv_val, (*pws)->bv_len);
+ void* d = ha_bufdec64(buf, bv->bv_val, MD5_LEN);
- if(!ha_bufdata(buf))
- return HA_ERROR;
-
- if(ha_buflen(buf) == MD5_LEN)
+ if(d)
{
- memcpy(rec->ha1, ha_bufdata(buf), MD5_LEN);
+ memcpy(ha1, ha_bufdata(buf), MD5_LEN);
return HA_OK;
}
}
- return HA_FALSE;
+ return ha_buferr(buf) ? HA_ERROR : HA_FALSE;
}
static int validate_ldap_password(ldap_context_t* ctx, LDAP* ld, LDAPMessage* entry,
ha_buffer_t* buf, const char* user, const char* clearpw)
{
- const char** pws;
+ char** pws;
const char* pw;
const char* p;
int type;
@@ -295,7 +448,7 @@ static int validate_ldap_password(ldap_context_t* ctx, LDAP* ld, LDAPMessage* en
}
}
- ldap_free_values(pws);
+ ldap_value_free(pws);
}
if(res == HA_FALSE && unknown)
@@ -320,11 +473,11 @@ static int validate_ldap_ha1(ldap_context_t* ctx, LDAP* ld, LDAPMessage* entry,
if(ha1s)
{
- make_digest_ha1(key, user, ctx->realm, clearpw);
+ digest_makeha1(key, user, ctx->realm, clearpw);
for( ; *ha1s; ha1s++)
{
- r = parse_ldap_h1(buf, *ha1s, k);
+ r = parse_ldap_ha1(buf, *ha1s, k);
if(r == HA_ERROR)
{
res = r;
@@ -334,7 +487,7 @@ static int validate_ldap_ha1(ldap_context_t* ctx, LDAP* ld, LDAPMessage* entry,
if(r == HA_FALSE)
{
if(first)
- ha_messagex(LOG_ERROR, "LDAP contains invalid HA1 digest hash for user: %s", user);
+ ha_messagex(LOG_ERR, "LDAP contains invalid HA1 digest hash for user: %s", user);
first = 0;
continue;
@@ -347,7 +500,7 @@ static int validate_ldap_ha1(ldap_context_t* ctx, LDAP* ld, LDAPMessage* entry,
}
}
- ldap_free_values_len(ha1s);
+ ldap_value_free_len(ha1s);
}
return res;
@@ -358,7 +511,7 @@ static LDAP* get_ldap_connection(ldap_context_t* ctx)
LDAP* ld;
int i, r;
- for(i = 0; i < ctx->pending_max; i++)
+ for(i = 0; i < ctx->ldap_max; i++)
{
/* An open connection in the pool */
if(ctx->pool[i])
@@ -369,16 +522,16 @@ static LDAP* get_ldap_connection(ldap_context_t* ctx)
}
}
- if(ctx->pool_mark >= ctx->pending_max)
+ if(ctx->pool_mark >= ctx->ldap_max)
{
- ha_messagex("too many open connections to LDAP");
+ ha_messagex(LOG_ERR, "too many open connections to LDAP");
return NULL;
}
ld = ldap_init(ctx->servers, ctx->port);
if(!ld)
{
- ha_message("couldn't initialize ldap connection");
+ ha_message(LOG_ERR, "couldn't initialize ldap connection");
return NULL;
}
@@ -388,10 +541,12 @@ static LDAP* get_ldap_connection(ldap_context_t* ctx)
ctx->password ? ctx->password : "");
if(r != LDAP_SUCCESS)
{
- report_ldap(r, NULL);
+ report_ldap("couldn't bind to LDAP server", r, NULL);
ldap_unbind_s(ld);
return NULL;
}
+
+ ctx->pool_mark++;
}
return ld;
@@ -399,13 +554,15 @@ static LDAP* get_ldap_connection(ldap_context_t* ctx)
static void save_ldap_connection(ldap_context_t* ctx, LDAP* ld)
{
- int i;
+ int i, e;
if(!ld)
return;
+ ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &e);
+
/* Make sure it's worth saving */
- switch(ld_errno(ld))
+ switch(e)
{
case LDAP_SERVER_DOWN:
case LDAP_LOCAL_ERROR:
@@ -413,7 +570,7 @@ static void save_ldap_connection(ldap_context_t* ctx, LDAP* ld)
break;
default:
- for(i = 0; i < ctx->pending_max; i++)
+ for(i = 0; i < ctx->ldap_max; i++)
{
/* An open connection in the pool */
if(!ctx->pool[i])
@@ -428,25 +585,88 @@ static void save_ldap_connection(ldap_context_t* ctx, LDAP* ld)
};
if(ld != NULL)
+ {
ldap_unbind_s(ld);
+ ctx->pool_mark--;
+ }
+}
+
+static int retrieve_user_entry(ldap_context_t* ctx, ha_buffer_t* buf, LDAP* ld,
+ const char* user, const char** dn,
+ LDAPMessage** entry, LDAPMessage** result)
+{
+ struct timeval tv;
+ const char* filter;
+ const char* attrs[3];
+ int r;
+
+ if(ctx->filter)
+ {
+ /* Filters can also have %u and %r */
+ filter = substitute_params(ctx, buf, user, ctx->filter);
+ if(!filter)
+ return HA_ERROR;
+ }
+ else
+ {
+ filter = "(objectClass=*)";
+ }
+
+ attrs[0] = ctx->dobind ? NULL : ctx->pw_attr;
+ attrs[1] = ctx->dobind ? NULL : ctx->ha1_attr;
+ attrs[2] = NULL;
+
+ tv.tv_sec = ctx->ldap_timeout;
+ tv.tv_usec = 0;
+
+ r = ldap_search_st(ld, *dn ? *dn : ctx->base,
+ *dn ? LDAP_SCOPE_BASE : ctx->scope,
+ filter, (char**)attrs, 0, &tv, result);
+
+ if(r != LDAP_SUCCESS)
+ return report_ldap("couldn't search LDAP server", r, NULL);
+
+
+ /* Only one result should exist */
+ switch(r = ldap_count_entries(ld, *result))
+ {
+ case 1:
+ *entry = ldap_first_entry(ld, *result);
+ if(!(*dn))
+ *dn = ldap_get_dn(ld, *entry);
+ return HA_OK;
+
+ case 0:
+ ha_messagex(LOG_WARNING, "user not found in LDAP: %s", user);
+ break;
+
+ default:
+ ha_messagex(LOG_WARNING, "more than one user found for filter: %s", filter);
+ break;
+ };
+
+ ldap_msgfree(*result);
+ return HA_FALSE;
}
-static int complete_digest_ha1(ldap_context_t* ctx, ha_digest_rec_t* rec,
- const char* user)
+static int complete_digest_ha1(ldap_context_t* ctx, digest_record_t* rec,
+ ha_buffer_t* buf, const char* user, int* code)
{
LDAP* ld = NULL; /* freed in finally */
LDAPMessage* results = NULL; /* freed in finally */
LDAPMessage* entry = NULL; /* no need to free */
- struct berval** pws; /* freed manually */
+ struct berval** ha1s; /* freed manually */
+ char** pws;
int ret = HA_FALSE;
-
- /* Hash in the user name */
- ha_md5string(user, rec->userhash);
-
+ const char* dn;
+ int r;
ld = get_ldap_connection(ctx);
if(!ld)
+ {
+ *code = HA_SERVER_ERROR;
goto finally;
+ }
/*
* Discover the DN of the user. If there's a DN map string
@@ -465,7 +685,7 @@ static int complete_digest_ha1(ldap_context_t* ctx, ha_digest_rec_t* rec,
}
/* Okay now we contact the LDAP server. */
- r = retrieve_user_entry(ctx, buf, user, &dn, &entry, &results);
+ r = retrieve_user_entry(ctx, buf, ld, user, &dn, &entry, &results);
if(r != HA_OK)
{
ret = r;
@@ -474,45 +694,45 @@ static int complete_digest_ha1(ldap_context_t* ctx, ha_digest_rec_t* rec,
/* Figure out the users ha1 */
if(ctx->ha1_attr)
- pws = ldap_get_values_len(ld, entry, ctx->ha1_attr);
+ ha1s = ldap_get_values_len(ld, entry, ctx->ha1_attr);
- if(pws)
+ if(ha1s)
{
- if(*pws)
+ if(*ha1s)
{
- r = parse_ldap_ha1(buf, *pws, rec->ha1);
+ r = parse_ldap_ha1(buf, *ha1s, rec->ha1);
if(r != HA_OK)
{
- ret = r
+ ret = r;
if(ret != HA_FALSE)
- ha_messagex(LOG_ERROR, "LDAP contains invalid HA1 digest hash for user: %s", user);
+ ha_messagex(LOG_ERR, "LDAP contains invalid HA1 digest hash for user: %s", user);
}
}
- ldap_free_values_len(pws);
+ ldap_value_free_len(ha1s);
goto finally;
}
/* If no ha1 set or none found, use password and make a HA1 */
- pws = ldap_get_values_len(ld, entry, ctx->pw_attr);
+ pws = ldap_get_values(ld, entry, ctx->pw_attr);
if(pws)
{
/* Find a cleartext password */
- const char* t = find_cleartext_password(buf, pws);
+ const char* t = find_cleartext_password(buf, (const char**)pws);
- ldap_free_values_len(pws);
+ ldap_value_free(pws);
if(t)
{
- make_digest_ha1(rec->ha1, user, ctx->realm, t);
+ digest_makeha1(rec->ha1, user, ctx->realm, t);
ret = HA_OK;
goto finally;
}
}
- ha_messagex(LOG_ERROR, "LDAP contains no cleartext password for user: %s", user);
+ ha_messagex(LOG_ERR, "LDAP contains no cleartext password for user: %s", user);
finally:
@@ -525,67 +745,10 @@ finally:
return ret;
}
-static int retrieve_user_entry(ldap_context_t* ctx, buffer_t* buf, LDAP* ld,
- const char* user, const char** dn,
- LDAPMessage** entry, LDAPMessage** result)
-{
- timeval tv;
- const char* filter;
- const char* attrs[3];
-
- if(ctx->filter)
- {
- /* Filters can also have %u and %r */
- filter = substitute_params(ctx, buf, user, ctx->filter);
- if(!filter)
- return HA_ERROR;
- }
- else
- {
- filter = "(objectClass=*)";
- }
-
- attrs[0] = ctx->dobind ? NULL : ctx->pw_attr;
- attrs[1] = ctx->dobind ? NULL : ctx->ha1_attr;
- attrs[2] = NULL;
-
- tv.tv_sec = ctx->ldap_timeout;
- tv.tv_usec = 0;
-
- r = ldap_search_st(ld, *dn ? *dn : ctx->base,
- *dn ? LDAP_SCOPE_BASE : ctx->scope,
- filter, attrs, 0, &tv, result);
-
- if(r != LDAP_SUCCESS)
- return report_ldap(r, resp, &ret);
-
-
- /* Only one result should exist */
- switch(r = ldap_count_entries(ld, *result))
- {
- case 1:
- *entry = ldap_first_entry(ld, *result);
- if(!(*dn))
- *dn = ldap_get_dn(ld, entry);
- return HA_OK;
-
- case 0:
- ha_messagex(LOG_WARNING, "user not found in LDAP: %s", basic.user);
- break;
-
- default:
- ha_messagex(LOG_WARNING, "more than one user found for filter: %s", filter);
- break;
- };
-
- ldap_msg_free(*result);
- return HA_FALSE;
-}
-
static int basic_ldap_response(ldap_context_t* ctx, const char* header,
ha_response_t* resp, ha_buffer_t* buf)
{
- ha_basic_header_t basic;
+ basic_header_t basic;
LDAP* ld = NULL;
LDAPMessage* entry = NULL;
LDAPMessage* results = NULL;
@@ -596,23 +759,18 @@ static int basic_ldap_response(ldap_context_t* ctx, const char* header,
ASSERT(buf && header && resp && buf);
- if(ha_parsebasic(header, buf, &basic) == HA_ERROR)
+ if(basic_parse(header, buf, &basic) == HA_ERROR)
return HA_ERROR;
/* Past this point we don't return directly */
/* Check and see if this connection is in the cache */
- ha_lock(NULL);
-
- if(hash_get(ctx->established, key) == BASIC_ESTABLISHED)
- {
- found = 1;
- ret = HA_OK;
- goto finally:
- }
-
- ha_unlock(NULL);
-
+ if(have_cached_basic(ctx, basic.key))
+ {
+ found = 1;
+ ret = HA_OK;
+ goto finally;
+ }
/* If we have a user name and password */
if(!basic.user || !basic.user[0] ||
@@ -620,7 +778,7 @@ static int basic_ldap_response(ldap_context_t* ctx, const char* header,
goto finally;
- ld = get_ldap_connection();
+ ld = get_ldap_connection(ctx);
if(!ld)
{
resp->code = HA_SERVER_ERROR;
@@ -662,7 +820,7 @@ static int basic_ldap_response(ldap_context_t* ctx, const char* header,
if(!ctx->dobind || !dn || ctx->filter)
{
- r = retrieve_user_entry(ctx, buf, basic.user, &dn, &entry, &results);
+ r = retrieve_user_entry(ctx, buf, ld, basic.user, &dn, &entry, &results);
if(r != HA_OK)
{
ret = r;
@@ -682,7 +840,7 @@ static int basic_ldap_response(ldap_context_t* ctx, const char* header,
if(r == LDAP_INVALID_CREDENTIALS)
ha_messagex(LOG_WARNING, "invalid login for: %s", basic.user);
else
- report_ldap(r, resp, &ret);
+ report_ldap("couldn't bind to LDAP server", r, resp);
goto finally;
}
@@ -717,80 +875,90 @@ finally:
if(resp->code == HA_SERVER_ACCEPT)
{
- resp->details = basic.user;
+ resp->detail = basic.user;
/* We put this connection into the successful connections */
- if(!hash_set(ctx->established, basic.key, LDAP_ESTABLISHED))
- {
- ha_messagex(LOG_CRIT, "out of memory");
- return HA_ERROR;
- }
+ ret = add_cached_basic(ctx, basic.key);
}
return ret;
}
+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* header;
+
+ /* Generate an nonce */
+ digest_makenonce(nonce, g_ldap_secret, NULL);
+
+ /* Now generate a message to send */
+ header = digest_challenge(buf, nonce, ctx->realm, ctx->domains, stale);
+
+ if(!header)
+ return HA_ERROR;
+
+ /* And append it nicely */
+ resp->code = HA_SERVER_DECLINE;
+ ha_addheader(resp, "WWW-Authenticate", header);
+
+ return HA_OK;
+}
static int digest_ldap_response(ldap_context_t* ctx, const char* header,
- const char* method, const char* uri,
- ha_response_t* resp, ha_buffer_t* buf)
+ const char* method, const char* uri, int timeout,
+ ha_response_t* resp, ha_buffer_t* buf)
{
- ha_digest_header_t dg;
- digest_rec_t* rec = NULL;
+ unsigned char nonce[DIGEST_NONCE_LEN];
+ digest_header_t dg;
+ digest_record_t* rec = NULL;
+ const char* t;
+ time_t expiry;
int ret = HA_FALSE;
int stale = 0;
- int pending = 0;
+ int r;
/* We use this below to send a default response */
resp->code = -1;
- if(ha_parsedigest(header, buf, &rec) == HA_ERROR)
+ if(digest_parse(header, buf, &dg, nonce) == HA_ERROR)
return HA_ERROR;
- /* Lookup our digest context based on the nonce */
- if(!dg.nonce || strlen(dg.nonce) != DIGEST_NONCE_LEN)
+ r = digest_checknonce(nonce, g_ldap_secret, &expiry);
+ if(r != HA_OK)
{
- ha_messagex(LOG_WARNING, "digest response contains invalid nonce");
+ if(r == HA_FALSE)
+ ha_messagex(LOG_WARNING, "digest response contains invalid nonce");
+
+ ret = r;
goto finally;
}
- ha_lock(NULL);
-
- rec = (digest_rec_t*)hash_get(ctx->pending, dg.nonce)
- if(rec)
- {
- pending = 1;
- hash_rem(ctx->pending, dg.nonce);
- }
-
- else
- {
- rec = (digest_rec_t*)hash_get(ctx->established, dg.nonce);
- }
+ rec = get_cached_digest(ctx, nonce);
- ha_unlock(NULL);
-
- /*
- * If nothing was found for this nonce, then it might
- * be a stale nonce. In any case prompt the client
- * to reauthenticate.
- */
- if(!rec)
+ /* Check to see if we're stale */
+ if((expiry + timeout) <= time(NULL))
{
stale = 1;
goto finally;
}
- /*
- * If we got a response from the pending table, then
- * we need to lookup the user name and figure out
- * who the dude is.
- */
- if(pending)
+ if(!rec)
{
- ASSERT(rec);
+ /*
+ * If we're valid but don't have a record in the
+ * cache then complete the record properly.
+ */
+
+ rec = digest_makerec(nonce, dg.username);
+ if(!rec)
+ {
+ ret = HA_ERROR;
+ goto finally;
+ }
- r = complete_digest_ha1(ctx, rec, dg->username);
+ r = complete_digest_ha1(ctx, rec, buf, dg.username, &(resp->code));
if(r != HA_OK)
{
ret = r;
@@ -801,28 +969,35 @@ static int digest_ldap_response(ldap_context_t* ctx, const char* header,
/* Increment our nonce count */
rec->nc++;
- ret = ha_digestcheck(ctx->realm, method, uri, buf, &dg, rec);
+ ret = digest_check(ctx->realm, method, uri, buf, &dg, rec);
if(ret == HA_OK)
{
resp->code = HA_SERVER_ACCEPT;
- resp->details = dg->username;
+ resp->detail = dg.username;
- /* Put the connection back into established */
+ /* Figure out if we need a new nonce */
+ if((expiry + (timeout - (timeout / 8))) < time(NULL))
+ {
+ digest_makenonce(nonce, g_ldap_secret, NULL);
+ stale = 1;
+ }
- ha_lock(NULL);
+ t = digest_respond(buf, &dg, rec, stale ? nonce : NULL);
+ if(!t)
+ {
+ ret = HA_ERROR;
+ goto finally;
+ }
- if(hash_set(ctx->established, dg.nonce, rec))
- {
- rec = NULL;
- }
- else
- {
- ha_messagex(LOG_CRIT, "out of memory");
- ret = HA_ERROR;
- }
+ if(t[0])
+ ha_addheader(resp, "Authentication-Info", t);
- ha_unlock(NULL);
+ /* Put the connection into the cache */
+ if(save_cached_digest(ctx, rec) == HA_ERROR)
+ ret = HA_ERROR;
+ else
+ rec = NULL;
}
finally:
@@ -847,7 +1022,7 @@ finally:
int ldap_config(ha_context_t* context, const char* name, const char* value)
{
- ldap_context_t* ctx = (ldap_context_t*)(context.data);
+ ldap_context_t* ctx = (ldap_context_t*)(context->data);
if(strcmp(name, "ldapservers") == 0)
{
@@ -903,6 +1078,12 @@ int ldap_config(ha_context_t* context, const char* name, const char* value)
return HA_OK;
}
+ else if(strcmp(name, "digestdomains") == 0)
+ {
+ ctx->domains = value;
+ return HA_OK;
+ }
+
else if(strcmp(name, "ldapscope") == 0)
{
if(strcmp(value, "sub") == 0 || strcmp(value, "subtree") == 0)
@@ -914,7 +1095,7 @@ int ldap_config(ha_context_t* context, const char* name, const char* value)
else
{
- messagex(LOG_ERR, "invalid value for '%s' (must be 'sub', 'base' or 'one')", name);
+ ha_messagex(LOG_ERR, "invalid value for '%s' (must be 'sub', 'base' or 'one')", name);
return HA_ERROR;
}
@@ -926,72 +1107,75 @@ int ldap_config(ha_context_t* context, const char* name, const char* value)
return ha_confbool(name, value, &(ctx->dobind));
}
- else if(strcmp(name, "pendingmax") == 0)
+ else if(strcmp(name, "ldapmax") == 0)
{
- return ha_confint(name, value, 1, 256, &(ctx->pending_max));
+ return ha_confint(name, value, 1, 256, &(ctx->ldap_max));
}
- else if(strcmp(name, "pendingtimeout") == 0)
+ else if(strcmp(name, "ldaptimeout") == 0)
{
- return ha_confint(name, value, 0, 86400, &(ctx->pending_timeout));
+ return ha_confint(name, value, 0, 86400, &(ctx->ldap_timeout));
}
- else if(strcmp(name, "ldaptimeout") == 0)
+ else if(strcmp(name, "cachemax") == 0)
{
- return ha_confint(name, value, 0, 86400, &(ctx->ldap_timeout));
+ return ha_confint(name, value, 0, 0x7FFFFFFF, &(ctx->cache_max));
}
return HA_FALSE;
}
-int ldap_initialize(ha_context_t* context)
+int ldap_inithand(ha_context_t* context)
{
- /* No global initialization */
+ /* Global initialization */
if(!context)
- return HA_OK;
-
- ldap_context_t* ctx = (ldap_context_t*)(context.data);
-
-
- /* Make sure there are some types of authentication we can do */
- if(!(context->types & (HA_TYPE_BASIC | HA_TYPE_DIGEST)))
{
- ha_messagex(LOG_ERR, "Digest module configured, but does not implement any "
- "configured authentication type.");
- return HA_ERROR;
+ return ha_genrandom(g_ldap_secret, DIGEST_SECRET_LEN);
}
- /* Check for mandatory configuration */
- if(!ctx->servers || (!ctx->dnmap || !ctx->filter))
+
+ /* Context specific initialization */
+ else
{
- ha_messagex(LOG_ERR, "Digest LDAP configuration incomplete. "
- "Must have LDAPServers and either LDAPFilter or LDAPDNMap.");
- return HA_ERROR;
- }
+ ldap_context_t* ctx = (ldap_context_t*)(context->data);
+ /* Make sure there are some types of authentication we can do */
+ if(!(context->types & (HA_TYPE_BASIC | HA_TYPE_DIGEST)))
+ {
+ ha_messagex(LOG_ERR, "LDAP module configured, but does not implement any "
+ "configured authentication type.");
+ return HA_ERROR;
+ }
- /* The hash tables */
- if(!(ctx->pending = hash_create(LDAP_HASH_KEY_LEN)) ||
- !(ctx->established = hash_create(LDAP_HASH_KEY_LEN)))
- {
- ha_messagex(LOG_CRIT, "out of memory");
- return HA_ERROR;
- }
+ /* Check for mandatory configuration */
+ if(!ctx->servers || (!ctx->dnmap || !ctx->filter))
+ {
+ ha_messagex(LOG_ERR, "Digest LDAP configuration incomplete. "
+ "Must have LDAPServers and either LDAPFilter or LDAPDNMap.");
+ return HA_ERROR;
+ }
- /*
- * Our connection pool. It's the size of our maximum
- * amount of pending connections as that's the max
- * we'd be able to use at a time anyway.
- */
- XXXXX
- ctx->pool = (LDAP**)malloc(sizeof(LDAP*) * ctx->pending_max);
- if(!ctx->pool)
- {
- ha_messagex(LOG_CRIT, "out of memory");
- return HA_ERROR;
- }
+ /* The cache for digest records and basic */
+ if(!(ctx->cache = hash_create(MD5_LEN, free_hash_object, NULL)))
+ {
+ ha_messagex(LOG_CRIT, "out of memory");
+ return HA_ERROR;
+ }
- memset(ctx->pool, 0, sizeof(LDAP*) * ctx->pending_max);
+ /*
+ * Our connection pool. It's the size of our maximum
+ * amount of pending connections as that's the max
+ * we'd be able to use at a time anyway.
+ */
+ ctx->pool = (LDAP**)malloc(sizeof(LDAP*) * ctx->ldap_max);
+ if(!ctx->pool)
+ {
+ ha_messagex(LOG_CRIT, "out of memory");
+ return HA_ERROR;
+ }
+
+ memset(ctx->pool, 0, sizeof(LDAP*) * ctx->ldap_max);
+ }
return HA_OK;
}
@@ -1001,17 +1185,15 @@ void ldap_destroy(ha_context_t* context)
int i;
if(!context)
- return HA_OK;
+ return;
- ldap_context_t* ctx = (digest_ldap_context_t*)(context.data);
+ ldap_context_t* ctx = (ldap_context_t*)(context->data);
/* Note: We don't need to be thread safe here anymore */
- hash_free(ctx->pending);
- hash_free(ctx->established);
+ hash_free(ctx->cache);
- XXXXX
/* Close any connections we have open */
- for(i = 0; i < ctx->pending_max; i++)
+ for(i = 0; i < ctx->ldap_max; i++)
{
if(ctx->pool[i])
ldap_unbind_s(ctx->pool[i]);
@@ -1032,14 +1214,8 @@ int ldap_process(ha_context_t* context, ha_request_t* req,
ha_lock(NULL);
- XXXXXX
- /*
- * Purge out stale connection stuff. This includes
- * authenticated connections which have expired as
- * well as half open connections which expire.
- */
- hash_purge(ctx->pending, t - ctx->pending_timeout);
- hash_purge(ctx->established, t - ctx->timeout);
+ /* Purge out stale connection stuff. */
+ hash_purge(ctx->cache, t - context->timeout);
ha_unlock(NULL);
@@ -1049,19 +1225,21 @@ int ldap_process(ha_context_t* context, ha_request_t* req,
/* Check the headers and see if we got a response thingy */
- if(ctx->types & HA_TYPE_DIGEST)
+ if(context->types & HA_TYPE_DIGEST)
{
header = ha_getheader(req, "Authorization", HA_PREFIX_DIGEST);
if(header)
{
- ret = digest_ldap_response(ctx, header, resp, buf);
+ ret = digest_ldap_response(ctx, header, req->args[AUTH_ARG_METHOD],
+ req->args[AUTH_ARG_URI], context->timeout,
+ resp, buf);
if(ret == HA_ERROR)
return ret;
}
}
/* Or a basic authentication */
- if(!header && ctx->types & HA_TYPE_BASIC)
+ if(!header && context->types & HA_TYPE_BASIC)
{
header = ha_getheader(req, "Authorization", HA_PREFIX_BASIC);
if(header)
@@ -1078,17 +1256,19 @@ int ldap_process(ha_context_t* context, ha_request_t* req,
{
resp->code = HA_SERVER_DECLINE;
- if(ctx->types & HA_TYPE_DIGEST)
+ if(context->types & HA_TYPE_DIGEST)
{
ret = digest_ldap_challenge(ctx, resp, buf, 0);
if(ret == HA_ERROR)
return ret;
}
- if(ctx->types & HA_TYPE_BASIC)
+ if(context->types & HA_TYPE_BASIC)
{
- ha_bufnext(buf);
- ha_bufcat(buf, "BASIC realm=\"", ctx->realm , "\"", NULL);
+ ha_bufmcat(buf, "BASIC realm=\"", ctx->realm , "\"", NULL);
+
+ if(ha_buferr(buf))
+ return HA_ERROR;
ha_addheader(resp, "WWW-Authenticate", ha_bufdata(buf));
}
@@ -1102,10 +1282,10 @@ int ldap_process(ha_context_t* context, ha_request_t* req,
* Handler Definition
*/
-ha_handler_t digest_ldap_handler =
+ha_handler_t ldap_handler =
{
"LDAP", /* The type */
- ldap_initialize, /* Initialization function */
+ ldap_inithand, /* Initialization function */
ldap_destroy, /* Uninitialization routine */
ldap_config, /* Config routine */
ldap_process, /* Processing routine */
diff --git a/daemon/misc.c b/daemon/misc.c
index 7091bfa..b585d8b 100644
--- a/daemon/misc.c
+++ b/daemon/misc.c
@@ -5,6 +5,8 @@
#include <syslog.h>
#include <err.h>
+#include <stdio.h>
+#include <fcntl.h>
extern int g_debugging;
extern int g_daemonized;
@@ -98,9 +100,9 @@ const char* ha_getheader(ha_request_t* req, const char* name, const char* prefix
if(!prefix)
return req->headers[i].data;
- l = strlen(prefix)
+ l = strlen(prefix);
- if(strncasecmp(prefix, req->headers[i].value, l) == 0)
+ if(strncasecmp(prefix, req->headers[i].data, l) == 0)
return req->headers[i].data + l;
}
}
@@ -185,7 +187,7 @@ int ha_confbool(const char* name, const char* conf, int* value)
int ha_confint(const char* name, const char* conf, int min, int max, int* value)
{
- const char* p;
+ char* p;
errno = 0;
*value = strtol(conf, &p, 10);
@@ -201,421 +203,360 @@ int ha_confint(const char* name, const char* conf, int min, int max, int* value)
/* -----------------------------------------------------------------------
- * Hash Stuff
- */
-
-void ha_md5string(const char* data, unsigned char* hash)
-{
- struct MD5Context md5;
- MD5Init(&md5);
- MD5Update(&md5, data, strlen(data));
- MD5Final(hash, &md5);
-}
-
-
-/* -----------------------------------------------------------------------
* Client Authentication Functions
*/
-int ha_parsebasic(char* header, ha_buffer_t* buf, ha_basic_header_t* rec)
+char* ha_uriformat(ha_buffer_t* buf, const ha_uri_t* uri)
{
- char* t;
-
- memset(rec, 0, sizeof(*rec));
+ /* This creates a new block */
+ ha_bufcpy(buf, "");
- /* Trim the white space */
- while(*header && isspace(*header))
- header++;
-
- /*
- * Authorization header is in this format:
- *
- * "Basic " B64(user ":" password)
- */
- ha_bufnext(buf);
- ha_bufdec64(buf, header);
+ if(uri->host)
+ {
+ const char* l = "";
+ const char* r = "";
- header = ha_bufdata(buf);
+ ha_bufmcat(buf, uri->scheme ? uri->scheme : "http",
+ "://", NULL);
- if(!header)
- return HA_ERROR;
+ if(uri->user)
+ {
+ ha_bufjoin(buf);
+ ha_bufmcat(buf, uri->user,
+ uri->pw ? ":" : "",
+ uri->pw ? uri->pw : "",
+ "@", NULL);
+ }
+ if(strchr(uri->host, ':')) /* v6 ip */
+ {
+ l = "[";
+ r = "]";
+ }
- /* We have a cache key at this point so hash it */
- ha_md5string(header, rec->key);
+ ha_bufjoin(buf);
+ ha_bufmcat(buf, l, uri->host, r, NULL);
+ }
+ if(uri->path)
+ {
+ ha_bufjoin(buf);
+ ha_bufmcat(buf, "/", uri->path);
+ }
- /* Parse the user. We need it in any case */
- t = strchr(header, ':');
- if(t != NULL)
+ if(uri->query)
{
- /* Break the string in half */
- *t = 0;
+ ha_bufjoin(buf);
+ ha_bufmcat(buf, "?", uri->query);
+ }
- rec->user = header;
- rec->password = t + 1;
+ if(uri->fragment)
+ {
+ ha_bufjoin(buf);
+ ha_bufmcat(buf, "#", uri->fragment);
}
- return HA_OK;
+ return ha_bufdata(buf);
}
+int ha_uriparse(ha_buffer_t* buf, const char* suri, ha_uri_t* uri)
+{
+ char* s;
+ char* s1;
+ char* hostinfo;
+ char* endstr;
+ char* str;
+ int port;
+ int v6_offset1 = 0;
+
+ /* Copy the memory */
+ str = ha_bufcpy(buf, suri);
+
+ if(!str)
+ return HA_ERROR;
-/*
- * Copyright 1999-2004 The Apache Software Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+ /* Initialize the structure */
+ memset(uri, 0, sizeof(*uri));
-int ha_parsedigest(char* header, ha_buffer_t* buf, ha_digest_header_t* rec)
-{
/*
- * This function destroys the contents of header by
- * terminating strings in it all over the place.
+ * We assume the processor has a branch predictor like most --
+ * it assumes forward branches are untaken and backwards are taken. That's
+ * the reason for the gotos. -djg
*/
- char next;
- char* key;
- char* value;
-
- ha_bufnext(buf);
- ha_bufcat(buf, header, NULL);
-
- header = ha_bufdata(buf);
-
- if(!header)
- return HA_ERROR;
-
- memset(rec, 0, sizeof(rec));
-
- while(header[0])
+ if(str[0] == '/')
{
- /* find key */
-
- while(header[0] && isspace(header[0]))
- header++;
-
- key = header;
-
- while(header[0] && header[0] != '=' && header[0] != ',' &&
- !isspace(header[0]))
- header++;
+deal_with_path:
+ *str == 0;
+ ++str;
- /* null terminate and move on */
- next = header[0];
- header[0] = '\0';
- header++;
-
- if(!next)
- break;
-
- if(isspace(t))
- {
- while(header[0] && isspace(header[0]))
- header++;
+ /*
+ * we expect uri to point to first character of path ... remember
+ * that the path could be empty -- http://foobar?query for example
+ */
+ s = str;
+ while(*s && *s != '?' && *s != '#')
+ ++s;
- next = header[0];
- }
+ if(s != str)
+ uri->path = str;
- /* find value */
+ if(*s == 0)
+ return HA_OK;
- if(next == '=')
+ if(*s == '?')
{
- header++;
-
- while(header[0] && isspace(header[0]))
- header++;
+ *s = 0;
+ ++s;
+ uri->query = s;
- vv = 0;
- if(header[0] == '\"') /* quoted string */
+ s1 = strchr(s, '#');
+ if(s1)
{
- header++;
- value = header;
-
- while(header[0] && header[0] != '\"')
- header++;
-
- header[0] = 0;
- header++;
+ *s1 = 0;
+ ++s1;
+ uri->fragment = s1;
}
- else /* token */
- {
- value = header;
-
- while(header[0] && header[0] != ',' && !isspace(header[0]))
- header++;
+ return HA_OK;
+ }
- header[0] = 0;
- header++;
- }
+ *s = 0;
+ ++s;
- while(header[0] && header[0] != ',')
- header++;
-
- if(header[0])
- header++;
-
- if(!strcasecmp(key, "username"))
- rec->username = value;
- else if(!strcasecmp(key, "realm"))
- rec->realm = value;
- else if (!strcasecmp(key, "nonce"))
- rec->nonce = value;
- else if (!strcasecmp(key, "uri"))
- rec->uri = value;
- else if (!strcasecmp(key, "response"))
- rec->digest = value;
- else if (!strcasecmp(key, "algorithm"))
- rec->algorithm = value;
- else if (!strcasecmp(key, "cnonce"))
- rec->cnonce = value;
- else if (!strcasecmp(key, "opaque"))
- rec->opaque = value;
- else if (!strcasecmp(key, "qop"))
- rec->qop = value;
- else if (!strcasecmp(key, "nc"))
- rec->nc = value;
- }
+ /* otherwise it's a fragment */
+ uri->fragment = s;
+ return HA_OK;
}
- if(rec->nonce)
- ha_md5string(rec->nonce, rec->key);
+ /* find the scheme: */
+ s = str;
- return HA_OK;
-}
+ while(*s && *s != ':' && *s != '/' && *s != '?' && *s != '#')
+ s++;
-int ha_digestcheck(const char* realm, const char* method, const char* uri,
- ha_buffer_t* buf, ha_digest_header_t* dg, ha_digest_record_t* rec)
-{
- unsigned char hash[MD5_LEN];
- struct MD5Context md5;
- const char* digest;
- const char* t;
+ /* scheme must be non-empty and followed by :// */
+ if(s == str || s[0] != ':' || s[1] != '/' || s[2] != '/')
+ goto deal_with_path; /* backwards predicted taken! */
- /* Check for digest */
- if(!dg->digest || !dg->digest[0])
- {
- ha_messagex(LOG_WARNING, "digest response missing digest");
- return HA_FALSE;
- }
+ uri->scheme = str;
+ *s = 0;
+ s += 3;
- /* Username */
- if(!dg->username || !dg->username[0] ||
- ha_md5strcmp(dg->username, rec->userhash) != 0)
- {
- ha_messagex(LOG_WARNING, "digest response missing username");
- return HA_FALSE;
- }
+ hostinfo = s;
+
+ while(*s && *s != '/' && *s != '?' && *s != '#')
+ ++s;
- /* The realm */
- if(!dg->realm || strcmp(dg->realm, realm) != 0)
+ str = s; /* whatever follows hostinfo is start of uri */
+
+ /*
+ * If there's a username:password@host:port, the @ we want is the last @...
+ * too bad there's no memrchr()... For the C purists, note that hostinfo
+ * is definately not the first character of the original uri so therefore
+ * &hostinfo[-1] < &hostinfo[0] ... and this loop is valid C.
+ */
+ do
{
- ha_messagex(LOG_WARNING, "digest response contains invalid realm: '%s'",
- dg->realm ? dg->realm : "");
- return HA_FALSE;
+ --s;
}
+ while(s >= hostinfo && *s != '@');
- /* Components in the new RFC */
- if(dg->qop)
+ if(s < hostinfo)
{
+ /* again we want the common case to be fall through */
+deal_with_host:
/*
- * We only support 'auth' qop. We don't have access to the data
- * and wouldn't be able to support anything else.
+ * We expect hostinfo to point to the first character of
+ * the hostname. If there's a port it is the first colon,
+ * except with IPv6.
*/
- if(strcmp(dg->qop, "auth") != 0)
- {
- ha_messagex(LOG_WARNING, "digest response contains unknown or unsupported qop: '%s'",
- dg->qop ? dg->qop : "");
- return HA_FALSE;
- }
-
- /* The nonce */
- if(!dg->cnonce || !dg->cnonce[0])
+ if(*hostinfo == '[')
{
- ha_messagex(LOG_WARNING, "digest response is missing cnonce value");
- return HA_FALSE;
- }
+ v6_offset1 = 1;
- /* The nonce count */
- if(!dg->nc || !dg->nc[0])
- {
- ha_messagex(LOG_WARNING, "digest response is missing nc value");
- return HA_FALSE;
+ s = str;
+ for(;;)
+ {
+ --s;
+
+ if(s < hostinfo)
+ {
+ s = NULL; /* no port */
+ break;
+ }
+
+ else if(*s == ']')
+ {
+ *s = 0;
+ s = NULL; /* no port */;
+ break;
+ }
+
+ else if(*s == ':')
+ {
+ *s = 0;
+ ++s; /* found a port */
+ break;
+ }
+ }
}
- /* Validate the nc */
else
{
- char* e;
- long nc = strtol(dg->nc, &e, 10);
-
- if(e != (dg->nc + strlen(e)) || nc != rec->nc)
+ s = memchr(hostinfo, ':', str - hostinfo);
+ if(s)
{
- ha_messagex(LOG_WARNING, "digest response has invalid nc value: %s",
- dg->nc);
- return HA_FALSE;
+ *s = 0;
+ ++s;
}
}
- }
-
- /* The algorithm */
- if(dg->algorithm && strcasecmp(dg->algorithm, "MD5") != 0)
- {
- ha_messagex(LOG_WARNING, "digest response contains unknown or unsupported algorithm: '%s'",
- dg->algorithm ? dg->algorithm : "");
- return HA_FALSE;
- }
- /* Request URI */
- if(!dg->uri)
- {
- ha_messagex(LOG_WARNING, "digest response is missing uri");
- return HA_FALSE;
- }
-
- if(strcmp(dg->uri, uri) != 0)
- {
- ha_uri_t d_uri;
- ha_uri_t s_uri;
+ uri->host = hostinfo + v6_offset1;
- if(ha_uriparse(dg->uri, &d_uri) == HA_ERROR)
+ if(s == NULL)
{
- ha_messagex(LOG_WARNING, "digest response constains invalid uri: %s", dg->uri);
- return HA_FALSE;
+ /* we expect the common case to have no port */
+ goto deal_with_path;
}
- if(ha_uriparse(uri, &s_uri) == HA_ERROR)
+ if(str != s)
{
- ha_messagex(LOG_ERR, "server sent us an invalid uri");
- return HA_ERROR;
+ port = strtol(s, &endstr, 10);
+ uri->port = port;
+ if(*endstr != '\0')
+ return HA_FALSE;
}
- if(ha_uricmp(&d_uri, &s_uri) != 0)
- {
- ha_messagex(LOG_WARNING, "digest response contains wrong uri: %s "
- "(should be %s)", dg->uri, uri);
- return HA_ERROR;
- }
+ goto deal_with_path;
}
- /*
- * nonce: should have already been validated by the caller who
- * found this nice rec structure to pass us.
- *
- * opaque: We also don't use opaque. The caller should have validated it
- * if it's used there.
- */
+ /* Remove the @ symbol */
+ *s = 0;
- /*
- * Now we validate the digest response
- */
+ /* first colon delimits username:password */
+ s1 = memchr(hostinfo, ':', s - hostinfo);
+ if(s1)
+ {
+ *s1 = 0;
+ ++s1;
- /* Encode ha1 */
- ha_bufnext(buf);
- ha_bufenchex(buf, rec->ha1, MD5_LEN);
+ uri->user = hostinfo;
+ uri->pw = s1;
+ }
+ else
+ {
+ str = hostinfo;
+ }
- if((t = ha_bufdata(buf)) == NULL)
- return HA_ERROR;
+ hostinfo = s + 1;
+ goto deal_with_host;
+}
- /* Encode ha2 */
- MD5Init(&md5);
- MD5Update(&md5, method, strlen(method));
- MD5Update(&md5, ":", 1);
- MD5Update(&md5, dg->uri, strlen(dg->uri));
- MD5Final(hash, &md5);
+int uri_cmp_part_s(const char* s1, const char* s2, const char* def, int cs)
+{
+ if(def)
+ {
+ if(s1 && strcmp(def, s1) == 0)
+ s1 = NULL;
+ if(s2 && strcmp(def, s2) == 0)
+ s2 = NULL;
+ }
- ha_bufnext(buf);
- ha_bufenchex(buf, hash, MD5_LEN);
+ if(!s1 && !s2)
+ return 0;
- if(!ha_bufdata(buf))
- return HA_ERROR;
+ if(!s1)
+ return -1;
+ if(!s2)
+ return 1;
- /* Old style digest (RFC 2069) */
- if(!dg->qop)
- {
- MD5Init(&md5);
- MD5Update(&md5, t, MD5_LEN * 2); /* ha1 */
- MD5Update(&md5, ":", 1);
- MD5Update(&md5, dg->nonce, strlen(dg->nonce)); /* nonce */
- MD5Update(&md5, ":", 1);
- MD5Update(&md5, ha_bufdata(buf), MD5_LEN * 2); /* ha1 */
- MD5Final(hash, &md5);
- }
+ return cs ? strcasecmp(s1, s2) : strcmp(s1, s2);
+}
- /* New style 'auth' digest (RFC 2617) */
- else
+int uri_cmp_part_n(int n1, int n2, int def)
+{
+ if(def)
{
- MD5Init(&md5);
- MD5Update(&md5, t, MD5_LEN * 2); /* ha1 */
- MD5Update(&md5, ":", 1);
- MD5Update(&md5, dg->nonce, strlen(dg->nonce)); /* nonce */
- MD5Update(&md5, ":", 1);
- MD5Update(&md5, dg->nc, strlen(dg->nc)); /* nc */
- MD5Update(&md5, ":", 1);
- MD5Update(&md5, dg->cnonce, strlen(dg->cnonce)); /* cnonce */
- MD5Update(&md5, ":", 1);
- MD5Update(&md5, dg->qop, strlen(dg->qop)); /* qop */
- MD5Update(&md5, ":", 1);
- MD5Update(&md5, ha_bufdata(buf), MD5_LEN * 2); /* ha2 */
- MD5Final(hash, &md5);
+ if(n1 == def)
+ n1 = 0;
+ if(n2 == def)
+ n2 = 0;
}
- /* Encode the digest */
- ha_bufnext(buf);
- ha_bufenchex(buf, hash, MD5_LEN);
+ if(n1 == n2)
+ return 0;
- if((digest = ha_bufdata(buf)) == NULL)
- return HA_ERROR;
+ else if(n1 < n2)
+ return -1;
- if(strcasecmp(dg->digest, digest) != 0)
- {
- ha_messagex(LOG_WARNING, "digest authentication failed for user: %s", dg->username);
- return HA_FALSE;
- }
+ else if(n2 > n1)
+ return 1;
- return HA_OK;
+ /* Not reached */
+ ASSERT(0);
}
-const char* ha_digestrespond(ha_buffer_t* buf, ha_digest_header_t* dg,
- ha_digest_rec_t* rec)
+int ha_uricmp(ha_uri_t* one, ha_uri_t* two)
{
- unsigned char hash[MD5_LEN];
- struct MD5Context md5;
- const char* t;
- const char* digest;
+ int r;
- /* Encode ha1 */
- ha_bufnext(buf);
- ha_bufenchex(buf, rec->ha1, MD5_LEN);
+ /* We don't compare user or password */
- if((t = ha_bufdata(buf)) == NULL)
- return HA_ERROR;
+ /* The scheme */
+ if((r = uri_cmp_part_s(one->scheme, two->scheme, "http", 0)) != 0 ||
+ (r = uri_cmp_part_s(one->host, two->host, NULL, 1)) != 0 != 0 ||
+ (r = uri_cmp_part_n(one->port, two->port, 80)) != 0 ||
+ (r = uri_cmp_part_s(one->path, two->path, NULL, 0)) != 0 ||
+ (r = uri_cmp_part_s(one->query, two->query, NULL, 0)) != 0 ||
+ (r = uri_cmp_part_s(one->fragment, two->fragment, NULL, 0)))
+ return r;
- /* Encode ha2 */
- MD5Init(&md5);
- MD5Update(&md5, method, strlen(method));
- MD5Update(&md5, ":", 1);
- MD5Update(&md5, dg->uri, strlen(dg->uri));
- MD5Final(hash, &md5);
+ return 0;
+}
- ha_bufnext(buf);
- ha_bufenchex(buf, hash, MD5_LEN);
+int ha_genrandom(unsigned char* data, size_t len)
+{
+ int r, dd;
- if(!ha_bufdata(buf))
+ dd = open("/dev/urandom", O_RDONLY);
+ if(dd == -1)
+ {
+ ha_message(LOG_ERR, "couldn't open /dev/urandom");
return HA_ERROR;
-}
+ }
+
+ for(;;)
+ {
+ r = read(dd, data, len);
+
+ if(r == -1)
+ {
+ switch(errno)
+ {
+ case EINTR:
+ case EAGAIN:
+ continue;
+
+ default:
+ ha_message(LOG_ERR, "couldn't read random bytes from /dev/urandom");
+ break;
+ }
+ break;
+ }
+
+ else if(r >= 0)
+ {
+ if(r >= len)
+ break;
-char* ha_uriformat(const ha_uri_t* uri, ha_buffer_t* buf);
-int ha_uriparse(const char* str, ha_uri_t* uri);
+ len -= r;
+ data += r;
+ }
+ }
+
+ close(dd);
+ return r == -1 ? HA_ERROR : HA_OK;
+}
diff --git a/daemon/ntlm.c b/daemon/ntlm.c
index 880ae9e..bdf2116 100644
--- a/daemon/ntlm.c
+++ b/daemon/ntlm.c
@@ -5,6 +5,8 @@
#include "httpauthd.h"
#include "hash.h"
#include "defaults.h"
+#include "md5.h"
+#include "basic.h"
#include <syslog.h>
@@ -15,8 +17,7 @@
* Defaults and Constants
*/
-/* This needs to be the same as an MD5 hash length */
-#define NTLM_HASH_KEY_LEN 16
+#define NTLM_HASH_KEY_LEN MD5_LEN
#define NTLM_ESTABLISHED (void*)1
@@ -75,8 +76,7 @@ static ntlm_connection_t* getpending(ntlm_context_t* ctx, const void* key)
ha_lock(NULL);
- if(ret = (ntlm_connection_t*)hash_get(ctx->pending, key))
- hash_rem(ctx->pending, key);
+ ret = (ntlm_connection_t*)hash_rem(ctx->pending, key);
ha_unlock(NULL);
@@ -87,18 +87,18 @@ static int putpending(ntlm_context_t* ctx, const void* key, ntlm_connection_t* c
{
int r = 0;
- ha_lock(NULL);
+ if(!hash_get(ctx->pending, key))
+ {
+ ha_lock(NULL);
- if(hash_get(ctx->pending, key))
- {
if(!hash_set(ctx->pending, key, (void*)conn))
{
ha_messagex(LOG_ERR, "out of memory");
r = -1;
}
- }
- ha_unlock(NULL);
+ ha_unlock(NULL);
+ }
return r;
}
@@ -132,7 +132,7 @@ static ntlm_connection_t* makeconnection(ntlm_context_t* ctx)
}
}
-static void freeconnection(ntlm_context_t* ctx, ntlm_connection_t* conn)
+static void freeconnection(ntlm_connection_t* conn)
{
if(conn->handle)
{
@@ -143,13 +143,20 @@ static void freeconnection(ntlm_context_t* ctx, ntlm_connection_t* conn)
free(conn);
}
+static void free_hash_object(void* arg, void* val)
+{
+ if(val)
+ freeconnection((ntlm_connection_t*)val);
+}
+
int ntlm_auth_basic(ntlm_context_t* ctx, char* key, const char* header,
ha_response_t* resp, ha_buffer_t* buf)
{
ntlm_connection_t* conn;
char* t;
- ha_basic_header_t basic;
+ basic_header_t basic;
const char* domain = NULL;
+ int found = 0;
/*
* We're doing basic authentication on the connection
@@ -158,15 +165,15 @@ int ntlm_auth_basic(ntlm_context_t* ctx, char* key, const char* header,
*/
conn = getpending(ctx, key);
if(conn)
- freeconnection(ctx, conn);
+ freeconnection(conn);
- if(ha_parsebasic(header, buf, &basic) == HA_ERROR)
+ if(basic_parse(header, buf, &basic) == HA_ERROR)
return HA_ERROR;
/* Check and see if this connection is in the cache */
ha_lock(NULL);
- if(hash_get(ctx->established, basic.key) == BASIC_ESTABLISHED)
+ if(hash_get(ctx->established, basic.key) == NTLM_ESTABLISHED)
found = 1;
ha_unlock(NULL);
@@ -204,18 +211,26 @@ int ntlm_auth_basic(ntlm_context_t* ctx, char* key, const char* header,
ctx->backup, domain) == NTV_NO_ERROR)
{
/* If valid then we return */
- resp->code = HA_SERVER_ACCEPT;
+ found = 1;
}
ha_unlock(&g_smblib_mutex);
}
- if(resp->code = HA_SERVER_ACCEPT)
+ if(found)
{
+ int r;
+ resp->code = HA_SERVER_ACCEPT;
resp->detail = basic.user;
- /* We put this connection into the successful connections */
- if(!hash_set(ctx->established, basic.key, BASIC_ESTABLISHED))
+ ha_lock(NULL);
+
+ /* We put this connection into the successful connections */
+ r = hash_set(ctx->established, basic.key, NTLM_ESTABLISHED);
+
+ ha_unlock(NULL);
+
+ if(!r)
{
ha_messagex(LOG_CRIT, "out of memory");
return HA_ERROR;
@@ -255,8 +270,7 @@ int ntlm_auth_ntlm(ntlm_context_t* ctx, void* key, const char* header,
* is sending us.
*/
- ha_bufnext(buf);
- ha_bufdec64(buf, header);
+ ha_bufdec64(buf, header, 0);
header = ha_bufdata(buf);
if(ha_buferr(buf))
@@ -312,14 +326,11 @@ int ntlm_auth_ntlm(ntlm_context_t* ctx, void* key, const char* header,
if(ctx->pending_max != -1)
{
ha_lock(NULL);
- r = (hash_count(ctx->pending) >= ctx->pending_max);
- ha_unlock(NULL);
- if(r)
- {
- resp->code = HA_SERVER_BUSY;
- goto finally;
- }
+ if(hash_count(ctx->pending) >= ctx->pending_max)
+ hash_bump(ctx->pending);
+
+ ha_unlock(NULL);
}
@@ -340,19 +351,20 @@ int ntlm_auth_ntlm(ntlm_context_t* ctx, void* key, const char* header,
conn->flags = flags;
/* Start building the header */
- ha_bufnext(buf);
- ha_bufcat(buf, NTLM_PREFIX);
+ ha_bufcpy(buf, HA_PREFIX_NTLM);
if(win9x)
{
struct ntlm_msg2_win9x msg_win9x;
ntlmssp_encode_msg2_win9x(conn->nonce, &msg_win9x, (char*)ctx->domain, flags);
+ ha_bufjoin(buf);
ha_bufenc64(buf, (unsigned char*)&msg_win9x, sizeof(msg_win9x));
}
else
{
struct ntlm_msg2 msg;
ntlmssp_encode_msg2(conn->nonce, &msg);
+ ha_bufjoin(buf);
ha_bufenc64(buf, (unsigned char*)&msg, sizeof(msg));
}
@@ -431,14 +443,25 @@ int ntlm_auth_ntlm(ntlm_context_t* ctx, void* key, const char* header,
/* A successful login ends here */
else
{
- resp->code = HA_SERVER_ACCEPT;
+ int r;
+ resp->detail = ntlmssp.user;
+
+ ha_lock(NULL);
- /* We put this connection into the successful connections */
- if(!hash_set(ctx->established, key, NTLM_ESTABLISHED))
+ /* We put this connection into the successful connections */
+ r = hash_set(ctx->established, key, NTLM_ESTABLISHED);
+
+ ha_unlock(NULL);
+
+ if(!r)
{
ha_messagex(LOG_CRIT, "out of memory");
ret = HA_ERROR;
}
+ else
+ {
+ ret = HA_OK;
+ }
}
goto finally;
@@ -456,7 +479,7 @@ finally:
ret = HA_ERROR;
if(conn)
- freeconnection(ctx, conn);
+ freeconnection(conn);
return ret;
}
@@ -531,8 +554,8 @@ int ntlm_init(ha_context_t* context)
}
/* Initialize our tables */
- if(!(ctx->pending = hash_create(NTLM_HASH_KEY_LEN)) ||
- !(ctx->established = hash_create(NTLM_HASH_KEY_LEN)))
+ if(!(ctx->pending = hash_create(NTLM_HASH_KEY_LEN, free_hash_object, NULL)) ||
+ !(ctx->established = hash_create(NTLM_HASH_KEY_LEN, NULL, NULL)))
{
ha_messagex(LOG_CRIT, "out of memory");
return HA_ERROR;
@@ -545,7 +568,7 @@ int ntlm_init(ha_context_t* context)
/* Create the smblib mutex */
if(pthread_mutexattr_init(&g_smblib_mutexattr) != 0 ||
pthread_mutexattr_settype(&g_smblib_mutexattr, HA_MUTEX_TYPE) ||
- pthread_mutex_init(&g_mutex, &g_smblib_mutexattr) != 0)
+ pthread_mutex_init(&g_smblib_mutex, &g_smblib_mutexattr) != 0)
{
ha_messagex(LOG_CRIT, "threading problem. can't create mutex");
return HA_ERROR;
@@ -589,13 +612,11 @@ int ntlm_process(ha_context_t* context, ha_request_t* req,
resp->code = -1;
/* Hash the unique key */
- ha_md5string(req->args[1], key);
+ md5_string(key, req->args[AUTH_ARG_CONN]);
ha_lock(NULL);
- XXX: These connections aren't being destroyed properly
-
/*
* Purge out stale connection stuff. This includes
* authenticated connections which have expired as
@@ -608,9 +629,9 @@ int ntlm_process(ha_context_t* context, ha_request_t* req,
/* Look for a NTLM header */
- if(context->types & HA_TYPES_NTLM)
+ if(context->types & HA_TYPE_NTLM)
{
- header = ha_getheader(req, "Authorization", NTLM_PREFIX);
+ header = ha_getheader(req, "Authorization", HA_PREFIX_NTLM);
if(header)
{
/* Trim off for decoding */
@@ -627,7 +648,7 @@ int ntlm_process(ha_context_t* context, ha_request_t* req,
if(!header && context->types & HA_TYPE_BASIC)
{
/* Look for a Basic header */
- header = ha_getheader(req, "Authorization", BASIC_PREFIX);
+ header = ha_getheader(req, "Authorization", HA_PREFIX_BASIC);
if(header)
{
/* Trim off for decoding */
@@ -669,14 +690,16 @@ int ntlm_process(ha_context_t* context, ha_request_t* req,
resp->code = HA_SERVER_DECLINE;
if(context->types & HA_TYPE_NTLM)
- ha_addheader(resp, "WWW-Authenticate", NTLM_PREFIX);
+ ha_addheader(resp, "WWW-Authenticate", HA_PREFIX_NTLM);
if(context->types & HA_TYPE_BASIC)
{
- ha_bufnext(buf);
- ha_bufcat(buf, BASIC_PREFIX, "realm=\"",
+ ha_bufmcat(buf, HA_PREFIX_BASIC, "realm=\"",
ctx->basic_realm ? ctx->basic_realm : "", "\"", NULL);
+ if(ha_buferr(buf))
+ return HA_ERROR;
+
ha_addheader(resp, "WWW-Authenticate", ha_bufdata(buf));
}
}
@@ -697,7 +720,7 @@ ha_handler_t ntlm_handler =
ntlm_destroy, /* Uninitialization routine */
ntlm_config, /* Config routine */
ntlm_process, /* Processing routine */
- ntlm_defaults, /* Default settings */
+ &ntlm_defaults, /* Default settings */
sizeof(ntlm_context_t)
};
diff --git a/daemon/simple.c b/daemon/simple.c
new file mode 100644
index 0000000..cd1b812
--- /dev/null
+++ b/daemon/simple.c
@@ -0,0 +1,663 @@
+
+/* On some linux this is required to get crypt to show itself */
+#define _XOPEN_SOURCE
+
+#include "usuals.h"
+#include "httpauthd.h"
+#include "defaults.h"
+#include "basic.h"
+#include "digest.h"
+#include "hash.h"
+#include "md5.h"
+
+#include <syslog.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+unsigned char g_simple_secret[DIGEST_SECRET_LEN];
+
+#define SIMPLE_MAXLINE 256
+#define BASIC_ESTABLISHED (void*)1
+
+/* -------------------------------------------------------------------------------
+ * Structures
+ */
+
+typedef struct simple_context
+{
+ const char* filename; /* The file name with the user names */
+ const char* realm; /* The realm for basic authentication */
+ const char* domains; /* Domains for which digest auth is valid */
+ int cache_max; /* Maximum number of connections at once */
+
+ /* Context ----------------------------------------------------------- */
+ hash_t* cache; /* Some cached records or basic */
+}
+simple_context_t;
+
+/* The defaults for the context */
+static const simple_context_t simple_defaults =
+{
+ NULL, /* filename */
+ NULL, /* realm */
+ NULL, /* domains */
+ 1000, /* cache_max */
+ NULL /* cache */
+};
+
+
+/* -------------------------------------------------------------------------------
+ * Internal Functions
+ */
+
+static void free_hash_object(void* arg, void* val)
+{
+ if(val && val != BASIC_ESTABLISHED)
+ free(val);
+}
+
+static digest_record_t* get_cached_digest(simple_context_t* ctx, unsigned char* nonce)
+{
+ digest_record_t* rec;
+
+ if(ctx->cache_max == 0)
+ return NULL;
+
+ ha_lock(NULL);
+
+ rec = (digest_record_t*)hash_get(ctx->cache, nonce);
+
+ /* Just in case it's a basic :) */
+ if(rec && rec != BASIC_ESTABLISHED)
+ hash_rem(ctx->cache, nonce);
+
+ ha_unlock(NULL);
+
+ ASSERT(!rec || memcmp(nonce, rec->nonce, DIGEST_NONCE_LEN) == 0);
+ return rec;
+}
+
+static int have_cached_basic(simple_context_t* ctx, unsigned char* key)
+{
+ int ret = 0;
+
+ ha_lock(NULL);
+
+ ret = (hash_get(ctx->cache, key) == BASIC_ESTABLISHED);
+
+ ha_unlock(NULL);
+
+ return ret;
+}
+
+static int save_cached_digest(simple_context_t* ctx, digest_record_t* rec)
+{
+ int r;
+
+ if(ctx->cache_max == 0)
+ return HA_FALSE;
+
+ ha_lock(NULL);
+
+ while(hash_count(ctx->cache) >= ctx->cache_max)
+ hash_bump(ctx->cache);
+
+ r = hash_set(ctx->cache, rec->nonce, rec);
+
+ ha_unlock(NULL);
+
+ if(!r)
+ {
+ ha_messagex(LOG_CRIT, "out of memory");
+ return HA_ERROR;
+ }
+
+ return HA_OK;
+}
+
+static int add_cached_basic(simple_context_t* ctx, unsigned char* key)
+{
+ int r;
+
+ if(ctx->cache_max == 0)
+ return HA_FALSE;
+
+ ha_lock(NULL);
+
+ while(hash_count(ctx->cache) >= ctx->cache_max)
+ hash_bump(ctx->cache);
+
+ r = hash_set(ctx->cache, key, BASIC_ESTABLISHED);
+
+ ha_unlock(NULL);
+
+ if(!r)
+ {
+ ha_messagex(LOG_CRIT, "out of memory");
+ return HA_ERROR;
+ }
+
+ return HA_OK;
+}
+
+static int complete_digest_ha1(simple_context_t* ctx, digest_record_t* rec,
+ ha_buffer_t* buf, const char* user, int* code)
+{
+ FILE* f;
+ int found = 0;
+ char* t;
+ char line[SIMPLE_MAXLINE];
+
+ f = fopen(ctx->filename, "r");
+ if(!f)
+ {
+ ha_message(LOG_ERR, "can't open file for basic auth: %s", ctx->filename);
+ *code = HA_SERVER_ERROR;
+ return HA_FALSE;
+ }
+
+ /*
+ * Note: There should be no returns or jumps between
+ * this point and the closing of the file below.
+ */
+
+ /* Now look through the whole file */
+ while(!feof(f))
+ {
+ fgets(line, SIMPLE_MAXLINE, f);
+
+ if(ferror(f))
+ {
+ ha_message(LOG_ERR, "error reading basic password file");
+ *code = HA_SERVER_ERROR;
+ break;
+ }
+
+ t = strchr(line, ':');
+ if(t)
+ {
+ /* Split the line */
+ *t = 0;
+ t++;
+
+ /* 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)
+ {
+ memcpy(rec->ha1, t, MD5_LEN);
+ found = 1;
+ break;
+ }
+
+ else
+ {
+ if(ha_buferr(buf))
+ break;
+
+ ha_messagex(LOG_WARNING, "user '%s' found in file, but password not in digest format", user);
+ }
+ }
+ }
+
+ fclose(f);
+ }
+
+ if(ha_buferr(buf))
+ return HA_ERROR;
+
+ return found ? HA_FALSE : HA_OK;
+}
+
+static int validate_user_password(simple_context_t* ctx, ha_buffer_t* buf,
+ const char* user, const char* clearpw, int* code)
+{
+ FILE* f;
+ int found = 0;
+ char line[SIMPLE_MAXLINE];
+ unsigned char ha1[MD5_LEN];
+ char* t;
+ char* t2;
+
+ f = fopen(ctx->filename, "r");
+ if(!f)
+ {
+ ha_message(LOG_ERR, "can't open file for basic auth: %s", ctx->filename);
+ *code = HA_SERVER_ERROR;
+ return HA_FALSE;
+ }
+
+ digest_makeha1(ha1, user, ctx->realm, clearpw);
+
+ /*
+ * Note: There should be no returns or jumps between
+ * this point and the closing of the file below.
+ */
+
+ /* Now look through the whole file */
+ while(!feof(f))
+ {
+ fgets(line, SIMPLE_MAXLINE, f);
+
+ if(ferror(f))
+ {
+ ha_message(LOG_ERR, "error reading basic password file");
+ *code = HA_SERVER_ERROR;
+ break;
+ }
+
+ t = strchr(line, ':');
+ if(t)
+ {
+ /* Split the line */
+ *t = 0;
+ t++;
+
+ /* 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);
+
+ if(t2 && memcmp(ha1, t2, MD5_LEN) == 0)
+ {
+ memcpy(ha1, t2, MD5_LEN);
+ found = 1;
+ break;
+ }
+
+ /* Otherwise we try a nice crypt style password */
+ else
+ {
+ /* Not sure if crypt is thread safe so we lock */
+ ha_lock();
+
+ /* Check the password */
+ if(strcmp(crypt(clearpw, t), t) == 0)
+ found = 1;
+
+ ha_unlock();
+
+ if(found)
+ break;
+ }
+
+ if(ha_buferr(buf))
+ break;
+ }
+ }
+
+ fclose(f);
+ }
+
+ if(ha_buferr(buf))
+ return HA_ERROR;
+
+ return found ? HA_FALSE : HA_OK;
+}
+
+static int simple_basic_response(simple_context_t* ctx, const char* header,
+ ha_response_t* resp, ha_buffer_t* buf)
+{
+ basic_header_t basic;
+ int ret = HA_FALSE;
+ int found = 0;
+ int r;
+
+ ASSERT(buf && header && resp && buf);
+
+ if(basic_parse(header, buf, &basic) == HA_ERROR)
+ return HA_ERROR;
+
+ /* Past this point we don't return directly */
+
+ /* Check and see if this connection is in the cache */
+ if(have_cached_basic(ctx, basic.key))
+ {
+ found = 1;
+ ret = HA_OK;
+ goto finally;
+ }
+
+ /* If we have a user name and password */
+ if(!basic.user || !basic.user[0] ||
+ !basic.password || !basic.password[0])
+ goto finally;
+
+
+ ret = validate_user_password(ctx, buf, basic.user, basic.password, &(resp->code));
+
+finally:
+
+ if(resp->code == HA_SERVER_ACCEPT)
+ {
+ resp->detail = basic.user;
+
+ /* We put this connection into the successful connections */
+ ret = add_cached_basic(ctx, basic.key);
+ }
+
+ return ret;
+}
+
+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* header;
+
+ /* Generate an nonce */
+ digest_makenonce(nonce, g_simple_secret, NULL);
+
+ /* Now generate a message to send */
+ header = digest_challenge(buf, nonce, ctx->realm, ctx->domains, stale);
+
+ if(!header)
+ return HA_ERROR;
+
+ /* And append it nicely */
+ resp->code = HA_SERVER_DECLINE;
+ ha_addheader(resp, "WWW-Authenticate", header);
+
+ return HA_OK;
+}
+
+static int simple_digest_response(simple_context_t* ctx, const char* header,
+ const char* method, const char* uri, int timeout,
+ ha_response_t* resp, ha_buffer_t* buf)
+{
+ unsigned char nonce[DIGEST_NONCE_LEN];
+ digest_header_t dg;
+ digest_record_t* rec = NULL;
+ const char* t;
+ time_t expiry;
+ int ret = HA_FALSE;
+ int stale = 0;
+ int r;
+
+ /* We use this below to send a default response */
+ resp->code = -1;
+
+ if(digest_parse(header, buf, &dg, nonce) == HA_ERROR)
+ return HA_ERROR;
+
+ 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);
+
+ /* Check to see if we're stale */
+ if((expiry + timeout) <= time(NULL))
+ {
+ stale = 1;
+ goto finally;
+ }
+
+ if(!rec)
+ {
+ /*
+ * If we're valid but don't have a record in the
+ * cache then complete the record properly.
+ */
+
+ rec = digest_makerec(nonce, dg.username);
+ if(!rec)
+ {
+ ret = HA_ERROR;
+ goto finally;
+ }
+
+ r = complete_digest_ha1(ctx, rec, buf, dg.username, &(resp->code));
+ if(r != HA_OK)
+ {
+ ret = r;
+ goto finally;
+ }
+ }
+
+ /* Increment our nonce count */
+ rec->nc++;
+
+ ret = digest_check(ctx->realm, method, uri, buf, &dg, rec);
+
+ if(ret == HA_OK)
+ {
+ resp->code = HA_SERVER_ACCEPT;
+ resp->detail = dg.username;
+
+ /* Figure out if we need a new nonce */
+ if((expiry + (timeout - (timeout / 8))) < time(NULL))
+ {
+ digest_makenonce(nonce, g_simple_secret, NULL);
+ stale = 1;
+ }
+
+ t = digest_respond(buf, &dg, rec, stale ? nonce : NULL);
+ if(!t)
+ {
+ ret = HA_ERROR;
+ goto finally;
+ }
+
+ if(t[0])
+ ha_addheader(resp, "Authentication-Info", t);
+
+ /* Put the connection into the cache */
+ if(save_cached_digest(ctx, rec) == HA_ERROR)
+ ret = HA_ERROR;
+ else
+ rec = NULL;
+ }
+
+finally:
+
+ /* If the record wasn't stored away then free it */
+ if(rec)
+ free(rec);
+
+ /* If nobody above responded then challenge the client again */
+ if(resp->code == -1)
+ return simple_digest_challenge(ctx, resp, buf, stale);
+
+ return ret;
+}
+
+
+/* -------------------------------------------------------------------------------
+ * Handler Functions
+ */
+
+int simple_config(ha_context_t* context, const char* name, const char* value)
+{
+ simple_context_t* ctx = (simple_context_t*)(context->data);
+
+ if(strcmp(name, "passwordfile") == 0)
+ {
+ ctx->filename = value;
+ return HA_OK;
+ }
+
+ else if(strcmp(name, "realm") == 0)
+ {
+ ctx->realm = value;
+ return HA_OK;
+ }
+
+ else if(strcmp(name, "digestdomains") == 0)
+ {
+ ctx->domains = value;
+ return HA_OK;
+ }
+
+ else if(strcmp(name, "cachemax") == 0)
+ {
+ return ha_confint(name, value, 0, 0x7FFFFFFF, &(ctx->cache_max));
+ }
+
+ return HA_FALSE;
+}
+
+int simple_init(ha_context_t* context)
+{
+ /* Global initialization */
+ if(!context)
+ {
+ return ha_genrandom(g_simple_secret, DIGEST_SECRET_LEN);
+ }
+
+ /* Context specific initialization */
+ else
+ {
+ simple_context_t* ctx = (simple_context_t*)(context->data);
+ int fd;
+
+ /* Make sure there are some types of authentication we can do */
+ if(!(context->types & (HA_TYPE_BASIC | HA_TYPE_DIGEST)))
+ {
+ ha_messagex(LOG_ERR, "Simple module configured, but does not implement any "
+ "configured authentication type.");
+ return HA_ERROR;
+ }
+
+
+ /* Check to make sure the file exists */
+ if(!ctx->filename)
+ {
+ ha_messagex(LOG_ERR, "Basic configuration incomplete. "
+ "Must have a PasswordFile configured.");
+ return HA_ERROR;
+ }
+
+ fd = open(ctx->filename, O_RDONLY);
+ if(fd == -1)
+ {
+ ha_message(LOG_ERR, "can't open file for simple authentication: %s", ctx->filename);
+ return HA_ERROR;
+ }
+
+ close(fd);
+
+ /* The cache for digest records and basic */
+ if(!(ctx->cache = hash_create(MD5_LEN, free_hash_object, NULL)))
+ {
+ ha_messagex(LOG_CRIT, "out of memory");
+ return HA_ERROR;
+ }
+ }
+
+ return HA_OK;
+}
+
+void simple_destroy(ha_context_t* context)
+{
+ /* Per context destroy */
+ if(context)
+ {
+ simple_context_t* ctx = (simple_context_t*)(context->data);
+
+ /* Note: We don't need to be thread safe here anymore */
+ hash_free(ctx->cache);
+ }
+}
+
+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;
+ int ret = HA_FALSE;
+ int found = 0;
+ basic_header_t basic;
+
+
+ ha_lock(NULL);
+
+ /* Purge the cache */
+ hash_purge(ctx->cache, time(NULL) - context->timeout);
+
+ ha_unlock(NULL);
+
+
+ /* We use this below to detect whether to send a default response */
+ resp->code = -1;
+
+
+ /* Check the headers and see if we got a response thingy */
+ if(context->types & HA_TYPE_DIGEST)
+ {
+ header = ha_getheader(req, "Authorization", HA_PREFIX_DIGEST);
+ if(header)
+ {
+ ret = simple_digest_response(ctx, header, req->args[AUTH_ARG_METHOD],
+ req->args[AUTH_ARG_URI], context->timeout,
+ resp, buf);
+ if(ret == HA_ERROR)
+ return ret;
+ }
+ }
+
+ /* Or a basic authentication */
+ if(!header && context->types & HA_TYPE_BASIC)
+ {
+ header = ha_getheader(req, "Authorization", HA_PREFIX_BASIC);
+ if(header)
+ {
+ ret = simple_basic_response(ctx, header, resp, buf);
+ if(ret == HA_ERROR)
+ return ret;
+ }
+ }
+
+
+ /* Send a default response if that's what we need */
+ if(resp->code == -1)
+ {
+ 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);
+
+ if(ha_buferr(buf))
+ return HA_ERROR;
+
+ ha_addheader(resp, "WWW-Authenticate", ha_bufdata(buf));
+ }
+ }
+
+ return ret;
+}
+
+
+/* -------------------------------------------------------------------------------
+ * Handler Definition
+ */
+
+ha_handler_t simple_handler =
+{
+ "SIMPLE", /* The type */
+ simple_init, /* Initialization function */
+ simple_destroy, /* Uninitialization routine */
+ simple_config, /* Config routine */
+ simple_process, /* Processing routine */
+ &simple_defaults, /* A default context */
+ sizeof(simple_context_t) /* Size of the context */
+};
+
diff --git a/daemon/usuals.h b/daemon/usuals.h
index 22dcb3a..e14ecf5 100644
--- a/daemon/usuals.h
+++ b/daemon/usuals.h
@@ -28,4 +28,11 @@
#define countof(x) (sizeof(x) / sizeof(x[0]))
+#ifdef _DEBUG
+ #include "assert.h"
+ #define ASSERT assert
+#else
+ #define ASSERT
+#endif
+
#endif /* __USUALS_H__ */