1 /* 2 * Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved. 3 * Copyright Siemens AG 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 <openssl/http.h> 12 #include <openssl/pem.h> 13 #include <openssl/x509v3.h> 14 #include <string.h> 15 16 #include "testutil.h" 17 18 static const ASN1_ITEM *x509_it = NULL; 19 static X509 *x509 = NULL; 20 #define RPATH "/path/result.crt" 21 22 typedef struct { 23 BIO *out; 24 char version; 25 int keep_alive; 26 } server_args; 27 28 /*- 29 * Pretty trivial HTTP mock server: 30 * For POST, copy request headers+body from mem BIO 'in' as response to 'out'. 31 * For GET, redirect to RPATH, else respond with 'rsp' of ASN1 type 'it'. 32 * Respond with HTTP version 1.'version' and 'keep_alive' (unless implicit). 33 */ 34 static int mock_http_server(BIO *in, BIO *out, char version, int keep_alive, 35 ASN1_VALUE *rsp, const ASN1_ITEM *it) 36 { 37 const char *req, *path; 38 long count = BIO_get_mem_data(in, (unsigned char **)&req); 39 const char *hdr = (char *)req; 40 int is_get = count >= 4 && strncmp(hdr, "GET ", 4) == 0; 41 int len; 42 43 /* first line should contain "<GET or POST> <path> HTTP/1.x" */ 44 if (is_get) 45 hdr += 4; 46 else if (TEST_true(count >= 5 && strncmp(hdr, "POST ", 5) == 0)) 47 hdr += 5; 48 else 49 return 0; 50 51 path = hdr; 52 hdr = strchr(hdr, ' '); 53 if (hdr == NULL) 54 return 0; 55 len = strlen("HTTP/1."); 56 if (!TEST_strn_eq(++hdr, "HTTP/1.", len)) 57 return 0; 58 hdr += len; 59 /* check for HTTP version 1.0 .. 1.1 */ 60 if (!TEST_char_le('0', *hdr) || !TEST_char_le(*hdr++, '1')) 61 return 0; 62 if (!TEST_char_eq(*hdr++, '\r') || !TEST_char_eq(*hdr++, '\n')) 63 return 0; 64 count -= (hdr - req); 65 if (count < 0 || out == NULL) 66 return 0; 67 68 if (strncmp(path, RPATH, strlen(RPATH)) != 0) { 69 if (!is_get) 70 return 0; 71 return BIO_printf(out, "HTTP/1.%c 301 Moved Permanently\r\n" 72 "Location: %s\r\n\r\n", 73 version, RPATH) > 0; /* same server */ 74 } 75 if (BIO_printf(out, "HTTP/1.%c 200 OK\r\n", version) <= 0) 76 return 0; 77 if ((version == '0') == keep_alive) /* otherwise, default */ 78 if (BIO_printf(out, "Connection: %s\r\n", 79 version == '0' ? "keep-alive" : "close") <= 0) 80 return 0; 81 if (is_get) { /* construct new header and body */ 82 if ((len = ASN1_item_i2d(rsp, NULL, it)) <= 0) 83 return 0; 84 if (BIO_printf(out, "Content-Type: application/x-x509-ca-cert\r\n" 85 "Content-Length: %d\r\n\r\n", len) <= 0) 86 return 0; 87 return ASN1_item_i2d_bio(it, out, rsp); 88 } else { 89 len = strlen("Connection: "); 90 if (strncmp(hdr, "Connection: ", len) == 0) { 91 /* skip req Connection header */ 92 hdr = strstr(hdr + len, "\r\n"); 93 if (hdr == NULL) 94 return 0; 95 hdr += 2; 96 } 97 /* echo remaining request header and body */ 98 return BIO_write(out, hdr, count) == count; 99 } 100 } 101 102 static long http_bio_cb_ex(BIO *bio, int oper, const char *argp, size_t len, 103 int cmd, long argl, int ret, size_t *processed) 104 { 105 server_args *args = (server_args *)BIO_get_callback_arg(bio); 106 107 if (oper == (BIO_CB_CTRL | BIO_CB_RETURN) && cmd == BIO_CTRL_FLUSH) 108 ret = mock_http_server(bio, args->out, args->version, args->keep_alive, 109 (ASN1_VALUE *)x509, x509_it); 110 return ret; 111 } 112 113 static int test_http_x509(int do_get) 114 { 115 X509 *rcert = NULL; 116 BIO *wbio = BIO_new(BIO_s_mem()); 117 BIO *rbio = BIO_new(BIO_s_mem()); 118 server_args mock_args = { NULL, '0', 0 }; 119 BIO *rsp, *req = ASN1_item_i2d_mem_bio(x509_it, (ASN1_VALUE *)x509); 120 STACK_OF(CONF_VALUE) *headers = NULL; 121 const char content_type[] = "application/x-x509-ca-cert"; 122 int res = 0; 123 124 if (wbio == NULL || rbio == NULL || req == NULL) 125 goto err; 126 mock_args.out = rbio; 127 BIO_set_callback_ex(wbio, http_bio_cb_ex); 128 BIO_set_callback_arg(wbio, (char *)&mock_args); 129 130 rsp = do_get ? 131 OSSL_HTTP_get("/will-be-redirected", 132 NULL /* proxy */, NULL /* no_proxy */, 133 wbio, rbio, NULL /* bio_update_fn */, NULL /* arg */, 134 0 /* buf_size */, headers, content_type, 135 1 /* expect_asn1 */, 136 OSSL_HTTP_DEFAULT_MAX_RESP_LEN, 0 /* timeout */) 137 : OSSL_HTTP_transfer(NULL, NULL /* host */, NULL /* port */, RPATH, 138 0 /* use_ssl */,NULL /* proxy */, NULL /* no_pr */, 139 wbio, rbio, NULL /* bio_fn */, NULL /* arg */, 140 0 /* buf_size */, headers, content_type, 141 req, content_type, 1 /* expect_asn1 */, 142 OSSL_HTTP_DEFAULT_MAX_RESP_LEN, 0 /* timeout */, 143 0 /* keep_alive */); 144 rcert = d2i_X509_bio(rsp, NULL); 145 BIO_free(rsp); 146 res = TEST_ptr(rcert) && TEST_int_eq(X509_cmp(x509, rcert), 0); 147 148 err: 149 X509_free(rcert); 150 BIO_free(req); 151 BIO_free(wbio); 152 BIO_free(rbio); 153 sk_CONF_VALUE_pop_free(headers, X509V3_conf_free); 154 return res; 155 } 156 157 static int test_http_keep_alive(char version, int keep_alive, int kept_alive) 158 { 159 BIO *wbio = BIO_new(BIO_s_mem()); 160 BIO *rbio = BIO_new(BIO_s_mem()); 161 BIO *rsp; 162 server_args mock_args = { NULL, '0', 0 }; 163 const char *const content_type = "application/x-x509-ca-cert"; 164 OSSL_HTTP_REQ_CTX *rctx = NULL; 165 int i, res = 0; 166 167 if (wbio == NULL || rbio == NULL) 168 goto err; 169 mock_args.out = rbio; 170 mock_args.version = version; 171 mock_args.keep_alive = kept_alive; 172 BIO_set_callback_ex(wbio, http_bio_cb_ex); 173 BIO_set_callback_arg(wbio, (char *)&mock_args); 174 175 for (res = 1, i = 1; res && i <= 2; i++) { 176 rsp = OSSL_HTTP_transfer(&rctx, NULL /* server */, NULL /* port */, 177 RPATH, 0 /* use_ssl */, 178 NULL /* proxy */, NULL /* no_proxy */, 179 wbio, rbio, NULL /* bio_update_fn */, NULL, 180 0 /* buf_size */, NULL /* headers */, 181 NULL /* content_type */, NULL /* req => GET */, 182 content_type, 0 /* ASN.1 not expected */, 183 0 /* max_resp_len */, 0 /* timeout */, 184 keep_alive); 185 if (keep_alive == 2 && kept_alive == 0) 186 res = res && TEST_ptr_null(rsp) 187 && TEST_int_eq(OSSL_HTTP_is_alive(rctx), 0); 188 else 189 res = res && TEST_ptr(rsp) 190 && TEST_int_eq(OSSL_HTTP_is_alive(rctx), keep_alive > 0); 191 BIO_free(rsp); 192 (void)BIO_reset(rbio); /* discard response contents */ 193 keep_alive = 0; 194 } 195 OSSL_HTTP_close(rctx, res); 196 197 err: 198 BIO_free(wbio); 199 BIO_free(rbio); 200 return res; 201 } 202 203 static int test_http_url_ok(const char *url, int exp_ssl, const char *exp_host, 204 const char *exp_port, const char *exp_path) 205 { 206 char *user, *host, *port, *path, *query, *frag; 207 int exp_num, num, ssl; 208 int res; 209 210 if (!TEST_int_eq(sscanf(exp_port, "%d", &exp_num), 1)) 211 return 0; 212 res = TEST_true(OSSL_HTTP_parse_url(url, &ssl, &user, &host, &port, &num, 213 &path, &query, &frag)) 214 && TEST_str_eq(host, exp_host) 215 && TEST_str_eq(port, exp_port) 216 && TEST_int_eq(num, exp_num) 217 && TEST_str_eq(path, exp_path) 218 && TEST_int_eq(ssl, exp_ssl); 219 if (res && *user != '\0') 220 res = TEST_str_eq(user, "user:pass"); 221 if (res && *frag != '\0') 222 res = TEST_str_eq(frag, "fr"); 223 if (res && *query != '\0') 224 res = TEST_str_eq(query, "q"); 225 OPENSSL_free(user); 226 OPENSSL_free(host); 227 OPENSSL_free(port); 228 OPENSSL_free(path); 229 OPENSSL_free(query); 230 OPENSSL_free(frag); 231 return res; 232 } 233 234 static int test_http_url_path_query_ok(const char *url, const char *exp_path_qu) 235 { 236 char *host, *path; 237 int res; 238 239 res = TEST_true(OSSL_HTTP_parse_url(url, NULL, NULL, &host, NULL, NULL, 240 &path, NULL, NULL)) 241 && TEST_str_eq(host, "host") 242 && TEST_str_eq(path, exp_path_qu); 243 OPENSSL_free(host); 244 OPENSSL_free(path); 245 return res; 246 } 247 248 static int test_http_url_dns(void) 249 { 250 return test_http_url_ok("host:65535/path", 0, "host", "65535", "/path"); 251 } 252 253 static int test_http_url_path_query(void) 254 { 255 return test_http_url_path_query_ok("http://usr@host:1/p?q=x#frag", "/p?q=x") 256 && test_http_url_path_query_ok("http://host?query#frag", "/?query") 257 && test_http_url_path_query_ok("http://host:9999#frag", "/"); 258 } 259 260 static int test_http_url_userinfo_query_fragment(void) 261 { 262 return test_http_url_ok("user:pass@host/p?q#fr", 0, "host", "80", "/p"); 263 } 264 265 static int test_http_url_ipv4(void) 266 { 267 return test_http_url_ok("https://1.2.3.4/p/q", 1, "1.2.3.4", "443", "/p/q"); 268 } 269 270 static int test_http_url_ipv6(void) 271 { 272 return test_http_url_ok("http://[FF01::101]:6", 0, "[FF01::101]", "6", "/"); 273 } 274 275 static int test_http_url_invalid(const char *url) 276 { 277 char *host = "1", *port = "1", *path = "1"; 278 int num = 1, ssl = 1; 279 int res; 280 281 res = TEST_false(OSSL_HTTP_parse_url(url, &ssl, NULL, &host, &port, &num, 282 &path, NULL, NULL)) 283 && TEST_ptr_null(host) 284 && TEST_ptr_null(port) 285 && TEST_ptr_null(path); 286 if (!res) { 287 OPENSSL_free(host); 288 OPENSSL_free(port); 289 OPENSSL_free(path); 290 } 291 return res; 292 } 293 294 static int test_http_url_invalid_prefix(void) 295 { 296 return test_http_url_invalid("htttps://1.2.3.4:65535/pkix"); 297 } 298 299 static int test_http_url_invalid_port(void) 300 { 301 return test_http_url_invalid("https://1.2.3.4:65536/pkix") 302 && test_http_url_invalid("https://1.2.3.4:"); 303 } 304 305 static int test_http_url_invalid_path(void) 306 { 307 return test_http_url_invalid("https://[FF01::101]pkix"); 308 } 309 310 static int test_http_get_x509(void) 311 { 312 return test_http_x509(1); 313 } 314 315 static int test_http_post_x509(void) 316 { 317 return test_http_x509(0); 318 } 319 320 static int test_http_keep_alive_0_no_no(void) 321 { 322 return test_http_keep_alive('0', 0, 0); 323 } 324 325 static int test_http_keep_alive_1_no_no(void) 326 { 327 return test_http_keep_alive('1', 0, 0); 328 } 329 330 static int test_http_keep_alive_0_prefer_yes(void) 331 { 332 return test_http_keep_alive('0', 1, 1); 333 } 334 335 static int test_http_keep_alive_1_prefer_yes(void) 336 { 337 return test_http_keep_alive('1', 1, 1); 338 } 339 340 static int test_http_keep_alive_0_require_yes(void) 341 { 342 return test_http_keep_alive('0', 2, 1); 343 } 344 345 static int test_http_keep_alive_1_require_yes(void) 346 { 347 return test_http_keep_alive('1', 2, 1); 348 } 349 350 static int test_http_keep_alive_0_require_no(void) 351 { 352 return test_http_keep_alive('0', 2, 0); 353 } 354 355 static int test_http_keep_alive_1_require_no(void) 356 { 357 return test_http_keep_alive('1', 2, 0); 358 } 359 360 void cleanup_tests(void) 361 { 362 X509_free(x509); 363 } 364 365 OPT_TEST_DECLARE_USAGE("cert.pem\n") 366 367 int setup_tests(void) 368 { 369 if (!test_skip_common_options()) 370 return 0; 371 372 x509_it = ASN1_ITEM_rptr(X509); 373 if (!TEST_ptr((x509 = load_cert_pem(test_get_argument(0), NULL)))) 374 return 0; 375 376 ADD_TEST(test_http_url_dns); 377 ADD_TEST(test_http_url_path_query); 378 ADD_TEST(test_http_url_userinfo_query_fragment); 379 ADD_TEST(test_http_url_ipv4); 380 ADD_TEST(test_http_url_ipv6); 381 ADD_TEST(test_http_url_invalid_prefix); 382 ADD_TEST(test_http_url_invalid_port); 383 ADD_TEST(test_http_url_invalid_path); 384 ADD_TEST(test_http_get_x509); 385 ADD_TEST(test_http_post_x509); 386 ADD_TEST(test_http_keep_alive_0_no_no); 387 ADD_TEST(test_http_keep_alive_1_no_no); 388 ADD_TEST(test_http_keep_alive_0_prefer_yes); 389 ADD_TEST(test_http_keep_alive_1_prefer_yes); 390 ADD_TEST(test_http_keep_alive_0_require_yes); 391 ADD_TEST(test_http_keep_alive_1_require_yes); 392 ADD_TEST(test_http_keep_alive_0_require_no); 393 ADD_TEST(test_http_keep_alive_1_require_no); 394 return 1; 395 } 396