2 * Copyright (C) 2009-2012 the libgit2 contributors
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 <arpa/inet.h>
15 # include <ws2tcpip.h>
17 # pragma comment(lib, "ws2_32.lib")
22 # include <openssl/ssl.h>
23 # include <openssl/x509v3.h>
27 #include "git2/errors.h"
33 #include "transport.h"
36 static void net_set_error(const char *str
)
38 int size
, error
= WSAGetLastError();
41 size
= FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
,
42 0, error
, 0, (LPSTR
)&err_str
, 0, 0);
44 giterr_set(GITERR_NET
, "%s: %s", str
, err_str
);
48 static void net_set_error(const char *str
)
50 giterr_set(GITERR_NET
, "%s: %s", str
, strerror(errno
));
55 static int ssl_set_error(gitno_ssl
*ssl
, int error
)
58 err
= SSL_get_error(ssl
->ssl
, error
);
59 giterr_set(GITERR_NET
, "SSL error: %s", ERR_error_string(err
, NULL
));
64 void gitno_buffer_setup(git_transport
*t
, gitno_buffer
*buf
, char *data
, size_t len
)
66 memset(buf
, 0x0, sizeof(gitno_buffer
));
67 memset(data
, 0x0, len
);
79 static int ssl_recv(gitno_ssl
*ssl
, void *data
, size_t len
)
84 ret
= SSL_read(ssl
->ssl
, data
, len
);
85 } while (SSL_get_error(ssl
->ssl
, ret
) == SSL_ERROR_WANT_READ
);
88 return ssl_set_error(ssl
, ret
);
94 int gitno_recv(gitno_buffer
*buf
)
99 if (buf
->ssl
!= NULL
) {
100 if ((ret
= ssl_recv(buf
->ssl
, buf
->data
+ buf
->offset
, buf
->len
- buf
->offset
)) < 0)
103 ret
= p_recv(buf
->fd
, buf
->data
+ buf
->offset
, buf
->len
- buf
->offset
, 0);
105 net_set_error("Error receiving socket data");
110 ret
= p_recv(buf
->fd
, buf
->data
+ buf
->offset
, buf
->len
- buf
->offset
, 0);
112 net_set_error("Error receiving socket data");
121 /* Consume up to ptr and move the rest of the buffer to the beginning */
122 void gitno_consume(gitno_buffer
*buf
, const char *ptr
)
126 assert(ptr
- buf
->data
>= 0);
127 assert(ptr
- buf
->data
<= (int) buf
->len
);
129 consumed
= ptr
- buf
->data
;
131 memmove(buf
->data
, ptr
, buf
->offset
- consumed
);
132 memset(buf
->data
+ buf
->offset
, 0x0, buf
->len
- buf
->offset
);
133 buf
->offset
-= consumed
;
136 /* Consume const bytes and move the rest of the buffer to the beginning */
137 void gitno_consume_n(gitno_buffer
*buf
, size_t cons
)
139 memmove(buf
->data
, buf
->data
+ cons
, buf
->len
- buf
->offset
);
140 memset(buf
->data
+ cons
, 0x0, buf
->len
- buf
->offset
);
144 int gitno_ssl_teardown(git_transport
*t
)
156 ret
= SSL_shutdown(t
->ssl
.ssl
);
159 return ssl_set_error(&t
->ssl
, ret
);
161 SSL_free(t
->ssl
.ssl
);
162 SSL_CTX_free(t
->ssl
.ctx
);
169 /* Match host names according to RFC 2818 rules */
170 static int match_host(const char *pattern
, const char *host
)
173 char c
= tolower(*pattern
++);
176 return *host
? -1 : 0;
180 /* '*' at the end matches everything left */
185 * We've found a pattern, so move towards the next matching
186 * char. The '.' is handled specially because wildcards aren't
187 * allowed to cross subdomains.
191 char h
= tolower(*host
);
193 return match_host(pattern
, host
++);
195 return match_host(pattern
, host
);
201 if (c
!= tolower(*host
++))
208 static int check_host_name(const char *name
, const char *host
)
210 if (!strcasecmp(name
, host
))
213 if (match_host(name
, host
) < 0)
219 static int verify_server_cert(git_transport
*t
, const char *host
)
222 X509_NAME
*peer_name
;
224 unsigned char *peer_cn
= NULL
;
225 int matched
= -1, type
= GEN_DNS
;
227 struct in6_addr addr6
;
228 struct in_addr addr4
;
233 /* Try to parse the host as an IP address to see if it is */
234 if (inet_pton(AF_INET
, host
, &addr4
)) {
238 if(inet_pton(AF_INET6
, host
, &addr6
)) {
245 cert
= SSL_get_peer_certificate(t
->ssl
.ssl
);
247 /* Check the alternative names */
248 alts
= X509_get_ext_d2i(cert
, NID_subject_alt_name
, NULL
, NULL
);
252 num
= sk_GENERAL_NAME_num(alts
);
253 for (i
= 0; i
< num
&& matched
!= 1; i
++) {
254 const GENERAL_NAME
*gn
= sk_GENERAL_NAME_value(alts
, i
);
255 const char *name
= (char *) ASN1_STRING_data(gn
->d
.ia5
);
256 size_t namelen
= (size_t) ASN1_STRING_length(gn
->d
.ia5
);
258 /* Skip any names of a type we're not looking for */
259 if (gn
->type
!= type
)
262 if (type
== GEN_DNS
) {
263 /* If it contains embedded NULs, don't even try */
264 if (memchr(name
, '\0', namelen
))
267 if (check_host_name(name
, host
) < 0)
271 } else if (type
== GEN_IPADD
) {
272 /* Here name isn't so much a name but a binary representation of the IP */
273 matched
= !!memcmp(name
, addr
, namelen
);
277 GENERAL_NAMES_free(alts
);
285 /* If no alternative names are available, check the common name */
286 peer_name
= X509_get_subject_name(cert
);
287 if (peer_name
== NULL
)
291 /* Get the index of the last CN entry */
292 while ((j
= X509_NAME_get_index_by_NID(peer_name
, NID_commonName
, i
)) >= 0)
299 str
= X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name
, i
));
303 /* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */
304 if (ASN1_STRING_type(str
) == V_ASN1_UTF8STRING
) {
305 int size
= ASN1_STRING_length(str
);
308 peer_cn
= OPENSSL_malloc(size
+ 1);
309 GITERR_CHECK_ALLOC(peer_cn
);
310 memcpy(peer_cn
, ASN1_STRING_data(str
), size
);
311 peer_cn
[size
] = '\0';
314 int size
= ASN1_STRING_to_UTF8(&peer_cn
, str
);
315 GITERR_CHECK_ALLOC(peer_cn
);
316 if (memchr(peer_cn
, '\0', size
))
320 if (check_host_name((char *)peer_cn
, host
) < 0)
323 OPENSSL_free(peer_cn
);
328 OPENSSL_free(peer_cn
);
329 return ssl_set_error(&t
->ssl
, 0);
332 OPENSSL_free(peer_cn
);
333 giterr_set(GITERR_SSL
, "Certificate host name check failed");
337 static int ssl_setup(git_transport
*t
, const char *host
)
342 SSL_load_error_strings();
343 t
->ssl
.ctx
= SSL_CTX_new(SSLv23_method());
344 if (t
->ssl
.ctx
== NULL
)
345 return ssl_set_error(&t
->ssl
, 0);
347 SSL_CTX_set_mode(t
->ssl
.ctx
, SSL_MODE_AUTO_RETRY
);
348 SSL_CTX_set_verify(t
->ssl
.ctx
, SSL_VERIFY_PEER
, NULL
);
349 if (!SSL_CTX_set_default_verify_paths(t
->ssl
.ctx
))
350 return ssl_set_error(&t
->ssl
, 0);
352 t
->ssl
.ssl
= SSL_new(t
->ssl
.ctx
);
353 if (t
->ssl
.ssl
== NULL
)
354 return ssl_set_error(&t
->ssl
, 0);
356 if((ret
= SSL_set_fd(t
->ssl
.ssl
, t
->socket
)) == 0)
357 return ssl_set_error(&t
->ssl
, ret
);
359 if ((ret
= SSL_connect(t
->ssl
.ssl
)) <= 0)
360 return ssl_set_error(&t
->ssl
, ret
);
362 if (t
->check_cert
&& verify_server_cert(t
, host
) < 0)
368 static int ssl_setup(git_transport
*t
, const char *host
)
376 int gitno_connect(git_transport
*t
, const char *host
, const char *port
)
378 struct addrinfo
*info
= NULL
, *p
;
379 struct addrinfo hints
;
381 GIT_SOCKET s
= INVALID_SOCKET
;
383 memset(&hints
, 0x0, sizeof(struct addrinfo
));
384 hints
.ai_socktype
= SOCK_STREAM
;
385 hints
.ai_family
= AF_UNSPEC
;
387 if ((ret
= p_getaddrinfo(host
, port
, &hints
, &info
)) < 0) {
388 giterr_set(GITERR_NET
,
389 "Failed to resolve address for %s: %s", host
, p_gai_strerror(ret
));
393 for (p
= info
; p
!= NULL
; p
= p
->ai_next
) {
394 s
= socket(p
->ai_family
, p
->ai_socktype
, p
->ai_protocol
);
396 if (s
== INVALID_SOCKET
) {
397 net_set_error("error creating socket");
401 if (connect(s
, p
->ai_addr
, (socklen_t
)p
->ai_addrlen
) == 0)
404 /* If we can't connect, try the next one */
409 /* Oops, we couldn't connect to any address */
410 if (s
== INVALID_SOCKET
&& p
== NULL
) {
411 giterr_set(GITERR_OS
, "Failed to connect to %s", host
);
416 p_freeaddrinfo(info
);
418 if (t
->encrypt
&& ssl_setup(t
, host
) < 0)
425 static int send_ssl(gitno_ssl
*ssl
, const char *msg
, size_t len
)
431 ret
= SSL_write(ssl
->ssl
, msg
+ off
, len
- off
);
433 return ssl_set_error(ssl
, ret
);
442 int gitno_send(git_transport
*t
, const char *msg
, size_t len
, int flags
)
449 return send_ssl(&t
->ssl
, msg
, len
);
454 ret
= p_send(t
->socket
, msg
+ off
, len
- off
, flags
);
456 net_set_error("Error sending data");
468 int gitno_close(GIT_SOCKET s
)
470 return closesocket(s
) == SOCKET_ERROR
? -1 : 0;
473 int gitno_close(GIT_SOCKET s
)
479 int gitno_select_in(gitno_buffer
*buf
, long int sec
, long int usec
)
488 FD_SET(buf
->fd
, &fds
);
490 /* The select(2) interface is silly */
491 return select((int)buf
->fd
+ 1, &fds
, NULL
, NULL
, &tv
);
494 int gitno_extract_host_and_port(char **host
, char **port
, const char *url
, const char *default_port
)
496 char *colon
, *slash
, *delim
;
498 colon
= strchr(url
, ':');
499 slash
= strchr(url
, '/');
502 giterr_set(GITERR_NET
, "Malformed URL: missing /");
507 *port
= git__strdup(default_port
);
509 *port
= git__strndup(colon
+ 1, slash
- colon
- 1);
511 GITERR_CHECK_ALLOC(*port
);
513 delim
= colon
== NULL
? slash
: colon
;
514 *host
= git__strndup(url
, delim
- url
);
515 GITERR_CHECK_ALLOC(*host
);