--- /dev/null
+#!/usr/bin/perl
+# Configure script for the sub-authentication daemon
+# Inspired by LibUCW examples
+
+use warnings;
+use strict;
+
+our($srcdir, $libdir);
+BEGIN {
+ # Find the sources
+ my $pkgfile = "subauthd.c";
+ if (!defined ($srcdir = $ENV{"SRCDIR"})) {
+ if (-f $pkgfile) {
+ $srcdir=".";
+ } elsif ($0 =~ m@^(.*)/configure$@ && -f "$1/$pkgfile") {
+ $srcdir=$1;
+ } else {
+ die "Don't know how to find myself. Please set SRCDIR manually.\n";
+ }
+ }
+ # Ask pkg-config if libucw is installed and find its configure modules
+ `pkg-config libucw --atleast-version=3.13`;
+ !$? or die "Package `libucw' (version 3.13 or newer) not found. Is PKG_CONFIG_PATH set properly?\n";
+ $libdir=`pkg-config libucw --variable=perl_modules_dir`;
+ chomp $libdir;
+ die "Unable to find the libucw configure system\n" if $? || not defined $libdir;
+}
+use lib $libdir;
+use UCW::Configure;
+
+Init($srcdir, 'default.cfg');
+Log "### Configuring subauthd ###\n\n";
+Include Get("CONFIG");
+# What should be detected?
+require UCW::Configure::Build;
+require UCW::Configure::Paths;
+require UCW::Configure::C;
+require UCW::Configure::Pkg;
+
+# Get some libraries
+UCW::Configure::Pkg::PkgConfig("libucw") or Fail("libUCW is required");
+Finish();
+
+Log "\nConfigured, run `make' to build everything.\n";
--- /dev/null
+/*
+ * Sub-authentication Client
+ *
+ * (c) 2017 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/opt.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "autoconf.h"
+
+static char *socket_path = INSTALL_RUN_DIR "/subauthd.socket";
+
+static const struct opt_section options = {
+ OPT_ITEMS {
+ OPT_HELP("A client to the sub-authentication daemon."),
+ OPT_HELP("Usage: subauth [options]"),
+ OPT_HELP(""),
+ OPT_HELP("Options:"),
+ OPT_HELP_OPTION,
+ OPT_END
+ }
+};
+
+int main(int argc UNUSED, char **argv)
+{
+ opt_parse(&options, argv+1);
+
+ int sk = socket(PF_UNIX, SOCK_SEQPACKET, 0);
+ if (sk < 0)
+ die("socket(PF_UNIX, SOCK_SEQPACKET): %m");
+
+ struct sockaddr_un sun;
+ sun.sun_family = AF_UNIX;
+ if (strlen(socket_path) >= sizeof(sun.sun_path))
+ die("Socket path too long");
+ strcpy(sun.sun_path, socket_path);
+
+ if (connect(sk, (struct sockaddr *) &sun, sizeof(sun)) < 0)
+ die("Cannot connect to %s: %m", socket_path);
+
+ char msg[] = "Brum!";
+ if (send(sk, msg, sizeof(msg), 0) < 0)
+ die("Send failed: %m");
+
+ return 0;
+}
--- /dev/null
+/*
+ * Sub-authentication Daemon
+ *
+ * (c) 2017 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/conf.h>
+#include <ucw/log.h>
+#include <ucw/mainloop.h>
+#include <ucw/opt.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "autoconf.h"
+
+static char *socket_path = "subauthd.socket";
+static uint max_connections = ~0U;
+
+static struct main_file listen_socket;
+static uint num_connections;
+
+#define SOCKET_TIMEOUT 60000 // in ms
+#define MAX_PACKET_SIZE 4096
+#define MAX_OOB_DATA_SIZE 4096
+
+struct client {
+ struct main_file socket;
+ struct main_timer timer;
+};
+
+static byte packet_buffer[MAX_PACKET_SIZE];
+static byte oob_data_buffer[MAX_OOB_DATA_SIZE];
+
+static void client_close(struct client *c)
+{
+ msg(L_INFO, "Closing connection");
+ file_del(&c->socket);
+ timer_del(&c->timer);
+ close(c->socket.fd);
+}
+
+static void socket_timeout_handler(struct main_timer *tm)
+{
+ struct client *c = tm->data;
+
+ msg(L_INFO, "Client timeout");
+
+ client_close(c);
+}
+
+static int socket_read_handler(struct main_file *fi)
+{
+ struct client *c = fi->data;
+
+ struct iovec iov = {
+ .iov_base = packet_buffer,
+ .iov_len = MAX_PACKET_SIZE,
+ };
+
+ struct msghdr mh = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = oob_data_buffer,
+ .msg_controllen = MAX_OOB_DATA_SIZE,
+ };
+
+ ssize_t len = recvmsg(fi->fd, &mh, 0);
+ if (len < 0)
+ {
+ if (errno != EAGAIN && errno != EINTR)
+ msg(L_ERROR, "Socket read: %m");
+ return HOOK_IDLE;
+ }
+
+ if (!len)
+ {
+ client_close(c);
+ return HOOK_IDLE;
+ }
+
+ msg(L_INFO, "Got packet: len=%zd", len);
+
+ struct ucred *cred = NULL;
+ for (struct cmsghdr *cm = CMSG_FIRSTHDR(&mh); cm; cm = CMSG_NXTHDR(&mh, cm))
+ {
+ if (cm->cmsg_level == SOL_SOCKET)
+ {
+ if (cm->cmsg_type == SCM_RIGHTS)
+ {
+ // We are not interested in receiving file descriptor, but despite
+ // that they could be attached to the message. If it happens, simply
+ // close them.
+ int *fdptr = (int *) CMSG_DATA(cm);
+ int nfd = cm->cmsg_len / sizeof(int);
+ for (int i=0; i<nfd; i++)
+ close(fdptr[i]);
+ }
+ else if (cm->cmsg_type == SCM_CREDENTIALS)
+ {
+ ASSERT(cm->cmsg_len >= sizeof(cred));
+ cred = (struct ucred *) CMSG_DATA(cm);
+ }
+ }
+ }
+
+ if (!cred)
+ {
+ msg(L_ERROR, "Dropping message with no credentials");
+ return HOOK_RETRY;
+ }
+
+ msg(L_INFO, "Credentials: pid=%d uid=%d gid=%d", (int) cred->pid, (int) cred->uid, (int) cred->gid);
+
+ return HOOK_RETRY;
+}
+
+static int listen_read_handler(struct main_file *fi)
+{
+ struct sockaddr_un client;
+ socklen_t addr_len = sizeof(client);
+
+ int new_sk = accept(fi->fd, &client, &addr_len);
+ if (new_sk < 0)
+ {
+ if (errno != EAGAIN && errno != EINTR)
+ msg(L_ERROR, "Socket accept: %m");
+ return HOOK_IDLE;
+ }
+
+ if (num_connections >= max_connections)
+ {
+ msg(L_WARN, "Too many connections (you might need to increase MaxConnections)");
+ close(new_sk);
+ return HOOK_IDLE;
+ }
+ num_connections++;
+
+ msg(L_INFO, "Accepted connection");
+
+ if (fcntl(new_sk, F_SETFL, fcntl(new_sk, F_GETFL) | O_NONBLOCK) < 0)
+ die("Cannot set O_NONBLOCK: %m");
+
+ struct client *c = xmalloc_zero(sizeof(*c));
+ c->socket.fd = new_sk;
+ c->socket.read_handler = socket_read_handler;
+ c->socket.data = c;
+ file_add(&c->socket);
+
+ c->timer.handler = socket_timeout_handler;
+ c->timer.data = c;
+ timer_add_rel(&c->timer, SOCKET_TIMEOUT);
+
+ return HOOK_RETRY;
+}
+
+static void init_socket(void)
+{
+ int sk = socket(PF_UNIX, SOCK_SEQPACKET, 0);
+ if (sk < 0)
+ die("socket(PF_UNIX, SOCK_SEQPACKET): %m");
+
+ if (fcntl(sk, F_SETFL, fcntl(sk, F_GETFL) | O_NONBLOCK) < 0)
+ die("Cannot set O_NONBLOCK: %m");
+
+ struct sockaddr_un sun;
+ sun.sun_family = AF_UNIX;
+ if (strlen(socket_path) >= sizeof(sun.sun_path))
+ die("SocketPath too long");
+ strcpy(sun.sun_path, socket_path);
+
+ if (unlink(socket_path) < 0 && errno != ENOENT)
+ die("Cannot unlink old socket %s: %m", socket_path);
+
+ if (bind(sk, (struct sockaddr *) &sun, sizeof(sun)) < 0)
+ die("Cannot bind to %s: %m", socket_path);
+
+ if (listen(sk, 64) < 0)
+ die("listen(): %m");
+
+ int one;
+ if (setsockopt(sk, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
+ die("setsockopt(SO_PASSCRED): %m");
+
+ listen_socket.fd = sk;
+ listen_socket.read_handler = listen_read_handler;
+ file_add(&listen_socket);
+
+ msg(L_INFO, "Listening on %s", socket_path);
+}
+
+static struct cf_section daemon_config = {
+ CF_ITEMS {
+ CF_STRING("SocketPath", &socket_path),
+ CF_UINT("MaxConnections", &max_connections),
+ CF_END
+ }
+};
+
+static const struct opt_section options = {
+ OPT_ITEMS {
+ OPT_HELP("A sub-authentication daemon."),
+ OPT_HELP("Usage: subauthd [options]"),
+ OPT_HELP(""),
+ OPT_HELP("Options:"),
+ OPT_HELP_OPTION,
+ OPT_CONF_OPTIONS,
+ OPT_END
+ }
+};
+
+int main(int argc UNUSED, char **argv)
+{
+ cf_def_file = CONFIG_DIR "/subauthd";
+ cf_declare_section("SubauthD", &daemon_config, 0);
+ opt_parse(&options, argv+1);
+
+ main_init();
+ init_socket();
+
+ main_loop();
+ return 0;
+}