From bc549f5f946a18054be08c1aacef3bacf1cacb6a Mon Sep 17 00:00:00 2001 From: Christoph Huber Date: Thu, 13 Jul 2023 17:28:13 +0200 Subject: [PATCH] httpauth: basic challenge creation and verification functions (#875) * httpauth: new httpauth_basic challenge creation and verification * httpauth: httpauth_basic challenge test case * add missing doxygen parameter hval * httpauth: simplify printer function * httpauth: use mem_seccmp and clean memory afterwards --- include/re_httpauth.h | 15 +++++ src/httpauth/basic.c | 124 ++++++++++++++++++++++++++++++++++++++++++ test/httpauth.c | 112 ++++++++++++++++++++++++++++++++++++++ test/test.c | 1 + test/test.h | 1 + 5 files changed, 253 insertions(+) diff --git a/include/re_httpauth.h b/include/re_httpauth.h index 5be7ac7df..83f6d3b21 100644 --- a/include/re_httpauth.h +++ b/include/re_httpauth.h @@ -41,6 +41,13 @@ struct httpauth_basic { struct pl auth; }; +struct httpauth_basic_req { + char *realm; + + /* optional */ + char *charset; +}; + int httpauth_digest_challenge_decode(struct httpauth_digest_chall *chall, const struct pl *hval); @@ -61,3 +68,11 @@ int httpauth_basic_decode(struct httpauth_basic *basic, int httpauth_basic_make_response(struct httpauth_basic *basic, const char *user, const char *pwd); int httpauth_basic_encode(const struct httpauth_basic *basic, struct mbuf *mb); + + +int httpauth_basic_request_print(struct re_printf *pf, + const struct httpauth_basic_req *req); +int httpauth_basic_verify(const struct pl *hval, const char *user, + const char *passwd); +int httpauth_basic_request(struct httpauth_basic_req **preq, + const char *realm, const char *charset); diff --git a/src/httpauth/basic.c b/src/httpauth/basic.c index 39fcb92c4..264b3f46f 100644 --- a/src/httpauth/basic.c +++ b/src/httpauth/basic.c @@ -123,3 +123,127 @@ int httpauth_basic_encode(const struct httpauth_basic *basic, struct mbuf *mb) mbuf_set_pos(mb, 0); return 0; } + +/* HTTPAUTH BASIC REQUESTS*/ + +static void httpauth_basic_request_destructor(void *arg) +{ + struct httpauth_basic_req *req = arg; + + mem_deref(req->realm); + mem_deref(req->charset); +} + + +int httpauth_basic_request_print(struct re_printf *pf, + const struct httpauth_basic_req *req) +{ + int err = 0; + + if (!pf || !req) + return EINVAL; + + err = re_hprintf(pf, "Basic realm=\"%s\"", req->realm); + if (str_isset(req->charset)) + err |= re_hprintf(pf, ", charset=\"%s\"", req->charset); + + return err; +} + + +/** + * Verify received credentials + * + * @param hval http authentication header value containing the credentials + * @param user user name (may be an UTF-8 string) + * @param passwd user password (may be an UTF-8 string) + * + * @return 0 if successfully verified, otherwise errorcode + */ +int httpauth_basic_verify(const struct pl *hval, const char *user, + const char *passwd) +{ + struct pl b64c = PL_INIT; + struct mbuf *mb = NULL; + char *c = NULL; + size_t clen = 0; + int err = 0; + + if (!hval || !user || !passwd) + return EINVAL; + + mb = mbuf_alloc(str_len(user) + str_len(passwd) + 1); + if (!mb) + return ENOMEM; + + if (re_regex(hval->p, hval->l, "[ \t\r\n]*Basic[ \t\r\n]+[~ \t\r\n]*", + NULL, NULL, &b64c) || !pl_isset(&b64c)) { + err = EBADMSG; + goto out; + } + + clen = b64c.l; + c = mem_zalloc(clen, NULL); + if (!c) { + err = ENOMEM; + goto out; + } + + err = base64_decode(b64c.p, b64c.l, (uint8_t *) c, &clen); + if (err) + goto out; + + err = mbuf_printf(mb, "%s:%s", user, passwd); + if (err) + goto out; + + if (mem_seccmp(mb->buf, (uint8_t *)c, clen) != 0) + err = EACCES; + +out: + if (c) + mem_secclean(c, clen); + + if (mb) + mem_secclean(mb->buf, mb->size); + + mem_deref(c); + mem_deref(mb); + + return err; +} + + +/** + * Create a Basic Authentication Request + * + * @param preq httpauth_basic_req object ptr + * @param realm realm + * @param charset optional charset + * + * @return 0 if successful, otherwise errorcode + */ +int httpauth_basic_request(struct httpauth_basic_req **preq, + const char *realm, const char *charset) +{ + struct httpauth_basic_req *req = NULL; + int err = 0; + + if (!preq || !realm) + return EINVAL; + + req = mem_zalloc(sizeof(*req), httpauth_basic_request_destructor); + if (!req) + return ENOMEM; + + err = str_dup(&req->realm, realm); + if (str_isset(charset) && str_casecmp(charset, "UTF-8") == 0) + err |= str_dup(&req->charset, charset); + + if (err) + mem_deref(req); + else + *preq = req; + + return err; +} diff --git a/test/httpauth.c b/test/httpauth.c index a0afb53dc..18bed195c 100644 --- a/test/httpauth.c +++ b/test/httpauth.c @@ -217,3 +217,115 @@ int test_httpauth_resp(void) return err; } + + +int test_httpauth_basic_request(void) { + static const struct { + const char *hval; + struct pl hval_response; + const char *realm; + const char *charset; + const char *user; + const char *passwd; + int err; + int auth_err; + } testv[] = { + { + "Basic realm=\"/my/home\"", + PL("Basic cmV0ZXN0OnJldGVzdHBhc3N3ZA=="), + "/my/home", NULL, "retest", "retestpasswd", 0, 0 + }, + { + "Basic realm=\"/my/home\", charset=\"UTF-8\"", + PL("Basic cmV0ZXN0OnJldGVzdHBhc3N3ZOKCrA=="), + "/my/home", "UTF-8", "retest", + "retestpasswd\xe2\x82\xac", + 0, 0 + }, + { + "Basic realm=\"/my/home\"", + PL("Basic d3Jvbmc6Y3JlZGVudGlhbHM=="), "/my/home", + NULL, "retest", "retestpasswd", 0, EACCES + }, + }; + unsigned int i; + int err = 0; + + for (i = 0; i < RE_ARRAY_SIZE(testv); ++i) { + struct httpauth_basic_req *req = NULL; + struct mbuf *mb = NULL; + int terr = 0; + int tauth_err = 0; + + terr = httpauth_basic_request(&req, + testv[i].realm, testv[i].charset); + if (terr == ENOMEM) { + err = ENOMEM; + break; + } + else if (terr != testv[i].err) { + DEBUG_WARNING("basic req: expected error %d, got %m\n", + testv[i].err, terr); + err = terr; + break; + } + + if (str_casecmp(req->realm, testv[i].realm) != 0) { + DEBUG_WARNING("basic req: expected realm %s, got %s\n", + testv[i].realm, req->realm); + err = EBADMSG; + mem_deref(req); + break; + } + + if (testv[i].charset) { + if (str_casecmp(req->charset, testv[i].charset) != 0) { + DEBUG_WARNING("basic req: expected charset" + "%s, got %s\n", testv[i].charset, + req->charset); + err = EBADMSG; + mem_deref(req); + break; + } + } + + mb = mbuf_alloc(512); + if (!mb) { + err = ENOMEM; + mem_deref(req); + break; + } + + err = mbuf_printf(mb, "%H", httpauth_basic_request_print, req); + if (err) { + mem_deref(mb); + mem_deref(req); + break; + } + + if (memcmp(testv[i].hval, mb->buf, + str_len(testv[i].hval)) != 0) { + DEBUG_WARNING("basic req: expected hval %s, got %s\n", + testv[i].hval, mb->buf); + err = EBADMSG; + mem_deref(mb); + mem_deref(req); + break; + } + + mem_deref(mb); + tauth_err = httpauth_basic_verify(&testv[i].hval_response, + testv[i].user, testv[i].passwd); + if (tauth_err != testv[i].auth_err) { + DEBUG_WARNING("basic req:" + "authentication expected %d, got %d\n", + testv[i].auth_err, tauth_err); + mem_deref(req); + break; + } + + mem_deref(req); + } + + return err; +} diff --git a/test/test.c b/test/test.c index dcc080357..488eff1d1 100644 --- a/test/test.c +++ b/test/test.c @@ -118,6 +118,7 @@ static const struct test tests[] = { #endif TEST(test_httpauth_chall), TEST(test_httpauth_resp), + TEST(test_httpauth_basic_request), TEST(test_ice_cand), TEST(test_ice_loop), TEST(test_jbuf), diff --git a/test/test.h b/test/test.h index f1627f0aa..8cbadca7c 100644 --- a/test/test.h +++ b/test/test.h @@ -225,6 +225,7 @@ int test_https_conn_post_handshake(void); #endif int test_httpauth_chall(void); int test_httpauth_resp(void); +int test_httpauth_basic_request(void); int test_ice_loop(void); int test_ice_cand(void); int test_jbuf(void);