diff --git a/src/headers/tomcrypt_private.h b/src/headers/tomcrypt_private.h index 4c21a7e6..6dd88fc6 100644 --- a/src/headers/tomcrypt_private.h +++ b/src/headers/tomcrypt_private.h @@ -382,12 +382,23 @@ 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_api { + int (*get)(struct get_char*); + int (*reset)(struct get_char*); + unsigned long (*len)(struct get_char*); +}; struct get_char { - int (*get)(struct get_char*); + struct get_char_api api; union { #ifndef LTC_NO_FILE - FILE *f; + struct { + FILE *f; + long original_pos; + } f; #endif /* LTC_NO_FILE */ struct bufp buf; } data; @@ -395,6 +406,17 @@ struct get_char { char unget_buf_[LTC_PEM_DECODE_BUFSZ]; int prev_get; }; + +#define pem_get_char_init(b, l) { \ + .api = get_char_buffer_api, \ + SET_BUFP(.data.buf, (b), (l)) \ +} + +#define pem_get_char_init_filehandle(fi) { \ + .api = get_char_filehandle_api, \ + .data.f.f = (fi), \ + .data.f.original_pos = ftell(fi) \ +} #endif /* others */ @@ -417,10 +439,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 */ @@ -709,7 +731,7 @@ int x509_decode_subject_public_key_info(const unsigned char *in, unsigned long i int x509_get_pka(const ltc_asn1_list *pub, enum ltc_pka_id *pka); int x509_get_sig_alg(const ltc_asn1_list *pub, ltc_x509_signature_algorithm *sig_alg); -int x509_import_spki(const unsigned char *asn1_cert, unsigned long asn1_len, ltc_pka_key *k, ltc_asn1_list **root); +int x509_import_spki(const unsigned char *buf, unsigned long len, ltc_pka_key *k, ltc_asn1_list **root); int x509_get_extensions(const ltc_asn1_list *seq, ltc_x509_extensions *extensions); void x509_free_extensions(const ltc_x509_extensions *extensions); int x509_get_serial(const ltc_asn1_list *asn1, ltc_x509_string *serial); diff --git a/src/misc/pem/pem_pkcs.c b/src/misc/pem/pem_pkcs.c index 7d726645..17aa4770 100644 --- a/src/misc/pem/pem_pkcs.c +++ b/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); } } diff --git a/src/misc/pem/pem_read.c b/src/misc/pem/pem_read.c index abefff92..15e67a5b 100644 --- a/src/misc/pem/pem_read.c +++ b/src/misc/pem/pem_read.c @@ -17,14 +17,98 @@ extern const struct str pem_dek_info_start; extern const struct blockcipher_info pem_dek_infos[]; extern const unsigned long pem_dek_infos_num; -#ifndef LTC_NO_FILE -int pem_get_char_from_file(struct get_char *g) +static LTC_INLINE unsigned long s_bufp_alloc_len(struct bufp *buf) { - return getc(g->data.f); + 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 +static int s_pem_get_char_from_file(struct get_char *g) +{ + return getc(g->data.f.f); +} + +static int s_pem_get_char_reset_file(struct get_char *g) +{ + if (g->data.f.original_pos == -1) + return -1; + return fseek(g->data.f.f, g->data.f.original_pos, SEEK_SET); +} + +static unsigned long s_pem_get_char_len_file(struct get_char *g) +{ + FILE *f = g->data.f.f; + long len, cur_pos = ftell(f); + if (cur_pos != -1) { + fseek(f, 0, SEEK_END); + len = ftell(f); + fseek(f, cur_pos, SEEK_SET); + return len - cur_pos; + } + return LTC_PEM_READ_BUFSIZE * 2; +} + +const struct get_char_api get_char_filehandle_api = { + .get = s_pem_get_char_from_file, + .reset = s_pem_get_char_reset_file, + .len = s_pem_get_char_len_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 +119,25 @@ int pem_get_char_from_buf(struct get_char *g) return ret; } +static int s_pem_get_char_reset(struct get_char *g) +{ + g->data.buf.work = g->data.buf.start; + return 0; +} + + + +static unsigned long s_pem_get_char_len(struct get_char *g) +{ + return s_bufp_alloc_len(&g->data.buf); +} + +const struct get_char_api get_char_buffer_api = { + .get = s_pem_get_char_from_buf, + .reset = s_pem_get_char_reset, + .len = s_pem_get_char_len, +}; + static void s_unget_line(char *buf, unsigned long buflen, struct get_char *g) { if (buflen > sizeof(g->unget_buf_)) @@ -81,7 +184,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') { @@ -103,26 +206,16 @@ static char* s_get_line_i(char *buf, unsigned long *buflen, struct get_char *g, 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,21 +283,19 @@ 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; 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) { + linelen = sizeof(line); + if (s_get_first_line(line, &linelen, g) == NULL) { if (g->prev_get == -1) return CRYPT_NOP; else @@ -212,9 +303,9 @@ int pem_read(void *asn1_cert, unsigned long *asn1_len, struct pem_headers *hdr, } if (linelen < 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 != linelen || XMEMCMP(line, hdr->id->start.p, hdr->id->start.len)) { + s_unget_line(line, linelen, g); return CRYPT_UNKNOWN_PEM; } @@ -223,9 +314,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 +325,25 @@ 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); } - - *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; + if (err == CRYPT_OK) { + *dest = b->start; + *len = slen; + } else { +error_out: + XFREE(b->start); } - return CRYPT_OK; + return err; } #endif /* LTC_PEM */ diff --git a/src/misc/pem/pem_ssh.c b/src/misc/pem/pem_ssh.c index 0117dbf3..eb8f419f 100644 --- a/src/misc/pem/pem_ssh.c +++ b/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); } } diff --git a/src/pk/asn1/x509/x509_import.c b/src/pk/asn1/x509/x509_import.c index fa5ee174..c1b65ebd 100644 --- a/src/pk/asn1/x509/x509_import.c +++ b/src/pk/asn1/x509/x509_import.c @@ -592,35 +592,24 @@ static int s_x509_import_pem(struct get_char *g, unsigned long *pem_len, const l { int err; struct pem_headers hdr = { .id = &pem_std_headers[0] }; - unsigned long alloc_len = *pem_len, len; + unsigned long len = 0; unsigned char *asn1_cert = NULL; - len = alloc_len; -retry: - asn1_cert = XREALLOC(asn1_cert, alloc_len); - if (asn1_cert == NULL) { - err = CRYPT_MEM; - goto err_out; - } - if ((err = pem_read(asn1_cert, &len, &hdr, g)) != CRYPT_OK) { - if (err == CRYPT_BUFFER_OVERFLOW && alloc_len == *pem_len) { - alloc_len = len; - goto retry; - } - goto err_out; + if ((err = pem_read((void**)&asn1_cert, &len, &hdr, g)) != CRYPT_OK) { + return err; } err = x509_import(asn1_cert, len, out); *pem_len = len; -err_out: XFREE(asn1_cert); + return err; } int x509_import_pem(const char *pem, unsigned long *pem_len, const ltc_x509_certificate **out) { - struct get_char g = { .get = pem_get_char_from_buf, SET_BUFP(.data.buf, pem, *pem_len) }; + struct get_char g = pem_get_char_init(pem, *pem_len); return s_x509_import_pem(&g, pem_len, out); } @@ -628,17 +617,7 @@ int x509_import_pem(const char *pem, unsigned long *pem_len, const ltc_x509_cert int x509_import_pem_filehandle(FILE *f, const ltc_x509_certificate **out) { unsigned long pem_len; - long cur_pos; - struct get_char g = { .get = pem_get_char_from_file, .data.f = f }; - cur_pos = ftell(f); - if (cur_pos != -1) { - fseek(f, 0, SEEK_END); - pem_len = ftell(f); - fseek(f, cur_pos, SEEK_SET); - pem_len -= cur_pos; - } else { - pem_len = LTC_PEM_READ_BUFSIZE; - } + struct get_char g = pem_get_char_init_filehandle(f); return s_x509_import_pem(&g, &pem_len, out); } #endif