xref: /libevent-2.1.12/sample/https-client.c (revision 64d9f161)
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 #include <event2/http_struct.h>
34be46c99bSCatalin Patulea 
35be46c99bSCatalin Patulea #include <openssl/ssl.h>
36be46c99bSCatalin Patulea #include <openssl/err.h>
37be46c99bSCatalin Patulea #include <openssl/rand.h>
38be46c99bSCatalin Patulea 
39*64d9f161SPatrick Pelletier #include "openssl_hostname_validation.h"
40*64d9f161SPatrick Pelletier 
41be46c99bSCatalin Patulea static struct event_base *base;
42be46c99bSCatalin Patulea 
43be46c99bSCatalin Patulea static void
44be46c99bSCatalin Patulea http_request_done(struct evhttp_request *req, void *ctx)
45be46c99bSCatalin Patulea {
46be46c99bSCatalin Patulea 	char buffer[256];
47be46c99bSCatalin Patulea 	int nread;
48be46c99bSCatalin Patulea 
49be46c99bSCatalin Patulea 	if (req == NULL) {
505754d96aSPatrick Pelletier 		/* If req is NULL, it means an error occurred, but
515754d96aSPatrick Pelletier 		 * sadly we are mostly left guessing what the error
525754d96aSPatrick Pelletier 		 * might have been.  We'll do our best... */
535754d96aSPatrick Pelletier 		struct bufferevent *bev = (struct bufferevent *) ctx;
545754d96aSPatrick Pelletier 		unsigned long oslerr;
555754d96aSPatrick Pelletier 		int printed_err = 0;
565754d96aSPatrick Pelletier 		int errcode = EVUTIL_SOCKET_ERROR();
57be46c99bSCatalin Patulea 		fprintf(stderr, "some request failed - no idea which one though!\n");
585754d96aSPatrick Pelletier 		/* Print out the OpenSSL error queue that libevent
595754d96aSPatrick Pelletier 		 * squirreled away for us, if any. */
605754d96aSPatrick Pelletier 		while ((oslerr = bufferevent_get_openssl_error(bev))) {
615754d96aSPatrick Pelletier 			ERR_error_string_n(oslerr, buffer, sizeof(buffer));
625754d96aSPatrick Pelletier 			fprintf(stderr, "%s\n", buffer);
635754d96aSPatrick Pelletier 			printed_err = 1;
645754d96aSPatrick Pelletier 		}
655754d96aSPatrick Pelletier 		/* If the OpenSSL error queue was empty, maybe it was a
665754d96aSPatrick Pelletier 		 * socket error; let's try printing that. */
675754d96aSPatrick Pelletier 		if (! printed_err)
685754d96aSPatrick Pelletier 			fprintf(stderr, "socket error = %s (%d)\n",
695754d96aSPatrick Pelletier 				evutil_socket_error_to_string(errcode),
705754d96aSPatrick Pelletier 				errcode);
71be46c99bSCatalin Patulea 		return;
72be46c99bSCatalin Patulea 	}
73be46c99bSCatalin Patulea 
74be46c99bSCatalin Patulea 	fprintf(stderr, "Response line: %d %s\n",
75be46c99bSCatalin Patulea 		req->response_code, req->response_code_line);
76be46c99bSCatalin Patulea 
77be46c99bSCatalin Patulea 	while ((nread = evbuffer_remove(req->input_buffer, buffer, sizeof(buffer)))
78be46c99bSCatalin Patulea 	       > 0) {
7942d7441aSPatrick Pelletier 		/* These are just arbitrary chunks of 256 bytes.
8042d7441aSPatrick Pelletier 		 * They are not lines, so we can't treat them as such. */
81be46c99bSCatalin Patulea 		fwrite(buffer, nread, 1, stdout);
82be46c99bSCatalin Patulea 	}
83be46c99bSCatalin Patulea }
84be46c99bSCatalin Patulea 
85be46c99bSCatalin Patulea static void
86be46c99bSCatalin Patulea syntax(void)
87be46c99bSCatalin Patulea {
88be46c99bSCatalin Patulea 	fputs("Syntax:\n", stderr);
89be46c99bSCatalin Patulea 	fputs("   https-client <https-url>\n", stderr);
90be46c99bSCatalin Patulea 	fputs("Example:\n", stderr);
91be46c99bSCatalin Patulea 	fputs("   https-client https://ip.appspot.com/\n", stderr);
92be46c99bSCatalin Patulea 
93be46c99bSCatalin Patulea 	exit(1);
94be46c99bSCatalin Patulea }
95be46c99bSCatalin Patulea 
96be46c99bSCatalin Patulea static void
97be46c99bSCatalin Patulea die(const char *msg)
98be46c99bSCatalin Patulea {
99be46c99bSCatalin Patulea 	fputs(msg, stderr);
100be46c99bSCatalin Patulea 	exit(1);
101be46c99bSCatalin Patulea }
102be46c99bSCatalin Patulea 
1035754d96aSPatrick Pelletier static void
1045754d96aSPatrick Pelletier die_openssl(const char *func)
1055754d96aSPatrick Pelletier {
1065754d96aSPatrick Pelletier 	fprintf (stderr, "%s failed:\n", func);
1075754d96aSPatrick Pelletier 
1085754d96aSPatrick Pelletier 	/* This is the OpenSSL function that prints the contents of the
1095754d96aSPatrick Pelletier 	 * error stack to the specified file handle. */
1105754d96aSPatrick Pelletier 	ERR_print_errors_fp (stderr);
1115754d96aSPatrick Pelletier 
1125754d96aSPatrick Pelletier 	exit(1);
1135754d96aSPatrick Pelletier }
1145754d96aSPatrick Pelletier 
115*64d9f161SPatrick Pelletier /* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */
116*64d9f161SPatrick Pelletier static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg)
117*64d9f161SPatrick Pelletier {
118*64d9f161SPatrick Pelletier 	char cert_str[256];
119*64d9f161SPatrick Pelletier 	const char *host = (const char *) arg;
120*64d9f161SPatrick Pelletier 	const char *res_str = "X509_verify_cert failed";
121*64d9f161SPatrick Pelletier 	HostnameValidationResult res = Error;
122*64d9f161SPatrick Pelletier 
123*64d9f161SPatrick Pelletier 	/* This is the function that OpenSSL would call if we hadn't called
124*64d9f161SPatrick Pelletier 	 * SSL_CTX_set_cert_verify_callback().  Therefore, we are "wrapping"
125*64d9f161SPatrick Pelletier 	 * the default functionality, rather than replacing it. */
126*64d9f161SPatrick Pelletier 	int ok_so_far = X509_verify_cert(x509_ctx);
127*64d9f161SPatrick Pelletier 
128*64d9f161SPatrick Pelletier 	X509 *server_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
129*64d9f161SPatrick Pelletier 
130*64d9f161SPatrick Pelletier 	if (ok_so_far) {
131*64d9f161SPatrick Pelletier 		res = validate_hostname(host, server_cert);
132*64d9f161SPatrick Pelletier 
133*64d9f161SPatrick Pelletier 		switch (res) {
134*64d9f161SPatrick Pelletier 		case MatchFound:
135*64d9f161SPatrick Pelletier 			res_str = "MatchFound";
136*64d9f161SPatrick Pelletier 			break;
137*64d9f161SPatrick Pelletier 		case MatchNotFound:
138*64d9f161SPatrick Pelletier 			res_str = "MatchNotFound";
139*64d9f161SPatrick Pelletier 			break;
140*64d9f161SPatrick Pelletier 		case NoSANPresent:
141*64d9f161SPatrick Pelletier 			res_str = "NoSANPresent";
142*64d9f161SPatrick Pelletier 			break;
143*64d9f161SPatrick Pelletier 		case MalformedCertificate:
144*64d9f161SPatrick Pelletier 			res_str = "MalformedCertificate";
145*64d9f161SPatrick Pelletier 			break;
146*64d9f161SPatrick Pelletier 		case Error:
147*64d9f161SPatrick Pelletier 			res_str = "Error";
148*64d9f161SPatrick Pelletier 			break;
149*64d9f161SPatrick Pelletier 		default:
150*64d9f161SPatrick Pelletier 			res_str = "WTF!";
151*64d9f161SPatrick Pelletier 			break;
152*64d9f161SPatrick Pelletier 		}
153*64d9f161SPatrick Pelletier 	}
154*64d9f161SPatrick Pelletier 
155*64d9f161SPatrick Pelletier 	X509_NAME_oneline(X509_get_subject_name (server_cert),
156*64d9f161SPatrick Pelletier 			  cert_str, sizeof (cert_str));
157*64d9f161SPatrick Pelletier 
158*64d9f161SPatrick Pelletier 	if (res == MatchFound) {
159*64d9f161SPatrick Pelletier 		printf("https server '%s' has this certificate, "
160*64d9f161SPatrick Pelletier 		       "which looks good to me:\n%s\n",
161*64d9f161SPatrick Pelletier 		       host, cert_str);
162*64d9f161SPatrick Pelletier 		return 1;
163*64d9f161SPatrick Pelletier 	} else {
164*64d9f161SPatrick Pelletier 		printf("Got '%s' for hostname '%s' and certificate:\n%s\n",
165*64d9f161SPatrick Pelletier 		       res_str, host, cert_str);
166*64d9f161SPatrick Pelletier 		return 0;
167*64d9f161SPatrick Pelletier 	}
168*64d9f161SPatrick Pelletier }
169*64d9f161SPatrick Pelletier 
170be46c99bSCatalin Patulea int
171be46c99bSCatalin Patulea main(int argc, char **argv)
172be46c99bSCatalin Patulea {
173be46c99bSCatalin Patulea 	int r;
174be46c99bSCatalin Patulea 
175be46c99bSCatalin Patulea 	struct evhttp_uri *http_uri;
176be46c99bSCatalin Patulea 	const char *url, *scheme, *host, *path, *query;
177be46c99bSCatalin Patulea 	char uri[256];
178be46c99bSCatalin Patulea 	int port;
179be46c99bSCatalin Patulea 
180be46c99bSCatalin Patulea 	SSL_CTX *ssl_ctx;
181be46c99bSCatalin Patulea 	SSL *ssl;
182be46c99bSCatalin Patulea 	struct bufferevent *bev;
183be46c99bSCatalin Patulea 	struct evhttp_connection *evcon;
184be46c99bSCatalin Patulea 	struct evhttp_request *req;
185be46c99bSCatalin Patulea 
186be46c99bSCatalin Patulea 	if (argc != 2)
187be46c99bSCatalin Patulea 		syntax();
188be46c99bSCatalin Patulea 
189be46c99bSCatalin Patulea 	url = argv[1];
190be46c99bSCatalin Patulea 	http_uri = evhttp_uri_parse(url);
191be46c99bSCatalin Patulea 	if (http_uri == NULL) {
192be46c99bSCatalin Patulea 		die("malformed url");
193be46c99bSCatalin Patulea 	}
194be46c99bSCatalin Patulea 
195be46c99bSCatalin Patulea 	scheme = evhttp_uri_get_scheme(http_uri);
196be46c99bSCatalin Patulea 	if (scheme == NULL || (strcasecmp(scheme, "https") != 0 &&
197be46c99bSCatalin Patulea 	                       strcasecmp(scheme, "http") != 0)) {
198be46c99bSCatalin Patulea 		die("url must be http or https");
199be46c99bSCatalin Patulea 	}
200be46c99bSCatalin Patulea 
201be46c99bSCatalin Patulea 	host = evhttp_uri_get_host(http_uri);
202be46c99bSCatalin Patulea 	if (host == NULL) {
203be46c99bSCatalin Patulea 		die("url must have a host");
204be46c99bSCatalin Patulea 	}
205be46c99bSCatalin Patulea 
206be46c99bSCatalin Patulea 	port = evhttp_uri_get_port(http_uri);
207be46c99bSCatalin Patulea 	if (port == -1) {
208be46c99bSCatalin Patulea 		port = (strcasecmp(scheme, "http") == 0) ? 80 : 443;
209be46c99bSCatalin Patulea 	}
210be46c99bSCatalin Patulea 
211be46c99bSCatalin Patulea 	path = evhttp_uri_get_path(http_uri);
212be46c99bSCatalin Patulea 	if (path == NULL) {
213be46c99bSCatalin Patulea 		path = "/";
214be46c99bSCatalin Patulea 	}
215be46c99bSCatalin Patulea 
216be46c99bSCatalin Patulea 	query = evhttp_uri_get_query(http_uri);
217be46c99bSCatalin Patulea 	if (query == NULL) {
218be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s", path);
219be46c99bSCatalin Patulea 	} else {
220be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query);
221be46c99bSCatalin Patulea 	}
222be46c99bSCatalin Patulea 	uri[sizeof(uri) - 1] = '\0';
223be46c99bSCatalin Patulea 
224be46c99bSCatalin Patulea 	// Initialize OpenSSL
225be46c99bSCatalin Patulea 	SSL_library_init();
226be46c99bSCatalin Patulea 	ERR_load_crypto_strings();
227be46c99bSCatalin Patulea 	SSL_load_error_strings();
228be46c99bSCatalin Patulea 	OpenSSL_add_all_algorithms();
2295754d96aSPatrick Pelletier 
2305754d96aSPatrick Pelletier 	/* This isn't strictly necessary... OpenSSL performs RAND_poll
2315754d96aSPatrick Pelletier 	 * automatically on first use of random number generator. */
232be46c99bSCatalin Patulea 	r = RAND_poll();
233be46c99bSCatalin Patulea 	if (r == 0) {
2345754d96aSPatrick Pelletier 		die_openssl("RAND_poll");
235be46c99bSCatalin Patulea 	}
2365754d96aSPatrick Pelletier 
2375754d96aSPatrick Pelletier 	/* Create a new OpenSSL context */
238be46c99bSCatalin Patulea 	ssl_ctx = SSL_CTX_new(SSLv23_method());
2395754d96aSPatrick Pelletier 	if (!ssl_ctx)
2405754d96aSPatrick Pelletier 		die_openssl("SSL_CTX_new");
241be46c99bSCatalin Patulea 
242aacd674cSPatrick Pelletier 	/* Attempt to use the system's trusted root certificates.
243aacd674cSPatrick Pelletier 	 * (This path is only valid for Debian-based systems.) */
244aacd674cSPatrick Pelletier 	if (1 != SSL_CTX_load_verify_locations(ssl_ctx,
245aacd674cSPatrick Pelletier 					       "/etc/ssl/certs/ca-certificates.crt",
246aacd674cSPatrick Pelletier 					       NULL))
247aacd674cSPatrick Pelletier 		die_openssl("SSL_CTX_load_verify_locations");
248*64d9f161SPatrick Pelletier 	/* Ask OpenSSL to verify the server certificate.  Note that this
249*64d9f161SPatrick Pelletier 	 * does NOT include verifying that the hostname is correct.
250*64d9f161SPatrick Pelletier 	 * So, by itself, this means anyone with any legitimate
251*64d9f161SPatrick Pelletier 	 * CA-issued certificate for any website, can impersonate any
252*64d9f161SPatrick Pelletier 	 * other website in the world.  This is not good.  See "The
253*64d9f161SPatrick Pelletier 	 * Most Dangerous Code in the World" article at
254*64d9f161SPatrick Pelletier 	 * https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html
255*64d9f161SPatrick Pelletier 	 */
256aacd674cSPatrick Pelletier 	SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
257*64d9f161SPatrick Pelletier 	/* This is how we solve the problem mentioned in the previous
258*64d9f161SPatrick Pelletier 	 * comment.  We "wrap" OpenSSL's validation routine in our
259*64d9f161SPatrick Pelletier 	 * own routine, which also validates the hostname by calling
260*64d9f161SPatrick Pelletier 	 * the code provided by iSECPartners.  Note that even though
261*64d9f161SPatrick Pelletier 	 * the "Everything You've Always Wanted to Know About
262*64d9f161SPatrick Pelletier 	 * Certificate Validation With OpenSSL (But Were Afraid to
263*64d9f161SPatrick Pelletier 	 * Ask)" paper from iSECPartners says very explicitly not to
264*64d9f161SPatrick Pelletier 	 * call SSL_CTX_set_cert_verify_callback (at the bottom of
265*64d9f161SPatrick Pelletier 	 * page 2), what we're doing here is safe because our
266*64d9f161SPatrick Pelletier 	 * cert_verify_callback() calls X509_verify_cert(), which is
267*64d9f161SPatrick Pelletier 	 * OpenSSL's built-in routine which would have been called if
268*64d9f161SPatrick Pelletier 	 * we hadn't set the callback.  Therefore, we're just
269*64d9f161SPatrick Pelletier 	 * "wrapping" OpenSSL's routine, not replacing it. */
270*64d9f161SPatrick Pelletier 	SSL_CTX_set_cert_verify_callback (ssl_ctx, cert_verify_callback,
271*64d9f161SPatrick Pelletier 					  (void *) host);
272aacd674cSPatrick Pelletier 
273be46c99bSCatalin Patulea 	// Create event base
274be46c99bSCatalin Patulea 	base = event_base_new();
275be46c99bSCatalin Patulea 	if (!base) {
276be46c99bSCatalin Patulea 		perror("event_base_new()");
277be46c99bSCatalin Patulea 		return 1;
278be46c99bSCatalin Patulea 	}
279be46c99bSCatalin Patulea 
280be46c99bSCatalin Patulea 	// Create OpenSSL bufferevent and stack evhttp on top of it
281be46c99bSCatalin Patulea 	ssl = SSL_new(ssl_ctx);
282be46c99bSCatalin Patulea 	if (ssl == NULL) {
2835754d96aSPatrick Pelletier 		die_openssl("SSL_new()");
284be46c99bSCatalin Patulea 	}
285be46c99bSCatalin Patulea 
286be46c99bSCatalin Patulea 	if (strcasecmp(scheme, "http") == 0) {
287be46c99bSCatalin Patulea 		bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
288be46c99bSCatalin Patulea 	} else {
289be46c99bSCatalin Patulea 		bev = bufferevent_openssl_socket_new(base, -1, ssl,
290be46c99bSCatalin Patulea 			BUFFEREVENT_SSL_CONNECTING,
291be46c99bSCatalin Patulea 			BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
292be46c99bSCatalin Patulea 	}
293be46c99bSCatalin Patulea 
294be46c99bSCatalin Patulea 	if (bev == NULL) {
295be46c99bSCatalin Patulea 		fprintf(stderr, "bufferevent_openssl_socket_new() failed\n");
296be46c99bSCatalin Patulea 		return 1;
297be46c99bSCatalin Patulea 	}
298be46c99bSCatalin Patulea 
299be46c99bSCatalin Patulea 	bufferevent_openssl_set_allow_dirty_shutdown(bev, 1);
300be46c99bSCatalin Patulea 
301be46c99bSCatalin Patulea 	// For simplicity, we let DNS resolution block. Everything else should be
302be46c99bSCatalin Patulea 	// asynchronous though.
303be46c99bSCatalin Patulea 	evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev,
304be46c99bSCatalin Patulea 		host, port);
305be46c99bSCatalin Patulea 	if (evcon == NULL) {
306be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n");
307be46c99bSCatalin Patulea 		return 1;
308be46c99bSCatalin Patulea 	}
309be46c99bSCatalin Patulea 
310be46c99bSCatalin Patulea 	// Fire off the request
3115754d96aSPatrick Pelletier 	req = evhttp_request_new(http_request_done, bev);
312be46c99bSCatalin Patulea 	if (req == NULL) {
313be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_request_new() failed\n");
314be46c99bSCatalin Patulea 		return 1;
315be46c99bSCatalin Patulea 	}
316be46c99bSCatalin Patulea 
317be46c99bSCatalin Patulea 	evhttp_add_header(req->output_headers, "Host", host);
318be46c99bSCatalin Patulea 	evhttp_add_header(req->output_headers, "Connection", "close");
319be46c99bSCatalin Patulea 
320be46c99bSCatalin Patulea 	r = evhttp_make_request(evcon, req, EVHTTP_REQ_GET, uri);
321be46c99bSCatalin Patulea 	if (r != 0) {
322be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_make_request() failed\n");
323be46c99bSCatalin Patulea 		return 1;
324be46c99bSCatalin Patulea 	}
325be46c99bSCatalin Patulea 
326be46c99bSCatalin Patulea 	event_base_dispatch(base);
327be46c99bSCatalin Patulea 
328be46c99bSCatalin Patulea 	evhttp_connection_free(evcon);
329be46c99bSCatalin Patulea 	event_base_free(base);
330be46c99bSCatalin Patulea 
331be46c99bSCatalin Patulea 	return 0;
332be46c99bSCatalin Patulea }
333