]>
Commit | Line | Data |
---|---|---|
29f178bd DDO |
1 | /* |
2 | * Copyright 2001-2020 The OpenSSL Project Authors. All Rights Reserved. | |
3 | * Copyright Siemens AG 2018-2020 | |
4 | * | |
5 | * Licensed under the Apache License 2.0 (the "License"). You may not use | |
6 | * this file except in compliance with the License. You can obtain a copy | |
7 | * in the file LICENSE in the source distribution or at | |
8 | * https://www.openssl.org/source/license.html | |
9 | */ | |
10 | ||
11 | #include "e_os.h" | |
12 | #include <stdio.h> | |
13 | #include <stdlib.h> | |
14 | #include "crypto/ctype.h" | |
15 | #include <string.h> | |
16 | #include <openssl/asn1.h> | |
17 | #include <openssl/evp.h> | |
18 | #include <openssl/err.h> | |
19 | #include <openssl/httperr.h> | |
20 | #include <openssl/cmperr.h> | |
21 | #include <openssl/buffer.h> | |
22 | #include <openssl/http.h> | |
23 | #include "internal/sockets.h" | |
24 | #include "internal/cryptlib.h" | |
25 | ||
26 | #include "http_local.h" | |
27 | ||
28 | #define HTTP_PREFIX "HTTP/" | |
29 | #define HTTP_VERSION_PATT "1." /* allow 1.x */ | |
30 | #define HTTP_VERSION_STR_LEN 3 | |
31 | #define HTTP_LINE1_MINLEN ((int)strlen(HTTP_PREFIX HTTP_VERSION_PATT "x 200\n")) | |
32 | #define HTTP_VERSION_MAX_REDIRECTIONS 50 | |
33 | ||
34 | #define HTTP_STATUS_CODE_OK 200 | |
35 | #define HTTP_STATUS_CODE_MOVED_PERMANENTLY 301 | |
36 | #define HTTP_STATUS_CODE_FOUND 302 | |
37 | ||
38 | ||
39 | /* Stateful HTTP request code, supporting blocking and non-blocking I/O */ | |
40 | ||
41 | /* Opaque HTTP request status structure */ | |
42 | ||
43 | struct ossl_http_req_ctx_st { | |
44 | int state; /* Current I/O state */ | |
45 | unsigned char *iobuf; /* Line buffer */ | |
46 | int iobuflen; /* Line buffer length */ | |
47 | BIO *wbio; /* BIO to send request to */ | |
48 | BIO *rbio; /* BIO to read response from */ | |
49 | BIO *mem; /* Memory BIO response is built into */ | |
50 | int method_GET; /* HTTP method "GET" or "POST" */ | |
51 | const char *expected_ct; /* expected Content-Type, or NULL */ | |
52 | int expect_asn1; /* response must be ASN.1-encoded */ | |
53 | unsigned long resp_len; /* length of response */ | |
54 | unsigned long max_resp_len; /* Maximum length of response */ | |
55 | time_t max_time; /* Maximum end time of the transfer, or 0 */ | |
56 | char *redirection_url; /* Location given with HTTP status 301/302 */ | |
57 | }; | |
58 | ||
59 | #define HTTP_DEFAULT_MAX_LINE_LENGTH (4 * 1024) | |
60 | #define HTTP_DEFAULT_MAX_RESP_LEN (100 * 1024) | |
61 | ||
62 | /* HTTP states */ | |
63 | ||
64 | #define OHS_NOREAD 0x1000 /* If set no reading should be performed */ | |
65 | #define OHS_ERROR (0 | OHS_NOREAD) /* Error condition */ | |
66 | #define OHS_FIRSTLINE 1 /* First line being read */ | |
67 | #define OHS_REDIRECT 0xa /* Looking for redirection location */ | |
68 | #define OHS_HEADERS 2 /* MIME headers being read */ | |
69 | #define OHS_ASN1_HEADER 3 /* HTTP initial header (tag+length) being read */ | |
70 | #define OHS_CONTENT 4 /* HTTP content octets being read */ | |
71 | #define OHS_WRITE_INIT (5 | OHS_NOREAD) /* 1st call: ready to start I/O */ | |
72 | #define OHS_WRITE (6 | OHS_NOREAD) /* Request being sent */ | |
73 | #define OHS_FLUSH (7 | OHS_NOREAD) /* Request being flushed */ | |
74 | #define OHS_DONE (8 | OHS_NOREAD) /* Completed */ | |
75 | #define OHS_HTTP_HEADER (9 | OHS_NOREAD) /* Headers set, w/o final \r\n */ | |
76 | ||
77 | OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio, | |
78 | int method_GET, int maxline, | |
79 | unsigned long max_resp_len, | |
80 | int timeout, | |
81 | const char *expected_content_type, | |
82 | int expect_asn1) | |
83 | { | |
84 | OSSL_HTTP_REQ_CTX *rctx; | |
85 | ||
86 | if (wbio == NULL || rbio == NULL) { | |
87 | HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); | |
88 | return NULL; | |
89 | } | |
90 | ||
91 | if ((rctx = OPENSSL_zalloc(sizeof(*rctx))) == NULL) | |
92 | return NULL; | |
93 | rctx->state = OHS_ERROR; | |
94 | rctx->iobuflen = maxline > 0 ? maxline : HTTP_DEFAULT_MAX_LINE_LENGTH; | |
95 | rctx->iobuf = OPENSSL_malloc(rctx->iobuflen); | |
96 | rctx->wbio = wbio; | |
97 | rctx->rbio = rbio; | |
98 | rctx->mem = BIO_new(BIO_s_mem()); | |
99 | if (rctx->iobuf == NULL || rctx->mem == NULL) { | |
100 | OSSL_HTTP_REQ_CTX_free(rctx); | |
101 | return NULL; | |
102 | } | |
103 | rctx->method_GET = method_GET; | |
104 | rctx->expected_ct = expected_content_type; | |
105 | rctx->expect_asn1 = expect_asn1; | |
106 | rctx->resp_len = 0; | |
107 | OSSL_HTTP_REQ_CTX_set_max_response_length(rctx, max_resp_len); | |
108 | rctx->max_time = timeout > 0 ? time(NULL) + timeout : 0; | |
109 | return rctx; | |
110 | } | |
111 | ||
112 | void OSSL_HTTP_REQ_CTX_free(OSSL_HTTP_REQ_CTX *rctx) | |
113 | { | |
114 | if (rctx == NULL) | |
115 | return; | |
116 | BIO_free(rctx->mem); /* this may indirectly call ERR_clear_error() */ | |
117 | OPENSSL_free(rctx->iobuf); | |
118 | OPENSSL_free(rctx); | |
119 | } | |
120 | ||
121 | BIO *OSSL_HTTP_REQ_CTX_get0_mem_bio(OSSL_HTTP_REQ_CTX *rctx) | |
122 | { | |
123 | if (rctx == NULL) { | |
124 | HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); | |
125 | return NULL; | |
126 | } | |
127 | return rctx->mem; | |
128 | } | |
129 | ||
130 | void OSSL_HTTP_REQ_CTX_set_max_response_length(OSSL_HTTP_REQ_CTX *rctx, | |
131 | unsigned long len) | |
132 | { | |
133 | if (rctx == NULL) { | |
134 | HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); | |
135 | return; | |
136 | } | |
137 | rctx->max_resp_len = len != 0 ? len : HTTP_DEFAULT_MAX_RESP_LEN; | |
138 | } | |
139 | ||
140 | /* | |
141 | * Create HTTP header using given op and path (or "/" in case path is NULL). | |
142 | * Server name (and port) must be given if and only if plain HTTP proxy is used. | |
143 | */ | |
144 | int OSSL_HTTP_REQ_CTX_header(OSSL_HTTP_REQ_CTX *rctx, const char *server, | |
145 | const char *port, const char *path) | |
146 | { | |
147 | if (rctx == NULL) { | |
148 | HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); | |
149 | return 0; | |
150 | } | |
151 | ||
152 | if (BIO_printf(rctx->mem, "%s ", rctx->method_GET ? "GET" : "POST") <= 0) | |
153 | return 0; | |
154 | ||
155 | if (server != NULL) { /* HTTP (but not HTTPS) proxy is used */ | |
156 | /* | |
157 | * Section 5.1.2 of RFC 1945 states that the absoluteURI form is only | |
158 | * allowed when using a proxy | |
159 | */ | |
160 | if (BIO_printf(rctx->mem, "http://%s", server) <= 0) | |
161 | return 0; | |
162 | if (port != NULL && BIO_printf(rctx->mem, ":%s", port) <= 0) | |
163 | return 0; | |
164 | } | |
165 | ||
166 | /* Make sure path includes a forward slash */ | |
167 | if (path == NULL) | |
168 | path = "/"; | |
169 | if (path[0] != '/' && BIO_printf(rctx->mem, "/") <= 0) | |
170 | return 0; | |
171 | ||
172 | if (BIO_printf(rctx->mem, "%s "HTTP_PREFIX"1.0\r\n", path) <= 0) | |
173 | return 0; | |
174 | rctx->state = OHS_HTTP_HEADER; | |
175 | return 1; | |
176 | } | |
177 | ||
178 | int OSSL_HTTP_REQ_CTX_add1_header(OSSL_HTTP_REQ_CTX *rctx, | |
179 | const char *name, const char *value) | |
180 | { | |
181 | if (rctx == NULL || name == NULL) { | |
182 | HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); | |
183 | return 0; | |
184 | } | |
185 | ||
186 | if (BIO_puts(rctx->mem, name) <= 0) | |
187 | return 0; | |
188 | if (value != NULL) { | |
189 | if (BIO_write(rctx->mem, ": ", 2) != 2) | |
190 | return 0; | |
191 | if (BIO_puts(rctx->mem, value) <= 0) | |
192 | return 0; | |
193 | } | |
194 | if (BIO_write(rctx->mem, "\r\n", 2) != 2) | |
195 | return 0; | |
196 | rctx->state = OHS_HTTP_HEADER; | |
197 | return 1; | |
198 | } | |
199 | ||
200 | static int OSSL_HTTP_REQ_CTX_content(OSSL_HTTP_REQ_CTX *rctx, | |
201 | const char *content_type, BIO *req_mem) | |
202 | { | |
203 | const unsigned char *req; | |
204 | long req_len; | |
205 | ||
206 | if (rctx == NULL || req_mem == NULL) { | |
207 | HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); | |
208 | return 0; | |
209 | } | |
210 | ||
211 | if (content_type != NULL | |
212 | && BIO_printf(rctx->mem, "Content-Type: %s\r\n", content_type) <= 0) | |
213 | return 0; | |
214 | ||
215 | if ((req_len = BIO_get_mem_data(req_mem, &req)) <= 0) | |
216 | return 0; | |
217 | rctx->state = OHS_WRITE_INIT; | |
218 | ||
219 | return BIO_printf(rctx->mem, "Content-Length: %ld\r\n\r\n", req_len) > 0 | |
220 | && BIO_write(rctx->mem, req, req_len) == (int)req_len; | |
221 | } | |
222 | ||
223 | BIO *HTTP_asn1_item2bio(const ASN1_ITEM *it, ASN1_VALUE *val) | |
224 | { | |
225 | BIO *res; | |
226 | ||
227 | if (it == NULL || val == NULL) { | |
228 | HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); | |
229 | return NULL; | |
230 | } | |
231 | ||
232 | if ((res = BIO_new(BIO_s_mem())) == NULL) | |
233 | return NULL; | |
234 | if (ASN1_item_i2d_bio(it, res, val) <= 0) { | |
235 | BIO_free(res); | |
236 | res = NULL; | |
237 | } | |
238 | return res; | |
239 | } | |
240 | ||
241 | int OSSL_HTTP_REQ_CTX_i2d(OSSL_HTTP_REQ_CTX *rctx, const char *content_type, | |
242 | const ASN1_ITEM *it, ASN1_VALUE *req) | |
243 | { | |
244 | BIO *mem; | |
245 | int res; | |
246 | ||
247 | if (rctx == NULL || it == NULL || req == NULL) { | |
248 | HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); | |
249 | return 0; | |
250 | } | |
251 | ||
252 | res = (mem = HTTP_asn1_item2bio(it, req)) != NULL | |
253 | && OSSL_HTTP_REQ_CTX_content(rctx, content_type, mem); | |
254 | BIO_free(mem); | |
255 | return res; | |
256 | } | |
257 | ||
258 | static int OSSL_HTTP_REQ_CTX_add1_headers(OSSL_HTTP_REQ_CTX *rctx, | |
259 | const STACK_OF(CONF_VALUE) *headers, | |
260 | const char *host) | |
261 | { | |
262 | int i; | |
263 | int add_host = 1; | |
264 | CONF_VALUE *hdr; | |
265 | ||
266 | for (i = 0; i < sk_CONF_VALUE_num(headers); i++) { | |
267 | hdr = sk_CONF_VALUE_value(headers, i); | |
268 | if (add_host && strcasecmp("host", hdr->name) == 0) | |
269 | add_host = 0; | |
270 | if (!OSSL_HTTP_REQ_CTX_add1_header(rctx, hdr->name, hdr->value)) | |
271 | return 0; | |
272 | } | |
273 | ||
274 | if (add_host && !OSSL_HTTP_REQ_CTX_add1_header(rctx, "Host", host)) | |
275 | return 0; | |
276 | return 1; | |
277 | } | |
278 | ||
279 | /*- | |
280 | * Create OSSL_HTTP_REQ_CTX structure using the values provided. | |
281 | * If !use_http_proxy then the 'server' and 'port' parameters are ignored. | |
282 | * If req_mem == NULL then use GET and ignore content_type, else POST. | |
283 | */ | |
284 | OSSL_HTTP_REQ_CTX *HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio, int use_http_proxy, | |
285 | const char *server, const char *port, | |
286 | const char *path, | |
287 | const STACK_OF(CONF_VALUE) *headers, | |
288 | const char *content_type, BIO *req_mem, | |
289 | int maxline, unsigned long max_resp_len, | |
290 | int timeout, | |
291 | const char *expected_content_type, | |
292 | int expect_asn1) | |
293 | { | |
294 | OSSL_HTTP_REQ_CTX *rctx; | |
295 | ||
296 | if (use_http_proxy && (server == NULL || port == NULL)) { | |
297 | HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); | |
298 | return NULL; | |
299 | } | |
300 | /* remaining parameters are checked indirectly by the functions called */ | |
301 | ||
302 | if ((rctx = OSSL_HTTP_REQ_CTX_new(wbio, rbio, req_mem == NULL, maxline, | |
303 | max_resp_len, timeout, | |
304 | expected_content_type, expect_asn1)) | |
305 | == NULL) | |
306 | return NULL; | |
307 | ||
308 | if (OSSL_HTTP_REQ_CTX_header(rctx, use_http_proxy ? server : NULL, | |
309 | port, path) | |
310 | && OSSL_HTTP_REQ_CTX_add1_headers(rctx, headers, server) | |
311 | && (req_mem == NULL | |
312 | || OSSL_HTTP_REQ_CTX_content(rctx, content_type, req_mem))) | |
313 | return rctx; | |
314 | ||
315 | OSSL_HTTP_REQ_CTX_free(rctx); | |
316 | return NULL; | |
317 | } | |
318 | ||
319 | /* | |
320 | * Parse first HTTP response line. This should be like this: "HTTP/1.0 200 OK". | |
321 | * We need to obtain the numeric code and (optional) informational message. | |
322 | */ | |
323 | ||
324 | static int parse_http_line1(char *line) | |
325 | { | |
326 | int retcode; | |
327 | char *code, *reason, *end; | |
328 | ||
329 | /* Skip to first whitespace (past protocol info) */ | |
330 | for (code = line; *code != '\0' && !ossl_isspace(*code); code++) | |
331 | continue; | |
332 | if (*code == '\0') { | |
333 | HTTPerr(0, HTTP_R_SERVER_RESPONSE_PARSE_ERROR); | |
334 | return 0; | |
335 | } | |
336 | ||
337 | /* Skip past whitespace to start of response code */ | |
338 | while (*code != '\0' && ossl_isspace(*code)) | |
339 | code++; | |
340 | ||
341 | if (*code == '\0') { | |
342 | HTTPerr(0, HTTP_R_SERVER_RESPONSE_PARSE_ERROR); | |
343 | return 0; | |
344 | } | |
345 | ||
346 | /* Find end of response code: first whitespace after start of code */ | |
347 | for (reason = code; *reason != '\0' && !ossl_isspace(*reason); reason++) | |
348 | continue; | |
349 | ||
350 | if (*reason == '\0') { | |
351 | HTTPerr(0, HTTP_R_SERVER_RESPONSE_PARSE_ERROR); | |
352 | return 0; | |
353 | } | |
354 | ||
355 | /* Set end of response code and start of message */ | |
356 | *reason++ = '\0'; | |
357 | ||
358 | /* Attempt to parse numeric code */ | |
359 | retcode = strtoul(code, &end, 10); | |
360 | ||
361 | if (*end != '\0') | |
362 | return 0; | |
363 | ||
364 | /* Skip over any leading whitespace in message */ | |
365 | while (*reason != '\0' && ossl_isspace(*reason)) | |
366 | reason++; | |
367 | ||
368 | if (*reason != '\0') { | |
369 | /* | |
370 | * Finally zap any trailing whitespace in message (include CRLF) | |
371 | */ | |
372 | ||
373 | /* chop any trailing whitespace from reason */ | |
374 | /* We know reason has a non-whitespace character so this is OK */ | |
375 | for (end = reason + strlen(reason) - 1; ossl_isspace(*end); end--) | |
376 | *end = '\0'; | |
377 | } | |
378 | ||
379 | switch (retcode) { | |
380 | case HTTP_STATUS_CODE_OK: | |
381 | case HTTP_STATUS_CODE_MOVED_PERMANENTLY: | |
382 | case HTTP_STATUS_CODE_FOUND: | |
383 | return retcode; | |
384 | default: | |
385 | if (retcode < 400) | |
386 | HTTPerr(0, HTTP_R_STATUS_CODE_UNSUPPORTED); | |
387 | else | |
388 | HTTPerr(0, HTTP_R_SERVER_SENT_ERROR); | |
389 | if (*reason == '\0') | |
390 | ERR_add_error_data(2, "Code=", code); | |
391 | else | |
392 | ERR_add_error_data(4, "Code=", code, ",Reason=", reason); | |
393 | return 0; | |
394 | } | |
395 | } | |
396 | ||
397 | static int check_set_resp_len(OSSL_HTTP_REQ_CTX *rctx, unsigned long len) | |
398 | { | |
399 | const char *tag = NULL; | |
400 | unsigned long val = 0; | |
401 | ||
402 | if (len > rctx->max_resp_len) { | |
403 | HTTPerr(0, HTTP_R_MAX_RESP_LEN_EXCEEDED); | |
404 | tag = ",max="; | |
405 | val = rctx->max_resp_len; | |
406 | } | |
407 | if (rctx->resp_len != 0 && rctx->resp_len != len) { | |
408 | HTTPerr(0, HTTP_R_INCONSISTENT_CONTENT_LENGTH); | |
409 | tag = ",before="; | |
410 | val = rctx->resp_len; | |
411 | } | |
412 | if (tag != NULL) { | |
413 | char len_str[32]; | |
414 | char str[32]; | |
415 | ||
416 | BIO_snprintf(len_str, sizeof(len_str), "%lu", len); | |
417 | BIO_snprintf(str, sizeof(str), "%lu", val); | |
418 | ERR_add_error_data(4, "length=", len_str, tag, str); | |
419 | return 0; | |
420 | } | |
421 | rctx->resp_len = len; | |
422 | return 1; | |
423 | } | |
424 | ||
425 | /* | |
426 | * Try exchanging request and response via HTTP on (non-)blocking BIO in rctx. | |
427 | * Returns 1 on success, 0 on error or redirection, -1 on BIO_should_retry. | |
428 | */ | |
429 | int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx) | |
430 | { | |
431 | int i; | |
432 | long n, n_to_send = 0; | |
433 | unsigned long resp_len; | |
434 | const unsigned char *p; | |
435 | char *key, *value, *line_end = NULL; | |
436 | ||
437 | if (rctx == NULL) { | |
438 | HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); | |
439 | return 0; | |
440 | } | |
441 | ||
442 | rctx->redirection_url = NULL; | |
443 | next_io: | |
444 | if ((rctx->state & OHS_NOREAD) == 0) { | |
445 | n = BIO_read(rctx->rbio, rctx->iobuf, rctx->iobuflen); | |
446 | if (n <= 0) { | |
447 | if (BIO_should_retry(rctx->rbio)) | |
448 | return -1; | |
449 | return 0; | |
450 | } | |
451 | ||
452 | /* Write data to memory BIO */ | |
453 | if (BIO_write(rctx->mem, rctx->iobuf, n) != n) | |
454 | return 0; | |
455 | } | |
456 | ||
457 | switch (rctx->state) { | |
458 | case OHS_HTTP_HEADER: | |
459 | /* Last operation was adding headers: need a final \r\n */ | |
460 | if (BIO_write(rctx->mem, "\r\n", 2) != 2) { | |
461 | rctx->state = OHS_ERROR; | |
462 | return 0; | |
463 | } | |
464 | rctx->state = OHS_WRITE_INIT; | |
465 | ||
466 | /* fall thru */ | |
467 | case OHS_WRITE_INIT: | |
468 | n_to_send = BIO_get_mem_data(rctx->mem, NULL); | |
469 | rctx->state = OHS_WRITE; | |
470 | ||
471 | /* fall thru */ | |
472 | case OHS_WRITE: | |
473 | n = BIO_get_mem_data(rctx->mem, &p); | |
474 | ||
475 | i = BIO_write(rctx->wbio, p + (n - n_to_send), n_to_send); | |
476 | ||
477 | if (i <= 0) { | |
478 | if (BIO_should_retry(rctx->wbio)) | |
479 | return -1; | |
480 | rctx->state = OHS_ERROR; | |
481 | return 0; | |
482 | } | |
483 | ||
484 | n_to_send -= i; | |
485 | ||
486 | if (n_to_send > 0) | |
487 | goto next_io; | |
488 | ||
489 | rctx->state = OHS_FLUSH; | |
490 | ||
491 | (void)BIO_reset(rctx->mem); | |
492 | ||
493 | /* fall thru */ | |
494 | case OHS_FLUSH: | |
495 | ||
496 | i = BIO_flush(rctx->wbio); | |
497 | ||
498 | if (i > 0) { | |
499 | rctx->state = OHS_FIRSTLINE; | |
500 | goto next_io; | |
501 | } | |
502 | ||
503 | if (BIO_should_retry(rctx->wbio)) | |
504 | return -1; | |
505 | ||
506 | rctx->state = OHS_ERROR; | |
507 | return 0; | |
508 | ||
509 | case OHS_ERROR: | |
510 | return 0; | |
511 | ||
512 | case OHS_FIRSTLINE: | |
513 | case OHS_HEADERS: | |
514 | case OHS_REDIRECT: | |
515 | ||
516 | /* Attempt to read a line in */ | |
517 | next_line: | |
518 | /* | |
519 | * Due to strange memory BIO behavior with BIO_gets we have to check | |
520 | * there's a complete line in there before calling BIO_gets or we'll | |
521 | * just get a partial read. | |
522 | */ | |
523 | n = BIO_get_mem_data(rctx->mem, &p); | |
524 | if (n <= 0 || memchr(p, '\n', n) == 0) { | |
525 | if (n >= rctx->iobuflen) { | |
526 | rctx->state = OHS_ERROR; | |
527 | return 0; | |
528 | } | |
529 | goto next_io; | |
530 | } | |
531 | n = BIO_gets(rctx->mem, (char *)rctx->iobuf, rctx->iobuflen); | |
532 | ||
533 | if (n <= 0) { | |
534 | if (BIO_should_retry(rctx->mem)) | |
535 | goto next_io; | |
536 | rctx->state = OHS_ERROR; | |
537 | return 0; | |
538 | } | |
539 | ||
540 | /* Don't allow excessive lines */ | |
541 | if (n == rctx->iobuflen) { | |
542 | HTTPerr(0, HTTP_R_RESPONSE_LINE_TOO_LONG); | |
543 | rctx->state = OHS_ERROR; | |
544 | return 0; | |
545 | } | |
546 | ||
547 | /* First line */ | |
548 | if (rctx->state == OHS_FIRSTLINE) { | |
549 | switch (parse_http_line1((char *)rctx->iobuf)) { | |
550 | case HTTP_STATUS_CODE_OK: | |
551 | rctx->state = OHS_HEADERS; | |
552 | goto next_line; | |
553 | case HTTP_STATUS_CODE_MOVED_PERMANENTLY: | |
554 | case HTTP_STATUS_CODE_FOUND: /* i.e., moved temporarily */ | |
555 | if (rctx->method_GET) { | |
556 | rctx->state = OHS_REDIRECT; | |
557 | goto next_line; | |
558 | } | |
559 | HTTPerr(0, HTTP_R_REDIRECTION_NOT_ENABLED); | |
560 | /* redirection is not supported/recommended for POST */ | |
561 | /* fall through */ | |
562 | default: | |
563 | rctx->state = OHS_ERROR; | |
564 | return 0; | |
565 | } | |
566 | } | |
567 | key = (char *)rctx->iobuf; | |
568 | value = strchr(key, ':'); | |
569 | if (value != NULL) { | |
570 | *(value++) = '\0'; | |
571 | while (ossl_isspace(*value)) | |
572 | value++; | |
573 | line_end = strchr(value, '\r'); | |
574 | if (line_end == NULL) | |
575 | line_end = strchr(value, '\n'); | |
576 | if (line_end != NULL) | |
577 | *line_end = '\0'; | |
578 | } | |
579 | if (value != NULL && line_end != NULL) { | |
580 | if (rctx->state == OHS_REDIRECT && strcmp(key, "Location") == 0) { | |
581 | rctx->redirection_url = value; | |
582 | return 0; | |
583 | } | |
584 | if (rctx->expected_ct != NULL && strcmp(key, "Content-Type") == 0) { | |
585 | if (strcmp(rctx->expected_ct, value) != 0) { | |
586 | HTTPerr(0, HTTP_R_UNEXPECTED_CONTENT_TYPE); | |
587 | ERR_add_error_data(4, "expected=", rctx->expected_ct, | |
588 | ",actual=", value); | |
589 | return 0; | |
590 | } | |
591 | rctx->expected_ct = NULL; /* content-type has been found */ | |
592 | } | |
593 | if (strcmp(key, "Content-Length") == 0) { | |
594 | resp_len = strtoul(value, &line_end, 10); | |
595 | if (line_end == value || *line_end != '\0') { | |
596 | HTTPerr(0, HTTP_R_ERROR_PARSING_CONTENT_LENGTH); | |
597 | ERR_add_error_data(2, "input=", value); | |
598 | return 0; | |
599 | } | |
600 | if (!check_set_resp_len(rctx, resp_len)) | |
601 | return 0; | |
602 | } | |
603 | } | |
604 | ||
605 | /* Look for blank line: end of headers */ | |
606 | for (p = rctx->iobuf; *p != '\0' ; p++) { | |
607 | if (*p != '\r' && *p != '\n') | |
608 | break; | |
609 | } | |
610 | if (*p != '\0') /* not end of headers */ | |
611 | goto next_line; | |
612 | ||
613 | if (rctx->expected_ct != NULL) { | |
614 | HTTPerr(0, HTTP_R_MISSING_CONTENT_TYPE); | |
615 | ERR_add_error_data(2, "expected=", rctx->expected_ct); | |
616 | return 0; | |
617 | } | |
618 | if (rctx->state == OHS_REDIRECT) { | |
619 | /* http status code indicated redirect but there was no Location */ | |
620 | HTTPerr(0, HTTP_R_MISSING_REDIRECT_LOCATION); | |
621 | return 0; | |
622 | } | |
623 | ||
624 | if (!rctx->expect_asn1) { | |
625 | rctx->state = OHS_CONTENT; | |
626 | goto content; | |
627 | } | |
628 | ||
629 | rctx->state = OHS_ASN1_HEADER; | |
630 | ||
631 | /* Fall thru */ | |
632 | case OHS_ASN1_HEADER: | |
633 | /* | |
634 | * Now reading ASN1 header: can read at least 2 bytes which is enough | |
635 | * for ASN1 SEQUENCE header and either length field or at least the | |
636 | * length of the length field. | |
637 | */ | |
638 | n = BIO_get_mem_data(rctx->mem, &p); | |
639 | if (n < 2) | |
640 | goto next_io; | |
641 | ||
642 | /* Check it is an ASN1 SEQUENCE */ | |
643 | if (*p++ != (V_ASN1_SEQUENCE | V_ASN1_CONSTRUCTED)) { | |
644 | HTTPerr(0, HTTP_R_MISSING_ASN1_ENCODING); | |
645 | return 0; | |
646 | } | |
647 | ||
648 | /* Check out length field */ | |
649 | if ((*p & 0x80) != 0) { | |
650 | /* | |
651 | * If MSB set on initial length octet we can now always read 6 | |
652 | * octets: make sure we have them. | |
653 | */ | |
654 | if (n < 6) | |
655 | goto next_io; | |
656 | n = *p & 0x7F; | |
657 | /* Not NDEF or excessive length */ | |
658 | if (n == 0 || (n > 4)) { | |
659 | HTTPerr(0, HTTP_R_ERROR_PARSING_ASN1_LENGTH); | |
660 | return 0; | |
661 | } | |
662 | p++; | |
663 | resp_len = 0; | |
664 | for (i = 0; i < n; i++) { | |
665 | resp_len <<= 8; | |
666 | resp_len |= *p++; | |
667 | } | |
668 | resp_len += n + 2; | |
669 | } else { | |
670 | resp_len = *p + 2; | |
671 | } | |
672 | if (!check_set_resp_len(rctx, resp_len)) | |
673 | return 0; | |
674 | ||
675 | content: | |
676 | rctx->state = OHS_CONTENT; | |
677 | ||
678 | /* Fall thru */ | |
679 | case OHS_CONTENT: | |
680 | default: | |
681 | n = BIO_get_mem_data(rctx->mem, NULL); | |
682 | if (n < (long)rctx->resp_len /* may be 0 if no Content-Type or ASN.1 */) | |
683 | goto next_io; | |
684 | ||
685 | rctx->state = OHS_DONE; | |
686 | return 1; | |
687 | } | |
688 | } | |
689 | ||
690 | #ifndef OPENSSL_NO_SOCK | |
691 | ||
692 | /* set up a new connection BIO, to HTTP server or to HTTP(S) proxy if given */ | |
693 | static BIO *HTTP_new_bio(const char *server, const char *server_port, | |
694 | const char *proxy, const char *proxy_port) | |
695 | { | |
696 | const char *host = server; | |
697 | const char *port = server_port; | |
698 | BIO *cbio; | |
699 | ||
700 | if (server == NULL) { | |
701 | HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); | |
702 | return NULL; | |
703 | } | |
704 | ||
705 | if (proxy != NULL) { | |
706 | host = proxy; | |
707 | port = proxy_port; | |
708 | } | |
709 | cbio = BIO_new_connect(host); | |
710 | if (cbio == NULL) | |
711 | goto end; | |
712 | if (port != NULL) | |
713 | (void)BIO_set_conn_port(cbio, port); | |
714 | ||
715 | end: | |
716 | return cbio; | |
717 | } | |
e8d0819d | 718 | #endif /* OPENSSL_NO_SOCK */ |
29f178bd DDO |
719 | |
720 | static ASN1_VALUE *BIO_mem_d2i(BIO *mem, const ASN1_ITEM *it) | |
721 | { | |
722 | const unsigned char *p; | |
723 | long len = BIO_get_mem_data(mem, &p); | |
724 | ASN1_VALUE *resp = ASN1_item_d2i(NULL, &p, len, it); | |
725 | ||
726 | if (resp == NULL) | |
727 | HTTPerr(0, HTTP_R_SERVER_RESPONSE_PARSE_ERROR); | |
728 | return resp; | |
729 | } | |
730 | ||
731 | static BIO *OSSL_HTTP_REQ_CTX_transfer(OSSL_HTTP_REQ_CTX *rctx) | |
732 | { | |
733 | int sending = 1; | |
734 | int rv; | |
735 | ||
736 | if (rctx == NULL) { | |
737 | HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); | |
738 | return NULL; | |
739 | } | |
740 | ||
741 | for (;;) { | |
742 | rv = OSSL_HTTP_REQ_CTX_nbio(rctx); | |
743 | if (rv != -1) | |
744 | break; | |
745 | /* BIO_should_retry was true */ | |
746 | sending = 0; | |
747 | /* will not actually wait if rctx->max_time == 0 */ | |
e8d0819d | 748 | if (BIO_wait(rctx->rbio, rctx->max_time, 100 /* milliseconds */) <= 0) |
29f178bd DDO |
749 | return NULL; |
750 | } | |
751 | ||
752 | if (rv == 0) { | |
753 | if (rctx->redirection_url == NULL) { /* an error occurred */ | |
754 | if (sending && (rctx->state & OHS_NOREAD) != 0) | |
755 | HTTPerr(0, HTTP_R_ERROR_SENDING); | |
756 | else | |
757 | HTTPerr(0, HTTP_R_ERROR_RECEIVING); | |
758 | } | |
759 | return NULL; | |
760 | } | |
761 | if (!BIO_up_ref(rctx->mem)) | |
762 | return NULL; | |
763 | return rctx->mem; | |
764 | } | |
765 | ||
766 | /* Exchange ASN.1-encoded request and response via HTTP on (non-)blocking BIO */ | |
767 | ASN1_VALUE *OSSL_HTTP_REQ_CTX_sendreq_d2i(OSSL_HTTP_REQ_CTX *rctx, | |
768 | const ASN1_ITEM *it) | |
769 | { | |
770 | if (rctx == NULL || it == NULL) { | |
771 | HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); | |
772 | return NULL; | |
773 | } | |
774 | return BIO_mem_d2i(OSSL_HTTP_REQ_CTX_transfer(rctx), it); | |
775 | } | |
776 | ||
777 | static int update_timeout(int timeout, time_t start_time) | |
778 | { | |
779 | long elapsed_time; | |
780 | ||
781 | if (timeout == 0) | |
782 | return 0; | |
783 | elapsed_time = (long)(time(NULL) - start_time); /* this might overflow */ | |
784 | return timeout <= elapsed_time ? -1 : timeout - elapsed_time; | |
785 | } | |
786 | ||
787 | /*- | |
788 | * Exchange HTTP request and response with the given server. | |
789 | * If req_mem == NULL then use GET and ignore content_type, else POST. | |
790 | * The redirection_url output (freed by caller) parameter is used only for GET. | |
791 | * | |
792 | * Typically the bio and rbio parameters are NULL and a network BIO is created | |
793 | * internally for connecting to the given server and port, optionally via a | |
794 | * proxy and its port, and is then used for exchanging the request and response. | |
795 | * If bio is given and rbio is NULL then this BIO is used instead. | |
796 | * If both bio and rbio are given (which may be memory BIOs for instance) | |
797 | * then no explicit connection is attempted, | |
798 | * bio is used for writing the request, and rbio for reading the response. | |
799 | * | |
800 | * bio_update_fn is an optional BIO connect/disconnect callback function, | |
801 | * which has the prototype | |
802 | * BIO *(*OSSL_HTTP_bio_cb_t) (BIO *bio, void *arg, int conn, int detail); | |
803 | * The callback may modify the HTTP BIO provided in the bio argument, | |
804 | * whereby it may make use of any custom defined argument 'arg'. | |
805 | * During connection establishment, just after BIO_connect_retry(), | |
806 | * the callback function is invoked with the 'conn' argument being 1 | |
807 | * 'detail' indicating whether a HTTPS (i.e., TLS) connection is requested. | |
808 | * On disconnect 'conn' is 0 and 'detail' indicates that no error occurred. | |
809 | * For instance, on connect the funct may prepend a TLS BIO to implement HTTPS; | |
810 | * after disconnect it may do some error diagnostics and/or specific cleanup. | |
811 | * The function should return NULL to indicate failure. | |
812 | * After disconnect the modified BIO will be deallocated using BIO_free_all(). | |
813 | */ | |
814 | BIO *OSSL_HTTP_transfer(const char *server, const char *port, const char *path, | |
815 | int use_ssl, const char *proxy, const char *proxy_port, | |
816 | BIO *bio, BIO *rbio, | |
817 | OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, | |
818 | const STACK_OF(CONF_VALUE) *headers, | |
819 | const char *content_type, BIO *req_mem, | |
820 | int maxline, unsigned long max_resp_len, int timeout, | |
821 | const char *expected_ct, int expect_asn1, | |
822 | char **redirection_url) | |
823 | { | |
824 | time_t start_time = timeout > 0 ? time(NULL) : 0; | |
825 | BIO *cbio; /* = bio if present, used as connection BIO if rbio is NULL */ | |
826 | OSSL_HTTP_REQ_CTX *rctx; | |
827 | BIO *resp = NULL; | |
828 | ||
829 | if (redirection_url != NULL) | |
830 | *redirection_url = NULL; /* do this beforehand to prevent dbl free */ | |
831 | ||
832 | if (use_ssl && bio_update_fn == NULL) { | |
833 | HTTPerr(0, HTTP_R_TLS_NOT_ENABLED); | |
834 | return NULL; | |
835 | } | |
836 | if (rbio != NULL && (bio == NULL || bio_update_fn != NULL)) { | |
837 | HTTPerr(0, ERR_R_PASSED_INVALID_ARGUMENT); | |
838 | return NULL; | |
839 | } | |
840 | /* remaining parameters are checked indirectly by the functions called */ | |
841 | ||
842 | if (bio != NULL) | |
843 | cbio = bio; | |
e8d0819d DDO |
844 | else |
845 | #ifndef OPENSSL_NO_SOCK | |
846 | if ((cbio = HTTP_new_bio(server, port, proxy, proxy_port)) == NULL) | |
847 | return NULL; | |
848 | #else | |
29f178bd | 849 | return NULL; |
e8d0819d | 850 | #endif |
29f178bd DDO |
851 | |
852 | (void)ERR_set_mark(); /* prepare removing any spurious libssl errors */ | |
853 | if (rbio == NULL && BIO_connect_retry(cbio, timeout) <= 0) | |
854 | goto end; | |
855 | /* now timeout is guaranteed to be >= 0 */ | |
856 | ||
857 | /* callback can be used to wrap or prepend TLS session */ | |
858 | if (bio_update_fn != NULL) { | |
859 | BIO *orig_bio = cbio; | |
860 | cbio = (*bio_update_fn)(cbio, arg, 1 /* connect */, use_ssl); | |
861 | if (cbio == NULL) { | |
862 | cbio = orig_bio; | |
863 | goto end; | |
864 | } | |
865 | } | |
866 | ||
867 | rctx = HTTP_REQ_CTX_new(cbio, rbio != NULL ? rbio : cbio, | |
868 | !use_ssl && proxy != NULL, server, port, path, | |
869 | headers, content_type, req_mem, maxline, | |
870 | max_resp_len, update_timeout(timeout, start_time), | |
871 | expected_ct, expect_asn1); | |
872 | if (rctx == NULL) | |
873 | goto end; | |
874 | ||
875 | resp = OSSL_HTTP_REQ_CTX_transfer(rctx); | |
876 | if (resp == NULL) { | |
877 | if (rctx->redirection_url != NULL) { | |
878 | if (redirection_url == NULL) | |
879 | HTTPerr(0, HTTP_R_REDIRECTION_NOT_ENABLED); | |
880 | else | |
881 | /* may be NULL if out of memory: */ | |
882 | *redirection_url = OPENSSL_strdup(rctx->redirection_url); | |
883 | } else { | |
884 | char buf[200]; | |
885 | unsigned long err = ERR_peek_error(); | |
886 | int lib = ERR_GET_LIB(err); | |
887 | int reason = ERR_GET_REASON(err); | |
888 | ||
889 | if (lib == ERR_LIB_SSL || lib == ERR_LIB_HTTP | |
890 | || (lib == ERR_LIB_BIO && reason == BIO_R_CONNECT_TIMEOUT) | |
891 | || (lib == ERR_LIB_BIO && reason == BIO_R_CONNECT_ERROR) | |
100cc8b0 | 892 | # ifndef OPENSSL_NO_CMP |
29f178bd | 893 | || (lib == ERR_LIB_CMP |
100cc8b0 DDO |
894 | && reason == CMP_R_POTENTIALLY_INVALID_CERTIFICATE) |
895 | # endif | |
896 | ) { | |
29f178bd DDO |
897 | BIO_snprintf(buf, 200, "server=%s:%s", server, port); |
898 | ERR_add_error_data(1, buf); | |
899 | if (err == 0) { | |
900 | BIO_snprintf(buf, 200, "server has disconnected%s", | |
901 | use_ssl ? " violating the protocol" : | |
902 | ", likely because it requires the use of TLS"); | |
903 | ERR_add_error_data(1, buf); | |
904 | } | |
905 | } | |
906 | } | |
907 | } | |
908 | OSSL_HTTP_REQ_CTX_free(rctx); | |
909 | ||
910 | /* callback can be used to clean up TLS session */ | |
911 | if (bio_update_fn != NULL | |
912 | && (*bio_update_fn)(cbio, arg, 0, resp != NULL) == NULL) { | |
913 | BIO_free(resp); | |
914 | resp = NULL; | |
915 | } | |
916 | ||
917 | end: | |
918 | /* | |
919 | * Use BIO_free_all() because bio_update_fn may prepend or append to cbio. | |
920 | * This also frees any (e.g., SSL/TLS) BIOs linked with bio and, | |
921 | * like BIO_reset(bio), calls SSL_shutdown() to notify/alert the peer. | |
922 | */ | |
923 | if (bio == NULL) /* cbio was not provided by caller */ | |
924 | BIO_free_all(cbio); | |
925 | ||
926 | if (resp != NULL) | |
927 | /* remove any spurious error queue entries by ssl_add_cert_chain() */ | |
928 | (void)ERR_pop_to_mark(); | |
929 | else | |
930 | (void)ERR_clear_last_mark(); | |
931 | ||
932 | return resp; | |
933 | } | |
934 | ||
935 | static int redirection_ok(int n_redir, const char *old_url, const char *new_url) | |
936 | { | |
937 | static const char https[] = "https:"; | |
938 | int https_len = 6; /* strlen(https) */ | |
939 | ||
940 | if (n_redir >= HTTP_VERSION_MAX_REDIRECTIONS) { | |
941 | HTTPerr(0, HTTP_R_TOO_MANY_REDIRECTIONS); | |
942 | return 0; | |
943 | } | |
944 | if (*new_url == '/') /* redirection to same server => same protocol */ | |
945 | return 1; | |
946 | if (strncmp(old_url, https, https_len) == 0 && | |
947 | strncmp(new_url, https, https_len) != 0) { | |
948 | HTTPerr(0, HTTP_R_REDIRECTION_FROM_HTTPS_TO_HTTP); | |
949 | return 0; | |
950 | } | |
951 | return 1; | |
952 | } | |
953 | ||
954 | /* Get data via HTTP from server at given URL, potentially with redirection */ | |
955 | BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *proxy_port, | |
956 | BIO *bio, BIO *rbio, | |
957 | OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, | |
958 | const STACK_OF(CONF_VALUE) *headers, | |
959 | int maxline, unsigned long max_resp_len, int timeout, | |
960 | const char *expected_content_type, int expect_asn1) | |
961 | { | |
962 | time_t start_time = timeout > 0 ? time(NULL) : 0; | |
963 | char *current_url, *redirection_url; | |
964 | int n_redirs = 0; | |
965 | char *host; | |
966 | char *port; | |
967 | char *path; | |
968 | int use_ssl; | |
969 | BIO *resp = NULL; | |
970 | ||
971 | if (url == NULL) { | |
972 | HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); | |
973 | return NULL; | |
974 | } | |
975 | if ((current_url = OPENSSL_strdup(url)) == NULL) | |
976 | return NULL; | |
977 | ||
978 | for (;;) { | |
979 | if (!OSSL_HTTP_parse_url(current_url, &host, &port, &path, &use_ssl)) | |
980 | break; | |
981 | ||
982 | new_rpath: | |
983 | resp = OSSL_HTTP_transfer(host, port, path, use_ssl, proxy, proxy_port, | |
984 | bio, rbio, | |
985 | bio_update_fn, arg, headers, NULL, NULL, | |
986 | maxline, max_resp_len, | |
987 | update_timeout(timeout, start_time), | |
988 | expected_content_type, expect_asn1, | |
989 | &redirection_url); | |
990 | OPENSSL_free(path); | |
991 | if (resp == NULL && redirection_url != NULL) { | |
992 | if (redirection_ok(++n_redirs, current_url, redirection_url)) { | |
993 | (void)BIO_reset(bio); | |
994 | OPENSSL_free(current_url); | |
995 | current_url = redirection_url; | |
996 | if (*redirection_url == '/') { /* redirection to same server */ | |
997 | path = OPENSSL_strdup(redirection_url); | |
998 | goto new_rpath; | |
999 | } | |
1000 | OPENSSL_free(host); | |
1001 | OPENSSL_free(port); | |
1002 | continue; | |
1003 | } | |
1004 | OPENSSL_free(redirection_url); | |
1005 | } | |
1006 | OPENSSL_free(host); | |
1007 | OPENSSL_free(port); | |
1008 | break; | |
1009 | } | |
1010 | OPENSSL_free(current_url); | |
1011 | return resp; | |
1012 | } | |
1013 | ||
1014 | /* Get ASN.1-encoded data via HTTP from server at given URL */ | |
1015 | ASN1_VALUE *OSSL_HTTP_get_asn1(const char *url, | |
1016 | const char *proxy, const char *proxy_port, | |
1017 | BIO *bio, BIO *rbio, | |
1018 | OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, | |
1019 | const STACK_OF(CONF_VALUE) *headers, | |
1020 | int maxline, unsigned long max_resp_len, | |
1021 | int timeout, const char *expected_content_type, | |
1022 | const ASN1_ITEM *it) | |
1023 | { | |
1024 | BIO *mem; | |
1025 | ASN1_VALUE *resp = NULL; | |
1026 | ||
1027 | if (url == NULL || it == NULL) { | |
1028 | HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); | |
1029 | return NULL; | |
1030 | } | |
1031 | if ((mem = OSSL_HTTP_get(url, proxy, proxy_port, bio, rbio, bio_update_fn, | |
1032 | arg, headers, maxline, max_resp_len, timeout, | |
1033 | expected_content_type, 1 /* expect_asn1 */)) | |
1034 | != NULL) | |
1035 | resp = BIO_mem_d2i(mem, it); | |
1036 | BIO_free(mem); | |
1037 | return resp; | |
1038 | } | |
1039 | ||
1040 | /* Post ASN.1-encoded request via HTTP to server return ASN.1 response */ | |
1041 | ASN1_VALUE *OSSL_HTTP_post_asn1(const char *server, const char *port, | |
1042 | const char *path, int use_ssl, | |
1043 | const char *proxy, const char *proxy_port, | |
1044 | BIO *bio, BIO *rbio, | |
1045 | OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, | |
1046 | const STACK_OF(CONF_VALUE) *headers, | |
1047 | const char *content_type, | |
1048 | ASN1_VALUE *req, const ASN1_ITEM *req_it, | |
1049 | int maxline, unsigned long max_resp_len, | |
1050 | int timeout, const char *expected_ct, | |
1051 | const ASN1_ITEM *rsp_it) | |
1052 | { | |
1053 | BIO *req_mem; | |
1054 | BIO *res_mem; | |
1055 | ASN1_VALUE *resp = NULL; | |
1056 | ||
1057 | if (req == NULL) { | |
1058 | HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); | |
1059 | return NULL; | |
1060 | } | |
1061 | /* remaining parameters are checked indirectly */ | |
1062 | ||
1063 | req_mem = HTTP_asn1_item2bio(req_it, req); | |
1064 | res_mem = OSSL_HTTP_transfer(server, port, path, use_ssl, proxy, proxy_port, | |
1065 | bio, rbio, | |
1066 | bio_update_fn, arg, headers, content_type, | |
1067 | req_mem /* may be NULL */, maxline, | |
1068 | max_resp_len, timeout, | |
1069 | expected_ct, 1 /* expect_asn1 */, NULL); | |
1070 | BIO_free(req_mem); | |
1071 | if (res_mem != NULL) | |
1072 | resp = BIO_mem_d2i(res_mem, rsp_it); | |
1073 | BIO_free(res_mem); | |
1074 | return resp; | |
1075 | } | |
1076 | ||
1077 | /* BASE64 encoder used for encoding basic proxy authentication credentials */ | |
1078 | static char *base64encode(const void *buf, size_t len) | |
1079 | { | |
1080 | int i; | |
1081 | size_t outl; | |
1082 | char *out; | |
1083 | ||
1084 | /* Calculate size of encoded data */ | |
1085 | outl = (len / 3); | |
1086 | if (len % 3 > 0) | |
1087 | outl++; | |
1088 | outl <<= 2; | |
1089 | out = OPENSSL_malloc(outl + 1); | |
1090 | if (out == NULL) | |
1091 | return 0; | |
1092 | ||
1093 | i = EVP_EncodeBlock((unsigned char *)out, buf, len); | |
1094 | if (!ossl_assert(0 <= i && (size_t)i <= outl)) { | |
1095 | OPENSSL_free(out); | |
1096 | return NULL; | |
1097 | } | |
1098 | return out; | |
1099 | } | |
1100 | ||
1101 | /* | |
1102 | * Promote the given connection BIO using the CONNECT method for a TLS proxy. | |
1103 | * This is typically called by an app, so bio_err and prog are used unless NULL | |
1104 | * to print additional diagnostic information in a user-oriented way. | |
1105 | */ | |
1106 | int OSSL_HTTP_proxy_connect(BIO *bio, const char *server, const char *port, | |
1107 | const char *proxyuser, const char *proxypass, | |
1108 | int timeout, BIO *bio_err, const char *prog) | |
1109 | { | |
1110 | # undef BUF_SIZE | |
1111 | # define BUF_SIZE (8 * 1024) | |
1112 | char *mbuf = OPENSSL_malloc(BUF_SIZE); | |
1113 | char *mbufp; | |
1114 | int read_len = 0; | |
29f178bd DDO |
1115 | int ret = 0; |
1116 | BIO *fbio = BIO_new(BIO_f_buffer()); | |
e8d0819d | 1117 | int rv; |
29f178bd DDO |
1118 | time_t max_time = timeout > 0 ? time(NULL) + timeout : 0; |
1119 | ||
1120 | if (bio == NULL || server == NULL || port == NULL | |
1121 | || (bio_err != NULL && prog == NULL)) { | |
1122 | HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); | |
1123 | goto end; | |
1124 | } | |
1125 | ||
1126 | if (mbuf == NULL || fbio == NULL) { | |
1127 | BIO_printf(bio_err /* may be NULL */, "%s: out of memory", prog); | |
1128 | goto end; | |
1129 | } | |
1130 | BIO_push(fbio, bio); | |
1131 | ||
1132 | BIO_printf(fbio, "CONNECT %s:%s "HTTP_PREFIX"1.0\r\n", server, port); | |
1133 | ||
1134 | /* | |
1135 | * Workaround for broken proxies which would otherwise close | |
1136 | * the connection when entering tunnel mode (e.g., Squid 2.6) | |
1137 | */ | |
1138 | BIO_printf(fbio, "Proxy-Connection: Keep-Alive\r\n"); | |
1139 | ||
1140 | /* Support for basic (base64) proxy authentication */ | |
1141 | if (proxyuser != NULL) { | |
1142 | size_t len = strlen(proxyuser) + 1; | |
1143 | char *proxyauth, *proxyauthenc = NULL; | |
1144 | ||
1145 | if (proxypass != NULL) | |
1146 | len += strlen(proxypass); | |
1147 | proxyauth = OPENSSL_malloc(len + 1); | |
1148 | if (proxyauth == NULL) | |
1149 | goto end; | |
1150 | if (BIO_snprintf(proxyauth, len + 1, "%s:%s", proxyuser, | |
1151 | proxypass != NULL ? proxypass : "") != (int)len) | |
1152 | goto proxy_end; | |
1153 | proxyauthenc = base64encode(proxyauth, len); | |
1154 | if (proxyauthenc != NULL) { | |
1155 | BIO_printf(fbio, "Proxy-Authorization: Basic %s\r\n", proxyauthenc); | |
1156 | OPENSSL_clear_free(proxyauthenc, strlen(proxyauthenc)); | |
1157 | } | |
1158 | proxy_end: | |
1159 | OPENSSL_clear_free(proxyauth, len); | |
1160 | if (proxyauthenc == NULL) | |
1161 | goto end; | |
1162 | } | |
1163 | ||
1164 | /* Terminate the HTTP CONNECT request */ | |
1165 | BIO_printf(fbio, "\r\n"); | |
1166 | ||
1167 | for (;;) { | |
1168 | if (BIO_flush(fbio) != 0) | |
1169 | break; | |
1170 | /* potentially needs to be retried if BIO is non-blocking */ | |
1171 | if (!BIO_should_retry(fbio)) | |
1172 | break; | |
1173 | } | |
1174 | ||
1175 | for (;;) { | |
1176 | /* will not actually wait if timeout == 0 */ | |
e8d0819d | 1177 | rv = BIO_wait(fbio, max_time, 100 /* milliseconds */); |
29f178bd DDO |
1178 | if (rv <= 0) { |
1179 | BIO_printf(bio_err, "%s: HTTP CONNECT %s\n", prog, | |
1180 | rv == 0 ? "timed out" : "failed waiting for data"); | |
1181 | goto end; | |
1182 | } | |
1183 | ||
1184 | /*- | |
1185 | * The first line is the HTTP response. | |
1186 | * According to RFC 7230, it is formatted exactly like this: | |
1187 | * HTTP/d.d ddd Reason text\r\n | |
1188 | */ | |
1189 | read_len = BIO_gets(fbio, mbuf, BUF_SIZE); | |
1190 | /* the BIO may not block, so we must wait for the 1st line to come in */ | |
1191 | if (read_len < HTTP_LINE1_MINLEN) | |
1192 | continue; | |
1193 | ||
1194 | /* RFC 7231 4.3.6: any 2xx status code is valid */ | |
1195 | if (strncmp(mbuf, HTTP_PREFIX, strlen(HTTP_PREFIX)) != 0) { | |
1196 | HTTPerr(0, HTTP_R_SERVER_RESPONSE_PARSE_ERROR); | |
1197 | BIO_printf(bio_err, "%s: HTTP CONNECT failed, non-HTTP response\n", | |
1198 | prog); | |
1199 | /* Wrong protocol, not even HTTP, so stop reading headers */ | |
1200 | goto end; | |
1201 | } | |
1202 | mbufp = mbuf + strlen(HTTP_PREFIX); | |
1203 | if (strncmp(mbufp, HTTP_VERSION_PATT, strlen(HTTP_VERSION_PATT)) != 0) { | |
1204 | HTTPerr(0, HTTP_R_SERVER_SENT_WRONG_HTTP_VERSION); | |
1205 | BIO_printf(bio_err, | |
1206 | "%s: HTTP CONNECT failed, bad HTTP version %.*s\n", | |
1207 | prog, HTTP_VERSION_STR_LEN, mbufp); | |
1208 | goto end; | |
1209 | } | |
1210 | mbufp += HTTP_VERSION_STR_LEN; | |
1211 | if (strncmp(mbufp, " 2", strlen(" 2")) != 0) { | |
1212 | mbufp += 1; | |
1213 | /* chop any trailing whitespace */ | |
1214 | while (read_len > 0 && ossl_isspace(mbuf[read_len - 1])) | |
1215 | read_len--; | |
1216 | mbuf[read_len] = '\0'; | |
1217 | HTTPerr(0, HTTP_R_CONNECT_FAILURE); | |
1218 | ERR_add_error_data(2, "Reason=", mbufp); | |
1219 | BIO_printf(bio_err, "%s: HTTP CONNECT failed, Reason=%s\n", | |
1220 | prog, mbufp); | |
1221 | goto end; | |
1222 | } | |
1223 | ret = 1; | |
1224 | break; | |
1225 | } | |
1226 | ||
1227 | /* Read past all following headers */ | |
1228 | do { | |
1229 | /* | |
1230 | * TODO: This does not necessarily catch the case when the full | |
1231 | * HTTP response came in in more than a single TCP message. | |
1232 | */ | |
1233 | read_len = BIO_gets(fbio, mbuf, BUF_SIZE); | |
1234 | } while (read_len > 2); | |
1235 | ||
1236 | end: | |
1237 | if (fbio != NULL) { | |
1238 | (void)BIO_flush(fbio); | |
1239 | BIO_pop(fbio); | |
1240 | BIO_free(fbio); | |
1241 | } | |
1242 | OPENSSL_free(mbuf); | |
1243 | return ret; | |
1244 | # undef BUF_SIZE | |
1245 | } | |
1246 |