OpenVPN
test_ncp.c
Go to the documentation of this file.
1 /*
2  * OpenVPN -- An application to securely tunnel IP networks
3  * over a single UDP port, with support for SSL/TLS-based
4  * session authentication and key exchange,
5  * packet encryption, packet authentication, and
6  * packet compression.
7  *
8  * Copyright (C) 2019-2024 Arne Schwabe <arne@rfc2549.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2
12  * as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include "syshead.h"
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <string.h>
34 #include <setjmp.h>
35 #include <cmocka.h>
36 
37 #include "ssl_ncp.c"
38 #include "test_common.h"
39 
40 /* Defines for use in the tests and the mock parse_line() */
41 
42 const char *bf_chacha = "BF-CBC:CHACHA20-POLY1305";
43 const char *aes_chacha = "AES-128-CBC:CHACHA20-POLY1305";
44 const char *aes_ciphers = "AES-256-GCM:AES-128-GCM";
45 
46 
47 /* Define this function here as dummy since including the ssl_*.c files
48  * leads to having to include even more unrelated code */
49 bool
51  const char *label, size_t label_size,
52  void *ekm, size_t ekm_size)
53 {
54  ASSERT(0);
55 }
56 
57 
58 /* Define a dummy dco cipher option to avoid linking against all the DCO
59  * units */
60 #if defined(ENABLE_DCO)
61 const char *
63 {
64  return "AES-192-GCM:AES-128-CBC:AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305";
65 }
66 #endif
67 
68 static void
70 {
71  struct gc_arena gc = gc_new();
72  bool have_chacha = cipher_valid("CHACHA20-POLY1305");
73  bool have_blowfish = cipher_valid("BF-CBC");
74 
75  assert_string_equal(mutate_ncp_cipher_list("none", &gc), "none");
76  assert_string_equal(mutate_ncp_cipher_list("AES-256-GCM:none", &gc),
77  "AES-256-GCM:none");
78 
79  assert_string_equal(mutate_ncp_cipher_list(aes_ciphers, &gc), aes_ciphers);
80 
81  if (have_chacha)
82  {
83  assert_string_equal(mutate_ncp_cipher_list(aes_chacha, &gc), aes_chacha);
84  }
85 
86  if (have_chacha && have_blowfish)
87  {
88  assert_string_equal(mutate_ncp_cipher_list(bf_chacha, &gc), bf_chacha);
89  assert_string_equal(mutate_ncp_cipher_list("BF-CBC:CHACHA20-POLY1305", &gc),
90  bf_chacha);
91  }
92  else
93  {
94  assert_ptr_equal(mutate_ncp_cipher_list(bf_chacha, &gc), NULL);
95  }
96 
97  /* Check that optional ciphers work */
98  assert_string_equal(mutate_ncp_cipher_list("AES-256-GCM:?vollbit:AES-128-GCM", &gc),
99  aes_ciphers);
100 
101  /* Check that optional ciphers work */
102  assert_string_equal(mutate_ncp_cipher_list("?AES-256-GCM:?AES-128-GCM", &gc),
103  aes_ciphers);
104 
105  /* All unsupported should still yield an empty list */
106  assert_ptr_equal(mutate_ncp_cipher_list("?kugelfisch:?grasshopper", &gc), NULL);
107 
108  /* If the last is optional, previous invalid ciphers should be ignored */
109  assert_ptr_equal(mutate_ncp_cipher_list("Vollbit:Littlebit:AES-256-CBC:BF-CBC:?nixbit", &gc), NULL);
110 
111  /* We do not support CCM ciphers */
112  assert_ptr_equal(mutate_ncp_cipher_list("AES-256-GCM:AES-128-CCM", &gc), NULL);
113 
114  assert_string_equal(mutate_ncp_cipher_list("AES-256-GCM:?AES-128-CCM:AES-128-GCM", &gc),
115  aes_ciphers);
116 
117  /* For testing that with OpenSSL 1.1.0+ that also accepts ciphers in
118  * a different spelling the normalised cipher output is the same */
119  bool have_chacha_mixed_case = cipher_valid("ChaCha20-Poly1305");
120  if (have_chacha_mixed_case)
121  {
122  assert_string_equal(mutate_ncp_cipher_list("AES-128-CBC:ChaCha20-Poly1305", &gc),
123  aes_chacha);
124  }
125 
126  assert_ptr_equal(mutate_ncp_cipher_list("vollbit", &gc), NULL);
127  assert_ptr_equal(mutate_ncp_cipher_list("AES-256-GCM:vollbit", &gc), NULL);
128  assert_ptr_equal(mutate_ncp_cipher_list("", &gc), NULL);
129 
130  assert_ptr_equal(mutate_ncp_cipher_list(
131  "ChaCha20-Poly1305:ChaCha20-Poly1305:ChaCha20-Poly1305:"
132  "ChaCha20-Poly1305:ChaCha20-Poly1305:ChaCha20-Poly1305:"
133  "ChaCha20-Poly1305", &gc), NULL);
134 
135 #ifdef ENABLE_CRYPTO_OPENSSL
136  assert_string_equal(mutate_ncp_cipher_list("id-aes128-GCM:id-aes256-GCM",
137  &gc), "AES-128-GCM:AES-256-GCM");
138 #else
139  if (have_blowfish)
140  {
141  assert_string_equal(mutate_ncp_cipher_list("BLOWFISH-CBC",
142  &gc), "BF-CBC");
143  }
144 #endif
145  gc_free(&gc);
146 }
147 
148 static void
150 {
151  struct gc_arena gc = gc_new();
152  const char *client_peer_info;
153  const char *peer_list;
154 
155  client_peer_info = "foo=bar\nIV_foo=y\nIV_NCP=2";
156  peer_list = tls_peer_ncp_list(client_peer_info, &gc);
157  assert_string_equal(aes_ciphers, peer_list);
158  assert_true(tls_peer_supports_ncp(client_peer_info));
159 
160  client_peer_info = "foo=bar\nIV_foo=y\nIV_NCP=2\nIV_CIPHERS=BF-CBC";
161  peer_list = tls_peer_ncp_list(client_peer_info, &gc);
162  assert_string_equal("BF-CBC", peer_list);
163  assert_true(tls_peer_supports_ncp(client_peer_info));
164 
165  client_peer_info = "IV_NCP=2\nIV_CIPHERS=BF-CBC:FOO-BAR\nIV_BAR=7";
166  peer_list = tls_peer_ncp_list(client_peer_info, &gc);
167  assert_string_equal("BF-CBC:FOO-BAR", peer_list);
168  assert_true(tls_peer_supports_ncp(client_peer_info));
169 
170  client_peer_info = "IV_CIPHERS=BF-CBC:FOO-BAR\nIV_BAR=7";
171  peer_list = tls_peer_ncp_list(client_peer_info, &gc);
172  assert_string_equal("BF-CBC:FOO-BAR", peer_list);
173  assert_true(tls_peer_supports_ncp(client_peer_info));
174 
175  client_peer_info = "IV_YOLO=NO\nIV_BAR=7";
176  peer_list = tls_peer_ncp_list(client_peer_info, &gc);
177  assert_string_equal("", peer_list);
178  assert_false(tls_peer_supports_ncp(client_peer_info));
179 
180  peer_list = tls_peer_ncp_list(NULL, &gc);
181  assert_string_equal("", peer_list);
182  assert_false(tls_peer_supports_ncp(client_peer_info));
183 
184  gc_free(&gc);
185 }
186 
187 static void
188 test_poor_man(void **state)
189 {
190  struct gc_arena gc = gc_new();
191  char *best_cipher;
192 
193  const char *serverlist = "CHACHA20_POLY1305:AES-128-GCM";
194  const char *serverlistbfcbc = "CHACHA20_POLY1305:AES-128-GCM:BF-CBC:none";
195 
196  best_cipher = ncp_get_best_cipher(serverlist,
197  "IV_YOLO=NO\nIV_BAR=7",
198  "BF-CBC", &gc);
199 
200  assert_ptr_equal(best_cipher, NULL);
201 
202 
203  best_cipher = ncp_get_best_cipher(serverlistbfcbc,
204  "IV_YOLO=NO\nIV_BAR=7",
205  "BF-CBC", &gc);
206 
207  assert_string_equal(best_cipher, "BF-CBC");
208 
209 
210  best_cipher = ncp_get_best_cipher(serverlist,
211  "IV_NCP=1\nIV_BAR=7",
212  "AES-128-GCM", &gc);
213 
214  assert_string_equal(best_cipher, "AES-128-GCM");
215 
216  best_cipher = ncp_get_best_cipher(serverlist, NULL,
217  "AES-128-GCM", &gc);
218 
219  assert_string_equal(best_cipher, "AES-128-GCM");
220 
221  best_cipher = ncp_get_best_cipher(serverlist, NULL,
222  "none", &gc);
223  assert_ptr_equal(best_cipher, NULL);
224 
225  best_cipher = ncp_get_best_cipher(serverlistbfcbc, NULL,
226  "none", &gc);
227  assert_string_equal(best_cipher, "none");
228 
229  best_cipher = ncp_get_best_cipher(serverlist, NULL, NULL, &gc);
230  assert_ptr_equal(best_cipher, NULL);
231 
232  gc_free(&gc);
233 }
234 
235 
236 static void
237 test_ncp_best(void **state)
238 {
239  struct gc_arena gc = gc_new();
240  char *best_cipher;
241 
242  const char *serverlist = "CHACHA20_POLY1305:AES-128-GCM:AES-256-GCM";
243 
244  best_cipher = ncp_get_best_cipher(serverlist,
245  "IV_YOLO=NO\nIV_NCP=2\nIV_BAR=7",
246  "BF-CBC", &gc);
247 
248  assert_string_equal(best_cipher, "AES-128-GCM");
249 
250  /* Best cipher is in --cipher of client */
251  best_cipher = ncp_get_best_cipher(serverlist, "IV_NCP=2\nIV_BAR=7",
252  "CHACHA20_POLY1305", &gc);
253 
254  assert_string_equal(best_cipher, "CHACHA20_POLY1305");
255 
256  /* Best cipher is in --cipher of client */
257  best_cipher = ncp_get_best_cipher(serverlist, "IV_CIPHERS=AES-128-GCM",
258  "AES-256-CBC", &gc);
259 
260 
261  assert_string_equal(best_cipher, "AES-128-GCM");
262 
263  /* IV_NCP=2 should be ignored if IV_CIPHERS is sent */
264  best_cipher = ncp_get_best_cipher(serverlist,
265  "IV_FOO=7\nIV_CIPHERS=AES-256-GCM\nIV_NCP=2",
266  "AES-256-CBC", &gc);
267 
268  assert_string_equal(best_cipher, "AES-256-GCM");
269 
270 
271  gc_free(&gc);
272 }
273 
274 static void
275 test_ncp_default(void **state)
276 {
277  bool have_chacha = cipher_valid("CHACHA20-POLY1305");
278 
279  struct options o = { 0 };
280 
281  o.gc = gc_new();
282 
283  /* no user specified string */
284  o.ncp_ciphers = NULL;
286 
287  if (have_chacha)
288  {
289  assert_string_equal(o.ncp_ciphers, "AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305");
290  }
291  else
292  {
293  assert_string_equal(o.ncp_ciphers, "AES-256-GCM:AES-128-GCM");
294  }
295  assert_string_equal(o.ncp_ciphers_conf, "DEFAULT");
296 
297  /* check that a default string is replaced with DEFAULT */
298  if (have_chacha)
299  {
300  o.ncp_ciphers = "AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305";
301  }
302  else
303  {
304  o.ncp_ciphers = "AES-256-GCM:AES-128-GCM";
305  }
306 
308  assert_string_equal(o.ncp_ciphers_conf, "DEFAULT");
309 
310  /* test default in the middle of the string */
311  o.ncp_ciphers = "BF-CBC:DEFAULT:AES-128-CBC:AES-256-CBC";
313 
314  if (have_chacha)
315  {
316  assert_string_equal(o.ncp_ciphers, "BF-CBC:AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305:AES-128-CBC:AES-256-CBC");
317  }
318  else
319  {
320  assert_string_equal(o.ncp_ciphers, "BF-CBC:AES-256-GCM:AES-128-GCM:AES-128-CBC:AES-256-CBC");
321  }
322  assert_string_equal(o.ncp_ciphers_conf, "BF-CBC:DEFAULT:AES-128-CBC:AES-256-CBC");
323 
324  /* string at the beginning */
325  o.ncp_ciphers = "DEFAULT:AES-128-CBC:AES-192-CBC";
327 
328  if (have_chacha)
329  {
330  assert_string_equal(o.ncp_ciphers, "AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305:AES-128-CBC:AES-192-CBC");
331  }
332  else
333  {
334  assert_string_equal(o.ncp_ciphers, "AES-256-GCM:AES-128-GCM:AES-128-CBC:AES-192-CBC");
335  }
336  assert_string_equal(o.ncp_ciphers_conf, "DEFAULT:AES-128-CBC:AES-192-CBC");
337 
338  /* DEFAULT at the end */
339  o.ncp_ciphers = "AES-192-GCM:AES-128-CBC:DEFAULT";
341 
342  if (have_chacha)
343  {
344  assert_string_equal(o.ncp_ciphers, "AES-192-GCM:AES-128-CBC:AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305");
345  }
346  else
347  {
348  assert_string_equal(o.ncp_ciphers, "AES-192-GCM:AES-128-CBC:AES-256-GCM:AES-128-GCM");
349  }
350  assert_string_equal(o.ncp_ciphers_conf, "AES-192-GCM:AES-128-CBC:DEFAULT");
351 
352  gc_free(&o.gc);
353 }
354 
355 static void
356 test_ncp_expand(void **state)
357 {
358  bool have_chacha = cipher_valid("CHACHA20-POLY1305");
359  struct options o = {0};
360 
361  o.gc = gc_new();
362  struct gc_arena gc = gc_new();
363 
364  /* no user specified string */
365  o.ncp_ciphers = NULL;
367 
368  const char *expanded = ncp_expanded_ciphers(&o, &gc);
369 
370  /* user specificed string with DEFAULT in it */
371  o.ncp_ciphers = "AES-192-GCM:DEFAULT";
373  const char *expanded2 = ncp_expanded_ciphers(&o, &gc);
374 
375  if (have_chacha)
376  {
377  assert_string_equal(expanded, " (AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305)");
378  assert_string_equal(expanded2, " (AES-192-GCM:AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305)");
379  }
380  else
381  {
382  assert_string_equal(expanded, " (AES-256-GCM:AES-128-GCM)");
383  assert_string_equal(expanded2, " (AES-192-GCM:AES-256-GCM:AES-128-GCM)");
384  }
385 
386  o.ncp_ciphers = "AES-192-GCM:BF-CBC";
388 
389  assert_string_equal(ncp_expanded_ciphers(&o, &gc), "");
390 
391  gc_free(&o.gc);
392  gc_free(&gc);
393 }
394 
395 
396 const struct CMUnitTest ncp_tests[] = {
397  cmocka_unit_test(test_check_ncp_ciphers_list),
398  cmocka_unit_test(test_extract_client_ciphers),
399  cmocka_unit_test(test_poor_man),
400  cmocka_unit_test(test_ncp_best),
401  cmocka_unit_test(test_ncp_default),
402  cmocka_unit_test(test_ncp_expand),
403 };
404 
405 
406 int
407 main(void)
408 {
410 #if defined(ENABLE_CRYPTO_OPENSSL)
411  OpenSSL_add_all_algorithms();
412 #endif
413  return cmocka_run_group_tests(ncp_tests, NULL, NULL);
414 }
test_poor_man
static void test_poor_man(void **state)
Definition: test_ncp.c:188
ncp_expanded_ciphers
const char * ncp_expanded_ciphers(struct options *o, struct gc_arena *gc)
returns the o->ncp_ciphers in brackets, e.g.
Definition: ssl_ncp.c:634
cipher_valid
static bool cipher_valid(const char *ciphername)
Returns if the cipher is valid, based on the given cipher name.
Definition: crypto_backend.h:205
bf_chacha
const char * bf_chacha
Definition: test_ncp.c:42
gc_new
static struct gc_arena gc_new(void)
Definition: buffer.h:1025
test_common.h
ssl_ncp.c
mutate_ncp_cipher_list
char * mutate_ncp_cipher_list(const char *list, struct gc_arena *gc)
Check whether the ciphers in the supplied list are supported.
Definition: ssl_ncp.c:98
dco_get_supported_ciphers
const char * dco_get_supported_ciphers(void)
Definition: dco_win.c:799
options::ncp_ciphers_conf
const char * ncp_ciphers_conf
The original ncp_ciphers specified by the user in the configuration.
Definition: options.h:577
ASSERT
#define ASSERT(x)
Definition: error.h:195
key_state_export_keying_material
bool key_state_export_keying_material(struct tls_session *session, const char *label, size_t label_size, void *ekm, size_t ekm_size)
Keying Material Exporters [RFC 5705] allows additional keying material to be derived from existing TL...
Definition: test_ncp.c:50
tls_peer_ncp_list
const char * tls_peer_ncp_list(const char *peer_info, struct gc_arena *gc)
Returns the support cipher list from the peer according to the IV_NCP and IV_CIPHER values in peer_in...
Definition: ssl_ncp.c:229
options::ncp_ciphers
const char * ncp_ciphers
Definition: options.h:578
test_check_ncp_ciphers_list
static void test_check_ncp_ciphers_list(void **state)
Definition: test_ncp.c:69
tls_peer_supports_ncp
bool tls_peer_supports_ncp(const char *peer_info)
Returns whether the client supports NCP either by announcing IV_NCP>=2 or the IV_CIPHERS list.
Definition: ssl_ncp.c:80
options
Definition: options.h:249
options::gc
struct gc_arena gc
Definition: options.h:251
aes_chacha
const char * aes_chacha
Definition: test_ncp.c:43
main
int main(void)
Definition: test_ncp.c:407
test_ncp_expand
static void test_ncp_expand(void **state)
Definition: test_ncp.c:356
tls_session
Security parameter state of a single session within a VPN tunnel.
Definition: ssl_common.h:479
syshead.h
test_ncp_best
static void test_ncp_best(void **state)
Definition: test_ncp.c:237
gc_arena
Garbage collection arena used to keep track of dynamically allocated memory.
Definition: buffer.h:116
openvpn_unit_test_setup
static void openvpn_unit_test_setup(void)
Sets up the environment for unit tests like making both stderr and stdout non-buffered to avoid messa...
Definition: test_common.h:36
ncp_get_best_cipher
char * ncp_get_best_cipher(const char *server_list, const char *peer_info, const char *remote_cipher, struct gc_arena *gc)
Iterates through the ciphers in server_list and return the first cipher that is also supported by the...
Definition: ssl_ncp.c:250
aes_ciphers
const char * aes_ciphers
Definition: test_ncp.c:44
test_extract_client_ciphers
static void test_extract_client_ciphers(void **state)
Definition: test_ncp.c:149
gc_free
static void gc_free(struct gc_arena *a)
Definition: buffer.h:1033
ncp_tests
const struct CMUnitTest ncp_tests[]
Definition: test_ncp.c:396
config.h
options_postprocess_setdefault_ncpciphers
void options_postprocess_setdefault_ncpciphers(struct options *o)
Checks for availibility of Chacha20-Poly1305 and sets the ncp_cipher to either AES-256-GCM:AES-128-GC...
Definition: ssl_ncp.c:591
session
Definition: keyingmaterialexporter.c:56
test_ncp_default
static void test_ncp_default(void **state)
Definition: test_ncp.c:275
gc
struct gc_arena gc
Definition: test_ssl.c:155