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);