diff options
Diffstat (limited to 'daemon/digest.c')
-rw-r--r-- | daemon/digest.c | 202 |
1 files changed, 98 insertions, 104 deletions
diff --git a/daemon/digest.c b/daemon/digest.c index 099bf31..bce588e 100644 --- a/daemon/digest.c +++ b/daemon/digest.c @@ -69,25 +69,6 @@ int digest_checknonce(unsigned char* nonce, unsigned char* secret, time_t* tm) return HA_FALSE; } -digest_record_t* digest_makerec(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; -} - const char* digest_challenge(ha_buffer_t* buf, const char* nonce_str, const char* realm, const char* domains, int stale) { @@ -127,16 +108,16 @@ const char* digest_challenge(ha_buffer_t* buf, const char* nonce_str, * limitations under the License. */ -int digest_parse(char* header, ha_buffer_t* buf, digest_header_t* rec, - unsigned char* nonce) +int digest_parse(const char* head, ha_buffer_t* buf, digest_header_t* rec) { char next; char* key; char* value; + char* header; - ASSERT(header && buf && rec); + ASSERT(head && buf && rec); - header = ha_bufcpy(buf, header); + header = ha_bufcpy(buf, head); if(!header) return HA_CRITERROR; @@ -230,93 +211,78 @@ int digest_parse(char* header, ha_buffer_t* buf, digest_header_t* rec, } } - if(nonce) - { - memset(nonce, 0, DIGEST_NONCE_LEN); + return HA_OK; +} - if(rec->nonce) - { - size_t len = DIGEST_NONCE_LEN; - void* d = ha_bufdechex(buf, rec->nonce, &len); +int digest_check(digest_context_t* dg, const ha_context_t* opts, ha_buffer_t* buf) +{ + int r; - if(d && len == DIGEST_NONCE_LEN) - memcpy(nonce, d, DIGEST_NONCE_LEN); - } - } + r = digest_pre_check(dg, opts, buf); + if(r == HA_OK) + r = digest_complete_check(dg, buf); - return HA_OK; + return r; } -int digest_check(digest_header_t* dg, digest_record_t* rec, const ha_context_t* ctx, - ha_buffer_t* buf, const char* method, const char* uri) +int digest_pre_check(digest_context_t* dg, const ha_context_t* opts, ha_buffer_t* buf) { - unsigned char hash[MD5_LEN]; - md5_ctx_t md5; - const char* digest; - const char* t; - - ASSERT(ctx && method && buf && dg && rec); + ASSERT(buf && buf && dg); /* TODO: This stuff should probably go into bd.c */ /* Check for digest */ - if(!dg->digest || !dg->digest[0]) + if(!dg->client.digest || !dg->client.digest[0]) { ha_messagex(NULL, LOG_WARNING, "digest response missing digest"); return HA_BADREQ; } /* Username */ - if(!dg->username || !dg->username[0]) + if(!dg->client.username || !dg->client.username[0]) { ha_messagex(NULL, LOG_WARNING, "digest response missing username"); return HA_BADREQ; } - if(md5_strcmp(rec->userhash, dg->username) != 0) - { - ha_messagex(NULL, LOG_ERR, "digest response contains invalid username"); - return HA_FALSE; - } - /* The realm */ - if(!dg->realm) + if(!dg->client.realm) { ha_messagex(NULL, LOG_WARNING, "digest response contains missing realm"); return HA_BADREQ; } - if(strcmp(dg->realm, ctx->realm) != 0) + if(strcmp(dg->client.realm, opts->realm) != 0) { - ha_messagex(NULL, LOG_ERR, "digest response contains invalid realm: %s", dg->realm); + ha_messagex(NULL, LOG_ERR, "digest response contains invalid realm: %s", dg->client.realm); return HA_FALSE; } /* Components in the new RFC */ - if(dg->qop) + if(dg->client.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) + if(strcmp(dg->client.qop, "auth") != 0) { ha_messagex(NULL, LOG_WARNING, "digest response contains unknown or unsupported qop: '%s'", - dg->qop ? dg->qop : ""); + dg->client.qop ? dg->client.qop : ""); return HA_BADREQ; } /* The cnonce */ - if(!dg->cnonce || !dg->cnonce[0]) + if(!dg->client.cnonce || !dg->client.cnonce[0]) { ha_messagex(NULL, LOG_WARNING, "digest response is missing cnonce value"); return HA_BADREQ; } - if(!ctx->digest_ignorenc) + if(!opts->digest_ignorenc) { /* The nonce count */ - if(!dg->nc || !dg->nc[0]) + if(!dg->client.nc || !dg->client.nc[0]) { ha_messagex(NULL, LOG_WARNING, "digest response is missing nc value"); return HA_BADREQ; @@ -326,22 +292,22 @@ int digest_check(digest_header_t* dg, digest_record_t* rec, const ha_context_t* else { char* e; - long nc = strtol(dg->nc, &e, 16); + long nc = strtol(dg->client.nc, &e, 16); if(*e) { - ha_messagex(NULL, LOG_ERR, "digest response has invalid nc value: %s", dg->nc); + ha_messagex(NULL, LOG_ERR, "digest response has invalid nc value: %s", dg->client.nc); return HA_BADREQ; } /* If we didn't a nc then save it away */ - if(!*e && rec->nc == 0) - rec->nc = nc; + if(!*e && dg->server_nc == 0) + dg->server_nc = nc; - if(*e || nc != rec->nc) + if(*e || nc != dg->server_nc) { ha_messagex(NULL, LOG_WARNING, "digest response has wrong nc value. " - "possible replay attack: %s", dg->nc); + "possible replay attack: %s", dg->client.nc); return HA_FALSE; } } @@ -349,47 +315,49 @@ int digest_check(digest_header_t* dg, digest_record_t* rec, const ha_context_t* } /* The algorithm */ - if(dg->algorithm && strcasecmp(dg->algorithm, "MD5") != 0) + if(dg->client.algorithm && strcasecmp(dg->client.algorithm, "MD5") != 0) { ha_messagex(NULL, LOG_WARNING, "digest response contains unknown or unsupported algorithm: '%s'", - dg->algorithm ? dg->algorithm : ""); + dg->client.algorithm ? dg->client.algorithm : ""); return HA_BADREQ; } /* Request URI */ - if(!dg->uri) + if(!dg->client.uri) { ha_messagex(NULL, LOG_WARNING, "digest response is missing uri"); return HA_BADREQ; } - if(!ctx->digest_ignoreuri && strcmp(dg->uri, uri) != 0) + if(!opts->digest_ignoreuri && strcmp(dg->client.uri, dg->server_uri) != 0) { ha_uri_t d_uri; ha_uri_t s_uri; - if(ha_uriparse(buf, dg->uri, &d_uri) < 0) + if(ha_uriparse(buf, dg->client.uri, &d_uri) < 0) { if(ha_buferr(buf)) return HA_CRITERROR; - ha_messagex(NULL, LOG_WARNING, "digest response constains invalid uri: %s", dg->uri); + ha_messagex(NULL, LOG_WARNING, "digest response constains invalid uri: %s", dg->client.uri); return HA_BADREQ; } - if(ha_uriparse(buf, uri, &s_uri) < 0) + ASSERT(dg->server_uri); + + if(ha_uriparse(buf, dg->server_uri, &s_uri) < 0) { if(ha_buferr(buf)) return HA_CRITERROR; - ha_messagex(NULL, LOG_ERR, "server sent us an invalid uri"); + ha_messagex(NULL, LOG_ERR, "server sent us an invalid uri: %s", dg->server_uri); return HA_BADREQ; } if(ha_uricmp(&d_uri, &s_uri) != 0) { ha_messagex(NULL, LOG_ERR, "digest response contains wrong uri: %s " - "(should be %s)", dg->uri, uri); + "(should be %s)", dg->client.uri, dg->server_uri); return HA_FALSE; } } @@ -402,21 +370,36 @@ int digest_check(digest_header_t* dg, digest_record_t* rec, const ha_context_t* * if it's used there. */ + return HA_OK; +} + + +int digest_complete_check(digest_context_t* dg, ha_buffer_t* buf) +{ + unsigned char hash[MD5_LEN]; + md5_ctx_t md5; + const char* t; + const char* digest; + + ASSERT(dg && buf); + /* * Now we validate the digest response */ /* Encode ha1 */ - t = ha_bufenchex(buf, rec->ha1, MD5_LEN); + t = ha_bufenchex(buf, dg->ha1, MD5_LEN); if(t == NULL) return HA_CRITERROR; + ASSERT(dg->server_method); + /* Encode ha2 */ md5_init(&md5); - md5_update(&md5, method, strlen(method)); + md5_update(&md5, dg->server_method, strlen(dg->server_method)); md5_update(&md5, ":", 1); - md5_update(&md5, dg->uri, strlen(dg->uri)); + md5_update(&md5, dg->client.uri, strlen(dg->client.uri)); md5_final(hash, &md5); ha_bufenchex(buf, hash, MD5_LEN); @@ -424,14 +407,15 @@ int digest_check(digest_header_t* dg, digest_record_t* rec, const ha_context_t* if(!ha_bufdata(buf)) return HA_CRITERROR; + ASSERT(dg->client.nonce); /* Old style digest (RFC 2069) */ - if(!dg->qop) + if(!dg->client.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, dg->client.nonce, strlen(dg->client.nonce)); /* nonce */ md5_update(&md5, ":", 1); md5_update(&md5, ha_bufdata(buf), MD5_LEN * 2); /* ha1 */ md5_final(hash, &md5); @@ -440,16 +424,20 @@ int digest_check(digest_header_t* dg, digest_record_t* rec, const ha_context_t* /* New style 'auth' digest (RFC 2617) */ else { + ASSERT(dg->client.nc); + ASSERT(dg->client.cnonce); + ASSERT(dg->client.qop); + md5_init(&md5); - md5_update(&md5, t, MD5_LEN * 2); /* ha1 */ + md5_update(&md5, t, MD5_LEN * 2); /* ha1 */ md5_update(&md5, ":", 1); - md5_update(&md5, dg->nonce, strlen(dg->nonce)); /* nonce */ + md5_update(&md5, dg->client.nonce, strlen(dg->client.nonce)); /* nonce */ md5_update(&md5, ":", 1); - md5_update(&md5, dg->nc, strlen(dg->nc)); /* nc */ + md5_update(&md5, dg->client.nc, strlen(dg->client.nc)); /* nc */ md5_update(&md5, ":", 1); - md5_update(&md5, dg->cnonce, strlen(dg->cnonce)); /* cnonce */ + md5_update(&md5, dg->client.cnonce, strlen(dg->client.cnonce)); /* cnonce */ md5_update(&md5, ":", 1); - md5_update(&md5, dg->qop, strlen(dg->qop)); /* qop */ + md5_update(&md5, dg->client.qop, strlen(dg->client.qop)); /* qop */ md5_update(&md5, ":", 1); md5_update(&md5, ha_bufdata(buf), MD5_LEN * 2); /* ha2 */ md5_final(hash, &md5); @@ -461,24 +449,23 @@ int digest_check(digest_header_t* dg, digest_record_t* rec, const ha_context_t* if(digest == NULL) return HA_CRITERROR; - if(strcasecmp(dg->digest, digest) != 0) - { - ha_messagex(NULL, LOG_WARNING, "digest authentication failed for user: %s", dg->username); + ASSERT(dg->client.digest); + + if(strcasecmp(dg->client.digest, digest) != 0) 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) +const char* digest_respond(digest_context_t* dg, ha_buffer_t* buf, + unsigned char* next) { unsigned char hash[MD5_LEN]; md5_ctx_t md5; const char* nextnonce = NULL; const char* t; - ASSERT(buf && dg && rec); + ASSERT(buf && dg); /* This makes a new buffer */ ha_bufcpy(buf, ""); @@ -492,7 +479,7 @@ const char* digest_respond(ha_buffer_t* buf, digest_header_t* dg, } /* For older clients RFC 2069 */ - if(!dg->qop) + if(!dg->client.qop) { if(nextnonce) ha_bufmcat(buf, "nextnonce=\"", nextnonce, "\"", NULL); @@ -503,15 +490,17 @@ const char* digest_respond(ha_buffer_t* buf, digest_header_t* dg, /* Otherwise we do the whole song and dance */ /* Encode ha1 */ - t = ha_bufenchex(buf, rec->ha1, MD5_LEN); + t = ha_bufenchex(buf, dg->ha1, MD5_LEN); if(t == NULL) return NULL; + ASSERT(dg->client.uri); + /* Encode ha2 */ md5_init(&md5); md5_update(&md5, ":", 1); - md5_update(&md5, dg->uri, strlen(dg->uri)); + md5_update(&md5, dg->client.uri, strlen(dg->client.uri)); md5_final(hash, &md5); ha_bufenchex(buf, hash, MD5_LEN); @@ -519,19 +508,24 @@ const char* digest_respond(ha_buffer_t* buf, digest_header_t* dg, if(!ha_bufdata(buf)) return NULL; + ASSERT(dg->client.nonce); + ASSERT(dg->client.nc); + ASSERT(dg->client.cnonce); + ASSERT(dg->client.qop); + /* New style 'auth' digest (RFC 2617) */ md5_init(&md5); - md5_update(&md5, t, MD5_LEN * 2); /* ha1 */ + md5_update(&md5, t, MD5_LEN * 2); /* ha1 */ md5_update(&md5, ":", 1); - md5_update(&md5, dg->nonce, strlen(dg->nonce)); /* nonce */ + md5_update(&md5, dg->client.nonce, strlen(dg->client.nonce)); /* nonce */ md5_update(&md5, ":", 1); - md5_update(&md5, dg->nc, strlen(dg->nc)); /* nc */ + md5_update(&md5, dg->client.nc, strlen(dg->client.nc)); /* nc */ md5_update(&md5, ":", 1); - md5_update(&md5, dg->cnonce, strlen(dg->cnonce)); /* cnonce */ + md5_update(&md5, dg->client.cnonce, strlen(dg->client.cnonce)); /* cnonce */ md5_update(&md5, ":", 1); - md5_update(&md5, dg->qop, strlen(dg->qop)); /* qop */ + md5_update(&md5, dg->client.qop, strlen(dg->client.qop)); /* qop */ md5_update(&md5, ":", 1); - md5_update(&md5, ha_bufdata(buf), MD5_LEN * 2); /* ha2 */ + md5_update(&md5, ha_bufdata(buf), MD5_LEN * 2); /* ha2 */ md5_final(hash, &md5); /* Encode the digest */ @@ -541,9 +535,9 @@ const char* digest_respond(ha_buffer_t* buf, digest_header_t* dg, return NULL; ha_bufmcat(buf, "rspauth=\"", t, "\"", - ", qop=", dg->qop, - ", nc=", dg->nc, - ", cnonce=\"", dg->cnonce, "\"", NULL); + ", qop=", dg->client.qop, + ", nc=", dg->client.nc, + ", cnonce=\"", dg->client.cnonce, "\"", NULL); if(nextnonce) { |