@cryptotaxi247 / netdata-1 / commits / 7b78bd851

add authorization to spawn requests (#18103)

Costa Tsaousis committed Jul 10, 2024 at 16:27 UTC 7b78bd851fa56e0f9d1e1a51f272aac64f7a03cb
7 files changed +126 -63
src/collectors/network-viewer.plugin/network-viewer.c
+1 -1
@@ -940,7 +940,7 @@ int main(int argc __maybe_unused, char **argv __maybe_unused) {
940
941 uc = system_usernames_cache_init();
942
943 - spawn_srv = spawn_server_create("setns", local_sockets_spawn_server_callback, argc, (const char **)argv);
943 + spawn_srv = spawn_server_create(SPAWN_SERVER_OPTION_CALLBACK, "setns", local_sockets_spawn_server_callback, argc, (const char **)argv);
944 if(spawn_srv == NULL) {
945 fprintf(stderr, "Cannot create spawn server.\n");
946 exit(1);
src/collectors/plugins.d/local_listeners.c
+1 -1
@@ -291,7 +291,7 @@ int main(int argc, char **argv) {
291 }
292 }
293
294 - SPAWN_SERVER *spawn_server = spawn_server_create(NULL, local_sockets_spawn_server_callback, argc, (const char **)argv);
294 + SPAWN_SERVER *spawn_server = spawn_server_create(SPAWN_SERVER_OPTION_CALLBACK, NULL, local_sockets_spawn_server_callback, argc, (const char **)argv);
295 if(spawn_server == NULL) {
296 fprintf(stderr, "Cannot create spawn server.\n");
297 exit(1);
src/libnetdata/libnetdata.c
+1 -1
@@ -493,7 +493,7 @@ char *strndupz(const char *s, size_t len) {
493
494 // If ptr is NULL, no operation is performed.
495 void freez(void *ptr) {
496 - free(ptr);
496 + if(likely(ptr)) free(ptr);
497 }
498
499 void *mallocz(size_t size) {
src/libnetdata/maps/local-sockets.h
+1 -1
@@ -1016,7 +1016,7 @@ static inline void local_sockets_init(LS_STATE *ls) {
1016 #endif
1017
1018 if(ls->config.namespaces && ls->spawn_server == NULL) {
1019 - ls->spawn_server = spawn_server_create(NULL, local_sockets_spawn_server_callback, 0, NULL);
1019 + ls->spawn_server = spawn_server_create(SPAWN_SERVER_OPTION_CALLBACK, NULL, local_sockets_spawn_server_callback, 0, NULL);
1020 ls->spawn_server_is_mine = true;
1021 }
1022 else
src/libnetdata/spawn_server/spawn_popen.c
+1 -1
@@ -9,7 +9,7 @@ bool netdata_main_spawn_server_init(const char *name, int argc, const char **arg
9 static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
10 spinlock_lock(&spinlock);
11 if(netdata_main_spawn_server == NULL)
12 - netdata_main_spawn_server = spawn_server_create(name, NULL, argc, argv);
12 + netdata_main_spawn_server = spawn_server_create(SPAWN_SERVER_OPTION_EXEC, name, NULL, argc, argv);
13 spinlock_unlock(&spinlock);
14 }
15
src/libnetdata/spawn_server/spawn_server.c
+112 -56
@@ -17,6 +17,11 @@ struct spawn_server {
17 size_t request_id;
18 const char *name;
19 #if !defined(OS_WINDOWS)
20 + SPAWN_SERVER_OPTIONS options;
21 +
22 + ND_UUID magic; // for authorizing requests, the client needs to know our random UUID
23 + // it is ignored for PING requests
24 +
25 int pipe[2];
26 int server_sock;
27 pid_t server_pid;
@@ -25,7 +30,6 @@ struct spawn_server {
30
31 int argc;
32 const char **argv;
28 - size_t argv0_size;
33 #endif
34 };
35
@@ -51,7 +55,7 @@ void spawn_server_instance_write_fd_unset(SPAWN_INSTANCE *si) { si->write_fd = -
55
56 #if defined(OS_WINDOWS)
57
54 -SPAWN_SERVER* spawn_server_create(const char *name, spawn_request_callback_t cb __maybe_unused, int argc __maybe_unused, const char **argv __maybe_unused) {
58 +SPAWN_SERVER* spawn_server_create(SPAWN_SERVER_OPTIONS options __maybe_unused, const char *name, spawn_request_callback_t cb __maybe_unused, int argc __maybe_unused, const char **argv __maybe_unused) {
59 SPAWN_SERVER* server = callocz(1, sizeof(SPAWN_SERVER));
60 if(name)
61 server->name = strdupz(name);
@@ -536,17 +540,18 @@ typedef enum __attribute__((packed)) {
540
541 static bool spawn_server_is_running(const char *path) {
542 struct msghdr msg = {0};
539 - struct iovec iov[6];
543 + struct iovec iov[7];
544 SPAWN_SERVER_MSG msg_type = SPAWN_SERVER_MSG_PING;
545 size_t dummy_size = 0;
546 SPAWN_INSTANCE_TYPE dummy_type = 0;
547 + ND_UUID magic = UUID_ZERO;
548 char cmsgbuf[CMSG_SPACE(sizeof(int))];
549
550 iov[0].iov_base = &msg_type;
551 iov[0].iov_len = sizeof(msg_type);
552
548 - iov[1].iov_base = &dummy_size;
549 - iov[1].iov_len = sizeof(dummy_size);
553 + iov[1].iov_base = magic.uuid;
554 + iov[1].iov_len = sizeof(magic.uuid);
555
556 iov[2].iov_base = &dummy_size;
557 iov[2].iov_len = sizeof(dummy_size);
@@ -557,11 +562,14 @@ static bool spawn_server_is_running(const char *path) {
562 iov[4].iov_base = &dummy_size;
563 iov[4].iov_len = sizeof(dummy_size);
564
560 - iov[5].iov_base = &dummy_type;
561 - iov[5].iov_len = sizeof(dummy_type);
565 + iov[5].iov_base = &dummy_size;
566 + iov[5].iov_len = sizeof(dummy_size);
567 +
568 + iov[6].iov_base = &dummy_type;
569 + iov[6].iov_len = sizeof(dummy_type);
570
571 msg.msg_iov = iov;
564 - msg.msg_iovlen = 6;
572 + msg.msg_iovlen = 7;
573 msg.msg_control = cmsgbuf;
574 msg.msg_controllen = sizeof(cmsgbuf);
575
@@ -588,7 +596,7 @@ static bool spawn_server_is_running(const char *path) {
596 return sr.status == STATUS_REPORT_PING;
597 }
598
591 -static bool spawn_server_send_request(SPAWN_REQUEST *request) {
599 +static bool spawn_server_send_request(ND_UUID *magic, SPAWN_REQUEST *request) {
600 bool ret = false;
601
602 size_t env_size = 0;
@@ -605,7 +613,7 @@ static bool spawn_server_send_request(SPAWN_REQUEST *request) {
613 struct cmsghdr *cmsg;
614 SPAWN_SERVER_MSG msg_type = SPAWN_SERVER_MSG_REQUEST;
615 char cmsgbuf[CMSG_SPACE(sizeof(int) * SPAWN_SERVER_TRANSFER_FDS)];
608 - struct iovec iov[10];
616 + struct iovec iov[11];
617
618
619 // We send 1 request with 10 iovec in it
@@ -616,35 +624,38 @@ static bool spawn_server_send_request(SPAWN_REQUEST *request) {
624 iov[0].iov_base = &msg_type;
625 iov[0].iov_len = sizeof(msg_type);
626
619 - iov[1].iov_base = &request->request_id;
620 - iov[1].iov_len = sizeof(request->request_id);
627 + iov[1].iov_base = magic->uuid;
628 + iov[1].iov_len = sizeof(magic->uuid);
629
622 - iov[2].iov_base = &env_size;
623 - iov[2].iov_len = sizeof(env_size);
630 + iov[2].iov_base = &request->request_id;
631 + iov[2].iov_len = sizeof(request->request_id);
632
625 - iov[3].iov_base = &argv_size;
626 - iov[3].iov_len = sizeof(argv_size);
633 + iov[3].iov_base = &env_size;
634 + iov[3].iov_len = sizeof(env_size);
635
628 - iov[4].iov_base = &request->data_size;
629 - iov[4].iov_len = sizeof(request->data_size);
636 + iov[4].iov_base = &argv_size;
637 + iov[4].iov_len = sizeof(argv_size);
638
631 - iov[5].iov_base = &request->type; // Added this line
632 - iov[5].iov_len = sizeof(request->type);
639 + iov[5].iov_base = &request->data_size;
640 + iov[5].iov_len = sizeof(request->data_size);
641
634 - iov[6].iov_base = encoded_env;
635 - iov[6].iov_len = env_size;
642 + iov[6].iov_base = &request->type; // Added this line
643 + iov[6].iov_len = sizeof(request->type);
644
637 - iov[7].iov_base = encoded_argv;
638 - iov[7].iov_len = argv_size;
645 + iov[7].iov_base = encoded_env;
646 + iov[7].iov_len = env_size;
647
640 - iov[8].iov_base = (char *)request->data;
641 - iov[8].iov_len = request->data_size;
648 + iov[8].iov_base = encoded_argv;
649 + iov[8].iov_len = argv_size;
650
643 - iov[9].iov_base = NULL;
644 - iov[9].iov_len = 0;
651 + iov[9].iov_base = (char *)request->data;
652 + iov[9].iov_len = request->data_size;
653 +
654 + iov[10].iov_base = NULL;
655 + iov[10].iov_len = 0;
656
657 msg.msg_iov = iov;
647 - msg.msg_iovlen = 10;
658 + msg.msg_iovlen = 11;
659 msg.msg_control = cmsgbuf;
660 msg.msg_controllen = CMSG_SPACE(sizeof(int) * SPAWN_SERVER_TRANSFER_FDS);
661
@@ -675,12 +686,13 @@ cleanup:
686
687 static void spawn_server_receive_request(int sock, SPAWN_SERVER *server) {
688 struct msghdr msg = {0};
678 - struct iovec iov[6];
689 + struct iovec iov[7];
690 SPAWN_SERVER_MSG msg_type = SPAWN_SERVER_MSG_INVALID;
691 size_t request_id;
692 size_t env_size;
693 size_t argv_size;
694 size_t data_size;
695 + ND_UUID magic = UUID_ZERO;
696 SPAWN_INSTANCE_TYPE type;
697 char cmsgbuf[CMSG_SPACE(sizeof(int) * SPAWN_SERVER_TRANSFER_FDS)];
698 char *envp = NULL, *argv = NULL, *data = NULL;
@@ -689,29 +701,67 @@ static void spawn_server_receive_request(int sock, SPAWN_SERVER *server) {
701 // First recvmsg() to read sizes and control message
702 iov[0].iov_base = &msg_type;
703 iov[0].iov_len = sizeof(msg_type);
692 - iov[1].iov_base = &request_id;
693 - iov[1].iov_len = sizeof(request_id);
694 - iov[2].iov_base = &env_size;
695 - iov[2].iov_len = sizeof(env_size);
696 - iov[3].iov_base = &argv_size;
697 - iov[3].iov_len = sizeof(argv_size);
698 - iov[4].iov_base = &data_size;
699 - iov[4].iov_len = sizeof(data_size);
700 - iov[5].iov_base = &type;
701 - iov[5].iov_len = sizeof(type);
704 +
705 + iov[1].iov_base = magic.uuid;
706 + iov[1].iov_len = sizeof(magic.uuid);
707 +
708 + iov[2].iov_base = &request_id;
709 + iov[2].iov_len = sizeof(request_id);
710 +
711 + iov[3].iov_base = &env_size;
712 + iov[3].iov_len = sizeof(env_size);
713 +
714 + iov[4].iov_base = &argv_size;
715 + iov[4].iov_len = sizeof(argv_size);
716 +
717 + iov[5].iov_base = &data_size;
718 + iov[5].iov_len = sizeof(data_size);
719 +
720 + iov[6].iov_base = &type;
721 + iov[6].iov_len = sizeof(type);
722
723 msg.msg_iov = iov;
704 - msg.msg_iovlen = 6;
724 + msg.msg_iovlen = 7;
725 msg.msg_control = cmsgbuf;
726 msg.msg_controllen = sizeof(cmsgbuf);
727
728 if (recvmsg(sock, &msg, 0) < 0) {
709 - nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: failed to recvmsg() the first part of the request.");
729 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
730 + "SPAWN SERVER: failed to recvmsg() the first part of the request.");
731 + close(sock);
732 return;
733 }
734
735 if(msg_type == SPAWN_SERVER_MSG_PING) {
736 spawn_server_send_status_ping(sock);
737 + close(sock);
738 + return;
739 + }
740 +
741 + if(!UUIDeq(magic, server->magic)) {
742 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
743 + "SPAWN SERVER: Invalid authorization key for request %zu. "
744 + "Rejecting request.",
745 + request_id);
746 + close(sock);
747 + return;
748 + }
749 +
750 + if(type == SPAWN_INSTANCE_TYPE_EXEC && !(server->options & SPAWN_SERVER_OPTION_EXEC)) {
751 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
752 + "SPAWN SERVER: Request %zu wants to exec, but exec is not allowed for this spawn server. "
753 + "Rejecting request.",
754 + request_id);
755 + close(sock);
756 + return;
757 + }
758 +
759 + if(type == SPAWN_INSTANCE_TYPE_CALLBACK && !(server->options & SPAWN_SERVER_OPTION_CALLBACK)) {
760 + nd_log(NDLS_COLLECTORS, NDLP_ERR,
761 + "SPAWN SERVER: Request %zu wants to run a callback, but callbacks are not allowed for this spawn server. "
762 + "Rejecting request.",
763 + request_id);
764 + close(sock);
765 return;
766 }
767
@@ -721,11 +771,13 @@ static void spawn_server_receive_request(int sock, SPAWN_SERVER *server) {
771 nd_log(NDLS_COLLECTORS, NDLP_ERR,
772 "SPAWN SERVER: Received invalid control message (expected %zu bytes, received %zu bytes)",
773 CMSG_LEN(sizeof(int) * SPAWN_SERVER_TRANSFER_FDS), cmsg?cmsg->cmsg_len:0);
774 + close(sock);
775 return;
776 }
777
778 if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
779 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: Received unexpected control message type.");
780 + close(sock);
781 return;
782 }
783
@@ -739,6 +791,7 @@ static void spawn_server_receive_request(int sock, SPAWN_SERVER *server) {
791 nd_log(NDLS_COLLECTORS, NDLP_ERR,
792 "SPAWN SERVER: invalid file descriptors received, stdin = %d, stdout = %d, stderr = %d",
793 stdin_fd, stdout_fd, stderr_fd);
794 + close(sock);
795 goto cleanup;
796 }
797
@@ -758,6 +811,7 @@ static void spawn_server_receive_request(int sock, SPAWN_SERVER *server) {
811 ssize_t total_bytes_received = recvmsg(sock, &msg, 0);
812 if (total_bytes_received < 0) {
813 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: failed to recvmsg() the second part of the request.");
814 + close(sock);
815 goto cleanup;
816 }
817
@@ -786,16 +840,15 @@ static void spawn_server_receive_request(int sock, SPAWN_SERVER *server) {
840 if (pid == 0) {
841 // the child
842 spawn_server_run_child(server, request);
789 - exit(1);
790 -
843 + exit(63);
844 }
845 else if (pid > 0) {
846 // the parent
794 - request->pid = pid;
795 - request->environment = NULL;
796 - request->argv = NULL;
797 - request->data = NULL;
847 + request->environment = NULL; // will be free'd at cleanup
848 + request->argv = NULL; // will be free'd at cleanup
849 + request->data = NULL; // will be free'd at cleanup
850 request->data_size = 0;
851 + request->pid = pid;
852 request->fds[0] = -1;
853 request->fds[1] = -1;
854 request->fds[2] = -1;
@@ -808,7 +861,9 @@ static void spawn_server_receive_request(int sock, SPAWN_SERVER *server) {
861 else {
862 nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN SERVER: Failed to fork() child.");
863 spawn_server_send_status_failure(stdout_fd);
811 - freez(request);
864 + // the other allocations (envp, argv, data) will be free'd at cleanup
865 + freez((void *)request);
866 + close(sock);
867 }
868
869 cleanup:
@@ -856,9 +911,10 @@ static void spawn_server_process_sigchld(void) {
911 bool send_report_remove_request = false;
912
913 if(WIFEXITED(status)) {
859 - nd_log(NDLS_COLLECTORS, NDLP_INFO,
860 - "SPAWN SERVER: child with pid %d (request %zu) exited normally with exit code %d",
861 - pid, request_id, WEXITSTATUS(status));
914 + if(WEXITSTATUS(status))
915 + nd_log(NDLS_COLLECTORS, NDLP_INFO,
916 + "SPAWN SERVER: child with pid %d (request %zu) exited normally with exit code %d",
917 + pid, request_id, WEXITSTATUS(status));
918 send_report_remove_request = true;
919 }
920 else if(WIFSIGNALED(status)) {
@@ -1079,7 +1135,7 @@ static void replace_stdio_with_dev_null() {
1135 close(dev_null_fd);
1136 }
1137
1082 -SPAWN_SERVER* spawn_server_create(const char *name, spawn_request_callback_t child_callback, int argc, const char **argv) {
1138 +SPAWN_SERVER* spawn_server_create(SPAWN_SERVER_OPTIONS options, const char *name, spawn_request_callback_t child_callback, int argc, const char **argv) {
1139 SPAWN_SERVER *server = callocz(1, sizeof(SPAWN_SERVER));
1140 server->pipe[0] = -1;
1141 server->pipe[1] = -1;
@@ -1087,9 +1143,9 @@ SPAWN_SERVER* spawn_server_create(const char *name, spawn_request_callback_t chi
1143 server->cb = child_callback;
1144 server->argc = argc;
1145 server->argv = argv;
1090 - server->argv0_size = (argv && argv[0]) ? strlen(argv[0]) : 0;
1091 -
1146 + server->options = options;
1147 server->id = __atomic_add_fetch(&spawn_server_id, 1, __ATOMIC_RELAXED);
1148 + os_uuid_generate_random(server->magic.uuid);
1149
1150 char *runtime_directory = getenv("NETDATA_CACHE_DIR");
1151 if(runtime_directory && !*runtime_directory) runtime_directory = NULL;
@@ -1268,7 +1324,7 @@ SPAWN_INSTANCE* spawn_server_exec(SPAWN_SERVER *server, int stderr_fd, int custo
1324 .type = type
1325 };
1326
1271 - if(!spawn_server_send_request(&request))
1327 + if(!spawn_server_send_request(&server->magic, &request))
1328 goto cleanup;
1329
1330 close(pipe_stdin[0]); pipe_stdin[0] = -1;
src/libnetdata/spawn_server/spawn_server.h
+9 -2
@@ -5,13 +5,20 @@
5
6 #define SPAWN_SERVER_TRANSFER_FDS 4
7
8 -typedef enum {
8 +typedef enum __attribute__((packed)) {
9 SPAWN_INSTANCE_TYPE_EXEC = 0,
10 #if !defined(OS_WINDOWS)
11 SPAWN_INSTANCE_TYPE_CALLBACK = 1
12 #endif
13 } SPAWN_INSTANCE_TYPE;
14
15 +typedef enum __attribute__((packed)) {
16 + SPAWN_SERVER_OPTION_EXEC = (1 << 0),
17 +#if !defined(OS_WINDOWS)
18 + SPAWN_SERVER_OPTION_CALLBACK = (1 << 1),
19 +#endif
20 +} SPAWN_SERVER_OPTIONS;
21 +
22 // this is only used publicly for SPAWN_INSTANCE_TYPE_CALLBACK
23 // which is not available in Windows
24 typedef struct spawn_request {
@@ -32,7 +39,7 @@ typedef void (*spawn_request_callback_t)(SPAWN_REQUEST *request);
39 typedef struct spawm_instance SPAWN_INSTANCE;
40 typedef struct spawn_server SPAWN_SERVER;
41
35 -SPAWN_SERVER* spawn_server_create(const char *name, spawn_request_callback_t child_callback, int argc, const char **argv);
42 +SPAWN_SERVER* spawn_server_create(SPAWN_SERVER_OPTIONS options, const char *name, spawn_request_callback_t child_callback, int argc, const char **argv);
43 void spawn_server_destroy(SPAWN_SERVER *server);
44
45 SPAWN_INSTANCE* spawn_server_exec(SPAWN_SERVER *server, int stderr_fd, int custom_fd, const char **argv, const void *data, size_t data_size, SPAWN_INSTANCE_TYPE type);