Selaa lähdekoodia

Put realloc logic into `pem_read()`

Signed-off-by: Steffen Jaeckel <[email protected]>
Steffen Jaeckel 3 kuukautta sitten
vanhempi
sitoutus
ea672df7a3
4 muutettua tiedostoa jossa 141 lisäystä ja 80 poistoa
  1. 26 5
      src/headers/tomcrypt_private.h
  2. 5 10
      src/misc/pem/pem_pkcs.c
  3. 102 54
      src/misc/pem/pem_read.c
  4. 8 11
      src/misc/pem/pem_ssh.c

+ 26 - 5
src/headers/tomcrypt_private.h

@@ -352,19 +352,40 @@ struct bufp {
 };
 
 #define SET_BUFP(n, d, l) n.start = (char*)d, n.work = (char*)d, n.end = (char*)d + l + 1
+#define UPDATE_BUFP(n, d, w, l) n.start = (char*)d, n.work = (char*)d + w, n.end = (char*)d + l + 1
 
-struct get_char {
+struct get_char;
+struct get_char_api {
    int (*get)(struct get_char*);
+};
+
+struct get_char {
+   struct get_char_api api;
    union {
 #ifndef LTC_NO_FILE
-      FILE *f;
+      struct {
+         FILE *f;
+      } f;
 #endif /* LTC_NO_FILE */
       struct bufp buf;
    } data;
    struct str unget_buf;
    char unget_buf_[LTC_PEM_DECODE_BUFSZ];
    int prev_get;
+   unsigned long total_read;
 };
+
+#define pem_get_char_init(b, l)   { \
+   .api = get_char_buffer_api, \
+   SET_BUFP(.data.buf, (b), (l)), \
+   .total_read = 0, \
+}
+
+#define pem_get_char_init_filehandle(fi)    { \
+   .api = get_char_filehandle_api, \
+   .data.f.f = (fi), \
+   .total_read = 0, \
+}
 #endif
 
 /* others */
@@ -387,10 +408,10 @@ int pem_decrypt(unsigned char *data, unsigned long *datalen,
                 const struct blockcipher_info *info,
                 enum padding_type padding);
 #ifndef LTC_NO_FILE
-int pem_get_char_from_file(struct get_char *g);
+extern const struct get_char_api get_char_filehandle_api;
 #endif /* LTC_NO_FILE */
-int pem_get_char_from_buf(struct get_char *g);
-int pem_read(void *asn1_cert, unsigned long *asn1_len, struct pem_headers *hdr, struct get_char *g);
+extern const struct get_char_api get_char_buffer_api;
+int pem_read(void **dest, unsigned long *len, struct pem_headers *hdr, struct get_char *g);
 #endif
 
 /* tomcrypt_pk.h */

+ 5 - 10
src/misc/pem/pem_pkcs.c

@@ -127,21 +127,16 @@ static const import_fn s_import_openssl_fns[LTC_PKA_NUM] = {
 static int s_decode(struct get_char *g, ltc_pka_key *k, const password_ctx *pw_ctx)
 {
    unsigned char *asn1_cert = NULL;
-   unsigned long w, asn1_len, n;
+   unsigned long w = 0, asn1_len, n;
    int err = CRYPT_ERROR;
    struct pem_headers hdr = { 0 };
    struct password pw = { 0 };
    enum ltc_pka_id pka;
    XMEMSET(k, 0, sizeof(*k));
-   w = LTC_PEM_READ_BUFSIZE * 2;
-retry:
-   asn1_cert = XREALLOC(asn1_cert, w);
    for (n = 0; n < pem_std_headers_num; ++n) {
       hdr.id = &pem_std_headers[n];
-      err = pem_read(asn1_cert, &w, &hdr, g);
-      if (err == CRYPT_BUFFER_OVERFLOW) {
-         goto retry;
-      } else if (err == CRYPT_OK) {
+      err = pem_read((void**)&asn1_cert, &w, &hdr, g);
+      if (err == CRYPT_OK) {
          break;
       } else if (err != CRYPT_UNKNOWN_PEM) {
          goto cleanup;
@@ -204,7 +199,7 @@ int pem_decode_pkcs_filehandle(FILE *f, ltc_pka_key *k, const password_ctx *pw_c
    LTC_ARGCHK(f != NULL);
    LTC_ARGCHK(k != NULL);
    {
-      struct get_char g = { .get = pem_get_char_from_file, .data.f = f };
+      struct get_char g = pem_get_char_init_filehandle(f);
       return s_decode(&g, k, pw_ctx);
    }
 }
@@ -216,7 +211,7 @@ int pem_decode_pkcs(const void *buf, unsigned long len, ltc_pka_key *k, const pa
    LTC_ARGCHK(len != 0);
    LTC_ARGCHK(k != NULL);
    {
-      struct get_char g = { .get = pem_get_char_from_buf, SET_BUFP(.data.buf, buf, len) };
+      struct get_char g = pem_get_char_init(buf, len);
       return s_decode(&g, k, pw_ctx);
    }
 }

+ 102 - 54
src/misc/pem/pem_read.c

@@ -17,14 +17,76 @@ extern const struct str pem_dek_info_start;
 extern const struct blockcipher_info pem_dek_infos[];
 extern const unsigned long pem_dek_infos_num;
 
+static LTC_INLINE unsigned long s_bufp_alloc_len(struct bufp *buf)
+{
+   if (buf->start == NULL || buf->end == NULL)
+      return 0;
+   return buf->end - buf->start - 1;
+}
+
+static LTC_INLINE unsigned long s_bufp_used_len(struct bufp *buf)
+{
+   if (buf->start == NULL || buf->end == NULL)
+      return 0;
+   return buf->work - buf->start;
+}
+
+static LTC_INLINE int s_bufp_grow(struct bufp *buf)
+{
+   int err = CRYPT_OK;
+   void *ret;
+   unsigned long alloc_len = s_bufp_alloc_len(buf), realloc_len;
+   unsigned long work_offset = s_bufp_used_len(buf);
+   if (alloc_len == 0)
+      realloc_len = LTC_PEM_READ_BUFSIZE;
+   else
+      realloc_len = alloc_len * 2;
+   if (realloc_len < alloc_len)
+      return CRYPT_OVERFLOW;
+   ret = XREALLOC(buf->start, realloc_len);
+   if (ret == NULL) {
+      err = CRYPT_MEM;
+   } else {
+      UPDATE_BUFP((*buf), ret, work_offset, realloc_len);
+   }
+   return err;
+}
+
+static LTC_INLINE int s_bufp_fits(struct bufp *buf, unsigned long to_write)
+{
+   char *d = buf->work;
+   char *e = buf->end;
+   char *w = d + to_write;
+   if (d == NULL || w < d || w > e)
+      return 0;
+   return 1;
+}
+
+static LTC_INLINE int s_bufp_add(struct bufp *buf, const void *src, unsigned long len)
+{
+   int err;
+   if (!s_bufp_fits(buf, len)) {
+      if ((err = s_bufp_grow(buf)) != CRYPT_OK) {
+         return err;
+      }
+   }
+   XMEMCPY(buf->work, src, len);
+   buf->work += len;
+   return CRYPT_OK;
+}
+
 #ifndef LTC_NO_FILE
-int pem_get_char_from_file(struct get_char *g)
+static int s_pem_get_char_from_file(struct get_char *g)
 {
-   return getc(g->data.f);
+   return getc(g->data.f.f);
 }
+
+const struct get_char_api get_char_filehandle_api = {
+                                                     .get = s_pem_get_char_from_file,
+};
 #endif /* LTC_NO_FILE */
 
-int pem_get_char_from_buf(struct get_char *g)
+static int s_pem_get_char_from_buf(struct get_char *g)
 {
    int ret;
    if (g->data.buf.work == g->data.buf.end) {
@@ -35,6 +97,10 @@ int pem_get_char_from_buf(struct get_char *g)
    return ret;
 }
 
+const struct get_char_api get_char_buffer_api = {
+                                                     .get = s_pem_get_char_from_buf,
+};
+
 static void s_unget_line(char *buf, unsigned long buflen, struct get_char *g)
 {
    if (buflen > sizeof(g->unget_buf_))
@@ -81,7 +147,7 @@ static char* s_get_line_i(char *buf, unsigned long *buflen, struct get_char *g,
    while(blen < *buflen || search_for_start) {
       wr = blen < *buflen ? blen : *buflen - 1;
       c_ = g->prev_get;
-      g->prev_get = g->get(g);
+      g->prev_get = g->api.get(g);
       if (g->prev_get == '\n') {
          buf[wr] = '\0';
          if (c_ == '\r') {
@@ -89,6 +155,7 @@ static char* s_get_line_i(char *buf, unsigned long *buflen, struct get_char *g,
          }
          s_tts(buf, &wr);
          *buflen = wr;
+         g->total_read++;
          return buf;
       }
       if (g->prev_get == -1 || g->prev_get == '\0') {
@@ -99,30 +166,21 @@ static char* s_get_line_i(char *buf, unsigned long *buflen, struct get_char *g,
       }
       buf[wr] = g->prev_get;
       blen++;
+      g->total_read++;
    }
    return NULL;
 }
 
-LTC_INLINE static char* s_get_first_line(char *buf, unsigned long *buflen, struct get_char *g)
+static LTC_INLINE char* s_get_first_line(char *buf, unsigned long *buflen, struct get_char *g)
 {
    return s_get_line_i(buf, buflen, g, 1);
 }
 
-LTC_INLINE static char* s_get_line(char *buf, unsigned long *buflen, struct get_char *g)
+static LTC_INLINE char* s_get_line(char *buf, unsigned long *buflen, struct get_char *g)
 {
    return s_get_line_i(buf, buflen, g, 0);
 }
 
-static LTC_INLINE int s_fits_buf(void *dest, unsigned long to_write, void *end)
-{
-   unsigned char *d = dest;
-   unsigned char *e = end;
-   unsigned char *w = d + to_write;
-   if (w < d || w > e)
-      return 0;
-   return 1;
-}
-
 static int s_pem_decode_headers(struct pem_headers *hdr, struct get_char *g)
 {
    char buf[LTC_PEM_DECODE_BUFSZ], *alg_start;
@@ -190,31 +248,29 @@ static int s_pem_decode_headers(struct pem_headers *hdr, struct get_char *g)
    return CRYPT_OK;
 }
 
-int pem_read(void *asn1_cert, unsigned long *asn1_len, struct pem_headers *hdr, struct get_char *g)
+int pem_read(void **dest, unsigned long *len, struct pem_headers *hdr, struct get_char *g)
 {
-   char buf[LTC_PEM_DECODE_BUFSZ];
-   char *wpem = asn1_cert;
-   char *end = wpem + *asn1_len;
+   char line[LTC_PEM_DECODE_BUFSZ];
+   struct bufp b_ = {0}, *b = &b_;
    const char pem_start[] = "----";
-   unsigned long slen, linelen;
+   unsigned long slen;
    int err, hdr_ok = 0;
-   int would_overflow = 0;
    unsigned char empty_lines = 0;
 
    g->prev_get = 0;
    do {
-      linelen = sizeof(buf);
-      if (s_get_first_line(buf, &linelen, g) == NULL) {
+      slen = sizeof(line);
+      if (s_get_first_line(line, &slen, g) == NULL) {
          if (g->prev_get == -1)
             return CRYPT_NOP;
          else
             return CRYPT_INVALID_PACKET;
       }
-      if (linelen < sizeof(pem_start) - 1)
+      if (slen < sizeof(pem_start) - 1)
          continue;
-   } while(XMEMCMP(buf, pem_start, sizeof(pem_start) - 1) != 0);
-   if (hdr->id->start.len != linelen || XMEMCMP(buf, hdr->id->start.p, hdr->id->start.len)) {
-      s_unget_line(buf, linelen, g);
+   } while(XMEMCMP(line, pem_start, sizeof(pem_start) - 1) != 0);
+   if (hdr->id->start.len != slen || XMEMCMP(line, hdr->id->start.p, hdr->id->start.len)) {
+      s_unget_line(line, slen, g);
       return CRYPT_UNKNOWN_PEM;
    }
 
@@ -223,9 +279,9 @@ int pem_read(void *asn1_cert, unsigned long *asn1_len, struct pem_headers *hdr,
       return err;
 
    /* Read the base64 encoded part of the PEM */
-   slen = sizeof(buf);
-   while (s_get_line(buf, &slen, g)) {
-      if (slen == hdr->id->end.len && !XMEMCMP(buf, hdr->id->end.p, slen)) {
+   slen = sizeof(line);
+   while (s_get_line(line, &slen, g)) {
+      if (slen == hdr->id->end.len && !XMEMCMP(line, hdr->id->end.p, slen)) {
          hdr_ok = 1;
          break;
       }
@@ -234,34 +290,26 @@ int pem_read(void *asn1_cert, unsigned long *asn1_len, struct pem_headers *hdr,
             break;
          empty_lines++;
       }
-      if (!would_overflow && s_fits_buf(wpem, slen, end)) {
-         XMEMCPY(wpem, buf, slen);
-      } else {
-         would_overflow = 1;
+      if ((err = s_bufp_add(b, line, slen)) != CRYPT_OK) {
+         goto error_out;
       }
-      wpem += slen;
-      slen = sizeof(buf);
+      slen = sizeof(line);
    }
-   if (!hdr_ok)
-      return CRYPT_INVALID_PACKET;
-
-   if (would_overflow || !s_fits_buf(wpem, 1, end)) {
-      /* NUL termination */
-      wpem++;
-      /* prevent a wrap-around */
-      if (wpem < (char*)asn1_cert)
-         return CRYPT_OVERFLOW;
-      *asn1_len = wpem - (char*)asn1_cert;
-      return CRYPT_BUFFER_OVERFLOW;
+   if (!hdr_ok) {
+      err = CRYPT_INVALID_PACKET;
+   } else {
+      slen = s_bufp_alloc_len(b);
+      err = base64_strict_decode(b->start, s_bufp_used_len(b), (void*)b->start, &slen);
    }
+   if (err == CRYPT_OK) {
+      *dest = b->start;
+      *len = slen;
 
-   *asn1_len = wpem - (char*)asn1_cert;
-   *wpem++ = '\0';
-
-   if ((err = base64_strict_decode(asn1_cert, *asn1_len, asn1_cert, asn1_len)) != CRYPT_OK) {
-      return err;
+   } else {
+error_out:
+      XFREE(b->start);
    }
-   return CRYPT_OK;
+   return err;
 }
 
 #endif /* LTC_PEM */

+ 8 - 11
src/misc/pem/pem_ssh.c

@@ -712,20 +712,15 @@ static const unsigned long pem_openssh_num = LTC_ARRAY_SIZE(pem_openssh);
 static int s_decode_openssh(struct get_char *g, ltc_pka_key *k, const password_ctx *pw_ctx)
 {
    unsigned char *pem = NULL, *p, *privkey = NULL, *tag;
-   unsigned long n, w, l, privkey_len, taglen;
+   unsigned long n, w = 0, l, privkey_len, taglen;
    int err;
    struct pem_headers hdr;
    struct kdf_options opts = { 0 };
    XMEMSET(k, 0, sizeof(*k));
-   w = LTC_PEM_READ_BUFSIZE * 2;
-retry:
-   pem = XREALLOC(pem, w);
    for (n = 0; n < pem_openssh_num; ++n) {
       hdr.id = &pem_openssh[n];
-      err = pem_read(pem, &w, &hdr, g);
-      if (err == CRYPT_BUFFER_OVERFLOW) {
-         goto retry;
-      } else if (err == CRYPT_OK) {
+      err = pem_read((void**)&pem, &w, &hdr, g);
+      if (err == CRYPT_OK) {
          break;
       } else if (err != CRYPT_UNKNOWN_PEM) {
          goto cleanup;
@@ -791,7 +786,9 @@ cleanup:
       zeromem(privkey, privkey_len);
       XFREE(privkey);
    }
-   XFREE(pem);
+   if (pem) {
+      XFREE(pem);
+   }
    return err;
 }
 
@@ -801,7 +798,7 @@ int pem_decode_openssh_filehandle(FILE *f, ltc_pka_key *k, const password_ctx *p
    LTC_ARGCHK(f != NULL);
    LTC_ARGCHK(k != NULL);
    {
-      struct get_char g = { .get = pem_get_char_from_file, .data.f = f };
+      struct get_char g = pem_get_char_init_filehandle(f);
       return s_decode_openssh(&g, k, pw_ctx);
    }
 }
@@ -841,7 +838,7 @@ int pem_decode_openssh(const void *buf, unsigned long len, ltc_pka_key *k, const
    LTC_ARGCHK(len != 0);
    LTC_ARGCHK(k != NULL);
    {
-      struct get_char g = { .get = pem_get_char_from_buf, SET_BUFP(.data.buf, buf, len) };
+      struct get_char g = pem_get_char_init(buf, len);
       return s_decode_openssh(&g, k, pw_ctx);
    }
 }