Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

httpauth: basic challenge creation and verification functions #875

Merged
merged 5 commits into from
Jul 13, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions include/re_httpauth.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
116 changes: 116 additions & 0 deletions src/httpauth/basic.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,119 @@ 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)
{
if (!req)
return 0;

if (req->charset && str_len(req->charset))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is better to use "str_isset" here

return re_hprintf(pf, "Basic realm=\"%s\", charset=\"%s\"",
req->realm, req->charset);
else
return re_hprintf(pf, "Basic realm=\"%s\"", req->realm);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this code can be simplified.

Print "basic realm" every time. If charset is set, append charset.

}


/**
* Verify received credentials
*
* @param user user name (may be an UTF-8 string)
sreimers marked this conversation as resolved.
Show resolved Hide resolved
* @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, "%b:%b",
user, str_len(user), passwd, str_len(passwd));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can also use %s:%s here ...

if (err)
goto out;

if (memcmp(mb->buf, c, clen) != 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is more secure to use this function:

int mem_seccmp(const uint8_t *s1, const uint8_t *s2, size_t n);

err = EACCES;

out:
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;
}
112 changes: 112 additions & 0 deletions test/httpauth.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
1 change: 1 addition & 0 deletions test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
1 change: 1 addition & 0 deletions test/test.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down