diff --git a/README.md b/README.md index e992a7a4b..930ea1afd 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,7 @@ legend: * [RFC 6455](https://tools.ietf.org/html/rfc6455) - The WebSocket Protocol * [RFC 7159](https://tools.ietf.org/html/rfc7159) - JavaScript Object Notation (JSON) * [RFC 7350](https://tools.ietf.org/html/rfc7350) - DTLS as Transport for STUN +* [RFC 7616](https://tools.ietf.org/html/rfc7616) - HTTP Digest Access Authentication * [RFC 7714](https://tools.ietf.org/html/rfc7714) - AES-GCM Authenticated Encryption in SRTP diff --git a/include/re_httpauth.h b/include/re_httpauth.h index 0fce26dbd..758a90862 100644 --- a/include/re_httpauth.h +++ b/include/re_httpauth.h @@ -35,6 +35,28 @@ struct httpauth_digest_chall { struct pl userhash; }; +struct httpauth_digest_enc_resp { + char *realm; + char *nonce; + char *opaque; + char *algorithm; + char *qop; + + /* response specific */ + char *response; + char *username; + char *username_star; + char *uri; + uint32_t cnonce; + uint32_t nc; + + /* optional */ + char *charset; + bool userhash; + void (*hashh)(const uint8_t *, size_t, uint8_t *); + size_t hash_length; +}; + /** HTTP Digest response */ struct httpauth_digest_resp { struct pl realm; @@ -81,6 +103,21 @@ int httpauth_digest_response_encode(const struct httpauth_digest_resp *resp, struct mbuf *mb); +int httpauth_digest_response_print(struct re_printf *pf, + const struct httpauth_digest_enc_resp *resp); +int httpauth_digest_response_set_cnonce(struct httpauth_digest_enc_resp *resp, + const struct httpauth_digest_chall *chall, const struct pl *method, + const char *user, const char *passwd, const char *entitybody, + const uint32_t cnonce, const uint32_t nc_); +int httpauth_digest_response(struct httpauth_digest_enc_resp **presp, + const struct httpauth_digest_chall *chall, const struct pl *method, + const char *uri, const char *user, const char *passwd, const char *qop, + const char *entitybody); +int httpauth_digest_response_full(struct httpauth_digest_enc_resp **presp, + const struct httpauth_digest_chall *chall, const struct pl *method, + const char *uri, const char *user, const char *passwd, const char *qop, + const char *entitybody, const char *charset, const bool userhash); + int httpauth_digest_chall_req_print(struct re_printf *pf, const struct httpauth_digest_chall_req *req); int httpauth_digest_chall_request(struct httpauth_digest_chall_req **preq, diff --git a/src/httpauth/digest.c b/src/httpauth/digest.c index 1358fd450..5bd6c3a25 100644 --- a/src/httpauth/digest.c +++ b/src/httpauth/digest.c @@ -20,17 +20,28 @@ typedef void (digest_decode_h)(const struct pl *name, const struct pl *val, void *arg); -static const struct pl param_algorithm = PL("algorithm"); -static const struct pl param_cnonce = PL("cnonce"); -static const struct pl param_nc = PL("nc"); +/* General fields */ +static const struct pl param_realm = PL("realm"); static const struct pl param_nonce = PL("nonce"); static const struct pl param_opaque = PL("opaque"); +static const struct pl param_algorithm = PL("algorithm"); static const struct pl param_qop = PL("qop"); -static const struct pl param_realm = PL("realm"); +static const struct pl param_stale = PL("stale"); + +/* Challenge fields */ +static const struct pl param_domain = PL("domain"); + +/* Response fields */ static const struct pl param_response = PL("response"); static const struct pl param_uri = PL("uri"); static const struct pl param_username = PL("username"); -static const struct pl param_stale = PL("stale"); +/* static const struct pl param_userstar = PL("username*"); future use */ +static const struct pl param_cnonce = PL("cnonce"); +static const struct pl param_nc = PL("nc"); + +/* Optional fields */ +static const struct pl param_charset = PL("charset"); +static const struct pl param_userhash = PL("userhash"); static void challenge_decode(const struct pl *name, const struct pl *val, @@ -40,6 +51,8 @@ static void challenge_decode(const struct pl *name, const struct pl *val, if (!pl_casecmp(name, ¶m_realm)) chall->realm = *val; + else if (!pl_casecmp(name, ¶m_domain)) + chall->domain = *val; else if (!pl_casecmp(name, ¶m_nonce)) chall->nonce = *val; else if (!pl_casecmp(name, ¶m_opaque)) @@ -50,6 +63,10 @@ static void challenge_decode(const struct pl *name, const struct pl *val, chall->algorithm = *val; else if (!pl_casecmp(name, ¶m_qop)) chall->qop = *val; + else if (!pl_casecmp(name, ¶m_charset)) + chall->charset = *val; + else if (!pl_casecmp(name, ¶m_userhash)) + chall->userhash = *val; } @@ -591,3 +608,366 @@ int httpauth_digest_chall_request_full(struct httpauth_digest_chall_req **preq, return err; } + + +static void httpauth_digest_response_destructor(void *arg) +{ + struct httpauth_digest_enc_resp *resp = arg; + + mem_deref(resp->realm); + mem_deref(resp->nonce); + mem_deref(resp->opaque); + mem_deref(resp->algorithm); + mem_deref(resp->qop); + mem_deref(resp->response); + mem_deref(resp->username); + mem_deref(resp->username_star); + mem_deref(resp->uri); + mem_deref(resp->charset); +} + + +static int digest_response(struct httpauth_digest_enc_resp *resp, + const struct httpauth_digest_chall *chall, + const struct pl *method, const char *user, + const char *passwd, const char *entitybody) +{ + uint8_t *hash1 = NULL; + uint8_t *hash2 = NULL; + struct mbuf *mb = NULL; + size_t hashstringl = (resp->hash_length * 2) + 1; + int err = 0, n = 0; + + if (!resp || !resp->hashh) + return EINVAL; + + mb = mbuf_alloc(str_len(user) + str_len(passwd) + chall->realm.l + 2); + if (!mb) + return ENOMEM; + + hash1 = mem_zalloc(resp->hash_length, NULL); + hash2 = mem_zalloc(resp->hash_length, NULL); + if (!resp->response) + resp->response = mem_zalloc(hashstringl, NULL); + + if (!resp->response || !hash1 || !hash2) { + err = ENOMEM; + goto out; + } + + /* HASH A2 */ + if (str_isset(resp->qop) && str_str(resp->qop, "auth-int")) { + if (!entitybody || str_casecmp(entitybody, "") == 0) { + resp->hashh((uint8_t *)"", 0, hash1); + } + else { + resp->hashh((uint8_t *)entitybody, + str_len(entitybody), hash1); + } + + err = mbuf_printf(mb, "%r:%s:%w", + method, resp->uri, hash1, resp->hash_length); + } + else { + err = mbuf_printf(mb, "%r:%s", method, resp->uri); + } + + if (err) + goto out; + + resp->hashh(mb->buf, mb->end, hash2); + mbuf_rewind(mb); + + /* HASH A1 */ + if (resp->userhash) { + if (!resp->username) + resp->username = mem_zalloc(hashstringl, NULL); + + if (!resp->username) { + err = ENOMEM; + goto out; + } + + err = mbuf_printf(mb, "%s:%s", user, resp->realm); + if (err) + goto out; + + resp->hashh(mb->buf, mb->end, hash1); + n = re_snprintf(resp->username, hashstringl, "%w", + hash1, hashstringl); + if (n == -1 || n != (int)hashstringl -1) { + err = ERANGE; + goto out; + } + + mbuf_rewind(mb); + err = mbuf_printf(mb, "%w:%s:%s", + hash1, resp->hash_length, resp->realm, passwd); + } + else { + err = mbuf_printf(mb, "%s:%s:%s", user, resp->realm, passwd); + resp->username = mem_deref(resp->username); + err |= str_dup(&resp->username, user); + } + + if (err) + goto out; + + resp->hashh(mb->buf, mb->end, hash1); + mbuf_rewind(mb); + + if (str_str(resp->algorithm, "-sess")) { + err = mbuf_printf(mb, "%w:%s:%08x", + hash1, resp->hash_length, resp->nonce, resp->cnonce); + if (err) + goto out; + + resp->hashh(mb->buf, mb->end, hash1); + mbuf_rewind(mb); + } + + /* DIGEST */ + if (str_isset(resp->qop)) { + err = mbuf_printf(mb, "%w:%s:%08x:%08x:%s:%w", + hash1, resp->hash_length, resp->nonce, resp->nc, + resp->cnonce, resp->qop, hash2, resp->hash_length); + } + else { + err = mbuf_printf(mb, "%w:%s:%w", hash1, resp->hash_length, + resp->nonce, hash2, resp->hash_length); + } + + if (err) + goto out; + + resp->hashh(mb->buf, mb->end, hash1); + n = re_snprintf(resp->response, hashstringl, "%w", + hash1, resp->hash_length); + if (n == -1 || n != (int)hashstringl - 1) + err = ERANGE; + +out: + mem_deref(mb); + mem_deref(hash1); + mem_deref(hash2); + + return err; +} + + +/** + * Prints / encodes an HTTP digest response + * + * @param pf Re_printf object + * @param resp Response to print + * + * @return 0 if success, otherwise errorcode + */ +int httpauth_digest_response_print(struct re_printf *pf, + const struct httpauth_digest_enc_resp *resp) +{ + int err = 0; + + if (!resp) + return EINVAL; + + /* historical reason quoted strings: */ + /* username, realm, nonce, uri, */ + /* response, cnonce, opaque */ + /* historical reason unquoted strings: */ + /* qop, algorithm, nc */ + err = re_hprintf(pf, "Digest realm=\"%s\"," + " nonce=\"%s\", username=\"%s\", uri=\"%s\"," + " response=\"%s\"", + resp->realm, resp->nonce, resp->username, + resp->uri, resp->response); + + if (str_isset(resp->opaque)) + err |= re_hprintf(pf, ", opaque=\"%s\"", resp->opaque); + if (str_isset(resp->algorithm)) + err |= re_hprintf(pf, ", algorithm=%s", resp->algorithm); + if (str_isset(resp->qop)) + err |= re_hprintf(pf, ", qop=%s, cnonce=\"%08x\", nc=\"%08x\"", + resp->qop, resp->cnonce, resp->nc); + + if (resp->userhash) + err |= re_hprintf(pf, ", userhash=true"); + if (str_isset(resp->charset)) + err |= re_hprintf(pf, ", charset=\"%s\"", resp->charset); + + return err; +} + + +/** + * Set cnonce and nc and recalculate the response value. + * This function should be used only for unit tests + * + * @param resp Httpauth_new_digest_response object pointer + * @param chall Received and decoded digest challenge + * @param method Used method + * @param user Username + * @param passwd User password + * @param entitybody Entitybody if qop=auth-int + * @param cnonce Cnonce + * @param nonce_counter Nonce counter + * + * @return 0 if success, otherwise errorcode + */ +int httpauth_digest_response_set_cnonce(struct httpauth_digest_enc_resp *resp, + const struct httpauth_digest_chall *chall, const struct pl *method, + const char *user, const char *passwd, const char *entitybody, + uint32_t cnonce, uint32_t nonce_counter) +{ + if (!resp || !chall || !method || !passwd) + return EINVAL; + + resp->cnonce = cnonce; + resp->nc = nonce_counter; + + return digest_response(resp, chall, method, + user, passwd, entitybody); +} + + +/** + * Create a digest authentication response + * + * @param presp Httpauth_new_digest_response object pointer + * @param chall Received and decoded digest challenge + * @param method Used method + * @param uri Accessed uri + * @param user Username + * @param passwd User password + * @param qop Quality of protection + * @param entitybody Entitybody if qop=auth-int + * + * @return 0 if success, otherwise errorcode + */ +int httpauth_digest_response(struct httpauth_digest_enc_resp **presp, + const struct httpauth_digest_chall *chall, const struct pl *method, + const char *uri, const char *user, const char *passwd, const char *qop, + const char *entitybody) +{ + return httpauth_digest_response_full(presp, chall, method, uri, + user, passwd, qop, entitybody, NULL, false); +} + + +/** + * Create a full configurable digest authentication response + * + * @param presp Httpauth_new_digest_response object pointer + * @param chall Received and decoded digest challenge + * @param method Used method + * @param uri Accessed uri + * @param user Username + * @param passwd User password + * @param qop Quality of protection + * @param entitybody Entitybody if qop=auth-int + * @param charset Used character set (only UTF-8 or NULL allowed) + * @param userhash Enable hashed usernames + * + * @return 0 if success, otherwise errorcode + */ +int httpauth_digest_response_full(struct httpauth_digest_enc_resp **presp, + const struct httpauth_digest_chall *chall, const struct pl *method, + const char *uri, const char *user, const char *passwd, const char *qop, + const char *entitybody, const char *charset, const bool userhash) +{ + struct httpauth_digest_enc_resp *resp = NULL; + int err = 0; + + if (!presp || !chall || !method || !uri || !user || !passwd) + return EINVAL; + + resp = mem_zalloc(sizeof(*resp), httpauth_digest_response_destructor); + if (!resp) { + return ENOMEM; + } + + /* create cnonce & nonce count */ + resp->cnonce = rand_u32(); + resp->nc = nc++; + + /* copy fields */ + err = pl_strdup(&resp->realm, &chall->realm); + err |= pl_strdup(&resp->nonce, &chall->nonce); + err |= pl_strdup(&resp->opaque, &chall->opaque); + if (err) { + goto out; + } + + /* userhash supported by server */ + if (userhash && (pl_strcasecmp(&chall->userhash, "true") == 0)) + resp->userhash = true; + + /* only allowed qop Nothing, "auth" or "auth-int" */ + if (str_isset(qop) && (str_casecmp(qop, "auth")) && + (str_casecmp(qop, "auth-int"))) { + err = EPROTONOSUPPORT; + goto out; + } + + /* qop supported by server */ + if (pl_isset(&chall->qop) && str_isset(qop) && + pl_strstr(&chall->qop, qop)) { + err = str_dup(&resp->qop, qop); + if (err) + goto out; + } + + /* only allowed charset Nothing or "UTF-8" */ + if (str_isset(charset) && str_casecmp(charset, "UTF-8")) { + err = EPROTONOSUPPORT; + goto out; + } + + /* charset supported by server */ + if (pl_isset(&chall->charset) && str_isset(charset) && + pl_strstr(&chall->charset, charset) == 0) { + err = str_dup(&resp->charset, charset); + if (err) + goto out; + } + + err = str_dup(&resp->uri, uri); + if (err) + goto out; + + if (pl_strstr(&chall->algorithm, "SHA-256-sess")) { + resp->hashh = &sha256; + resp->hash_length = SHA256_DIGEST_LENGTH; + err = str_dup(&resp->algorithm, "SHA-256-sess"); + } + else if (pl_strstr(&chall->algorithm, "SHA-256")) { + resp->hashh = &sha256; + resp->hash_length = SHA256_DIGEST_LENGTH; + err = str_dup(&resp->algorithm, "SHA-256"); + } + else if (pl_strstr(&chall->algorithm, "MD5-sess")) { + resp->hashh = &md5; + resp->hash_length = MD5_SIZE; + err = str_dup(&resp->algorithm, "MD5-sess"); + } + else if (!pl_isset(&chall->algorithm) || + pl_strstr(&chall->algorithm, "MD5")) { + resp->hashh = &md5; + resp->hash_length = MD5_SIZE; + err = str_dup(&resp->algorithm, "MD5"); + } + else { + err = EPROTONOSUPPORT; + goto out; + } + + err = digest_response(resp, chall, method, user, passwd, entitybody); + +out: + if (err) + mem_deref(resp); + else + *presp = resp; + + return err; +} diff --git a/test/httpauth.c b/test/httpauth.c index 4c01a981a..cc0c91ec4 100644 --- a/test/httpauth.c +++ b/test/httpauth.c @@ -45,6 +45,9 @@ static bool chall_equal(const struct httpauth_digest_chall *a, err |= pl_equal("stale", &a->stale, &b->stale); err |= pl_equal("algorithm", &a->algorithm, &b->algorithm); err |= pl_equal("qop", &a->qop, &b->qop); + err |= pl_equal("domain", &a->domain, &b->domain); + err |= pl_equal("charset", &a->charset, &b->charset); + err |= pl_equal("userhash", &a->userhash, &b->userhash); return err == 0; } @@ -371,38 +374,23 @@ int test_httpauth_digest_request(void) }, { "Digest realm=\"/my/home\", qop=\"auth\"," - " nonce=\"%s\", algorithm=SHA256", + " nonce=\"%s\", algorithm=SHA-256", "/my/home", NULL, "localhost:5060", NULL, false, - "SHA256", "auth", NULL, false, 0 + "SHA-256", "auth", NULL, false, 0 }, { "Digest realm=\"/my/home\", qop=\"auth\"," - " nonce=\"%s\", algorithm=SHA256-sess, stale=true", + " nonce=\"%s\", algorithm=SHA-256-sess, stale=true", "/my/home", NULL, "localhost:5060", NULL, true, - "SHA256-sess", "auth", NULL, false, 0 + "SHA-256-sess", "auth", NULL, false, 0 }, { "Digest realm=\"/my/home\", qop=\"auth\"," - " nonce=\"%s\", algorithm=SHA1," - " stale=true, userhash=true", - "/my/home", NULL, "localhost:5060", NULL, true, - "SHA1", "auth", NULL, true, 0 - }, - { - "Digest realm=\"/my/home\", qop=\"auth\"," - " nonce=\"%s\", algorithm=SHA1-sess," + " nonce=\"%s\", algorithm=SHA-256," " domain=\"example.com\", stale=true," " charset=\"UTF-8\", userhash=true", "/my/home", "example.com", "localhost:5060", NULL, - true, "SHA1-sess", "auth", "UTF-8", true, 0 - }, - { - "Digest realm=\"/my/home\", qop=\"auth\"," - " nonce=\"%s\", algorithm=SHA256," - " domain=\"example.com\", stale=true," - " charset=\"UTF-8\", userhash=true", - "/my/home", "example.com", "localhost:5060", NULL, - true, "SHA256", "auth", "UTF-8", true, 0 + true, "SHA-256", "auth", "UTF-8", true, 0 }, { "Digest realm=\"/my/home\", qop=\"auth-int\"," @@ -412,14 +400,6 @@ int test_httpauth_digest_request(void) "/my/home", "example.com", "localhost:5060", NULL, true, "MD5-sess", "auth-int", "UTF-8", true, 0 }, - { - "Digest realm=\"/my/home\", qop=\"auth-int\"," - " nonce=\"%s\", algorithm=SHA1-sess," - " domain=\"example.com\", stale=true," - " charset=\"UTF-8\", userhash=true", - "/my/home", "example.com", "213579023", NULL, - true, "SHA1-sess", "auth-int", "UTF-8", true, 0 - }, { "Digest realm=\"/my/home\", qop=\"auth-int\"," " nonce=\"%s\", algorithm=MD5," @@ -431,7 +411,7 @@ int test_httpauth_digest_request(void) }; int err = 0; - for (unsigned int i = 0; i < RE_ARRAY_SIZE(testv); i++) { + for (size_t i = 0; i < RE_ARRAY_SIZE(testv); i++) { struct httpauth_digest_chall_req *req = NULL; struct mbuf *mb_refval = NULL; struct mbuf *mb_printed = NULL; @@ -504,3 +484,181 @@ int test_httpauth_digest_request(void) return err; } + + +int test_httpauth_digest_response(void) +{ + static const struct { + const struct httpauth_digest_chall chall; + const char *user; + const char *passwd; + const char *qop; + const struct pl method; + const char *uri; + const char *entitybody; + const char *precalc_digest; + const char *resp_hval; + } testv [] = { + { + { + PL("/my/home"), + PL("b5c64f319d37323ac652b77012817ccaa" + "6e9a7e4e7563155f1f9556414dd4615"), + PL("324DF3428BCF42D29A"), PL_INIT, + PL("MD5"), PL("auth"), PL_INIT, PL_INIT, + PL_INIT + }, + "retest", "sec_pwd_retest", "auth", PL("GET"), + "example.com/my/home/something", NULL, + "88f41f7227700e07d0d65256714a5a1a", + + "Digest realm=\"/my/home\"," + " nonce=\"b5c64f319d37323ac652b77012817ccaa6e" + "9a7e4e7563155f1f9556414dd4615\"," + " username=\"retest\"," + " uri=\"example.com/my/home/something\"," + " response=\"88f41f7227700e07d0d65256714a5a1a\"," + " opaque=\"324DF3428BCF42D29A\", algorithm=MD5," + " qop=auth, cnonce=\"deadbeef\", nc=\"00000001\"", + }, + { + { + PL("/my/home"), + PL("b5c64f319d37323ac652b77012817ccaa" + "6e9a7e4e7563155f1f9556414dd4615"), + PL("324DF3428BCF42D29A"), PL_INIT, + PL("SHA-256"), PL("auth"), PL_INIT, PL_INIT, + PL_INIT + }, + "retest", "sec_pwd_retest", "auth", PL("GET"), + "example.com/my/home/something", NULL, + "c22b56ce81bbb59570f0fbbc0ba27210dbbfcb2b23fe" + "a371d214722f319dc41c", + + "Digest realm=\"/my/home\"," + " nonce=\"b5c64f319d37323ac652b77012817ccaa6e" + "9a7e4e7563155f1f9556414dd4615\", username=\"retest\"," + " uri=\"example.com/my/home/something\"," + " response=\"c22b56ce81bbb59570f0fbbc0ba27210dbbfcb2b2" + "3fea371d214722f319dc41c\"," + " opaque=\"324DF3428BCF42D29A\", algorithm=SHA-256," + " qop=auth, cnonce=\"deadbeef\", nc=\"00000001\"", + }, + { + { + PL("/my/home"), + PL("b5c64f319d37323ac652b77012817ccaa" + "6e9a7e4e7563155f1f9556414dd4615"), + PL("324DF3428BCF42D29A"), PL_INIT, + PL("MD5-sess"), PL("auth"), PL_INIT, PL_INIT, + PL_INIT + }, + "retest", "sec_pwd_retest", "auth", PL("GET"), + "example.com/my/home/something", NULL, + "1e79ac7105a4fdf416aaacfc50349110", + + "Digest realm=\"/my/home\"," + " nonce=\"b5c64f319d37323ac652b77012817ccaa6e9a7e4e756" + "3155f1f9556414dd4615\", username=\"retest\"," + " uri=\"example.com/my/home/something\"," + " response=\"1e79ac7105a4fdf416aaacfc50349110\"," + " opaque=\"324DF3428BCF42D29A\", algorithm=MD5-sess," + " qop=auth, cnonce=\"deadbeef\", nc=\"00000001\"", + }, + { + { + PL("/my/home"), + PL("b5c64f319d37323ac652b77012817ccaa" + "6e9a7e4e7563155f1f9556414dd4615"), + PL("324DF3428BCF42D29A"), PL_INIT, + PL("SHA-256"), PL("auth-int"), PL_INIT, + PL_INIT, PL_INIT + }, + "retest", "sec_pwd_retest", "auth-int", PL("GET"), + "example.com/my/home/something", "", + "2c0746b7174441314164d8d9a980d8920732de32e163" + "03f0e6a82970230e79e4", + + "Digest realm=\"/my/home\"," + " nonce=\"b5c64f319d37323ac652b77012817ccaa6e9a7e4e756" + "3155f1f9556414dd4615\", username=\"retest\"," + " uri=\"example.com/my/home/something\"," + " response=\"2c0746b7174441314164d8d9a980d8920732de32e" + "16303f0e6a82970230e79e4\"," + " opaque=\"324DF3428BCF42D29A\", algorithm=SHA-256," + " qop=auth-int, cnonce=\"deadbeef\", nc=\"00000001\"", + }, + }; + + int err; + + for (size_t i = 0; i < RE_ARRAY_SIZE(testv); i++) { + struct httpauth_digest_enc_resp *resp = NULL; + struct mbuf *mb_printed = NULL; + + mb_printed = mbuf_alloc(512); + if (!mb_printed) { + err = ENOMEM; + goto for_out; + } + + err = httpauth_digest_response_full(&resp, &testv[i].chall, + &testv[i].method, testv[i].uri, testv[i].user, + testv[i].passwd, testv[i].qop, testv[i].entitybody, + NULL, false); + if (err == ENOMEM) { + goto for_out; + } + else if (err) { + DEBUG_WARNING("[%d]" + " Could not generate response %m\n", i, err); + goto for_out; + } + + err = httpauth_digest_response_set_cnonce(resp, + &testv[i].chall, &testv[i].method, testv[i].user, + testv[i].passwd, testv[i].entitybody, + 0xdeadbeef, 0x00000001); + if (err) { + DEBUG_WARNING("[%d]" + " Response recalculation failed %m\n", i, err); + goto for_out; + } + + err = mbuf_printf(mb_printed, "%H", + httpauth_digest_response_print, resp); + if (err) + goto for_out; + + if (str_casecmp(resp->response, + testv[i].precalc_digest) != 0) { + err = EINVAL; + DEBUG_WARNING("[%d]" + " Expected response %s, got %w\n", i, + testv[i].precalc_digest, + resp->response, resp->hash_length); + goto for_out; + } + + if (memcmp(testv[i].resp_hval, + mb_printed->buf, mb_printed->end)) { + err = EINVAL; + DEBUG_WARNING("[%d]" + " Expected header %s, got %b\n", + i, testv[i].resp_hval, + mb_printed->buf, mb_printed->end); + goto for_out; + } + + mb_printed = mem_deref (mb_printed); + resp = mem_deref(resp); + continue; + +for_out: + mb_printed = mem_deref (mb_printed); + resp = mem_deref(resp); + break; + } + + return err; +} diff --git a/test/test.c b/test/test.c index daec37bb1..039dd6894 100644 --- a/test/test.c +++ b/test/test.c @@ -122,6 +122,7 @@ static const struct test tests[] = { TEST(test_httpauth_resp), TEST(test_httpauth_basic_request), TEST(test_httpauth_digest_request), + TEST(test_httpauth_digest_response), TEST(test_ice_cand), TEST(test_ice_loop), TEST(test_json), diff --git a/test/test.h b/test/test.h index 1eb31009e..ee386b5e9 100644 --- a/test/test.h +++ b/test/test.h @@ -226,6 +226,7 @@ int test_httpauth_chall(void); int test_httpauth_resp(void); int test_httpauth_basic_request(void); int test_httpauth_digest_request(void); +int test_httpauth_digest_response(void); int test_ice_loop(void); int test_ice_cand(void); int test_json(void);