diff options
author | Daniel Mack <github@zonque.org> | 2015-07-12 14:10:39 -0400 |
---|---|---|
committer | Daniel Mack <github@zonque.org> | 2015-07-12 14:10:39 -0400 |
commit | 89a2faeed5abf9ee284c157c1940a5ac1d829f9b (patch) | |
tree | d89026ced870f184d808d6e252a5c6c498616da0 | |
parent | 9e400131f799e7cb565bf2865b874d876cb2937f (diff) | |
parent | 13a5d76b3277a2a499345cc24facc21eb17ccdae (diff) |
Merge pull request #566 from teg/util-base64-2
util: add base64 handling
-rw-r--r-- | src/basic/util.c | 196 | ||||
-rw-r--r-- | src/basic/util.h | 7 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp-lease.c | 9 | ||||
-rw-r--r-- | src/libsystemd/sd-bus/bus-socket.c | 20 | ||||
-rw-r--r-- | src/test/test-util.c | 125 |
5 files changed, 338 insertions, 19 deletions
diff --git a/src/basic/util.c b/src/basic/util.c index 13a67e9ab8..bc917ae574 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -916,32 +916,218 @@ char *hexmem(const void *p, size_t l) { return r; } -void *unhexmem(const char *p, size_t l) { - uint8_t *r, *z; +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; + assert(mem); + assert(len); assert(p); z = r = malloc((l + 1) / 2 + 1); if (!r) - return NULL; + return -ENOMEM; for (x = p; x < p + l; x += 2) { int a, b; a = unhexchar(x[0]); - if (x+1 < p + l) + if (a < 0) + return a; + else if (x+1 < p + l) { b = unhexchar(x[1]); - else + if (b < 0) + return b; + } else b = 0; *(z++) = (uint8_t) a << 4 | (uint8_t) b; } *z = 0; + + *mem = r; + r = NULL; + *len = (l + 1) / 2; + + return 0; +} + +/* https://tools.ietf.org/html/rfc4648#section-4 */ +char base64char(int x) { + static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + return table[x & 63]; +} + +int unbase64char(char c) { + unsigned offset; + + if (c >= 'A' && c <= 'Z') + return c - 'A'; + + offset = 'Z' - 'A' + 1; + + if (c >= 'a' && c <= 'z') + return c - 'a' + offset; + + offset += 'z' - 'a' + 1; + + if (c >= '0' && c <= '9') + return c - '0' + offset; + + offset += '9' - '0' + 1; + + if (c == '+') + return offset; + + offset ++; + + if (c == '/') + return offset; + + return -EINVAL; +} + +char *base64mem(const void *p, size_t l) { + char *r, *z; + const uint8_t *x; + + /* three input bytes makes four output bytes, padding is added so we must round up */ + z = r = malloc(4 * (l + 2) / 3 + 1); + if (!r) + return NULL; + + for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) { + /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */ + *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ + *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */ + *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */ + *(z++) = base64char(x[2] & 63); /* 00ZZZZZZ */ + } + + switch (l % 3) { + case 2: + *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ + *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */ + *(z++) = base64char((x[1] & 15) << 2); /* 00YYYY00 */ + *(z++) = '='; + + break; + case 1: + *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ + *(z++) = base64char((x[0] & 3) << 4); /* 00XX0000 */ + *(z++) = '='; + *(z++) = '='; + + break; + } + + *z = 0; return r; } +int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) { + _cleanup_free_ uint8_t *t = NULL; + int a, b, c, d; + uint8_t *r, *z; + const char *x; + size_t len; + + assert(p); + + /* padding ensures any base63 input has input divisible by 4 */ + if (l % 4 != 0) + return -EINVAL; + + /* strip the padding */ + if (l > 0 && p[l - 1] == '=') + l --; + if (l > 0 && p[l - 1] == '=') + l --; + + /* a group of four input bytes needs three output bytes, in case of + padding we need to add two or three extra bytes */ + len = (l / 4) * 3 + (l % 4 ? (l % 4) - 1 : 0); + + z = r = malloc(len + 1); + if (!r) + return -ENOMEM; + + for (x = p; x < p + (l / 4) * 4; x += 4) { + /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */ + a = unbase64char(x[0]); + if (a < 0) + return -EINVAL; + + b = unbase64char(x[1]); + if (b < 0) + return -EINVAL; + + c = unbase64char(x[2]); + if (c < 0) + return -EINVAL; + + d = unbase64char(x[3]); + if (d < 0) + return -EINVAL; + + *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ + *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ + *(z++) = (uint8_t) c << 6 | (uint8_t) d; /* ZZWWWWWW */ + } + + switch (l % 4) { + case 3: + a = unbase64char(x[0]); + if (a < 0) + return -EINVAL; + + b = unbase64char(x[1]); + if (b < 0) + return -EINVAL; + + c = unbase64char(x[2]); + if (c < 0) + return -EINVAL; + + /* c == 00ZZZZ00 */ + if (c & 3) + return -EINVAL; + + *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ + *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ + + break; + case 2: + a = unbase64char(x[0]); + if (a < 0) + return -EINVAL; + + b = unbase64char(x[1]); + if (b < 0) + return -EINVAL; + + /* b == 00YY0000 */ + if (b & 15) + return -EINVAL; + + *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */ + + break; + } + + *z = 0; + + *mem = r; + r = NULL; + *_len = len; + + return 0; +} + char octchar(int x) { return '0' + (x & 7); } diff --git a/src/basic/util.h b/src/basic/util.h index a1d1dd15c3..dae43006e4 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -240,6 +240,8 @@ char octchar(int x) _const_; int unoctchar(char c) _const_; char decchar(int x) _const_; int undecchar(char c) _const_; +char base64char(int x) _const_; +int unbase64char(char c) _const_; char *cescape(const char *s); size_t cescape_char(char c, char *buf); @@ -614,7 +616,10 @@ static inline void *mempset(void *s, int c, size_t n) { } char *hexmem(const void *p, size_t l); -void *unhexmem(const char *p, size_t l); +int unhexmem(const char *p, size_t l, void **mem, size_t *len); + +char *base64mem(const void *p, size_t l); +int unbase64mem(const char *p, size_t l, void **mem, size_t *len); char *strextend(char **x, ...) _sentinel_; char *strrep(const char *s, unsigned n); diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index d8bc76edda..7548e50b7d 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -811,13 +811,12 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { } if (client_id_hex) { - if (strlen (client_id_hex) % 2) + if (strlen(client_id_hex) % 2) return -EINVAL; - lease->client_id = unhexmem (client_id_hex, strlen (client_id_hex)); - if (!lease->client_id) - return -ENOMEM; - lease->client_id_len = strlen (client_id_hex) / 2; + r = unhexmem(client_id_hex, strlen(client_id_hex), (void**) &lease->client_id, &lease->client_id_len); + if (r < 0) + return r; } *ret = lease; diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c index 322d57ddbb..735a775cb4 100644 --- a/src/libsystemd/sd-bus/bus-socket.c +++ b/src/libsystemd/sd-bus/bus-socket.c @@ -264,6 +264,8 @@ static bool line_begins(const char *s, size_t m, const char *word) { static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) { _cleanup_free_ char *token = NULL; + size_t len; + int r; if (!b->anonymous_auth) return 0; @@ -276,11 +278,12 @@ static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) { if (l % 2 != 0) return 0; - token = unhexmem(p, l); - if (!token) - return -ENOMEM; - if (memchr(token, 0, l/2)) + r = unhexmem(p, l, (void **) &token, &len); + if (r < 0) + return 0; + + if (memchr(token, 0, len)) return 0; return !!utf8_is_valid(token); @@ -288,6 +291,7 @@ static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) { static int verify_external_token(sd_bus *b, const char *p, size_t l) { _cleanup_free_ char *token = NULL; + size_t len; uid_t u; int r; @@ -307,11 +311,11 @@ static int verify_external_token(sd_bus *b, const char *p, size_t l) { if (l % 2 != 0) return 0; - token = unhexmem(p, l); - if (!token) - return -ENOMEM; + r = unhexmem(p, l, (void**) &token, &len); + if (r < 0) + return 0; - if (memchr(token, 0, l/2)) + if (memchr(token, 0, len)) return 0; r = parse_uid(token, &u); diff --git a/src/test/test-util.c b/src/test/test-util.c index 9fbfece14f..72fbc345c2 100644 --- a/src/test/test-util.c +++ b/src/test/test-util.c @@ -390,6 +390,24 @@ static void test_unhexchar(void) { assert_se(unhexchar('0') == 0x0); } +static void test_base64char(void) { + assert_se(base64char(0) == 'A'); + assert_se(base64char(26) == 'a'); + assert_se(base64char(63) == '/'); +} + +static void test_unbase64char(void) { + assert_se(unbase64char('A') == 0); + assert_se(unbase64char('Z') == 25); + assert_se(unbase64char('a') == 26); + assert_se(unbase64char('z') == 51); + assert_se(unbase64char('0') == 52); + assert_se(unbase64char('9') == 61); + assert_se(unbase64char('+') == 62); + assert_se(unbase64char('/') == 63); + assert_se(unbase64char('=') == -EINVAL); +} + static void test_octchar(void) { assert_se(octchar(00) == '0'); assert_se(octchar(07) == '7'); @@ -410,6 +428,108 @@ static void test_undecchar(void) { assert_se(undecchar('9') == 9); } +static void test_unhexmem(void) { + const char *hex = "efa214921"; + const char *hex_invalid = "efa214921o"; + _cleanup_free_ char *hex2 = NULL; + _cleanup_free_ void *mem = NULL; + size_t len; + + assert_se(unhexmem(hex, strlen(hex), &mem, &len) == 0); + assert_se(unhexmem(hex, strlen(hex) + 1, &mem, &len) == -EINVAL); + assert_se(unhexmem(hex_invalid, strlen(hex_invalid), &mem, &len) == -EINVAL); + + assert_se((hex2 = hexmem(mem, len))); + + free(mem); + + assert_se(memcmp(hex, hex2, strlen(hex)) == 0); + + free(hex2); + + assert_se(unhexmem(hex, strlen(hex) - 1, &mem, &len) == 0); + assert_se((hex2 = hexmem(mem, len))); + assert_se(memcmp(hex, hex2, strlen(hex) - 1) == 0); +} + +/* https://tools.ietf.org/html/rfc4648#section-10 */ +static void test_base64mem(void) { + char *b64; + + b64 = base64mem("", strlen("")); + assert_se(b64); + assert_se(streq(b64, "")); + free(b64); + + b64 = base64mem("f", strlen("f")); + assert_se(b64); + assert_se(streq(b64, "Zg==")); + free(b64); + + b64 = base64mem("fo", strlen("fo")); + assert_se(b64); + assert_se(streq(b64, "Zm8=")); + free(b64); + + b64 = base64mem("foo", strlen("foo")); + assert_se(b64); + assert_se(streq(b64, "Zm9v")); + free(b64); + + b64 = base64mem("foob", strlen("foob")); + assert_se(b64); + assert_se(streq(b64, "Zm9vYg==")); + free(b64); + + b64 = base64mem("fooba", strlen("fooba")); + assert_se(b64); + assert_se(streq(b64, "Zm9vYmE=")); + free(b64); + + b64 = base64mem("foobar", strlen("foobar")); + assert_se(b64); + assert_se(streq(b64, "Zm9vYmFy")); + free(b64); +} + +static void test_unbase64mem(void) { + void *mem; + size_t len; + + assert_se(unbase64mem("", strlen(""), &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "")); + free(mem); + + assert_se(unbase64mem("Zg==", strlen("Zg=="), &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "f")); + free(mem); + + assert_se(unbase64mem("Zm8=", strlen("Zm8="), &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "fo")); + free(mem); + + assert_se(unbase64mem("Zm9v", strlen("Zm9v"), &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "foo")); + free(mem); + + assert_se(unbase64mem("Zm9vYg==", strlen("Zm9vYg=="), &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "foob")); + free(mem); + + assert_se(unbase64mem("Zm9vYmE=", strlen("Zm9vYmE="), &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "fooba")); + free(mem); + + assert_se(unbase64mem("Zm9vYmFy", strlen("Zm9vYmFy"), &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "foobar")); + free(mem); + + assert_se(unbase64mem("A", strlen("A"), &mem, &len) == -EINVAL); + assert_se(unbase64mem("A====", strlen("A===="), &mem, &len) == -EINVAL); + assert_se(unbase64mem("AAB==", strlen("AAB=="), &mem, &len) == -EINVAL); + assert_se(unbase64mem("AAAB=", strlen("AAAB="), &mem, &len) == -EINVAL); +} + static void test_cescape(void) { _cleanup_free_ char *escaped; @@ -1804,10 +1924,15 @@ int main(int argc, char *argv[]) { test_in_charset(); test_hexchar(); test_unhexchar(); + test_base64char(); + test_unbase64char(); test_octchar(); test_unoctchar(); test_decchar(); test_undecchar(); + test_unhexmem(); + test_base64mem(); + test_unbase64mem(); test_cescape(); test_cunescape(); test_foreach_word(); |