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