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
1369c3516bSJoakim Söderberg // Get rid of OSX 10.7 and greater deprecation warnings.
14d7be7887SJoakim Soderberg #if defined(__APPLE__) && defined(__clang__)
1569c3516bSJoakim Söderberg #pragma clang diagnostic ignored "-Wdeprecated-declarations"
160ef1d04eSJoakim Soderberg #endif
1769c3516bSJoakim Söderberg
18be46c99bSCatalin Patulea #include <stdio.h>
19be46c99bSCatalin Patulea #include <assert.h>
20be46c99bSCatalin Patulea #include <stdlib.h>
21be46c99bSCatalin Patulea #include <string.h>
22be46c99bSCatalin Patulea #include <errno.h>
23be46c99bSCatalin Patulea
2488ecda3bSNick Mathewson #ifdef _WIN32
25be46c99bSCatalin Patulea #include <winsock2.h>
26be46c99bSCatalin Patulea #include <ws2tcpip.h>
2719222e52SJoakim Soderberg
2819222e52SJoakim Soderberg #define snprintf _snprintf
2919222e52SJoakim Soderberg #define strcasecmp _stricmp
30be46c99bSCatalin Patulea #else
31be46c99bSCatalin Patulea #include <sys/socket.h>
32be46c99bSCatalin Patulea #include <netinet/in.h>
33be46c99bSCatalin Patulea #endif
34be46c99bSCatalin Patulea
35be46c99bSCatalin Patulea #include <event2/bufferevent_ssl.h>
36be46c99bSCatalin Patulea #include <event2/bufferevent.h>
37be46c99bSCatalin Patulea #include <event2/buffer.h>
38be46c99bSCatalin Patulea #include <event2/listener.h>
39be46c99bSCatalin Patulea #include <event2/util.h>
40be46c99bSCatalin Patulea #include <event2/http.h>
41be46c99bSCatalin Patulea
42be46c99bSCatalin Patulea #include <openssl/ssl.h>
43be46c99bSCatalin Patulea #include <openssl/err.h>
44be46c99bSCatalin Patulea #include <openssl/rand.h>
45be46c99bSCatalin Patulea
4664d9f161SPatrick Pelletier #include "openssl_hostname_validation.h"
4764d9f161SPatrick Pelletier
48c5887f73SAlexey Ozeritsky static int ignore_cert = 0;
49be46c99bSCatalin Patulea
50be46c99bSCatalin Patulea static void
http_request_done(struct evhttp_request * req,void * ctx)51be46c99bSCatalin Patulea http_request_done(struct evhttp_request *req, void *ctx)
52be46c99bSCatalin Patulea {
53be46c99bSCatalin Patulea char buffer[256];
54be46c99bSCatalin Patulea int nread;
55be46c99bSCatalin Patulea
56a8a04565Swenyg if (!req || !evhttp_request_get_response_code(req)) {
575754d96aSPatrick Pelletier /* If req is NULL, it means an error occurred, but
585754d96aSPatrick Pelletier * sadly we are mostly left guessing what the error
595754d96aSPatrick Pelletier * might have been. We'll do our best... */
605754d96aSPatrick Pelletier struct bufferevent *bev = (struct bufferevent *) ctx;
615754d96aSPatrick Pelletier unsigned long oslerr;
625754d96aSPatrick Pelletier int printed_err = 0;
635754d96aSPatrick Pelletier int errcode = EVUTIL_SOCKET_ERROR();
64be46c99bSCatalin Patulea fprintf(stderr, "some request failed - no idea which one though!\n");
655754d96aSPatrick Pelletier /* Print out the OpenSSL error queue that libevent
665754d96aSPatrick Pelletier * squirreled away for us, if any. */
675754d96aSPatrick Pelletier while ((oslerr = bufferevent_get_openssl_error(bev))) {
685754d96aSPatrick Pelletier ERR_error_string_n(oslerr, buffer, sizeof(buffer));
695754d96aSPatrick Pelletier fprintf(stderr, "%s\n", buffer);
705754d96aSPatrick Pelletier printed_err = 1;
715754d96aSPatrick Pelletier }
725754d96aSPatrick Pelletier /* If the OpenSSL error queue was empty, maybe it was a
735754d96aSPatrick Pelletier * socket error; let's try printing that. */
745754d96aSPatrick Pelletier if (! printed_err)
755754d96aSPatrick Pelletier fprintf(stderr, "socket error = %s (%d)\n",
765754d96aSPatrick Pelletier evutil_socket_error_to_string(errcode),
775754d96aSPatrick Pelletier errcode);
78be46c99bSCatalin Patulea return;
79be46c99bSCatalin Patulea }
80be46c99bSCatalin Patulea
81be46c99bSCatalin Patulea fprintf(stderr, "Response line: %d %s\n",
828a90a850SNick Mathewson evhttp_request_get_response_code(req),
838a90a850SNick Mathewson evhttp_request_get_response_code_line(req));
84be46c99bSCatalin Patulea
8595acdaa3SNick Mathewson while ((nread = evbuffer_remove(evhttp_request_get_input_buffer(req),
8695acdaa3SNick Mathewson buffer, sizeof(buffer)))
87be46c99bSCatalin Patulea > 0) {
8842d7441aSPatrick Pelletier /* These are just arbitrary chunks of 256 bytes.
8942d7441aSPatrick Pelletier * They are not lines, so we can't treat them as such. */
90be46c99bSCatalin Patulea fwrite(buffer, nread, 1, stdout);
91be46c99bSCatalin Patulea }
92be46c99bSCatalin Patulea }
93be46c99bSCatalin Patulea
94be46c99bSCatalin Patulea static void
syntax(void)95be46c99bSCatalin Patulea syntax(void)
96be46c99bSCatalin Patulea {
97be46c99bSCatalin Patulea fputs("Syntax:\n", stderr);
98fdf713a0SAzat Khuzhin fputs(" https-client -url <https-url> [-data data-file.bin] [-ignore-cert] [-retries num] [-timeout sec] [-crt crt]\n", stderr);
99be46c99bSCatalin Patulea fputs("Example:\n", stderr);
100c5887f73SAlexey Ozeritsky fputs(" https-client -url https://ip.appspot.com/\n", stderr);
101be46c99bSCatalin Patulea }
102be46c99bSCatalin Patulea
103be46c99bSCatalin Patulea static void
err(const char * msg)10424a1f25aSAzat Khuzhin err(const char *msg)
105be46c99bSCatalin Patulea {
106be46c99bSCatalin Patulea fputs(msg, stderr);
107be46c99bSCatalin Patulea }
108be46c99bSCatalin Patulea
1095754d96aSPatrick Pelletier static void
err_openssl(const char * func)11024a1f25aSAzat Khuzhin err_openssl(const char *func)
1115754d96aSPatrick Pelletier {
1125754d96aSPatrick Pelletier fprintf (stderr, "%s failed:\n", func);
1135754d96aSPatrick Pelletier
1145754d96aSPatrick Pelletier /* This is the OpenSSL function that prints the contents of the
1155754d96aSPatrick Pelletier * error stack to the specified file handle. */
1165754d96aSPatrick Pelletier ERR_print_errors_fp (stderr);
1175754d96aSPatrick Pelletier
1185754d96aSPatrick Pelletier exit(1);
1195754d96aSPatrick Pelletier }
1205754d96aSPatrick Pelletier
12164d9f161SPatrick Pelletier /* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */
cert_verify_callback(X509_STORE_CTX * x509_ctx,void * arg)12264d9f161SPatrick Pelletier static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg)
12364d9f161SPatrick Pelletier {
12464d9f161SPatrick Pelletier char cert_str[256];
12564d9f161SPatrick Pelletier const char *host = (const char *) arg;
12664d9f161SPatrick Pelletier const char *res_str = "X509_verify_cert failed";
12764d9f161SPatrick Pelletier HostnameValidationResult res = Error;
12864d9f161SPatrick Pelletier
12964d9f161SPatrick Pelletier /* This is the function that OpenSSL would call if we hadn't called
13064d9f161SPatrick Pelletier * SSL_CTX_set_cert_verify_callback(). Therefore, we are "wrapping"
13164d9f161SPatrick Pelletier * the default functionality, rather than replacing it. */
13229af65ebSAlexey Ozeritsky int ok_so_far = 0;
13364d9f161SPatrick Pelletier
13429af65ebSAlexey Ozeritsky X509 *server_cert = NULL;
13529af65ebSAlexey Ozeritsky
13629af65ebSAlexey Ozeritsky if (ignore_cert) {
13729af65ebSAlexey Ozeritsky return 1;
13829af65ebSAlexey Ozeritsky }
13929af65ebSAlexey Ozeritsky
14029af65ebSAlexey Ozeritsky ok_so_far = X509_verify_cert(x509_ctx);
14129af65ebSAlexey Ozeritsky
14229af65ebSAlexey Ozeritsky server_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
14364d9f161SPatrick Pelletier
14464d9f161SPatrick Pelletier if (ok_so_far) {
14564d9f161SPatrick Pelletier res = validate_hostname(host, server_cert);
14664d9f161SPatrick Pelletier
14764d9f161SPatrick Pelletier switch (res) {
14864d9f161SPatrick Pelletier case MatchFound:
14964d9f161SPatrick Pelletier res_str = "MatchFound";
15064d9f161SPatrick Pelletier break;
15164d9f161SPatrick Pelletier case MatchNotFound:
15264d9f161SPatrick Pelletier res_str = "MatchNotFound";
15364d9f161SPatrick Pelletier break;
15464d9f161SPatrick Pelletier case NoSANPresent:
15564d9f161SPatrick Pelletier res_str = "NoSANPresent";
15664d9f161SPatrick Pelletier break;
15764d9f161SPatrick Pelletier case MalformedCertificate:
15864d9f161SPatrick Pelletier res_str = "MalformedCertificate";
15964d9f161SPatrick Pelletier break;
16064d9f161SPatrick Pelletier case Error:
16164d9f161SPatrick Pelletier res_str = "Error";
16264d9f161SPatrick Pelletier break;
16364d9f161SPatrick Pelletier default:
16464d9f161SPatrick Pelletier res_str = "WTF!";
16564d9f161SPatrick Pelletier break;
16664d9f161SPatrick Pelletier }
16764d9f161SPatrick Pelletier }
16864d9f161SPatrick Pelletier
16964d9f161SPatrick Pelletier X509_NAME_oneline(X509_get_subject_name (server_cert),
17064d9f161SPatrick Pelletier cert_str, sizeof (cert_str));
17164d9f161SPatrick Pelletier
17264d9f161SPatrick Pelletier if (res == MatchFound) {
17364d9f161SPatrick Pelletier printf("https server '%s' has this certificate, "
17464d9f161SPatrick Pelletier "which looks good to me:\n%s\n",
17564d9f161SPatrick Pelletier host, cert_str);
17664d9f161SPatrick Pelletier return 1;
17764d9f161SPatrick Pelletier } else {
17864d9f161SPatrick Pelletier printf("Got '%s' for hostname '%s' and certificate:\n%s\n",
17964d9f161SPatrick Pelletier res_str, host, cert_str);
18064d9f161SPatrick Pelletier return 0;
18164d9f161SPatrick Pelletier }
18264d9f161SPatrick Pelletier }
183*e9478640Syuangongji
184*e9478640Syuangongji #ifdef _WIN32
185*e9478640Syuangongji static int
add_cert_for_store(X509_STORE * store,const char * name)186*e9478640Syuangongji add_cert_for_store(X509_STORE *store, const char *name)
187*e9478640Syuangongji {
188*e9478640Syuangongji HCERTSTORE sys_store = NULL;
189*e9478640Syuangongji PCCERT_CONTEXT ctx = NULL;
190*e9478640Syuangongji int r = 0;
191*e9478640Syuangongji
192*e9478640Syuangongji sys_store = CertOpenSystemStore(0, name);
193*e9478640Syuangongji if (!sys_store) {
194*e9478640Syuangongji err("failed to open system certificate store");
195*e9478640Syuangongji return -1;
196*e9478640Syuangongji }
197*e9478640Syuangongji while ((ctx = CertEnumCertificatesInStore(sys_store, ctx))) {
198*e9478640Syuangongji X509 *x509 = d2i_X509(NULL, (unsigned char const **)&ctx->pbCertEncoded,
199*e9478640Syuangongji ctx->cbCertEncoded);
200*e9478640Syuangongji if (x509) {
201*e9478640Syuangongji X509_STORE_add_cert(store, x509);
202*e9478640Syuangongji X509_free(x509);
203*e9478640Syuangongji } else {
204*e9478640Syuangongji r = -1;
205*e9478640Syuangongji err_openssl("d2i_X509");
206*e9478640Syuangongji break;
207*e9478640Syuangongji }
208*e9478640Syuangongji }
209*e9478640Syuangongji CertCloseStore(sys_store, 0);
210*e9478640Syuangongji return r;
211*e9478640Syuangongji }
21213a4acdaSAzat Khuzhin #endif
21364d9f161SPatrick Pelletier
214be46c99bSCatalin Patulea int
main(int argc,char ** argv)215be46c99bSCatalin Patulea main(int argc, char **argv)
216be46c99bSCatalin Patulea {
217be46c99bSCatalin Patulea int r;
218c0adad8fSAzat Khuzhin struct event_base *base = NULL;
21924a1f25aSAzat Khuzhin struct evhttp_uri *http_uri = NULL;
22090786eb0SNick Mathewson const char *url = NULL, *data_file = NULL;
2215c0132f3SDavid Disseldorp const char *crt = NULL;
22290786eb0SNick Mathewson const char *scheme, *host, *path, *query;
223be46c99bSCatalin Patulea char uri[256];
224be46c99bSCatalin Patulea int port;
225d9da8443SAzat Khuzhin int retries = 0;
2264637aa88SAzat Khuzhin int timeout = -1;
227be46c99bSCatalin Patulea
22824a1f25aSAzat Khuzhin SSL_CTX *ssl_ctx = NULL;
229f3d7ff5dSAzat Khuzhin SSL *ssl = NULL;
230be46c99bSCatalin Patulea struct bufferevent *bev;
23124a1f25aSAzat Khuzhin struct evhttp_connection *evcon = NULL;
232be46c99bSCatalin Patulea struct evhttp_request *req;
2338a90a850SNick Mathewson struct evkeyvalq *output_headers;
23429af65ebSAlexey Ozeritsky struct evbuffer *output_buffer;
235be46c99bSCatalin Patulea
236c5887f73SAlexey Ozeritsky int i;
23724a1f25aSAzat Khuzhin int ret = 0;
23824a1f25aSAzat Khuzhin enum { HTTP, HTTPS } type = HTTP;
239be46c99bSCatalin Patulea
240c5887f73SAlexey Ozeritsky for (i = 1; i < argc; i++) {
241c5887f73SAlexey Ozeritsky if (!strcmp("-url", argv[i])) {
242c5887f73SAlexey Ozeritsky if (i < argc - 1) {
243c5887f73SAlexey Ozeritsky url = argv[i + 1];
244c5887f73SAlexey Ozeritsky } else {
245c5887f73SAlexey Ozeritsky syntax();
24624a1f25aSAzat Khuzhin goto error;
247c5887f73SAlexey Ozeritsky }
248fdf713a0SAzat Khuzhin } else if (!strcmp("-crt", argv[i])) {
249fdf713a0SAzat Khuzhin if (i < argc - 1) {
250fdf713a0SAzat Khuzhin crt = argv[i + 1];
251fdf713a0SAzat Khuzhin } else {
252fdf713a0SAzat Khuzhin syntax();
253fdf713a0SAzat Khuzhin goto error;
254fdf713a0SAzat Khuzhin }
255c5887f73SAlexey Ozeritsky } else if (!strcmp("-ignore-cert", argv[i])) {
256c5887f73SAlexey Ozeritsky ignore_cert = 1;
257c5887f73SAlexey Ozeritsky } else if (!strcmp("-data", argv[i])) {
258c5887f73SAlexey Ozeritsky if (i < argc - 1) {
259c5887f73SAlexey Ozeritsky data_file = argv[i + 1];
260c5887f73SAlexey Ozeritsky } else {
261c5887f73SAlexey Ozeritsky syntax();
26224a1f25aSAzat Khuzhin goto error;
263c5887f73SAlexey Ozeritsky }
264d9da8443SAzat Khuzhin } else if (!strcmp("-retries", argv[i])) {
265d9da8443SAzat Khuzhin if (i < argc - 1) {
266d9da8443SAzat Khuzhin retries = atoi(argv[i + 1]);
267d9da8443SAzat Khuzhin } else {
268d9da8443SAzat Khuzhin syntax();
26924a1f25aSAzat Khuzhin goto error;
270d9da8443SAzat Khuzhin }
2714637aa88SAzat Khuzhin } else if (!strcmp("-timeout", argv[i])) {
2724637aa88SAzat Khuzhin if (i < argc - 1) {
2734637aa88SAzat Khuzhin timeout = atoi(argv[i + 1]);
2744637aa88SAzat Khuzhin } else {
2754637aa88SAzat Khuzhin syntax();
2764637aa88SAzat Khuzhin goto error;
2774637aa88SAzat Khuzhin }
278c5887f73SAlexey Ozeritsky } else if (!strcmp("-help", argv[i])) {
279c5887f73SAlexey Ozeritsky syntax();
28024a1f25aSAzat Khuzhin goto error;
281c5887f73SAlexey Ozeritsky }
282c5887f73SAlexey Ozeritsky }
283c5887f73SAlexey Ozeritsky
284c5887f73SAlexey Ozeritsky if (!url) {
285c5887f73SAlexey Ozeritsky syntax();
28624a1f25aSAzat Khuzhin goto error;
287c5887f73SAlexey Ozeritsky }
288c5887f73SAlexey Ozeritsky
2894e143958SJoakim Söderberg #ifdef _WIN32
29019222e52SJoakim Soderberg {
29119222e52SJoakim Soderberg WORD wVersionRequested;
29219222e52SJoakim Soderberg WSADATA wsaData;
29319222e52SJoakim Soderberg int err;
29419222e52SJoakim Soderberg
29519222e52SJoakim Soderberg wVersionRequested = MAKEWORD(2, 2);
29619222e52SJoakim Soderberg
29719222e52SJoakim Soderberg err = WSAStartup(wVersionRequested, &wsaData);
29819222e52SJoakim Soderberg if (err != 0) {
29919222e52SJoakim Soderberg printf("WSAStartup failed with error: %d\n", err);
30024a1f25aSAzat Khuzhin goto error;
30119222e52SJoakim Soderberg }
30219222e52SJoakim Soderberg }
3034e143958SJoakim Söderberg #endif // _WIN32
30419222e52SJoakim Soderberg
305be46c99bSCatalin Patulea http_uri = evhttp_uri_parse(url);
306be46c99bSCatalin Patulea if (http_uri == NULL) {
30724a1f25aSAzat Khuzhin err("malformed url");
30824a1f25aSAzat Khuzhin goto error;
309be46c99bSCatalin Patulea }
310be46c99bSCatalin Patulea
311be46c99bSCatalin Patulea scheme = evhttp_uri_get_scheme(http_uri);
312be46c99bSCatalin Patulea if (scheme == NULL || (strcasecmp(scheme, "https") != 0 &&
313be46c99bSCatalin Patulea strcasecmp(scheme, "http") != 0)) {
31424a1f25aSAzat Khuzhin err("url must be http or https");
31524a1f25aSAzat Khuzhin goto error;
316be46c99bSCatalin Patulea }
317be46c99bSCatalin Patulea
318be46c99bSCatalin Patulea host = evhttp_uri_get_host(http_uri);
319be46c99bSCatalin Patulea if (host == NULL) {
32024a1f25aSAzat Khuzhin err("url must have a host");
32124a1f25aSAzat Khuzhin goto error;
322be46c99bSCatalin Patulea }
323be46c99bSCatalin Patulea
324be46c99bSCatalin Patulea port = evhttp_uri_get_port(http_uri);
325be46c99bSCatalin Patulea if (port == -1) {
326be46c99bSCatalin Patulea port = (strcasecmp(scheme, "http") == 0) ? 80 : 443;
327be46c99bSCatalin Patulea }
328be46c99bSCatalin Patulea
329be46c99bSCatalin Patulea path = evhttp_uri_get_path(http_uri);
33029a04825SAndrey Skriabin if (strlen(path) == 0) {
331be46c99bSCatalin Patulea path = "/";
332be46c99bSCatalin Patulea }
333be46c99bSCatalin Patulea
334be46c99bSCatalin Patulea query = evhttp_uri_get_query(http_uri);
335be46c99bSCatalin Patulea if (query == NULL) {
336be46c99bSCatalin Patulea snprintf(uri, sizeof(uri) - 1, "%s", path);
337be46c99bSCatalin Patulea } else {
338be46c99bSCatalin Patulea snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query);
339be46c99bSCatalin Patulea }
340be46c99bSCatalin Patulea uri[sizeof(uri) - 1] = '\0';
341be46c99bSCatalin Patulea
342894ca48aSBernard Spil #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
343894ca48aSBernard Spil (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
344be46c99bSCatalin Patulea // Initialize OpenSSL
345be46c99bSCatalin Patulea SSL_library_init();
346be46c99bSCatalin Patulea ERR_load_crypto_strings();
347be46c99bSCatalin Patulea SSL_load_error_strings();
348be46c99bSCatalin Patulea OpenSSL_add_all_algorithms();
3493e9e0a0dSKurt Roeckx #endif
3505754d96aSPatrick Pelletier
3515754d96aSPatrick Pelletier /* This isn't strictly necessary... OpenSSL performs RAND_poll
3525754d96aSPatrick Pelletier * automatically on first use of random number generator. */
353be46c99bSCatalin Patulea r = RAND_poll();
354be46c99bSCatalin Patulea if (r == 0) {
35524a1f25aSAzat Khuzhin err_openssl("RAND_poll");
35624a1f25aSAzat Khuzhin goto error;
357be46c99bSCatalin Patulea }
3585754d96aSPatrick Pelletier
3595754d96aSPatrick Pelletier /* Create a new OpenSSL context */
360be46c99bSCatalin Patulea ssl_ctx = SSL_CTX_new(SSLv23_method());
36124a1f25aSAzat Khuzhin if (!ssl_ctx) {
36224a1f25aSAzat Khuzhin err_openssl("SSL_CTX_new");
36324a1f25aSAzat Khuzhin goto error;
36424a1f25aSAzat Khuzhin }
365be46c99bSCatalin Patulea
3665c0132f3SDavid Disseldorp if (crt == NULL) {
3675c0132f3SDavid Disseldorp X509_STORE *store;
3685c0132f3SDavid Disseldorp /* Attempt to use the system's trusted root certificates. */
3695c0132f3SDavid Disseldorp store = SSL_CTX_get_cert_store(ssl_ctx);
370*e9478640Syuangongji #ifdef _WIN32
371*e9478640Syuangongji if (add_cert_for_store(store, "CA") < 0 ||
372*e9478640Syuangongji add_cert_for_store(store, "AuthRoot") < 0 ||
373*e9478640Syuangongji add_cert_for_store(store, "ROOT") < 0) {
374*e9478640Syuangongji goto error;
375*e9478640Syuangongji }
376*e9478640Syuangongji #else // _WIN32
3775c0132f3SDavid Disseldorp if (X509_STORE_set_default_paths(store) != 1) {
3785c0132f3SDavid Disseldorp err_openssl("X509_STORE_set_default_paths");
3795c0132f3SDavid Disseldorp goto error;
3805c0132f3SDavid Disseldorp }
381*e9478640Syuangongji #endif // _WIN32
3825c0132f3SDavid Disseldorp } else {
3835c0132f3SDavid Disseldorp if (SSL_CTX_load_verify_locations(ssl_ctx, crt, NULL) != 1) {
38424a1f25aSAzat Khuzhin err_openssl("SSL_CTX_load_verify_locations");
38524a1f25aSAzat Khuzhin goto error;
38624a1f25aSAzat Khuzhin }
3875c0132f3SDavid Disseldorp }
38864d9f161SPatrick Pelletier /* Ask OpenSSL to verify the server certificate. Note that this
38964d9f161SPatrick Pelletier * does NOT include verifying that the hostname is correct.
39064d9f161SPatrick Pelletier * So, by itself, this means anyone with any legitimate
39164d9f161SPatrick Pelletier * CA-issued certificate for any website, can impersonate any
39264d9f161SPatrick Pelletier * other website in the world. This is not good. See "The
39364d9f161SPatrick Pelletier * Most Dangerous Code in the World" article at
39464d9f161SPatrick Pelletier * https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html
39564d9f161SPatrick Pelletier */
396aacd674cSPatrick Pelletier SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
39764d9f161SPatrick Pelletier /* This is how we solve the problem mentioned in the previous
39864d9f161SPatrick Pelletier * comment. We "wrap" OpenSSL's validation routine in our
39964d9f161SPatrick Pelletier * own routine, which also validates the hostname by calling
40064d9f161SPatrick Pelletier * the code provided by iSECPartners. Note that even though
40164d9f161SPatrick Pelletier * the "Everything You've Always Wanted to Know About
40264d9f161SPatrick Pelletier * Certificate Validation With OpenSSL (But Were Afraid to
40364d9f161SPatrick Pelletier * Ask)" paper from iSECPartners says very explicitly not to
40464d9f161SPatrick Pelletier * call SSL_CTX_set_cert_verify_callback (at the bottom of
40564d9f161SPatrick Pelletier * page 2), what we're doing here is safe because our
40664d9f161SPatrick Pelletier * cert_verify_callback() calls X509_verify_cert(), which is
40764d9f161SPatrick Pelletier * OpenSSL's built-in routine which would have been called if
40864d9f161SPatrick Pelletier * we hadn't set the callback. Therefore, we're just
40964d9f161SPatrick Pelletier * "wrapping" OpenSSL's routine, not replacing it. */
41064d9f161SPatrick Pelletier SSL_CTX_set_cert_verify_callback(ssl_ctx, cert_verify_callback,
41164d9f161SPatrick Pelletier (void *) host);
412aacd674cSPatrick Pelletier
413be46c99bSCatalin Patulea // Create event base
414be46c99bSCatalin Patulea base = event_base_new();
415be46c99bSCatalin Patulea if (!base) {
416be46c99bSCatalin Patulea perror("event_base_new()");
41724a1f25aSAzat Khuzhin goto error;
418be46c99bSCatalin Patulea }
419be46c99bSCatalin Patulea
420be46c99bSCatalin Patulea // Create OpenSSL bufferevent and stack evhttp on top of it
421be46c99bSCatalin Patulea ssl = SSL_new(ssl_ctx);
422be46c99bSCatalin Patulea if (ssl == NULL) {
42324a1f25aSAzat Khuzhin err_openssl("SSL_new()");
42424a1f25aSAzat Khuzhin goto error;
425be46c99bSCatalin Patulea }
426be46c99bSCatalin Patulea
4275c7282f7SJoakim Soderberg #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
428d1976f8eSNick Mathewson // Set hostname for SNI extension
429d1976f8eSNick Mathewson SSL_set_tlsext_host_name(ssl, host);
4305c7282f7SJoakim Soderberg #endif
431d1976f8eSNick Mathewson
432be46c99bSCatalin Patulea if (strcasecmp(scheme, "http") == 0) {
433be46c99bSCatalin Patulea bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
434be46c99bSCatalin Patulea } else {
43524a1f25aSAzat Khuzhin type = HTTPS;
436be46c99bSCatalin Patulea bev = bufferevent_openssl_socket_new(base, -1, ssl,
437be46c99bSCatalin Patulea BUFFEREVENT_SSL_CONNECTING,
438be46c99bSCatalin Patulea BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
439be46c99bSCatalin Patulea }
440be46c99bSCatalin Patulea
441be46c99bSCatalin Patulea if (bev == NULL) {
442be46c99bSCatalin Patulea fprintf(stderr, "bufferevent_openssl_socket_new() failed\n");
44324a1f25aSAzat Khuzhin goto error;
444be46c99bSCatalin Patulea }
445be46c99bSCatalin Patulea
446be46c99bSCatalin Patulea bufferevent_openssl_set_allow_dirty_shutdown(bev, 1);
447be46c99bSCatalin Patulea
448be46c99bSCatalin Patulea // For simplicity, we let DNS resolution block. Everything else should be
449be46c99bSCatalin Patulea // asynchronous though.
450be46c99bSCatalin Patulea evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev,
451be46c99bSCatalin Patulea host, port);
452be46c99bSCatalin Patulea if (evcon == NULL) {
453be46c99bSCatalin Patulea fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n");
45424a1f25aSAzat Khuzhin goto error;
455be46c99bSCatalin Patulea }
456be46c99bSCatalin Patulea
457d9da8443SAzat Khuzhin if (retries > 0) {
458d9da8443SAzat Khuzhin evhttp_connection_set_retries(evcon, retries);
459d9da8443SAzat Khuzhin }
4604637aa88SAzat Khuzhin if (timeout >= 0) {
4614637aa88SAzat Khuzhin evhttp_connection_set_timeout(evcon, timeout);
4624637aa88SAzat Khuzhin }
463d9da8443SAzat Khuzhin
464be46c99bSCatalin Patulea // Fire off the request
4655754d96aSPatrick Pelletier req = evhttp_request_new(http_request_done, bev);
466be46c99bSCatalin Patulea if (req == NULL) {
467be46c99bSCatalin Patulea fprintf(stderr, "evhttp_request_new() failed\n");
46824a1f25aSAzat Khuzhin goto error;
469be46c99bSCatalin Patulea }
470be46c99bSCatalin Patulea
4718a90a850SNick Mathewson output_headers = evhttp_request_get_output_headers(req);
4728a90a850SNick Mathewson evhttp_add_header(output_headers, "Host", host);
4738a90a850SNick Mathewson evhttp_add_header(output_headers, "Connection", "close");
474be46c99bSCatalin Patulea
475c5887f73SAlexey Ozeritsky if (data_file) {
47690786eb0SNick Mathewson /* NOTE: In production code, you'd probably want to use
47790786eb0SNick Mathewson * evbuffer_add_file() or evbuffer_add_file_segment(), to
47890786eb0SNick Mathewson * avoid needless copying. */
479c5887f73SAlexey Ozeritsky FILE * f = fopen(data_file, "rb");
480c5887f73SAlexey Ozeritsky char buf[1024];
481d7be7887SJoakim Soderberg size_t s;
482c5887f73SAlexey Ozeritsky size_t bytes = 0;
483c5887f73SAlexey Ozeritsky
484c5887f73SAlexey Ozeritsky if (!f) {
485c5887f73SAlexey Ozeritsky syntax();
48624a1f25aSAzat Khuzhin goto error;
487c5887f73SAlexey Ozeritsky }
488c5887f73SAlexey Ozeritsky
48929af65ebSAlexey Ozeritsky output_buffer = evhttp_request_get_output_buffer(req);
490c5887f73SAlexey Ozeritsky while ((s = fread(buf, 1, sizeof(buf), f)) > 0) {
49129af65ebSAlexey Ozeritsky evbuffer_add(output_buffer, buf, s);
492c5887f73SAlexey Ozeritsky bytes += s;
493c5887f73SAlexey Ozeritsky }
494462e6b60SNick Mathewson evutil_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long)bytes);
49529af65ebSAlexey Ozeritsky evhttp_add_header(output_headers, "Content-Length", buf);
496c5887f73SAlexey Ozeritsky fclose(f);
497c5887f73SAlexey Ozeritsky }
498c5887f73SAlexey Ozeritsky
499c5887f73SAlexey Ozeritsky r = evhttp_make_request(evcon, req, data_file ? EVHTTP_REQ_POST : EVHTTP_REQ_GET, uri);
500be46c99bSCatalin Patulea if (r != 0) {
501be46c99bSCatalin Patulea fprintf(stderr, "evhttp_make_request() failed\n");
50224a1f25aSAzat Khuzhin goto error;
503be46c99bSCatalin Patulea }
504be46c99bSCatalin Patulea
505be46c99bSCatalin Patulea event_base_dispatch(base);
50624a1f25aSAzat Khuzhin goto cleanup;
507be46c99bSCatalin Patulea
50824a1f25aSAzat Khuzhin error:
50924a1f25aSAzat Khuzhin ret = 1;
51024a1f25aSAzat Khuzhin cleanup:
51124a1f25aSAzat Khuzhin if (evcon)
512be46c99bSCatalin Patulea evhttp_connection_free(evcon);
51324a1f25aSAzat Khuzhin if (http_uri)
51424a1f25aSAzat Khuzhin evhttp_uri_free(http_uri);
515c0adad8fSAzat Khuzhin if (base)
516be46c99bSCatalin Patulea event_base_free(base);
517be46c99bSCatalin Patulea
51824a1f25aSAzat Khuzhin if (ssl_ctx)
51924a1f25aSAzat Khuzhin SSL_CTX_free(ssl_ctx);
520f3d7ff5dSAzat Khuzhin if (type == HTTP && ssl)
52124a1f25aSAzat Khuzhin SSL_free(ssl);
522894ca48aSBernard Spil #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
523894ca48aSBernard Spil (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
52424a1f25aSAzat Khuzhin EVP_cleanup();
52524a1f25aSAzat Khuzhin ERR_free_strings();
52624a1f25aSAzat Khuzhin
527e7bd9e03SAzat Khuzhin #if OPENSSL_VERSION_NUMBER < 0x10000000L
528c4e9d9bdSAzat Khuzhin ERR_remove_state(0);
529e7bd9e03SAzat Khuzhin #else
530e7bd9e03SAzat Khuzhin ERR_remove_thread_state(NULL);
531c4e9d9bdSAzat Khuzhin #endif
532e7bd9e03SAzat Khuzhin
53324a1f25aSAzat Khuzhin CRYPTO_cleanup_all_ex_data();
53424a1f25aSAzat Khuzhin
53524a1f25aSAzat Khuzhin sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
536894ca48aSBernard Spil #endif /* (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
537894ca48aSBernard Spil (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) */
53824a1f25aSAzat Khuzhin
5394e143958SJoakim Söderberg #ifdef _WIN32
54019222e52SJoakim Soderberg WSACleanup();
54119222e52SJoakim Soderberg #endif
54219222e52SJoakim Soderberg
54324a1f25aSAzat Khuzhin return ret;
544be46c99bSCatalin Patulea }
545