summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile15
-rw-r--r--arg.h41
-rw-r--r--nlplug.c220
-rw-r--r--nlsockd.c4
4 files changed, 279 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index b32c051..bbd1213 100644
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,22 @@
+CFLAGS ?= -Wall -Werror -g
+CFLAGS += -D_GNU_SOURCE
nlsockd_SRCS = nlsockd.c
nlsockd_OBJS = $(nlsockd_SRCS:%.c=%.o)
+nlplug_SRCS = nlplug.c
+nlplug_OBJS = $(nlplug_SRCS:%.c=%.o)
+
+all: nlsockd nlplug
+
nlsockd: $(nlsockd_OBJS)
- echo $^
+
+nlplug: $(nlplug_OBJS)
+
+
+nlsockd nlplug:
$(CC) -o $@ $(CFLAGS) $($@_CFLAGS) $($@_OBJS) $(LDFLAGS) $($@_LDFLAGS)
+clean:
+ rm -f nlplug nlsockd
diff --git a/arg.h b/arg.h
new file mode 100644
index 0000000..6414822
--- /dev/null
+++ b/arg.h
@@ -0,0 +1,41 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#ifndef __ARG_H__
+#define __ARG_H__
+
+extern char *argv0;
+
+#define USED(x) ((void)(x))
+
+#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\
+ argv[0] && argv[0][1]\
+ && argv[0][0] == '-';\
+ argc--, argv++) {\
+ char _argc;\
+ char **_argv;\
+ if (argv[0][1] == '-' && argv[0][2] == '\0') {\
+ argv++;\
+ argc--;\
+ break;\
+ }\
+ for (argv[0]++, _argv = argv; argv[0][0];\
+ argv[0]++) {\
+ if (_argv != argv)\
+ break;\
+ _argc = argv[0][0];\
+ switch (_argc)
+
+#define ARGEND }\
+ USED(_argc);\
+ }\
+ USED(argv);\
+ USED(argc);
+
+#define EARGF(x) ((argv[1] == NULL)? ((x), abort(), (char *)0) :\
+ (argc--, argv++, argv[0]))
+
+#endif
+
diff --git a/nlplug.c b/nlplug.c
new file mode 100644
index 0000000..ed3dbcd
--- /dev/null
+++ b/nlplug.c
@@ -0,0 +1,220 @@
+
+/*
+ * Copy me if you can.
+ * by 20h
+ *
+ * Copyright (c) 2015 Natanael Copa <ncopa@alpinelinux.org>
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+#include <linux/netlink.h>
+
+#include "arg.h"
+
+#define EVENT_TIMEOUT 2000
+
+int dodebug;
+char *argv0;
+
+#if defined(DEBUG)
+#include <stdarg.h>
+void
+dbg(char *fmt, ...)
+{
+ va_list fmtargs;
+
+ fprintf(stderr, "%s: ", argv0);
+ va_start(fmtargs, fmt);
+ vfprintf(stderr, fmt, fmtargs);
+ va_end(fmtargs);
+ fprintf(stderr, "\n");
+}
+#else
+#define dbg(...)
+#endif
+
+void
+run_child(char **argv)
+{
+ pid_t pid;
+
+ if (!(pid = fork())) {
+ dbg("running %s", runpath);
+ if (execv(argv[0], argv) < 0)
+ err(1, argv[0]);
+ exit(0);
+ }
+ if (pid < 0)
+ err(1,"fork");
+
+ waitpid(pid, NULL, 0);
+}
+
+
+void
+usage(void)
+{
+ errx(1, "usage: %s [-dku] [-f subsystem] PATH\n", argv0);
+}
+
+int main(int argc, char *argv[])
+{
+ struct pollfd fds;
+ int r;
+ char *subsystem = NULL;
+
+ int showudev, showkernel;
+
+ showudev = 1;
+ showkernel = 1;
+
+ ARGBEGIN {
+ case 'd':
+ dodebug = 1;
+ break;
+ case 'k':
+ showudev = 0;
+ break;
+ case 'u':
+ showkernel = 0;
+ break;
+ case 'f':
+ subsystem = EARGF(usage());
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ if (argv[0] == NULL)
+ usage();
+
+ fds.fd = 0; /* stdin */
+ fds.events = POLLIN;
+ while ((r = poll(&fds, 1, EVENT_TIMEOUT)) > 0) {
+ size_t len;
+ int i, slen;
+ char *key, *value;
+ struct iovec iov;
+ char cbuf[CMSG_SPACE(sizeof(struct ucred))];
+ char buf[16384];
+ struct cmsghdr *chdr;
+ struct ucred *cred;
+ struct msghdr hdr;
+ struct sockaddr_nl cnls;
+
+ clearenv();
+ setenv("PATH", "/sbin:/bin", 1);
+
+ if (!(fds.revents & POLLIN))
+ continue;
+
+ iov.iov_base = &buf;
+ iov.iov_len = sizeof(buf);
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.msg_iov = &iov;
+ hdr.msg_iovlen = 1;
+ hdr.msg_control = cbuf;
+ hdr.msg_controllen = sizeof(cbuf);
+ hdr.msg_name = &cnls;
+ hdr.msg_namelen = sizeof(cnls);
+
+ len = recvmsg(fds.fd, &hdr, 0);
+ if (len < 0) {
+ if (errno == EINTR)
+ continue;
+ err(1, "recvmsg");
+ }
+ if (len < 32 || len >= sizeof(buf))
+ continue;
+
+ chdr = CMSG_FIRSTHDR(&hdr);
+ if (chdr == NULL || chdr->cmsg_type != SCM_CREDENTIALS)
+ continue;
+
+ /*
+ * Don't allow anyone but root to send us messages.
+ *
+ * We will allow users to send us messages, when
+ * udev is enabled. Udev is just a toy you should
+ * only use for testing.
+ */
+ cred = (struct ucred *)CMSG_DATA(chdr);
+ if (cred->uid != 0 && !showudev)
+ continue;
+
+ if (!memcmp(buf, "libudev", 8)) {
+ /*
+ * Receiving messages from udev is insecure.
+ */
+ if (!showudev)
+ continue;
+ } else {
+ if (!showkernel)
+ continue;
+ /*
+ * Kernel messages shouldn't come from the
+ * userspace.
+ */
+ if (cnls.nl_pid > 0)
+ continue;
+ }
+
+ for (i = 0; i < len; i += slen + 1) {
+ key = buf + i;
+ value = strchr(key, '=');
+ slen = strlen(buf+i);
+
+ if (!slen || value == NULL)
+ continue;
+ if (subsystem && !strncmp(key, "SUBSYSTEM=", 10)
+ && !strstr(key+10, subsystem)) {
+ dbg("subsystem filter '%s' applied.",
+ subsystem);
+ break;
+ }
+
+ value[0] = '\0';
+ value++;
+
+ /*
+ * We generally trust the kernel. But there
+ * might be some udev flaw. (It's >20k sloc!)
+ */
+ if (strcmp(key, "PATH")) {
+ setenv(key, value, 1);
+ dbg("%s = \"%s\"", key, value);
+ }
+ }
+ if (getenv("ACTION") != NULL &&
+ getenv("DEVPATH") != NULL &&
+ getenv("SUBSYSTEM") != NULL &&
+ getenv("SEQNUM") != NULL) {
+ run_child(argv);
+ }
+
+ if (fds.revents & POLLHUP) {
+ dbg("parent hung up\n");
+ return 0;
+ }
+ }
+ if (r == -1)
+ err(1, "poll");
+
+ if (r == 0)
+ dbg("exit due to timeout");
+
+ return 0;
+}
+
+
diff --git a/nlsockd.c b/nlsockd.c
index ac7a920..2d82fe5 100644
--- a/nlsockd.c
+++ b/nlsockd.c
@@ -1,4 +1,8 @@
/*
+ * Copy me if you can.
+ * by 20h
+ *
+ * Copyright (c) 2015 Natanael Copa <ncopa@alpinelinux.org>
*/
#include <err.h>