#define _POSIX_C_SOURCE 200809L #include #include #include "tlse.h" struct pem_loc { size_t start; size_t len; size_t end; }; #if 0 static const unsigned char *find_mem(const char *needle, size_t nlen, const unsigned char *haystack, size_t hlen, size_t *offset) { size_t i; const unsigned char *res = 0; /* can't be found if it's longer */ if (nlen > hlen) { return NULL; } /* empty string at beginning */ if (nlen == 0) { *offset = 0; return haystack; } for (i=0; i hlen) { /* can't match any more */ break; } /* check for first byte */ if (needle[0] == haystack[i]) { if (memcmp(needle, haystack+i, nlen) == 0) { res = haystack+i; if (offset) { *offset = i; } break; } } } return res; } #endif #if 0 /* fills in a struct pem_loc with offsets from in to the start * of a pem, and one past the end, and the length of the pem data */ static const unsigned char *next_pem(const unsigned char *in, size_t len, struct pem_loc *pem) { size_t i; const unsigned char *next; next = find_mem("BEGIN CERT", 10, in, len, &i); if (!next) { return NULL; } while (i= len) { return NULL; } i++; pem->start = i; /* look for terminating line */ next = find_mem("END CERT", 8, in+pem->start, len, &i); if (!next) { return NULL; } i += pem->start; pem->end = i; /* find prev newline */ while (in[i] != '\n') { i--; } pem->len = i - pem->start; i += pem->start; /* skip over the terminating line */ while (pem->end < len && in[pem->end] != '\n') { pem->end++; } #if 0 fprintf(stderr, "found cert:\n"); write(2, in+pem->start, pem->len); write(2, in+pem->start+pem->len, pem->end-pem->start-pem->len+1); #endif return in + pem->start; } #endif unsigned char *uudecode(const unsigned char *in, size_t len, size_t *outlen) { size_t need = len/4 * 3 + 1; unsigned char *out; out = malloc(need); if (out) { base64decode((const char *)in, len, out, &need); if (outlen) { *outlen = need; } } return out; } unsigned char *tls_pem_decode(const unsigned char *data_in, unsigned int input_length, int cert_index, unsigned int *output_len) { unsigned int i; size_t alloc_len = input_length / 4 * 3; unsigned char *output = malloc(alloc_len); *output_len = 0; if (!output) return NULL; unsigned int start_at = 0; unsigned int idx = 0; for (i = 0; i < input_length; i++) { if ((data_in[i] == '\n') || (data_in[i] == '\r')) continue; if (data_in[i] != '-') { /* read entire line */ while ((i < input_length) && (data_in[i] != '\n')) i++; continue; } if (data_in[i] == '-') { unsigned int end_idx = i; /* read until end of line */ while ((i < input_length) && (data_in[i] != '\n')) i++; if (start_at) { if (cert_index > 0) { cert_index--; start_at = 0; } else { base64decode((const char *) &data_in [start_at], end_idx - start_at, output, &alloc_len); idx = alloc_len; break; } } else start_at = i + 1; } } *output_len = idx; if (!idx) { free(output); return NULL; } return output; } #if 0 int load_cert_list(struct TLSContext *tls, const unsigned char *buf, size_t bufsize, struct TLSCertificate **certs, int flags) { struct pem_loc loc = { 0 }; struct TLSCertificate *cert = 0; const unsigned char *pem = buf; int count = 0; while (bufsize > 0) { if (bufsize <= loc.end) { break; } bufsize -= loc.end; //fprintf(stderr, "checking %zu bytes at %p\n", remaining, pem); pem = next_pem(pem, bufsize, &loc); if (!pem) { break; } cert = asn1_parse(tls, buf+loc.start, loc.len, 0); if (!cert) { continue; } #ifdef TLS_X509_V1_SUPPORT if (cert->version != 2 && cert->version != 0) #else if (cert->version != 2) #endif { free(cert); cert = 0; } count++; //fprintf(stderr, "found length %zu pem starting at %zu taking %zu bytes\n", //loc.len, //loc.start, //loc.end); } return 0; } #endif int add_to_list(struct TLSCertificate ***list, int *len, struct TLSCertificate *cert) { void *new; new = TLS_REALLOC(*list, (*len+1) * sizeof *list); if (new) { *list = new; (*list)[(*len)++] = cert; } else { return 0; } return *len; } int tls_load_certificates(struct TLSContext *tls, const unsigned char *pem, int size) { if (!tls) return TLS_GENERIC_ERROR; unsigned int len; int idx = 0; do { unsigned char *data = tls_pem_decode(pem, size, idx++, &len); if ((!data) || (!len)) break; struct TLSCertificate *cert = asn1_parse(tls, data, len, 0); if (cert) { if ((cert->version == 2) #ifdef TLS_X509_V1_SUPPORT || (cert->version == 0) #endif ) { free(cert->der_bytes); cert->der_bytes = data; cert->der_len = len; data = NULL; if (cert->priv) { DEBUG_PRINT ("WARNING - parse error (private key encountered in certificate)\n"); free(cert->priv); cert->priv = NULL; cert->priv_len = 0; } add_to_list(&tls->certificates, &tls->certificates_count, cert); } else { DEBUG_PRINT ("WARNING - certificate version error (v%d)\n", (int) cert->version); tls_destroy_certificate(cert); } } free(data); } while (1); return tls->certificates_count; } int tls_load_private_key(struct TLSContext *tls, const unsigned char *pem_buffer, int pem_size) { if (!tls) return TLS_GENERIC_ERROR; unsigned int len; int idx = 0; do { unsigned char *data = tls_pem_decode(pem_buffer, pem_size, idx++, &len); if ((!data) || (!len)) break; struct TLSCertificate *cert = asn1_parse(tls, data, len, -1); if (cert) { if (!cert->der_len) { free(cert->der_bytes); cert->der_bytes = data; cert->der_len = len; } else { free(data); } if ((cert) && (cert->priv) && (cert->priv_len)) { #ifdef TLS_ECDSA_SUPPORTED if (cert->ec_algorithm) { DEBUG_PRINT ("Loaded ECC private key\n"); if (tls->ec_private_key) tls_destroy_certificate (tls->ec_private_key); tls->ec_private_key = cert; return 1; } else #endif { DEBUG_PRINT ("Loaded private key\n"); if (tls->private_key) tls_destroy_certificate (tls->private_key); tls->private_key = cert; return 1; } } tls_destroy_certificate(cert); } else free(data); } while (1); return 0; } int tls_load_root_certificates(struct TLSContext *tls, const unsigned char *pem_buffer, int pem_size) { if (!tls) return -1; unsigned int len; int idx = 0; do { unsigned char *data = tls_pem_decode(pem_buffer, pem_size, idx++, &len); if (!data || !len) { break; } struct TLSCertificate *cert = asn1_parse(NULL, data, len, 0); if (cert) { if (cert->version == 2) { if (cert->priv) { DEBUG_PRINT ("WARNING - parse error (private key encountered in certificate)\n"); free(cert->priv); cert->priv = NULL; cert->priv_len = 0; } add_to_list(&tls->root_certificates, &tls->root_count, cert); DEBUG_PRINT("Loaded certificate: %d\n", tls->root_count); } else { DEBUG_PRINT ("WARNING - certificate version error (v%d)\n", (int) cert->version); tls_destroy_certificate(cert); } } free(data); } while (1); return tls->root_count; }