Commit ec1ce4584a6a8ec2b5b227301a918548907a2b02

Carlos Martín Nieto 2014-08-10T17:06:53

http: send the DER-encoded cert to the callback Instead of the parsed data, we can ask OpenSSL to give us the DER-encoded version of the certificate, which the user can then parse and validate.

diff --git a/include/git2/transport.h b/include/git2/transport.h
index cd4429f..7365cff 100644
--- a/include/git2/transport.h
+++ b/include/git2/transport.h
@@ -26,10 +26,9 @@ GIT_BEGIN_DECL
 typedef enum git_cert_t {
         /**
          * The `data` argument to the callback will be a pointer to
-         * OpenSSL's `X509` structure.
+         * the DER-encoded data.
          */
-	GIT_CERT_X509_OPENSSL,
-	GIT_CERT_X509_WINHTTP,
+	GIT_CERT_X509,
         /**
          * The `data` argument to the callback will be a pointer to a
          * `git_cert_hostkey` structure.
diff --git a/include/git2/types.h b/include/git2/types.h
index 0009a8a..b574d29 100644
--- a/include/git2/types.h
+++ b/include/git2/types.h
@@ -261,9 +261,10 @@ typedef enum git_cert_t git_cert_t;
  *
  * @param type The type of certificate or host info, SSH or X.509
  * @param data The data for the certificate or host info
+ * @param len The size of the certificate or host info
  * @param payload Payload provided by the caller
  */
-typedef int (*git_transport_certificate_check_cb)(git_cert_t type, void *data, void *payload);
+typedef int (*git_transport_certificate_check_cb)(git_cert_t type, void *data, size_t len, void *payload);
 
 /**
  * Opaque structure representing a submodule.
diff --git a/src/transports/http.c b/src/transports/http.c
index d37059b..ab47754 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -555,9 +555,32 @@ static int http_connect(http_subtransport *t)
 #ifdef GIT_SSL
 	if (error == GIT_ECERTIFICATE && t->owner->certificate_check_cb != NULL) {
                 X509 *cert = SSL_get_peer_certificate(t->socket.ssl.ssl);
-                int allow;
+                int allow, len;
+		unsigned char *guard, *encoded_cert;
+
+		/* Retrieve the length of the certificate first */
+		len = i2d_X509(cert, NULL);
+		if (len < 0) {
+			giterr_set(GITERR_NET, "failed to retrieve certificate information");
+			return -1;
+		}
+
+
+		encoded_cert = git__malloc(len);
+		GITERR_CHECK_ALLOC(encoded_cert);
+		/* i2d_X509 makes 'copy' point to just after the data */
+		guard = encoded_cert;
+
+		len = i2d_X509(cert, &guard);
+		if (len < 0) {
+			git__free(encoded_cert);
+			giterr_set(GITERR_NET, "failed to retrieve certificate information");
+			return -1;
+		}
+
+                allow = t->owner->certificate_check_cb(GIT_CERT_X509, encoded_cert, len, t->owner->message_cb_payload);
+		git__free(encoded_cert);
 
-                allow = t->owner->certificate_check_cb(GIT_CERT_X509_OPENSSL, cert, t->owner->message_cb_payload);
                 if (allow < 0) {
                         error = allow;
                 } else if (!allow) {
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index fa7dca7..ba549ff 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -517,28 +517,31 @@ static int _git_ssh_setup_conn(
 	if (error < 0)
 		goto on_error;
 
-        if (t->owner->certificate_check_cb != NULL) {
-                git_cert_hostkey cert;
-                const char *key;
-                int allow;
-
-                cert.type = LIBSSH2_HOSTKEY_HASH_SHA1;
-                key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
-                if (key != NULL) {
-                        memcpy(&cert.hash, key, 20);
-                } else {
-                        cert.type = LIBSSH2_HOSTKEY_HASH_MD5;
-                        key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
-                        if (key != NULL)
-                                memcpy(&cert.hash, key, 16);
-                }
+	if (t->owner->certificate_check_cb != NULL) {
+		git_cert_hostkey cert;
+		const char *key;
+		int allow;
+		size_t certlen;
+
+		cert.type = LIBSSH2_HOSTKEY_HASH_SHA1;
+		key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
+		if (key != NULL) {
+			certlen = 20;
+			memcpy(&cert.hash, key, certlen);
+		} else {
+			cert.type = LIBSSH2_HOSTKEY_HASH_MD5;
+			key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
+			certlen = 16;
+			if (key != NULL)
+				memcpy(&cert.hash, key, certlen);
+		}
 
-                if (key == NULL) {
-                        giterr_set(GITERR_SSH, "unable to get the host key");
-                        return -1;
-                }
+		if (key == NULL) {
+			giterr_set(GITERR_SSH, "unable to get the host key");
+			return -1;
+		}
 
-                allow = t->owner->certificate_check_cb(GIT_CERT_HOSTKEY_LIBSSH2, &cert, t->owner->message_cb_payload);
+                allow = t->owner->certificate_check_cb(GIT_CERT_HOSTKEY_LIBSSH2, &cert, certlen, t->owner->message_cb_payload);
                 if (allow < 0) {
                         error = allow;
                         goto on_error;