From 1e1fe52923a8f582c4f50b41f0dd978d5d7c9bd3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 15 Feb 2013 12:32:19 -0800 Subject: [PATCH 1/3] imap-send: move #ifdef around Instead of adding an early return to the inside of the ssl_socket_connect() function for NO_OPENSSL compilation, split it into a separate stub function. No functional change, but the next change to extend ssl_socket_connect() will become easier to read this way. Signed-off-by: Junio C Hamano --- imap-send.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/imap-send.c b/imap-send.c index 9992233b96..94f53c2c1d 100644 --- a/imap-send.c +++ b/imap-send.c @@ -266,12 +266,17 @@ static void socket_perror(const char *func, struct imap_socket *sock, int ret) } } +#ifdef NO_OPENSSL static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify) { -#ifdef NO_OPENSSL fprintf(stderr, "SSL requested but SSL support not compiled in\n"); return -1; +} + #else + +static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify) +{ #if (OPENSSL_VERSION_NUMBER >= 0x10000000L) const SSL_METHOD *meth; #else @@ -323,8 +328,8 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve } return 0; -#endif } +#endif static int socket_read(struct imap_socket *sock, char *buf, int len) { From b62fb077d5504deadea931fd16075729f39b8f47 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Fri, 15 Feb 2013 12:50:35 -0800 Subject: [PATCH 2/3] imap-send: the subject of SSL certificate must match the host We did not check a valid certificate's subject at all, and would have happily talked with a wrong host after connecting to an incorrect address and getting a valid certificate that does not belong to the host we intended to talk to. Signed-off-by: Oswald Buddenhagen Signed-off-by: Junio C Hamano --- imap-send.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/imap-send.c b/imap-send.c index 94f53c2c1d..0b9c464ad9 100644 --- a/imap-send.c +++ b/imap-send.c @@ -275,6 +275,35 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve #else +static int host_matches(const char *host, const char *pattern) +{ + if (pattern[0] == '*' && pattern[1] == '.') { + pattern += 2; + if (!(host = strchr(host, '.'))) + return 0; + host++; + } + + return *host && *pattern && !strcasecmp(host, pattern); +} + +static int verify_hostname(X509 *cert, const char *hostname) +{ + int len; + X509_NAME *subj; + char cname[1000]; + + /* try the common name */ + if (!(subj = X509_get_subject_name(cert))) + return error("cannot get certificate subject"); + if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0) + return error("cannot get certificate common name"); + if (strlen(cname) == (size_t)len && host_matches(hostname, cname)) + return 0; + return error("certificate owner '%s' does not match hostname '%s'", + cname, hostname); +} + static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify) { #if (OPENSSL_VERSION_NUMBER >= 0x10000000L) @@ -284,6 +313,7 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve #endif SSL_CTX *ctx; int ret; + X509 *cert; SSL_library_init(); SSL_load_error_strings(); @@ -327,6 +357,15 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve return -1; } + if (verify) { + /* make sure the hostname matches that of the certificate */ + cert = SSL_get_peer_certificate(sock->ssl); + if (!cert) + return error("unable to get peer certificate."); + if (verify_hostname(cert, server.host) < 0) + return -1; + } + return 0; } #endif From e174744ad17a55d4df68cec97bfbf6b0c28e762b Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Fri, 15 Feb 2013 12:59:53 -0800 Subject: [PATCH 3/3] imap-send: support subjectAltName as well Check not only the common name of the certificate subject, but also check the subject alternative DNS names as well, when verifying that the certificate matches that of the host we are trying to talk to. Signed-off-by: Oswald Buddenhagen Signed-off-by: Junio C Hamano --- imap-send.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/imap-send.c b/imap-send.c index 0b9c464ad9..171c887076 100644 --- a/imap-send.c +++ b/imap-send.c @@ -30,6 +30,7 @@ typedef void *SSL; #else #include #include +#include #endif struct store_conf { @@ -292,6 +293,24 @@ static int verify_hostname(X509 *cert, const char *hostname) int len; X509_NAME *subj; char cname[1000]; + int i, found; + STACK_OF(GENERAL_NAME) *subj_alt_names; + + /* try the DNS subjectAltNames */ + found = 0; + if ((subj_alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL))) { + int num_subj_alt_names = sk_GENERAL_NAME_num(subj_alt_names); + for (i = 0; !found && i < num_subj_alt_names; i++) { + GENERAL_NAME *subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i); + if (subj_alt_name->type == GEN_DNS && + strlen((const char *)subj_alt_name->d.ia5->data) == (size_t)subj_alt_name->d.ia5->length && + host_matches(hostname, (const char *)(subj_alt_name->d.ia5->data))) + found = 1; + } + sk_GENERAL_NAME_pop_free(subj_alt_names, GENERAL_NAME_free); + } + if (found) + return 0; /* try the common name */ if (!(subj = X509_get_subject_name(cert)))