]> git.proxmox.com Git - libgit2.git/blob - src/netops.c
Merge branch 'development' into clar2
[libgit2.git] / src / netops.c
1 /*
2 * Copyright (C) 2009-2012 the libgit2 contributors
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 <arpa/inet.h>
14 #else
15 # include <ws2tcpip.h>
16 # ifdef _MSC_VER
17 # pragma comment(lib, "ws2_32")
18 # endif
19 #endif
20
21 #ifdef __FreeBSD__
22 # include <netinet/in.h>
23 #endif
24
25 #ifdef GIT_SSL
26 # include <openssl/ssl.h>
27 # include <openssl/err.h>
28 # include <openssl/x509v3.h>
29 #endif
30
31 #include <ctype.h>
32 #include "git2/errors.h"
33
34 #include "common.h"
35 #include "netops.h"
36 #include "posix.h"
37 #include "buffer.h"
38
39 #ifdef GIT_WIN32
40 static void net_set_error(const char *str)
41 {
42 int size, error = WSAGetLastError();
43 LPSTR err_str = NULL;
44
45 size = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
46 0, error, 0, (LPSTR)&err_str, 0, 0);
47
48 GIT_UNUSED(size);
49
50 giterr_set(GITERR_NET, "%s: %s", str, err_str);
51 LocalFree(err_str);
52 }
53 #else
54 static void net_set_error(const char *str)
55 {
56 giterr_set(GITERR_NET, "%s: %s", str, strerror(errno));
57 }
58 #endif
59
60 #ifdef GIT_SSL
61 static int ssl_set_error(gitno_ssl *ssl, int error)
62 {
63 int err;
64 unsigned long e;
65
66 err = SSL_get_error(ssl->ssl, error);
67
68 assert(err != SSL_ERROR_WANT_READ);
69 assert(err != SSL_ERROR_WANT_WRITE);
70
71 switch (err) {
72 case SSL_ERROR_WANT_CONNECT:
73 case SSL_ERROR_WANT_ACCEPT:
74 giterr_set(GITERR_NET, "SSL error: connection failure\n");
75 break;
76 case SSL_ERROR_WANT_X509_LOOKUP:
77 giterr_set(GITERR_NET, "SSL error: x509 error\n");
78 break;
79 case SSL_ERROR_SYSCALL:
80 e = ERR_get_error();
81 if (e > 0) {
82 giterr_set(GITERR_NET, "SSL error: %s",
83 ERR_error_string(e, NULL));
84 break;
85 } else if (error < 0) {
86 giterr_set(GITERR_OS, "SSL error: syscall failure");
87 break;
88 }
89 giterr_set(GITERR_NET, "SSL error: received early EOF");
90 break;
91 case SSL_ERROR_SSL:
92 e = ERR_get_error();
93 giterr_set(GITERR_NET, "SSL error: %s",
94 ERR_error_string(e, NULL));
95 break;
96 case SSL_ERROR_NONE:
97 case SSL_ERROR_ZERO_RETURN:
98 default:
99 giterr_set(GITERR_NET, "SSL error: unknown error");
100 break;
101 }
102 return -1;
103 }
104 #endif
105
106 int gitno_recv(gitno_buffer *buf)
107 {
108 return buf->recv(buf);
109 }
110
111 #ifdef GIT_SSL
112 static int gitno__recv_ssl(gitno_buffer *buf)
113 {
114 int ret;
115
116 do {
117 ret = SSL_read(buf->socket->ssl.ssl, buf->data + buf->offset, buf->len - buf->offset);
118 } while (SSL_get_error(buf->socket->ssl.ssl, ret) == SSL_ERROR_WANT_READ);
119
120 if (ret < 0) {
121 net_set_error("Error receiving socket data");
122 return -1;
123 }
124
125 buf->offset += ret;
126 return ret;
127 }
128 #endif
129
130 static int gitno__recv(gitno_buffer *buf)
131 {
132 int ret;
133
134 ret = p_recv(buf->socket->socket, buf->data + buf->offset, buf->len - buf->offset, 0);
135 if (ret < 0) {
136 net_set_error("Error receiving socket data");
137 return -1;
138 }
139
140 buf->offset += ret;
141 return ret;
142 }
143
144 void gitno_buffer_setup_callback(
145 gitno_socket *socket,
146 gitno_buffer *buf,
147 char *data,
148 size_t len,
149 int (*recv)(gitno_buffer *buf), void *cb_data)
150 {
151 memset(data, 0x0, len);
152 buf->data = data;
153 buf->len = len;
154 buf->offset = 0;
155 buf->socket = socket;
156 buf->recv = recv;
157 buf->cb_data = cb_data;
158 }
159
160 void gitno_buffer_setup(gitno_socket *socket, gitno_buffer *buf, char *data, size_t len)
161 {
162 #ifdef GIT_SSL
163 if (socket->ssl.ctx) {
164 gitno_buffer_setup_callback(socket, buf, data, len, gitno__recv_ssl, NULL);
165 return;
166 }
167 #endif
168
169 gitno_buffer_setup_callback(socket, buf, data, len, gitno__recv, NULL);
170 }
171
172 /* Consume up to ptr and move the rest of the buffer to the beginning */
173 void gitno_consume(gitno_buffer *buf, const char *ptr)
174 {
175 size_t consumed;
176
177 assert(ptr - buf->data >= 0);
178 assert(ptr - buf->data <= (int) buf->len);
179
180 consumed = ptr - buf->data;
181
182 memmove(buf->data, ptr, buf->offset - consumed);
183 memset(buf->data + buf->offset, 0x0, buf->len - buf->offset);
184 buf->offset -= consumed;
185 }
186
187 /* Consume const bytes and move the rest of the buffer to the beginning */
188 void gitno_consume_n(gitno_buffer *buf, size_t cons)
189 {
190 memmove(buf->data, buf->data + cons, buf->len - buf->offset);
191 memset(buf->data + cons, 0x0, buf->len - buf->offset);
192 buf->offset -= cons;
193 }
194
195 #ifdef GIT_SSL
196
197 static int gitno_ssl_teardown(gitno_ssl *ssl)
198 {
199 int ret;
200
201 ret = SSL_shutdown(ssl->ssl);
202 if (ret < 0)
203 ret = ssl_set_error(ssl, ret);
204 else
205 ret = 0;
206
207 SSL_free(ssl->ssl);
208 SSL_CTX_free(ssl->ctx);
209 return ret;
210 }
211
212 /* Match host names according to RFC 2818 rules */
213 static int 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 match_host(pattern, host++);
237 if (h == '.')
238 return 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 (match_host(name, host) < 0)
257 return -1;
258
259 return 0;
260 }
261
262 static int verify_server_cert(gitno_ssl *ssl, const char *host)
263 {
264 X509 *cert;
265 X509_NAME *peer_name;
266 ASN1_STRING *str;
267 unsigned char *peer_cn = NULL;
268 int matched = -1, type = GEN_DNS;
269 GENERAL_NAMES *alts;
270 struct in6_addr addr6;
271 struct in_addr addr4;
272 void *addr;
273 int i = -1,j;
274
275 if (SSL_get_verify_result(ssl->ssl) != X509_V_OK) {
276 giterr_set(GITERR_SSL, "The SSL certificate is invalid");
277 return -1;
278 }
279
280 /* Try to parse the host as an IP address to see if it is */
281 if (p_inet_pton(AF_INET, host, &addr4)) {
282 type = GEN_IPADD;
283 addr = &addr4;
284 } else {
285 if(p_inet_pton(AF_INET6, host, &addr6)) {
286 type = GEN_IPADD;
287 addr = &addr6;
288 }
289 }
290
291
292 cert = SSL_get_peer_certificate(ssl->ssl);
293
294 /* Check the alternative names */
295 alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
296 if (alts) {
297 int num;
298
299 num = sk_GENERAL_NAME_num(alts);
300 for (i = 0; i < num && matched != 1; i++) {
301 const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i);
302 const char *name = (char *) ASN1_STRING_data(gn->d.ia5);
303 size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5);
304
305 /* Skip any names of a type we're not looking for */
306 if (gn->type != type)
307 continue;
308
309 if (type == GEN_DNS) {
310 /* If it contains embedded NULs, don't even try */
311 if (memchr(name, '\0', namelen))
312 continue;
313
314 if (check_host_name(name, host) < 0)
315 matched = 0;
316 else
317 matched = 1;
318 } else if (type == GEN_IPADD) {
319 /* Here name isn't so much a name but a binary representation of the IP */
320 matched = !!memcmp(name, addr, namelen);
321 }
322 }
323 }
324 GENERAL_NAMES_free(alts);
325
326 if (matched == 0)
327 goto cert_fail;
328
329 if (matched == 1)
330 return 0;
331
332 /* If no alternative names are available, check the common name */
333 peer_name = X509_get_subject_name(cert);
334 if (peer_name == NULL)
335 goto on_error;
336
337 if (peer_name) {
338 /* Get the index of the last CN entry */
339 while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0)
340 i = j;
341 }
342
343 if (i < 0)
344 goto on_error;
345
346 str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i));
347 if (str == NULL)
348 goto on_error;
349
350 /* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */
351 if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) {
352 int size = ASN1_STRING_length(str);
353
354 if (size > 0) {
355 peer_cn = OPENSSL_malloc(size + 1);
356 GITERR_CHECK_ALLOC(peer_cn);
357 memcpy(peer_cn, ASN1_STRING_data(str), size);
358 peer_cn[size] = '\0';
359 }
360 } else {
361 int size = ASN1_STRING_to_UTF8(&peer_cn, str);
362 GITERR_CHECK_ALLOC(peer_cn);
363 if (memchr(peer_cn, '\0', size))
364 goto cert_fail;
365 }
366
367 if (check_host_name((char *)peer_cn, host) < 0)
368 goto cert_fail;
369
370 OPENSSL_free(peer_cn);
371
372 return 0;
373
374 on_error:
375 OPENSSL_free(peer_cn);
376 return ssl_set_error(ssl, 0);
377
378 cert_fail:
379 OPENSSL_free(peer_cn);
380 giterr_set(GITERR_SSL, "Certificate host name check failed");
381 return -1;
382 }
383
384 static int ssl_setup(gitno_socket *socket, const char *host, int flags)
385 {
386 int ret;
387
388 SSL_library_init();
389 SSL_load_error_strings();
390 socket->ssl.ctx = SSL_CTX_new(SSLv23_method());
391 if (socket->ssl.ctx == NULL)
392 return ssl_set_error(&socket->ssl, 0);
393
394 SSL_CTX_set_mode(socket->ssl.ctx, SSL_MODE_AUTO_RETRY);
395 SSL_CTX_set_verify(socket->ssl.ctx, SSL_VERIFY_NONE, NULL);
396 if (!SSL_CTX_set_default_verify_paths(socket->ssl.ctx))
397 return ssl_set_error(&socket->ssl, 0);
398
399 socket->ssl.ssl = SSL_new(socket->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 if (GITNO_CONNECT_SSL_NO_CHECK_CERT & flags)
410 return 0;
411
412 return verify_server_cert(&socket->ssl, host);
413 }
414 #endif
415
416 static int gitno__close(GIT_SOCKET s)
417 {
418 #ifdef GIT_WIN32
419 if (SOCKET_ERROR == closesocket(s))
420 return -1;
421
422 if (0 != WSACleanup()) {
423 giterr_set(GITERR_OS, "Winsock cleanup failed");
424 return -1;
425 }
426
427 return 0;
428 #else
429 return close(s);
430 #endif
431 }
432
433 int gitno_connect(gitno_socket *s_out, const char *host, const char *port, int flags)
434 {
435 struct addrinfo *info = NULL, *p;
436 struct addrinfo hints;
437 GIT_SOCKET s = INVALID_SOCKET;
438 int ret;
439
440 #ifdef GIT_WIN32
441 /* on win32, the WSA context needs to be initialized
442 * before any socket calls can be performed */
443 WSADATA wsd;
444
445 if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
446 giterr_set(GITERR_OS, "Winsock init failed");
447 return -1;
448 }
449
450 if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) {
451 WSACleanup();
452 giterr_set(GITERR_OS, "Winsock init failed");
453 return -1;
454 }
455 #endif
456
457 /* Zero the socket structure provided */
458 memset(s_out, 0x0, sizeof(gitno_socket));
459
460 memset(&hints, 0x0, sizeof(struct addrinfo));
461 hints.ai_socktype = SOCK_STREAM;
462 hints.ai_family = AF_UNSPEC;
463
464 if ((ret = p_getaddrinfo(host, port, &hints, &info)) < 0) {
465 giterr_set(GITERR_NET,
466 "Failed to resolve address for %s: %s", host, p_gai_strerror(ret));
467 return -1;
468 }
469
470 for (p = info; p != NULL; p = p->ai_next) {
471 s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
472
473 if (s == INVALID_SOCKET) {
474 net_set_error("error creating socket");
475 break;
476 }
477
478 if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
479 break;
480
481 /* If we can't connect, try the next one */
482 gitno__close(s);
483 s = INVALID_SOCKET;
484 }
485
486 /* Oops, we couldn't connect to any address */
487 if (s == INVALID_SOCKET && p == NULL) {
488 giterr_set(GITERR_OS, "Failed to connect to %s", host);
489 return -1;
490 }
491
492 s_out->socket = s;
493 p_freeaddrinfo(info);
494
495 #ifdef GIT_SSL
496 if ((flags & GITNO_CONNECT_SSL) && ssl_setup(s_out, host, flags) < 0)
497 return -1;
498 #else
499 /* SSL is not supported */
500 if (flags & GITNO_CONNECT_SSL) {
501 giterr_set(GITERR_OS, "SSL is not supported by this copy of libgit2.");
502 return -1;
503 }
504 #endif
505
506 return 0;
507 }
508
509 #ifdef GIT_SSL
510 static int gitno_send_ssl(gitno_ssl *ssl, const char *msg, size_t len, int flags)
511 {
512 int ret;
513 size_t off = 0;
514
515 GIT_UNUSED(flags);
516
517 while (off < len) {
518 ret = SSL_write(ssl->ssl, msg + off, len - off);
519 if (ret <= 0 && ret != SSL_ERROR_WANT_WRITE)
520 return ssl_set_error(ssl, ret);
521
522 off += ret;
523 }
524
525 return off;
526 }
527 #endif
528
529 int gitno_send(gitno_socket *socket, const char *msg, size_t len, int flags)
530 {
531 int ret;
532 size_t off = 0;
533
534 #ifdef GIT_SSL
535 if (socket->ssl.ctx)
536 return gitno_send_ssl(&socket->ssl, msg, len, flags);
537 #endif
538
539 while (off < len) {
540 errno = 0;
541 ret = p_send(socket->socket, msg + off, len - off, flags);
542 if (ret < 0) {
543 net_set_error("Error sending data");
544 return -1;
545 }
546
547 off += ret;
548 }
549
550 return (int)off;
551 }
552
553 int gitno_close(gitno_socket *s)
554 {
555 #ifdef GIT_SSL
556 if (s->ssl.ctx &&
557 gitno_ssl_teardown(&s->ssl) < 0)
558 return -1;
559 #endif
560
561 return gitno__close(s->socket);
562 }
563
564 int gitno_select_in(gitno_buffer *buf, long int sec, long int usec)
565 {
566 fd_set fds;
567 struct timeval tv;
568
569 tv.tv_sec = sec;
570 tv.tv_usec = usec;
571
572 FD_ZERO(&fds);
573 FD_SET(buf->socket->socket, &fds);
574
575 /* The select(2) interface is silly */
576 return select((int)buf->socket->socket + 1, &fds, NULL, NULL, &tv);
577 }
578
579 int gitno_extract_host_and_port(char **host, char **port, const char *url, const char *default_port)
580 {
581 char *colon, *slash, *delim;
582
583 colon = strchr(url, ':');
584 slash = strchr(url, '/');
585
586 if (slash == NULL) {
587 giterr_set(GITERR_NET, "Malformed URL: missing /");
588 return -1;
589 }
590
591 if (colon == NULL) {
592 *port = git__strdup(default_port);
593 } else {
594 *port = git__strndup(colon + 1, slash - colon - 1);
595 }
596 GITERR_CHECK_ALLOC(*port);
597
598 delim = colon == NULL ? slash : colon;
599 *host = git__strndup(url, delim - url);
600 GITERR_CHECK_ALLOC(*host);
601
602 return 0;
603 }