xref: /libevent-2.1.12/sample/https-client.c (revision 95acdaa3)
1be46c99bSCatalin Patulea /*
2be46c99bSCatalin Patulea   This is an example of how to hook up evhttp with bufferevent_ssl
3be46c99bSCatalin Patulea 
4be46c99bSCatalin Patulea   It just GETs an https URL given on the command-line and prints the response
5be46c99bSCatalin Patulea   body to stdout.
6be46c99bSCatalin Patulea 
7be46c99bSCatalin Patulea   Actually, it also accepts plain http URLs to make it easy to compare http vs
8be46c99bSCatalin Patulea   https code paths.
9be46c99bSCatalin Patulea 
10be46c99bSCatalin Patulea   Loosely based on le-proxy.c.
11be46c99bSCatalin Patulea  */
12be46c99bSCatalin Patulea 
13be46c99bSCatalin Patulea #include <stdio.h>
14be46c99bSCatalin Patulea #include <assert.h>
15be46c99bSCatalin Patulea #include <stdlib.h>
16be46c99bSCatalin Patulea #include <string.h>
17be46c99bSCatalin Patulea #include <errno.h>
18be46c99bSCatalin Patulea 
19be46c99bSCatalin Patulea #ifdef WIN32
20be46c99bSCatalin Patulea #include <winsock2.h>
21be46c99bSCatalin Patulea #include <ws2tcpip.h>
22be46c99bSCatalin Patulea #else
23be46c99bSCatalin Patulea #include <sys/socket.h>
24be46c99bSCatalin Patulea #include <netinet/in.h>
25be46c99bSCatalin Patulea #endif
26be46c99bSCatalin Patulea 
27be46c99bSCatalin Patulea #include <event2/bufferevent_ssl.h>
28be46c99bSCatalin Patulea #include <event2/bufferevent.h>
29be46c99bSCatalin Patulea #include <event2/buffer.h>
30be46c99bSCatalin Patulea #include <event2/listener.h>
31be46c99bSCatalin Patulea #include <event2/util.h>
32be46c99bSCatalin Patulea #include <event2/http.h>
33be46c99bSCatalin Patulea 
34be46c99bSCatalin Patulea #include <openssl/ssl.h>
35be46c99bSCatalin Patulea #include <openssl/err.h>
36be46c99bSCatalin Patulea #include <openssl/rand.h>
37be46c99bSCatalin Patulea 
3864d9f161SPatrick Pelletier #include "openssl_hostname_validation.h"
3964d9f161SPatrick Pelletier 
40be46c99bSCatalin Patulea static struct event_base *base;
41be46c99bSCatalin Patulea 
42be46c99bSCatalin Patulea static void
43be46c99bSCatalin Patulea http_request_done(struct evhttp_request *req, void *ctx)
44be46c99bSCatalin Patulea {
45be46c99bSCatalin Patulea 	char buffer[256];
46be46c99bSCatalin Patulea 	int nread;
47be46c99bSCatalin Patulea 
48be46c99bSCatalin Patulea 	if (req == NULL) {
495754d96aSPatrick Pelletier 		/* If req is NULL, it means an error occurred, but
505754d96aSPatrick Pelletier 		 * sadly we are mostly left guessing what the error
515754d96aSPatrick Pelletier 		 * might have been.  We'll do our best... */
525754d96aSPatrick Pelletier 		struct bufferevent *bev = (struct bufferevent *) ctx;
535754d96aSPatrick Pelletier 		unsigned long oslerr;
545754d96aSPatrick Pelletier 		int printed_err = 0;
555754d96aSPatrick Pelletier 		int errcode = EVUTIL_SOCKET_ERROR();
56be46c99bSCatalin Patulea 		fprintf(stderr, "some request failed - no idea which one though!\n");
575754d96aSPatrick Pelletier 		/* Print out the OpenSSL error queue that libevent
585754d96aSPatrick Pelletier 		 * squirreled away for us, if any. */
595754d96aSPatrick Pelletier 		while ((oslerr = bufferevent_get_openssl_error(bev))) {
605754d96aSPatrick Pelletier 			ERR_error_string_n(oslerr, buffer, sizeof(buffer));
615754d96aSPatrick Pelletier 			fprintf(stderr, "%s\n", buffer);
625754d96aSPatrick Pelletier 			printed_err = 1;
635754d96aSPatrick Pelletier 		}
645754d96aSPatrick Pelletier 		/* If the OpenSSL error queue was empty, maybe it was a
655754d96aSPatrick Pelletier 		 * socket error; let's try printing that. */
665754d96aSPatrick Pelletier 		if (! printed_err)
675754d96aSPatrick Pelletier 			fprintf(stderr, "socket error = %s (%d)\n",
685754d96aSPatrick Pelletier 				evutil_socket_error_to_string(errcode),
695754d96aSPatrick Pelletier 				errcode);
70be46c99bSCatalin Patulea 		return;
71be46c99bSCatalin Patulea 	}
72be46c99bSCatalin Patulea 
73be46c99bSCatalin Patulea 	fprintf(stderr, "Response line: %d %s\n",
748a90a850SNick Mathewson 	    evhttp_request_get_response_code(req),
758a90a850SNick Mathewson 	    evhttp_request_get_response_code_line(req));
76be46c99bSCatalin Patulea 
77*95acdaa3SNick Mathewson 	while ((nread = evbuffer_remove(evhttp_request_get_input_buffer(req),
78*95acdaa3SNick Mathewson 		    buffer, sizeof(buffer)))
79be46c99bSCatalin Patulea 	       > 0) {
8042d7441aSPatrick Pelletier 		/* These are just arbitrary chunks of 256 bytes.
8142d7441aSPatrick Pelletier 		 * They are not lines, so we can't treat them as such. */
82be46c99bSCatalin Patulea 		fwrite(buffer, nread, 1, stdout);
83be46c99bSCatalin Patulea 	}
84be46c99bSCatalin Patulea }
85be46c99bSCatalin Patulea 
86be46c99bSCatalin Patulea static void
87be46c99bSCatalin Patulea syntax(void)
88be46c99bSCatalin Patulea {
89be46c99bSCatalin Patulea 	fputs("Syntax:\n", stderr);
90be46c99bSCatalin Patulea 	fputs("   https-client <https-url>\n", stderr);
91be46c99bSCatalin Patulea 	fputs("Example:\n", stderr);
92be46c99bSCatalin Patulea 	fputs("   https-client https://ip.appspot.com/\n", stderr);
93be46c99bSCatalin Patulea 
94be46c99bSCatalin Patulea 	exit(1);
95be46c99bSCatalin Patulea }
96be46c99bSCatalin Patulea 
97be46c99bSCatalin Patulea static void
98be46c99bSCatalin Patulea die(const char *msg)
99be46c99bSCatalin Patulea {
100be46c99bSCatalin Patulea 	fputs(msg, stderr);
101be46c99bSCatalin Patulea 	exit(1);
102be46c99bSCatalin Patulea }
103be46c99bSCatalin Patulea 
1045754d96aSPatrick Pelletier static void
1055754d96aSPatrick Pelletier die_openssl(const char *func)
1065754d96aSPatrick Pelletier {
1075754d96aSPatrick Pelletier 	fprintf (stderr, "%s failed:\n", func);
1085754d96aSPatrick Pelletier 
1095754d96aSPatrick Pelletier 	/* This is the OpenSSL function that prints the contents of the
1105754d96aSPatrick Pelletier 	 * error stack to the specified file handle. */
1115754d96aSPatrick Pelletier 	ERR_print_errors_fp (stderr);
1125754d96aSPatrick Pelletier 
1135754d96aSPatrick Pelletier 	exit(1);
1145754d96aSPatrick Pelletier }
1155754d96aSPatrick Pelletier 
11664d9f161SPatrick Pelletier /* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */
11764d9f161SPatrick Pelletier static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg)
11864d9f161SPatrick Pelletier {
11964d9f161SPatrick Pelletier 	char cert_str[256];
12064d9f161SPatrick Pelletier 	const char *host = (const char *) arg;
12164d9f161SPatrick Pelletier 	const char *res_str = "X509_verify_cert failed";
12264d9f161SPatrick Pelletier 	HostnameValidationResult res = Error;
12364d9f161SPatrick Pelletier 
12464d9f161SPatrick Pelletier 	/* This is the function that OpenSSL would call if we hadn't called
12564d9f161SPatrick Pelletier 	 * SSL_CTX_set_cert_verify_callback().  Therefore, we are "wrapping"
12664d9f161SPatrick Pelletier 	 * the default functionality, rather than replacing it. */
12764d9f161SPatrick Pelletier 	int ok_so_far = X509_verify_cert(x509_ctx);
12864d9f161SPatrick Pelletier 
12964d9f161SPatrick Pelletier 	X509 *server_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
13064d9f161SPatrick Pelletier 
13164d9f161SPatrick Pelletier 	if (ok_so_far) {
13264d9f161SPatrick Pelletier 		res = validate_hostname(host, server_cert);
13364d9f161SPatrick Pelletier 
13464d9f161SPatrick Pelletier 		switch (res) {
13564d9f161SPatrick Pelletier 		case MatchFound:
13664d9f161SPatrick Pelletier 			res_str = "MatchFound";
13764d9f161SPatrick Pelletier 			break;
13864d9f161SPatrick Pelletier 		case MatchNotFound:
13964d9f161SPatrick Pelletier 			res_str = "MatchNotFound";
14064d9f161SPatrick Pelletier 			break;
14164d9f161SPatrick Pelletier 		case NoSANPresent:
14264d9f161SPatrick Pelletier 			res_str = "NoSANPresent";
14364d9f161SPatrick Pelletier 			break;
14464d9f161SPatrick Pelletier 		case MalformedCertificate:
14564d9f161SPatrick Pelletier 			res_str = "MalformedCertificate";
14664d9f161SPatrick Pelletier 			break;
14764d9f161SPatrick Pelletier 		case Error:
14864d9f161SPatrick Pelletier 			res_str = "Error";
14964d9f161SPatrick Pelletier 			break;
15064d9f161SPatrick Pelletier 		default:
15164d9f161SPatrick Pelletier 			res_str = "WTF!";
15264d9f161SPatrick Pelletier 			break;
15364d9f161SPatrick Pelletier 		}
15464d9f161SPatrick Pelletier 	}
15564d9f161SPatrick Pelletier 
15664d9f161SPatrick Pelletier 	X509_NAME_oneline(X509_get_subject_name (server_cert),
15764d9f161SPatrick Pelletier 			  cert_str, sizeof (cert_str));
15864d9f161SPatrick Pelletier 
15964d9f161SPatrick Pelletier 	if (res == MatchFound) {
16064d9f161SPatrick Pelletier 		printf("https server '%s' has this certificate, "
16164d9f161SPatrick Pelletier 		       "which looks good to me:\n%s\n",
16264d9f161SPatrick Pelletier 		       host, cert_str);
16364d9f161SPatrick Pelletier 		return 1;
16464d9f161SPatrick Pelletier 	} else {
16564d9f161SPatrick Pelletier 		printf("Got '%s' for hostname '%s' and certificate:\n%s\n",
16664d9f161SPatrick Pelletier 		       res_str, host, cert_str);
16764d9f161SPatrick Pelletier 		return 0;
16864d9f161SPatrick Pelletier 	}
16964d9f161SPatrick Pelletier }
17064d9f161SPatrick Pelletier 
171be46c99bSCatalin Patulea int
172be46c99bSCatalin Patulea main(int argc, char **argv)
173be46c99bSCatalin Patulea {
174be46c99bSCatalin Patulea 	int r;
175be46c99bSCatalin Patulea 
176be46c99bSCatalin Patulea 	struct evhttp_uri *http_uri;
177be46c99bSCatalin Patulea 	const char *url, *scheme, *host, *path, *query;
178be46c99bSCatalin Patulea 	char uri[256];
179be46c99bSCatalin Patulea 	int port;
180be46c99bSCatalin Patulea 
181be46c99bSCatalin Patulea 	SSL_CTX *ssl_ctx;
182be46c99bSCatalin Patulea 	SSL *ssl;
183be46c99bSCatalin Patulea 	struct bufferevent *bev;
184be46c99bSCatalin Patulea 	struct evhttp_connection *evcon;
185be46c99bSCatalin Patulea 	struct evhttp_request *req;
1868a90a850SNick Mathewson 	struct evkeyvalq *output_headers;
187be46c99bSCatalin Patulea 
188be46c99bSCatalin Patulea 	if (argc != 2)
189be46c99bSCatalin Patulea 		syntax();
190be46c99bSCatalin Patulea 
191be46c99bSCatalin Patulea 	url = argv[1];
192be46c99bSCatalin Patulea 	http_uri = evhttp_uri_parse(url);
193be46c99bSCatalin Patulea 	if (http_uri == NULL) {
194be46c99bSCatalin Patulea 		die("malformed url");
195be46c99bSCatalin Patulea 	}
196be46c99bSCatalin Patulea 
197be46c99bSCatalin Patulea 	scheme = evhttp_uri_get_scheme(http_uri);
198be46c99bSCatalin Patulea 	if (scheme == NULL || (strcasecmp(scheme, "https") != 0 &&
199be46c99bSCatalin Patulea 	                       strcasecmp(scheme, "http") != 0)) {
200be46c99bSCatalin Patulea 		die("url must be http or https");
201be46c99bSCatalin Patulea 	}
202be46c99bSCatalin Patulea 
203be46c99bSCatalin Patulea 	host = evhttp_uri_get_host(http_uri);
204be46c99bSCatalin Patulea 	if (host == NULL) {
205be46c99bSCatalin Patulea 		die("url must have a host");
206be46c99bSCatalin Patulea 	}
207be46c99bSCatalin Patulea 
208be46c99bSCatalin Patulea 	port = evhttp_uri_get_port(http_uri);
209be46c99bSCatalin Patulea 	if (port == -1) {
210be46c99bSCatalin Patulea 		port = (strcasecmp(scheme, "http") == 0) ? 80 : 443;
211be46c99bSCatalin Patulea 	}
212be46c99bSCatalin Patulea 
213be46c99bSCatalin Patulea 	path = evhttp_uri_get_path(http_uri);
214be46c99bSCatalin Patulea 	if (path == NULL) {
215be46c99bSCatalin Patulea 		path = "/";
216be46c99bSCatalin Patulea 	}
217be46c99bSCatalin Patulea 
218be46c99bSCatalin Patulea 	query = evhttp_uri_get_query(http_uri);
219be46c99bSCatalin Patulea 	if (query == NULL) {
220be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s", path);
221be46c99bSCatalin Patulea 	} else {
222be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query);
223be46c99bSCatalin Patulea 	}
224be46c99bSCatalin Patulea 	uri[sizeof(uri) - 1] = '\0';
225be46c99bSCatalin Patulea 
226be46c99bSCatalin Patulea 	// Initialize OpenSSL
227be46c99bSCatalin Patulea 	SSL_library_init();
228be46c99bSCatalin Patulea 	ERR_load_crypto_strings();
229be46c99bSCatalin Patulea 	SSL_load_error_strings();
230be46c99bSCatalin Patulea 	OpenSSL_add_all_algorithms();
2315754d96aSPatrick Pelletier 
2325754d96aSPatrick Pelletier 	/* This isn't strictly necessary... OpenSSL performs RAND_poll
2335754d96aSPatrick Pelletier 	 * automatically on first use of random number generator. */
234be46c99bSCatalin Patulea 	r = RAND_poll();
235be46c99bSCatalin Patulea 	if (r == 0) {
2365754d96aSPatrick Pelletier 		die_openssl("RAND_poll");
237be46c99bSCatalin Patulea 	}
2385754d96aSPatrick Pelletier 
2395754d96aSPatrick Pelletier 	/* Create a new OpenSSL context */
240be46c99bSCatalin Patulea 	ssl_ctx = SSL_CTX_new(SSLv23_method());
2415754d96aSPatrick Pelletier 	if (!ssl_ctx)
2425754d96aSPatrick Pelletier 		die_openssl("SSL_CTX_new");
243be46c99bSCatalin Patulea 
244aacd674cSPatrick Pelletier 	/* Attempt to use the system's trusted root certificates.
245aacd674cSPatrick Pelletier 	 * (This path is only valid for Debian-based systems.) */
246aacd674cSPatrick Pelletier 	if (1 != SSL_CTX_load_verify_locations(ssl_ctx,
247aacd674cSPatrick Pelletier 					       "/etc/ssl/certs/ca-certificates.crt",
248aacd674cSPatrick Pelletier 					       NULL))
249aacd674cSPatrick Pelletier 		die_openssl("SSL_CTX_load_verify_locations");
25064d9f161SPatrick Pelletier 	/* Ask OpenSSL to verify the server certificate.  Note that this
25164d9f161SPatrick Pelletier 	 * does NOT include verifying that the hostname is correct.
25264d9f161SPatrick Pelletier 	 * So, by itself, this means anyone with any legitimate
25364d9f161SPatrick Pelletier 	 * CA-issued certificate for any website, can impersonate any
25464d9f161SPatrick Pelletier 	 * other website in the world.  This is not good.  See "The
25564d9f161SPatrick Pelletier 	 * Most Dangerous Code in the World" article at
25664d9f161SPatrick Pelletier 	 * https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html
25764d9f161SPatrick Pelletier 	 */
258aacd674cSPatrick Pelletier 	SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
25964d9f161SPatrick Pelletier 	/* This is how we solve the problem mentioned in the previous
26064d9f161SPatrick Pelletier 	 * comment.  We "wrap" OpenSSL's validation routine in our
26164d9f161SPatrick Pelletier 	 * own routine, which also validates the hostname by calling
26264d9f161SPatrick Pelletier 	 * the code provided by iSECPartners.  Note that even though
26364d9f161SPatrick Pelletier 	 * the "Everything You've Always Wanted to Know About
26464d9f161SPatrick Pelletier 	 * Certificate Validation With OpenSSL (But Were Afraid to
26564d9f161SPatrick Pelletier 	 * Ask)" paper from iSECPartners says very explicitly not to
26664d9f161SPatrick Pelletier 	 * call SSL_CTX_set_cert_verify_callback (at the bottom of
26764d9f161SPatrick Pelletier 	 * page 2), what we're doing here is safe because our
26864d9f161SPatrick Pelletier 	 * cert_verify_callback() calls X509_verify_cert(), which is
26964d9f161SPatrick Pelletier 	 * OpenSSL's built-in routine which would have been called if
27064d9f161SPatrick Pelletier 	 * we hadn't set the callback.  Therefore, we're just
27164d9f161SPatrick Pelletier 	 * "wrapping" OpenSSL's routine, not replacing it. */
27264d9f161SPatrick Pelletier 	SSL_CTX_set_cert_verify_callback (ssl_ctx, cert_verify_callback,
27364d9f161SPatrick Pelletier 					  (void *) host);
274aacd674cSPatrick Pelletier 
275be46c99bSCatalin Patulea 	// Create event base
276be46c99bSCatalin Patulea 	base = event_base_new();
277be46c99bSCatalin Patulea 	if (!base) {
278be46c99bSCatalin Patulea 		perror("event_base_new()");
279be46c99bSCatalin Patulea 		return 1;
280be46c99bSCatalin Patulea 	}
281be46c99bSCatalin Patulea 
282be46c99bSCatalin Patulea 	// Create OpenSSL bufferevent and stack evhttp on top of it
283be46c99bSCatalin Patulea 	ssl = SSL_new(ssl_ctx);
284be46c99bSCatalin Patulea 	if (ssl == NULL) {
2855754d96aSPatrick Pelletier 		die_openssl("SSL_new()");
286be46c99bSCatalin Patulea 	}
287be46c99bSCatalin Patulea 
288be46c99bSCatalin Patulea 	if (strcasecmp(scheme, "http") == 0) {
289be46c99bSCatalin Patulea 		bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
290be46c99bSCatalin Patulea 	} else {
291be46c99bSCatalin Patulea 		bev = bufferevent_openssl_socket_new(base, -1, ssl,
292be46c99bSCatalin Patulea 			BUFFEREVENT_SSL_CONNECTING,
293be46c99bSCatalin Patulea 			BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
294be46c99bSCatalin Patulea 	}
295be46c99bSCatalin Patulea 
296be46c99bSCatalin Patulea 	if (bev == NULL) {
297be46c99bSCatalin Patulea 		fprintf(stderr, "bufferevent_openssl_socket_new() failed\n");
298be46c99bSCatalin Patulea 		return 1;
299be46c99bSCatalin Patulea 	}
300be46c99bSCatalin Patulea 
301be46c99bSCatalin Patulea 	bufferevent_openssl_set_allow_dirty_shutdown(bev, 1);
302be46c99bSCatalin Patulea 
303be46c99bSCatalin Patulea 	// For simplicity, we let DNS resolution block. Everything else should be
304be46c99bSCatalin Patulea 	// asynchronous though.
305be46c99bSCatalin Patulea 	evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev,
306be46c99bSCatalin Patulea 		host, port);
307be46c99bSCatalin Patulea 	if (evcon == NULL) {
308be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n");
309be46c99bSCatalin Patulea 		return 1;
310be46c99bSCatalin Patulea 	}
311be46c99bSCatalin Patulea 
312be46c99bSCatalin Patulea 	// Fire off the request
3135754d96aSPatrick Pelletier 	req = evhttp_request_new(http_request_done, bev);
314be46c99bSCatalin Patulea 	if (req == NULL) {
315be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_request_new() failed\n");
316be46c99bSCatalin Patulea 		return 1;
317be46c99bSCatalin Patulea 	}
318be46c99bSCatalin Patulea 
3198a90a850SNick Mathewson 	output_headers = evhttp_request_get_output_headers(req);
3208a90a850SNick Mathewson 	evhttp_add_header(output_headers, "Host", host);
3218a90a850SNick Mathewson 	evhttp_add_header(output_headers, "Connection", "close");
322be46c99bSCatalin Patulea 
323be46c99bSCatalin Patulea 	r = evhttp_make_request(evcon, req, EVHTTP_REQ_GET, uri);
324be46c99bSCatalin Patulea 	if (r != 0) {
325be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_make_request() failed\n");
326be46c99bSCatalin Patulea 		return 1;
327be46c99bSCatalin Patulea 	}
328be46c99bSCatalin Patulea 
329be46c99bSCatalin Patulea 	event_base_dispatch(base);
330be46c99bSCatalin Patulea 
331be46c99bSCatalin Patulea 	evhttp_connection_free(evcon);
332be46c99bSCatalin Patulea 	event_base_free(base);
333be46c99bSCatalin Patulea 
334be46c99bSCatalin Patulea 	return 0;
335be46c99bSCatalin Patulea }
336