]> git.proxmox.com Git - libgit2.git/blame - src/openssl_stream.c
proxy: don't require the trailing slash on WinHTTP
[libgit2.git] / src / openssl_stream.c
CommitLineData
468d7b11
CMN
1/*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7
24e53d2f 8#ifdef GIT_OPENSSL
468d7b11 9
468d7b11
CMN
10#include <ctype.h>
11
12#include "global.h"
13#include "posix.h"
14#include "stream.h"
15#include "socket_stream.h"
1b75c29e 16#include "netops.h"
468d7b11 17#include "git2/transport.h"
c8fe6c09 18#include "git2/sys/openssl.h"
468d7b11 19
e247649d
CMN
20#ifdef GIT_CURL
21# include "curl_stream.h"
22#endif
23
a944c6cc
AK
24#ifndef GIT_WIN32
25# include <sys/types.h>
26# include <sys/socket.h>
27# include <netinet/in.h>
28#endif
29
ec032442
AK
30#include <openssl/ssl.h>
31#include <openssl/err.h>
32#include <openssl/x509v3.h>
e247649d
CMN
33#include <openssl/bio.h>
34
8a6d6677
ET
35SSL_CTX *git__ssl_ctx;
36
fa72d6da
DB
37#define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA"
38
8a6d6677
ET
39#ifdef GIT_THREADS
40
41static git_mutex *openssl_locks;
42
43static void openssl_locking_function(
44 int mode, int n, const char *file, int line)
45{
46 int lock;
47
48 GIT_UNUSED(file);
49 GIT_UNUSED(line);
50
51 lock = mode & CRYPTO_LOCK;
52
53 if (lock) {
54 git_mutex_lock(&openssl_locks[n]);
55 } else {
56 git_mutex_unlock(&openssl_locks[n]);
57 }
58}
59
60static void shutdown_ssl_locking(void)
61{
62 int num_locks, i;
63
64 num_locks = CRYPTO_num_locks();
65 CRYPTO_set_locking_callback(NULL);
66
67 for (i = 0; i < num_locks; ++i)
68 git_mutex_free(openssl_locks);
69 git__free(openssl_locks);
70}
71
72#endif /* GIT_THREADS */
73
74/**
75 * This function aims to clean-up the SSL context which
76 * we allocated.
77 */
78static void shutdown_ssl(void)
79{
80 if (git__ssl_ctx) {
81 SSL_CTX_free(git__ssl_ctx);
82 git__ssl_ctx = NULL;
83 }
84}
85
86int git_openssl_stream_global_init(void)
87{
88#ifdef GIT_OPENSSL
89 long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
fa72d6da 90 const char *ciphers = git_libgit2__ssl_ciphers();
8a6d6677
ET
91
92 /* Older OpenSSL and MacOS OpenSSL doesn't have this */
93#ifdef SSL_OP_NO_COMPRESSION
94 ssl_opts |= SSL_OP_NO_COMPRESSION;
95#endif
96
97 SSL_load_error_strings();
98 OpenSSL_add_ssl_algorithms();
99 /*
100 * Load SSLv{2,3} and TLSv1 so that we can talk with servers
101 * which use the SSL hellos, which are often used for
102 * compatibility. We then disable SSL so we only allow OpenSSL
103 * to speak TLSv1 to perform the encryption itself.
104 */
105 git__ssl_ctx = SSL_CTX_new(SSLv23_method());
106 SSL_CTX_set_options(git__ssl_ctx, ssl_opts);
107 SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY);
108 SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL);
109 if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) {
110 SSL_CTX_free(git__ssl_ctx);
111 git__ssl_ctx = NULL;
112 return -1;
113 }
fa72d6da
DB
114
115 if (!ciphers) {
116 ciphers = GIT_SSL_DEFAULT_CIPHERS;
117 }
118
119 if(!SSL_CTX_set_cipher_list(git__ssl_ctx, ciphers)) {
120 SSL_CTX_free(git__ssl_ctx);
121 git__ssl_ctx = NULL;
122 return -1;
123 }
8a6d6677
ET
124#endif
125
126 git__on_shutdown(shutdown_ssl);
127
128 return 0;
129}
130
131int git_openssl_set_locking(void)
132{
133#ifdef GIT_THREADS
134 int num_locks, i;
135
136 num_locks = CRYPTO_num_locks();
137 openssl_locks = git__calloc(num_locks, sizeof(git_mutex));
138 GITERR_CHECK_ALLOC(openssl_locks);
139
140 for (i = 0; i < num_locks; i++) {
141 if (git_mutex_init(&openssl_locks[i]) != 0) {
142 giterr_set(GITERR_SSL, "failed to initialize openssl locks");
143 return -1;
144 }
145 }
146
147 CRYPTO_set_locking_callback(openssl_locking_function);
148 git__on_shutdown(shutdown_ssl_locking);
149 return 0;
150#else
151 giterr_set(GITERR_THREAD, "libgit2 as not built with threads");
152 return -1;
153#endif
154}
155
156
e247649d
CMN
157static int bio_create(BIO *b)
158{
159 b->init = 1;
160 b->num = 0;
161 b->ptr = NULL;
162 b->flags = 0;
163
164 return 1;
165}
166
167static int bio_destroy(BIO *b)
168{
169 if (!b)
170 return 0;
171
172 b->init = 0;
173 b->num = 0;
174 b->ptr = NULL;
175 b->flags = 0;
176
177 return 1;
178}
179
180static int bio_read(BIO *b, char *buf, int len)
181{
182 git_stream *io = (git_stream *) b->ptr;
183 return (int) git_stream_read(io, buf, len);
184}
185
186static int bio_write(BIO *b, const char *buf, int len)
187{
188 git_stream *io = (git_stream *) b->ptr;
189 return (int) git_stream_write(io, buf, len, 0);
190}
191
192static long bio_ctrl(BIO *b, int cmd, long num, void *ptr)
193{
194 GIT_UNUSED(b);
195 GIT_UNUSED(num);
196 GIT_UNUSED(ptr);
197
198 if (cmd == BIO_CTRL_FLUSH)
199 return 1;
200
201 return 0;
202}
203
204static int bio_gets(BIO *b, char *buf, int len)
205{
206 GIT_UNUSED(b);
207 GIT_UNUSED(buf);
208 GIT_UNUSED(len);
209 return -1;
210}
211
212static int bio_puts(BIO *b, const char *str)
213{
214 return bio_write(b, str, strlen(str));
215}
216
217static BIO_METHOD git_stream_bio_method = {
218 BIO_TYPE_SOURCE_SINK,
219 "git_stream",
220 bio_write,
221 bio_read,
222 bio_puts,
223 bio_gets,
224 bio_ctrl,
225 bio_create,
226 bio_destroy
227};
ec032442 228
468d7b11
CMN
229static int ssl_set_error(SSL *ssl, int error)
230{
231 int err;
232 unsigned long e;
233
234 err = SSL_get_error(ssl, error);
235
236 assert(err != SSL_ERROR_WANT_READ);
237 assert(err != SSL_ERROR_WANT_WRITE);
238
239 switch (err) {
240 case SSL_ERROR_WANT_CONNECT:
241 case SSL_ERROR_WANT_ACCEPT:
242 giterr_set(GITERR_NET, "SSL error: connection failure\n");
243 break;
244 case SSL_ERROR_WANT_X509_LOOKUP:
245 giterr_set(GITERR_NET, "SSL error: x509 error\n");
246 break;
247 case SSL_ERROR_SYSCALL:
248 e = ERR_get_error();
249 if (e > 0) {
250 giterr_set(GITERR_NET, "SSL error: %s",
251 ERR_error_string(e, NULL));
252 break;
253 } else if (error < 0) {
254 giterr_set(GITERR_OS, "SSL error: syscall failure");
255 break;
256 }
257 giterr_set(GITERR_NET, "SSL error: received early EOF");
1396c381 258 return GIT_EEOF;
468d7b11
CMN
259 break;
260 case SSL_ERROR_SSL:
261 e = ERR_get_error();
262 giterr_set(GITERR_NET, "SSL error: %s",
263 ERR_error_string(e, NULL));
264 break;
265 case SSL_ERROR_NONE:
266 case SSL_ERROR_ZERO_RETURN:
267 default:
268 giterr_set(GITERR_NET, "SSL error: unknown error");
269 break;
270 }
271 return -1;
272}
273
274static int ssl_teardown(SSL *ssl)
275{
276 int ret;
277
278 ret = SSL_shutdown(ssl);
279 if (ret < 0)
280 ret = ssl_set_error(ssl, ret);
281 else
282 ret = 0;
283
468d7b11
CMN
284 return ret;
285}
286
287static int check_host_name(const char *name, const char *host)
288{
289 if (!strcasecmp(name, host))
290 return 0;
291
292 if (gitno__match_host(name, host) < 0)
293 return -1;
294
295 return 0;
296}
297
298static int verify_server_cert(SSL *ssl, const char *host)
299{
300 X509 *cert;
301 X509_NAME *peer_name;
302 ASN1_STRING *str;
303 unsigned char *peer_cn = NULL;
304 int matched = -1, type = GEN_DNS;
305 GENERAL_NAMES *alts;
306 struct in6_addr addr6;
307 struct in_addr addr4;
308 void *addr;
309 int i = -1,j;
310
311 if (SSL_get_verify_result(ssl) != X509_V_OK) {
312 giterr_set(GITERR_SSL, "The SSL certificate is invalid");
1b75c29e 313 return GIT_ECERTIFICATE;
468d7b11
CMN
314 }
315
316 /* Try to parse the host as an IP address to see if it is */
317 if (p_inet_pton(AF_INET, host, &addr4)) {
318 type = GEN_IPADD;
319 addr = &addr4;
320 } else {
321 if(p_inet_pton(AF_INET6, host, &addr6)) {
322 type = GEN_IPADD;
323 addr = &addr6;
324 }
325 }
326
327
328 cert = SSL_get_peer_certificate(ssl);
329 if (!cert) {
330 giterr_set(GITERR_SSL, "the server did not provide a certificate");
331 return -1;
332 }
333
334 /* Check the alternative names */
335 alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
336 if (alts) {
337 int num;
338
339 num = sk_GENERAL_NAME_num(alts);
340 for (i = 0; i < num && matched != 1; i++) {
341 const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i);
342 const char *name = (char *) ASN1_STRING_data(gn->d.ia5);
343 size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5);
344
345 /* Skip any names of a type we're not looking for */
346 if (gn->type != type)
347 continue;
348
349 if (type == GEN_DNS) {
350 /* If it contains embedded NULs, don't even try */
351 if (memchr(name, '\0', namelen))
352 continue;
353
354 if (check_host_name(name, host) < 0)
355 matched = 0;
356 else
357 matched = 1;
358 } else if (type == GEN_IPADD) {
359 /* Here name isn't so much a name but a binary representation of the IP */
360 matched = !!memcmp(name, addr, namelen);
361 }
362 }
363 }
364 GENERAL_NAMES_free(alts);
365
366 if (matched == 0)
367 goto cert_fail_name;
368
369 if (matched == 1)
370 return 0;
371
372 /* If no alternative names are available, check the common name */
373 peer_name = X509_get_subject_name(cert);
374 if (peer_name == NULL)
375 goto on_error;
376
377 if (peer_name) {
378 /* Get the index of the last CN entry */
379 while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0)
380 i = j;
381 }
382
383 if (i < 0)
384 goto on_error;
385
386 str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i));
387 if (str == NULL)
388 goto on_error;
389
390 /* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */
391 if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) {
392 int size = ASN1_STRING_length(str);
393
394 if (size > 0) {
395 peer_cn = OPENSSL_malloc(size + 1);
396 GITERR_CHECK_ALLOC(peer_cn);
397 memcpy(peer_cn, ASN1_STRING_data(str), size);
398 peer_cn[size] = '\0';
05bf67b9
PS
399 } else {
400 goto cert_fail_name;
468d7b11
CMN
401 }
402 } else {
403 int size = ASN1_STRING_to_UTF8(&peer_cn, str);
404 GITERR_CHECK_ALLOC(peer_cn);
405 if (memchr(peer_cn, '\0', size))
406 goto cert_fail_name;
407 }
408
409 if (check_host_name((char *)peer_cn, host) < 0)
410 goto cert_fail_name;
411
412 OPENSSL_free(peer_cn);
413
414 return 0;
415
416on_error:
417 OPENSSL_free(peer_cn);
418 return ssl_set_error(ssl, 0);
419
420cert_fail_name:
421 OPENSSL_free(peer_cn);
422 giterr_set(GITERR_SSL, "hostname does not match certificate");
423 return GIT_ECERTIFICATE;
424}
425
426typedef struct {
427 git_stream parent;
e247649d 428 git_stream *io;
146a96de 429 bool connected;
e247649d 430 char *host;
468d7b11
CMN
431 SSL *ssl;
432 git_cert_x509 cert_info;
433} openssl_stream;
434
435int openssl_close(git_stream *stream);
436
437int openssl_connect(git_stream *stream)
438{
439 int ret;
e247649d 440 BIO *bio;
468d7b11
CMN
441 openssl_stream *st = (openssl_stream *) stream;
442
e247649d 443 if ((ret = git_stream_connect(st->io)) < 0)
468d7b11
CMN
444 return ret;
445
146a96de
CMN
446 st->connected = true;
447
e247649d
CMN
448 bio = BIO_new(&git_stream_bio_method);
449 GITERR_CHECK_ALLOC(bio);
450 bio->ptr = st->io;
468d7b11 451
e247649d 452 SSL_set_bio(st->ssl, bio, bio);
987045c7 453 /* specify the host in case SNI is needed */
febc8c46 454#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
e247649d 455 SSL_set_tlsext_host_name(st->ssl, st->host);
febc8c46 456#endif
987045c7 457
468d7b11
CMN
458 if ((ret = SSL_connect(st->ssl)) <= 0)
459 return ssl_set_error(st->ssl, ret);
460
e247649d 461 return verify_server_cert(st->ssl, st->host);
468d7b11
CMN
462}
463
464int openssl_certificate(git_cert **out, git_stream *stream)
465{
466 openssl_stream *st = (openssl_stream *) stream;
467 int len;
468 X509 *cert = SSL_get_peer_certificate(st->ssl);
469 unsigned char *guard, *encoded_cert;
470
471 /* Retrieve the length of the certificate first */
472 len = i2d_X509(cert, NULL);
473 if (len < 0) {
474 giterr_set(GITERR_NET, "failed to retrieve certificate information");
475 return -1;
476 }
477
478 encoded_cert = git__malloc(len);
479 GITERR_CHECK_ALLOC(encoded_cert);
480 /* i2d_X509 makes 'guard' point to just after the data */
481 guard = encoded_cert;
482
483 len = i2d_X509(cert, &guard);
484 if (len < 0) {
485 git__free(encoded_cert);
486 giterr_set(GITERR_NET, "failed to retrieve certificate information");
487 return -1;
488 }
489
79698030 490 st->cert_info.parent.cert_type = GIT_CERT_X509;
468d7b11
CMN
491 st->cert_info.data = encoded_cert;
492 st->cert_info.len = len;
493
79698030
ET
494 *out = &st->cert_info.parent;
495
468d7b11
CMN
496 return 0;
497}
498
e247649d
CMN
499static int openssl_set_proxy(git_stream *stream, const char *proxy_url)
500{
501 openssl_stream *st = (openssl_stream *) stream;
502
503 return git_stream_set_proxy(st->io, proxy_url);
504}
505
49ae22ba 506ssize_t openssl_write(git_stream *stream, const char *data, size_t len, int flags)
468d7b11
CMN
507{
508 openssl_stream *st = (openssl_stream *) stream;
509 int ret;
468d7b11
CMN
510
511 GIT_UNUSED(flags);
512
77bffc2c
CMN
513 if ((ret = SSL_write(st->ssl, data, len)) <= 0) {
514 return ssl_set_error(st->ssl, ret);
515 }
468d7b11 516
77bffc2c 517 return ret;
468d7b11
CMN
518}
519
520ssize_t openssl_read(git_stream *stream, void *data, size_t len)
521{
522 openssl_stream *st = (openssl_stream *) stream;
523 int ret;
524
77bffc2c 525 if ((ret = SSL_read(st->ssl, data, len)) <= 0)
468d7b11 526 ssl_set_error(st->ssl, ret);
468d7b11
CMN
527
528 return ret;
529}
530
531int openssl_close(git_stream *stream)
532{
533 openssl_stream *st = (openssl_stream *) stream;
534 int ret;
535
146a96de 536 if (st->connected && (ret = ssl_teardown(st->ssl)) < 0)
468d7b11
CMN
537 return -1;
538
146a96de
CMN
539 st->connected = false;
540
e247649d 541 return git_stream_close(st->io);
468d7b11
CMN
542}
543
544void openssl_free(git_stream *stream)
545{
546 openssl_stream *st = (openssl_stream *) stream;
547
deecaa2e 548 SSL_free(st->ssl);
3ca84ac0 549 git__free(st->host);
468d7b11 550 git__free(st->cert_info.data);
e247649d 551 git_stream_free(st->io);
468d7b11
CMN
552 git__free(st);
553}
554
555int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
556{
e247649d 557 int error;
468d7b11
CMN
558 openssl_stream *st;
559
560 st = git__calloc(1, sizeof(openssl_stream));
561 GITERR_CHECK_ALLOC(st);
562
2baf854e 563 st->io = NULL;
e247649d 564#ifdef GIT_CURL
8443f492 565 error = git_curl_stream_new(&st->io, host, port);
e247649d 566#else
cae2a555 567 error = git_socket_stream_new(&st->io, host, port);
e247649d
CMN
568#endif
569
570 if (error < 0)
2baf854e 571 goto out_err;
468d7b11
CMN
572
573 st->ssl = SSL_new(git__ssl_ctx);
574 if (st->ssl == NULL) {
575 giterr_set(GITERR_SSL, "failed to create ssl object");
2baf854e
PS
576 error = -1;
577 goto out_err;
468d7b11
CMN
578 }
579
e247649d
CMN
580 st->host = git__strdup(host);
581 GITERR_CHECK_ALLOC(st->host);
582
468d7b11
CMN
583 st->parent.version = GIT_STREAM_VERSION;
584 st->parent.encrypted = 1;
e247649d 585 st->parent.proxy_support = git_stream_supports_proxy(st->io);
468d7b11
CMN
586 st->parent.connect = openssl_connect;
587 st->parent.certificate = openssl_certificate;
e247649d 588 st->parent.set_proxy = openssl_set_proxy;
468d7b11
CMN
589 st->parent.read = openssl_read;
590 st->parent.write = openssl_write;
591 st->parent.close = openssl_close;
592 st->parent.free = openssl_free;
593
594 *out = (git_stream *) st;
595 return 0;
2baf854e
PS
596
597out_err:
598 git_stream_free(st->io);
599 git__free(st);
600
601 return error;
468d7b11
CMN
602}
603
604#else
605
606#include "stream.h"
68ad3156 607#include "git2/sys/openssl.h"
468d7b11 608
8a6d6677
ET
609int git_openssl_stream_global_init(void)
610{
611 return 0;
612}
613
614int git_openssl_set_locking(void)
615{
616 giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support");
617 return -1;
618}
619
468d7b11
CMN
620int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
621{
70b852ce
CMN
622 GIT_UNUSED(out);
623 GIT_UNUSED(host);
624 GIT_UNUSED(port);
625
468d7b11
CMN
626 giterr_set(GITERR_SSL, "openssl is not supported in this version");
627 return -1;
628}
629
630#endif