summaryrefslogtreecommitdiff
path: root/daemon/bd.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/bd.c')
-rw-r--r--daemon/bd.c238
1 files changed, 194 insertions, 44 deletions
diff --git a/daemon/bd.c b/daemon/bd.c
index 6cd7b6b..5519cfa 100644
--- a/daemon/bd.c
+++ b/daemon/bd.c
@@ -12,12 +12,23 @@
static unsigned char g_digest_secret[DIGEST_SECRET_LEN];
+
+
/* -------------------------------------------------------------------------------
* Defaults and Constants
*/
#define BASIC_ESTABLISHED (void*)1
+/* Kept by the us 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;
/* -------------------------------------------------------------------------------
* Internal Functions
@@ -127,6 +138,25 @@ static int add_cached_basic(bd_context_t* ctx, ha_context_t* c,
return HA_OK;
}
+digest_record_t* make_digest_rec(unsigned char* nonce, const char* user)
+{
+ digest_record_t* rec = (digest_record_t*)malloc(sizeof(*rec));
+
+ ASSERT(nonce && user);
+
+ if(!rec)
+ {
+ ha_messagex(NULL, 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;
+}
+
static int do_basic_response(ha_request_t* rq, bd_context_t* ctx, const char* header)
{
basic_header_t basic;
@@ -217,7 +247,7 @@ static int do_digest_challenge(ha_request_t* rq, bd_context_t* ctx, int stale)
static int do_digest_response(ha_request_t* rq, bd_context_t* ctx, const char* header)
{
unsigned char nonce[DIGEST_NONCE_LEN];
- digest_header_t dg;
+ digest_context_t dg;
digest_record_t* rec = NULL;
const char* t;
time_t expiry;
@@ -230,15 +260,20 @@ static int do_digest_response(ha_request_t* rq, bd_context_t* ctx, const char* h
/* We use this below to send a default response */
rq->resp_code = -1;
- if((r = digest_parse(header, rq->buf, &dg, nonce)) < 0)
+ if((r = digest_parse(header, rq->buf, &(dg.client))) < 0)
return r;
+ if(!dg.client.username)
+ {
+ ha_messagex(rq, LOG_WARNING, "digest response contains no user name");
+ goto finally;
+ }
+
#ifdef _DEBUG
if(rq->context->digest_debugnonce)
{
- if(dg.nonce && strcmp(dg.nonce, rq->context->digest_debugnonce) != 0)
+ if(dg.client.nonce && strcmp(dg.client.nonce, rq->context->digest_debugnonce) != 0)
{
- ret = HA_FALSE;
ha_messagex(rq, LOG_WARNING, "digest response contains invalid nonce");
goto finally;
}
@@ -252,6 +287,18 @@ static int do_digest_response(ha_request_t* rq, bd_context_t* ctx, const char* h
else
#endif
{
+ /* Parse out the nonce from that header */
+ memset(nonce, 0, DIGEST_NONCE_LEN);
+
+ if(dg.client.nonce)
+ {
+ size_t len = DIGEST_NONCE_LEN;
+ void* d = ha_bufdechex(rq->buf, dg.client.nonce, &len);
+
+ if(d && len == DIGEST_NONCE_LEN)
+ memcpy(nonce, d, DIGEST_NONCE_LEN);
+ }
+
r = digest_checknonce(nonce, g_digest_secret, &expiry);
if(r != HA_OK)
{
@@ -260,95 +307,142 @@ static int do_digest_response(ha_request_t* rq, bd_context_t* ctx, const char* h
goto finally;
}
+
+ /* Check to see if we're stale */
+ if((expiry + rq->context->cache_timeout) <= time(NULL))
+ {
+ ha_messagex(rq, LOG_INFO, "nonce expired, sending stale challenge: %s",
+ dg.client.username);
+
+ stale = 1;
+ goto finally;
+ }
}
+ /* See if we have this one from before. */
rec = get_cached_digest(ctx, rq->context, nonce);
- /* Check to see if we're stale */
- if((expiry + rq->context->cache_timeout) <= time(NULL))
+ /*
+ * Fill in the required fields.
+ */
+ dg.server_nc = rec ? ++(rec->nc) : 0; /* Note bumping up nc */
+ dg.server_uri = rq->req_args[AUTH_ARG_URI];
+ dg.server_method = rq->req_args[AUTH_ARG_METHOD];
+
+ /* Check the majority of the fields */
+ ret = digest_pre_check(&dg, rq->context, rq->buf);
+
+ if(ret != HA_OK)
{
- ha_messagex(rq, LOG_INFO, "nonce expired, sending stale challenge: %s",
- dg.username);
+ if(ret == HA_BADREQ)
+ {
+ ret = HA_FALSE;
+ rq->resp_code = HA_SERVER_BADREQ;
+ }
- stale = 1;
goto finally;
}
+ /*
+ * If this is the first instance then we pass off to our derived
+ * handler for validation and completion of the ha1. This completes
+ * the authentication, and leaves us the ha1 caching.
+ */
if(!rec)
{
- ha_messagex(rq, LOG_INFO, "no record in cache, creating one: %s", dg.username);
+ ha_messagex(rq, LOG_INFO, "no record in cache, creating one: %s", dg.client.username);
/*
* If we're valid but don't have a record in the
* cache then complete the record properly.
*/
- rec = digest_makerec(nonce, dg.username);
+ rec = make_digest_rec(nonce, dg.client.username);
if(!rec)
{
ret = HA_CRITERROR;
goto finally;
}
- ASSERT(ctx->f_complete_digest);
- r = ctx->f_complete_digest(rq, dg.username, rec->ha1);
+
+ ASSERT(ctx->f_validate_digest);
+ r = ctx->f_validate_digest(rq, dg.client.username, &dg);
if(r != HA_OK)
{
ret = r;
goto finally;
}
+
+ /* Save away pertinent information when successful*/
+ memcpy(rec->ha1, dg.ha1, MD5_LEN);
}
/* We had a record so ... */
else
{
+ /* Bump up the ncount */
rec->nc++;
- }
- ret = digest_check(&dg, rec, rq->context, rq->buf,
- rq->req_args[AUTH_ARG_METHOD], rq->req_args[AUTH_ARG_URI]);
+ /* Check the user name */
+ if(md5_strcmp(rec->userhash, dg.client.username) != 0)
+ {
+ ha_messagex(NULL, LOG_ERR, "digest response contains invalid username");
+ ret = HA_FALSE;
+ goto finally;
+ }
- if(ret == HA_BADREQ)
- {
- ret = HA_FALSE;
- rq->resp_code = HA_SERVER_BADREQ;
- }
+ /* And do the validation ourselves */
+ memcpy(dg.ha1, rec->ha1, MD5_LEN);
- else if(ret == HA_OK)
- {
- rq->resp_code = HA_SERVER_OK;
- rq->resp_detail = dg.username;
+ ret = digest_complete_check(&dg, rq->buf);
- /* Figure out if we need a new nonce */
- if((expiry + (rq->context->cache_timeout -
- (rq->context->cache_timeout / 8))) < time(NULL))
+ if(ret != HA_OK)
{
- ha_messagex(rq, LOG_INFO, "nonce almost expired, creating new one: %s",
- dg.username);
+ if(ret == HA_BADREQ)
+ {
+ ret = HA_FALSE;
+ rq->resp_code = HA_SERVER_BADREQ;
+ }
- digest_makenonce(nonce, g_digest_secret, NULL);
- stale = 1;
- }
+ if(ret == HA_FALSE)
+ ha_messagex(NULL, LOG_WARNING, "digest re-authentication failed for user: %s",
+ dg.client.username);
- t = digest_respond(rq->buf, &dg, rec, stale ? nonce : NULL);
- if(!t)
- {
- ret = HA_CRITERROR;
goto finally;
}
+ }
- if(t[0])
- ha_addheader(rq, "Authentication-Info", t);
- ha_messagex(rq, LOG_NOTICE, "validated digest user: %s", dg.username);
+ rq->resp_code = HA_SERVER_OK;
+ rq->resp_detail = dg.client.username;
- /* Put the connection into the cache */
- if((r = save_cached_digest(ctx, rq->context, rec)) < 0)
- ret = r;
+ /* Figure out if we need a new nonce */
+ if((expiry + (rq->context->cache_timeout -
+ (rq->context->cache_timeout / 8))) < time(NULL))
+ {
+ ha_messagex(rq, LOG_INFO, "nonce almost expired, creating new one: %s",
+ dg.client.username);
- rec = NULL;
+ digest_makenonce(nonce, g_digest_secret, NULL);
+ stale = 1;
}
+ t = digest_respond(&dg, rq->buf, stale ? nonce : NULL);
+ if(!t)
+ {
+ ret = HA_CRITERROR;
+ goto finally;
+ }
+
+ if(t[0])
+ ha_addheader(rq, "Authentication-Info", t);
+
+ ha_messagex(rq, LOG_NOTICE, "validated digest user: %s", dg.client.username);
+
+ /* Put the connection into the cache */
+ if((ret = save_cached_digest(ctx, rq->context, rec)) == HA_OK)
+ rec = NULL;
+
finally:
/* If the record wasn't stored away then free it */
@@ -363,6 +457,62 @@ finally:
}
+
+const char* bd_substitute(const ha_request_t* rq, const char* user, const char* str)
+{
+ bd_context_t* ctx = (bd_context_t*)rq->context->ctx_data;
+ const char* t;
+
+ ASSERT(rq && user && str);
+ ASSERT(ctx->f_escape_value);
+
+ /* TODO: We need to be escaping the user and realm properly */
+ /* This starts a new block to join */
+ ha_bufcpy(rq->buf, "");
+
+ while(str[0])
+ {
+ t = strchr(str, '%');
+ if(!t)
+ {
+ ha_bufjoin(rq->buf);
+ ha_bufcpy(rq->buf, str);
+ break;
+ }
+
+ ha_bufjoin(rq->buf);
+ ha_bufncpy(rq->buf, str, t - str);
+
+ t++;
+
+ switch(t[0])
+ {
+ case 'u':
+ ha_bufjoin(rq->buf);
+ (ctx->f_escape_value)(rq, rq->buf, user);
+ t++;
+ break;
+
+ case 'r':
+ ha_bufjoin(rq->buf);
+ (ctx->f_escape_value)(rq, rq->buf, rq->context->realm);
+ t++;
+ break;
+
+ case '%':
+ ha_bufjoin(rq->buf);
+ ha_bufcpy(rq->buf, "%");
+ t++;
+ break;
+ };
+
+ str = t;
+ }
+
+ return ha_bufdata(rq->buf);
+}
+
+
/* -------------------------------------------------------------------------------
* Handler Functions
*/