https-client.c revision 1.1.1.1.2.2 1 1.1.1.1.2.2 pgoyette /* $NetBSD: https-client.c,v 1.1.1.1.2.2 2017/03/20 06:52:23 pgoyette Exp $ */
2 1.1.1.1.2.2 pgoyette /*
3 1.1.1.1.2.2 pgoyette This is an example of how to hook up evhttp with bufferevent_ssl
4 1.1.1.1.2.2 pgoyette
5 1.1.1.1.2.2 pgoyette It just GETs an https URL given on the command-line and prints the response
6 1.1.1.1.2.2 pgoyette body to stdout.
7 1.1.1.1.2.2 pgoyette
8 1.1.1.1.2.2 pgoyette Actually, it also accepts plain http URLs to make it easy to compare http vs
9 1.1.1.1.2.2 pgoyette https code paths.
10 1.1.1.1.2.2 pgoyette
11 1.1.1.1.2.2 pgoyette Loosely based on le-proxy.c.
12 1.1.1.1.2.2 pgoyette */
13 1.1.1.1.2.2 pgoyette
14 1.1.1.1.2.2 pgoyette // Get rid of OSX 10.7 and greater deprecation warnings.
15 1.1.1.1.2.2 pgoyette #if defined(__APPLE__) && defined(__clang__)
16 1.1.1.1.2.2 pgoyette #pragma clang diagnostic ignored "-Wdeprecated-declarations"
17 1.1.1.1.2.2 pgoyette #endif
18 1.1.1.1.2.2 pgoyette
19 1.1.1.1.2.2 pgoyette #include <stdio.h>
20 1.1.1.1.2.2 pgoyette #include <assert.h>
21 1.1.1.1.2.2 pgoyette #include <stdlib.h>
22 1.1.1.1.2.2 pgoyette #include <string.h>
23 1.1.1.1.2.2 pgoyette #include <errno.h>
24 1.1.1.1.2.2 pgoyette
25 1.1.1.1.2.2 pgoyette #ifdef _WIN32
26 1.1.1.1.2.2 pgoyette #include <winsock2.h>
27 1.1.1.1.2.2 pgoyette #include <ws2tcpip.h>
28 1.1.1.1.2.2 pgoyette
29 1.1.1.1.2.2 pgoyette #define snprintf _snprintf
30 1.1.1.1.2.2 pgoyette #define strcasecmp _stricmp
31 1.1.1.1.2.2 pgoyette #else
32 1.1.1.1.2.2 pgoyette #include <sys/socket.h>
33 1.1.1.1.2.2 pgoyette #include <netinet/in.h>
34 1.1.1.1.2.2 pgoyette #endif
35 1.1.1.1.2.2 pgoyette
36 1.1.1.1.2.2 pgoyette #include <event2/bufferevent_ssl.h>
37 1.1.1.1.2.2 pgoyette #include <event2/bufferevent.h>
38 1.1.1.1.2.2 pgoyette #include <event2/buffer.h>
39 1.1.1.1.2.2 pgoyette #include <event2/listener.h>
40 1.1.1.1.2.2 pgoyette #include <event2/util.h>
41 1.1.1.1.2.2 pgoyette #include <event2/http.h>
42 1.1.1.1.2.2 pgoyette
43 1.1.1.1.2.2 pgoyette #include <openssl/ssl.h>
44 1.1.1.1.2.2 pgoyette #include <openssl/err.h>
45 1.1.1.1.2.2 pgoyette #include <openssl/rand.h>
46 1.1.1.1.2.2 pgoyette
47 1.1.1.1.2.2 pgoyette #include "openssl_hostname_validation.h"
48 1.1.1.1.2.2 pgoyette
49 1.1.1.1.2.2 pgoyette static struct event_base *base;
50 1.1.1.1.2.2 pgoyette static int ignore_cert = 0;
51 1.1.1.1.2.2 pgoyette
52 1.1.1.1.2.2 pgoyette static void
53 1.1.1.1.2.2 pgoyette http_request_done(struct evhttp_request *req, void *ctx)
54 1.1.1.1.2.2 pgoyette {
55 1.1.1.1.2.2 pgoyette char buffer[256];
56 1.1.1.1.2.2 pgoyette int nread;
57 1.1.1.1.2.2 pgoyette
58 1.1.1.1.2.2 pgoyette if (req == NULL) {
59 1.1.1.1.2.2 pgoyette /* If req is NULL, it means an error occurred, but
60 1.1.1.1.2.2 pgoyette * sadly we are mostly left guessing what the error
61 1.1.1.1.2.2 pgoyette * might have been. We'll do our best... */
62 1.1.1.1.2.2 pgoyette struct bufferevent *bev = (struct bufferevent *) ctx;
63 1.1.1.1.2.2 pgoyette unsigned long oslerr;
64 1.1.1.1.2.2 pgoyette int printed_err = 0;
65 1.1.1.1.2.2 pgoyette int errcode = EVUTIL_SOCKET_ERROR();
66 1.1.1.1.2.2 pgoyette fprintf(stderr, "some request failed - no idea which one though!\n");
67 1.1.1.1.2.2 pgoyette /* Print out the OpenSSL error queue that libevent
68 1.1.1.1.2.2 pgoyette * squirreled away for us, if any. */
69 1.1.1.1.2.2 pgoyette while ((oslerr = bufferevent_get_openssl_error(bev))) {
70 1.1.1.1.2.2 pgoyette ERR_error_string_n(oslerr, buffer, sizeof(buffer));
71 1.1.1.1.2.2 pgoyette fprintf(stderr, "%s\n", buffer);
72 1.1.1.1.2.2 pgoyette printed_err = 1;
73 1.1.1.1.2.2 pgoyette }
74 1.1.1.1.2.2 pgoyette /* If the OpenSSL error queue was empty, maybe it was a
75 1.1.1.1.2.2 pgoyette * socket error; let's try printing that. */
76 1.1.1.1.2.2 pgoyette if (! printed_err)
77 1.1.1.1.2.2 pgoyette fprintf(stderr, "socket error = %s (%d)\n",
78 1.1.1.1.2.2 pgoyette evutil_socket_error_to_string(errcode),
79 1.1.1.1.2.2 pgoyette errcode);
80 1.1.1.1.2.2 pgoyette return;
81 1.1.1.1.2.2 pgoyette }
82 1.1.1.1.2.2 pgoyette
83 1.1.1.1.2.2 pgoyette fprintf(stderr, "Response line: %d %s\n",
84 1.1.1.1.2.2 pgoyette evhttp_request_get_response_code(req),
85 1.1.1.1.2.2 pgoyette evhttp_request_get_response_code_line(req));
86 1.1.1.1.2.2 pgoyette
87 1.1.1.1.2.2 pgoyette while ((nread = evbuffer_remove(evhttp_request_get_input_buffer(req),
88 1.1.1.1.2.2 pgoyette buffer, sizeof(buffer)))
89 1.1.1.1.2.2 pgoyette > 0) {
90 1.1.1.1.2.2 pgoyette /* These are just arbitrary chunks of 256 bytes.
91 1.1.1.1.2.2 pgoyette * They are not lines, so we can't treat them as such. */
92 1.1.1.1.2.2 pgoyette fwrite(buffer, nread, 1, stdout);
93 1.1.1.1.2.2 pgoyette }
94 1.1.1.1.2.2 pgoyette }
95 1.1.1.1.2.2 pgoyette
96 1.1.1.1.2.2 pgoyette static void
97 1.1.1.1.2.2 pgoyette syntax(void)
98 1.1.1.1.2.2 pgoyette {
99 1.1.1.1.2.2 pgoyette fputs("Syntax:\n", stderr);
100 1.1.1.1.2.2 pgoyette fputs(" https-client -url <https-url> [-data data-file.bin] [-ignore-cert] [-retries num] [-timeout sec] [-crt crt]\n", stderr);
101 1.1.1.1.2.2 pgoyette fputs("Example:\n", stderr);
102 1.1.1.1.2.2 pgoyette fputs(" https-client -url https://ip.appspot.com/\n", stderr);
103 1.1.1.1.2.2 pgoyette }
104 1.1.1.1.2.2 pgoyette
105 1.1.1.1.2.2 pgoyette static void
106 1.1.1.1.2.2 pgoyette err(const char *msg)
107 1.1.1.1.2.2 pgoyette {
108 1.1.1.1.2.2 pgoyette fputs(msg, stderr);
109 1.1.1.1.2.2 pgoyette }
110 1.1.1.1.2.2 pgoyette
111 1.1.1.1.2.2 pgoyette static void
112 1.1.1.1.2.2 pgoyette err_openssl(const char *func)
113 1.1.1.1.2.2 pgoyette {
114 1.1.1.1.2.2 pgoyette fprintf (stderr, "%s failed:\n", func);
115 1.1.1.1.2.2 pgoyette
116 1.1.1.1.2.2 pgoyette /* This is the OpenSSL function that prints the contents of the
117 1.1.1.1.2.2 pgoyette * error stack to the specified file handle. */
118 1.1.1.1.2.2 pgoyette ERR_print_errors_fp (stderr);
119 1.1.1.1.2.2 pgoyette
120 1.1.1.1.2.2 pgoyette exit(1);
121 1.1.1.1.2.2 pgoyette }
122 1.1.1.1.2.2 pgoyette
123 1.1.1.1.2.2 pgoyette #ifndef _WIN32
124 1.1.1.1.2.2 pgoyette /* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */
125 1.1.1.1.2.2 pgoyette static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg)
126 1.1.1.1.2.2 pgoyette {
127 1.1.1.1.2.2 pgoyette char cert_str[256];
128 1.1.1.1.2.2 pgoyette const char *host = (const char *) arg;
129 1.1.1.1.2.2 pgoyette const char *res_str = "X509_verify_cert failed";
130 1.1.1.1.2.2 pgoyette HostnameValidationResult res = Error;
131 1.1.1.1.2.2 pgoyette
132 1.1.1.1.2.2 pgoyette /* This is the function that OpenSSL would call if we hadn't called
133 1.1.1.1.2.2 pgoyette * SSL_CTX_set_cert_verify_callback(). Therefore, we are "wrapping"
134 1.1.1.1.2.2 pgoyette * the default functionality, rather than replacing it. */
135 1.1.1.1.2.2 pgoyette int ok_so_far = 0;
136 1.1.1.1.2.2 pgoyette
137 1.1.1.1.2.2 pgoyette X509 *server_cert = NULL;
138 1.1.1.1.2.2 pgoyette
139 1.1.1.1.2.2 pgoyette if (ignore_cert) {
140 1.1.1.1.2.2 pgoyette return 1;
141 1.1.1.1.2.2 pgoyette }
142 1.1.1.1.2.2 pgoyette
143 1.1.1.1.2.2 pgoyette ok_so_far = X509_verify_cert(x509_ctx);
144 1.1.1.1.2.2 pgoyette
145 1.1.1.1.2.2 pgoyette server_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
146 1.1.1.1.2.2 pgoyette
147 1.1.1.1.2.2 pgoyette if (ok_so_far) {
148 1.1.1.1.2.2 pgoyette res = validate_hostname(host, server_cert);
149 1.1.1.1.2.2 pgoyette
150 1.1.1.1.2.2 pgoyette switch (res) {
151 1.1.1.1.2.2 pgoyette case MatchFound:
152 1.1.1.1.2.2 pgoyette res_str = "MatchFound";
153 1.1.1.1.2.2 pgoyette break;
154 1.1.1.1.2.2 pgoyette case MatchNotFound:
155 1.1.1.1.2.2 pgoyette res_str = "MatchNotFound";
156 1.1.1.1.2.2 pgoyette break;
157 1.1.1.1.2.2 pgoyette case NoSANPresent:
158 1.1.1.1.2.2 pgoyette res_str = "NoSANPresent";
159 1.1.1.1.2.2 pgoyette break;
160 1.1.1.1.2.2 pgoyette case MalformedCertificate:
161 1.1.1.1.2.2 pgoyette res_str = "MalformedCertificate";
162 1.1.1.1.2.2 pgoyette break;
163 1.1.1.1.2.2 pgoyette case Error:
164 1.1.1.1.2.2 pgoyette res_str = "Error";
165 1.1.1.1.2.2 pgoyette break;
166 1.1.1.1.2.2 pgoyette default:
167 1.1.1.1.2.2 pgoyette res_str = "WTF!";
168 1.1.1.1.2.2 pgoyette break;
169 1.1.1.1.2.2 pgoyette }
170 1.1.1.1.2.2 pgoyette }
171 1.1.1.1.2.2 pgoyette
172 1.1.1.1.2.2 pgoyette X509_NAME_oneline(X509_get_subject_name (server_cert),
173 1.1.1.1.2.2 pgoyette cert_str, sizeof (cert_str));
174 1.1.1.1.2.2 pgoyette
175 1.1.1.1.2.2 pgoyette if (res == MatchFound) {
176 1.1.1.1.2.2 pgoyette printf("https server '%s' has this certificate, "
177 1.1.1.1.2.2 pgoyette "which looks good to me:\n%s\n",
178 1.1.1.1.2.2 pgoyette host, cert_str);
179 1.1.1.1.2.2 pgoyette return 1;
180 1.1.1.1.2.2 pgoyette } else {
181 1.1.1.1.2.2 pgoyette printf("Got '%s' for hostname '%s' and certificate:\n%s\n",
182 1.1.1.1.2.2 pgoyette res_str, host, cert_str);
183 1.1.1.1.2.2 pgoyette return 0;
184 1.1.1.1.2.2 pgoyette }
185 1.1.1.1.2.2 pgoyette }
186 1.1.1.1.2.2 pgoyette #endif
187 1.1.1.1.2.2 pgoyette
188 1.1.1.1.2.2 pgoyette int
189 1.1.1.1.2.2 pgoyette main(int argc, char **argv)
190 1.1.1.1.2.2 pgoyette {
191 1.1.1.1.2.2 pgoyette int r;
192 1.1.1.1.2.2 pgoyette
193 1.1.1.1.2.2 pgoyette struct evhttp_uri *http_uri = NULL;
194 1.1.1.1.2.2 pgoyette const char *url = NULL, *data_file = NULL;
195 1.1.1.1.2.2 pgoyette const char *crt = "/etc/ssl/certs/ca-certificates.crt";
196 1.1.1.1.2.2 pgoyette const char *scheme, *host, *path, *query;
197 1.1.1.1.2.2 pgoyette char uri[256];
198 1.1.1.1.2.2 pgoyette int port;
199 1.1.1.1.2.2 pgoyette int retries = 0;
200 1.1.1.1.2.2 pgoyette int timeout = -1;
201 1.1.1.1.2.2 pgoyette
202 1.1.1.1.2.2 pgoyette SSL_CTX *ssl_ctx = NULL;
203 1.1.1.1.2.2 pgoyette SSL *ssl = NULL;
204 1.1.1.1.2.2 pgoyette struct bufferevent *bev;
205 1.1.1.1.2.2 pgoyette struct evhttp_connection *evcon = NULL;
206 1.1.1.1.2.2 pgoyette struct evhttp_request *req;
207 1.1.1.1.2.2 pgoyette struct evkeyvalq *output_headers;
208 1.1.1.1.2.2 pgoyette struct evbuffer *output_buffer;
209 1.1.1.1.2.2 pgoyette
210 1.1.1.1.2.2 pgoyette int i;
211 1.1.1.1.2.2 pgoyette int ret = 0;
212 1.1.1.1.2.2 pgoyette enum { HTTP, HTTPS } type = HTTP;
213 1.1.1.1.2.2 pgoyette
214 1.1.1.1.2.2 pgoyette for (i = 1; i < argc; i++) {
215 1.1.1.1.2.2 pgoyette if (!strcmp("-url", argv[i])) {
216 1.1.1.1.2.2 pgoyette if (i < argc - 1) {
217 1.1.1.1.2.2 pgoyette url = argv[i + 1];
218 1.1.1.1.2.2 pgoyette } else {
219 1.1.1.1.2.2 pgoyette syntax();
220 1.1.1.1.2.2 pgoyette goto error;
221 1.1.1.1.2.2 pgoyette }
222 1.1.1.1.2.2 pgoyette } else if (!strcmp("-crt", argv[i])) {
223 1.1.1.1.2.2 pgoyette if (i < argc - 1) {
224 1.1.1.1.2.2 pgoyette crt = argv[i + 1];
225 1.1.1.1.2.2 pgoyette } else {
226 1.1.1.1.2.2 pgoyette syntax();
227 1.1.1.1.2.2 pgoyette goto error;
228 1.1.1.1.2.2 pgoyette }
229 1.1.1.1.2.2 pgoyette } else if (!strcmp("-ignore-cert", argv[i])) {
230 1.1.1.1.2.2 pgoyette ignore_cert = 1;
231 1.1.1.1.2.2 pgoyette } else if (!strcmp("-data", argv[i])) {
232 1.1.1.1.2.2 pgoyette if (i < argc - 1) {
233 1.1.1.1.2.2 pgoyette data_file = argv[i + 1];
234 1.1.1.1.2.2 pgoyette } else {
235 1.1.1.1.2.2 pgoyette syntax();
236 1.1.1.1.2.2 pgoyette goto error;
237 1.1.1.1.2.2 pgoyette }
238 1.1.1.1.2.2 pgoyette } else if (!strcmp("-retries", argv[i])) {
239 1.1.1.1.2.2 pgoyette if (i < argc - 1) {
240 1.1.1.1.2.2 pgoyette retries = atoi(argv[i + 1]);
241 1.1.1.1.2.2 pgoyette } else {
242 1.1.1.1.2.2 pgoyette syntax();
243 1.1.1.1.2.2 pgoyette goto error;
244 1.1.1.1.2.2 pgoyette }
245 1.1.1.1.2.2 pgoyette } else if (!strcmp("-timeout", argv[i])) {
246 1.1.1.1.2.2 pgoyette if (i < argc - 1) {
247 1.1.1.1.2.2 pgoyette timeout = atoi(argv[i + 1]);
248 1.1.1.1.2.2 pgoyette } else {
249 1.1.1.1.2.2 pgoyette syntax();
250 1.1.1.1.2.2 pgoyette goto error;
251 1.1.1.1.2.2 pgoyette }
252 1.1.1.1.2.2 pgoyette } else if (!strcmp("-help", argv[i])) {
253 1.1.1.1.2.2 pgoyette syntax();
254 1.1.1.1.2.2 pgoyette goto error;
255 1.1.1.1.2.2 pgoyette }
256 1.1.1.1.2.2 pgoyette }
257 1.1.1.1.2.2 pgoyette
258 1.1.1.1.2.2 pgoyette if (!url) {
259 1.1.1.1.2.2 pgoyette syntax();
260 1.1.1.1.2.2 pgoyette goto error;
261 1.1.1.1.2.2 pgoyette }
262 1.1.1.1.2.2 pgoyette
263 1.1.1.1.2.2 pgoyette #ifdef _WIN32
264 1.1.1.1.2.2 pgoyette {
265 1.1.1.1.2.2 pgoyette WORD wVersionRequested;
266 1.1.1.1.2.2 pgoyette WSADATA wsaData;
267 1.1.1.1.2.2 pgoyette int err;
268 1.1.1.1.2.2 pgoyette
269 1.1.1.1.2.2 pgoyette wVersionRequested = MAKEWORD(2, 2);
270 1.1.1.1.2.2 pgoyette
271 1.1.1.1.2.2 pgoyette err = WSAStartup(wVersionRequested, &wsaData);
272 1.1.1.1.2.2 pgoyette if (err != 0) {
273 1.1.1.1.2.2 pgoyette printf("WSAStartup failed with error: %d\n", err);
274 1.1.1.1.2.2 pgoyette goto error;
275 1.1.1.1.2.2 pgoyette }
276 1.1.1.1.2.2 pgoyette }
277 1.1.1.1.2.2 pgoyette #endif // _WIN32
278 1.1.1.1.2.2 pgoyette
279 1.1.1.1.2.2 pgoyette http_uri = evhttp_uri_parse(url);
280 1.1.1.1.2.2 pgoyette if (http_uri == NULL) {
281 1.1.1.1.2.2 pgoyette err("malformed url");
282 1.1.1.1.2.2 pgoyette goto error;
283 1.1.1.1.2.2 pgoyette }
284 1.1.1.1.2.2 pgoyette
285 1.1.1.1.2.2 pgoyette scheme = evhttp_uri_get_scheme(http_uri);
286 1.1.1.1.2.2 pgoyette if (scheme == NULL || (strcasecmp(scheme, "https") != 0 &&
287 1.1.1.1.2.2 pgoyette strcasecmp(scheme, "http") != 0)) {
288 1.1.1.1.2.2 pgoyette err("url must be http or https");
289 1.1.1.1.2.2 pgoyette goto error;
290 1.1.1.1.2.2 pgoyette }
291 1.1.1.1.2.2 pgoyette
292 1.1.1.1.2.2 pgoyette host = evhttp_uri_get_host(http_uri);
293 1.1.1.1.2.2 pgoyette if (host == NULL) {
294 1.1.1.1.2.2 pgoyette err("url must have a host");
295 1.1.1.1.2.2 pgoyette goto error;
296 1.1.1.1.2.2 pgoyette }
297 1.1.1.1.2.2 pgoyette
298 1.1.1.1.2.2 pgoyette port = evhttp_uri_get_port(http_uri);
299 1.1.1.1.2.2 pgoyette if (port == -1) {
300 1.1.1.1.2.2 pgoyette port = (strcasecmp(scheme, "http") == 0) ? 80 : 443;
301 1.1.1.1.2.2 pgoyette }
302 1.1.1.1.2.2 pgoyette
303 1.1.1.1.2.2 pgoyette path = evhttp_uri_get_path(http_uri);
304 1.1.1.1.2.2 pgoyette if (strlen(path) == 0) {
305 1.1.1.1.2.2 pgoyette path = "/";
306 1.1.1.1.2.2 pgoyette }
307 1.1.1.1.2.2 pgoyette
308 1.1.1.1.2.2 pgoyette query = evhttp_uri_get_query(http_uri);
309 1.1.1.1.2.2 pgoyette if (query == NULL) {
310 1.1.1.1.2.2 pgoyette snprintf(uri, sizeof(uri) - 1, "%s", path);
311 1.1.1.1.2.2 pgoyette } else {
312 1.1.1.1.2.2 pgoyette snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query);
313 1.1.1.1.2.2 pgoyette }
314 1.1.1.1.2.2 pgoyette uri[sizeof(uri) - 1] = '\0';
315 1.1.1.1.2.2 pgoyette
316 1.1.1.1.2.2 pgoyette #if OPENSSL_VERSION_NUMBER < 0x10100000L
317 1.1.1.1.2.2 pgoyette // Initialize OpenSSL
318 1.1.1.1.2.2 pgoyette SSL_library_init();
319 1.1.1.1.2.2 pgoyette ERR_load_crypto_strings();
320 1.1.1.1.2.2 pgoyette SSL_load_error_strings();
321 1.1.1.1.2.2 pgoyette OpenSSL_add_all_algorithms();
322 1.1.1.1.2.2 pgoyette #endif
323 1.1.1.1.2.2 pgoyette
324 1.1.1.1.2.2 pgoyette /* This isn't strictly necessary... OpenSSL performs RAND_poll
325 1.1.1.1.2.2 pgoyette * automatically on first use of random number generator. */
326 1.1.1.1.2.2 pgoyette r = RAND_poll();
327 1.1.1.1.2.2 pgoyette if (r == 0) {
328 1.1.1.1.2.2 pgoyette err_openssl("RAND_poll");
329 1.1.1.1.2.2 pgoyette goto error;
330 1.1.1.1.2.2 pgoyette }
331 1.1.1.1.2.2 pgoyette
332 1.1.1.1.2.2 pgoyette /* Create a new OpenSSL context */
333 1.1.1.1.2.2 pgoyette ssl_ctx = SSL_CTX_new(SSLv23_method());
334 1.1.1.1.2.2 pgoyette if (!ssl_ctx) {
335 1.1.1.1.2.2 pgoyette err_openssl("SSL_CTX_new");
336 1.1.1.1.2.2 pgoyette goto error;
337 1.1.1.1.2.2 pgoyette }
338 1.1.1.1.2.2 pgoyette
339 1.1.1.1.2.2 pgoyette #ifndef _WIN32
340 1.1.1.1.2.2 pgoyette /* TODO: Add certificate loading on Windows as well */
341 1.1.1.1.2.2 pgoyette
342 1.1.1.1.2.2 pgoyette /* Attempt to use the system's trusted root certificates.
343 1.1.1.1.2.2 pgoyette * (This path is only valid for Debian-based systems.) */
344 1.1.1.1.2.2 pgoyette if (1 != SSL_CTX_load_verify_locations(ssl_ctx, crt, NULL)) {
345 1.1.1.1.2.2 pgoyette err_openssl("SSL_CTX_load_verify_locations");
346 1.1.1.1.2.2 pgoyette goto error;
347 1.1.1.1.2.2 pgoyette }
348 1.1.1.1.2.2 pgoyette /* Ask OpenSSL to verify the server certificate. Note that this
349 1.1.1.1.2.2 pgoyette * does NOT include verifying that the hostname is correct.
350 1.1.1.1.2.2 pgoyette * So, by itself, this means anyone with any legitimate
351 1.1.1.1.2.2 pgoyette * CA-issued certificate for any website, can impersonate any
352 1.1.1.1.2.2 pgoyette * other website in the world. This is not good. See "The
353 1.1.1.1.2.2 pgoyette * Most Dangerous Code in the World" article at
354 1.1.1.1.2.2 pgoyette * https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html
355 1.1.1.1.2.2 pgoyette */
356 1.1.1.1.2.2 pgoyette SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
357 1.1.1.1.2.2 pgoyette /* This is how we solve the problem mentioned in the previous
358 1.1.1.1.2.2 pgoyette * comment. We "wrap" OpenSSL's validation routine in our
359 1.1.1.1.2.2 pgoyette * own routine, which also validates the hostname by calling
360 1.1.1.1.2.2 pgoyette * the code provided by iSECPartners. Note that even though
361 1.1.1.1.2.2 pgoyette * the "Everything You've Always Wanted to Know About
362 1.1.1.1.2.2 pgoyette * Certificate Validation With OpenSSL (But Were Afraid to
363 1.1.1.1.2.2 pgoyette * Ask)" paper from iSECPartners says very explicitly not to
364 1.1.1.1.2.2 pgoyette * call SSL_CTX_set_cert_verify_callback (at the bottom of
365 1.1.1.1.2.2 pgoyette * page 2), what we're doing here is safe because our
366 1.1.1.1.2.2 pgoyette * cert_verify_callback() calls X509_verify_cert(), which is
367 1.1.1.1.2.2 pgoyette * OpenSSL's built-in routine which would have been called if
368 1.1.1.1.2.2 pgoyette * we hadn't set the callback. Therefore, we're just
369 1.1.1.1.2.2 pgoyette * "wrapping" OpenSSL's routine, not replacing it. */
370 1.1.1.1.2.2 pgoyette SSL_CTX_set_cert_verify_callback(ssl_ctx, cert_verify_callback,
371 1.1.1.1.2.2 pgoyette (void *) host);
372 1.1.1.1.2.2 pgoyette #else // _WIN32
373 1.1.1.1.2.2 pgoyette (void)crt;
374 1.1.1.1.2.2 pgoyette #endif // _WIN32
375 1.1.1.1.2.2 pgoyette
376 1.1.1.1.2.2 pgoyette // Create event base
377 1.1.1.1.2.2 pgoyette base = event_base_new();
378 1.1.1.1.2.2 pgoyette if (!base) {
379 1.1.1.1.2.2 pgoyette perror("event_base_new()");
380 1.1.1.1.2.2 pgoyette goto error;
381 1.1.1.1.2.2 pgoyette }
382 1.1.1.1.2.2 pgoyette
383 1.1.1.1.2.2 pgoyette // Create OpenSSL bufferevent and stack evhttp on top of it
384 1.1.1.1.2.2 pgoyette ssl = SSL_new(ssl_ctx);
385 1.1.1.1.2.2 pgoyette if (ssl == NULL) {
386 1.1.1.1.2.2 pgoyette err_openssl("SSL_new()");
387 1.1.1.1.2.2 pgoyette goto error;
388 1.1.1.1.2.2 pgoyette }
389 1.1.1.1.2.2 pgoyette
390 1.1.1.1.2.2 pgoyette #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
391 1.1.1.1.2.2 pgoyette // Set hostname for SNI extension
392 1.1.1.1.2.2 pgoyette SSL_set_tlsext_host_name(ssl, host);
393 1.1.1.1.2.2 pgoyette #endif
394 1.1.1.1.2.2 pgoyette
395 1.1.1.1.2.2 pgoyette if (strcasecmp(scheme, "http") == 0) {
396 1.1.1.1.2.2 pgoyette bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
397 1.1.1.1.2.2 pgoyette } else {
398 1.1.1.1.2.2 pgoyette type = HTTPS;
399 1.1.1.1.2.2 pgoyette bev = bufferevent_openssl_socket_new(base, -1, ssl,
400 1.1.1.1.2.2 pgoyette BUFFEREVENT_SSL_CONNECTING,
401 1.1.1.1.2.2 pgoyette BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
402 1.1.1.1.2.2 pgoyette }
403 1.1.1.1.2.2 pgoyette
404 1.1.1.1.2.2 pgoyette if (bev == NULL) {
405 1.1.1.1.2.2 pgoyette fprintf(stderr, "bufferevent_openssl_socket_new() failed\n");
406 1.1.1.1.2.2 pgoyette goto error;
407 1.1.1.1.2.2 pgoyette }
408 1.1.1.1.2.2 pgoyette
409 1.1.1.1.2.2 pgoyette bufferevent_openssl_set_allow_dirty_shutdown(bev, 1);
410 1.1.1.1.2.2 pgoyette
411 1.1.1.1.2.2 pgoyette // For simplicity, we let DNS resolution block. Everything else should be
412 1.1.1.1.2.2 pgoyette // asynchronous though.
413 1.1.1.1.2.2 pgoyette evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev,
414 1.1.1.1.2.2 pgoyette host, port);
415 1.1.1.1.2.2 pgoyette if (evcon == NULL) {
416 1.1.1.1.2.2 pgoyette fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n");
417 1.1.1.1.2.2 pgoyette goto error;
418 1.1.1.1.2.2 pgoyette }
419 1.1.1.1.2.2 pgoyette
420 1.1.1.1.2.2 pgoyette if (retries > 0) {
421 1.1.1.1.2.2 pgoyette evhttp_connection_set_retries(evcon, retries);
422 1.1.1.1.2.2 pgoyette }
423 1.1.1.1.2.2 pgoyette if (timeout >= 0) {
424 1.1.1.1.2.2 pgoyette evhttp_connection_set_timeout(evcon, timeout);
425 1.1.1.1.2.2 pgoyette }
426 1.1.1.1.2.2 pgoyette
427 1.1.1.1.2.2 pgoyette // Fire off the request
428 1.1.1.1.2.2 pgoyette req = evhttp_request_new(http_request_done, bev);
429 1.1.1.1.2.2 pgoyette if (req == NULL) {
430 1.1.1.1.2.2 pgoyette fprintf(stderr, "evhttp_request_new() failed\n");
431 1.1.1.1.2.2 pgoyette goto error;
432 1.1.1.1.2.2 pgoyette }
433 1.1.1.1.2.2 pgoyette
434 1.1.1.1.2.2 pgoyette output_headers = evhttp_request_get_output_headers(req);
435 1.1.1.1.2.2 pgoyette evhttp_add_header(output_headers, "Host", host);
436 1.1.1.1.2.2 pgoyette evhttp_add_header(output_headers, "Connection", "close");
437 1.1.1.1.2.2 pgoyette
438 1.1.1.1.2.2 pgoyette if (data_file) {
439 1.1.1.1.2.2 pgoyette /* NOTE: In production code, you'd probably want to use
440 1.1.1.1.2.2 pgoyette * evbuffer_add_file() or evbuffer_add_file_segment(), to
441 1.1.1.1.2.2 pgoyette * avoid needless copying. */
442 1.1.1.1.2.2 pgoyette FILE * f = fopen(data_file, "rb");
443 1.1.1.1.2.2 pgoyette char buf[1024];
444 1.1.1.1.2.2 pgoyette size_t s;
445 1.1.1.1.2.2 pgoyette size_t bytes = 0;
446 1.1.1.1.2.2 pgoyette
447 1.1.1.1.2.2 pgoyette if (!f) {
448 1.1.1.1.2.2 pgoyette syntax();
449 1.1.1.1.2.2 pgoyette goto error;
450 1.1.1.1.2.2 pgoyette }
451 1.1.1.1.2.2 pgoyette
452 1.1.1.1.2.2 pgoyette output_buffer = evhttp_request_get_output_buffer(req);
453 1.1.1.1.2.2 pgoyette while ((s = fread(buf, 1, sizeof(buf), f)) > 0) {
454 1.1.1.1.2.2 pgoyette evbuffer_add(output_buffer, buf, s);
455 1.1.1.1.2.2 pgoyette bytes += s;
456 1.1.1.1.2.2 pgoyette }
457 1.1.1.1.2.2 pgoyette evutil_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long)bytes);
458 1.1.1.1.2.2 pgoyette evhttp_add_header(output_headers, "Content-Length", buf);
459 1.1.1.1.2.2 pgoyette fclose(f);
460 1.1.1.1.2.2 pgoyette }
461 1.1.1.1.2.2 pgoyette
462 1.1.1.1.2.2 pgoyette r = evhttp_make_request(evcon, req, data_file ? EVHTTP_REQ_POST : EVHTTP_REQ_GET, uri);
463 1.1.1.1.2.2 pgoyette if (r != 0) {
464 1.1.1.1.2.2 pgoyette fprintf(stderr, "evhttp_make_request() failed\n");
465 1.1.1.1.2.2 pgoyette goto error;
466 1.1.1.1.2.2 pgoyette }
467 1.1.1.1.2.2 pgoyette
468 1.1.1.1.2.2 pgoyette event_base_dispatch(base);
469 1.1.1.1.2.2 pgoyette goto cleanup;
470 1.1.1.1.2.2 pgoyette
471 1.1.1.1.2.2 pgoyette error:
472 1.1.1.1.2.2 pgoyette ret = 1;
473 1.1.1.1.2.2 pgoyette cleanup:
474 1.1.1.1.2.2 pgoyette if (evcon)
475 1.1.1.1.2.2 pgoyette evhttp_connection_free(evcon);
476 1.1.1.1.2.2 pgoyette if (http_uri)
477 1.1.1.1.2.2 pgoyette evhttp_uri_free(http_uri);
478 1.1.1.1.2.2 pgoyette event_base_free(base);
479 1.1.1.1.2.2 pgoyette
480 1.1.1.1.2.2 pgoyette if (ssl_ctx)
481 1.1.1.1.2.2 pgoyette SSL_CTX_free(ssl_ctx);
482 1.1.1.1.2.2 pgoyette if (type == HTTP && ssl)
483 1.1.1.1.2.2 pgoyette SSL_free(ssl);
484 1.1.1.1.2.2 pgoyette #if OPENSSL_VERSION_NUMBER < 0x10100000L
485 1.1.1.1.2.2 pgoyette EVP_cleanup();
486 1.1.1.1.2.2 pgoyette ERR_free_strings();
487 1.1.1.1.2.2 pgoyette
488 1.1.1.1.2.2 pgoyette #ifdef EVENT__HAVE_ERR_REMOVE_THREAD_STATE
489 1.1.1.1.2.2 pgoyette ERR_remove_thread_state(NULL);
490 1.1.1.1.2.2 pgoyette #else
491 1.1.1.1.2.2 pgoyette ERR_remove_state(0);
492 1.1.1.1.2.2 pgoyette #endif
493 1.1.1.1.2.2 pgoyette CRYPTO_cleanup_all_ex_data();
494 1.1.1.1.2.2 pgoyette
495 1.1.1.1.2.2 pgoyette sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
496 1.1.1.1.2.2 pgoyette #endif /*OPENSSL_VERSION_NUMBER < 0x10100000L */
497 1.1.1.1.2.2 pgoyette
498 1.1.1.1.2.2 pgoyette #ifdef _WIN32
499 1.1.1.1.2.2 pgoyette WSACleanup();
500 1.1.1.1.2.2 pgoyette #endif
501 1.1.1.1.2.2 pgoyette
502 1.1.1.1.2.2 pgoyette return ret;
503 1.1.1.1.2.2 pgoyette }
504