netops.c 13.2 KB
Newer Older
1
/*
Edward Thomson committed
2
 * Copyright (C) the libgit2 contributors. All rights reserved.
3
 *
Vicent Marti committed
4 5
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
6
 */
7
#ifndef _WIN32
8 9 10
#	include <sys/types.h>
#	include <sys/socket.h>
#	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 <ws2tcpip.h>
17
#	ifdef _MSC_VER
18
#		pragma comment(lib, "ws2_32")
19
#	endif
20
#endif
21

Fraser Tweedale committed
22 23 24 25
#ifdef __FreeBSD__
#	include <netinet/in.h>
#endif

26
#ifdef GIT_SSL
27
# include <openssl/ssl.h>
28
# include <openssl/err.h>
29
# include <openssl/x509v3.h>
30
#endif
31

32
#include <ctype.h>
33 34 35 36
#include "git2/errors.h"

#include "common.h"
#include "netops.h"
37
#include "posix.h"
38 39 40 41 42
#include "buffer.h"

#ifdef GIT_WIN32
static void net_set_error(const char *str)
{
43
	int error = WSAGetLastError();
44
	char * win32_error = git_win32_get_error_message(error);
45

46 47 48 49 50
	if (win32_error) {
		giterr_set(GITERR_NET, "%s: %s", str, win32_error);
		git__free(win32_error);
	} else {
		giterr_set(GITERR_NET, str);
51
	}
52 53 54 55 56 57 58
}
#else
static void net_set_error(const char *str)
{
	giterr_set(GITERR_NET, "%s: %s", str, strerror(errno));
}
#endif
59

60
#ifdef GIT_SSL
61 62 63
static int ssl_set_error(gitno_ssl *ssl, int error)
{
	int err;
64 65
	unsigned long e;

66
	err = SSL_get_error(ssl->ssl, error);
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101

	assert(err != SSL_ERROR_WANT_READ);
	assert(err != SSL_ERROR_WANT_WRITE);

	switch (err) {
	case SSL_ERROR_WANT_CONNECT:
	case SSL_ERROR_WANT_ACCEPT:
		giterr_set(GITERR_NET, "SSL error: connection failure\n");
		break;
	case SSL_ERROR_WANT_X509_LOOKUP:
		giterr_set(GITERR_NET, "SSL error: x509 error\n");
		break;
	case SSL_ERROR_SYSCALL:
		e = ERR_get_error();
		if (e > 0) {
			giterr_set(GITERR_NET, "SSL error: %s",
					ERR_error_string(e, NULL));
			break;
		} else if (error < 0) {
			giterr_set(GITERR_OS, "SSL error: syscall failure");
			break;
		}
		giterr_set(GITERR_NET, "SSL error: received early EOF");
		break;
	case SSL_ERROR_SSL:
		e = ERR_get_error();
		giterr_set(GITERR_NET, "SSL error: %s",
				ERR_error_string(e, NULL));
		break;
	case SSL_ERROR_NONE:
	case SSL_ERROR_ZERO_RETURN:
	default:
		giterr_set(GITERR_NET, "SSL error: unknown error");
		break;
	}
102 103
	return -1;
}
104 105
#endif

106 107 108
int gitno_recv(gitno_buffer *buf)
{
	return buf->recv(buf);
109 110
}

111
#ifdef GIT_SSL
112
static int gitno__recv_ssl(gitno_buffer *buf)
113 114 115 116
{
	int ret;

	do {
117 118
		ret = SSL_read(buf->socket->ssl.ssl, buf->data + buf->offset, buf->len - buf->offset);
	} while (SSL_get_error(buf->socket->ssl.ssl, ret) == SSL_ERROR_WANT_READ);
119

120 121 122 123
	if (ret < 0) {
		net_set_error("Error receiving socket data");
		return -1;
	}
124

125
	buf->offset += ret;
126
	return ret;
127
}
128
#endif
129

130
static int gitno__recv(gitno_buffer *buf)
131 132 133
{
	int ret;

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 138
		return -1;
	}
139 140 141 142 143

	buf->offset += ret;
	return ret;
}

144
void gitno_buffer_setup_callback(
145
	gitno_socket *socket,
146 147 148 149
	gitno_buffer *buf,
	char *data,
	size_t len,
	int (*recv)(gitno_buffer *buf), void *cb_data)
150 151 152 153 154
{
	memset(data, 0x0, len);
	buf->data = data;
	buf->len = len;
	buf->offset = 0;
155
	buf->socket = socket;
156 157 158 159
	buf->recv = recv;
	buf->cb_data = cb_data;
}

160
void gitno_buffer_setup(gitno_socket *socket, gitno_buffer *buf, char *data, size_t len)
161 162
{
#ifdef GIT_SSL
163 164 165 166
	if (socket->ssl.ctx) {
		gitno_buffer_setup_callback(socket, buf, data, len, gitno__recv_ssl, NULL);
		return;
	}
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 179
	assert(ptr - buf->data <= (int) buf->len);

180
	consumed = ptr - buf->data;
181

182 183 184
	memmove(buf->data, ptr, buf->offset - consumed);
	memset(buf->data + buf->offset, 0x0, buf->len - buf->offset);
	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 191 192 193 194
{
	memmove(buf->data, buf->data + cons, buf->len - buf->offset);
	memset(buf->data + cons, 0x0, buf->len - buf->offset);
	buf->offset -= cons;
}

195
#ifdef GIT_SSL
196

197 198 199
static int gitno_ssl_teardown(gitno_ssl *ssl)
{
	int ret;
200

201
	ret = SSL_shutdown(ssl->ssl);
202
	if (ret < 0)
203 204 205
		ret = ssl_set_error(ssl, ret);
	else
		ret = 0;
206

207 208
	SSL_free(ssl->ssl);
	SSL_CTX_free(ssl->ctx);
209
	return ret;
210 211
}

212
/* Match host names according to RFC 2818 rules */
213 214 215
static int match_host(const char *pattern, const char *host)
{
	for (;;) {
216
		char c = tolower(*pattern++);
217 218 219 220 221 222 223 224 225 226

		if (c == '\0')
			return *host ? -1 : 0;

		if (c == '*') {
			c = *pattern;
			/* '*' at the end matches everything left */
			if (c == '\0')
				return 0;

227 228 229 230 231 232 233 234 235 236 237 238 239
	/*
	 * We've found a pattern, so move towards the next matching
	 * char. The '.' is handled specially because wildcards aren't
	 * allowed to cross subdomains.
	 */

			while(*host) {
				char h = tolower(*host);
				if (c == h)
					return match_host(pattern, host++);
				if (h == '.')
					return match_host(pattern, host);
				host++;
240
			}
241
			return -1;
242 243
		}

244
		if (c != tolower(*host++))
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
			return -1;
	}

	return -1;
}

static int check_host_name(const char *name, const char *host)
{
	if (!strcasecmp(name, host))
		return 0;

	if (match_host(name, host) < 0)
		return -1;

	return 0;
}

262
static int verify_server_cert(gitno_ssl *ssl, const char *host)
263 264 265
{
	X509 *cert;
	X509_NAME *peer_name;
266 267
	ASN1_STRING *str;
	unsigned char *peer_cn = NULL;
268
	int matched = -1, type = GEN_DNS;
269
	GENERAL_NAMES *alts;
270 271 272
	struct in6_addr addr6;
	struct in_addr addr4;
	void *addr;
273 274
	int i = -1,j;

275
	if (SSL_get_verify_result(ssl->ssl) != X509_V_OK) {
276 277 278
		giterr_set(GITERR_SSL, "The SSL certificate is invalid");
		return -1;
	}
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 283 284
		type = GEN_IPADD;
		addr = &addr4;
	} else {
285
		if(p_inet_pton(AF_INET6, host, &addr6)) {
286 287 288 289 290
			type = GEN_IPADD;
			addr = &addr6;
		}
	}

291

292
	cert = SSL_get_peer_certificate(ssl->ssl);
293 294 295 296

	/* Check the alternative names */
	alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
	if (alts) {
297
		int num;
298 299 300 301 302 303 304

		num = sk_GENERAL_NAME_num(alts);
		for (i = 0; i < num && matched != 1; i++) {
			const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i);
			const char *name = (char *) ASN1_STRING_data(gn->d.ia5);
			size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5);

305 306
			/* Skip any names of a type we're not looking for */
			if (gn->type != type)
307 308
				continue;

309 310
			if (type == GEN_DNS) {
				/* If it contains embedded NULs, don't even try */
311
				if (memchr(name, '\0', namelen))
312 313 314 315 316 317 318 319 320 321
					continue;

				if (check_host_name(name, host) < 0)
					matched = 0;
				else
					matched = 1;
			} else if (type == GEN_IPADD) {
				/* Here name isn't so much a name but a binary representation of the IP */
				matched = !!memcmp(name, addr, namelen);
			}
322 323 324 325
		}
	}
	GENERAL_NAMES_free(alts);

326
	if (matched == 0)
327
		goto cert_fail;
328

329 330 331 332 333
	if (matched == 1)
		return 0;

	/* If no alternative names are available, check the common name */
	peer_name = X509_get_subject_name(cert);
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
	if (peer_name == NULL)
		goto on_error;

	if (peer_name) {
		/* Get the index of the last CN entry */
		while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0)
			i = j;
	}

	if (i < 0)
		goto on_error;

	str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i));
	if (str == NULL)
		goto on_error;

	/* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */
	if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) {
		int size = ASN1_STRING_length(str);

		if (size > 0) {
			peer_cn = OPENSSL_malloc(size + 1);
			GITERR_CHECK_ALLOC(peer_cn);
			memcpy(peer_cn, ASN1_STRING_data(str), size);
			peer_cn[size] = '\0';
		}
	} else {
		int size = ASN1_STRING_to_UTF8(&peer_cn, str);
		GITERR_CHECK_ALLOC(peer_cn);
		if (memchr(peer_cn, '\0', size))
			goto cert_fail;
365 366
	}

367 368 369 370 371
	if (check_host_name((char *)peer_cn, host) < 0)
		goto cert_fail;

	OPENSSL_free(peer_cn);

372
	return 0;
373 374 375

on_error:
	OPENSSL_free(peer_cn);
376
	return ssl_set_error(ssl, 0);
377 378 379 380 381

cert_fail:
	OPENSSL_free(peer_cn);
	giterr_set(GITERR_SSL, "Certificate host name check failed");
	return -1;
382 383
}

384
static int ssl_setup(gitno_socket *socket, const char *host, int flags)
385
{
386 387 388 389
	int ret;

	SSL_library_init();
	SSL_load_error_strings();
390 391 392
	socket->ssl.ctx = SSL_CTX_new(SSLv23_method());
	if (socket->ssl.ctx == NULL)
		return ssl_set_error(&socket->ssl, 0);
393

394 395 396 397
	SSL_CTX_set_mode(socket->ssl.ctx, SSL_MODE_AUTO_RETRY);
	SSL_CTX_set_verify(socket->ssl.ctx, SSL_VERIFY_NONE, NULL);
	if (!SSL_CTX_set_default_verify_paths(socket->ssl.ctx))
		return ssl_set_error(&socket->ssl, 0);
398

399 400 401
	socket->ssl.ssl = SSL_new(socket->ssl.ctx);
	if (socket->ssl.ssl == NULL)
		return ssl_set_error(&socket->ssl, 0);
402

403 404
	if((ret = SSL_set_fd(socket->ssl.ssl, socket->socket)) == 0)
		return ssl_set_error(&socket->ssl, ret);
405

406 407
	if ((ret = SSL_connect(socket->ssl.ssl)) <= 0)
		return ssl_set_error(&socket->ssl, ret);
408

409 410
	if (GITNO_CONNECT_SSL_NO_CHECK_CERT & flags)
		return 0;
411

412
	return verify_server_cert(&socket->ssl, host);
413
}
414 415 416
#endif

static int gitno__close(GIT_SOCKET s)
417
{
418 419 420 421 422 423 424 425 426
#ifdef GIT_WIN32
	if (SOCKET_ERROR == closesocket(s))
		return -1;

	if (0 != WSACleanup()) {
		giterr_set(GITERR_OS, "Winsock cleanup failed");
		return -1;
	}

427
	return 0;
428 429
#else
	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 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
	int ret;

#ifdef GIT_WIN32
	/* on win32, the WSA context needs to be initialized
	 * before any socket calls can be performed */
	WSADATA wsd;

	if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
		giterr_set(GITERR_OS, "Winsock init failed");
		return -1;
	}

	if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) {
		WSACleanup();
		giterr_set(GITERR_OS, "Winsock init failed");
		return -1;
	}
#endif

	/* Zero the socket structure provided */
	memset(s_out, 0x0, sizeof(gitno_socket));
459 460 461

	memset(&hints, 0x0, sizeof(struct addrinfo));
	hints.ai_socktype = SOCK_STREAM;
462
	hints.ai_family = AF_UNSPEC;
463

Vicent Marti committed
464 465 466
	if ((ret = p_getaddrinfo(host, port, &hints, &info)) < 0) {
		giterr_set(GITERR_NET,
			"Failed to resolve address for %s: %s", host, p_gai_strerror(ret));
467
		return -1;
468 469 470 471
	}

	for (p = info; p != NULL; p = p->ai_next) {
		s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
472

473
		if (s == INVALID_SOCKET) {
474 475
			net_set_error("error creating socket");
			break;
476 477
		}

478 479
		if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
			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);
Philip Kelley committed
489
		p_freeaddrinfo(info);
490 491
		return -1;
	}
492

493
	s_out->socket = s;
Vicent Marti committed
494
	p_freeaddrinfo(info);
495

496 497
#ifdef GIT_SSL
	if ((flags & GITNO_CONNECT_SSL) && ssl_setup(s_out, host, flags) < 0)
498
		return -1;
499 500 501 502 503 504 505
#else
	/* SSL is not supported */
	if (flags & GITNO_CONNECT_SSL) {
		giterr_set(GITERR_OS, "SSL is not supported by this copy of libgit2.");
		return -1;
	}
#endif
506

507
	return 0;
508
}
509

510
#ifdef GIT_SSL
511
static int gitno_send_ssl(gitno_ssl *ssl, const char *msg, size_t len, int flags)
512
{
513 514
	int ret;
	size_t off = 0;
515

516 517
	GIT_UNUSED(flags);

518
	while (off < len) {
519
		ret = SSL_write(ssl->ssl, msg + off, len - off);
520
		if (ret <= 0 && ret != SSL_ERROR_WANT_WRITE)
521 522 523
			return ssl_set_error(ssl, ret);

		off += ret;
524
	}	
525

526 527
	return off;
}
528 529
#endif

530
int gitno_send(gitno_socket *socket, const char *msg, size_t len, int flags)
531 532 533 534
{
	int ret;
	size_t off = 0;

535
#ifdef GIT_SSL
536 537
	if (socket->ssl.ctx)
		return gitno_send_ssl(&socket->ssl, msg, len, flags);
538 539 540 541
#endif

	while (off < len) {
		errno = 0;
542
		ret = p_send(socket->socket, msg + off, len - off, flags);
543
		if (ret < 0) {
544
			net_set_error("Error sending data");
545 546
			return -1;
		}
547 548 549 550

		off += ret;
	}

551
	return (int)off;
552
}
553

554
int gitno_close(gitno_socket *s)
555
{
556 557 558 559
#ifdef GIT_SSL
	if (s->ssl.ctx &&
		gitno_ssl_teardown(&s->ssl) < 0)
		return -1;
560 561
#endif

562 563 564
	return gitno__close(s->socket);
}

565 566 567 568 569 570 571 572 573
int gitno_select_in(gitno_buffer *buf, long int sec, long int usec)
{
	fd_set fds;
	struct timeval tv;

	tv.tv_sec = sec;
	tv.tv_usec = usec;

	FD_ZERO(&fds);
574
	FD_SET(buf->socket->socket, &fds);
575 576

	/* The select(2) interface is silly */
577
	return select((int)buf->socket->socket + 1, &fds, NULL, NULL, &tv);
578
}
579

580 581 582 583 584 585 586
int gitno_extract_url_parts(
		char **host,
		char **port,
		char **username,
		char **password,
		const char *url,
		const char *default_port)
587
{
588
	char *colon, *slash, *at, *end;
589
	const char *start;
590

591 592 593 594
	/*
	 *
	 * ==> [user[:pass]@]hostname.tld[:port]/resource
	 */
595 596 597

	colon = strchr(url, ':');
	slash = strchr(url, '/');
598
	at = strchr(url, '@');
599

600
	if (slash == NULL) {
601 602
		giterr_set(GITERR_NET, "Malformed URL: missing /");
		return -1;
603
	}
604

605 606 607
	start = url;
	if (at && at < slash) {
		start = at+1;
Ben Straub committed
608
		*username = git__substrdup(url, at - url);
609 610 611 612
	}

	if (colon && colon < at) {
		git__free(*username);
Ben Straub committed
613 614
		*username = git__substrdup(url, colon-url);
		*password = git__substrdup(colon+1, at-colon-1);
615 616 617
		colon = strchr(at, ':');
	}

618 619 620
	if (colon == NULL) {
		*port = git__strdup(default_port);
	} else {
Ben Straub committed
621
		*port = git__substrdup(colon + 1, slash - colon - 1);
622
	}
623
	GITERR_CHECK_ALLOC(*port);
624

625
	end = colon == NULL ? slash : colon;
626

Ben Straub committed
627
	*host = git__substrdup(start, end - start);
628
	GITERR_CHECK_ALLOC(*host);
629

630
	return 0;
631
}