waffle

user and group backend daemon
git clone git://git.2f30.org/waffle
Log | Files | Refs | LICENSE

commit 4aec1a1705a634555089db8b948f4a9ba8eda9ac
Author: sin <sin@2f30.org>
Date:   Sat,  7 Mar 2015 17:06:57 +0000

Initial commit

Diffstat:
ALICENSE | 13+++++++++++++
AMakefile | 35+++++++++++++++++++++++++++++++++++
Aarg.h | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abackend.c | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adummy.c | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Aproto.h | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Areq.c | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Awaffle.c | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Awaffle.h | 27+++++++++++++++++++++++++++
9 files changed, 592 insertions(+), 0 deletions(-)

diff --git a/LICENSE b/LICENSE @@ -0,0 +1,13 @@ +© 2015 Dimitris Papastamos <sin@2f30.org> + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile @@ -0,0 +1,35 @@ +VERSION = 0.0 + +PREFIX = /usr/local + +CPPFLAGS = -D_DEFAULT_SOURCE +CFLAGS = -std=c99 -Wall +LDFLAGS = -static + +OBJ =\ + backend.o\ + dummy.o\ + req.o\ + waffle.o + +BIN = waffle + +all: $(BIN) + +$(BIN): $(OBJ) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJ) + +$(OBJ): arg.h proto.h waffle.h + +install: all + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/$(BIN) + +clean: + rm -f $(BIN) $(OBJ) + +.PHONY: + all install uninstall clean diff --git a/arg.h b/arg.h @@ -0,0 +1,63 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][1]\ + && argv[0][0] == '-';\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +/* Handles obsolete -NUM syntax */ +#define ARGNUM case '0':\ + case '1':\ + case '2':\ + case '3':\ + case '4':\ + case '5':\ + case '6':\ + case '7':\ + case '8':\ + case '9' + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define ARGNUMF(base) (brk_ = 1, estrtol(argv[0], (base))) + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#endif diff --git a/backend.c b/backend.c @@ -0,0 +1,60 @@ +#include <errno.h> +#include <stddef.h> +#include <stdlib.h> +#include "waffle.h" + +static struct { + char *name; + struct backend_ops *ops; +} backends[] = { + { .name = "dummy", .ops = &dummy_ops }, +}; + +int +backends_init(void) +{ + size_t i; + + for (i = 0; i < LEN(backends); i++) + if (backends[i].ops->init() < 0) + return -1; + return 0; +} + +void +backends_term(void) +{ + size_t i; + + for (i = 0; i < LEN(backends); i++) + backends[i].ops->term(); +} + +int +backends_pwbyname(const char *key, struct passwd *pw) +{ + size_t i; + + for (i = 0; i < LEN(backends); i++) + if (!backends[i].ops->pwbyname(key, pw)) + return 0; + return -1; +} + + +int +backends_pwbyuid(const char *key, struct passwd *pw) +{ + char *end; + uid_t uid; + size_t i; + + errno = 0; + uid = strtol(key, &end, 10); + if (*end || errno) + return -1; + for (i = 0; i < LEN(backends); i++) + if (!backends[i].ops->pwbyuid(uid, pw)) + return 0; + return -1; +} diff --git a/dummy.c b/dummy.c @@ -0,0 +1,50 @@ +#include <string.h> +#include "waffle.h" + +static int +dummy_init(void) +{ + return 0; +} + +static void +dummy_term(void) +{ +} + +static int +dummy_pwbyname(const char *name, struct passwd *pw) +{ + if (strcmp(name, "dummy")) + return -1; + pw->pw_name = "dummy"; + pw->pw_passwd = "passwd"; + pw->pw_uid = 1337; + pw->pw_gid = 1337; + pw->pw_gecos = "gecos"; + pw->pw_dir = "/home/dummy"; + pw->pw_shell = "/bin/sh"; + return 0; +} + +static int +dummy_pwbyuid(uid_t uid, struct passwd *pw) +{ + if (uid != 1337) + return -1; + pw->pw_name = "dummy"; + pw->pw_passwd = "passwd"; + pw->pw_uid = 1337; + pw->pw_gid = 1337; + pw->pw_gecos = "gecos"; + pw->pw_dir = "/home/dummy"; + pw->pw_shell = "/bin/sh"; + return 0; +} + +struct backend_ops dummy_ops = { + .init = dummy_init, + .term = dummy_term, + .pwbyname = dummy_pwbyname, + .pwbyuid = dummy_pwbyuid +}; diff --git a/proto.h b/proto.h @@ -0,0 +1,56 @@ +#include <stdint.h> + +enum { + NSCDVERSION = 2 +}; + +enum { + NSCDTIMEOUT = 200 /* in ms */ +}; + +enum { + NSCDMAXKEYLEN = 1024 +}; + +enum { + GETPWBYNAME = 0, + GETPWBYUID = 1, + + GETGRBYNAME = 2, + GETGRBYUID = 3, + + INITGROUPS = 15, +}; + +struct nscdreq { + int32_t version; + int type; + int32_t keylen; +}; + +struct nscdpasswd { + int32_t version; + int32_t found; + int32_t namelen; + int32_t passwdlen; + int32_t uid; + int32_t gid; + int32_t gecoslen; + int32_t dirlen; + int32_t shelllen; +}; + +struct nscdgrp { + int32_t version; + int32_t found; + int32_t namelen; + int32_t passwdlen; + int32_t gid; + int32_t memcnt; +}; + +struct nscdinitgrp { + int32_t version; + int32_t found; + int32_t ngrps; +}; diff --git a/req.c b/req.c @@ -0,0 +1,122 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "waffle.h" + +int +handle_req(int clifd) +{ + ssize_t n; + struct nscdreq req; + char key[NSCDMAXKEYLEN]; + + n = read(clifd, &req, sizeof(req)); + if (n <= 0) + return -1; + + if ((size_t)n != sizeof(req)) + return -1; + + if (req.version != NSCDVERSION) + return -1; + + if (req.keylen < 0 || req.keylen > NSCDMAXKEYLEN) + return -1; + + if (!req.keylen) { + key[0] = '\0'; + return process_req(clifd, &req, key); + } + + n = read(clifd, key, sizeof(key)); + if (n <= 0) + return -1; + + if (n != req.keylen) + return -1; + + if (key[n - 1] != '\0') + return -1; + + return process_req(clifd, &req, key); +} + +static int +replypwd(int clifd, struct passwd *pwd) +{ + struct nscdpasswd header; + char *buf; + size_t len, offset; + ssize_t n; + + header.version = NSCDVERSION; + header.found = pwd ? 1 : 0; + header.namelen = pwd ? strlen(pwd->pw_name) + 1 : 0; + header.passwdlen = pwd ? strlen(pwd->pw_passwd) + 1 : 0; + header.uid = pwd ? pwd->pw_uid : -1; + header.gid = pwd ? pwd->pw_gid : -1; + header.gecoslen = pwd ? strlen(pwd->pw_gecos) + 1 : 0; + header.dirlen = pwd ? strlen(pwd->pw_dir) + 1 : 0; + header.shelllen = pwd ? strlen(pwd->pw_shell) + 1 : 0; + + len = sizeof(header) + + header.namelen + + header.passwdlen + + header.gecoslen + + header.dirlen + + header.shelllen; + + buf = malloc(len); + if (!buf) + return -1; + + if (!pwd) { + memcpy(buf, &header, sizeof(header)); + } else { + memcpy(buf, &header, sizeof(header)); + offset = sizeof(header); + + memcpy(buf + offset, pwd->pw_name, header.namelen); + offset += header.namelen; + + memcpy(buf + offset, pwd->pw_passwd, header.passwdlen); + offset += header.passwdlen; + + memcpy(buf + offset, pwd->pw_gecos, header.gecoslen); + offset += header.gecoslen; + + memcpy(buf + offset, pwd->pw_dir, header.dirlen); + offset += header.dirlen; + + memcpy(buf + offset, pwd->pw_shell, header.shelllen); + } + + n = write(clifd, buf, len); + free(buf); + return (size_t)n != len ? -1 : 0; +} + +int +process_req(int clifd, struct nscdreq *req, char *key) +{ + struct passwd pwd; + + switch (req->type) { + case GETPWBYNAME: + if (!backends_pwbyname(key, &pwd)) + return replypwd(clifd, &pwd); + else + return replypwd(clifd, NULL); + case GETPWBYUID: + if (!backends_pwbyuid(key, &pwd)) + return replypwd(clifd, &pwd); + else + return replypwd(clifd, NULL); + } + + if (debug) + printf("Unhandled request type %ld\n", + (long)req->type); + return -1; +} diff --git a/waffle.c b/waffle.c @@ -0,0 +1,166 @@ +#include <sys/select.h> +#include <sys/signalfd.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/un.h> +#include <err.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "arg.h" +#include "waffle.h" + +#define SOCKPATH "/var/run/nscd/socket" + +int debug = 0; +char *argv0; +static fd_set master; +static int fdmax; + +static int +serv_listen(const char *name) +{ + struct sockaddr_un sun; + int fd, r; + socklen_t len; + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + err(1, "socket"); + + unlink(name); + + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_UNIX; + strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); + + len = sizeof(sun); + r = bind(fd, (struct sockaddr *)&sun, len); + if (r < 0) + err(1, "bind %s", name); + + r = listen(fd, 5); + if (r < 0) + err(1, "listen"); + + return fd; +} + +static int +serv_accept(int listenfd) +{ + struct sockaddr_un sun; + int clifd; + socklen_t len; + + len = sizeof(sun); + clifd = accept(listenfd, (struct sockaddr *)&sun, &len); + if (clifd < 0) + err(1, "accept"); + return clifd; +} + +static int +add_cli(int clifd) +{ + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = NSCDTIMEOUT * 1000; + if (setsockopt(clifd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { + close(clifd); + return -1; + } + FD_SET(clifd, &master); + if (clifd > fdmax) + fdmax = clifd; + return 0; +} + +static void +del_cli(int clifd) +{ + FD_CLR(clifd, &master); + close(clifd); +} + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-d]\n", argv0); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + sigset_t mask; + struct signalfd_siginfo si; + fd_set rfds; + int listenfd, clifd, sfd; + int i, n; + ssize_t r; + + ARGBEGIN { + case 'd': + debug = 1; + break; + default: + usage(); + } ARGEND; + + signal(SIGPIPE, SIG_IGN); + sigemptyset(&mask); + sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGINT); + sigprocmask(SIG_BLOCK, &mask, NULL); + + listenfd = serv_listen(SOCKPATH); + FD_ZERO(&master); + FD_ZERO(&rfds); + FD_SET(listenfd, &master); + fdmax = listenfd; + + sfd = signalfd(-1, &mask, 0); + if (sfd < 0) + err(1, "signalfd"); + FD_SET(sfd, &master); + if (sfd > fdmax) + fdmax = sfd; + + while (1) { + rfds = master; + n = select(fdmax + 1, &rfds, NULL, NULL, NULL); + if (n < 0) { + if (errno == EINTR) + continue; + err(1, "select"); + } + for (i = 0; i <= fdmax; i++) { + if (!FD_ISSET(i, &rfds)) + continue; + if (i == sfd) { + r = read(sfd, &si, sizeof(si)); + if (r < 0 || (size_t)r != sizeof(si)) + continue; + switch (si.ssi_signo) { + case SIGTERM: + case SIGINT: + goto out; + } + } else if (i == listenfd) { + clifd = serv_accept(listenfd); + if (add_cli(clifd) < 0) + continue; + } else { + handle_req(i); + del_cli(i); + } + } + } +out: + exit(0); +} diff --git a/waffle.h b/waffle.h @@ -0,0 +1,27 @@ +#include <sys/types.h> +#include <pwd.h> +#include "proto.h" + +#define LEN(x) (sizeof (x) / sizeof *(x)) + +struct backend_ops { + int (*init)(void); + void (*term)(void); + int (*pwbyname)(const char *name, struct passwd *pw); + int (*pwbyuid)(uid_t uid, struct passwd *pw); +}; + +/* nscd.c */ +extern int debug; +/* dummy.c */ +extern struct backend_ops dummy_ops; + +/* req.c */ +int handle_req(int); +int process_req(int, struct nscdreq *, char *); + +/* backend.c */ +int backends_init(void); +void backends_term(void); +int backends_pwbyname(const char *, struct passwd *); +int backends_pwbyuid(const char *, struct passwd *);