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 <ossi@kde.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Oswald Buddenhagen committed Feb 15, 2013 at 12:50 UTC b62fb077d5504deadea931fd16075729f39b8f47
1 file changed +39
imap-send.c
+39
@@ -275,6 +275,35 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
275
276 #else
277
278 +static int host_matches(const char *host, const char *pattern)
279 +{
280 + if (pattern[0] == '*' && pattern[1] == '.') {
281 + pattern += 2;
282 + if (!(host = strchr(host, '.')))
283 + return 0;
284 + host++;
285 + }
286 +
287 + return *host && *pattern && !strcasecmp(host, pattern);
288 +}
289 +
290 +static int verify_hostname(X509 *cert, const char *hostname)
291 +{
292 + int len;
293 + X509_NAME *subj;
294 + char cname[1000];
295 +
296 + /* try the common name */
297 + if (!(subj = X509_get_subject_name(cert)))
298 + return error("cannot get certificate subject");
299 + if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)
300 + return error("cannot get certificate common name");
301 + if (strlen(cname) == (size_t)len && host_matches(hostname, cname))
302 + return 0;
303 + return error("certificate owner '%s' does not match hostname '%s'",
304 + cname, hostname);
305 +}
306 +
307 static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
308 {
309 #if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
@@ -284,6 +313,7 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
313 #endif
314 SSL_CTX *ctx;
315 int ret;
316 + X509 *cert;
317
318 SSL_library_init();
319 SSL_load_error_strings();
@@ -327,6 +357,15 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
357 return -1;
358 }
359
360 + if (verify) {
361 + /* make sure the hostname matches that of the certificate */
362 + cert = SSL_get_peer_certificate(sock->ssl);
363 + if (!cert)
364 + return error("unable to get peer certificate.");
365 + if (verify_hostname(cert, server.host) < 0)
366 + return -1;
367 + }
368 +
369 return 0;
370 }
371 #endif