]> git.ipfire.org Git - thirdparty/openssl.git/blob - test/quicfaultstest.c
Security hardening: Expose Build flags for Position Independed Execution (PIE)
[thirdparty/openssl.git] / test / quicfaultstest.c
1 /*
2 * Copyright 2022-2024 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10 #include <string.h>
11 #include <openssl/ssl.h>
12 #include "helpers/quictestlib.h"
13 #include "internal/quic_error.h"
14 #include "testutil.h"
15
16 static char *cert = NULL;
17 static char *privkey = NULL;
18
19 /*
20 * Basic test that just creates a connection and sends some data without any
21 * faults injected.
22 */
23 static int test_basic(void)
24 {
25 int testresult = 0;
26 SSL_CTX *cctx = SSL_CTX_new(OSSL_QUIC_client_method());
27 QUIC_TSERVER *qtserv = NULL;
28 SSL *cssl = NULL;
29 char *msg = "Hello World!";
30 size_t msglen = strlen(msg);
31 unsigned char buf[80];
32 size_t bytesread;
33
34 if (!TEST_ptr(cctx))
35 goto err;
36
37 if (!TEST_true(qtest_create_quic_objects(NULL, cctx, NULL, cert, privkey, 0,
38 &qtserv, &cssl, NULL, NULL)))
39 goto err;
40
41 if (!TEST_true(qtest_create_quic_connection(qtserv, cssl)))
42 goto err;
43
44 if (!TEST_int_eq(SSL_write(cssl, msg, msglen), msglen))
45 goto err;
46
47 ossl_quic_tserver_tick(qtserv);
48 if (!TEST_true(ossl_quic_tserver_read(qtserv, 0, buf, sizeof(buf), &bytesread)))
49 goto err;
50
51 /*
52 * We assume the entire message is read from the server in one go. In
53 * theory this could get fragmented but its a small message so we assume
54 * not.
55 */
56 if (!TEST_mem_eq(msg, msglen, buf, bytesread))
57 goto err;
58
59 testresult = 1;
60 err:
61 SSL_free(cssl);
62 ossl_quic_tserver_free(qtserv);
63 SSL_CTX_free(cctx);
64 return testresult;
65 }
66
67 /*
68 * Test that adding an unknown frame type is handled correctly
69 */
70 static int add_unknown_frame_cb(QTEST_FAULT *fault, QUIC_PKT_HDR *hdr,
71 unsigned char *buf, size_t len, void *cbarg)
72 {
73 static size_t done = 0;
74 /*
75 * There are no "reserved" frame types which are definitately safe for us
76 * to use for testing purposes - but we just use the highest possible
77 * value (8 byte length integer) and with no payload bytes
78 */
79 unsigned char unknown_frame[] = {
80 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
81 };
82
83 /* We only ever add the unknown frame to one packet */
84 if (done++)
85 return 1;
86
87 return qtest_fault_prepend_frame(fault, unknown_frame,
88 sizeof(unknown_frame));
89 }
90
91 static int test_unknown_frame(void)
92 {
93 int testresult = 0, ret;
94 SSL_CTX *cctx = SSL_CTX_new(OSSL_QUIC_client_method());
95 QUIC_TSERVER *qtserv = NULL;
96 SSL *cssl = NULL;
97 char *msg = "Hello World!";
98 size_t msglen = strlen(msg);
99 unsigned char buf[80];
100 size_t byteswritten;
101 QTEST_FAULT *fault = NULL;
102 uint64_t sid = UINT64_MAX;
103
104 if (!TEST_ptr(cctx))
105 goto err;
106
107 if (!TEST_true(qtest_create_quic_objects(NULL, cctx, NULL, cert, privkey, 0,
108 &qtserv, &cssl, &fault, NULL)))
109 goto err;
110
111 if (!TEST_true(qtest_create_quic_connection(qtserv, cssl)))
112 goto err;
113
114 /*
115 * Write a message from the server to the client and add an unknown frame
116 * type
117 */
118 if (!TEST_true(qtest_fault_set_packet_plain_listener(fault,
119 add_unknown_frame_cb,
120 NULL)))
121 goto err;
122
123 if (!TEST_true(ossl_quic_tserver_stream_new(qtserv, /*is_uni=*/0, &sid))
124 || !TEST_uint64_t_eq(sid, 1))
125 goto err;
126
127 if (!TEST_true(ossl_quic_tserver_write(qtserv, sid, (unsigned char *)msg, msglen,
128 &byteswritten)))
129 goto err;
130
131 if (!TEST_size_t_eq(msglen, byteswritten))
132 goto err;
133
134 ossl_quic_tserver_tick(qtserv);
135 if (!TEST_true(SSL_handle_events(cssl)))
136 goto err;
137
138 if (!TEST_int_le(ret = SSL_read(cssl, buf, sizeof(buf)), 0))
139 goto err;
140
141 if (!TEST_int_eq(SSL_get_error(cssl, ret), SSL_ERROR_SSL))
142 goto err;
143
144 if (!TEST_int_eq(ERR_GET_REASON(ERR_peek_error()),
145 SSL_R_QUIC_PROTOCOL_ERROR))
146 goto err;
147
148 if (!TEST_true(qtest_check_server_frame_encoding_err(qtserv)))
149 goto err;
150
151 testresult = 1;
152 err:
153 qtest_fault_free(fault);
154 SSL_free(cssl);
155 ossl_quic_tserver_free(qtserv);
156 SSL_CTX_free(cctx);
157 return testresult;
158 }
159
160 /*
161 * Test that a server that fails to provide transport params cannot be
162 * connected to.
163 */
164 static int drop_extensions_cb(QTEST_FAULT *fault,
165 QTEST_ENCRYPTED_EXTENSIONS *ee,
166 size_t eelen, void *encextcbarg)
167 {
168 int *ext = (int *)encextcbarg;
169
170 if (!qtest_fault_delete_extension(fault, *ext, ee->extensions,
171 &ee->extensionslen, NULL))
172 return 0;
173
174 return 1;
175 }
176
177 static int test_drop_extensions(int idx)
178 {
179 int testresult = 0;
180 SSL_CTX *cctx = SSL_CTX_new(OSSL_QUIC_client_method());
181 QUIC_TSERVER *qtserv = NULL;
182 SSL *cssl = NULL;
183 QTEST_FAULT *fault = NULL;
184 int ext, err;
185
186 if (!TEST_ptr(cctx))
187 goto err;
188
189 if (!TEST_true(qtest_create_quic_objects(NULL, cctx, NULL, cert, privkey, 0,
190 &qtserv, &cssl, &fault, NULL)))
191 goto err;
192
193 if (idx == 0) {
194 ext = TLSEXT_TYPE_quic_transport_parameters;
195 err = OSSL_QUIC_ERR_CRYPTO_MISSING_EXT;
196 } else {
197 ext = TLSEXT_TYPE_application_layer_protocol_negotiation;
198 err = OSSL_QUIC_ERR_CRYPTO_NO_APP_PROTO;
199 }
200
201 if (!TEST_true(qtest_fault_set_hand_enc_ext_listener(fault,
202 drop_extensions_cb,
203 &ext)))
204 goto err;
205
206 /*
207 * We expect the connection to fail because the server failed to provide
208 * transport parameters
209 */
210 if (!TEST_false(qtest_create_quic_connection(qtserv, cssl)))
211 goto err;
212
213 if (!TEST_true(qtest_check_server_transport_err(qtserv, err)))
214 goto err;
215
216 testresult = 1;
217 err:
218 qtest_fault_free(fault);
219 SSL_free(cssl);
220 ossl_quic_tserver_free(qtserv);
221 SSL_CTX_free(cctx);
222 return testresult;
223 }
224
225 /*
226 * Test that corrupted packets/datagrams are dropped and retransmitted
227 */
228 static int docorrupt = 0;
229
230 static int on_packet_cipher_cb(QTEST_FAULT *fault, QUIC_PKT_HDR *hdr,
231 unsigned char *buf, size_t len, void *cbarg)
232 {
233 if (!docorrupt || len == 0)
234 return 1;
235
236 buf[(size_t)test_random() % len] ^= 0xff;
237 docorrupt = 0;
238
239 return 1;
240 }
241
242 static int on_datagram_cb(QTEST_FAULT *fault, BIO_MSG *m, size_t stride,
243 void *cbarg)
244 {
245 if (!docorrupt || m->data_len == 0)
246 return 1;
247
248 if (!qtest_fault_resize_datagram(fault, m->data_len - 1))
249 return 1;
250
251 docorrupt = 0;
252
253 return 1;
254 }
255
256 /*
257 * Test 1: Corrupt by flipping bits in an encrypted packet
258 * Test 2: Corrupt by truncating an entire datagram
259 */
260 static int test_corrupted_data(int idx)
261 {
262 QTEST_FAULT *fault = NULL;
263 int testresult = 0;
264 SSL_CTX *cctx = SSL_CTX_new(OSSL_QUIC_client_method());
265 QUIC_TSERVER *qtserv = NULL;
266 SSL *cssl = NULL;
267 char *msg = "Hello World!";
268 size_t msglen = strlen(msg);
269 unsigned char buf[80];
270 size_t bytesread, byteswritten;
271 uint64_t sid = UINT64_MAX;
272
273 if (!TEST_ptr(cctx))
274 goto err;
275
276 if (!TEST_true(qtest_create_quic_objects(NULL, cctx, NULL, cert, privkey,
277 QTEST_FLAG_FAKE_TIME, &qtserv,
278 &cssl, &fault, NULL)))
279 goto err;
280
281 if (idx == 0) {
282 /* Listen for encrypted packets being sent */
283 if (!TEST_true(qtest_fault_set_packet_cipher_listener(fault,
284 on_packet_cipher_cb,
285 NULL)))
286 goto err;
287 } else {
288 /* Listen for datagrams being sent */
289 if (!TEST_true(qtest_fault_set_datagram_listener(fault,
290 on_datagram_cb,
291 NULL)))
292 goto err;
293 }
294 if (!TEST_true(qtest_create_quic_connection(qtserv, cssl)))
295 goto err;
296
297 /* Corrupt the next server packet*/
298 docorrupt = 1;
299
300 if (!TEST_true(ossl_quic_tserver_stream_new(qtserv, /*is_uni=*/0, &sid))
301 || !TEST_uint64_t_eq(sid, 1))
302 goto err;
303
304 /*
305 * Send first 5 bytes of message. This will get corrupted and is treated as
306 * "lost"
307 */
308 if (!TEST_true(ossl_quic_tserver_write(qtserv, sid, (unsigned char *)msg, 5,
309 &byteswritten)))
310 goto err;
311
312 if (!TEST_size_t_eq(byteswritten, 5))
313 goto err;
314
315 /*
316 * Introduce a small delay so that the above packet has time to be detected
317 * as lost. Loss detection times are based on RTT which should be very
318 * fast for us since there isn't really a network. The loss delay timer is
319 * always at least 1ms though. We skip forward 100ms
320 */
321 qtest_add_time(100);
322
323 /* Send rest of message */
324 if (!TEST_true(ossl_quic_tserver_write(qtserv, sid, (unsigned char *)msg + 5,
325 msglen - 5, &byteswritten)))
326 goto err;
327
328 if (!TEST_size_t_eq(byteswritten, msglen - 5))
329 goto err;
330
331 /*
332 * Receive the corrupted packet. This should get dropped and is effectively
333 * "lost". We also process the second packet which should be decrypted
334 * successfully. Therefore we ack the frames in it
335 */
336 if (!TEST_true(SSL_handle_events(cssl)))
337 goto err;
338
339 /*
340 * Process the ack. Detect that the first part of the message must have
341 * been lost due to the time elapsed since it was sent and resend it
342 */
343 ossl_quic_tserver_tick(qtserv);
344
345 /* Receive and process the newly arrived message data resend */
346 if (!TEST_true(SSL_handle_events(cssl)))
347 goto err;
348
349 /* The whole message should now have arrived */
350 if (!TEST_true(SSL_read_ex(cssl, buf, sizeof(buf), &bytesread)))
351 goto err;
352
353 if (!TEST_mem_eq(msg, msglen, buf, bytesread))
354 goto err;
355
356 /*
357 * If the test was successful then we corrupted exactly one packet and
358 * docorrupt was reset
359 */
360 if (!TEST_false(docorrupt))
361 goto err;
362
363 testresult = 1;
364 err:
365 qtest_fault_free(fault);
366 SSL_free(cssl);
367 ossl_quic_tserver_free(qtserv);
368 SSL_CTX_free(cctx);
369 return testresult;
370 }
371
372 OPT_TEST_DECLARE_USAGE("certsdir\n")
373
374 int setup_tests(void)
375 {
376 char *certsdir = NULL;
377
378 if (!test_skip_common_options()) {
379 TEST_error("Error parsing test options\n");
380 return 0;
381 }
382
383 if (!TEST_ptr(certsdir = test_get_argument(0)))
384 return 0;
385
386 cert = test_mk_file_path(certsdir, "servercert.pem");
387 if (cert == NULL)
388 goto err;
389
390 privkey = test_mk_file_path(certsdir, "serverkey.pem");
391 if (privkey == NULL)
392 goto err;
393
394 ADD_TEST(test_basic);
395 ADD_TEST(test_unknown_frame);
396 ADD_ALL_TESTS(test_drop_extensions, 2);
397 ADD_ALL_TESTS(test_corrupted_data, 2);
398
399 return 1;
400
401 err:
402 OPENSSL_free(cert);
403 OPENSSL_free(privkey);
404 return 0;
405 }
406
407 void cleanup_tests(void)
408 {
409 OPENSSL_free(cert);
410 OPENSSL_free(privkey);
411 }