From d561a39c75a85d4db879b8e19711178eef251551 Mon Sep 17 00:00:00 2001 From: Vince Thyng Date: Sun, 30 Apr 2023 17:49:21 -0700 Subject: [PATCH] Add non capsicum build option This is a stepping stone to a linux port --- .gitignore | 4 + Makefile | 19 +++- README.md | 14 +++ client/netdump-client.c | 1 + netdumpd.c | 76 ++++++++++++++-- netdumpd.h | 8 ++ nocap_handler.c | 76 ++++++++++++++++ nocap_herald.c | 189 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 374 insertions(+), 13 deletions(-) create mode 100644 README.md create mode 100644 nocap_handler.c create mode 100644 nocap_herald.c diff --git a/.gitignore b/.gitignore index 858485a..633f205 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,7 @@ netdumpd.debug netdumpd.8.gz .ccls-cache + +client/netdump-client +client/netdump-client.debug +client/netdump-client.full diff --git a/Makefile b/Makefile index 39105c8..af424c0 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,25 @@ PROG= netdumpd + +.if defined(WITHOUT_CAPSICUM) + +LDADD+= -lnv -lutil +SRCS= netdumpd.c \ + nocap_handler.c \ + nocap_herald.c + +.else + +CFLAGS+= -DWITH_CASPER -DWITH_CAPSICUM +LDADD+= -lcasper -lnv -lutil SRCS= netdumpd.c \ cap_dns.c \ cap_handler.c \ cap_herald.c -MAN= netdumpd.8 -BINDIR= /usr/sbin -LDADD+= -lcasper -lnv -lutil +.endif -CFLAGS+= -DWITH_CASPER +MAN= netdumpd.8 +BINDIR= /usr/sbin WARNS?= 6 diff --git a/README.md b/README.md new file mode 100644 index 0000000..2e0efa9 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# Summary +Netdump is a daemon that accepts network core dumps + +# How to build +## With Capsicum +``` +make +``` + +## Without Capsicum +``` +# Requires FreeBSD 13 or higher +make WITHOUT_CAPSICUM=1 +``` diff --git a/client/netdump-client.c b/client/netdump-client.c index 0aca309..caccd0d 100644 --- a/client/netdump-client.c +++ b/client/netdump-client.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include diff --git a/netdumpd.c b/netdumpd.c index 6c1b80e..1b9d0dd 100644 --- a/netdumpd.c +++ b/netdumpd.c @@ -2,7 +2,7 @@ * SDPX-License-Identifier: BSD-2-Clause * * Copyright (c) 2005-2011 Sandvine Incorporated. All rights reserved. - * Copyright (c) 2016-2018 Dell EMC + * Copyright (c) 2016-2023 Dell EMC * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,12 +27,11 @@ */ #include -#include + #include #include #include #include -#include #include #include #include @@ -44,7 +43,14 @@ #include #include + +#ifdef WITH_CAPSICUM +#include +#include #include +#include "cap_dns.h" +#endif + #include #include #include @@ -62,7 +68,6 @@ #include -#include "cap_dns.h" #include "netdumpd.h" #include "kerneldump_compat.h" @@ -108,8 +113,10 @@ struct netdump_client { /* Clients list. */ static LIST_HEAD(, netdump_client) g_clients = LIST_HEAD_INITIALIZER(g_clients); +#ifdef WITH_CAPSICUM /* Capabilities. */ static cap_channel_t *g_capdns, *g_caphandler, *g_capherald; +#endif /* Program arguments handlers. */ static char g_dumpdir[MAXPATHLEN]; @@ -192,7 +199,7 @@ read_index(struct netdump_client *client, const char *dir) return (-1); } fd = openat(g_dumpdir_fd, bounds, - O_RDONLY | O_CREAT | O_CLOEXEC, 0600); + O_RDONLY | O_CREAT | O_CLOEXEC | O_RESOLVE_BENEATH, 0600); if (fd < 0) { LOGERR("openat(%s): %s\n", bounds, strerror(errno)); return (-1); @@ -244,7 +251,7 @@ write_index(struct netdump_client *client) LOGERR("Truncated bounds file path: '%s'\n", bounds); return (-1); } - fd = openat(g_dumpdir_fd, bounds, O_WRONLY | O_CLOEXEC); + fd = openat(g_dumpdir_fd, bounds, O_WRONLY | O_CLOEXEC | O_RESOLVE_BENEATH); if (fd < 0) { LOGERR("openat(%s): %s\n", bounds, strerror(errno)); return (-1); @@ -277,7 +284,7 @@ open_info_file(struct netdump_client *client, const char *dir, int idx) } fd = openat(g_dumpdir_fd, client->infofilename, - O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0600); + O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC | O_RESOLVE_BENEATH, 0600); if (fd == -1 && errno != EEXIST) LOGERR("openat(\"%s\"): %s\n", client->infofilename, strerror(errno)); @@ -330,7 +337,7 @@ open_client_files(struct netdump_client *client, const char *dir) return (-1); } fd = openat(g_dumpdir_fd, client->corefilename, - O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0600); + O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_RESOLVE_BENEATH, 0600); if (fd == -1) { error = errno; /* Failed. Keep the numbers in sync. */ @@ -370,6 +377,7 @@ alloc_client(int sd, struct sockaddr_in *saddr, char *path) client->last_msg = g_now; client->ip = saddr->sin_addr; +#ifdef WITH_CAPSICUM error = cap_getnameinfo(g_capdns, (struct sockaddr *)saddr, saddr->sin_len, client->hostname, sizeof(client->hostname), NULL, 0, NI_NAMEREQD); @@ -388,6 +396,26 @@ alloc_client(int sd, struct sockaddr_in *saddr, char *path) if (firstdot) *firstdot = '\0'; } +#else + error = getnameinfo((struct sockaddr *)saddr, + saddr->sin_len, client->hostname, sizeof(client->hostname), + NULL, 0, NI_NAMEREQD); + if (error != 0) { + /* Can't resolve, try with a numeric IP. */ + error = getnameinfo((struct sockaddr *)saddr, + saddr->sin_len, client->hostname, sizeof(client->hostname), + NULL, 0, 0); + if (error != 0) { + LOGERR("getnameinfo(): %s\n", gai_strerror(error)); + goto error_out; + } + } else { + /* Strip off the domain name. */ + firstdot = strchr(client->hostname, '.'); + if (firstdot) + *firstdot = '\0'; + } +#endif /* It should be enough to hold approximatively twice the chunk size. */ bufsz = 128 * 1024; @@ -474,12 +502,24 @@ exec_handler(struct netdump_client *client, const char *reason) struct kevent ev; int pd; +#ifdef WITH_CAPSICUM if (g_caphandler == NULL) return; pd = netdump_cap_handler(g_caphandler, reason, client_ntoa(client), client->hostname, client->infofilename, client->corefilename); if (pd < 0) LOGERR("netdump_cap_handler(): %m"); + +#else + if(g_handler_script == NULL) + return; + pd = netdump_handler(g_handler_script, reason, client_ntoa(client), + client->hostname, client->infofilename, client->corefilename); + if (pd < 0) + LOGERR("netdump_handler(): %m"); + +#endif + EV_SET(&ev, pd, EVFILT_PROCDESC, EV_ADD, NOTE_EXIT, 0, NULL); if (kevent(g_kq, &ev, 1, NULL, 0, NULL) != 0) { LOGERR_PERROR("kevent(procdesc)"); @@ -721,7 +761,7 @@ handle_ekcd_key(struct netdump_client *client, struct netdump_pkt *pkt) } fd = openat(g_dumpdir_fd, keyfile, - O_WRONLY | O_CREAT | O_CLOEXEC, 0400); + O_WRONLY | O_CREAT | O_CLOEXEC | O_RESOLVE_BENEATH, 0400); if (fd < 0) { LOGERR_PERROR("openat()"); return; @@ -841,12 +881,21 @@ server_event(void) uint32_t seqno; int error, sd; +#ifdef WITH_CAPSICUM error = netdump_cap_herald(g_capherald, &sd, &saddr, &seqno, &path); if (error != 0) { LOGERR("netdump_cap_herald(): %s\n", strerror(error)); return; } +#else + error = netdump_herald(g_sock, &sd, &saddr, &seqno, &path); + if (error != 0) { + LOGERR("netdump_herald(): %s\n", strerror(error)); + return; + } + +#endif LIST_FOREACH(client, &g_clients, iter) { if (client->ip.s_addr == saddr.sin_addr.s_addr) break; @@ -1023,6 +1072,7 @@ get_script_option(void) return (script); } +#ifdef WITH_CAPSICUM /* * Enter capability mode. This effectively sandboxes netdumpd by restricting * its ability to acquire new rights. In particular, capability mode enforces @@ -1116,6 +1166,7 @@ init_cap_mode(void) cap_close(capcasper); return (error); } +#endif static int init_kqueue(void) @@ -1181,6 +1232,7 @@ init_server_socket(void) bindaddr.sin_family = AF_INET; bindaddr.sin_addr.s_addr = g_bindip.s_addr; bindaddr.sin_port = htons(NETDUMP_PORT); + if (bind(g_sock, (struct sockaddr *)&bindaddr, sizeof(bindaddr))) { LOGERR_PERROR("bind()"); return (1); @@ -1303,9 +1355,13 @@ main(int argc, char **argv) goto cleanup; if (init_kqueue()) goto cleanup; + +#ifdef WITH_CAPSICUM if (init_cap_mode()) goto cleanup; +#endif + // Loop here until service is stopped exit_code = eventloop(); cleanup: @@ -1315,9 +1371,11 @@ main(int argc, char **argv) free(g_handler_script); if (g_sock != -1) close(g_sock); +#ifdef WITH_CAPSICUM if (g_capherald != NULL) cap_close(g_capherald); if (g_capdns != NULL) cap_close(g_capdns); +#endif return (exit_code); } diff --git a/netdumpd.h b/netdumpd.h index 2ef386d..d722696 100644 --- a/netdumpd.h +++ b/netdumpd.h @@ -33,11 +33,19 @@ struct cap_channel; struct sockaddr_in; +#ifdef WITH_CAPSICUM int netdump_cap_handler(struct cap_channel *, const char *, const char *, const char *, const char *, const char *); int netdump_cap_herald(struct cap_channel *, int *, struct sockaddr_in *, uint32_t *, char **); +#else +int netdump_handler(const char *, const char *, const char *, + const char *, const char *, const char *); +int netdump_herald(int g_sock, int *, struct sockaddr_in *, + uint32_t *, char **); +#endif + #define NETDUMP_DATASIZE 4096 #define NETDUMP_PORT 20023 #define NETDUMP_ACKPORT 20024 diff --git a/nocap_handler.c b/nocap_handler.c new file mode 100644 index 0000000..a88ef05 --- /dev/null +++ b/nocap_handler.c @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 2023 Dell EMC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "netdumpd.h" + +/* + * The handler capability lets us invoke a script upon completion (successful or + * otherwise) of a netdump. The script is executed with cwd set to the dumpdir. + */ + +int +netdump_handler(const char *script, const char *reason, const char *ip, + const char *hostname, const char *infofile, const char *corefile) +{ + int pd; + const char *argv[7]; + pid_t pid; + + // Starting with linux unfriendly code (pdfork) + // ?? errno? + if ((pid = pdfork(&pd, PD_CLOEXEC)) < 0) + return (errno); + if (pid == 0) { + argv[0] = script; + argv[1] = reason; + argv[2] = ip; + argv[3] = hostname; + argv[4] = infofile; + argv[5] = corefile; + argv[6] = NULL; + (void)execve(script, __DECONST(char *const *, argv), NULL); + _exit(1); + } + + // ?? is this correct? + // if (pd != -1) { + // (void)close(pd); + // pd = -1; + // } + // return (pd); + return pd; +} diff --git a/nocap_herald.c b/nocap_herald.c new file mode 100644 index 0000000..090114e --- /dev/null +++ b/nocap_herald.c @@ -0,0 +1,189 @@ +/*- + * Copyright (c) 2023 Dell EMC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "netdumpd.h" + +/* + * The herald capability allows netdumpd to process netdump herald messages. + * Upon receipt of such a message, netdumpd needs to reply using a socket bound + * to the destination address of the message and an ephemeral port. + * + * The netdump_herald reads a herald message from the pre-defined server socket. + * If the message is valid, the service will create, bind, and connect a socket + * with which to continue the transfer, and will the send the socket and some + * other client parameters to netdumpd. + */ + +int +netdump_herald(int g_sock, int *nsd, struct sockaddr_in *sinp, + uint32_t *seqno, char **pathp) +{ + int error; + + struct { + struct netdump_msg_hdr hdr; + char data[MAXPATHLEN]; + } ndmsg; + struct iovec iov; + struct msghdr msg; + struct sockaddr_storage ss; + struct sockaddr_in sin, *from; + struct cmsghdr *cmh; + struct in_addr *dip; + size_t cmsgsz, pathsz; + ssize_t len; + + error = 0; + *nsd = -1; + + // zero out all the things + memset(&msg, 0, sizeof(msg)); + memset(&ss, 0, sizeof(ss)); + memset(&ndmsg, 0, sizeof(ndmsg)); + + // Set io vector to the netdump message buffer + iov.iov_base = &ndmsg; + iov.iov_len = sizeof(ndmsg); + + // Track the socket and message together in msg + msg.msg_name = &ss; + msg.msg_namelen = sizeof(ss); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + // Set the size in bytes for control messages and track size in cmsgcz + cmsgsz = CMSG_SPACE(sizeof(struct in_addr)); + + // Allocate memory for the control messages and store size + msg.msg_control = calloc(1, cmsgsz); + if (msg.msg_control == NULL) { + error = errno; + goto out; + } + msg.msg_controllen = cmsgsz; + + // Read message from server socket in to msg, should include path + len = recvmsg(g_sock, &msg, 0); + if (len < 0) { + error = errno; + goto out; + } + + // Confirm we got the entire message header + if ((size_t)len < sizeof(struct netdump_msg_hdr)) { + error = EINVAL; + goto out; + } + + // Convert netdump header to standard header + ndtoh(&ndmsg.hdr); + + // Error if message header type != HERALD + // or original netdump header msg length - struct's length + // or socket protocol != AF_INET + if (ndmsg.hdr.mh_type != NETDUMP_HERALD || + (size_t)len - sizeof(struct netdump_msg_hdr) != ndmsg.hdr.mh_len || + ss.ss_family != AF_INET) { + error = EINVAL; + goto out; + } + + // Read control message header + cmh = CMSG_FIRSTHDR(&msg); + if (cmh->cmsg_level != IPPROTO_IP || cmh->cmsg_type != IP_RECVDSTADDR) { + error = EINVAL; + goto out; + } + + // This is the IP receiving the HERALD msg + // Useful when netdump server is multi homed + dip = (struct in_addr *)(void *)CMSG_DATA(msg.msg_control); + + // Setup new client socket + *nsd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, + IPPROTO_UDP); + if (*nsd < 0) { + error = errno; + goto out; + } + + // Setup sockaddr_in for new listener on server + // Zero out socket in, copy contents from msg, set dip IP + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = dip->s_addr; + sin.sin_port = htons(0); // pick random port + + if (bind(*nsd, (struct sockaddr *)&sin, sin.sin_len) != 0) { + error = errno; + goto out; + } + + // Resuse sockaddr in msg for the ACK message + from = (struct sockaddr_in *)msg.msg_name; + from->sin_port = htons(NETDUMP_ACKPORT); + if (connect(*nsd, (struct sockaddr *)from, from->sin_len) != 0) { + error = errno; + goto out; + } + + // Setup sockaddr to return for alloc_client later + memset(sinp, 0, sizeof(*sinp)); + memcpy(sinp, msg.msg_name, msg.msg_namelen); + + // Get the file path from the control message + pathsz = ndmsg.hdr.mh_len; + + *pathp = NULL; + if (pathsz > 0 && pathsz <= MIN(MAXPATHLEN, NETDUMP_DATASIZE) && + ndmsg.data[pathsz - 1] == '\0') + *pathp = strdup(ndmsg.data); + + *seqno = ndmsg.hdr.mh_seqno; + +out: + // ?? Is this all I need to clean up? + if (msg.msg_control != NULL) + free(msg.msg_control); + if (error != 0 && *nsd >= 0) + (void)close(*nsd); + return (error); +}