summaryrefslogtreecommitdiff
path: root/src/basic/hexdecoct.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/basic/hexdecoct.c')
-rw-r--r--src/basic/hexdecoct.c169
1 files changed, 96 insertions, 73 deletions
diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c
index b61b032d9..689f51f4e 100644
--- a/src/basic/hexdecoct.c
+++ b/src/basic/hexdecoct.c
@@ -623,112 +623,135 @@ int base64_append(
return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1);
}
-int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) {
- _cleanup_free_ uint8_t *r = NULL;
- int a, b, c, d;
- uint8_t *z;
+static int unbase64_next(const char **p, size_t *l) {
+ int ret;
+
+ assert(p);
+ assert(l);
+
+ /* Find the next non-whitespace character, and decode it. If we find padding, we return it as INT_MAX. 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)--;
+ }
+
+ if (**p == '=')
+ ret = INT_MAX; /* return padding as INT_MAX */
+ else {
+ ret = unbase64char(**p);
+ if (ret < 0)
+ return ret;
+ }
+
+ for (;;) {
+ (*p)++, (*l)--;
+
+ if (*l == 0)
+ break;
+ if (!strchr(WHITESPACE, **p))
+ break;
+
+ /* Skip following whitespace */
+ }
+
+ return ret;
+}
+
+int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) {
+ _cleanup_free_ uint8_t *buf = NULL;
const char *x;
+ uint8_t *z;
size_t len;
assert(p || l == 0);
- assert(mem);
- assert(_len);
+ assert(ret);
+ assert(ret_size);
if (l == (size_t) -1)
l = strlen(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);
+ /* 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 */
+ len = (l / 4) * 3 + (l % 4 != 0 ? (l % 4) - 1 : 0);
- z = r = malloc(len + 1);
- if (!r)
+ buf = malloc(len + 1);
+ if (!buf)
return -ENOMEM;
- for (x = p; x < p + (l / 4) * 4; x += 4) {
- /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
- a = unbase64char(x[0]);
+ for (x = p, z = buf;;) {
+ int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
+
+ a = unbase64_next(&x, &l);
+ if (a == -EPIPE) /* End of string */
+ break;
if (a < 0)
+ return a;
+ if (a == INT_MAX) /* Padding is not allowed at at the beginning of a 4ch block */
return -EINVAL;
- b = unbase64char(x[1]);
+ b = unbase64_next(&x, &l);
if (b < 0)
+ return b;
+ if (b == INT_MAX) /* Padding is not allowed at the second character of a 4ch block either */
return -EINVAL;
- c = unbase64char(x[2]);
+ c = unbase64_next(&x, &l);
if (c < 0)
- return -EINVAL;
+ return c;
- d = unbase64char(x[3]);
+ d = unbase64_next(&x, &l);
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) {
+ return d;
- case 3:
- a = unbase64char(x[0]);
- if (a < 0)
- return -EINVAL;
+ if (c == INT_MAX) { /* Padding at the third character */
- b = unbase64char(x[1]);
- if (b < 0)
- return -EINVAL;
+ if (d != INT_MAX) /* If the third character is padding, the fourth must be too */
+ return -EINVAL;
- c = unbase64char(x[2]);
- if (c < 0)
- return -EINVAL;
+ /* b == 00YY0000 */
+ if (b & 15)
+ 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;
+ if (l > 0) /* Trailing rubbish? */
+ return -ENAMETOOLONG;
- b = unbase64char(x[1]);
- if (b < 0)
- return -EINVAL;
+ *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
+ break;
+ }
- /* b == 00YY0000 */
- if (b & 15)
- return -EINVAL;
+ if (d == INT_MAX) {
+ /* c == 00ZZZZ00 */
+ if (c & 3)
+ return -EINVAL;
- *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
+ if (l > 0) /* Trailing rubbish? */
+ return -ENAMETOOLONG;
- break;
- case 0:
+ *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
+ *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
+ break;
+ }
- break;
- default:
- 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 */
}
*z = 0;
- *mem = r;
- r = NULL;
- *_len = len;
+ if (ret_size)
+ *ret_size = (size_t) (z - buf);
+
+ *ret = buf;
+ buf = NULL;
return 0;
}