return r;
}
-int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_len) {
+int unhexmem_full(
+ const char *p,
+ size_t l,
+ bool secure,
+ void **ret,
+ size_t *ret_len) {
+
_cleanup_free_ uint8_t *buf = NULL;
size_t buf_size;
const char *x;
uint8_t *z;
- int r;
assert(p || l == 0);
if (!buf)
return -ENOMEM;
+ CLEANUP_ERASE_PTR(secure ? &buf : NULL, buf_size);
+
for (x = p, z = buf;;) {
int a, b;
a = unhex_next(&x, &l);
if (a == -EPIPE) /* End of string */
break;
- if (a < 0) {
- r = a;
- goto on_failure;
- }
+ if (a < 0)
+ return a;
b = unhex_next(&x, &l);
- if (b < 0) {
- r = b;
- goto on_failure;
- }
+ if (b < 0)
+ return b;
*(z++) = (uint8_t) a << 4 | (uint8_t) b;
}
*ret = TAKE_PTR(buf);
return 0;
-
-on_failure:
- if (secure)
- explicit_bzero_safe(buf, buf_size);
-
- return r;
}
/* https://tools.ietf.org/html/rfc4648#section-6
return ret;
}
-int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_size) {
+int unbase64mem_full(
+ const char *p,
+ size_t l,
+ bool secure,
+ void **ret,
+ size_t *ret_size) {
+
_cleanup_free_ uint8_t *buf = NULL;
const char *x;
uint8_t *z;
size_t len;
- int r;
assert(p || l == 0);
if (!buf)
return -ENOMEM;
+ CLEANUP_ERASE_PTR(secure ? &buf : NULL, len);
+
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) {
- r = a;
- goto on_failure;
- }
- if (a == INT_MAX) { /* Padding is not allowed at the beginning of a 4ch block */
- r = -EINVAL;
- goto on_failure;
- }
+ if (a < 0)
+ return a;
+ if (a == INT_MAX) /* Padding is not allowed at the beginning of a 4ch block */
+ return -EINVAL;
b = unbase64_next(&x, &l);
- if (b < 0) {
- r = b;
- goto on_failure;
- }
- if (b == INT_MAX) { /* Padding is not allowed at the second character of a 4ch block either */
- r = -EINVAL;
- goto on_failure;
- }
+ 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 = unbase64_next(&x, &l);
- if (c < 0) {
- r = c;
- goto on_failure;
- }
+ if (c < 0)
+ return c;
d = unbase64_next(&x, &l);
- if (d < 0) {
- r = d;
- goto on_failure;
- }
+ if (d < 0)
+ return d;
if (c == INT_MAX) { /* Padding at the third character */
- if (d != INT_MAX) { /* If the third character is padding, the fourth must be too */
- r = -EINVAL;
- goto on_failure;
- }
+ if (d != INT_MAX) /* If the third character is padding, the fourth must be too */
+ return -EINVAL;
/* b == 00YY0000 */
- if (b & 15) {
- r = -EINVAL;
- goto on_failure;
- }
+ if (b & 15)
+ return -EINVAL;
- if (l > 0) { /* Trailing rubbish? */
- r = -ENAMETOOLONG;
- goto on_failure;
- }
+ if (l > 0) /* Trailing rubbish? */
+ return -ENAMETOOLONG;
*(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
break;
if (d == INT_MAX) {
/* c == 00ZZZZ00 */
- if (c & 3) {
- r = -EINVAL;
- goto on_failure;
- }
+ if (c & 3)
+ return -EINVAL;
- if (l > 0) { /* Trailing rubbish? */
- r = -ENAMETOOLONG;
- goto on_failure;
- }
+ if (l > 0) /* Trailing rubbish? */
+ return -ENAMETOOLONG;
*(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
*(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
*z = 0;
+ assert((size_t) (z - buf) <= len);
+
if (ret_size)
*ret_size = (size_t) (z - buf);
if (ret)
*ret = TAKE_PTR(buf);
return 0;
-
-on_failure:
- if (secure)
- explicit_bzero_safe(buf, len);
-
- return r;
}
void hexdump(FILE *f, const void *p, size_t s) {
#endif
struct VarEraser {
+ /* NB: This is a pointer to memory to erase in case of CLEANUP_ERASE(). Pointer to pointer to memory
+ * to erase in case of CLEANUP_ERASE_PTR() */
void *p;
size_t size;
};
}
/* Mark var to be erased when leaving scope. */
-#define CLEANUP_ERASE(var) \
- _cleanup_(erase_var) _unused_ struct VarEraser CONCATENATE(_eraser_, UNIQ) = { .p = &var, .size = sizeof(var) }
+#define CLEANUP_ERASE(var) \
+ _cleanup_(erase_var) _unused_ struct VarEraser CONCATENATE(_eraser_, UNIQ) = { \
+ .p = &(var), \
+ .size = sizeof(var), \
+ }
+
+static inline void erase_varp(struct VarEraser *e) {
+
+ /* Very similar to erase_var(), but assumes `p` is a pointer to a pointer whose memory shall be destructed. */
+ if (!e->p)
+ return;
+
+ explicit_bzero_safe(*(void**) e->p, e->size);
+}
+
+/* Mark pointer so that memory pointed to is erased when leaving scope. Note: this takes a pointer to the
+ * specified pointer, instead of just a copy of it. This is to allow callers to invalidate the pointer after
+ * use, if they like, disabling our automatic erasure (for example because they succeeded with whatever they
+ * wanted to do and now intend to return the allocated buffer to their caller without it being erased). */
+#define CLEANUP_ERASE_PTR(ptr, sz) \
+ _cleanup_(erase_varp) _unused_ struct VarEraser CONCATENATE(_eraser_, UNIQ) = { \
+ .p = (ptr), \
+ .size = (sz), \
+ }
assert_se(decoded_size == n);
assert_se(memcmp(data, decoded, n) == 0);
+ /* Also try in secure mode */
+ decoded = mfree(decoded);
+ decoded_size = 0;
+ assert_se(unbase64mem_full(encoded, SIZE_MAX, /* secure= */ true, &decoded, &decoded_size) >= 0);
+ assert_se(decoded_size == n);
+ assert_se(memcmp(data, decoded, n) == 0);
+
for (size_t j = 0; j < (size_t) l; j++)
assert_se((encoded[j] == '\n') == (j % (m + 1) == m));
}
size_t size = 0;
assert_se(unbase64mem(input, SIZE_MAX, &buffer, &size) == ret);
+ if (ret >= 0) {
+ assert_se(size == strlen(output));
+ assert_se(memcmp(buffer, output, size) == 0);
+ assert_se(((char*) buffer)[size] == 0);
+ }
+
+ /* also try in secure mode */
+ buffer = mfree(buffer);
+ size = 0;
+ assert_se(unbase64mem_full(input, SIZE_MAX, /* secure=*/ true, &buffer, &size) == ret);
if (ret >= 0) {
assert_se(size == strlen(output));
assert_se(memcmp(buffer, output, size) == 0);