Skip to content

Commit 1e7c960

Browse files
committed
Add wrapper for strtoul(,,16) for safely parsing hex strings:
* src/ne_string.c (ne_strhextoul): New function. * test/string-tests.c (strhextoul): Add test case. * src/ne_request.c (read_response_block): Use ne_strhextoul() rather than strtoul. * src/ne_auth.c (verify_digest_response): Likewise. * src/neon.vers, src/ne_string.h: Add new API.
1 parent 9a23c93 commit 1e7c960

File tree

7 files changed

+99
-16
lines changed

7 files changed

+99
-16
lines changed

src/ne_auth.c

+2-3
Original file line numberDiff line numberDiff line change
@@ -1305,10 +1305,9 @@ static int verify_digest_response(struct auth_request *req, auth_session *sess,
13051305
"client nonce mismatch"));
13061306
}
13071307
else if (nc) {
1308-
char *ptr;
1308+
const char *ptr;
13091309

1310-
errno = 0;
1311-
nonce_count = strtoul(nc, &ptr, 16);
1310+
nonce_count = ne_strhextoul(nc, &ptr);
13121311
if (*ptr != '\0' || errno) {
13131312
ret = NE_ERROR;
13141313
ne_set_error(sess->sess, _("Digest mutual authentication failure: "

src/ne_request.c

+6-13
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,7 @@ static int read_response_block(ne_request *req, struct ne_response *resp,
935935
* number of bytes left to read in the current chunk. */
936936
if (resp->body.chunk.remain == 0) {
937937
unsigned long chunk_len;
938+
const char *cptr;
938939
char *ptr;
939940

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

959-
/* Reject things strtoul would otherwise allow */
960-
ptr = req->respbuf;
961-
if (*ptr == '\0' || *ptr == '-' || *ptr == '+'
962-
|| (ptr[0] == '0' && ptr[1] == 'x')) {
963-
return aborted(req, _("Could not parse chunk size"), 0);
964-
}
965-
966-
/* Limit chunk size to <= UINT_MAX, for sanity; must have
967-
* a following NUL due to chunk-ext handling above. */
968-
errno = 0;
969-
chunk_len = strtoul(req->respbuf, &ptr, 16);
970-
if (errno || ptr == req->respbuf || (*ptr != '\0' && *ptr != '\r')
971-
|| chunk_len == ULONG_MAX || chunk_len > UINT_MAX) {
960+
/* Limit chunk size to <= UINT_MAX, for sanity; must have
961+
* a following NUL due to chunk-ext handling above. */
962+
chunk_len = ne_strhextoul(req->respbuf, &cptr);
963+
if (errno || (*cptr != '\0' && *cptr != '\r')
964+
|| chunk_len > UINT_MAX) {
972965
return aborted(req, _("Could not parse chunk size"), 0);
973966
}
974967
NE_DEBUG(NE_DBG_HTTP, "req: Chunk size: %lu\n", chunk_len);

src/ne_string.c

+23
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
#include <stdio.h>
3838
#include <assert.h>
39+
#include <errno.h>
3940

4041
#include "ne_alloc.h"
4142
#include "ne_string.h"
@@ -801,3 +802,25 @@ char *ne_strparam(const char *charset, const char *lang,
801802

802803
return rv;
803804
}
805+
806+
unsigned long ne_strhextoul(const char *str, const char **end)
807+
{
808+
unsigned long ret;
809+
char *p;
810+
811+
if ((str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
812+
|| !((str[0] >= '0' && str[0] <= '9')
813+
|| (str[0] >= 'A' && str[0] <= 'Z')
814+
|| (str[0] >= 'a' && str[0] <= 'z'))) {
815+
errno = EINVAL;
816+
p = (char *)str;
817+
ret = ULONG_MAX;
818+
}
819+
else {
820+
errno = 0;
821+
ret = strtoul(str, &p, 16);
822+
}
823+
if (end) *end = (const char *)p;
824+
825+
return ret;
826+
}

src/ne_string.h

+7
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,13 @@ char *ne_strparam(const char *charset, const char *lang,
220220
const unsigned char *value)
221221
ne_attribute((nonnull (1, 3))) ne_attribute_malloc;
222222

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

225232
#endif /* NE_STRING_H */

src/neon.vers

+3
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,6 @@ NEON_0_34 {
5151
ne_sock_getproto;
5252
};
5353

54+
NEON_0_35 {
55+
ne_strhextoul;
56+
};

test/request.c

+2
Original file line numberDiff line numberDiff line change
@@ -1491,6 +1491,8 @@ static int fail_on_invalid(void)
14911491
"Could not parse chunk size" },
14921492
{ RESP200 TE_CHUNKED "\r\n" "0x5\r\n" VALID_ABCDE,
14931493
"Could not parse chunk size" },
1494+
{ RESP200 TE_CHUNKED "\r\n" "0X5\r\n" VALID_ABCDE,
1495+
"Could not parse chunk size" },
14941496
{ RESP200 TE_CHUNKED "\r\n" "+5\r\n" VALID_ABCDE,
14951497
"Could not parse chunk size" },
14961498
{ RESP200 TE_CHUNKED "\r\n" "5 5\r\n" VALID_ABCDE,

test/string-tests.c

+56
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
#ifdef HAVE_ERRNO_H
3030
#include <errno.h> /* for the ENOENT definitions in str_errors */
3131
#endif
32+
#ifdef HAVE_LIMITS_H
33+
#include <limits.h>
34+
#endif
3235

3336
#include "ne_string.h"
3437
#include "ne_utils.h"
@@ -783,6 +786,58 @@ static int strparam(void)
783786
return OK;
784787
}
785788

789+
static int strhextoul(void)
790+
{
791+
static const struct {
792+
const char *input;
793+
unsigned long expect;
794+
int error; /* errno */
795+
const char *endp;
796+
} *t, ts[] = {
797+
{ "0x0", ULONG_MAX, EINVAL },
798+
{ "0X1", ULONG_MAX, EINVAL },
799+
{ "+1", ULONG_MAX, EINVAL },
800+
{ "-0", ULONG_MAX, EINVAL },
801+
{ "+0x1", ULONG_MAX, EINVAL },
802+
{ "+0X1", ULONG_MAX, EINVAL },
803+
{ "", ULONG_MAX, EINVAL },
804+
{ "1", 1, 0, "" },
805+
{ " 10", ULONG_MAX, EINVAL, " 10" },
806+
{ "0000010 ", 16, 0, " " },
807+
{ "4242", 16962, 0 },
808+
{ "4242zZZz", 16962, 0, "zZZz" },
809+
{ "cAfEBeEf", 3405692655 },
810+
#if SIZEOF_LONG == 8
811+
{ "10000000000000000", ULONG_MAX, ERANGE },
812+
{ "100000000", 0x100000000, 0 },
813+
#elif SIZEOF_LONG == 4
814+
{ "100000000", ULONG_MAX, ERANGE },
815+
#endif
816+
{ NULL }
817+
};
818+
unsigned n;
819+
820+
for (n = 0; ts[n].input; n++) {
821+
unsigned long actual;
822+
const char *endp = "(unset)", **endpp;
823+
int errnum;
824+
825+
t = ts + n;
826+
827+
endpp = t->endp ? &endp : NULL;
828+
errno = ENOENT;
829+
actual = ne_strhextoul(t->input, endpp);
830+
errnum = errno;
831+
ONV(errnum != t->error,
832+
("got errno %d not %d for [%s]", errnum, t->error, t->input));
833+
ONV(actual != t->expect,
834+
("got %lu not %lu for [%s]", actual, t->expect, t->input));
835+
if (endpp) ONCMP(*endpp, t->endp);
836+
}
837+
838+
return OK;
839+
}
840+
786841
ne_test tests[] = {
787842
T(simple),
788843
T(buf_concat),
@@ -816,6 +871,7 @@ ne_test tests[] = {
816871
T(strhash_sha_512),
817872
T(strhash_sha_512_256),
818873
T(strparam),
874+
T(strhextoul),
819875
T(NULL)
820876
};
821877

0 commit comments

Comments
 (0)