commit 4aec1a1705a634555089db8b948f4a9ba8eda9ac
Author: sin <sin@2f30.org>
Date: Sat, 7 Mar 2015 17:06:57 +0000
Initial commit
Diffstat:
A | LICENSE | | | 13 | +++++++++++++ |
A | Makefile | | | 35 | +++++++++++++++++++++++++++++++++++ |
A | arg.h | | | 63 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | backend.c | | | 60 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | dummy.c | | | 50 | ++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | proto.h | | | 56 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | req.c | | | 122 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | waffle.c | | | 166 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | waffle.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 *);