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 13*69c3516bSJoakim Söderberg // Get rid of OSX 10.7 and greater deprecation warnings. 14*69c3516bSJoakim Söderberg #pragma clang diagnostic ignored "-Wdeprecated-declarations" 15*69c3516bSJoakim Söderberg 16be46c99bSCatalin Patulea #include <stdio.h> 17be46c99bSCatalin Patulea #include <assert.h> 18be46c99bSCatalin Patulea #include <stdlib.h> 19be46c99bSCatalin Patulea #include <string.h> 20be46c99bSCatalin Patulea #include <errno.h> 21be46c99bSCatalin Patulea 22be46c99bSCatalin Patulea #ifdef WIN32 23be46c99bSCatalin Patulea #include <winsock2.h> 24be46c99bSCatalin Patulea #include <ws2tcpip.h> 2519222e52SJoakim Soderberg 2619222e52SJoakim Soderberg #define snprintf _snprintf 2719222e52SJoakim Soderberg #define strcasecmp _stricmp 28be46c99bSCatalin Patulea #else 29be46c99bSCatalin Patulea #include <sys/socket.h> 30be46c99bSCatalin Patulea #include <netinet/in.h> 31be46c99bSCatalin Patulea #endif 32be46c99bSCatalin Patulea 33be46c99bSCatalin Patulea #include <event2/bufferevent_ssl.h> 34be46c99bSCatalin Patulea #include <event2/bufferevent.h> 35be46c99bSCatalin Patulea #include <event2/buffer.h> 36be46c99bSCatalin Patulea #include <event2/listener.h> 37be46c99bSCatalin Patulea #include <event2/util.h> 38be46c99bSCatalin Patulea #include <event2/http.h> 39be46c99bSCatalin Patulea 40be46c99bSCatalin Patulea #include <openssl/ssl.h> 41be46c99bSCatalin Patulea #include <openssl/err.h> 42be46c99bSCatalin Patulea #include <openssl/rand.h> 43be46c99bSCatalin Patulea 4464d9f161SPatrick Pelletier #include "openssl_hostname_validation.h" 4564d9f161SPatrick Pelletier 46be46c99bSCatalin Patulea static struct event_base *base; 47be46c99bSCatalin Patulea 48be46c99bSCatalin Patulea static void 49be46c99bSCatalin Patulea http_request_done(struct evhttp_request *req, void *ctx) 50be46c99bSCatalin Patulea { 51be46c99bSCatalin Patulea char buffer[256]; 52be46c99bSCatalin Patulea int nread; 53be46c99bSCatalin Patulea 54be46c99bSCatalin Patulea if (req == NULL) { 555754d96aSPatrick Pelletier /* If req is NULL, it means an error occurred, but 565754d96aSPatrick Pelletier * sadly we are mostly left guessing what the error 575754d96aSPatrick Pelletier * might have been. We'll do our best... */ 585754d96aSPatrick Pelletier struct bufferevent *bev = (struct bufferevent *) ctx; 595754d96aSPatrick Pelletier unsigned long oslerr; 605754d96aSPatrick Pelletier int printed_err = 0; 615754d96aSPatrick Pelletier int errcode = EVUTIL_SOCKET_ERROR(); 62be46c99bSCatalin Patulea fprintf(stderr, "some request failed - no idea which one though!\n"); 635754d96aSPatrick Pelletier /* Print out the OpenSSL error queue that libevent 645754d96aSPatrick Pelletier * squirreled away for us, if any. */ 655754d96aSPatrick Pelletier while ((oslerr = bufferevent_get_openssl_error(bev))) { 665754d96aSPatrick Pelletier ERR_error_string_n(oslerr, buffer, sizeof(buffer)); 675754d96aSPatrick Pelletier fprintf(stderr, "%s\n", buffer); 685754d96aSPatrick Pelletier printed_err = 1; 695754d96aSPatrick Pelletier } 705754d96aSPatrick Pelletier /* If the OpenSSL error queue was empty, maybe it was a 715754d96aSPatrick Pelletier * socket error; let's try printing that. */ 725754d96aSPatrick Pelletier if (! printed_err) 735754d96aSPatrick Pelletier fprintf(stderr, "socket error = %s (%d)\n", 745754d96aSPatrick Pelletier evutil_socket_error_to_string(errcode), 755754d96aSPatrick Pelletier errcode); 76be46c99bSCatalin Patulea return; 77be46c99bSCatalin Patulea } 78be46c99bSCatalin Patulea 79be46c99bSCatalin Patulea fprintf(stderr, "Response line: %d %s\n", 808a90a850SNick Mathewson evhttp_request_get_response_code(req), 818a90a850SNick Mathewson evhttp_request_get_response_code_line(req)); 82be46c99bSCatalin Patulea 8395acdaa3SNick Mathewson while ((nread = evbuffer_remove(evhttp_request_get_input_buffer(req), 8495acdaa3SNick Mathewson buffer, sizeof(buffer))) 85be46c99bSCatalin Patulea > 0) { 8642d7441aSPatrick Pelletier /* These are just arbitrary chunks of 256 bytes. 8742d7441aSPatrick Pelletier * They are not lines, so we can't treat them as such. */ 88be46c99bSCatalin Patulea fwrite(buffer, nread, 1, stdout); 89be46c99bSCatalin Patulea } 90be46c99bSCatalin Patulea } 91be46c99bSCatalin Patulea 92be46c99bSCatalin Patulea static void 93be46c99bSCatalin Patulea syntax(void) 94be46c99bSCatalin Patulea { 95be46c99bSCatalin Patulea fputs("Syntax:\n", stderr); 96be46c99bSCatalin Patulea fputs(" https-client <https-url>\n", stderr); 97be46c99bSCatalin Patulea fputs("Example:\n", stderr); 98be46c99bSCatalin Patulea fputs(" https-client https://ip.appspot.com/\n", stderr); 99be46c99bSCatalin Patulea 100be46c99bSCatalin Patulea exit(1); 101be46c99bSCatalin Patulea } 102be46c99bSCatalin Patulea 103be46c99bSCatalin Patulea static void 104be46c99bSCatalin Patulea die(const char *msg) 105be46c99bSCatalin Patulea { 106be46c99bSCatalin Patulea fputs(msg, stderr); 107be46c99bSCatalin Patulea exit(1); 108be46c99bSCatalin Patulea } 109be46c99bSCatalin Patulea 1105754d96aSPatrick Pelletier static void 1115754d96aSPatrick Pelletier die_openssl(const char *func) 1125754d96aSPatrick Pelletier { 1135754d96aSPatrick Pelletier fprintf (stderr, "%s failed:\n", func); 1145754d96aSPatrick Pelletier 1155754d96aSPatrick Pelletier /* This is the OpenSSL function that prints the contents of the 1165754d96aSPatrick Pelletier * error stack to the specified file handle. */ 1175754d96aSPatrick Pelletier ERR_print_errors_fp (stderr); 1185754d96aSPatrick Pelletier 1195754d96aSPatrick Pelletier exit(1); 1205754d96aSPatrick Pelletier } 1215754d96aSPatrick Pelletier 12264d9f161SPatrick Pelletier /* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */ 12364d9f161SPatrick Pelletier static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg) 12464d9f161SPatrick Pelletier { 12564d9f161SPatrick Pelletier char cert_str[256]; 12664d9f161SPatrick Pelletier const char *host = (const char *) arg; 12764d9f161SPatrick Pelletier const char *res_str = "X509_verify_cert failed"; 12864d9f161SPatrick Pelletier HostnameValidationResult res = Error; 12964d9f161SPatrick Pelletier 13064d9f161SPatrick Pelletier /* This is the function that OpenSSL would call if we hadn't called 13164d9f161SPatrick Pelletier * SSL_CTX_set_cert_verify_callback(). Therefore, we are "wrapping" 13264d9f161SPatrick Pelletier * the default functionality, rather than replacing it. */ 13364d9f161SPatrick Pelletier int ok_so_far = X509_verify_cert(x509_ctx); 13464d9f161SPatrick Pelletier 13564d9f161SPatrick Pelletier X509 *server_cert = X509_STORE_CTX_get_current_cert(x509_ctx); 13664d9f161SPatrick Pelletier 13764d9f161SPatrick Pelletier if (ok_so_far) { 13864d9f161SPatrick Pelletier res = validate_hostname(host, server_cert); 13964d9f161SPatrick Pelletier 14064d9f161SPatrick Pelletier switch (res) { 14164d9f161SPatrick Pelletier case MatchFound: 14264d9f161SPatrick Pelletier res_str = "MatchFound"; 14364d9f161SPatrick Pelletier break; 14464d9f161SPatrick Pelletier case MatchNotFound: 14564d9f161SPatrick Pelletier res_str = "MatchNotFound"; 14664d9f161SPatrick Pelletier break; 14764d9f161SPatrick Pelletier case NoSANPresent: 14864d9f161SPatrick Pelletier res_str = "NoSANPresent"; 14964d9f161SPatrick Pelletier break; 15064d9f161SPatrick Pelletier case MalformedCertificate: 15164d9f161SPatrick Pelletier res_str = "MalformedCertificate"; 15264d9f161SPatrick Pelletier break; 15364d9f161SPatrick Pelletier case Error: 15464d9f161SPatrick Pelletier res_str = "Error"; 15564d9f161SPatrick Pelletier break; 15664d9f161SPatrick Pelletier default: 15764d9f161SPatrick Pelletier res_str = "WTF!"; 15864d9f161SPatrick Pelletier break; 15964d9f161SPatrick Pelletier } 16064d9f161SPatrick Pelletier } 16164d9f161SPatrick Pelletier 16264d9f161SPatrick Pelletier X509_NAME_oneline(X509_get_subject_name (server_cert), 16364d9f161SPatrick Pelletier cert_str, sizeof (cert_str)); 16464d9f161SPatrick Pelletier 16564d9f161SPatrick Pelletier if (res == MatchFound) { 16664d9f161SPatrick Pelletier printf("https server '%s' has this certificate, " 16764d9f161SPatrick Pelletier "which looks good to me:\n%s\n", 16864d9f161SPatrick Pelletier host, cert_str); 16964d9f161SPatrick Pelletier return 1; 17064d9f161SPatrick Pelletier } else { 17164d9f161SPatrick Pelletier printf("Got '%s' for hostname '%s' and certificate:\n%s\n", 17264d9f161SPatrick Pelletier res_str, host, cert_str); 17364d9f161SPatrick Pelletier return 0; 17464d9f161SPatrick Pelletier } 17564d9f161SPatrick Pelletier } 17664d9f161SPatrick Pelletier 177be46c99bSCatalin Patulea int 178be46c99bSCatalin Patulea main(int argc, char **argv) 179be46c99bSCatalin Patulea { 180be46c99bSCatalin Patulea int r; 181be46c99bSCatalin Patulea 182be46c99bSCatalin Patulea struct evhttp_uri *http_uri; 183be46c99bSCatalin Patulea const char *url, *scheme, *host, *path, *query; 184be46c99bSCatalin Patulea char uri[256]; 185be46c99bSCatalin Patulea int port; 186be46c99bSCatalin Patulea 187be46c99bSCatalin Patulea SSL_CTX *ssl_ctx; 188be46c99bSCatalin Patulea SSL *ssl; 189be46c99bSCatalin Patulea struct bufferevent *bev; 190be46c99bSCatalin Patulea struct evhttp_connection *evcon; 191be46c99bSCatalin Patulea struct evhttp_request *req; 1928a90a850SNick Mathewson struct evkeyvalq *output_headers; 193be46c99bSCatalin Patulea 194be46c99bSCatalin Patulea if (argc != 2) 195be46c99bSCatalin Patulea syntax(); 196be46c99bSCatalin Patulea 19719222e52SJoakim Soderberg #ifdef WIN32 19819222e52SJoakim Soderberg { 19919222e52SJoakim Soderberg WORD wVersionRequested; 20019222e52SJoakim Soderberg WSADATA wsaData; 20119222e52SJoakim Soderberg int err; 20219222e52SJoakim Soderberg 20319222e52SJoakim Soderberg wVersionRequested = MAKEWORD(2, 2); 20419222e52SJoakim Soderberg 20519222e52SJoakim Soderberg err = WSAStartup(wVersionRequested, &wsaData); 20619222e52SJoakim Soderberg if (err != 0) { 20719222e52SJoakim Soderberg printf("WSAStartup failed with error: %d\n", err); 20819222e52SJoakim Soderberg return 1; 20919222e52SJoakim Soderberg } 21019222e52SJoakim Soderberg } 21119222e52SJoakim Soderberg #endif 21219222e52SJoakim Soderberg 213be46c99bSCatalin Patulea url = argv[1]; 214be46c99bSCatalin Patulea http_uri = evhttp_uri_parse(url); 215be46c99bSCatalin Patulea if (http_uri == NULL) { 216be46c99bSCatalin Patulea die("malformed url"); 217be46c99bSCatalin Patulea } 218be46c99bSCatalin Patulea 219be46c99bSCatalin Patulea scheme = evhttp_uri_get_scheme(http_uri); 220be46c99bSCatalin Patulea if (scheme == NULL || (strcasecmp(scheme, "https") != 0 && 221be46c99bSCatalin Patulea strcasecmp(scheme, "http") != 0)) { 222be46c99bSCatalin Patulea die("url must be http or https"); 223be46c99bSCatalin Patulea } 224be46c99bSCatalin Patulea 225be46c99bSCatalin Patulea host = evhttp_uri_get_host(http_uri); 226be46c99bSCatalin Patulea if (host == NULL) { 227be46c99bSCatalin Patulea die("url must have a host"); 228be46c99bSCatalin Patulea } 229be46c99bSCatalin Patulea 230be46c99bSCatalin Patulea port = evhttp_uri_get_port(http_uri); 231be46c99bSCatalin Patulea if (port == -1) { 232be46c99bSCatalin Patulea port = (strcasecmp(scheme, "http") == 0) ? 80 : 443; 233be46c99bSCatalin Patulea } 234be46c99bSCatalin Patulea 235be46c99bSCatalin Patulea path = evhttp_uri_get_path(http_uri); 236be46c99bSCatalin Patulea if (path == NULL) { 237be46c99bSCatalin Patulea path = "/"; 238be46c99bSCatalin Patulea } 239be46c99bSCatalin Patulea 240be46c99bSCatalin Patulea query = evhttp_uri_get_query(http_uri); 241be46c99bSCatalin Patulea if (query == NULL) { 242be46c99bSCatalin Patulea snprintf(uri, sizeof(uri) - 1, "%s", path); 243be46c99bSCatalin Patulea } else { 244be46c99bSCatalin Patulea snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query); 245be46c99bSCatalin Patulea } 246be46c99bSCatalin Patulea uri[sizeof(uri) - 1] = '\0'; 247be46c99bSCatalin Patulea 248be46c99bSCatalin Patulea // Initialize OpenSSL 249be46c99bSCatalin Patulea SSL_library_init(); 250be46c99bSCatalin Patulea ERR_load_crypto_strings(); 251be46c99bSCatalin Patulea SSL_load_error_strings(); 252be46c99bSCatalin Patulea OpenSSL_add_all_algorithms(); 2535754d96aSPatrick Pelletier 2545754d96aSPatrick Pelletier /* This isn't strictly necessary... OpenSSL performs RAND_poll 2555754d96aSPatrick Pelletier * automatically on first use of random number generator. */ 256be46c99bSCatalin Patulea r = RAND_poll(); 257be46c99bSCatalin Patulea if (r == 0) { 2585754d96aSPatrick Pelletier die_openssl("RAND_poll"); 259be46c99bSCatalin Patulea } 2605754d96aSPatrick Pelletier 2615754d96aSPatrick Pelletier /* Create a new OpenSSL context */ 262be46c99bSCatalin Patulea ssl_ctx = SSL_CTX_new(SSLv23_method()); 2635754d96aSPatrick Pelletier if (!ssl_ctx) 2645754d96aSPatrick Pelletier die_openssl("SSL_CTX_new"); 265be46c99bSCatalin Patulea 26619222e52SJoakim Soderberg #ifndef WIN32 26719222e52SJoakim Soderberg /* TODO: Add certificate loading on Windows as well */ 26819222e52SJoakim Soderberg 269aacd674cSPatrick Pelletier /* Attempt to use the system's trusted root certificates. 270aacd674cSPatrick Pelletier * (This path is only valid for Debian-based systems.) */ 271aacd674cSPatrick Pelletier if (1 != SSL_CTX_load_verify_locations(ssl_ctx, 272aacd674cSPatrick Pelletier "/etc/ssl/certs/ca-certificates.crt", 273aacd674cSPatrick Pelletier NULL)) 274aacd674cSPatrick Pelletier die_openssl("SSL_CTX_load_verify_locations"); 27564d9f161SPatrick Pelletier /* Ask OpenSSL to verify the server certificate. Note that this 27664d9f161SPatrick Pelletier * does NOT include verifying that the hostname is correct. 27764d9f161SPatrick Pelletier * So, by itself, this means anyone with any legitimate 27864d9f161SPatrick Pelletier * CA-issued certificate for any website, can impersonate any 27964d9f161SPatrick Pelletier * other website in the world. This is not good. See "The 28064d9f161SPatrick Pelletier * Most Dangerous Code in the World" article at 28164d9f161SPatrick Pelletier * https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html 28264d9f161SPatrick Pelletier */ 283aacd674cSPatrick Pelletier SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); 28464d9f161SPatrick Pelletier /* This is how we solve the problem mentioned in the previous 28564d9f161SPatrick Pelletier * comment. We "wrap" OpenSSL's validation routine in our 28664d9f161SPatrick Pelletier * own routine, which also validates the hostname by calling 28764d9f161SPatrick Pelletier * the code provided by iSECPartners. Note that even though 28864d9f161SPatrick Pelletier * the "Everything You've Always Wanted to Know About 28964d9f161SPatrick Pelletier * Certificate Validation With OpenSSL (But Were Afraid to 29064d9f161SPatrick Pelletier * Ask)" paper from iSECPartners says very explicitly not to 29164d9f161SPatrick Pelletier * call SSL_CTX_set_cert_verify_callback (at the bottom of 29264d9f161SPatrick Pelletier * page 2), what we're doing here is safe because our 29364d9f161SPatrick Pelletier * cert_verify_callback() calls X509_verify_cert(), which is 29464d9f161SPatrick Pelletier * OpenSSL's built-in routine which would have been called if 29564d9f161SPatrick Pelletier * we hadn't set the callback. Therefore, we're just 29664d9f161SPatrick Pelletier * "wrapping" OpenSSL's routine, not replacing it. */ 29764d9f161SPatrick Pelletier SSL_CTX_set_cert_verify_callback (ssl_ctx, cert_verify_callback, 29864d9f161SPatrick Pelletier (void *) host); 29919222e52SJoakim Soderberg #endif // not WIN32 300aacd674cSPatrick Pelletier 301be46c99bSCatalin Patulea // Create event base 302be46c99bSCatalin Patulea base = event_base_new(); 303be46c99bSCatalin Patulea if (!base) { 304be46c99bSCatalin Patulea perror("event_base_new()"); 305be46c99bSCatalin Patulea return 1; 306be46c99bSCatalin Patulea } 307be46c99bSCatalin Patulea 308be46c99bSCatalin Patulea // Create OpenSSL bufferevent and stack evhttp on top of it 309be46c99bSCatalin Patulea ssl = SSL_new(ssl_ctx); 310be46c99bSCatalin Patulea if (ssl == NULL) { 3115754d96aSPatrick Pelletier die_openssl("SSL_new()"); 312be46c99bSCatalin Patulea } 313be46c99bSCatalin Patulea 314be46c99bSCatalin Patulea if (strcasecmp(scheme, "http") == 0) { 315be46c99bSCatalin Patulea bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); 316be46c99bSCatalin Patulea } else { 317be46c99bSCatalin Patulea bev = bufferevent_openssl_socket_new(base, -1, ssl, 318be46c99bSCatalin Patulea BUFFEREVENT_SSL_CONNECTING, 319be46c99bSCatalin Patulea BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); 320be46c99bSCatalin Patulea } 321be46c99bSCatalin Patulea 322be46c99bSCatalin Patulea if (bev == NULL) { 323be46c99bSCatalin Patulea fprintf(stderr, "bufferevent_openssl_socket_new() failed\n"); 324be46c99bSCatalin Patulea return 1; 325be46c99bSCatalin Patulea } 326be46c99bSCatalin Patulea 327be46c99bSCatalin Patulea bufferevent_openssl_set_allow_dirty_shutdown(bev, 1); 328be46c99bSCatalin Patulea 329be46c99bSCatalin Patulea // For simplicity, we let DNS resolution block. Everything else should be 330be46c99bSCatalin Patulea // asynchronous though. 331be46c99bSCatalin Patulea evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev, 332be46c99bSCatalin Patulea host, port); 333be46c99bSCatalin Patulea if (evcon == NULL) { 334be46c99bSCatalin Patulea fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n"); 335be46c99bSCatalin Patulea return 1; 336be46c99bSCatalin Patulea } 337be46c99bSCatalin Patulea 338be46c99bSCatalin Patulea // Fire off the request 3395754d96aSPatrick Pelletier req = evhttp_request_new(http_request_done, bev); 340be46c99bSCatalin Patulea if (req == NULL) { 341be46c99bSCatalin Patulea fprintf(stderr, "evhttp_request_new() failed\n"); 342be46c99bSCatalin Patulea return 1; 343be46c99bSCatalin Patulea } 344be46c99bSCatalin Patulea 3458a90a850SNick Mathewson output_headers = evhttp_request_get_output_headers(req); 3468a90a850SNick Mathewson evhttp_add_header(output_headers, "Host", host); 3478a90a850SNick Mathewson evhttp_add_header(output_headers, "Connection", "close"); 348be46c99bSCatalin Patulea 349be46c99bSCatalin Patulea r = evhttp_make_request(evcon, req, EVHTTP_REQ_GET, uri); 350be46c99bSCatalin Patulea if (r != 0) { 351be46c99bSCatalin Patulea fprintf(stderr, "evhttp_make_request() failed\n"); 352be46c99bSCatalin Patulea return 1; 353be46c99bSCatalin Patulea } 354be46c99bSCatalin Patulea 355be46c99bSCatalin Patulea event_base_dispatch(base); 356be46c99bSCatalin Patulea 357be46c99bSCatalin Patulea evhttp_connection_free(evcon); 358be46c99bSCatalin Patulea event_base_free(base); 359be46c99bSCatalin Patulea 36019222e52SJoakim Soderberg #ifdef WIN32 36119222e52SJoakim Soderberg WSACleanup(); 36219222e52SJoakim Soderberg #endif 36319222e52SJoakim Soderberg 364be46c99bSCatalin Patulea return 0; 365be46c99bSCatalin Patulea } 366