Skip to content
New issue

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

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

Already on GitHub? # to your account

Add wrapper for strtoul(,,16) for safely parsing hex strings #183

Merged
merged 1 commit into from
Dec 22, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 2 additions & 3 deletions src/ne_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -1305,10 +1305,9 @@ static int verify_digest_response(struct auth_request *req, auth_session *sess,
"client nonce mismatch"));
}
else if (nc) {
char *ptr;
const char *ptr;

errno = 0;
nonce_count = strtoul(nc, &ptr, 16);
nonce_count = ne_strhextoul(nc, &ptr);
if (*ptr != '\0' || errno) {
ret = NE_ERROR;
ne_set_error(sess->sess, _("Digest mutual authentication failure: "
Expand Down
19 changes: 6 additions & 13 deletions src/ne_request.c
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,7 @@ static int read_response_block(ne_request *req, struct ne_response *resp,
* number of bytes left to read in the current chunk. */
if (resp->body.chunk.remain == 0) {
unsigned long chunk_len;
const char *cptr;
char *ptr;

/* Read chunk-size. */
Expand All @@ -956,19 +957,11 @@ static int read_response_block(ne_request *req, struct ne_response *resp,
*ptr = '\0';
}

/* Reject things strtoul would otherwise allow */
ptr = req->respbuf;
if (*ptr == '\0' || *ptr == '-' || *ptr == '+'
|| (ptr[0] == '0' && ptr[1] == 'x')) {
return aborted(req, _("Could not parse chunk size"), 0);
}

/* Limit chunk size to <= UINT_MAX, for sanity; must have
* a following NUL due to chunk-ext handling above. */
errno = 0;
chunk_len = strtoul(req->respbuf, &ptr, 16);
if (errno || ptr == req->respbuf || (*ptr != '\0' && *ptr != '\r')
|| chunk_len == ULONG_MAX || chunk_len > UINT_MAX) {
/* Limit chunk size to <= UINT_MAX, for sanity; must have
* a following NUL due to chunk-ext handling above. */
chunk_len = ne_strhextoul(req->respbuf, &cptr);
if (errno || (*cptr != '\0' && *cptr != '\r')
|| chunk_len > UINT_MAX) {
return aborted(req, _("Could not parse chunk size"), 0);
}
NE_DEBUG(NE_DBG_HTTP, "req: Chunk size: %lu\n", chunk_len);
Expand Down
23 changes: 23 additions & 0 deletions src/ne_string.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

#include <stdio.h>
#include <assert.h>
#include <errno.h>

#include "ne_alloc.h"
#include "ne_string.h"
Expand Down Expand Up @@ -801,3 +802,25 @@ char *ne_strparam(const char *charset, const char *lang,

return rv;
}

unsigned long ne_strhextoul(const char *str, const char **end)
{
unsigned long ret;
char *p;

if ((str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
|| !((str[0] >= '0' && str[0] <= '9')
|| (str[0] >= 'A' && str[0] <= 'Z')
|| (str[0] >= 'a' && str[0] <= 'z'))) {
errno = EINVAL;
p = (char *)str;
ret = ULONG_MAX;
}
else {
errno = 0;
ret = strtoul(str, &p, 16);
}
if (end) *end = (const char *)p;

return ret;
}
7 changes: 7 additions & 0 deletions src/ne_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,13 @@ char *ne_strparam(const char *charset, const char *lang,
const unsigned char *value)
ne_attribute((nonnull (1, 3))) ne_attribute_malloc;

/* Parse a hex string like strtoul(,,16), but:
* a) any whitespace, 0x or -/+ prefixes result in EINVAL
* b) errno is always set (to zero or an error)
* c) end pointer is const char *
*/
unsigned long ne_strhextoul(const char *str, const char **endptr);

NE_END_DECLS

#endif /* NE_STRING_H */
3 changes: 3 additions & 0 deletions src/neon.vers
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,6 @@ NEON_0_34 {
ne_sock_getproto;
};

NEON_0_35 {
ne_strhextoul;
};
2 changes: 2 additions & 0 deletions test/request.c
Original file line number Diff line number Diff line change
Expand Up @@ -1491,6 +1491,8 @@ static int fail_on_invalid(void)
"Could not parse chunk size" },
{ RESP200 TE_CHUNKED "\r\n" "0x5\r\n" VALID_ABCDE,
"Could not parse chunk size" },
{ RESP200 TE_CHUNKED "\r\n" "0X5\r\n" VALID_ABCDE,
"Could not parse chunk size" },
{ RESP200 TE_CHUNKED "\r\n" "+5\r\n" VALID_ABCDE,
"Could not parse chunk size" },
{ RESP200 TE_CHUNKED "\r\n" "5 5\r\n" VALID_ABCDE,
Expand Down
56 changes: 56 additions & 0 deletions test/string-tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
#ifdef HAVE_ERRNO_H
#include <errno.h> /* for the ENOENT definitions in str_errors */
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif

#include "ne_string.h"
#include "ne_utils.h"
Expand Down Expand Up @@ -783,6 +786,58 @@ static int strparam(void)
return OK;
}

static int strhextoul(void)
{
static const struct {
const char *input;
unsigned long expect;
int error; /* errno */
const char *endp;
} *t, ts[] = {
{ "0x0", ULONG_MAX, EINVAL },
{ "0X1", ULONG_MAX, EINVAL },
{ "+1", ULONG_MAX, EINVAL },
{ "-0", ULONG_MAX, EINVAL },
{ "+0x1", ULONG_MAX, EINVAL },
{ "+0X1", ULONG_MAX, EINVAL },
{ "", ULONG_MAX, EINVAL },
{ "1", 1, 0, "" },
{ " 10", ULONG_MAX, EINVAL, " 10" },
{ "0000010 ", 16, 0, " " },
{ "4242", 16962, 0 },
{ "4242zZZz", 16962, 0, "zZZz" },
{ "cAfEBeEf", 3405692655 },
#if SIZEOF_LONG == 8
{ "10000000000000000", ULONG_MAX, ERANGE },
{ "100000000", 0x100000000, 0 },
#elif SIZEOF_LONG == 4
{ "100000000", ULONG_MAX, ERANGE },
#endif
{ NULL }
};
unsigned n;

for (n = 0; ts[n].input; n++) {
unsigned long actual;
const char *endp = "(unset)", **endpp;
int errnum;

t = ts + n;

endpp = t->endp ? &endp : NULL;
errno = ENOENT;
actual = ne_strhextoul(t->input, endpp);
errnum = errno;
ONV(errnum != t->error,
("got errno %d not %d for [%s]", errnum, t->error, t->input));
ONV(actual != t->expect,
("got %lu not %lu for [%s]", actual, t->expect, t->input));
if (endpp) ONCMP(*endpp, t->endp);
}

return OK;
}

ne_test tests[] = {
T(simple),
T(buf_concat),
Expand Down Expand Up @@ -816,6 +871,7 @@ ne_test tests[] = {
T(strhash_sha_512),
T(strhash_sha_512_256),
T(strparam),
T(strhextoul),
T(NULL)
};

Loading