]>
Commit | Line | Data |
---|---|---|
29f178bd | 1 | /* |
4333b89f | 2 | * Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved. |
29f178bd DDO |
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 SERVER "mock.server" | |
21 | #define PORT "81" | |
22 | #define RPATH "path/any.crt" | |
23 | static const char *rpath; | |
24 | ||
29f178bd DDO |
25 | /* |
26 | * pretty trivial HTTP mock server: | |
27 | * for POST, copy request headers+body from mem BIO 'in' as response to 'out' | |
28 | * for GET, first redirect the request then respond with 'rsp' of ASN1 type 'it' | |
29 | */ | |
30 | static int mock_http_server(BIO *in, BIO *out, | |
31 | ASN1_VALUE *rsp, const ASN1_ITEM *it) | |
32 | { | |
33 | const char *req; | |
34 | long count = BIO_get_mem_data(in, (unsigned char **)&req); | |
35 | const char *hdr = (char *)req; | |
36 | int is_get = count >= 4 && strncmp(hdr, "GET ", 4) == 0; | |
37 | int len; | |
38 | ||
39 | /* first line should contain "<GET or POST> <rpath> HTTP/1.x" */ | |
40 | if (is_get) | |
41 | hdr += 4; | |
42 | else if (TEST_true(count >= 5 && strncmp(hdr, "POST ", 5) == 0)) | |
43 | hdr += 5; | |
44 | else | |
45 | return 0; | |
46 | ||
47 | while (*rpath == '/') | |
48 | rpath++; | |
49 | while (*hdr == '/') | |
50 | hdr++; | |
51 | len = strlen(rpath); | |
52 | if (!TEST_strn_eq(hdr, rpath, len) || !TEST_char_eq(hdr++[len], ' ')) | |
53 | return 0; | |
54 | hdr += len; | |
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 (is_get && strcmp(rpath, RPATH) == 0) { | |
69 | rpath = "path/new.crt"; | |
70 | return BIO_printf(out, "HTTP/1.1 301 Moved Permanently\r\n" | |
71 | "Location: /%s\r\n\r\n", rpath) > 0; /* same server */ | |
72 | } | |
73 | if (BIO_printf(out, "HTTP/1.1 200 OK\r\n") <= 0) | |
74 | return 0; | |
75 | if (is_get) { /* construct new header and body */ | |
76 | if ((len = ASN1_item_i2d(rsp, NULL, it)) <= 0) | |
77 | return 0; | |
78 | if (BIO_printf(out, "Content-Type: application/x-x509-ca-cert\r\n" | |
79 | "Content-Length: %d\r\n\r\n", len) <= 0) | |
80 | return 0; | |
81 | return ASN1_item_i2d_bio(it, out, rsp); | |
82 | } else { | |
83 | return BIO_write(out, hdr, count) == count; /* echo header and body */ | |
84 | } | |
85 | } | |
86 | ||
87 | static long http_bio_cb_ex(BIO *bio, int oper, const char *argp, size_t len, | |
88 | int cmd, long argl, int ret, size_t *processed) | |
89 | { | |
90 | ||
91 | if (oper == (BIO_CB_CTRL | BIO_CB_RETURN) && cmd == BIO_CTRL_FLUSH) | |
92 | ret = mock_http_server(bio, (BIO *)BIO_get_callback_arg(bio), | |
93 | (ASN1_VALUE *)x509, x509_it); | |
94 | return ret; | |
95 | } | |
96 | ||
97 | static int test_http_x509(int do_get) | |
98 | { | |
99 | X509 *rcert = NULL; | |
100 | BIO *wbio = BIO_new(BIO_s_mem()); | |
101 | BIO *rbio = BIO_new(BIO_s_mem()); | |
8f965908 | 102 | BIO *rsp, *req = ASN1_item_i2d_mem_bio(x509_it, (ASN1_VALUE *)x509); |
29f178bd | 103 | STACK_OF(CONF_VALUE) *headers = NULL; |
8f965908 | 104 | const char content_type[] = "application/x-x509-ca-cert"; |
29f178bd DDO |
105 | int res = 0; |
106 | ||
8f965908 | 107 | if (wbio == NULL || rbio == NULL || req == NULL) |
29f178bd DDO |
108 | goto err; |
109 | BIO_set_callback_ex(wbio, http_bio_cb_ex); | |
110 | BIO_set_callback_arg(wbio, (char *)rbio); | |
111 | ||
112 | rpath = RPATH; | |
8f965908 DDO |
113 | rsp = do_get ? |
114 | OSSL_HTTP_get("http://"SERVER":"PORT"/"RPATH, | |
115 | NULL /* proxy */, NULL /* no_proxy */, | |
116 | wbio, rbio, NULL /* bio_fn */, NULL /* arg */, | |
117 | 0 /* buf_size */, headers, content_type, | |
118 | 1 /* expect_asn1 */, | |
119 | HTTP_DEFAULT_MAX_RESP_LEN, 0 /* timeout */) | |
120 | : OSSL_HTTP_transfer(NULL, NULL /* host */, NULL /* port */, RPATH, | |
121 | 0 /* use_ssl */,NULL /* proxy */, NULL /* no_pr */, | |
122 | wbio, rbio, NULL /* bio_fn */, NULL /* arg */, | |
123 | 0 /* buf_size */, headers, content_type, | |
124 | req, content_type, 1 /* expect_asn1 */, | |
125 | HTTP_DEFAULT_MAX_RESP_LEN, 0 /* timeout */, | |
126 | 0 /* keep_alive */); | |
127 | rcert = d2i_X509_bio(rsp, NULL); | |
128 | BIO_free(rsp); | |
29f178bd DDO |
129 | res = TEST_ptr(rcert) && TEST_int_eq(X509_cmp(x509, rcert), 0); |
130 | ||
131 | err: | |
132 | X509_free(rcert); | |
8f965908 | 133 | BIO_free(req); |
29f178bd DDO |
134 | BIO_free(wbio); |
135 | BIO_free(rbio); | |
136 | sk_CONF_VALUE_pop_free(headers, X509V3_conf_free); | |
137 | return res; | |
138 | } | |
139 | ||
7932982b DDO |
140 | static int test_http_url_ok(const char *url, int exp_ssl, const char *exp_host, |
141 | const char *exp_port, const char *exp_path) | |
d7fcee3b | 142 | { |
7932982b DDO |
143 | char *user, *host, *port, *path, *query, *frag; |
144 | int exp_num, num, ssl; | |
d7fcee3b DDO |
145 | int res; |
146 | ||
b6a06b13 DDO |
147 | if (!TEST_int_eq(sscanf(exp_port, "%d", &exp_num), 1)) |
148 | return 0; | |
7932982b DDO |
149 | res = TEST_true(OSSL_HTTP_parse_url(url, &ssl, &user, &host, &port, &num, |
150 | &path, &query, &frag)) | |
d7fcee3b | 151 | && TEST_str_eq(host, exp_host) |
7932982b DDO |
152 | && TEST_str_eq(port, exp_port) |
153 | && TEST_int_eq(num, exp_num) | |
154 | && TEST_str_eq(path, exp_path) | |
d7fcee3b | 155 | && TEST_int_eq(ssl, exp_ssl); |
7932982b DDO |
156 | if (res && *user != '\0') |
157 | res = TEST_str_eq(user, "user:pass"); | |
158 | if (res && *frag != '\0') | |
159 | res = TEST_str_eq(frag, "fr"); | |
160 | if (res && *query != '\0') | |
161 | res = TEST_str_eq(query, "q"); | |
162 | OPENSSL_free(user); | |
d7fcee3b DDO |
163 | OPENSSL_free(host); |
164 | OPENSSL_free(port); | |
165 | OPENSSL_free(path); | |
7932982b DDO |
166 | OPENSSL_free(query); |
167 | OPENSSL_free(frag); | |
168 | return res; | |
169 | } | |
170 | ||
171 | static int test_http_url_path_query_ok(const char *url, const char *exp_path_qu) | |
172 | { | |
173 | char *host, *path; | |
174 | int res; | |
175 | ||
176 | res = TEST_true(OSSL_HTTP_parse_url(url, NULL, NULL, &host, NULL, NULL, | |
177 | &path, NULL, NULL)) | |
178 | && TEST_str_eq(host, "host") | |
179 | && TEST_str_eq(path, exp_path_qu); | |
180 | OPENSSL_free(host); | |
181 | OPENSSL_free(path); | |
d7fcee3b DDO |
182 | return res; |
183 | } | |
184 | ||
185 | static int test_http_url_dns(void) | |
186 | { | |
7932982b DDO |
187 | return test_http_url_ok("host:65535/path", 0, "host", "65535", "/path"); |
188 | } | |
189 | ||
190 | static int test_http_url_path_query(void) | |
191 | { | |
192 | return test_http_url_path_query_ok("http://usr@host:1/p?q=x#frag", "/p?q=x") | |
193 | && test_http_url_path_query_ok("http://host?query#frag", "/?query") | |
194 | && test_http_url_path_query_ok("http://host:9999#frag", "/"); | |
195 | } | |
196 | ||
197 | static int test_http_url_userinfo_query_fragment(void) | |
198 | { | |
199 | return test_http_url_ok("user:pass@host/p?q#fr", 0, "host", "80", "/p"); | |
d7fcee3b DDO |
200 | } |
201 | ||
202 | static int test_http_url_ipv4(void) | |
203 | { | |
7932982b | 204 | return test_http_url_ok("https://1.2.3.4/p/q", 1, "1.2.3.4", "443", "/p/q"); |
d7fcee3b DDO |
205 | } |
206 | ||
207 | static int test_http_url_ipv6(void) | |
208 | { | |
23183791 | 209 | return test_http_url_ok("http://[FF01::101]:6", 0, "[FF01::101]", "6", "/"); |
d7fcee3b DDO |
210 | } |
211 | ||
212 | static int test_http_url_invalid(const char *url) | |
213 | { | |
214 | char *host = "1", *port = "1", *path = "1"; | |
215 | int num = 1, ssl = 1; | |
216 | int res; | |
217 | ||
7932982b DDO |
218 | res = TEST_false(OSSL_HTTP_parse_url(url, &ssl, NULL, &host, &port, &num, |
219 | &path, NULL, NULL)) | |
d7fcee3b DDO |
220 | && TEST_ptr_null(host) |
221 | && TEST_ptr_null(port) | |
222 | && TEST_ptr_null(path); | |
223 | if (!res) { | |
224 | OPENSSL_free(host); | |
225 | OPENSSL_free(port); | |
226 | OPENSSL_free(path); | |
227 | } | |
228 | return res; | |
229 | } | |
230 | ||
231 | static int test_http_url_invalid_prefix(void) | |
232 | { | |
233 | return test_http_url_invalid("htttps://1.2.3.4:65535/pkix"); | |
234 | } | |
235 | ||
236 | static int test_http_url_invalid_port(void) | |
237 | { | |
238 | return test_http_url_invalid("https://1.2.3.4:65536/pkix"); | |
239 | } | |
240 | ||
241 | static int test_http_url_invalid_path(void) | |
242 | { | |
243 | return test_http_url_invalid("https://[FF01::101]pkix"); | |
244 | } | |
245 | ||
29f178bd DDO |
246 | static int test_http_get_x509(void) |
247 | { | |
248 | return test_http_x509(1); | |
249 | } | |
250 | ||
251 | static int test_http_post_x509(void) | |
252 | { | |
253 | return test_http_x509(0); | |
254 | } | |
255 | ||
256 | void cleanup_tests(void) | |
257 | { | |
258 | X509_free(x509); | |
259 | } | |
260 | ||
261 | int setup_tests(void) | |
262 | { | |
263 | if (!test_skip_common_options()) { | |
264 | TEST_error("Error parsing test options\n"); | |
265 | return 0; | |
266 | } | |
267 | ||
268 | x509_it = ASN1_ITEM_rptr(X509); | |
0b7368dd | 269 | if (!TEST_ptr((x509 = load_cert_pem(test_get_argument(0), NULL)))) |
29f178bd DDO |
270 | return 1; |
271 | ||
d7fcee3b | 272 | ADD_TEST(test_http_url_dns); |
7932982b DDO |
273 | ADD_TEST(test_http_url_path_query); |
274 | ADD_TEST(test_http_url_userinfo_query_fragment); | |
d7fcee3b DDO |
275 | ADD_TEST(test_http_url_ipv4); |
276 | ADD_TEST(test_http_url_ipv6); | |
277 | ADD_TEST(test_http_url_invalid_prefix); | |
278 | ADD_TEST(test_http_url_invalid_port); | |
279 | ADD_TEST(test_http_url_invalid_path); | |
29f178bd DDO |
280 | ADD_TEST(test_http_get_x509); |
281 | ADD_TEST(test_http_post_x509); | |
282 | return 1; | |
283 | } |