]> git.meshlink.io Git - meshlink/blob - test/blackbox/run_blackbox_tests/test_cases_key_rotation.c
Fix __warn_unused_result__, add more of it and fix the resulting warnings.
[meshlink] / test / blackbox / run_blackbox_tests / test_cases_key_rotation.c
1 /*
2     test_cases_key_rotation.c -- Execution of specific meshlink black box test cases
3     Copyright (C) 2019  Guus Sliepen <guus@meshlink.io>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #ifdef NDEBUG
21 #undef NDEBUG
22 #endif
23
24 #include <assert.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <stdarg.h>
28 #include <setjmp.h>
29 #include <cmocka.h>
30 #include <pthread.h>
31 #include <stdio.h>
32 #include <sys/types.h>
33 #include <dirent.h>
34 #include <sys/wait.h>
35 #include <signal.h>
36 #include <sys/stat.h>
37 #include <unistd.h>
38 #include <utime.h>
39 #include "execute_tests.h"
40 #include "test_cases_key_rotation.h"
41 #include "../common/test_step.h"
42 #include "../common/common_handlers.h"
43 #include "../../../src/devtools.h"
44 #include "../../utils.h"
45
46 static void test_case_key_rotation_01(void **state);
47 static bool test_key_rotation_01(void);
48 static void test_case_key_rotation_02(void **state);
49 static bool test_key_rotation_02(void);
50 static void test_case_key_rotation_03(void **state);
51 static bool test_key_rotation_03(void);
52 static void test_case_key_rotation_04(void **state);
53 static bool test_key_rotation_04(void);
54 static void test_case_key_rotation_05(void **state);
55 static bool test_key_rotation_05(void);
56
57 /* Execute key rotation Test Case # 1 - Sanity test */
58 static void test_case_key_rotation_01(void **state) {
59         execute_test(test_key_rotation_01, state);
60 }
61
62 /* Test Steps for key rotation Test Case # 1
63
64     Test Steps:
65     1. Open encrypted node instance, call encrypted rotate API with
66         invalid input parameters to the call.
67
68     Expected Result:
69     Key rotate should fail when called with invalid parameters.
70 */
71 static bool test_key_rotation_01(void) {
72         meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb);
73         assert(meshlink_destroy("encrypted_conf"));
74
75         // Open a new meshlink instance.
76
77         meshlink_handle_t *mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "oldkey", 6);
78         assert_int_not_equal(mesh, NULL);
79
80         // Pass invalid arguments
81
82         bool keyrotation_status = meshlink_encrypted_key_rotate(mesh, NULL, 5);
83         assert_int_equal(keyrotation_status, false);
84
85         keyrotation_status = meshlink_encrypted_key_rotate(NULL, "newkey", 6);
86         assert_int_equal(keyrotation_status, false);
87
88         keyrotation_status = meshlink_encrypted_key_rotate(mesh, "newkey", 0);
89         assert_int_equal(keyrotation_status, false);
90
91         // Cleanup
92
93         meshlink_close(mesh);
94         assert(meshlink_destroy("encrypted_conf"));
95
96         return true;
97 }
98
99 /* Execute key rotation Test Case # 2 - Sanity test */
100 static void test_case_key_rotation_02(void **state) {
101         execute_test(test_key_rotation_02, state);
102 }
103
104 /* Test Steps for key rotation Test Case # 2
105
106     Test Steps:
107     1. Open encrypted node instance, rotate it's key with a newkey and close the node.
108     2. Reopen the encrypted node instance with the newkey
109
110     Expected Result:
111     Opening encrypted node instance should succeed when tried to open with newkey that's
112     been changed to new by key rotate API.
113 */
114 static bool test_key_rotation_02(void) {
115         meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb);
116         assert(meshlink_destroy("encrypted_conf"));
117
118         // Open a new meshlink instance.
119
120         meshlink_handle_t *mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "oldkey", 6);
121         assert_int_not_equal(mesh, NULL);
122         meshlink_set_log_cb(mesh, MESHLINK_DEBUG, log_cb);
123
124         // Set a new port for the mesh
125
126         int port = 0x1000 + (rand() & 0x7fff);
127         assert_int_equal(meshlink_set_port(mesh, port), true);
128
129         // Key rotate the encrypted_conf storage with new key
130
131         bool keyrotation_status = meshlink_encrypted_key_rotate(mesh, "newkey", 6);
132         assert_int_equal(keyrotation_status, true);
133
134         meshlink_close(mesh);
135
136         // Reopen the meshlink instance with the new key
137
138         mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "newkey", 6);
139         assert_int_not_equal(mesh, NULL);
140
141         // Validate the port number that we changed in the last run.
142
143         assert_int_equal(meshlink_get_port(mesh), port);
144
145         // Cleanup
146
147         meshlink_close(mesh);
148         assert(meshlink_destroy("encrypted_conf"));
149
150         return true;
151 }
152
153 /* Execute key rotation Test Case # 3 - Sanity test */
154 static void test_case_key_rotation_03(void **state) {
155         execute_test(test_key_rotation_03, state);
156 }
157
158 /* Test Steps for key rotation Test Case # 3
159
160     Test Steps:
161     1. Open encrypted node instance, rotate it's key with a newkey and close the node.
162     2. Reopen the encrypted node instance with the oldkey
163
164     Expected Result:
165     Opening encrypted node instance should fail when tried to open with oldkey that's
166     been changed to new by key rotate API.
167 */
168 static bool test_key_rotation_03(void) {
169         assert(meshlink_destroy("encrypted_conf"));
170         meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb);
171
172         // Open a new meshlink instance.
173
174         meshlink_handle_t *mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "oldkey", 6);
175         assert_int_not_equal(mesh, NULL);
176
177         // Key rotate the encrypted_conf storage with new key
178
179         bool keyrotation_status = meshlink_encrypted_key_rotate(mesh, "newkey", 6);
180         assert_int_equal(keyrotation_status, true);
181
182         meshlink_close(mesh);
183
184         // Reopen the meshlink instance with the new key
185
186         mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "oldkey", 6);
187         assert_int_equal(mesh, NULL);
188
189         // Cleanup
190
191         assert(meshlink_destroy("encrypted_conf"));
192
193         return true;
194 }
195
196 /* Execute key rotation Test Case # 4 - Sanity test */
197 static void test_case_key_rotation_04(void **state) {
198         execute_test(test_key_rotation_04, state);
199 }
200
201 /* Test Steps for key rotation Test Case # 4
202     Verify whether key rotation API gracefully handles invitations porting from
203     old key to new key.
204
205     Test Steps:
206     1. Open foo node instance and generate invitations for peer and bar.
207     2. Do key rotation with newkey and verify invitation timestamps post key rotation.
208     3. Change timestamp of peer key to expire and Open instances of foo, bar and peer nodes
209         and try to join bar and peer node.
210
211     Expected Result:
212     Key rotation API should never change the any file status attributes of an invitation file.
213 */
214 static bool test_key_rotation_04(void) {
215         meshlink_handle_t *mesh;
216         meshlink_handle_t *mesh1;
217         meshlink_handle_t *mesh2;
218         struct dirent *ent;
219         DIR *d;
220         char invitation_path_buff[500];
221         struct stat temp_stat;
222         struct stat peer_stat;
223         struct utimbuf timebuf;
224         bool join_status;
225         char *invitations_directory_path = "encrypted_conf/current/invitations/";
226
227         assert(meshlink_destroy("encrypted_conf"));
228         meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb);
229
230         // Open a new meshlink instance.
231
232         mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "oldkey", 6);
233         assert_int_not_equal(mesh, NULL);
234
235         // Generate invitations
236
237         char *invitation1 = meshlink_invite(mesh, NULL, "peer");
238         assert_int_not_equal(invitation1, NULL);
239
240         // Read the peer invitation file status structure
241
242         strcpy(invitation_path_buff, invitations_directory_path);
243         d = opendir(invitation_path_buff);
244         assert(d);
245
246         while((ent = readdir(d)) != NULL) {
247                 if(ent->d_name[0] == '.') {
248                         continue;
249                 }
250
251                 strcpy(invitation_path_buff, invitations_directory_path);
252                 strcat(invitation_path_buff, ent->d_name);
253                 assert(stat(invitation_path_buff, &temp_stat) != -1);
254
255                 if((temp_stat.st_mode & S_IFMT) == S_IFREG) {
256                         break;
257                 }
258         }
259
260         assert(ent);
261
262         closedir(d);
263
264         char *invitation2 = meshlink_invite(mesh, NULL, "bar");
265         assert_int_not_equal(invitation2, NULL);
266
267         // Key rotate the encrypted_conf storage with new key
268
269         bool keyrotation_status = meshlink_encrypted_key_rotate(mesh, "newkey", 6);
270         assert_int_equal(keyrotation_status, true);
271
272         meshlink_close(mesh);
273
274         // Compare invitation file timestamps of old key with new key
275
276         assert(stat(invitation_path_buff, &peer_stat) != -1);
277         assert_int_equal(peer_stat.st_mtime, temp_stat.st_mtime);
278
279         // Change timestamp for @ peer @ node invitation
280
281         timebuf.actime = peer_stat.st_atime;
282         timebuf.modtime = peer_stat.st_mtime - 604805; // > 1 week
283
284         assert(utime(invitation_path_buff, &timebuf) != -1);
285
286
287         // Reopen the meshlink instance with the new key
288
289         mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "newkey", 6);
290         assert_int_not_equal(mesh, NULL);
291
292         mesh1 = meshlink_open("encrypted_conf.1", "peer", "encrypted", DEV_CLASS_BACKBONE);
293         assert_int_not_equal(mesh1, NULL);
294
295         mesh2 = meshlink_open("encrypted_conf.2", "bar", "encrypted", DEV_CLASS_BACKBONE);
296         assert_int_not_equal(mesh2, NULL);
297
298         assert(meshlink_start(mesh));
299
300         join_status = meshlink_join(mesh1, invitation1);
301         assert_int_equal(join_status, false);
302
303         join_status = meshlink_join(mesh2, invitation2);
304         assert_int_equal(join_status, true);
305
306         // Cleanup
307
308         free(invitation1);
309         free(invitation2);
310         meshlink_close(mesh);
311         meshlink_close(mesh1);
312         meshlink_close(mesh2);
313         assert(meshlink_destroy("encrypted_conf"));
314         assert(meshlink_destroy("encrypted_conf.1"));
315         assert(meshlink_destroy("encrypted_conf.2"));
316
317         return true;
318 }
319
320 /* Execute key rotation Test Case # 5 - Atomicity test */
321 static void test_case_key_rotation_05(void **state) {
322         execute_test(test_key_rotation_05, state);
323 }
324
325 static int break_stage;
326
327 static void nop_stage(int stage) {
328         (void)stage;
329
330         return;
331 }
332
333 static void debug_probe(int stage) {
334
335         // Terminate the node at the specified stage (by @ break_stage @ )
336         if(stage == break_stage) {
337                 raise(SIGINT);
338         } else if((break_stage < 1) || (break_stage > 3)) {
339                 fprintf(stderr, "INVALID stage break\n");
340                 raise(SIGABRT);
341         }
342
343         return;
344 }
345
346 /* Test Steps for key rotation Test Case # 5
347     Debug all stages of key rotate API and verify it's atomicity
348
349     Test Steps:
350     1. Open foo node instance.
351     2. In a loop break meshlink node instance at each stage incrementally
352         in a fork process
353     3. Reopen node instance post termination.
354
355     Expected Result:
356     Terminating node instance when meshlink_encrypted_key_rotate function called
357     at any stage should give atomic result when reopened.
358 */
359 static bool test_key_rotation_05(void) {
360         pid_t pid;
361         int status;
362         meshlink_handle_t *mesh;
363         assert(meshlink_destroy("encrypted_conf"));
364         meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb);
365
366         assert(signal(SIGINT, SIG_DFL) != SIG_ERR);
367         assert(signal(SIGABRT, SIG_DFL) != SIG_ERR);
368
369         // Set debug_probe callback
370
371         devtool_keyrotate_probe = debug_probe;
372         int new_port = 12000;
373         int pipefd[2];
374
375         // incrementally debug meshlink_encrypted_key_rotate API atomicity
376
377         for(break_stage = 1; break_stage <= 3; break_stage += 1) {
378                 fprintf(stderr, "Debugging stage %d\n", break_stage);
379                 assert(meshlink_destroy("encrypted_conf"));
380
381                 assert(pipe(pipefd) != -1);
382
383                 pid = fork();
384                 assert(pid != -1);
385
386                 if(!pid) {
387                         close(pipefd[0]);
388                         mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "oldkey", 6);
389                         assert(mesh);
390                         meshlink_set_log_cb(mesh, MESHLINK_DEBUG, log_cb);
391                         meshlink_enable_discovery(mesh, false);
392
393                         assert(meshlink_set_port(mesh, new_port));
394
395                         char *invitation = meshlink_invite(mesh, NULL, "bar");
396                         assert(invitation);
397
398                         assert(write(pipefd[1], invitation, strlen(invitation) + 1) != -1);
399
400                         assert(meshlink_encrypted_key_rotate(mesh, "newkey", 6));
401                         raise(SIGABRT);
402                 }
403
404                 close(pipefd[1]);
405
406                 // Wait for child exit and verify which signal terminated it
407
408                 assert(waitpid(pid, &status, 0) != -1);
409                 assert_int_equal(WIFSIGNALED(status), true);
410                 assert_int_equal(WTERMSIG(status), SIGINT);
411
412                 // Reopen the node with invalid key other than old and new key should fail and should not affect
413                 // the existing confbase
414
415                 fprintf(stderr, "Opening mesh with invalid key\n");
416                 mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "invalidkey", 9);
417                 assert_int_equal(mesh, NULL);
418
419                 // Reopen the node with the "newkey", if it failed to open with "newkey" then
420                 // opening with the "oldkey" should succeed
421
422                 fprintf(stderr, "Opening mesh with new-key\n");
423                 mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "newkey", 6);
424
425                 if(!mesh) {
426                         fprintf(stderr, "Opening mesh with new-key failed trying to open with old-key\n");
427                         mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "oldkey", 6);
428                         assert_int_not_equal(mesh, NULL);
429                 }
430
431                 meshlink_set_log_cb(mesh, MESHLINK_DEBUG, log_cb);
432                 meshlink_enable_discovery(mesh, false);
433
434                 // Verify the newly set port and generated invitation
435
436                 int get_port = meshlink_get_port(mesh);
437                 assert_int_equal(get_port, new_port);
438
439                 char invitation[200];
440                 assert(read(pipefd[0], invitation, sizeof(invitation)) != -1);
441
442                 assert(meshlink_start(mesh));
443
444                 assert(meshlink_destroy("encrypted_conf.1"));
445
446                 meshlink_handle_t *mesh2 = meshlink_open("encrypted_conf.1", "bar", "bar", DEV_CLASS_BACKBONE);
447                 assert(mesh2);
448
449                 meshlink_set_log_cb(mesh2, MESHLINK_DEBUG, log_cb);
450                 meshlink_enable_discovery(mesh2, false);
451
452                 assert_int_equal(meshlink_join(mesh2, invitation), true);
453
454                 // cleanup
455
456                 meshlink_close(mesh);
457                 meshlink_close(mesh2);
458
459                 close(pipefd[0]);
460         }
461
462         // Cleanup
463
464         assert(meshlink_destroy("encrypted_conf"));
465         assert(meshlink_destroy("encrypted_conf.1"));
466         devtool_keyrotate_probe = nop_stage;
467         return true;
468 }
469
470 int test_meshlink_encrypted_key_rotation(void) {
471         /* State structures for key rotation Test Cases */
472         black_box_state_t test_case_key_rotation_01_state = {
473                 .test_case_name = "test_case_key_rotation_01",
474         };
475         black_box_state_t test_case_key_rotation_02_state = {
476                 .test_case_name = "test_case_key_rotation_02",
477         };
478         black_box_state_t test_case_key_rotation_03_state = {
479                 .test_case_name = "test_case_key_rotation_03",
480         };
481         black_box_state_t test_case_key_rotation_04_state = {
482                 .test_case_name = "test_case_key_rotation_04",
483         };
484         black_box_state_t test_case_key_rotation_05_state = {
485                 .test_case_name = "test_case_key_rotation_05",
486         };
487
488         const struct CMUnitTest blackbox_status_tests[] = {
489                 cmocka_unit_test_prestate_setup_teardown(test_case_key_rotation_01, NULL, NULL,
490                                 (void *)&test_case_key_rotation_01_state),
491                 cmocka_unit_test_prestate_setup_teardown(test_case_key_rotation_02, NULL, NULL,
492                                 (void *)&test_case_key_rotation_02_state),
493                 cmocka_unit_test_prestate_setup_teardown(test_case_key_rotation_03, NULL, NULL,
494                                 (void *)&test_case_key_rotation_03_state),
495                 cmocka_unit_test_prestate_setup_teardown(test_case_key_rotation_04, NULL, NULL,
496                                 (void *)&test_case_key_rotation_04_state),
497                 cmocka_unit_test_prestate_setup_teardown(test_case_key_rotation_05, NULL, NULL,
498                                 (void *)&test_case_key_rotation_05_state),
499         };
500         total_tests += sizeof(blackbox_status_tests) / sizeof(blackbox_status_tests[0]);
501
502         return cmocka_run_group_tests(blackbox_status_tests, NULL, NULL);
503 }