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", 74*8a90a850SNick Mathewson evhttp_request_get_response_code(req), 75*8a90a850SNick Mathewson evhttp_request_get_response_code_line(req)); 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 11564d9f161SPatrick Pelletier /* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */ 11664d9f161SPatrick Pelletier static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg) 11764d9f161SPatrick Pelletier { 11864d9f161SPatrick Pelletier char cert_str[256]; 11964d9f161SPatrick Pelletier const char *host = (const char *) arg; 12064d9f161SPatrick Pelletier const char *res_str = "X509_verify_cert failed"; 12164d9f161SPatrick Pelletier HostnameValidationResult res = Error; 12264d9f161SPatrick Pelletier 12364d9f161SPatrick Pelletier /* This is the function that OpenSSL would call if we hadn't called 12464d9f161SPatrick Pelletier * SSL_CTX_set_cert_verify_callback(). Therefore, we are "wrapping" 12564d9f161SPatrick Pelletier * the default functionality, rather than replacing it. */ 12664d9f161SPatrick Pelletier int ok_so_far = X509_verify_cert(x509_ctx); 12764d9f161SPatrick Pelletier 12864d9f161SPatrick Pelletier X509 *server_cert = X509_STORE_CTX_get_current_cert(x509_ctx); 12964d9f161SPatrick Pelletier 13064d9f161SPatrick Pelletier if (ok_so_far) { 13164d9f161SPatrick Pelletier res = validate_hostname(host, server_cert); 13264d9f161SPatrick Pelletier 13364d9f161SPatrick Pelletier switch (res) { 13464d9f161SPatrick Pelletier case MatchFound: 13564d9f161SPatrick Pelletier res_str = "MatchFound"; 13664d9f161SPatrick Pelletier break; 13764d9f161SPatrick Pelletier case MatchNotFound: 13864d9f161SPatrick Pelletier res_str = "MatchNotFound"; 13964d9f161SPatrick Pelletier break; 14064d9f161SPatrick Pelletier case NoSANPresent: 14164d9f161SPatrick Pelletier res_str = "NoSANPresent"; 14264d9f161SPatrick Pelletier break; 14364d9f161SPatrick Pelletier case MalformedCertificate: 14464d9f161SPatrick Pelletier res_str = "MalformedCertificate"; 14564d9f161SPatrick Pelletier break; 14664d9f161SPatrick Pelletier case Error: 14764d9f161SPatrick Pelletier res_str = "Error"; 14864d9f161SPatrick Pelletier break; 14964d9f161SPatrick Pelletier default: 15064d9f161SPatrick Pelletier res_str = "WTF!"; 15164d9f161SPatrick Pelletier break; 15264d9f161SPatrick Pelletier } 15364d9f161SPatrick Pelletier } 15464d9f161SPatrick Pelletier 15564d9f161SPatrick Pelletier X509_NAME_oneline(X509_get_subject_name (server_cert), 15664d9f161SPatrick Pelletier cert_str, sizeof (cert_str)); 15764d9f161SPatrick Pelletier 15864d9f161SPatrick Pelletier if (res == MatchFound) { 15964d9f161SPatrick Pelletier printf("https server '%s' has this certificate, " 16064d9f161SPatrick Pelletier "which looks good to me:\n%s\n", 16164d9f161SPatrick Pelletier host, cert_str); 16264d9f161SPatrick Pelletier return 1; 16364d9f161SPatrick Pelletier } else { 16464d9f161SPatrick Pelletier printf("Got '%s' for hostname '%s' and certificate:\n%s\n", 16564d9f161SPatrick Pelletier res_str, host, cert_str); 16664d9f161SPatrick Pelletier return 0; 16764d9f161SPatrick Pelletier } 16864d9f161SPatrick Pelletier } 16964d9f161SPatrick 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; 185*8a90a850SNick Mathewson struct evkeyvalq *output_headers; 186be46c99bSCatalin Patulea 187be46c99bSCatalin Patulea if (argc != 2) 188be46c99bSCatalin Patulea syntax(); 189be46c99bSCatalin Patulea 190be46c99bSCatalin Patulea url = argv[1]; 191be46c99bSCatalin Patulea http_uri = evhttp_uri_parse(url); 192be46c99bSCatalin Patulea if (http_uri == NULL) { 193be46c99bSCatalin Patulea die("malformed url"); 194be46c99bSCatalin Patulea } 195be46c99bSCatalin Patulea 196be46c99bSCatalin Patulea scheme = evhttp_uri_get_scheme(http_uri); 197be46c99bSCatalin Patulea if (scheme == NULL || (strcasecmp(scheme, "https") != 0 && 198be46c99bSCatalin Patulea strcasecmp(scheme, "http") != 0)) { 199be46c99bSCatalin Patulea die("url must be http or https"); 200be46c99bSCatalin Patulea } 201be46c99bSCatalin Patulea 202be46c99bSCatalin Patulea host = evhttp_uri_get_host(http_uri); 203be46c99bSCatalin Patulea if (host == NULL) { 204be46c99bSCatalin Patulea die("url must have a host"); 205be46c99bSCatalin Patulea } 206be46c99bSCatalin Patulea 207be46c99bSCatalin Patulea port = evhttp_uri_get_port(http_uri); 208be46c99bSCatalin Patulea if (port == -1) { 209be46c99bSCatalin Patulea port = (strcasecmp(scheme, "http") == 0) ? 80 : 443; 210be46c99bSCatalin Patulea } 211be46c99bSCatalin Patulea 212be46c99bSCatalin Patulea path = evhttp_uri_get_path(http_uri); 213be46c99bSCatalin Patulea if (path == NULL) { 214be46c99bSCatalin Patulea path = "/"; 215be46c99bSCatalin Patulea } 216be46c99bSCatalin Patulea 217be46c99bSCatalin Patulea query = evhttp_uri_get_query(http_uri); 218be46c99bSCatalin Patulea if (query == NULL) { 219be46c99bSCatalin Patulea snprintf(uri, sizeof(uri) - 1, "%s", path); 220be46c99bSCatalin Patulea } else { 221be46c99bSCatalin Patulea snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query); 222be46c99bSCatalin Patulea } 223be46c99bSCatalin Patulea uri[sizeof(uri) - 1] = '\0'; 224be46c99bSCatalin Patulea 225be46c99bSCatalin Patulea // Initialize OpenSSL 226be46c99bSCatalin Patulea SSL_library_init(); 227be46c99bSCatalin Patulea ERR_load_crypto_strings(); 228be46c99bSCatalin Patulea SSL_load_error_strings(); 229be46c99bSCatalin Patulea OpenSSL_add_all_algorithms(); 2305754d96aSPatrick Pelletier 2315754d96aSPatrick Pelletier /* This isn't strictly necessary... OpenSSL performs RAND_poll 2325754d96aSPatrick Pelletier * automatically on first use of random number generator. */ 233be46c99bSCatalin Patulea r = RAND_poll(); 234be46c99bSCatalin Patulea if (r == 0) { 2355754d96aSPatrick Pelletier die_openssl("RAND_poll"); 236be46c99bSCatalin Patulea } 2375754d96aSPatrick Pelletier 2385754d96aSPatrick Pelletier /* Create a new OpenSSL context */ 239be46c99bSCatalin Patulea ssl_ctx = SSL_CTX_new(SSLv23_method()); 2405754d96aSPatrick Pelletier if (!ssl_ctx) 2415754d96aSPatrick Pelletier die_openssl("SSL_CTX_new"); 242be46c99bSCatalin Patulea 243aacd674cSPatrick Pelletier /* Attempt to use the system's trusted root certificates. 244aacd674cSPatrick Pelletier * (This path is only valid for Debian-based systems.) */ 245aacd674cSPatrick Pelletier if (1 != SSL_CTX_load_verify_locations(ssl_ctx, 246aacd674cSPatrick Pelletier "/etc/ssl/certs/ca-certificates.crt", 247aacd674cSPatrick Pelletier NULL)) 248aacd674cSPatrick Pelletier die_openssl("SSL_CTX_load_verify_locations"); 24964d9f161SPatrick Pelletier /* Ask OpenSSL to verify the server certificate. Note that this 25064d9f161SPatrick Pelletier * does NOT include verifying that the hostname is correct. 25164d9f161SPatrick Pelletier * So, by itself, this means anyone with any legitimate 25264d9f161SPatrick Pelletier * CA-issued certificate for any website, can impersonate any 25364d9f161SPatrick Pelletier * other website in the world. This is not good. See "The 25464d9f161SPatrick Pelletier * Most Dangerous Code in the World" article at 25564d9f161SPatrick Pelletier * https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html 25664d9f161SPatrick Pelletier */ 257aacd674cSPatrick Pelletier SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); 25864d9f161SPatrick Pelletier /* This is how we solve the problem mentioned in the previous 25964d9f161SPatrick Pelletier * comment. We "wrap" OpenSSL's validation routine in our 26064d9f161SPatrick Pelletier * own routine, which also validates the hostname by calling 26164d9f161SPatrick Pelletier * the code provided by iSECPartners. Note that even though 26264d9f161SPatrick Pelletier * the "Everything You've Always Wanted to Know About 26364d9f161SPatrick Pelletier * Certificate Validation With OpenSSL (But Were Afraid to 26464d9f161SPatrick Pelletier * Ask)" paper from iSECPartners says very explicitly not to 26564d9f161SPatrick Pelletier * call SSL_CTX_set_cert_verify_callback (at the bottom of 26664d9f161SPatrick Pelletier * page 2), what we're doing here is safe because our 26764d9f161SPatrick Pelletier * cert_verify_callback() calls X509_verify_cert(), which is 26864d9f161SPatrick Pelletier * OpenSSL's built-in routine which would have been called if 26964d9f161SPatrick Pelletier * we hadn't set the callback. Therefore, we're just 27064d9f161SPatrick Pelletier * "wrapping" OpenSSL's routine, not replacing it. */ 27164d9f161SPatrick Pelletier SSL_CTX_set_cert_verify_callback (ssl_ctx, cert_verify_callback, 27264d9f161SPatrick Pelletier (void *) host); 273aacd674cSPatrick Pelletier 274be46c99bSCatalin Patulea // Create event base 275be46c99bSCatalin Patulea base = event_base_new(); 276be46c99bSCatalin Patulea if (!base) { 277be46c99bSCatalin Patulea perror("event_base_new()"); 278be46c99bSCatalin Patulea return 1; 279be46c99bSCatalin Patulea } 280be46c99bSCatalin Patulea 281be46c99bSCatalin Patulea // Create OpenSSL bufferevent and stack evhttp on top of it 282be46c99bSCatalin Patulea ssl = SSL_new(ssl_ctx); 283be46c99bSCatalin Patulea if (ssl == NULL) { 2845754d96aSPatrick Pelletier die_openssl("SSL_new()"); 285be46c99bSCatalin Patulea } 286be46c99bSCatalin Patulea 287be46c99bSCatalin Patulea if (strcasecmp(scheme, "http") == 0) { 288be46c99bSCatalin Patulea bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); 289be46c99bSCatalin Patulea } else { 290be46c99bSCatalin Patulea bev = bufferevent_openssl_socket_new(base, -1, ssl, 291be46c99bSCatalin Patulea BUFFEREVENT_SSL_CONNECTING, 292be46c99bSCatalin Patulea BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); 293be46c99bSCatalin Patulea } 294be46c99bSCatalin Patulea 295be46c99bSCatalin Patulea if (bev == NULL) { 296be46c99bSCatalin Patulea fprintf(stderr, "bufferevent_openssl_socket_new() failed\n"); 297be46c99bSCatalin Patulea return 1; 298be46c99bSCatalin Patulea } 299be46c99bSCatalin Patulea 300be46c99bSCatalin Patulea bufferevent_openssl_set_allow_dirty_shutdown(bev, 1); 301be46c99bSCatalin Patulea 302be46c99bSCatalin Patulea // For simplicity, we let DNS resolution block. Everything else should be 303be46c99bSCatalin Patulea // asynchronous though. 304be46c99bSCatalin Patulea evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev, 305be46c99bSCatalin Patulea host, port); 306be46c99bSCatalin Patulea if (evcon == NULL) { 307be46c99bSCatalin Patulea fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n"); 308be46c99bSCatalin Patulea return 1; 309be46c99bSCatalin Patulea } 310be46c99bSCatalin Patulea 311be46c99bSCatalin Patulea // Fire off the request 3125754d96aSPatrick Pelletier req = evhttp_request_new(http_request_done, bev); 313be46c99bSCatalin Patulea if (req == NULL) { 314be46c99bSCatalin Patulea fprintf(stderr, "evhttp_request_new() failed\n"); 315be46c99bSCatalin Patulea return 1; 316be46c99bSCatalin Patulea } 317be46c99bSCatalin Patulea 318*8a90a850SNick Mathewson output_headers = evhttp_request_get_output_headers(req); 319*8a90a850SNick Mathewson evhttp_add_header(output_headers, "Host", host); 320*8a90a850SNick Mathewson evhttp_add_header(output_headers, "Connection", "close"); 321be46c99bSCatalin Patulea 322be46c99bSCatalin Patulea r = evhttp_make_request(evcon, req, EVHTTP_REQ_GET, uri); 323be46c99bSCatalin Patulea if (r != 0) { 324be46c99bSCatalin Patulea fprintf(stderr, "evhttp_make_request() failed\n"); 325be46c99bSCatalin Patulea return 1; 326be46c99bSCatalin Patulea } 327be46c99bSCatalin Patulea 328be46c99bSCatalin Patulea event_base_dispatch(base); 329be46c99bSCatalin Patulea 330be46c99bSCatalin Patulea evhttp_connection_free(evcon); 331be46c99bSCatalin Patulea event_base_free(base); 332be46c99bSCatalin Patulea 333be46c99bSCatalin Patulea return 0; 334be46c99bSCatalin Patulea } 335