diff options
Diffstat (limited to 'daemon/bd.c')
-rw-r--r-- | daemon/bd.c | 238 |
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 */ |