Merge pull request #8924 from yuwata/fix-3682

resolve: allow whitespaces in the digest
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2018-05-11 11:48:05 +02:00 committed by GitHub
commit 2407ed7b63
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 161 additions and 142 deletions

View file

@ -77,33 +77,69 @@ char *hexmem(const void *p, size_t l) {
return r;
}
int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
_cleanup_free_ uint8_t *r = NULL;
uint8_t *z;
const char *x;
static int unhex_next(const char **p, size_t *l) {
int r;
assert(mem);
assert(len);
assert(p);
assert(l);
/* Find the next non-whitespace character, and decode it. We
* greedily skip all preceeding and all following whitespace. */
for (;;) {
if (*l == 0)
return -EPIPE;
if (!strchr(WHITESPACE, **p))
break;
/* Skip leading whitespace */
(*p)++, (*l)--;
}
r = unhexchar(**p);
if (r < 0)
return r;
for (;;) {
(*p)++, (*l)--;
if (*l == 0 || !strchr(WHITESPACE, **p))
break;
/* Skip following whitespace */
}
return r;
}
int unhexmem(const char *p, size_t l, void **ret, size_t *ret_len) {
_cleanup_free_ uint8_t *buf = NULL;
const char *x;
uint8_t *z;
assert(ret);
assert(ret_len);
assert(p || l == 0);
if (l == (size_t) -1)
l = strlen(p);
if (l % 2 != 0)
return -EINVAL;
z = r = malloc((l + 1) / 2 + 1);
if (!r)
/* Note that the calculation of memory size is an upper boundary, as we ignore whitespace while decoding */
buf = malloc((l + 1) / 2 + 1);
if (!buf)
return -ENOMEM;
for (x = p; x < p + l; x += 2) {
for (x = p, z = buf;;) {
int a, b;
a = unhexchar(x[0]);
a = unhex_next(&x, &l);
if (a == -EPIPE) /* End of string */
break;
if (a < 0)
return a;
b = unhexchar(x[1]);
b = unhex_next(&x, &l);
if (b < 0)
return b;
@ -112,8 +148,8 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
*z = 0;
*mem = TAKE_PTR(r);
*len = (l + 1) / 2;
*ret_len = (size_t) (z - buf);
*ret = TAKE_PTR(buf);
return 0;
}
@ -181,7 +217,7 @@ char *base32hexmem(const void *p, size_t l, bool padding) {
for (x = p; x < (const uint8_t*) p + (l / 5) * 5; x += 5) {
/* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ
x[3] == QQQQQQQQ; x[4] == WWWWWWWW */
* x[3] == QQQQQQQQ; x[4] == WWWWWWWW */
*(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */
*(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
*(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */
@ -281,7 +317,7 @@ int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_l
}
/* a group of eight input bytes needs five output bytes, in case of
padding we need to add some extra bytes */
* padding we need to add some extra bytes */
len = (l / 8) * 5;
switch (l % 8) {
@ -309,7 +345,7 @@ int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_l
for (x = p; x < p + (l / 8) * 8; x += 8) {
/* a == 000XXXXX; b == 000YYYYY; c == 000ZZZZZ; d == 000WWWWW
e == 000SSSSS; f == 000QQQQQ; g == 000VVVVV; h == 000RRRRR */
* e == 000SSSSS; f == 000QQQQQ; g == 000VVVVV; h == 000RRRRR */
a = unbase32hexchar(x[0]);
if (a < 0)
return -EINVAL;
@ -665,7 +701,7 @@ int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) {
l = strlen(p);
/* A group of four input bytes needs three output bytes, in case of padding we need to add two or three extra
bytes. Note that this calculation is an upper boundary, as we ignore whitespace while decoding */
* bytes. Note that this calculation is an upper boundary, as we ignore whitespace while decoding */
len = (l / 4) * 3 + (l % 4 != 0 ? (l % 4) - 1 : 0);
buf = malloc(len + 1);
@ -733,9 +769,7 @@ int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) {
*z = 0;
if (ret_size)
*ret_size = (size_t) (z - buf);
*ret_size = (size_t) (z - buf);
*ret = TAKE_PTR(buf);
return 0;

View file

@ -656,14 +656,3 @@ int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t siz
return 0;
}
int deserialize_dhcp_option(void **data, size_t *data_len, const char *string) {
assert(data);
assert(data_len);
assert(string);
if (strlen(string) % 2)
return -EINVAL;
return unhexmem(string, strlen(string), (void **)data, data_len);
}

View file

@ -78,5 +78,5 @@ struct sd_dhcp_route;
void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size);
int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string);
/* It is not necessary to add deserialize_dhcp_option(). Use unhexmem() instead. */
int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size);
int deserialize_dhcp_option(void **data, size_t *data_len, const char *string);

View file

@ -1193,13 +1193,13 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
}
if (client_id_hex) {
r = deserialize_dhcp_option(&lease->client_id, &lease->client_id_len, client_id_hex);
r = unhexmem(client_id_hex, (size_t) -1, &lease->client_id, &lease->client_id_len);
if (r < 0)
log_debug_errno(r, "Failed to parse client ID %s, ignoring: %m", client_id_hex);
}
if (vendor_specific_hex) {
r = deserialize_dhcp_option(&lease->vendor_specific, &lease->vendor_specific_len, vendor_specific_hex);
r = unhexmem(vendor_specific_hex, (size_t) -1, &lease->vendor_specific, &lease->vendor_specific_len);
if (r < 0)
log_debug_errno(r, "Failed to parse vendor specific data %s, ignoring: %m", vendor_specific_hex);
}
@ -1211,7 +1211,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
if (!options[i])
continue;
r = deserialize_dhcp_option(&data, &len, options[i]);
r = unhexmem(options[i], (size_t) -1, &data, &len);
if (r < 0) {
log_debug_errno(r, "Failed to parse private DHCP option %s, ignoring: %m", options[i]);
continue;

View file

@ -242,18 +242,18 @@ static int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, u
}
if (strcaseeq(type, "DS")) {
_cleanup_free_ char *key_tag = NULL, *algorithm = NULL, *digest_type = NULL, *digest = NULL;
_cleanup_free_ char *key_tag = NULL, *algorithm = NULL, *digest_type = NULL;
_cleanup_free_ void *dd = NULL;
uint16_t kt;
int a, dt;
size_t l;
r = extract_many_words(&p, NULL, 0, &key_tag, &algorithm, &digest_type, &digest, NULL);
r = extract_many_words(&p, NULL, 0, &key_tag, &algorithm, &digest_type, NULL);
if (r < 0) {
log_warning_errno(r, "Failed to parse DS parameters on line %s:%u: %m", path, line);
return -EINVAL;
}
if (r != 4) {
if (r != 3) {
log_warning("Missing DS parameters on line %s:%u", path, line);
return -EINVAL;
}
@ -274,9 +274,14 @@ static int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, u
return -EINVAL;
}
r = unhexmem(digest, strlen(digest), &dd, &l);
if (isempty(p)) {
log_warning("Missing DS digest on line %s:%u", path, line);
return -EINVAL;
}
r = unhexmem(p, strlen(p), &dd, &l);
if (r < 0) {
log_warning("Failed to parse DS digest %s on line %s:%u", digest, path, line);
log_warning("Failed to parse DS digest %s on line %s:%u", p, path, line);
return -EINVAL;
}
@ -291,16 +296,16 @@ static int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, u
rr->ds.digest = TAKE_PTR(dd);
} else if (strcaseeq(type, "DNSKEY")) {
_cleanup_free_ char *flags = NULL, *protocol = NULL, *algorithm = NULL, *key = NULL;
_cleanup_free_ char *flags = NULL, *protocol = NULL, *algorithm = NULL;
_cleanup_free_ void *k = NULL;
uint16_t f;
size_t l;
int a;
r = extract_many_words(&p, NULL, 0, &flags, &protocol, &algorithm, &key, NULL);
r = extract_many_words(&p, NULL, 0, &flags, &protocol, &algorithm, NULL);
if (r < 0)
return log_warning_errno(r, "Failed to parse DNSKEY parameters on line %s:%u: %m", path, line);
if (r != 4) {
if (r != 3) {
log_warning("Missing DNSKEY parameters on line %s:%u", path, line);
return -EINVAL;
}
@ -328,9 +333,14 @@ static int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, u
return -EINVAL;
}
r = unbase64mem(key, strlen(key), &k, &l);
if (isempty(p)) {
log_warning("Missing DNSKEY key on line %s:%u", path, line);
return -EINVAL;
}
r = unbase64mem(p, strlen(p), &k, &l);
if (r < 0)
return log_warning_errno(r, "Failed to parse DNSKEY key data %s on line %s:%u", key, path, line);
return log_warning_errno(r, "Failed to parse DNSKEY key data %s on line %s:%u", p, path, line);
rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, domain);
if (!rr)
@ -347,11 +357,6 @@ static int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, u
return -EINVAL;
}
if (!isempty(p)) {
log_warning("Trailing garbage on line %s:%u, ignoring line.", path, line);
return -EINVAL;
}
r = hashmap_ensure_allocated(&d->positive_by_key, &dns_resource_key_hash_ops);
if (r < 0)
return log_oom();

View file

@ -76,20 +76,40 @@ static void test_undecchar(void) {
assert_se(undecchar('9') == 9);
}
static void test_unhexmem(void) {
const char *hex = "efa2149213";
const char *hex_invalid = "efa214921o";
_cleanup_free_ char *hex2 = NULL;
static void test_unhexmem_one(const char *s, size_t l, int retval) {
_cleanup_free_ char *hex = NULL;
_cleanup_free_ void *mem = NULL;
size_t len;
assert_se(unhexmem(hex_invalid, strlen(hex_invalid), &mem, &len) == -EINVAL);
assert_se(unhexmem(hex, strlen(hex) + 1, &mem, &len) == -EINVAL);
assert_se(unhexmem(hex, strlen(hex) - 1, &mem, &len) == -EINVAL);
assert_se(unhexmem(hex, strlen(hex), &mem, &len) == 0);
assert_se(unhexmem(s, l, &mem, &len) == retval);
if (retval == 0) {
char *answer;
assert_se((hex2 = hexmem(mem, len)));
assert_se(streq(hex, hex2));
if (l == (size_t) - 1)
l = strlen(s);
assert_se((hex = hexmem(mem, len)));
answer = strndupa(s, l);
assert_se(streq(delete_chars(answer, WHITESPACE), hex));
}
}
static void test_unhexmem(void) {
const char *hex = "efa2149213";
const char *hex_space = " e f a\n 2\r 14\n\r\t9\t2 \n1\r3 \r\r\t";
const char *hex_invalid = "efa214921o";
test_unhexmem_one(NULL, 0, 0);
test_unhexmem_one("", 0, 0);
test_unhexmem_one("", (size_t) -1, 0);
test_unhexmem_one(" \n \t\r \t\t \n\n\n", (size_t) -1, 0);
test_unhexmem_one(hex_invalid, strlen(hex_invalid), -EINVAL);
test_unhexmem_one(hex_invalid, (size_t) - 1, -EINVAL);
test_unhexmem_one(hex, strlen(hex) - 1, -EPIPE);
test_unhexmem_one(hex, strlen(hex), 0);
test_unhexmem_one(hex, (size_t) -1, 0);
test_unhexmem_one(hex_space, strlen(hex_space), 0);
test_unhexmem_one(hex_space, (size_t) -1, 0);
}
/* https://tools.ietf.org/html/rfc4648#section-10 */
@ -167,94 +187,65 @@ static void test_base32hexmem(void) {
free(b32);
}
static void test_unbase32hexmem(void) {
void *mem;
static void test_unbase32hexmem_one(const char *hex, bool padding, int retval, const char *ans) {
_cleanup_free_ void *mem = NULL;
size_t len;
assert_se(unbase32hexmem("", STRLEN(""), true, &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), ""));
free(mem);
assert_se(unbase32hexmem(hex, (size_t) -1, padding, &mem, &len) == retval);
if (retval == 0) {
char *str;
assert_se(unbase32hexmem("CO======", STRLEN("CO======"), true, &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), "f"));
free(mem);
str = strndupa(mem, len);
assert_se(streq(str, ans));
}
}
assert_se(unbase32hexmem("CPNG====", STRLEN("CPNG===="), true, &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), "fo"));
free(mem);
static void test_unbase32hexmem(void) {
test_unbase32hexmem_one("", true, 0, "");
assert_se(unbase32hexmem("CPNMU===", STRLEN("CPNMU==="), true, &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), "foo"));
free(mem);
test_unbase32hexmem_one("CO======", true, 0, "f");
test_unbase32hexmem_one("CPNG====", true, 0, "fo");
test_unbase32hexmem_one("CPNMU===", true, 0, "foo");
test_unbase32hexmem_one("CPNMUOG=", true, 0, "foob");
test_unbase32hexmem_one("CPNMUOJ1", true, 0, "fooba");
test_unbase32hexmem_one("CPNMUOJ1E8======", true, 0, "foobar");
assert_se(unbase32hexmem("CPNMUOG=", STRLEN("CPNMUOG="), true, &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), "foob"));
free(mem);
test_unbase32hexmem_one("A", true, -EINVAL, NULL);
test_unbase32hexmem_one("A=======", true, -EINVAL, NULL);
test_unbase32hexmem_one("AAA=====", true, -EINVAL, NULL);
test_unbase32hexmem_one("AAAAAA==", true, -EINVAL, NULL);
test_unbase32hexmem_one("AB======", true, -EINVAL, NULL);
test_unbase32hexmem_one("AAAB====", true, -EINVAL, NULL);
test_unbase32hexmem_one("AAAAB===", true, -EINVAL, NULL);
test_unbase32hexmem_one("AAAAAAB=", true, -EINVAL, NULL);
assert_se(unbase32hexmem("CPNMUOJ1", STRLEN("CPNMUOJ1"), true, &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), "fooba"));
free(mem);
test_unbase32hexmem_one("XPNMUOJ1", true, -EINVAL, NULL);
test_unbase32hexmem_one("CXNMUOJ1", true, -EINVAL, NULL);
test_unbase32hexmem_one("CPXMUOJ1", true, -EINVAL, NULL);
test_unbase32hexmem_one("CPNXUOJ1", true, -EINVAL, NULL);
test_unbase32hexmem_one("CPNMXOJ1", true, -EINVAL, NULL);
test_unbase32hexmem_one("CPNMUXJ1", true, -EINVAL, NULL);
test_unbase32hexmem_one("CPNMUOX1", true, -EINVAL, NULL);
test_unbase32hexmem_one("CPNMUOJX", true, -EINVAL, NULL);
assert_se(unbase32hexmem("CPNMUOJ1E8======", STRLEN("CPNMUOJ1E8======"), true, &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), "foobar"));
free(mem);
test_unbase32hexmem_one("", false, 0, "");
test_unbase32hexmem_one("CO", false, 0, "f");
test_unbase32hexmem_one("CPNG", false, 0, "fo");
test_unbase32hexmem_one("CPNMU", false, 0, "foo");
test_unbase32hexmem_one("CPNMUOG", false, 0, "foob");
test_unbase32hexmem_one("CPNMUOJ1", false, 0, "fooba");
test_unbase32hexmem_one("CPNMUOJ1E8", false, 0, "foobar");
test_unbase32hexmem_one("CPNMUOG=", false, -EINVAL, NULL);
test_unbase32hexmem_one("CPNMUOJ1E8======", false, -EINVAL, NULL);
assert_se(unbase32hexmem("A", STRLEN("A"), true, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("A=======", STRLEN("A======="), true, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("AAA=====", STRLEN("AAA====="), true, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("AAAAAA==", STRLEN("AAAAAA=="), true, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("AB======", STRLEN("AB======"), true, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("AAAB====", STRLEN("AAAB===="), true, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("AAAAB===", STRLEN("AAAAB==="), true, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("AAAAAAB=", STRLEN("AAAAAAB="), true, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("XPNMUOJ1", STRLEN("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("CXNMUOJ1", STRLEN("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("CPXMUOJ1", STRLEN("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("CPNXUOJ1", STRLEN("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("CPNMXOJ1", STRLEN("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("CPNMUXJ1", STRLEN("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("CPNMUOX1", STRLEN("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("CPNMUOJX", STRLEN("CPNMUOJ1"), true, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("", STRLEN(""), false, &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), ""));
free(mem);
assert_se(unbase32hexmem("CO", STRLEN("CO"), false, &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), "f"));
free(mem);
assert_se(unbase32hexmem("CPNG", STRLEN("CPNG"), false, &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), "fo"));
free(mem);
assert_se(unbase32hexmem("CPNMU", STRLEN("CPNMU"), false, &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), "foo"));
free(mem);
assert_se(unbase32hexmem("CPNMUOG", STRLEN("CPNMUOG"), false, &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), "foob"));
free(mem);
assert_se(unbase32hexmem("CPNMUOJ1", STRLEN("CPNMUOJ1"), false, &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), "fooba"));
free(mem);
assert_se(unbase32hexmem("CPNMUOJ1E8", STRLEN("CPNMUOJ1E8"), false, &mem, &len) == 0);
assert_se(streq(strndupa(mem, len), "foobar"));
free(mem);
assert_se(unbase32hexmem("CPNMUOG=", STRLEN("CPNMUOG="), false, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("CPNMUOJ1E8======", STRLEN("CPNMUOJ1E8======"), false, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("A", STRLEN("A"), false, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("A", STRLEN("A"), false, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("AAA", STRLEN("AAA"), false, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("AAAAAA", STRLEN("AAAAAA"), false, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("AB", STRLEN("AB"), false, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("AAAB", STRLEN("AAAB"), false, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("AAAAB", STRLEN("AAAAB"), false, &mem, &len) == -EINVAL);
assert_se(unbase32hexmem("AAAAAAB", STRLEN("AAAAAAB"), false, &mem, &len) == -EINVAL);
test_unbase32hexmem_one("A", false, -EINVAL, NULL);
test_unbase32hexmem_one("A", false, -EINVAL, NULL);
test_unbase32hexmem_one("AAA", false, -EINVAL, NULL);
test_unbase32hexmem_one("AAAAAA", false, -EINVAL, NULL);
test_unbase32hexmem_one("AB", false, -EINVAL, NULL);
test_unbase32hexmem_one("AAAB", false, -EINVAL, NULL);
test_unbase32hexmem_one("AAAAB", false, -EINVAL, NULL);
test_unbase32hexmem_one("AAAAAAB", false, -EINVAL, NULL);
}
/* https://tools.ietf.org/html/rfc4648#section-10 */