2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
8 # include <sys/types.h>
9 # include <sys/socket.h>
10 # include <sys/select.h>
11 # include <sys/time.h>
13 # include <netinet/in.h>
14 # include <arpa/inet.h>
16 # include <winsock2.h>
17 # include <ws2tcpip.h>
19 # pragma comment(lib, "ws2_32")
24 # include <openssl/ssl.h>
25 # include <openssl/err.h>
26 # include <openssl/x509v3.h>
30 #include "git2/errors.h"
36 #include "http_parser.h"
40 static void net_set_error(const char *str
)
42 int error
= WSAGetLastError();
43 char * win32_error
= git_win32_get_error_message(error
);
46 giterr_set(GITERR_NET
, "%s: %s", str
, win32_error
);
47 git__free(win32_error
);
49 giterr_set(GITERR_NET
, str
);
53 static void net_set_error(const char *str
)
55 giterr_set(GITERR_NET
, "%s: %s", str
, strerror(errno
));
60 static int ssl_set_error(gitno_ssl
*ssl
, int error
)
65 err
= SSL_get_error(ssl
->ssl
, error
);
67 assert(err
!= SSL_ERROR_WANT_READ
);
68 assert(err
!= SSL_ERROR_WANT_WRITE
);
71 case SSL_ERROR_WANT_CONNECT
:
72 case SSL_ERROR_WANT_ACCEPT
:
73 giterr_set(GITERR_NET
, "SSL error: connection failure\n");
75 case SSL_ERROR_WANT_X509_LOOKUP
:
76 giterr_set(GITERR_NET
, "SSL error: x509 error\n");
78 case SSL_ERROR_SYSCALL
:
81 giterr_set(GITERR_NET
, "SSL error: %s",
82 ERR_error_string(e
, NULL
));
84 } else if (error
< 0) {
85 giterr_set(GITERR_OS
, "SSL error: syscall failure");
88 giterr_set(GITERR_NET
, "SSL error: received early EOF");
92 giterr_set(GITERR_NET
, "SSL error: %s",
93 ERR_error_string(e
, NULL
));
96 case SSL_ERROR_ZERO_RETURN
:
98 giterr_set(GITERR_NET
, "SSL error: unknown error");
105 int gitno_recv(gitno_buffer
*buf
)
107 return buf
->recv(buf
);
111 static int gitno__recv_ssl(gitno_buffer
*buf
)
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
);
120 net_set_error("Error receiving socket data");
129 static int gitno__recv(gitno_buffer
*buf
)
133 ret
= p_recv(buf
->socket
->socket
, buf
->data
+ buf
->offset
, buf
->len
- buf
->offset
, 0);
135 net_set_error("Error receiving socket data");
143 void gitno_buffer_setup_callback(
144 gitno_socket
*socket
,
148 int (*recv
)(gitno_buffer
*buf
), void *cb_data
)
150 memset(data
, 0x0, len
);
154 buf
->socket
= socket
;
156 buf
->cb_data
= cb_data
;
159 void gitno_buffer_setup(gitno_socket
*socket
, gitno_buffer
*buf
, char *data
, size_t len
)
162 if (socket
->ssl
.ssl
) {
163 gitno_buffer_setup_callback(socket
, buf
, data
, len
, gitno__recv_ssl
, NULL
);
168 gitno_buffer_setup_callback(socket
, buf
, data
, len
, gitno__recv
, NULL
);
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
)
176 assert(ptr
- buf
->data
>= 0);
177 assert(ptr
- buf
->data
<= (int) buf
->len
);
179 consumed
= ptr
- buf
->data
;
181 memmove(buf
->data
, ptr
, buf
->offset
- consumed
);
182 memset(buf
->data
+ buf
->offset
, 0x0, buf
->len
- buf
->offset
);
183 buf
->offset
-= consumed
;
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
)
189 memmove(buf
->data
, buf
->data
+ cons
, buf
->len
- buf
->offset
);
190 memset(buf
->data
+ cons
, 0x0, buf
->len
- buf
->offset
);
196 static int gitno_ssl_teardown(gitno_ssl
*ssl
)
200 ret
= SSL_shutdown(ssl
->ssl
);
202 ret
= ssl_set_error(ssl
, ret
);
212 /* Match host names according to RFC 2818 rules */
213 int gitno__match_host(const char *pattern
, const char *host
)
216 char c
= tolower(*pattern
++);
219 return *host
? -1 : 0;
223 /* '*' at the end matches everything left */
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.
234 char h
= tolower(*host
);
236 return gitno__match_host(pattern
, host
++);
238 return gitno__match_host(pattern
, host
);
244 if (c
!= tolower(*host
++))
251 static int check_host_name(const char *name
, const char *host
)
253 if (!strcasecmp(name
, host
))
256 if (gitno__match_host(name
, host
) < 0)
264 static int verify_server_cert(gitno_ssl
*ssl
, const char *host
)
267 X509_NAME
*peer_name
;
269 unsigned char *peer_cn
= NULL
;
270 int matched
= -1, type
= GEN_DNS
;
272 struct in6_addr addr6
;
273 struct in_addr addr4
;
277 if (SSL_get_verify_result(ssl
->ssl
) != X509_V_OK
) {
278 giterr_set(GITERR_SSL
, "The SSL certificate is invalid");
282 /* Try to parse the host as an IP address to see if it is */
283 if (p_inet_pton(AF_INET
, host
, &addr4
)) {
287 if(p_inet_pton(AF_INET6
, host
, &addr6
)) {
294 cert
= SSL_get_peer_certificate(ssl
->ssl
);
296 giterr_set(GITERR_SSL
, "the server did not provide a certificate");
300 /* Check the alternative names */
301 alts
= X509_get_ext_d2i(cert
, NID_subject_alt_name
, NULL
, NULL
);
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
);
311 /* Skip any names of a type we're not looking for */
312 if (gn
->type
!= type
)
315 if (type
== GEN_DNS
) {
316 /* If it contains embedded NULs, don't even try */
317 if (memchr(name
, '\0', namelen
))
320 if (check_host_name(name
, host
) < 0)
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
);
330 GENERAL_NAMES_free(alts
);
338 /* If no alternative names are available, check the common name */
339 peer_name
= X509_get_subject_name(cert
);
340 if (peer_name
== NULL
)
344 /* Get the index of the last CN entry */
345 while ((j
= X509_NAME_get_index_by_NID(peer_name
, NID_commonName
, i
)) >= 0)
352 str
= X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name
, i
));
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
);
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';
367 int size
= ASN1_STRING_to_UTF8(&peer_cn
, str
);
368 GITERR_CHECK_ALLOC(peer_cn
);
369 if (memchr(peer_cn
, '\0', size
))
373 if (check_host_name((char *)peer_cn
, host
) < 0)
376 OPENSSL_free(peer_cn
);
381 OPENSSL_free(peer_cn
);
382 return ssl_set_error(ssl
, 0);
385 OPENSSL_free(peer_cn
);
386 giterr_set(GITERR_SSL
, "hostname does not match certificate");
387 return GIT_ECERTIFICATE
;
390 static int ssl_setup(gitno_socket
*socket
, const char *host
)
394 if (git__ssl_ctx
== NULL
) {
395 giterr_set(GITERR_NET
, "OpenSSL initialization failed");
399 socket
->ssl
.ssl
= SSL_new(git__ssl_ctx
);
400 if (socket
->ssl
.ssl
== NULL
)
401 return ssl_set_error(&socket
->ssl
, 0);
403 if((ret
= SSL_set_fd(socket
->ssl
.ssl
, socket
->socket
)) == 0)
404 return ssl_set_error(&socket
->ssl
, ret
);
406 if ((ret
= SSL_connect(socket
->ssl
.ssl
)) <= 0)
407 return ssl_set_error(&socket
->ssl
, ret
);
409 return verify_server_cert(&socket
->ssl
, host
);
413 static int gitno__close(GIT_SOCKET s
)
416 if (SOCKET_ERROR
== closesocket(s
))
419 if (0 != WSACleanup()) {
420 giterr_set(GITERR_OS
, "Winsock cleanup failed");
430 int gitno_connect(gitno_socket
*s_out
, const char *host
, const char *port
, int flags
)
432 struct addrinfo
*info
= NULL
, *p
;
433 struct addrinfo hints
;
434 GIT_SOCKET s
= INVALID_SOCKET
;
438 /* on win32, the WSA context needs to be initialized
439 * before any socket calls can be performed */
442 if (WSAStartup(MAKEWORD(2,2), &wsd
) != 0) {
443 giterr_set(GITERR_OS
, "Winsock init failed");
447 if (LOBYTE(wsd
.wVersion
) != 2 || HIBYTE(wsd
.wVersion
) != 2) {
449 giterr_set(GITERR_OS
, "Winsock init failed");
454 /* Zero the socket structure provided */
455 memset(s_out
, 0x0, sizeof(gitno_socket
));
457 memset(&hints
, 0x0, sizeof(struct addrinfo
));
458 hints
.ai_socktype
= SOCK_STREAM
;
459 hints
.ai_family
= AF_UNSPEC
;
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
));
467 for (p
= info
; p
!= NULL
; p
= p
->ai_next
) {
468 s
= socket(p
->ai_family
, p
->ai_socktype
, p
->ai_protocol
);
470 if (s
== INVALID_SOCKET
) {
471 net_set_error("error creating socket");
475 if (connect(s
, p
->ai_addr
, (socklen_t
)p
->ai_addrlen
) == 0)
478 /* If we can't connect, try the next one */
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
);
491 p_freeaddrinfo(info
);
494 if ((flags
& GITNO_CONNECT_SSL
) &&
495 (ret
= ssl_setup(s_out
, host
)) < 0)
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.");
509 static int gitno_send_ssl(gitno_ssl
*ssl
, const char *msg
, size_t len
, int flags
)
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
);
528 int gitno_send(gitno_socket
*socket
, const char *msg
, size_t len
, int flags
)
535 return gitno_send_ssl(&socket
->ssl
, msg
, len
, flags
);
540 ret
= p_send(socket
->socket
, msg
+ off
, len
- off
, flags
);
542 net_set_error("Error sending data");
552 int gitno_close(gitno_socket
*s
)
556 gitno_ssl_teardown(&s
->ssl
) < 0)
560 return gitno__close(s
->socket
);
563 int gitno_select_in(gitno_buffer
*buf
, long int sec
, long int usec
)
572 FD_SET(buf
->socket
->socket
, &fds
);
574 /* The select(2) interface is silly */
575 return select((int)buf
->socket
->socket
+ 1, &fds
, NULL
, NULL
, &tv
);
578 static const char *prefix_http
= "http://";
579 static const char *prefix_https
= "https://";
581 int gitno_connection_data_from_url(
582 gitno_connection_data
*data
,
584 const char *service_suffix
)
587 const char *default_port
= NULL
, *path_search_start
= NULL
;
588 char *original_host
= NULL
;
590 /* service_suffix is optional */
593 /* Save these for comparison later */
594 original_host
= data
->host
;
596 gitno_connection_data_free_ptrs(data
);
598 if (!git__prefixcmp(url
, prefix_http
)) {
599 path_search_start
= url
+ strlen(prefix_http
);
603 giterr_set(GITERR_NET
, "Redirect from HTTPS to HTTP is not allowed");
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";
614 giterr_set(GITERR_NET
, "Unrecognized URL prefix");
618 error
= gitno_extract_url_parts(
619 &data
->host
, &data
->port
, &data
->path
, &data
->user
, &data
->pass
,
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
;
631 const char *path
= strchr(path_search_start
, '/');
632 size_t pathlen
= strlen(path
);
633 size_t suffixlen
= service_suffix
? strlen(service_suffix
) : 0;
636 !memcmp(path
+ pathlen
- suffixlen
, service_suffix
, suffixlen
)) {
637 git__free(data
->path
);
638 data
->path
= git__strndup(path
, pathlen
- suffixlen
);
640 git__free(data
->path
);
641 data
->path
= git__strdup(path
);
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");
652 if (original_host
) git__free(original_host
);
656 void gitno_connection_data_free_ptrs(gitno_connection_data
*d
)
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
;
665 #define hex2c(c) ((c | 32) % 39 - 9)
666 static char* unescape(char *str
)
669 int len
= (int)strlen(str
);
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]);
683 int gitno_extract_url_parts(
690 const char *default_port
)
692 struct http_parser_url u
= {0};
693 const char *_host
, *_port
, *_path
, *_userinfo
;
695 if (http_parser_parse_url(url
, strlen(url
), false, &u
)) {
696 giterr_set(GITERR_NET
, "Malformed URL '%s'", url
);
697 return GIT_EINVALIDSPEC
;
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
;
705 if (u
.field_set
& (1 << UF_HOST
)) {
706 *host
= git__substrdup(_host
, u
.field_data
[UF_HOST
].len
);
707 GITERR_CHECK_ALLOC(*host
);
710 if (u
.field_set
& (1 << UF_PORT
))
711 *port
= git__substrdup(_port
, u
.field_data
[UF_PORT
].len
);
713 *port
= git__strdup(default_port
);
714 GITERR_CHECK_ALLOC(*port
);
716 if (u
.field_set
& (1 << UF_PATH
)) {
717 *path
= git__substrdup(_path
, u
.field_data
[UF_PATH
].len
);
718 GITERR_CHECK_ALLOC(*path
);
720 giterr_set(GITERR_NET
, "invalid url, missing path");
721 return GIT_EINVALIDSPEC
;
724 if (u
.field_set
& (1 << UF_USERINFO
)) {
725 const char *colon
= memchr(_userinfo
, ':', u
.field_data
[UF_USERINFO
].len
);
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
);
731 *username
= git__substrdup(_userinfo
, u
.field_data
[UF_USERINFO
].len
);
733 GITERR_CHECK_ALLOC(*username
);