aboutsummaryrefslogtreecommitdiff
path: root/lib/vtls
diff options
context:
space:
mode:
authorVilmos Nebehaj <v.nebehaj@gmail.com>2014-08-28 21:31:17 +0200
committerVilmos Nebehaj <v.nebehaj@gmail.com>2014-08-30 20:10:07 +0200
commit4c134bcfcefa8a78e77afde03f74104b88b45dad (patch)
treea00194f030590415dcd81ac6b1b203e9b8b0c94a /lib/vtls
parent2434a4e88de35a0c7eced46f010292e2b4bfb851 (diff)
Fix CA certificate bundle handling in darwinssl.
If the --cacert option is used with a CA certificate bundle that contains multiple CA certificates, iterate through it, adding each certificate as a trusted root CA.
Diffstat (limited to 'lib/vtls')
-rw-r--r--lib/vtls/curl_darwinssl.c163
1 files changed, 111 insertions, 52 deletions
diff --git a/lib/vtls/curl_darwinssl.c b/lib/vtls/curl_darwinssl.c
index bfa5c6acd..9ba287d0e 100644
--- a/lib/vtls/curl_darwinssl.c
+++ b/lib/vtls/curl_darwinssl.c
@@ -1527,43 +1527,42 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
return CURLE_OK;
}
-static int pem_to_der(const char *in, unsigned char **out, size_t *outlen)
+static long pem_to_der(const char *in, unsigned char **out, size_t *outlen)
{
- char *sep, *start, *end;
+ char *sep_start, *sep_end, *cert_start, *cert_end;
size_t i, j, err;
size_t len;
unsigned char *b64;
- /* Jump through the separators in the first line. */
- sep = strstr(in, "-----");
- if(sep == NULL)
- return -1;
- sep = strstr(sep + 1, "-----");
- if(sep == NULL)
+ /* Jump through the separators at the beginning of the certificate. */
+ sep_start = strstr(in, "-----");
+ if(sep_start == NULL)
+ return 0;
+ cert_start = strstr(sep_start + 1, "-----");
+ if(cert_start == NULL)
return -1;
- start = sep + 5;
+ cert_start += 5;
- /* Find beginning of last line separator. */
- end = strstr(start, "-----");
- if(end == NULL)
+ /* Find separator after the end of the certificate. */
+ cert_end = strstr(cert_start, "-----");
+ if(cert_end == NULL)
return -1;
- len = end - start;
- *out = malloc(len);
- if(!*out)
+ sep_end = strstr(cert_end + 1, "-----");
+ if(sep_end == NULL)
return -1;
+ sep_end += 5;
+ len = cert_end - cert_start;
b64 = malloc(len + 1);
- if(!b64) {
- free(*out);
+ if(!b64)
return -1;
- }
/* Create base64 string without linefeeds. */
for(i = 0, j = 0; i < len; i++) {
- if(start[i] != '\r' && start[i] != '\n')
- b64[j++] = start[i];
+ if(cert_start[i] != '\r' && cert_start[i] != '\n')
+ b64[j++] = cert_start[i];
}
b64[j] = '\0';
@@ -1574,15 +1573,14 @@ static int pem_to_der(const char *in, unsigned char **out, size_t *outlen)
return -1;
}
- return 0;
+ return sep_end - in;
}
static int read_cert(const char *file, unsigned char **out, size_t *outlen)
{
int fd;
ssize_t n, len = 0, cap = 512;
- size_t derlen;
- unsigned char buf[cap], *data, *der;
+ unsigned char buf[cap], *data;
fd = open(file, 0);
if(fd < 0)
@@ -1620,16 +1618,6 @@ static int read_cert(const char *file, unsigned char **out, size_t *outlen)
}
data[len] = '\0';
- /*
- * Check if the certificate is in PEM format, and convert it to DER. If this
- * fails, we assume the certificate is in DER format.
- */
- if(pem_to_der((const char *)data, &der, &derlen) == 0) {
- free(data);
- data = der;
- len = derlen;
- }
-
*out = data;
*outlen = len;
@@ -1665,51 +1653,124 @@ static int sslerr_to_curlerr(struct SessionHandle *data, int err)
}
}
+static int append_cert_to_array(struct SessionHandle *data,
+ unsigned char *buf, size_t buflen,
+ CFMutableArrayRef array)
+{
+ CFDataRef certdata = CFDataCreate(kCFAllocatorDefault, buf, buflen);
+ if(!certdata) {
+ failf(data, "SSL: failed to allocate array for CA certificate");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ SecCertificateRef cacert =
+ SecCertificateCreateWithData(kCFAllocatorDefault, certdata);
+ CFRelease(certdata);
+ if(!cacert) {
+ failf(data, "SSL: failed to create SecCertificate from CA certificate");
+ return CURLE_SSL_CACERT;
+ }
+
+ CFArrayAppendValue(array, cacert);
+ CFRelease(cacert);
+
+ return CURLE_OK;
+}
+
static int verify_cert(const char *cafile, struct SessionHandle *data,
SSLContextRef ctx)
{
- unsigned char *certbuf;
- size_t buflen;
+ int n = 0, rc;
+ long res;
+ unsigned char *certbuf, *der;
+ size_t buflen, derlen, offset = 0;
+
if(read_cert(cafile, &certbuf, &buflen) < 0) {
failf(data, "SSL: failed to read or invalid CA certificate");
return CURLE_SSL_CACERT;
}
- CFDataRef certdata = CFDataCreate(kCFAllocatorDefault, certbuf, buflen);
- free(certbuf);
- if(!certdata) {
- failf(data, "SSL: failed to allocate array for CA certificate");
+ /*
+ * Certbuf now contains the contents of the certificate file, which can be
+ * - a single DER certificate,
+ * - a single PEM certificate or
+ * - a bunch of PEM certificates (certificate bundle).
+ *
+ * Go through certbuf, and convert any PEM certificate in it into DER
+ * format.
+ */
+ CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeArrayCallBacks);
+ if(array == NULL) {
+ free(certbuf);
+ failf(data, "SSL: out of memory creating CA certificate array");
return CURLE_OUT_OF_MEMORY;
}
- SecCertificateRef cacert = SecCertificateCreateWithData(kCFAllocatorDefault,
- certdata);
- CFRelease(certdata);
- if(!cacert) {
- failf(data, "SSL: failed to create SecCertificate from CA certificate");
- return CURLE_SSL_CACERT;
+ while(offset < buflen) {
+ n++;
+
+ /*
+ * Check if the certificate is in PEM format, and convert it to DER. If
+ * this fails, we assume the certificate is in DER format.
+ */
+ res = pem_to_der((const char *)certbuf + offset, &der, &derlen);
+ if(res < 0) {
+ free(certbuf);
+ CFRelease(array);
+ failf(data, "SSL: invalid CA certificate #%d (offset %d) in bundle",
+ n, offset);
+ return CURLE_SSL_CACERT;
+ }
+ offset += res;
+
+ if(res == 0 && offset == 0) {
+ /* This is not a PEM file, probably a certificate in DER format. */
+ rc = append_cert_to_array(data, certbuf, buflen, array);
+ free(certbuf);
+ if(rc != CURLE_OK) {
+ CFRelease(array);
+ return rc;
+ }
+ break;
+ }
+ else if(res == 0) {
+ /* No more certificates in the bundle. */
+ free(certbuf);
+ break;
+ }
+
+ rc = append_cert_to_array(data, der, derlen, array);
+ free(der);
+ if(rc != CURLE_OK) {
+ free(certbuf);
+ CFRelease(array);
+ return rc;
+ }
}
SecTrustRef trust;
OSStatus ret = SSLCopyPeerTrust(ctx, &trust);
if(trust == NULL) {
failf(data, "SSL: error getting certificate chain");
+ CFRelease(array);
return CURLE_OUT_OF_MEMORY;
}
else if(ret != noErr) {
+ CFRelease(array);
return sslerr_to_curlerr(data, ret);
}
- CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeArrayCallBacks);
- CFArrayAppendValue(array, cacert);
- CFRelease(cacert);
-
ret = SecTrustSetAnchorCertificates(trust, array);
if(ret != noErr) {
CFRelease(trust);
return sslerr_to_curlerr(data, ret);
}
+ ret = SecTrustSetAnchorCertificatesOnly(trust, true);
+ if(ret != noErr) {
+ CFRelease(trust);
+ return sslerr_to_curlerr(data, ret);
+ }
SecTrustResultType trust_eval = 0;
ret = SecTrustEvaluate(trust, &trust_eval);
@@ -1722,8 +1783,6 @@ static int verify_cert(const char *cafile, struct SessionHandle *data,
switch (trust_eval) {
case kSecTrustResultUnspecified:
case kSecTrustResultProceed:
- infof(data, "SSL: certificate verification succeeded (result: %d)",
- trust_eval);
return CURLE_OK;
case kSecTrustResultRecoverableTrustFailure: