summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2015-03-19 10:51:48 +0100
committerCarlos Martín Nieto <cmn@dwim.me>2015-04-23 17:43:43 +0200
commitb7e1c81d1bb779bf97a209f239388d1a1cd25509 (patch)
treeb2c1531f9da5cf94364448cf82394cb79960456c
parent24e53d2fba1ea10c27c3b19f202dc92cabedf0ed (diff)
downloadlibgit2-b7e1c81d1bb779bf97a209f239388d1a1cd25509.tar.gz
SecureTransport: allow overriding a bad certificate
Do not automatically fail on a bad certificate, but let the caller decide. This means we don't need our switch on errors anymore but can return a string representation from the security framework.
-rw-r--r--src/stransport_stream.c55
1 files changed, 41 insertions, 14 deletions
diff --git a/src/stransport_stream.c b/src/stransport_stream.c
index 09cc7cb00..644a5a7c2 100644
--- a/src/stransport_stream.c
+++ b/src/stransport_stream.c
@@ -17,17 +17,19 @@
int stransport_error(OSStatus ret)
{
- switch (ret) {
- case noErr:
+ CFStringRef message;
+
+ if (ret == noErr) {
giterr_clear();
return 0;
- case errSSLXCertChainInvalid:
- case errSSLBadCert:
- return GIT_ECERTIFICATE;
- default:
- giterr_set(GITERR_NET, "SecureTransport error %d", ret);
- return -1;
}
+
+ message = SecCopyErrorMessageString(ret, NULL);
+ GITERR_CHECK_ALLOC(message);
+
+ giterr_set(GITERR_NET, "SecureTransport error: %s", CFStringGetCStringPtr(message, kCFStringEncodingUTF8));
+ CFRelease(message);
+ return -1;
}
typedef struct {
@@ -42,15 +44,43 @@ int stransport_connect(git_stream *stream)
{
stransport_stream *st = (stransport_stream *) stream;
int error;
+ SecTrustRef trust = NULL;
+ SecTrustResultType sec_res;
OSStatus ret;
if ((error = git_stream_connect(st->io)) < 0)
return error;
- if ((ret = SSLHandshake(st->ctx)) != noErr)
- return stransport_error(ret);
+ ret = SSLHandshake(st->ctx);
+ if (ret != errSSLServerAuthCompleted) {
+ giterr_set(GITERR_SSL, "unexpected return value from ssl handshake %d", ret);
+ return -1;
+ }
+
+ if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr)
+ goto on_error;
+
+ if ((ret = SecTrustEvaluate(trust, &sec_res)) != noErr)
+ goto on_error;
+
+ CFRelease(trust);
+
+ if (sec_res == kSecTrustResultInvalid || sec_res == kSecTrustResultOtherError) {
+ giterr_set(GITERR_SSL, "internal security trust error");
+ return -1;
+ }
+
+ if (sec_res == kSecTrustResultDeny || sec_res == kSecTrustResultRecoverableTrustFailure ||
+ sec_res == kSecTrustResultFatalTrustFailure)
+ return GIT_ECERTIFICATE;
return 0;
+
+on_error:
+ if (trust)
+ CFRelease(trust);
+
+ return stransport_error(ret);
}
int stransport_certificate(git_cert **out, git_stream *stream)
@@ -58,15 +88,11 @@ int stransport_certificate(git_cert **out, git_stream *stream)
stransport_stream *st = (stransport_stream *) stream;
SecTrustRef trust = NULL;
SecCertificateRef sec_cert;
- SecTrustResultType sec_res;
OSStatus ret;
if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr)
return stransport_error(ret);
- if ((ret = SecTrustEvaluate(trust, &sec_res)) != noErr)
- return stransport_error(ret);
-
sec_cert = SecTrustGetCertificateAtIndex(trust, 0);
st->der_data = SecCertificateCopyData(sec_cert);
CFRelease(trust);
@@ -198,6 +224,7 @@ int git_stransport_stream_new(git_stream **out, const char *host, const char *po
if ((ret = SSLSetIOFuncs(st->ctx, read_cb, write_cb)) != noErr ||
(ret = SSLSetConnection(st->ctx, st->io)) != noErr ||
+ (ret = SSLSetSessionOption(st->ctx, kSSLSessionOptionBreakOnServerAuth, true)) != noErr ||
(ret = SSLSetPeerDomainName(st->ctx, host, strlen(host))) != noErr) {
git_stream_free((git_stream *)st);
return stransport_error(ret);