--- /dev/null
+#define _POSIX_C_SOURCE 200809L
+
+#include <stdio.h>
+#include <unistd.h>
+
+#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; i++) {
+ if (nlen + 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 && in[i] != '\n') {
+ i++;
+ }
+ if (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;
+}
+