Currently the callers of these functions must allocate a suitably
sized buffer for the decoded data. It is easier to let the decoders
allocate the result buffer, as implemented in this commit. The callers
in crypt.c and gcrypt.c are adjusted accordingly.
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char Pad64 = '=';
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char Pad64 = '=';
+/** Maximal possible size of the decoded data. */
+#define BASE64_MAX_DECODED_SIZE(_encoded_size) ((_encoded_size) / 4 * 3)
+
/**
* base64-decode a buffer.
*
* \param src The buffer to decode.
/**
* base64-decode a buffer.
*
* \param src The buffer to decode.
- * \param target Result is stored here.
- * \param targsize Number of bytes of \a target.
+ * \param encoded_size The special value -1 means: look for terminating zero byte.
+ * \param result Points to dynamically allocated target buffer on success.
+ * \param decoded_size Number of bytes written to \a result.
*
* Skips all whitespace anywhere. Converts characters, four at a time, starting
* at (or after) src from base - 64 numbers into three 8 bit bytes in the
* target area.
*
*
* Skips all whitespace anywhere. Converts characters, four at a time, starting
* at (or after) src from base - 64 numbers into three 8 bit bytes in the
* target area.
*
- * \return The number of data bytes stored at the target, -E_BASE64 on errors.
+ * It is OK to pass a \p NULL pointer as \a decoded_size. The result is
+ * terminated with a zero byte.
+ *
+ * \return Standard. The contents of result \a and \a decoded_size are
+ * undefined on failure.
-int base64_decode(char const *src, unsigned char *target, size_t targsize)
+int base64_decode(char const *src, size_t encoded_size, char **result,
+ size_t *decoded_size)
{
unsigned int tarindex, state;
int ch;
char *pos;
{
unsigned int tarindex, state;
int ch;
char *pos;
+ const char *end = src + encoded_size;
+ unsigned char *target;
+ size_t targsize;
+
+ if (encoded_size == (size_t)-1)
+ encoded_size = strlen(src);
+ targsize = BASE64_MAX_DECODED_SIZE(encoded_size);
+ target = para_malloc(targsize + 1);
- while ((ch = *src++) != '\0') {
+ while (src < end) {
+ ch = *src++;
if (para_isspace(ch)) /* Skip whitespace anywhere. */
continue;
if (para_isspace(ch)) /* Skip whitespace anywhere. */
continue;
pos = strchr(Base64, ch);
if (pos == NULL) /* A non-base64 character. */
pos = strchr(Base64, ch);
if (pos == NULL) /* A non-base64 character. */
switch (state) {
case 0:
if (tarindex >= targsize)
switch (state) {
case 0:
if (tarindex >= targsize)
target[tarindex] = (pos - Base64) << 2;
state = 1;
break;
case 1:
if (tarindex + 1 >= targsize)
target[tarindex] = (pos - Base64) << 2;
state = 1;
break;
case 1:
if (tarindex + 1 >= targsize)
target[tarindex] |= (pos - Base64) >> 4;
target[tarindex + 1] = ((pos - Base64) & 0x0f) << 4;
tarindex++;
target[tarindex] |= (pos - Base64) >> 4;
target[tarindex + 1] = ((pos - Base64) & 0x0f) << 4;
tarindex++;
break;
case 2:
if (tarindex + 1 >= targsize)
break;
case 2:
if (tarindex + 1 >= targsize)
target[tarindex] |= (pos - Base64) >> 2;
target[tarindex + 1] = ((pos - Base64) & 0x03) << 6;
tarindex++;
target[tarindex] |= (pos - Base64) >> 2;
target[tarindex + 1] = ((pos - Base64) & 0x03) << 6;
tarindex++;
break;
case 3:
if (tarindex >= targsize)
break;
case 3:
if (tarindex >= targsize)
target[tarindex] |= pos - Base64;
tarindex++;
state = 0;
target[tarindex] |= pos - Base64;
tarindex++;
state = 0;
* on a byte boundary, and/or with erroneous trailing characters.
*/
* on a byte boundary, and/or with erroneous trailing characters.
*/
- if (ch == Pad64) { /* We got a pad char. */
+ if (*src == Pad64) { /* We got a pad char. */
ch = *src++; /* Skip it, get next. */
switch (state) {
case 0: /* Invalid = in first position */
case 1: /* Invalid = in second position */
ch = *src++; /* Skip it, get next. */
switch (state) {
case 0: /* Invalid = in first position */
case 1: /* Invalid = in second position */
case 2: /* Valid, means one byte of info */
/* Skip any number of spaces. */
case 2: /* Valid, means one byte of info */
/* Skip any number of spaces. */
break;
/* Make sure there is another trailing = sign. */
if (ch != Pad64)
break;
/* Make sure there is another trailing = sign. */
if (ch != Pad64)
ch = *src++; /* Skip the = */
/* Fall through to "single trailing =" case. */
/* FALLTHROUGH */
ch = *src++; /* Skip the = */
/* Fall through to "single trailing =" case. */
/* FALLTHROUGH */
*/
for (; ch != '\0'; ch = *src++)
if (!isspace(ch))
*/
for (; ch != '\0'; ch = *src++)
if (!isspace(ch))
/*
* Now make sure for cases 2 and 3 that the "extra"
/*
* Now make sure for cases 2 and 3 that the "extra"
* subliminal channel.
*/
if (target[tarindex] != 0)
* subliminal channel.
*/
if (target[tarindex] != 0)
* have no partial bytes lying around.
*/
if (state != 0)
* have no partial bytes lying around.
*/
if (state != 0)
+ /* success */
+ target[tarindex] = '\0'; /* just to be sure */
+ *result = (char *)target;
+ if (decoded_size)
+ *decoded_size = tarindex;
+ return 1;
+fail:
+ free(target);
+ return -E_BASE64;
+ * Decode a buffer using the uuencode Base64 algorithm.
*
* \param src The buffer to decode.
*
* \param src The buffer to decode.
- * \param target Result buffer.
- * \param targsize The length of \a target in bytes.
+ * \param encoded_size Number of input bytes in the source buffer.
+ * \param result Contains the decoded data on success.
+ * \param decoded_size Number of output bytes on success.
+ *
+ * This is just a simple wrapper for \ref base64_decode() which strips
+ * whitespace.
- * This is just a simple wrapper for base64_decode() which strips whitespace.
+ * \return The return value of the underlying call to \ref base64_decode().
- * \return The return value of the underlying call to base64_decode().
+ * \sa uuencode(1), uudecode(1).
-int uudecode(const char *src, unsigned char *target, size_t targsize)
+int uudecode(char const *src, size_t encoded_size, char **result,
+ size_t *decoded_size)
- int len;
- char *encoded, *p;
+ const char *end = src + encoded_size, *p;
- /* copy the 'readonly' source */
- encoded = para_strdup(src);
/* skip whitespace and data */
/* skip whitespace and data */
- for (p = encoded; *p == ' ' || *p == '\t'; p++)
+ for (p = src; p < end && (*p == ' ' || *p == '\t'); p++)
- for (; *p != '\0' && *p != ' ' && *p != '\t'; p++)
+ for (; p < end && *p != '\0' && *p != ' ' && *p != '\t'; p++)
;
/* and remove trailing whitespace because base64_decode needs this */
;
/* and remove trailing whitespace because base64_decode needs this */
- *p = '\0';
- len = base64_decode(encoded, target, targsize);
- free(encoded);
- return len;
+ return base64_decode(src, p - src, result, decoded_size);
-int uudecode(const char *src, unsigned char *target, size_t targsize);
-int base64_decode(char const *src, unsigned char *target, size_t targsize);
+int uudecode(char const *src, size_t encoded_size, char **result,
+ size_t *decoded_size);
+int base64_decode(char const *src, size_t encoded_size, char **result,
+ size_t *decoded_size);
struct asymmetric_key *key = NULL;
void *map = NULL;
unsigned char *blob = NULL;
struct asymmetric_key *key = NULL;
void *map = NULL;
unsigned char *blob = NULL;
- size_t map_size, blob_size, decoded_size;
+ size_t map_size, encoded_size, decoded_size;
goto out;
}
cp = map + ret;
goto out;
}
cp = map + ret;
+ encoded_size = map_size - ret;
PARA_INFO_LOG("decoding public rsa-ssh key %s\n", key_file);
PARA_INFO_LOG("decoding public rsa-ssh key %s\n", key_file);
- ret = -ERRNO_TO_PARA_ERROR(EOVERFLOW);
- if (map_size > INT_MAX / 4)
- goto out_unmap;
- blob_size = 2 * map_size;
- blob = para_malloc(blob_size);
- ret = uudecode(cp, blob, blob_size);
+ ret = uudecode(cp, encoded_size, (char **)&blob, &decoded_size);
if (ret < 0)
goto out_unmap;
if (ret < 0)
goto out_unmap;
ret = check_ssh_key_header(blob, decoded_size);
if (ret < 0)
goto out_unmap;
ret = check_ssh_key_header(blob, decoded_size);
if (ret < 0)
goto out_unmap;
key[j++] = begin[i];
}
key[j] = '\0';
key[j++] = begin[i];
}
key[j] = '\0';
- blob_size = key_size * 2;
- blob = para_malloc(blob_size);
- ret = base64_decode(key, blob, blob_size);
+ ret = base64_decode(key, j, (char **)&blob, &blob_size);
free(key);
if (ret < 0)
goto free_unmap;
free(key);
if (ret < 0)
goto free_unmap;
goto unmap;
free_unmap:
free(blob);
goto unmap;
free_unmap:
free(blob);
gcry_mpi_t e = NULL, n = NULL;
PARA_DEBUG_LOG("decoding %d byte public rsa-ssh key\n", size);
gcry_mpi_t e = NULL, n = NULL;
PARA_DEBUG_LOG("decoding %d byte public rsa-ssh key\n", size);
- if (size > INT_MAX / 4)
- return -ERRNO_TO_PARA_ERROR(EOVERFLOW);
- blob = para_malloc(2 * size);
- ret = uudecode((char *)data, blob, 2 * size);
+ ret = uudecode((char *)data, size, (char **)&blob, &decoded_size);
if (ret < 0)
goto free_blob;
if (ret < 0)
goto free_blob;
end = blob + decoded_size;
dump_buffer("decoded key", blob, decoded_size);
ret = check_ssh_key_header(blob, decoded_size);
end = blob + decoded_size;
dump_buffer("decoded key", blob, decoded_size);
ret = check_ssh_key_header(blob, decoded_size);