]> git.proxmox.com Git - libgit2.git/blob - src/netops.c
Merge pull request #2582 from swansontec/master
[libgit2.git] / src / netops.c
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 #ifndef _WIN32
8 # include <sys/types.h>
9 # include <sys/socket.h>
10 # include <sys/select.h>
11 # include <sys/time.h>
12 # include <netdb.h>
13 # include <netinet/in.h>
14 # include <arpa/inet.h>
15 #else
16 # include <winsock2.h>
17 # include <ws2tcpip.h>
18 # ifdef _MSC_VER
19 # pragma comment(lib, "ws2_32")
20 # endif
21 #endif
22
23 #ifdef GIT_SSL
24 # include <openssl/ssl.h>
25 # include <openssl/err.h>
26 # include <openssl/x509v3.h>
27 #endif
28
29 #include <ctype.h>
30 #include "git2/errors.h"
31
32 #include "common.h"
33 #include "netops.h"
34 #include "posix.h"
35 #include "buffer.h"
36 #include "http_parser.h"
37 #include "global.h"
38
39 #ifdef GIT_WIN32
40 static void net_set_error(const char *str)
41 {
42 int error = WSAGetLastError();
43 char * win32_error = git_win32_get_error_message(error);
44
45 if (win32_error) {
46 giterr_set(GITERR_NET, "%s: %s", str, win32_error);
47 git__free(win32_error);
48 } else {
49 giterr_set(GITERR_NET, str);
50 }
51 }
52 #else
53 static void net_set_error(const char *str)
54 {
55 giterr_set(GITERR_NET, "%s: %s", str, strerror(errno));
56 }
57 #endif
58
59 #ifdef GIT_SSL
60 static int ssl_set_error(gitno_ssl *ssl, int error)
61 {
62 int err;
63 unsigned long e;
64
65 err = SSL_get_error(ssl->ssl, error);
66
67 assert(err != SSL_ERROR_WANT_READ);
68 assert(err != SSL_ERROR_WANT_WRITE);
69
70 switch (err) {
71 case SSL_ERROR_WANT_CONNECT:
72 case SSL_ERROR_WANT_ACCEPT:
73 giterr_set(GITERR_NET, "SSL error: connection failure\n");
74 break;
75 case SSL_ERROR_WANT_X509_LOOKUP:
76 giterr_set(GITERR_NET, "SSL error: x509 error\n");
77 break;
78 case SSL_ERROR_SYSCALL:
79 e = ERR_get_error();
80 if (e > 0) {
81 giterr_set(GITERR_NET, "SSL error: %s",
82 ERR_error_string(e, NULL));
83 break;
84 } else if (error < 0) {
85 giterr_set(GITERR_OS, "SSL error: syscall failure");
86 break;
87 }
88 giterr_set(GITERR_NET, "SSL error: received early EOF");
89 break;
90 case SSL_ERROR_SSL:
91 e = ERR_get_error();
92 giterr_set(GITERR_NET, "SSL error: %s",
93 ERR_error_string(e, NULL));
94 break;
95 case SSL_ERROR_NONE:
96 case SSL_ERROR_ZERO_RETURN:
97 default:
98 giterr_set(GITERR_NET, "SSL error: unknown error");
99 break;
100 }
101 return -1;
102 }
103 #endif
104
105 int gitno_recv(gitno_buffer *buf)
106 {
107 return buf->recv(buf);
108 }
109
110 #ifdef GIT_SSL
111 static int gitno__recv_ssl(gitno_buffer *buf)
112 {
113 int ret;
114
115 do {
116 ret = SSL_read(buf->socket->ssl.ssl, buf->data + buf->offset, buf->len - buf->offset);
117 } while (SSL_get_error(buf->socket->ssl.ssl, ret) == SSL_ERROR_WANT_READ);
118
119 if (ret < 0) {
120 net_set_error("Error receiving socket data");
121 return -1;
122 }
123
124 buf->offset += ret;
125 return ret;
126 }
127 #endif
128
129 static int gitno__recv(gitno_buffer *buf)
130 {
131 int ret;
132
133 ret = p_recv(buf->socket->socket, buf->data + buf->offset, buf->len - buf->offset, 0);
134 if (ret < 0) {
135 net_set_error("Error receiving socket data");
136 return -1;
137 }
138
139 buf->offset += ret;
140 return ret;
141 }
142
143 void gitno_buffer_setup_callback(
144 gitno_socket *socket,
145 gitno_buffer *buf,
146 char *data,
147 size_t len,
148 int (*recv)(gitno_buffer *buf), void *cb_data)
149 {
150 memset(data, 0x0, len);
151 buf->data = data;
152 buf->len = len;
153 buf->offset = 0;
154 buf->socket = socket;
155 buf->recv = recv;
156 buf->cb_data = cb_data;
157 }
158
159 void gitno_buffer_setup(gitno_socket *socket, gitno_buffer *buf, char *data, size_t len)
160 {
161 #ifdef GIT_SSL
162 if (socket->ssl.ssl) {
163 gitno_buffer_setup_callback(socket, buf, data, len, gitno__recv_ssl, NULL);
164 return;
165 }
166 #endif
167
168 gitno_buffer_setup_callback(socket, buf, data, len, gitno__recv, NULL);
169 }
170
171 /* Consume up to ptr and move the rest of the buffer to the beginning */
172 void gitno_consume(gitno_buffer *buf, const char *ptr)
173 {
174 size_t consumed;
175
176 assert(ptr - buf->data >= 0);
177 assert(ptr - buf->data <= (int) buf->len);
178
179 consumed = ptr - buf->data;
180
181 memmove(buf->data, ptr, buf->offset - consumed);
182 memset(buf->data + buf->offset, 0x0, buf->len - buf->offset);
183 buf->offset -= consumed;
184 }
185
186 /* Consume const bytes and move the rest of the buffer to the beginning */
187 void gitno_consume_n(gitno_buffer *buf, size_t cons)
188 {
189 memmove(buf->data, buf->data + cons, buf->len - buf->offset);
190 memset(buf->data + cons, 0x0, buf->len - buf->offset);
191 buf->offset -= cons;
192 }
193
194 #ifdef GIT_SSL
195
196 static int gitno_ssl_teardown(gitno_ssl *ssl)
197 {
198 int ret;
199
200 ret = SSL_shutdown(ssl->ssl);
201 if (ret < 0)
202 ret = ssl_set_error(ssl, ret);
203 else
204 ret = 0;
205
206 SSL_free(ssl->ssl);
207 return ret;
208 }
209
210 #endif
211
212 /* Match host names according to RFC 2818 rules */
213 int gitno__match_host(const char *pattern, const char *host)
214 {
215 for (;;) {
216 char c = tolower(*pattern++);
217
218 if (c == '\0')
219 return *host ? -1 : 0;
220
221 if (c == '*') {
222 c = *pattern;
223 /* '*' at the end matches everything left */
224 if (c == '\0')
225 return 0;
226
227 /*
228 * We've found a pattern, so move towards the next matching
229 * char. The '.' is handled specially because wildcards aren't
230 * allowed to cross subdomains.
231 */
232
233 while(*host) {
234 char h = tolower(*host);
235 if (c == h)
236 return gitno__match_host(pattern, host++);
237 if (h == '.')
238 return gitno__match_host(pattern, host);
239 host++;
240 }
241 return -1;
242 }
243
244 if (c != tolower(*host++))
245 return -1;
246 }
247
248 return -1;
249 }
250
251 static int check_host_name(const char *name, const char *host)
252 {
253 if (!strcasecmp(name, host))
254 return 0;
255
256 if (gitno__match_host(name, host) < 0)
257 return -1;
258
259 return 0;
260 }
261
262 #ifdef GIT_SSL
263
264 static int verify_server_cert(gitno_ssl *ssl, const char *host)
265 {
266 X509 *cert;
267 X509_NAME *peer_name;
268 ASN1_STRING *str;
269 unsigned char *peer_cn = NULL;
270 int matched = -1, type = GEN_DNS;
271 GENERAL_NAMES *alts;
272 struct in6_addr addr6;
273 struct in_addr addr4;
274 void *addr;
275 int i = -1,j;
276
277 if (SSL_get_verify_result(ssl->ssl) != X509_V_OK) {
278 giterr_set(GITERR_SSL, "The SSL certificate is invalid");
279 return -1;
280 }
281
282 /* Try to parse the host as an IP address to see if it is */
283 if (p_inet_pton(AF_INET, host, &addr4)) {
284 type = GEN_IPADD;
285 addr = &addr4;
286 } else {
287 if(p_inet_pton(AF_INET6, host, &addr6)) {
288 type = GEN_IPADD;
289 addr = &addr6;
290 }
291 }
292
293
294 cert = SSL_get_peer_certificate(ssl->ssl);
295 if (!cert) {
296 giterr_set(GITERR_SSL, "the server did not provide a certificate");
297 return -1;
298 }
299
300 /* Check the alternative names */
301 alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
302 if (alts) {
303 int num;
304
305 num = sk_GENERAL_NAME_num(alts);
306 for (i = 0; i < num && matched != 1; i++) {
307 const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i);
308 const char *name = (char *) ASN1_STRING_data(gn->d.ia5);
309 size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5);
310
311 /* Skip any names of a type we're not looking for */
312 if (gn->type != type)
313 continue;
314
315 if (type == GEN_DNS) {
316 /* If it contains embedded NULs, don't even try */
317 if (memchr(name, '\0', namelen))
318 continue;
319
320 if (check_host_name(name, host) < 0)
321 matched = 0;
322 else
323 matched = 1;
324 } else if (type == GEN_IPADD) {
325 /* Here name isn't so much a name but a binary representation of the IP */
326 matched = !!memcmp(name, addr, namelen);
327 }
328 }
329 }
330 GENERAL_NAMES_free(alts);
331
332 if (matched == 0)
333 goto cert_fail_name;
334
335 if (matched == 1)
336 return 0;
337
338 /* If no alternative names are available, check the common name */
339 peer_name = X509_get_subject_name(cert);
340 if (peer_name == NULL)
341 goto on_error;
342
343 if (peer_name) {
344 /* Get the index of the last CN entry */
345 while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0)
346 i = j;
347 }
348
349 if (i < 0)
350 goto on_error;
351
352 str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i));
353 if (str == NULL)
354 goto on_error;
355
356 /* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */
357 if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) {
358 int size = ASN1_STRING_length(str);
359
360 if (size > 0) {
361 peer_cn = OPENSSL_malloc(size + 1);
362 GITERR_CHECK_ALLOC(peer_cn);
363 memcpy(peer_cn, ASN1_STRING_data(str), size);
364 peer_cn[size] = '\0';
365 }
366 } else {
367 int size = ASN1_STRING_to_UTF8(&peer_cn, str);
368 GITERR_CHECK_ALLOC(peer_cn);
369 if (memchr(peer_cn, '\0', size))
370 goto cert_fail_name;
371 }
372
373 if (check_host_name((char *)peer_cn, host) < 0)
374 goto cert_fail_name;
375
376 OPENSSL_free(peer_cn);
377
378 return 0;
379
380 on_error:
381 OPENSSL_free(peer_cn);
382 return ssl_set_error(ssl, 0);
383
384 cert_fail_name:
385 OPENSSL_free(peer_cn);
386 giterr_set(GITERR_SSL, "hostname does not match certificate");
387 return GIT_ECERTIFICATE;
388 }
389
390 static int ssl_setup(gitno_socket *socket, const char *host)
391 {
392 int ret;
393
394 if (git__ssl_ctx == NULL) {
395 giterr_set(GITERR_NET, "OpenSSL initialization failed");
396 return -1;
397 }
398
399 socket->ssl.ssl = SSL_new(git__ssl_ctx);
400 if (socket->ssl.ssl == NULL)
401 return ssl_set_error(&socket->ssl, 0);
402
403 if((ret = SSL_set_fd(socket->ssl.ssl, socket->socket)) == 0)
404 return ssl_set_error(&socket->ssl, ret);
405
406 if ((ret = SSL_connect(socket->ssl.ssl)) <= 0)
407 return ssl_set_error(&socket->ssl, ret);
408
409 return verify_server_cert(&socket->ssl, host);
410 }
411 #endif
412
413 static int gitno__close(GIT_SOCKET s)
414 {
415 #ifdef GIT_WIN32
416 if (SOCKET_ERROR == closesocket(s))
417 return -1;
418
419 if (0 != WSACleanup()) {
420 giterr_set(GITERR_OS, "Winsock cleanup failed");
421 return -1;
422 }
423
424 return 0;
425 #else
426 return close(s);
427 #endif
428 }
429
430 int gitno_connect(gitno_socket *s_out, const char *host, const char *port, int flags)
431 {
432 struct addrinfo *info = NULL, *p;
433 struct addrinfo hints;
434 GIT_SOCKET s = INVALID_SOCKET;
435 int ret;
436
437 #ifdef GIT_WIN32
438 /* on win32, the WSA context needs to be initialized
439 * before any socket calls can be performed */
440 WSADATA wsd;
441
442 if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
443 giterr_set(GITERR_OS, "Winsock init failed");
444 return -1;
445 }
446
447 if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) {
448 WSACleanup();
449 giterr_set(GITERR_OS, "Winsock init failed");
450 return -1;
451 }
452 #endif
453
454 /* Zero the socket structure provided */
455 memset(s_out, 0x0, sizeof(gitno_socket));
456
457 memset(&hints, 0x0, sizeof(struct addrinfo));
458 hints.ai_socktype = SOCK_STREAM;
459 hints.ai_family = AF_UNSPEC;
460
461 if ((ret = p_getaddrinfo(host, port, &hints, &info)) != 0) {
462 giterr_set(GITERR_NET,
463 "Failed to resolve address for %s: %s", host, p_gai_strerror(ret));
464 return -1;
465 }
466
467 for (p = info; p != NULL; p = p->ai_next) {
468 s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
469
470 if (s == INVALID_SOCKET) {
471 net_set_error("error creating socket");
472 break;
473 }
474
475 if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
476 break;
477
478 /* If we can't connect, try the next one */
479 gitno__close(s);
480 s = INVALID_SOCKET;
481 }
482
483 /* Oops, we couldn't connect to any address */
484 if (s == INVALID_SOCKET && p == NULL) {
485 giterr_set(GITERR_OS, "Failed to connect to %s", host);
486 p_freeaddrinfo(info);
487 return -1;
488 }
489
490 s_out->socket = s;
491 p_freeaddrinfo(info);
492
493 #ifdef GIT_SSL
494 if ((flags & GITNO_CONNECT_SSL) &&
495 (ret = ssl_setup(s_out, host)) < 0)
496 return ret;
497 #else
498 /* SSL is not supported */
499 if (flags & GITNO_CONNECT_SSL) {
500 giterr_set(GITERR_OS, "SSL is not supported by this copy of libgit2.");
501 return -1;
502 }
503 #endif
504
505 return 0;
506 }
507
508 #ifdef GIT_SSL
509 static int gitno_send_ssl(gitno_ssl *ssl, const char *msg, size_t len, int flags)
510 {
511 int ret;
512 size_t off = 0;
513
514 GIT_UNUSED(flags);
515
516 while (off < len) {
517 ret = SSL_write(ssl->ssl, msg + off, len - off);
518 if (ret <= 0 && ret != SSL_ERROR_WANT_WRITE)
519 return ssl_set_error(ssl, ret);
520
521 off += ret;
522 }
523
524 return off;
525 }
526 #endif
527
528 int gitno_send(gitno_socket *socket, const char *msg, size_t len, int flags)
529 {
530 int ret;
531 size_t off = 0;
532
533 #ifdef GIT_SSL
534 if (socket->ssl.ssl)
535 return gitno_send_ssl(&socket->ssl, msg, len, flags);
536 #endif
537
538 while (off < len) {
539 errno = 0;
540 ret = p_send(socket->socket, msg + off, len - off, flags);
541 if (ret < 0) {
542 net_set_error("Error sending data");
543 return -1;
544 }
545
546 off += ret;
547 }
548
549 return (int)off;
550 }
551
552 int gitno_close(gitno_socket *s)
553 {
554 #ifdef GIT_SSL
555 if (s->ssl.ssl &&
556 gitno_ssl_teardown(&s->ssl) < 0)
557 return -1;
558 #endif
559
560 return gitno__close(s->socket);
561 }
562
563 int gitno_select_in(gitno_buffer *buf, long int sec, long int usec)
564 {
565 fd_set fds;
566 struct timeval tv;
567
568 tv.tv_sec = sec;
569 tv.tv_usec = usec;
570
571 FD_ZERO(&fds);
572 FD_SET(buf->socket->socket, &fds);
573
574 /* The select(2) interface is silly */
575 return select((int)buf->socket->socket + 1, &fds, NULL, NULL, &tv);
576 }
577
578 static const char *prefix_http = "http://";
579 static const char *prefix_https = "https://";
580
581 int gitno_connection_data_from_url(
582 gitno_connection_data *data,
583 const char *url,
584 const char *service_suffix)
585 {
586 int error = -1;
587 const char *default_port = NULL, *path_search_start = NULL;
588 char *original_host = NULL;
589
590 /* service_suffix is optional */
591 assert(data && url);
592
593 /* Save these for comparison later */
594 original_host = data->host;
595 data->host = NULL;
596 gitno_connection_data_free_ptrs(data);
597
598 if (!git__prefixcmp(url, prefix_http)) {
599 path_search_start = url + strlen(prefix_http);
600 default_port = "80";
601
602 if (data->use_ssl) {
603 giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP is not allowed");
604 goto cleanup;
605 }
606 } else if (!git__prefixcmp(url, prefix_https)) {
607 path_search_start = url + strlen(prefix_https);
608 default_port = "443";
609 data->use_ssl = true;
610 } else if (url[0] == '/')
611 default_port = data->use_ssl ? "443" : "80";
612
613 if (!default_port) {
614 giterr_set(GITERR_NET, "Unrecognized URL prefix");
615 goto cleanup;
616 }
617
618 error = gitno_extract_url_parts(
619 &data->host, &data->port, &data->path, &data->user, &data->pass,
620 url, default_port);
621
622 if (url[0] == '/') {
623 /* Relative redirect; reuse original host name and port */
624 path_search_start = url;
625 git__free(data->host);
626 data->host = original_host;
627 original_host = NULL;
628 }
629
630 if (!error) {
631 const char *path = strchr(path_search_start, '/');
632 size_t pathlen = strlen(path);
633 size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
634
635 if (suffixlen &&
636 !memcmp(path + pathlen - suffixlen, service_suffix, suffixlen)) {
637 git__free(data->path);
638 data->path = git__strndup(path, pathlen - suffixlen);
639 } else {
640 git__free(data->path);
641 data->path = git__strdup(path);
642 }
643
644 /* Check for errors in the resulting data */
645 if (original_host && url[0] != '/' && strcmp(original_host, data->host)) {
646 giterr_set(GITERR_NET, "Cross host redirect not allowed");
647 error = -1;
648 }
649 }
650
651 cleanup:
652 if (original_host) git__free(original_host);
653 return error;
654 }
655
656 void gitno_connection_data_free_ptrs(gitno_connection_data *d)
657 {
658 git__free(d->host); d->host = NULL;
659 git__free(d->port); d->port = NULL;
660 git__free(d->path); d->path = NULL;
661 git__free(d->user); d->user = NULL;
662 git__free(d->pass); d->pass = NULL;
663 }
664
665 #define hex2c(c) ((c | 32) % 39 - 9)
666 static char* unescape(char *str)
667 {
668 int x, y;
669 int len = (int)strlen(str);
670
671 for (x=y=0; str[y]; ++x, ++y) {
672 if ((str[x] = str[y]) == '%') {
673 if (y < len-2 && isxdigit(str[y+1]) && isxdigit(str[y+2])) {
674 str[x] = (hex2c(str[y+1]) << 4) + hex2c(str[y+2]);
675 y += 2;
676 }
677 }
678 }
679 str[x] = '\0';
680 return str;
681 }
682
683 int gitno_extract_url_parts(
684 char **host,
685 char **port,
686 char **path,
687 char **username,
688 char **password,
689 const char *url,
690 const char *default_port)
691 {
692 struct http_parser_url u = {0};
693 const char *_host, *_port, *_path, *_userinfo;
694
695 if (http_parser_parse_url(url, strlen(url), false, &u)) {
696 giterr_set(GITERR_NET, "Malformed URL '%s'", url);
697 return GIT_EINVALIDSPEC;
698 }
699
700 _host = url+u.field_data[UF_HOST].off;
701 _port = url+u.field_data[UF_PORT].off;
702 _path = url+u.field_data[UF_PATH].off;
703 _userinfo = url+u.field_data[UF_USERINFO].off;
704
705 if (u.field_set & (1 << UF_HOST)) {
706 *host = git__substrdup(_host, u.field_data[UF_HOST].len);
707 GITERR_CHECK_ALLOC(*host);
708 }
709
710 if (u.field_set & (1 << UF_PORT))
711 *port = git__substrdup(_port, u.field_data[UF_PORT].len);
712 else
713 *port = git__strdup(default_port);
714 GITERR_CHECK_ALLOC(*port);
715
716 if (u.field_set & (1 << UF_PATH)) {
717 *path = git__substrdup(_path, u.field_data[UF_PATH].len);
718 GITERR_CHECK_ALLOC(*path);
719 } else {
720 giterr_set(GITERR_NET, "invalid url, missing path");
721 return GIT_EINVALIDSPEC;
722 }
723
724 if (u.field_set & (1 << UF_USERINFO)) {
725 const char *colon = memchr(_userinfo, ':', u.field_data[UF_USERINFO].len);
726 if (colon) {
727 *username = unescape(git__substrdup(_userinfo, colon - _userinfo));
728 *password = unescape(git__substrdup(colon+1, u.field_data[UF_USERINFO].len - (colon+1-_userinfo)));
729 GITERR_CHECK_ALLOC(*password);
730 } else {
731 *username = git__substrdup(_userinfo, u.field_data[UF_USERINFO].len);
732 }
733 GITERR_CHECK_ALLOC(*username);
734
735 }
736
737 return 0;
738 }