From ec1ce4584a6a8ec2b5b227301a918548907a2b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 10 Aug 2014 17:06:53 +0200 Subject: [PATCH] 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. --- include/git2/transport.h | 5 ++--- include/git2/types.h | 3 ++- src/transports/http.c | 27 ++++++++++++++++++++++++-- src/transports/ssh.c | 41 +++++++++++++++++++++------------------- 4 files changed, 51 insertions(+), 25 deletions(-) diff --git a/include/git2/transport.h b/include/git2/transport.h index cd4429fee..7365cffdf 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 0009a8aa5..b574d2945 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 d37059bae..ab477547a 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 fa7dca7c3..ba549ff3b 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; + 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) { - 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); - } + 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;