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