+/* This implementation seems somewhat ugly, but it's the way the
+ * GnuTLS implements the same thing internally, so it should probably
+ * be interoperable, at least. */
+static int readcrtchain(struct certbuffer *ret, struct charbuf *pem)
+{
+ static char *headers[] = {"-----BEGIN CERTIFICATE", "-----BEGIN X509 CERTIFICATE"};
+ int i, rv;
+ char *p, *p2, *f;
+ gnutls_x509_crt_t crt;
+
+ for(i = 0, p = NULL; i < sizeof(headers) / sizeof(*headers); i++) {
+ f = memmem(pem->b, pem->d, headers[i], strlen(headers[i]));
+ if((f != NULL) && ((p == NULL) || (f < p)))
+ p = f;
+ }
+ if(p == NULL)
+ return(-GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ do {
+ if((rv = gnutls_x509_crt_init(&crt)) < 0)
+ goto error;
+ if((rv = gnutls_x509_crt_import(crt, &(gnutls_datum_t){.data = (unsigned char *)p, .size = pem->d - (p - pem->b)}, GNUTLS_X509_FMT_PEM)) < 0) {
+ gnutls_x509_crt_deinit(crt);
+ goto error;
+ }
+ bufadd(*ret, crt);
+ for(i = 0, p2 = NULL; i < sizeof(headers) / sizeof(*headers); i++) {
+ f = memmem(p + 1, pem->d - (p + 1 - pem->b), headers[i], strlen(headers[i]));
+ if((f != NULL) && ((p2 == NULL) || (f < p2)))
+ p2 = f;
+ }
+ } while((p = p2) != NULL);
+ return(0);
+error:
+ for(i = 0; i < ret->d; i++)
+ gnutls_x509_crt_deinit(ret->b[i]);
+ ret->d = 0;
+ return(rv);
+}
+
+static struct namedcreds *readncreds(char *file, gnutls_x509_privkey_t defkey)