126fd3d56SCy Schubert /*
226fd3d56SCy Schubert This is an example of how to hook up evhttp with bufferevent_ssl
326fd3d56SCy Schubert
426fd3d56SCy Schubert It just GETs an https URL given on the command-line and prints the response
526fd3d56SCy Schubert body to stdout.
626fd3d56SCy Schubert
726fd3d56SCy Schubert Actually, it also accepts plain http URLs to make it easy to compare http vs
826fd3d56SCy Schubert https code paths.
926fd3d56SCy Schubert
1026fd3d56SCy Schubert Loosely based on le-proxy.c.
1126fd3d56SCy Schubert */
1226fd3d56SCy Schubert
1326fd3d56SCy Schubert // Get rid of OSX 10.7 and greater deprecation warnings.
1426fd3d56SCy Schubert #if defined(__APPLE__) && defined(__clang__)
1526fd3d56SCy Schubert #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1626fd3d56SCy Schubert #endif
1726fd3d56SCy Schubert
1826fd3d56SCy Schubert #include <stdio.h>
1926fd3d56SCy Schubert #include <assert.h>
2026fd3d56SCy Schubert #include <stdlib.h>
2126fd3d56SCy Schubert #include <string.h>
2226fd3d56SCy Schubert #include <errno.h>
2326fd3d56SCy Schubert
2426fd3d56SCy Schubert #ifdef _WIN32
2526fd3d56SCy Schubert #include <winsock2.h>
2626fd3d56SCy Schubert #include <ws2tcpip.h>
2726fd3d56SCy Schubert
2826fd3d56SCy Schubert #define snprintf _snprintf
2926fd3d56SCy Schubert #define strcasecmp _stricmp
3026fd3d56SCy Schubert #else
3126fd3d56SCy Schubert #include <sys/socket.h>
3226fd3d56SCy Schubert #include <netinet/in.h>
3326fd3d56SCy Schubert #endif
3426fd3d56SCy Schubert
3526fd3d56SCy Schubert #include <event2/bufferevent_ssl.h>
3626fd3d56SCy Schubert #include <event2/bufferevent.h>
3726fd3d56SCy Schubert #include <event2/buffer.h>
3826fd3d56SCy Schubert #include <event2/listener.h>
3926fd3d56SCy Schubert #include <event2/util.h>
4026fd3d56SCy Schubert #include <event2/http.h>
4126fd3d56SCy Schubert
4226fd3d56SCy Schubert #include <openssl/ssl.h>
4326fd3d56SCy Schubert #include <openssl/err.h>
4426fd3d56SCy Schubert #include <openssl/rand.h>
4526fd3d56SCy Schubert
4626fd3d56SCy Schubert #include "openssl_hostname_validation.h"
4726fd3d56SCy Schubert
4826fd3d56SCy Schubert static int ignore_cert = 0;
4926fd3d56SCy Schubert
5026fd3d56SCy Schubert static void
http_request_done(struct evhttp_request * req,void * ctx)5126fd3d56SCy Schubert http_request_done(struct evhttp_request *req, void *ctx)
5226fd3d56SCy Schubert {
5326fd3d56SCy Schubert char buffer[256];
5426fd3d56SCy Schubert int nread;
5526fd3d56SCy Schubert
5626fd3d56SCy Schubert if (!req || !evhttp_request_get_response_code(req)) {
5726fd3d56SCy Schubert /* If req is NULL, it means an error occurred, but
5826fd3d56SCy Schubert * sadly we are mostly left guessing what the error
5926fd3d56SCy Schubert * might have been. We'll do our best... */
6026fd3d56SCy Schubert struct bufferevent *bev = (struct bufferevent *) ctx;
6126fd3d56SCy Schubert unsigned long oslerr;
6226fd3d56SCy Schubert int printed_err = 0;
6326fd3d56SCy Schubert int errcode = EVUTIL_SOCKET_ERROR();
6426fd3d56SCy Schubert fprintf(stderr, "some request failed - no idea which one though!\n");
6526fd3d56SCy Schubert /* Print out the OpenSSL error queue that libevent
6626fd3d56SCy Schubert * squirreled away for us, if any. */
6726fd3d56SCy Schubert while ((oslerr = bufferevent_get_openssl_error(bev))) {
6826fd3d56SCy Schubert ERR_error_string_n(oslerr, buffer, sizeof(buffer));
6926fd3d56SCy Schubert fprintf(stderr, "%s\n", buffer);
7026fd3d56SCy Schubert printed_err = 1;
7126fd3d56SCy Schubert }
7226fd3d56SCy Schubert /* If the OpenSSL error queue was empty, maybe it was a
7326fd3d56SCy Schubert * socket error; let's try printing that. */
7426fd3d56SCy Schubert if (! printed_err)
7526fd3d56SCy Schubert fprintf(stderr, "socket error = %s (%d)\n",
7626fd3d56SCy Schubert evutil_socket_error_to_string(errcode),
7726fd3d56SCy Schubert errcode);
7826fd3d56SCy Schubert return;
7926fd3d56SCy Schubert }
8026fd3d56SCy Schubert
8126fd3d56SCy Schubert fprintf(stderr, "Response line: %d %s\n",
8226fd3d56SCy Schubert evhttp_request_get_response_code(req),
8326fd3d56SCy Schubert evhttp_request_get_response_code_line(req));
8426fd3d56SCy Schubert
8526fd3d56SCy Schubert while ((nread = evbuffer_remove(evhttp_request_get_input_buffer(req),
8626fd3d56SCy Schubert buffer, sizeof(buffer)))
8726fd3d56SCy Schubert > 0) {
8826fd3d56SCy Schubert /* These are just arbitrary chunks of 256 bytes.
8926fd3d56SCy Schubert * They are not lines, so we can't treat them as such. */
9026fd3d56SCy Schubert fwrite(buffer, nread, 1, stdout);
9126fd3d56SCy Schubert }
9226fd3d56SCy Schubert }
9326fd3d56SCy Schubert
9426fd3d56SCy Schubert static void
syntax(void)9526fd3d56SCy Schubert syntax(void)
9626fd3d56SCy Schubert {
9726fd3d56SCy Schubert fputs("Syntax:\n", stderr);
9826fd3d56SCy Schubert fputs(" https-client -url <https-url> [-data data-file.bin] [-ignore-cert] [-retries num] [-timeout sec] [-crt crt]\n", stderr);
9926fd3d56SCy Schubert fputs("Example:\n", stderr);
10026fd3d56SCy Schubert fputs(" https-client -url https://ip.appspot.com/\n", stderr);
10126fd3d56SCy Schubert }
10226fd3d56SCy Schubert
10326fd3d56SCy Schubert static void
err(const char * msg)10426fd3d56SCy Schubert err(const char *msg)
10526fd3d56SCy Schubert {
10626fd3d56SCy Schubert fputs(msg, stderr);
10726fd3d56SCy Schubert }
10826fd3d56SCy Schubert
10926fd3d56SCy Schubert static void
err_openssl(const char * func)11026fd3d56SCy Schubert err_openssl(const char *func)
11126fd3d56SCy Schubert {
11226fd3d56SCy Schubert fprintf (stderr, "%s failed:\n", func);
11326fd3d56SCy Schubert
11426fd3d56SCy Schubert /* This is the OpenSSL function that prints the contents of the
11526fd3d56SCy Schubert * error stack to the specified file handle. */
11626fd3d56SCy Schubert ERR_print_errors_fp (stderr);
11726fd3d56SCy Schubert
11826fd3d56SCy Schubert exit(1);
11926fd3d56SCy Schubert }
12026fd3d56SCy Schubert
12126fd3d56SCy Schubert /* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */
cert_verify_callback(X509_STORE_CTX * x509_ctx,void * arg)12226fd3d56SCy Schubert static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg)
12326fd3d56SCy Schubert {
12426fd3d56SCy Schubert char cert_str[256];
12526fd3d56SCy Schubert const char *host = (const char *) arg;
12626fd3d56SCy Schubert const char *res_str = "X509_verify_cert failed";
12726fd3d56SCy Schubert HostnameValidationResult res = Error;
12826fd3d56SCy Schubert
12926fd3d56SCy Schubert /* This is the function that OpenSSL would call if we hadn't called
13026fd3d56SCy Schubert * SSL_CTX_set_cert_verify_callback(). Therefore, we are "wrapping"
13126fd3d56SCy Schubert * the default functionality, rather than replacing it. */
13226fd3d56SCy Schubert int ok_so_far = 0;
13326fd3d56SCy Schubert
13426fd3d56SCy Schubert X509 *server_cert = NULL;
13526fd3d56SCy Schubert
13626fd3d56SCy Schubert if (ignore_cert) {
13726fd3d56SCy Schubert return 1;
13826fd3d56SCy Schubert }
13926fd3d56SCy Schubert
14026fd3d56SCy Schubert ok_so_far = X509_verify_cert(x509_ctx);
14126fd3d56SCy Schubert
14226fd3d56SCy Schubert server_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
14326fd3d56SCy Schubert
14426fd3d56SCy Schubert if (ok_so_far) {
14526fd3d56SCy Schubert res = validate_hostname(host, server_cert);
14626fd3d56SCy Schubert
14726fd3d56SCy Schubert switch (res) {
14826fd3d56SCy Schubert case MatchFound:
14926fd3d56SCy Schubert res_str = "MatchFound";
15026fd3d56SCy Schubert break;
15126fd3d56SCy Schubert case MatchNotFound:
15226fd3d56SCy Schubert res_str = "MatchNotFound";
15326fd3d56SCy Schubert break;
15426fd3d56SCy Schubert case NoSANPresent:
15526fd3d56SCy Schubert res_str = "NoSANPresent";
15626fd3d56SCy Schubert break;
15726fd3d56SCy Schubert case MalformedCertificate:
15826fd3d56SCy Schubert res_str = "MalformedCertificate";
15926fd3d56SCy Schubert break;
16026fd3d56SCy Schubert case Error:
16126fd3d56SCy Schubert res_str = "Error";
16226fd3d56SCy Schubert break;
16326fd3d56SCy Schubert default:
16426fd3d56SCy Schubert res_str = "WTF!";
16526fd3d56SCy Schubert break;
16626fd3d56SCy Schubert }
16726fd3d56SCy Schubert }
16826fd3d56SCy Schubert
16926fd3d56SCy Schubert X509_NAME_oneline(X509_get_subject_name (server_cert),
17026fd3d56SCy Schubert cert_str, sizeof (cert_str));
17126fd3d56SCy Schubert
17226fd3d56SCy Schubert if (res == MatchFound) {
17326fd3d56SCy Schubert printf("https server '%s' has this certificate, "
17426fd3d56SCy Schubert "which looks good to me:\n%s\n",
17526fd3d56SCy Schubert host, cert_str);
17626fd3d56SCy Schubert return 1;
17726fd3d56SCy Schubert } else {
17826fd3d56SCy Schubert printf("Got '%s' for hostname '%s' and certificate:\n%s\n",
17926fd3d56SCy Schubert res_str, host, cert_str);
18026fd3d56SCy Schubert return 0;
18126fd3d56SCy Schubert }
18226fd3d56SCy Schubert }
18326fd3d56SCy Schubert
18426fd3d56SCy Schubert #ifdef _WIN32
18526fd3d56SCy Schubert static int
add_cert_for_store(X509_STORE * store,const char * name)18626fd3d56SCy Schubert add_cert_for_store(X509_STORE *store, const char *name)
18726fd3d56SCy Schubert {
18826fd3d56SCy Schubert HCERTSTORE sys_store = NULL;
18926fd3d56SCy Schubert PCCERT_CONTEXT ctx = NULL;
19026fd3d56SCy Schubert int r = 0;
19126fd3d56SCy Schubert
19226fd3d56SCy Schubert sys_store = CertOpenSystemStore(0, name);
19326fd3d56SCy Schubert if (!sys_store) {
19426fd3d56SCy Schubert err("failed to open system certificate store");
19526fd3d56SCy Schubert return -1;
19626fd3d56SCy Schubert }
19726fd3d56SCy Schubert while ((ctx = CertEnumCertificatesInStore(sys_store, ctx))) {
19826fd3d56SCy Schubert X509 *x509 = d2i_X509(NULL, (unsigned char const **)&ctx->pbCertEncoded,
19926fd3d56SCy Schubert ctx->cbCertEncoded);
20026fd3d56SCy Schubert if (x509) {
20126fd3d56SCy Schubert X509_STORE_add_cert(store, x509);
20226fd3d56SCy Schubert X509_free(x509);
20326fd3d56SCy Schubert } else {
20426fd3d56SCy Schubert r = -1;
20526fd3d56SCy Schubert err_openssl("d2i_X509");
20626fd3d56SCy Schubert break;
20726fd3d56SCy Schubert }
20826fd3d56SCy Schubert }
20926fd3d56SCy Schubert CertCloseStore(sys_store, 0);
21026fd3d56SCy Schubert return r;
21126fd3d56SCy Schubert }
21226fd3d56SCy Schubert #endif
21326fd3d56SCy Schubert
21426fd3d56SCy Schubert int
main(int argc,char ** argv)21526fd3d56SCy Schubert main(int argc, char **argv)
21626fd3d56SCy Schubert {
21726fd3d56SCy Schubert int r;
21826fd3d56SCy Schubert struct event_base *base = NULL;
21926fd3d56SCy Schubert struct evhttp_uri *http_uri = NULL;
22026fd3d56SCy Schubert const char *url = NULL, *data_file = NULL;
22126fd3d56SCy Schubert const char *crt = NULL;
22226fd3d56SCy Schubert const char *scheme, *host, *path, *query;
22326fd3d56SCy Schubert char uri[256];
22426fd3d56SCy Schubert int port;
22526fd3d56SCy Schubert int retries = 0;
22626fd3d56SCy Schubert int timeout = -1;
22726fd3d56SCy Schubert
22826fd3d56SCy Schubert SSL_CTX *ssl_ctx = NULL;
22926fd3d56SCy Schubert SSL *ssl = NULL;
23026fd3d56SCy Schubert struct bufferevent *bev;
23126fd3d56SCy Schubert struct evhttp_connection *evcon = NULL;
23226fd3d56SCy Schubert struct evhttp_request *req;
23326fd3d56SCy Schubert struct evkeyvalq *output_headers;
23426fd3d56SCy Schubert struct evbuffer *output_buffer;
23526fd3d56SCy Schubert
23626fd3d56SCy Schubert int i;
23726fd3d56SCy Schubert int ret = 0;
23826fd3d56SCy Schubert enum { HTTP, HTTPS } type = HTTP;
23926fd3d56SCy Schubert
24026fd3d56SCy Schubert for (i = 1; i < argc; i++) {
24126fd3d56SCy Schubert if (!strcmp("-url", argv[i])) {
24226fd3d56SCy Schubert if (i < argc - 1) {
24326fd3d56SCy Schubert url = argv[i + 1];
24426fd3d56SCy Schubert } else {
24526fd3d56SCy Schubert syntax();
24626fd3d56SCy Schubert goto error;
24726fd3d56SCy Schubert }
24826fd3d56SCy Schubert } else if (!strcmp("-crt", argv[i])) {
24926fd3d56SCy Schubert if (i < argc - 1) {
25026fd3d56SCy Schubert crt = argv[i + 1];
25126fd3d56SCy Schubert } else {
25226fd3d56SCy Schubert syntax();
25326fd3d56SCy Schubert goto error;
25426fd3d56SCy Schubert }
25526fd3d56SCy Schubert } else if (!strcmp("-ignore-cert", argv[i])) {
25626fd3d56SCy Schubert ignore_cert = 1;
25726fd3d56SCy Schubert } else if (!strcmp("-data", argv[i])) {
25826fd3d56SCy Schubert if (i < argc - 1) {
25926fd3d56SCy Schubert data_file = argv[i + 1];
26026fd3d56SCy Schubert } else {
26126fd3d56SCy Schubert syntax();
26226fd3d56SCy Schubert goto error;
26326fd3d56SCy Schubert }
26426fd3d56SCy Schubert } else if (!strcmp("-retries", argv[i])) {
26526fd3d56SCy Schubert if (i < argc - 1) {
26626fd3d56SCy Schubert retries = atoi(argv[i + 1]);
26726fd3d56SCy Schubert } else {
26826fd3d56SCy Schubert syntax();
26926fd3d56SCy Schubert goto error;
27026fd3d56SCy Schubert }
27126fd3d56SCy Schubert } else if (!strcmp("-timeout", argv[i])) {
27226fd3d56SCy Schubert if (i < argc - 1) {
27326fd3d56SCy Schubert timeout = atoi(argv[i + 1]);
27426fd3d56SCy Schubert } else {
27526fd3d56SCy Schubert syntax();
27626fd3d56SCy Schubert goto error;
27726fd3d56SCy Schubert }
27826fd3d56SCy Schubert } else if (!strcmp("-help", argv[i])) {
27926fd3d56SCy Schubert syntax();
28026fd3d56SCy Schubert goto error;
28126fd3d56SCy Schubert }
28226fd3d56SCy Schubert }
28326fd3d56SCy Schubert
28426fd3d56SCy Schubert if (!url) {
28526fd3d56SCy Schubert syntax();
28626fd3d56SCy Schubert goto error;
28726fd3d56SCy Schubert }
28826fd3d56SCy Schubert
28926fd3d56SCy Schubert #ifdef _WIN32
29026fd3d56SCy Schubert {
29126fd3d56SCy Schubert WORD wVersionRequested;
29226fd3d56SCy Schubert WSADATA wsaData;
29326fd3d56SCy Schubert int err;
29426fd3d56SCy Schubert
29526fd3d56SCy Schubert wVersionRequested = MAKEWORD(2, 2);
29626fd3d56SCy Schubert
29726fd3d56SCy Schubert err = WSAStartup(wVersionRequested, &wsaData);
29826fd3d56SCy Schubert if (err != 0) {
29926fd3d56SCy Schubert printf("WSAStartup failed with error: %d\n", err);
30026fd3d56SCy Schubert goto error;
30126fd3d56SCy Schubert }
30226fd3d56SCy Schubert }
30326fd3d56SCy Schubert #endif // _WIN32
30426fd3d56SCy Schubert
30526fd3d56SCy Schubert http_uri = evhttp_uri_parse(url);
30626fd3d56SCy Schubert if (http_uri == NULL) {
30726fd3d56SCy Schubert err("malformed url");
30826fd3d56SCy Schubert goto error;
30926fd3d56SCy Schubert }
31026fd3d56SCy Schubert
31126fd3d56SCy Schubert scheme = evhttp_uri_get_scheme(http_uri);
31226fd3d56SCy Schubert if (scheme == NULL || (strcasecmp(scheme, "https") != 0 &&
31326fd3d56SCy Schubert strcasecmp(scheme, "http") != 0)) {
31426fd3d56SCy Schubert err("url must be http or https");
31526fd3d56SCy Schubert goto error;
31626fd3d56SCy Schubert }
31726fd3d56SCy Schubert
31826fd3d56SCy Schubert host = evhttp_uri_get_host(http_uri);
31926fd3d56SCy Schubert if (host == NULL) {
32026fd3d56SCy Schubert err("url must have a host");
32126fd3d56SCy Schubert goto error;
32226fd3d56SCy Schubert }
32326fd3d56SCy Schubert
32426fd3d56SCy Schubert port = evhttp_uri_get_port(http_uri);
32526fd3d56SCy Schubert if (port == -1) {
32626fd3d56SCy Schubert port = (strcasecmp(scheme, "http") == 0) ? 80 : 443;
32726fd3d56SCy Schubert }
32826fd3d56SCy Schubert
32926fd3d56SCy Schubert path = evhttp_uri_get_path(http_uri);
33026fd3d56SCy Schubert if (strlen(path) == 0) {
33126fd3d56SCy Schubert path = "/";
33226fd3d56SCy Schubert }
33326fd3d56SCy Schubert
33426fd3d56SCy Schubert query = evhttp_uri_get_query(http_uri);
33526fd3d56SCy Schubert if (query == NULL) {
33626fd3d56SCy Schubert snprintf(uri, sizeof(uri) - 1, "%s", path);
33726fd3d56SCy Schubert } else {
33826fd3d56SCy Schubert snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query);
33926fd3d56SCy Schubert }
34026fd3d56SCy Schubert uri[sizeof(uri) - 1] = '\0';
34126fd3d56SCy Schubert
34226fd3d56SCy Schubert #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
34326fd3d56SCy Schubert (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
34426fd3d56SCy Schubert // Initialize OpenSSL
34526fd3d56SCy Schubert SSL_library_init();
34626fd3d56SCy Schubert ERR_load_crypto_strings();
34726fd3d56SCy Schubert SSL_load_error_strings();
34826fd3d56SCy Schubert OpenSSL_add_all_algorithms();
34926fd3d56SCy Schubert #endif
35026fd3d56SCy Schubert
35126fd3d56SCy Schubert /* This isn't strictly necessary... OpenSSL performs RAND_poll
35226fd3d56SCy Schubert * automatically on first use of random number generator. */
35326fd3d56SCy Schubert r = RAND_poll();
35426fd3d56SCy Schubert if (r == 0) {
35526fd3d56SCy Schubert err_openssl("RAND_poll");
35626fd3d56SCy Schubert goto error;
35726fd3d56SCy Schubert }
35826fd3d56SCy Schubert
35926fd3d56SCy Schubert /* Create a new OpenSSL context */
36026fd3d56SCy Schubert ssl_ctx = SSL_CTX_new(SSLv23_method());
36126fd3d56SCy Schubert if (!ssl_ctx) {
36226fd3d56SCy Schubert err_openssl("SSL_CTX_new");
36326fd3d56SCy Schubert goto error;
36426fd3d56SCy Schubert }
36526fd3d56SCy Schubert
36626fd3d56SCy Schubert if (crt == NULL) {
36726fd3d56SCy Schubert X509_STORE *store;
36826fd3d56SCy Schubert /* Attempt to use the system's trusted root certificates. */
36926fd3d56SCy Schubert store = SSL_CTX_get_cert_store(ssl_ctx);
37026fd3d56SCy Schubert #ifdef _WIN32
37126fd3d56SCy Schubert if (add_cert_for_store(store, "CA") < 0 ||
37226fd3d56SCy Schubert add_cert_for_store(store, "AuthRoot") < 0 ||
37326fd3d56SCy Schubert add_cert_for_store(store, "ROOT") < 0) {
37426fd3d56SCy Schubert goto error;
37526fd3d56SCy Schubert }
37626fd3d56SCy Schubert #else // _WIN32
37726fd3d56SCy Schubert if (X509_STORE_set_default_paths(store) != 1) {
37826fd3d56SCy Schubert err_openssl("X509_STORE_set_default_paths");
37926fd3d56SCy Schubert goto error;
38026fd3d56SCy Schubert }
38126fd3d56SCy Schubert #endif // _WIN32
38226fd3d56SCy Schubert } else {
38326fd3d56SCy Schubert if (SSL_CTX_load_verify_locations(ssl_ctx, crt, NULL) != 1) {
38426fd3d56SCy Schubert err_openssl("SSL_CTX_load_verify_locations");
38526fd3d56SCy Schubert goto error;
38626fd3d56SCy Schubert }
38726fd3d56SCy Schubert }
38826fd3d56SCy Schubert /* Ask OpenSSL to verify the server certificate. Note that this
38926fd3d56SCy Schubert * does NOT include verifying that the hostname is correct.
39026fd3d56SCy Schubert * So, by itself, this means anyone with any legitimate
39126fd3d56SCy Schubert * CA-issued certificate for any website, can impersonate any
39226fd3d56SCy Schubert * other website in the world. This is not good. See "The
39326fd3d56SCy Schubert * Most Dangerous Code in the World" article at
39426fd3d56SCy Schubert * https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html
39526fd3d56SCy Schubert */
39626fd3d56SCy Schubert SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
39726fd3d56SCy Schubert /* This is how we solve the problem mentioned in the previous
39826fd3d56SCy Schubert * comment. We "wrap" OpenSSL's validation routine in our
39926fd3d56SCy Schubert * own routine, which also validates the hostname by calling
40026fd3d56SCy Schubert * the code provided by iSECPartners. Note that even though
40126fd3d56SCy Schubert * the "Everything You've Always Wanted to Know About
40226fd3d56SCy Schubert * Certificate Validation With OpenSSL (But Were Afraid to
40326fd3d56SCy Schubert * Ask)" paper from iSECPartners says very explicitly not to
40426fd3d56SCy Schubert * call SSL_CTX_set_cert_verify_callback (at the bottom of
40526fd3d56SCy Schubert * page 2), what we're doing here is safe because our
40626fd3d56SCy Schubert * cert_verify_callback() calls X509_verify_cert(), which is
40726fd3d56SCy Schubert * OpenSSL's built-in routine which would have been called if
40826fd3d56SCy Schubert * we hadn't set the callback. Therefore, we're just
40926fd3d56SCy Schubert * "wrapping" OpenSSL's routine, not replacing it. */
41026fd3d56SCy Schubert SSL_CTX_set_cert_verify_callback(ssl_ctx, cert_verify_callback,
41126fd3d56SCy Schubert (void *) host);
41226fd3d56SCy Schubert
41326fd3d56SCy Schubert // Create event base
41426fd3d56SCy Schubert base = event_base_new();
41526fd3d56SCy Schubert if (!base) {
41626fd3d56SCy Schubert perror("event_base_new()");
41726fd3d56SCy Schubert goto error;
41826fd3d56SCy Schubert }
41926fd3d56SCy Schubert
42026fd3d56SCy Schubert // Create OpenSSL bufferevent and stack evhttp on top of it
42126fd3d56SCy Schubert ssl = SSL_new(ssl_ctx);
42226fd3d56SCy Schubert if (ssl == NULL) {
42326fd3d56SCy Schubert err_openssl("SSL_new()");
42426fd3d56SCy Schubert goto error;
42526fd3d56SCy Schubert }
42626fd3d56SCy Schubert
42726fd3d56SCy Schubert #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
42826fd3d56SCy Schubert // Set hostname for SNI extension
42926fd3d56SCy Schubert SSL_set_tlsext_host_name(ssl, host);
43026fd3d56SCy Schubert #endif
43126fd3d56SCy Schubert
43226fd3d56SCy Schubert if (strcasecmp(scheme, "http") == 0) {
43326fd3d56SCy Schubert bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
43426fd3d56SCy Schubert } else {
43526fd3d56SCy Schubert type = HTTPS;
43626fd3d56SCy Schubert bev = bufferevent_openssl_socket_new(base, -1, ssl,
43726fd3d56SCy Schubert BUFFEREVENT_SSL_CONNECTING,
43826fd3d56SCy Schubert BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
43926fd3d56SCy Schubert }
44026fd3d56SCy Schubert
44126fd3d56SCy Schubert if (bev == NULL) {
44226fd3d56SCy Schubert fprintf(stderr, "bufferevent_openssl_socket_new() failed\n");
44326fd3d56SCy Schubert goto error;
44426fd3d56SCy Schubert }
44526fd3d56SCy Schubert
44626fd3d56SCy Schubert bufferevent_openssl_set_allow_dirty_shutdown(bev, 1);
44726fd3d56SCy Schubert
44826fd3d56SCy Schubert // For simplicity, we let DNS resolution block. Everything else should be
44926fd3d56SCy Schubert // asynchronous though.
45026fd3d56SCy Schubert evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev,
45126fd3d56SCy Schubert host, port);
45226fd3d56SCy Schubert if (evcon == NULL) {
45326fd3d56SCy Schubert fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n");
45426fd3d56SCy Schubert goto error;
45526fd3d56SCy Schubert }
45626fd3d56SCy Schubert
45726fd3d56SCy Schubert if (retries > 0) {
45826fd3d56SCy Schubert evhttp_connection_set_retries(evcon, retries);
45926fd3d56SCy Schubert }
46026fd3d56SCy Schubert if (timeout >= 0) {
46126fd3d56SCy Schubert evhttp_connection_set_timeout(evcon, timeout);
46226fd3d56SCy Schubert }
46326fd3d56SCy Schubert
46426fd3d56SCy Schubert // Fire off the request
46526fd3d56SCy Schubert req = evhttp_request_new(http_request_done, bev);
46626fd3d56SCy Schubert if (req == NULL) {
46726fd3d56SCy Schubert fprintf(stderr, "evhttp_request_new() failed\n");
46826fd3d56SCy Schubert goto error;
46926fd3d56SCy Schubert }
47026fd3d56SCy Schubert
47126fd3d56SCy Schubert output_headers = evhttp_request_get_output_headers(req);
47226fd3d56SCy Schubert evhttp_add_header(output_headers, "Host", host);
47326fd3d56SCy Schubert evhttp_add_header(output_headers, "Connection", "close");
47426fd3d56SCy Schubert
47526fd3d56SCy Schubert if (data_file) {
47626fd3d56SCy Schubert /* NOTE: In production code, you'd probably want to use
47726fd3d56SCy Schubert * evbuffer_add_file() or evbuffer_add_file_segment(), to
47826fd3d56SCy Schubert * avoid needless copying. */
47926fd3d56SCy Schubert FILE * f = fopen(data_file, "rb");
48026fd3d56SCy Schubert char buf[1024];
48126fd3d56SCy Schubert size_t s;
48226fd3d56SCy Schubert size_t bytes = 0;
48326fd3d56SCy Schubert
48426fd3d56SCy Schubert if (!f) {
48526fd3d56SCy Schubert syntax();
48626fd3d56SCy Schubert goto error;
48726fd3d56SCy Schubert }
48826fd3d56SCy Schubert
48926fd3d56SCy Schubert output_buffer = evhttp_request_get_output_buffer(req);
49026fd3d56SCy Schubert while ((s = fread(buf, 1, sizeof(buf), f)) > 0) {
49126fd3d56SCy Schubert evbuffer_add(output_buffer, buf, s);
49226fd3d56SCy Schubert bytes += s;
49326fd3d56SCy Schubert }
49426fd3d56SCy Schubert evutil_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long)bytes);
49526fd3d56SCy Schubert evhttp_add_header(output_headers, "Content-Length", buf);
49626fd3d56SCy Schubert fclose(f);
49726fd3d56SCy Schubert }
49826fd3d56SCy Schubert
49926fd3d56SCy Schubert r = evhttp_make_request(evcon, req, data_file ? EVHTTP_REQ_POST : EVHTTP_REQ_GET, uri);
50026fd3d56SCy Schubert if (r != 0) {
50126fd3d56SCy Schubert fprintf(stderr, "evhttp_make_request() failed\n");
50226fd3d56SCy Schubert goto error;
50326fd3d56SCy Schubert }
50426fd3d56SCy Schubert
50526fd3d56SCy Schubert event_base_dispatch(base);
50626fd3d56SCy Schubert goto cleanup;
50726fd3d56SCy Schubert
50826fd3d56SCy Schubert error:
50926fd3d56SCy Schubert ret = 1;
51026fd3d56SCy Schubert cleanup:
51126fd3d56SCy Schubert if (evcon)
51226fd3d56SCy Schubert evhttp_connection_free(evcon);
51326fd3d56SCy Schubert if (http_uri)
51426fd3d56SCy Schubert evhttp_uri_free(http_uri);
51526fd3d56SCy Schubert if (base)
51626fd3d56SCy Schubert event_base_free(base);
51726fd3d56SCy Schubert
51826fd3d56SCy Schubert if (ssl_ctx)
51926fd3d56SCy Schubert SSL_CTX_free(ssl_ctx);
52026fd3d56SCy Schubert if (type == HTTP && ssl)
52126fd3d56SCy Schubert SSL_free(ssl);
52226fd3d56SCy Schubert #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
52326fd3d56SCy Schubert (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
52426fd3d56SCy Schubert EVP_cleanup();
52526fd3d56SCy Schubert ERR_free_strings();
52626fd3d56SCy Schubert
52726fd3d56SCy Schubert #if OPENSSL_VERSION_NUMBER < 0x10000000L
52826fd3d56SCy Schubert ERR_remove_state(0);
52926fd3d56SCy Schubert #else
53026fd3d56SCy Schubert ERR_remove_thread_state(NULL);
53126fd3d56SCy Schubert #endif
53226fd3d56SCy Schubert
53326fd3d56SCy Schubert CRYPTO_cleanup_all_ex_data();
53426fd3d56SCy Schubert
53526fd3d56SCy Schubert sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
53626fd3d56SCy Schubert #endif /* (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
53726fd3d56SCy Schubert (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) */
53826fd3d56SCy Schubert
53926fd3d56SCy Schubert #ifdef _WIN32
54026fd3d56SCy Schubert WSACleanup();
54126fd3d56SCy Schubert #endif
54226fd3d56SCy Schubert
54326fd3d56SCy Schubert return ret;
54426fd3d56SCy Schubert }
545