mirror of
https://github.com/moparisthebest/curl
synced 2024-11-16 14:35:03 -05:00
Merge pull request #115 from ldx/darwinsslfixpr
darwinssl: now accepts cacert bundles in PEM format in addition to single certs
This commit is contained in:
commit
0c14b31df4
@ -1527,43 +1527,42 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
|
|||||||
return CURLE_OK;
|
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 i, j, err;
|
||||||
size_t len;
|
size_t len;
|
||||||
unsigned char *b64;
|
unsigned char *b64;
|
||||||
|
|
||||||
/* Jump through the separators in the first line. */
|
/* Jump through the separators at the beginning of the certificate. */
|
||||||
sep = strstr(in, "-----");
|
sep_start = strstr(in, "-----");
|
||||||
if(sep == NULL)
|
if(sep_start == NULL)
|
||||||
return -1;
|
return 0;
|
||||||
sep = strstr(sep + 1, "-----");
|
cert_start = strstr(sep_start + 1, "-----");
|
||||||
if(sep == NULL)
|
if(cert_start == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
start = sep + 5;
|
cert_start += 5;
|
||||||
|
|
||||||
/* Find beginning of last line separator. */
|
/* Find separator after the end of the certificate. */
|
||||||
end = strstr(start, "-----");
|
cert_end = strstr(cert_start, "-----");
|
||||||
if(end == NULL)
|
if(cert_end == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
len = end - start;
|
sep_end = strstr(cert_end + 1, "-----");
|
||||||
*out = malloc(len);
|
if(sep_end == NULL)
|
||||||
if(!*out)
|
|
||||||
return -1;
|
return -1;
|
||||||
|
sep_end += 5;
|
||||||
|
|
||||||
|
len = cert_end - cert_start;
|
||||||
b64 = malloc(len + 1);
|
b64 = malloc(len + 1);
|
||||||
if(!b64) {
|
if(!b64)
|
||||||
free(*out);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
|
|
||||||
/* Create base64 string without linefeeds. */
|
/* Create base64 string without linefeeds. */
|
||||||
for(i = 0, j = 0; i < len; i++) {
|
for(i = 0, j = 0; i < len; i++) {
|
||||||
if(start[i] != '\r' && start[i] != '\n')
|
if(cert_start[i] != '\r' && cert_start[i] != '\n')
|
||||||
b64[j++] = start[i];
|
b64[j++] = cert_start[i];
|
||||||
}
|
}
|
||||||
b64[j] = '\0';
|
b64[j] = '\0';
|
||||||
|
|
||||||
@ -1574,15 +1573,14 @@ static int pem_to_der(const char *in, unsigned char **out, size_t *outlen)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return sep_end - in;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_cert(const char *file, unsigned char **out, size_t *outlen)
|
static int read_cert(const char *file, unsigned char **out, size_t *outlen)
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
ssize_t n, len = 0, cap = 512;
|
ssize_t n, len = 0, cap = 512;
|
||||||
size_t derlen;
|
unsigned char buf[cap], *data;
|
||||||
unsigned char buf[cap], *data, *der;
|
|
||||||
|
|
||||||
fd = open(file, 0);
|
fd = open(file, 0);
|
||||||
if(fd < 0)
|
if(fd < 0)
|
||||||
@ -1620,16 +1618,6 @@ static int read_cert(const char *file, unsigned char **out, size_t *outlen)
|
|||||||
}
|
}
|
||||||
data[len] = '\0';
|
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;
|
*out = data;
|
||||||
*outlen = len;
|
*outlen = len;
|
||||||
|
|
||||||
@ -1665,51 +1653,134 @@ static int sslerr_to_curlerr(struct SessionHandle *data, int err)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int verify_cert(const char *cafile, struct SessionHandle *data,
|
static int append_cert_to_array(struct SessionHandle *data,
|
||||||
SSLContextRef ctx)
|
unsigned char *buf, size_t buflen,
|
||||||
|
CFMutableArrayRef array)
|
||||||
{
|
{
|
||||||
unsigned char *certbuf;
|
CFDataRef certdata = CFDataCreate(kCFAllocatorDefault, buf, buflen);
|
||||||
size_t buflen;
|
|
||||||
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) {
|
if(!certdata) {
|
||||||
failf(data, "SSL: failed to allocate array for CA certificate");
|
failf(data, "SSL: failed to allocate array for CA certificate");
|
||||||
return CURLE_OUT_OF_MEMORY;
|
return CURLE_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
SecCertificateRef cacert = SecCertificateCreateWithData(kCFAllocatorDefault,
|
SecCertificateRef cacert =
|
||||||
certdata);
|
SecCertificateCreateWithData(kCFAllocatorDefault, certdata);
|
||||||
CFRelease(certdata);
|
CFRelease(certdata);
|
||||||
if(!cacert) {
|
if(!cacert) {
|
||||||
failf(data, "SSL: failed to create SecCertificate from CA certificate");
|
failf(data, "SSL: failed to create SecCertificate from CA certificate");
|
||||||
return CURLE_SSL_CACERT;
|
return CURLE_SSL_CACERT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check if cacert is valid. */
|
||||||
|
SecKeyRef key;
|
||||||
|
OSStatus ret = SecCertificateCopyPublicKey(cacert, &key);
|
||||||
|
if(ret != noErr) {
|
||||||
|
CFRelease(cacert);
|
||||||
|
failf(data, "SSL: invalid CA certificate");
|
||||||
|
return CURLE_SSL_CACERT;
|
||||||
|
}
|
||||||
|
CFRelease(key);
|
||||||
|
|
||||||
|
CFArrayAppendValue(array, cacert);
|
||||||
|
CFRelease(cacert);
|
||||||
|
|
||||||
|
return CURLE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int verify_cert(const char *cafile, struct SessionHandle *data,
|
||||||
|
SSLContextRef ctx)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
SecTrustRef trust;
|
||||||
OSStatus ret = SSLCopyPeerTrust(ctx, &trust);
|
OSStatus ret = SSLCopyPeerTrust(ctx, &trust);
|
||||||
if(trust == NULL) {
|
if(trust == NULL) {
|
||||||
failf(data, "SSL: error getting certificate chain");
|
failf(data, "SSL: error getting certificate chain");
|
||||||
|
CFRelease(array);
|
||||||
return CURLE_OUT_OF_MEMORY;
|
return CURLE_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
else if(ret != noErr) {
|
else if(ret != noErr) {
|
||||||
|
CFRelease(array);
|
||||||
return sslerr_to_curlerr(data, ret);
|
return sslerr_to_curlerr(data, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0,
|
|
||||||
&kCFTypeArrayCallBacks);
|
|
||||||
CFArrayAppendValue(array, cacert);
|
|
||||||
CFRelease(cacert);
|
|
||||||
|
|
||||||
ret = SecTrustSetAnchorCertificates(trust, array);
|
ret = SecTrustSetAnchorCertificates(trust, array);
|
||||||
if(ret != noErr) {
|
if(ret != noErr) {
|
||||||
CFRelease(trust);
|
CFRelease(trust);
|
||||||
return sslerr_to_curlerr(data, ret);
|
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;
|
SecTrustResultType trust_eval = 0;
|
||||||
ret = SecTrustEvaluate(trust, &trust_eval);
|
ret = SecTrustEvaluate(trust, &trust_eval);
|
||||||
@ -1722,8 +1793,6 @@ static int verify_cert(const char *cafile, struct SessionHandle *data,
|
|||||||
switch (trust_eval) {
|
switch (trust_eval) {
|
||||||
case kSecTrustResultUnspecified:
|
case kSecTrustResultUnspecified:
|
||||||
case kSecTrustResultProceed:
|
case kSecTrustResultProceed:
|
||||||
infof(data, "SSL: certificate verification succeeded (result: %d)",
|
|
||||||
trust_eval);
|
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
|
|
||||||
case kSecTrustResultRecoverableTrustFailure:
|
case kSecTrustResultRecoverableTrustFailure:
|
||||||
|
Loading…
Reference in New Issue
Block a user