Test: unix socket: test credential passing
[lttng-tools.git] / tests / unit / test_unix_socket.c
CommitLineData
0b5a4de9
JG
1/*
2 * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-only
5 *
6 */
7
7b0027b3 8#include <common/compat/fcntl.h>
0b5a4de9
JG
9#include <common/sessiond-comm/sessiond-comm.h>
10#include <common/payload.h>
11#include <common/payload-view.h>
12#include <common/unix.h>
13#include <common/utils.h>
14#include <common/defaults.h>
15#include <tap/tap.h>
0b5a4de9
JG
16#include <stdbool.h>
17#include <common/error.h>
18#include <lttng/constant.h>
19#include <stdio.h>
20#include <pthread.h>
21#include <unistd.h>
707602aa
JG
22#include <sys/wait.h>
23#include <stdlib.h>
0b5a4de9
JG
24
25#define HIGH_FD_COUNT LTTCOMM_MAX_SEND_FDS
26#define MESSAGE_COUNT 4
27#define LARGE_PAYLOAD_SIZE 4 * 1024
28#define LARGE_PAYLOAD_RECV_SIZE 100
29
707602aa 30static const int TEST_COUNT = 37;
0b5a4de9
JG
31
32/* For error.h */
33int lttng_opt_quiet;
34int lttng_opt_verbose;
35int lttng_opt_mi;
36
37/*
38 * Validate that a large number of file descriptors can be received in one shot.
39 */
40static void test_high_fd_count(unsigned int fd_count)
41{
42 int sockets[2] = {-1, -1};
43 int ret;
44 unsigned int i;
45 const unsigned int payload_content = 42;
46 struct lttng_payload sent_payload;
47 struct lttng_payload received_payload;
48
49 diag("Send and receive high FD count atomically (%u FDs)", fd_count);
50 lttng_payload_init(&sent_payload);
51 lttng_payload_init(&received_payload);
52
53 ret = lttcomm_create_anon_unix_socketpair(sockets);
54 ok(ret == 0, "Created anonymous unix socket pair");
55 if (ret < 0) {
56 PERROR("Failed to create an anonymous pair of unix sockets");
57 goto error;
58 }
59
60 /* Add dummy content to payload. */
61 ret = lttng_dynamic_buffer_append(&sent_payload.buffer,
62 &payload_content, sizeof(payload_content));
63 if (ret) {
64 PERROR("Failed to initialize test payload");
65 goto error;
66 }
67
68 for (i = 0; i < fd_count; i++) {
69 struct fd_handle *handle;
7b0027b3 70 int fd = fcntl(STDOUT_FILENO, F_DUPFD, 0);
0b5a4de9
JG
71
72 if (fd < 0) {
7b0027b3 73 PERROR("Failed to create fd while creating test payload");
0b5a4de9
JG
74 goto error;
75 }
76
77 handle = fd_handle_create(fd);
78 if (!handle) {
79 if (close(fd)) {
7b0027b3 80 PERROR("Failed to close fd while preparing test payload");
0b5a4de9
JG
81 goto error;
82 }
83 }
84
85 ret = lttng_payload_push_fd_handle(&sent_payload, handle);
86 fd_handle_put(handle);
87 if (ret) {
88 PERROR("Failed to add fd handle to test payload");
89 goto error;
90 }
91 }
92
93 /* Send payload. */
94 {
95 ssize_t sock_ret;
96 struct lttng_payload_view pv = lttng_payload_view_from_payload(
97 &sent_payload, 0, -1);
98
99 /* Not expected to block considering the size of the payload. */
100 sock_ret = lttcomm_send_unix_sock(
101 sockets[0], pv.buffer.data, pv.buffer.size);
102 ok(sock_ret == pv.buffer.size, "Sent complete test payload");
103 if (sock_ret != pv.buffer.size) {
104 ERR("Failed to send test payload bytes: ret = %zd, expected = %zu",
105 sock_ret, pv.buffer.size);
106 goto error;
107 }
108
109 sock_ret = lttcomm_send_payload_view_fds_unix_sock(
110 sockets[0], &pv);
111 ok(sock_ret == 1, "Sent test payload file descriptors");
112 if (sock_ret != 1) {
113 if (sock_ret < 0) {
114 PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d",
115 sock_ret, 1);
116 } else {
117 diag("Failed to send test payload file descriptors: ret = %zd, expected = %d",
118 sock_ret, 1);
119 }
120
121 goto error;
122 }
123 }
124
125 /* Receive payload */
126 {
127 ssize_t sock_ret;
128
129 ret = lttng_dynamic_buffer_set_size(&received_payload.buffer,
130 sent_payload.buffer.size);
131 if (ret) {
132 PERROR("Failed to pre-allocate reception buffer");
133 goto error;
134 }
135
136 sock_ret = lttcomm_recv_unix_sock(sockets[1],
137 received_payload.buffer.data,
138 received_payload.buffer.size);
139 ok(sock_ret == received_payload.buffer.size,
140 "Received payload bytes");
141 if (sock_ret != received_payload.buffer.size) {
142 ERR("Failed to receive payload bytes: ret = %zd, expected = %zu",
143 sock_ret, received_payload.buffer.size);
144 goto error;
145 }
146
147 sock_ret = lttcomm_recv_payload_fds_unix_sock(
148 sockets[1], fd_count, &received_payload);
149 ok(sock_ret == (int) (sizeof(int) * fd_count),
150 "FD reception return value is number of fd * sizeof(int)");
151 if (sock_ret != (int) (sizeof(int) * fd_count)) {
152 ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %d",
153 sock_ret,
154 (int) (fd_count * sizeof(int)));
155 goto error;
156 }
157
158 {
159 const struct lttng_payload_view pv =
160 lttng_payload_view_from_payload(
161 &received_payload, 0,
162 -1);
163 const int fd_handle_count =
164 lttng_payload_view_get_fd_handle_count(
165 &pv);
166
167 ok(fd_handle_count == fd_count,
168 "Received all test payload file descriptors in one invocation");
169 }
170 }
171
172error:
173 for (i = 0; i < 2; i++) {
174 if (sockets[i] < 0) {
175 continue;
176 }
177
178 if (close(sockets[i])) {
179 PERROR("Failed to close unix socket");
180 }
181 }
182
183 lttng_payload_reset(&sent_payload);
184 lttng_payload_reset(&received_payload);
185}
186
187/*
188 * Validate that if the sender sent multiple messages, each containing 1 fd,
189 * the receiver can receive one message at a time (the binary payload and its
190 * fd) and is not forced to receive all file descriptors at once.
191 */
192static void test_one_fd_per_message(unsigned int message_count)
193{
194 const unsigned int payload_content = 42;
195 int sockets[2] = {-1, -1};
196 int ret;
197 unsigned int i;
198 struct lttng_payload sent_payload;
199 struct lttng_payload received_payload;
200
201 diag("Send and receive small messages with one FD each (%u messages)",
202 message_count);
203 lttng_payload_init(&sent_payload);
204 lttng_payload_init(&received_payload);
205
206 ret = lttcomm_create_anon_unix_socketpair(sockets);
207 ok(ret == 0, "Created anonymous unix socket pair");
208 if (ret < 0) {
209 PERROR("Failed to create an anonymous pair of unix sockets");
210 goto error;
211 }
212
213 /* Send messages with one fd each. */
214 for (i = 0; i < message_count; i++) {
215 struct fd_handle *handle;
216 int fd;
217
218 /* Add dummy content to payload. */
219 ret = lttng_dynamic_buffer_append(&sent_payload.buffer,
220 &payload_content, sizeof(payload_content));
221 if (ret) {
222 PERROR("Failed to initialize test payload");
223 goto error;
224 }
225
7b0027b3 226 fd = fcntl(STDOUT_FILENO, F_DUPFD, 0);
0b5a4de9 227 if (fd < 0) {
7b0027b3 228 PERROR("Failed to create fd while creating test payload");
0b5a4de9
JG
229 goto error;
230 }
231
232 handle = fd_handle_create(fd);
233 if (!handle) {
234 if (close(fd)) {
7b0027b3 235 PERROR("Failed to close fd while preparing test payload");
0b5a4de9
JG
236 goto error;
237 }
238 }
239
240 ret = lttng_payload_push_fd_handle(&sent_payload, handle);
241 fd_handle_put(handle);
242 if (ret) {
243 PERROR("Failed to add fd handle to test payload");
244 goto error;
245 }
246
247 /* Send payload. */
248 {
249 ssize_t sock_ret;
250 struct lttng_payload_view pv =
251 lttng_payload_view_from_payload(
252 &sent_payload, 0, -1);
253
254 /* Not expected to block considering the size of the
255 * payload. */
256 sock_ret = lttcomm_send_unix_sock(sockets[0],
257 pv.buffer.data, pv.buffer.size);
258 ok(sock_ret == pv.buffer.size,
259 "Sent binary payload for message %u",
260 i);
261 if (sock_ret != pv.buffer.size) {
262 ERR("Failed to send test payload bytes: ret = %zd, expected = %zu",
263 sock_ret, pv.buffer.size);
264 goto error;
265 }
266
267 sock_ret = lttcomm_send_payload_view_fds_unix_sock(
268 sockets[0], &pv);
269 ok(sock_ret == 1,
270 "Sent file descriptors payload for message %u",
271 i);
272 if (sock_ret != 1) {
273 if (sock_ret < 0) {
274 PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d",
275 sock_ret, 1);
276 } else {
277 diag("Failed to send test payload file descriptors: ret = %zd, expected = %d",
278 sock_ret, 1);
279 }
280
281 goto error;
282 }
283 }
284
285 lttng_payload_clear(&sent_payload);
286 }
287
288 /* Receive messages one at a time. */
289 for (i = 0; i < message_count; i++) {
290 ssize_t sock_ret;
291
292 ret = lttng_dynamic_buffer_set_size(&received_payload.buffer,
293 sizeof(payload_content));
294 if (ret) {
295 PERROR("Failed to pre-allocate reception buffer");
296 goto error;
297 }
298
299 sock_ret = lttcomm_recv_unix_sock(sockets[1],
300 received_payload.buffer.data,
301 received_payload.buffer.size);
302 ok(sock_ret == received_payload.buffer.size,
303 "Received payload bytes for message %u", i);
304 if (sock_ret != received_payload.buffer.size) {
305 ERR("Failed to receive payload bytes: ret = %zd, expected = %zu",
306 sock_ret, received_payload.buffer.size);
307 goto error;
308 }
309
310 sock_ret = lttcomm_recv_payload_fds_unix_sock(
311 sockets[1], 1, &received_payload);
312 ok(sock_ret == (int) sizeof(int), "Received fd for message %u",
313 i);
314 if (sock_ret != (int) sizeof(int)) {
315 ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %u",
316 sock_ret, (int) sizeof(int));
317 goto error;
318 }
319
320 {
321 const struct lttng_payload_view pv =
322 lttng_payload_view_from_payload(
323 &received_payload, 0,
324 -1);
325 const int fd_handle_count =
326 lttng_payload_view_get_fd_handle_count(
327 &pv);
328
329 ok(fd_handle_count == 1,
330 "Payload contains 1 fd for message %u",
331 i);
332 }
333
334 lttng_payload_clear(&received_payload);
335 }
336
337error:
338 for (i = 0; i < 2; i++) {
339 if (sockets[i] < 0) {
340 continue;
341 }
342
343 if (close(sockets[i])) {
344 PERROR("Failed to close unix socket");
345 }
346 }
347
348 lttng_payload_reset(&sent_payload);
349 lttng_payload_reset(&received_payload);
350}
351
352/*
353 * Validate that a large message can be received in multiple chunks.
354 */
355static void test_receive_in_chunks(
356 unsigned int payload_size, unsigned int max_recv_size)
357{
358 int sockets[2] = {-1, -1};
359 int ret;
360 unsigned int i;
361 struct lttng_payload sent_payload;
362 struct lttng_payload received_payload;
363 struct fd_handle *handle;
364 int fd;
365 ssize_t sock_ret, received = 0;
366
367 diag("Receive a message in multiple chunks");
368 lttng_payload_init(&sent_payload);
369 lttng_payload_init(&received_payload);
370
371 ret = lttcomm_create_anon_unix_socketpair(sockets);
372 ok(ret == 0, "Created anonymous unix socket pair");
373 if (ret < 0) {
374 PERROR("Failed to create an anonymous pair of unix sockets");
375 goto error;
376 }
377
378 /* Add dummy content to payload. */
379 ret = lttng_dynamic_buffer_set_size(&sent_payload.buffer, payload_size);
380 if (ret) {
381 PERROR("Failed to initialize test payload");
382 goto error;
383 }
384
7b0027b3 385 fd = fcntl(STDOUT_FILENO, F_DUPFD, 0);
0b5a4de9 386 if (fd < 0) {
7b0027b3 387 PERROR("Failed to create fd while creating test payload");
0b5a4de9
JG
388 goto error;
389 }
390
391 handle = fd_handle_create(fd);
392 if (!handle) {
393 if (close(fd)) {
7b0027b3 394 PERROR("Failed to close fd while preparing test payload");
0b5a4de9
JG
395 goto error;
396 }
397 }
398
399 ret = lttng_payload_push_fd_handle(&sent_payload, handle);
400 fd_handle_put(handle);
401 if (ret) {
402 PERROR("Failed to add fd handle to test payload");
403 goto error;
404 }
405
406 /* Send payload. */
407 {
408 struct lttng_payload_view pv = lttng_payload_view_from_payload(
409 &sent_payload, 0, -1);
410
411 /* Not expected to block considering the size of the payload. */
412 sock_ret = lttcomm_send_unix_sock(
413 sockets[0], pv.buffer.data, pv.buffer.size);
414 ok(sock_ret == pv.buffer.size, "Sent complete test payload");
415 if (sock_ret != pv.buffer.size) {
416 ERR("Failed to send test payload bytes: ret = %zd, expected = %zu",
417 sock_ret, pv.buffer.size);
418 goto error;
419 }
420
421 sock_ret = lttcomm_send_payload_view_fds_unix_sock(
422 sockets[0], &pv);
423 ok(sock_ret == 1, "Sent test payload file descriptors");
424 if (sock_ret != 1) {
425 if (sock_ret < 0) {
426 PERROR("Failed to send test payload file descriptors: ret = %zd, expected = %d",
427 sock_ret, 1);
428 } else {
429 diag("Failed to send test payload file descriptors: ret = %zd, expected = %d",
430 sock_ret, 1);
431 }
432
433 goto error;
434 }
435 }
436
437 /* Receive payload */
438 ret = lttng_dynamic_buffer_set_size(
439 &received_payload.buffer, sent_payload.buffer.size);
440 if (ret) {
441 PERROR("Failed to pre-allocate reception buffer");
442 goto error;
443 }
444
445 do {
446 const ssize_t to_receive_this_pass = min(max_recv_size,
447 sent_payload.buffer.size - received);
448
449 sock_ret = lttcomm_recv_unix_sock(sockets[1],
450 received_payload.buffer.data + received,
451 to_receive_this_pass);
452 if (sock_ret != to_receive_this_pass) {
453 ERR("Failed to receive payload bytes: ret = %zd, expected = %zu",
454 sock_ret, to_receive_this_pass);
455 break;
456 }
457
458 received += sock_ret;
459 } while (received < sent_payload.buffer.size);
460
461 ok(received == sent_payload.buffer.size,
462 "Received complete payload in chunks of %u bytes",
463 max_recv_size);
464 if (received != sent_payload.buffer.size) {
465 goto error;
466 }
467
468 sock_ret = lttcomm_recv_payload_fds_unix_sock(
469 sockets[1], 1, &received_payload);
470 ok(sock_ret == (int) sizeof(int),
471 "Received file descriptor after receiving payload in chunks");
472 if (sock_ret != (int) sizeof(int)) {
473 ERR("Failed to receive test payload file descriptors: ret = %zd, expected = %d",
474 sock_ret, (int) sizeof(int));
475 goto error;
476 }
477
478 {
479 const struct lttng_payload_view pv =
480 lttng_payload_view_from_payload(
481 &received_payload, 0, -1);
482 const int fd_handle_count =
483 lttng_payload_view_get_fd_handle_count(&pv);
484
485 ok(fd_handle_count == 1,
486 "Payload contains 1 fd after receiving payload in chunks");
487 }
488
489error:
490 for (i = 0; i < 2; i++) {
491 if (sockets[i] < 0) {
492 continue;
493 }
494
495 if (close(sockets[i])) {
496 PERROR("Failed to close unix socket");
497 }
498 }
499
500 lttng_payload_reset(&sent_payload);
501 lttng_payload_reset(&received_payload);
502}
503
707602aa
JG
504static
505void test_creds_passing(void)
506{
507 pid_t fork_ret = -1;
508 int ret, parent_socket = -1, child_connection_socket = -1;
509 ssize_t sock_ret;
510 char socket_dir_path[] = "/tmp/test.unix.socket.creds.passing.XXXXXX";
511 char socket_path[PATH_MAX] = {};
512 struct expected_creds {
513 uid_t euid;
514 gid_t egid;
515 pid_t pid;
516 } expected_creds;
517
518 diag("Receive peer's effective uid, effective gid, and pid from a unix socket");
519
520 if (!mkdtemp(socket_dir_path)) {
521 PERROR("Failed to generate temporary socket location");
522 goto error;
523 }
524
525 strncat(socket_path, socket_dir_path,
526 sizeof(socket_path) - strlen(socket_path) - 1);
527 strncat(socket_path, "/test_unix_socket",
528 sizeof(socket_path) - strlen(socket_path) - 1);
529
530 parent_socket = lttcomm_create_unix_sock(socket_path);
531 ok(parent_socket >= 0, "Created unix socket at path `%s`", socket_path);
532 if (parent_socket < 0) {
533 PERROR("Failed to create unix socket at path `%s`", socket_path);
534 goto error;
535 }
536
537 ret = lttcomm_listen_unix_sock(parent_socket);
538 if (ret < 0) {
539 PERROR("Failed to mark parent socket as a passive socket");
540 goto error;
541 }
542
543 ret = lttcomm_setsockopt_creds_unix_sock(parent_socket);
544 if (ret) {
545 PERROR("Failed to set SO_PASSCRED on parent socket");
546 goto error;
547 }
548
549 fork_ret = fork();
550 if (fork_ret < 0) {
551 PERROR("Failed to fork");
552 goto error;
553 }
554
555 if (fork_ret == 0) {
556 /* Child. */
557 int child_socket;
558
559 expected_creds = (struct expected_creds){
560 .euid = geteuid(),
561 .egid = getegid(),
562 .pid = getpid(),
563 };
564
565 child_socket = lttcomm_connect_unix_sock(socket_path);
566 if (child_socket < 0) {
567 PERROR("Failed to connect to parent socket");
568 goto error;
569 }
570
571 ret = lttcomm_setsockopt_creds_unix_sock(child_socket);
572 if (ret) {
573 PERROR("Failed to set SO_PASSCRED on child socket");
574 }
575
576 sock_ret = lttcomm_send_creds_unix_sock(child_socket, &expected_creds,
577 sizeof(expected_creds));
578 if (sock_ret < 0) {
579 PERROR("Failed to send expected credentials");
580 }
581
582 ret = close(child_socket);
583 if (ret) {
584 PERROR("Failed to close child socket");
585 }
586 } else {
587 /* Parent. */
588 int child_status;
589 pid_t wait_pid_ret;
590 lttng_sock_cred received_creds = {};
591
592 child_connection_socket =
593 lttcomm_accept_unix_sock(parent_socket);
594 if (child_connection_socket < 0) {
595 PERROR();
596 goto error;
597 }
598
599 ret = lttcomm_setsockopt_creds_unix_sock(
600 child_connection_socket);
601 if (ret) {
602 PERROR("Failed to set SO_PASSCRED on child connection socket");
603 goto error;
604 }
605
606 sock_ret = lttcomm_recv_creds_unix_sock(child_connection_socket,
607 &expected_creds, sizeof(expected_creds),
608 &received_creds);
609 if (sock_ret < 0) {
610 PERROR("Failed to receive credentials");
611 goto error;
612 }
613
614 wait_pid_ret = waitpid(fork_ret, &child_status, 0);
615 if (wait_pid_ret == -1) {
616 PERROR("Failed to wait for termination of child process");
617 goto error;
618 }
619 if (!WIFEXITED(child_status) || WEXITSTATUS(child_status)) {
620 diag("Child process reported an error, test failed");
621 goto error;
622 }
623
624 ok(expected_creds.euid == received_creds.uid,
625 "Received the expected effective uid (%d == %d)",
626 expected_creds.euid, received_creds.uid);
627 ok(expected_creds.egid == received_creds.gid,
628 "Received the expected effective gid (%d == %d)",
629 expected_creds.egid, received_creds.gid);
630 ok(expected_creds.pid == received_creds.pid,
631 "Received the expected pid (%d == %d)",
632 expected_creds.pid, received_creds.pid);
633 }
634
635error:
636 if (parent_socket >= 0) {
637 ret = close(parent_socket);
638 if (ret) {
639 PERROR("Failed to close parent socket");
640 }
641 }
642
643 if (fork_ret == 0) {
644 if (child_connection_socket >= 0) {
645 ret = close(child_connection_socket);
646 if (ret) {
647 PERROR("Failed to close child connection socket");
648 }
649 }
650
651 /* Prevent libtap from printing a result for the child. */
652 fclose(stdout);
653 fclose(stderr);
654
655 /* Child exits at the end of this test. */
656 exit(0);
657 } else if (parent_socket >= 0) {
658 ret = unlink(socket_path);
659 if (ret) {
660 PERROR("Failed to unlink socket at path `%s`",
661 socket_path);
662 }
663
664 ret = rmdir(socket_dir_path);
665 if (ret) {
666 PERROR("Failed to remove test directory at `%s`",
667 socket_dir_path);
668 }
669 }
670}
671
0b5a4de9
JG
672int main(void)
673{
674 plan_tests(TEST_COUNT);
675
676 test_high_fd_count(HIGH_FD_COUNT);
677 test_one_fd_per_message(MESSAGE_COUNT);
678 test_receive_in_chunks(LARGE_PAYLOAD_SIZE, LARGE_PAYLOAD_RECV_SIZE);
707602aa 679 test_creds_passing();
0b5a4de9
JG
680
681 return exit_status();
682}
This page took 0.054039 seconds and 5 git commands to generate.