Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/nss
diff options
context:
space:
mode:
Diffstat (limited to 'nss')
-rw-r--r--nss/Makefile73
-rw-r--r--nss/buf.c92
-rw-r--r--nss/buf.h2
-rw-r--r--nss/cgen.h10
-rw-r--r--nss/config/debug.h3
-rw-r--r--nss/config/shell.h2
-rw-r--r--nss/config/spool.h2
-rw-r--r--nss/gids.c33
-rw-r--r--nss/group.c56
-rw-r--r--nss/nat.c50
-rw-r--r--nss/nat.h3
-rw-r--r--nss/nss-rainbow.c478
-rw-r--r--nss/slist.c32
-rw-r--r--nss/slist.h39
-rw-r--r--nss/test_endgrent.c9
-rw-r--r--nss/test_format_buf.c78
-rw-r--r--nss/test_nat.c24
-rw-r--r--nss/test_slist.c46
-rw-r--r--nss/uids.c28
19 files changed, 1060 insertions, 0 deletions
diff --git a/nss/Makefile b/nss/Makefile
new file mode 100644
index 0000000..c4ff2ea
--- /dev/null
+++ b/nss/Makefile
@@ -0,0 +1,73 @@
+
+WARNFLAGS = \
+ -W -Wformat -Wall -Wundef -Wpointer-arith -Wcast-qual \
+ -Wcast-align -Wwrite-strings -Wsign-compare \
+ -Wmissing-noreturn \
+ -Wextra -Wstrict-aliasing=2 \
+ -Wunsafe-loop-optimizations \
+ -Wuninitialized
+
+# Compiler flags for generating dependencies
+DEPFLAGS = -MMD -MP
+CFLAGS ?= -O3
+# CFLAGS ?= -O0 -g
+# LDFLAGS ?= -g
+ALLCFLAGS = -std=gnu99 -fPIC $(WARNFLAGS) $(DEPFLAGS) $(CFLAGS)
+
+# declarations
+
+RAINBOW_OBJS = nss-rainbow.o buf.o nat.o slist.o
+UIDS_OBJS = uids.o
+GIDS_OBJS = gids.o
+TEST_SLIST_OBJS = test_slist.o slist.o
+TEST_ENDGRENT_OBJS = test_endgrent.o
+TEST_NAT_OBJS = test_nat.o nat.o
+TEST_FORMAT_BUF_OBJS = test_format_buf.o buf.o
+ALL_OBJS = $(RAINBOW_OBJS) $(UIDS_OBJS) $(GIDS_OBJS) $(TEST_SLIST_OBJS) $(TEST_NAT_OBJS)
+BINARIES = libnss_rainbow.so.2 uids gids test_slist test_nat test_endgrent test_format_buf
+
+# targets
+
+all: $(BINARIES)
+
+clean:
+ rm -f $(BINARIES) *.d *.o
+
+install:
+ install -D -m 0755 libnss_rainbow.so.2 $(LIBDIR)/libnss_rainbow.so.2
+
+# objects
+
+%.o: %.c
+ $(CC) $(ALLCFLAGS) -c -o $@ $<
+
+
+# linked binaries
+
+test_endgrent: $(TEST_ENDGRENT_OBJS)
+ $(CC) $(ALLCFLAGS) -o $@ $^
+
+test_slist: $(TEST_SLIST_OBJS)
+ $(CC) $(ALLCFLAGS) -o $@ $^
+
+test_nat: $(TEST_NAT_OBJS)
+ $(CC) $(ALLCFLAGS) -o $@ $^
+
+test_format_buf: $(TEST_FORMAT_BUF_OBJS)
+ $(CC) $(ALLCFLAGS) -o $@ $^
+
+uids: $(UIDS_OBJS)
+ $(CC) $(ALLCFLAGS) -o $@ $^
+
+gids: $(GIDS_OBJS)
+ $(CC) $(ALLCFLAGS) -o $@ $^
+
+libnss_rainbow.so.2: $(RAINBOW_OBJS)
+ $(CC) -shared $(LDFLAGS) -o $@ -Wl,-soname,$@ $^
+
+
+.PHONY: clean install
+
+-include $(ALL_OBJS:%.o=%.d)
+
+# vim: noet sts=4 ts=4 sw=4 :
diff --git a/nss/buf.c b/nss/buf.c
new file mode 100644
index 0000000..75c62f9
--- /dev/null
+++ b/nss/buf.c
@@ -0,0 +1,92 @@
+#define _GNU_SOURCE
+#include <errno.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "cgen.h"
+#include "buf.h"
+
+#include "config/debug.h"
+
+
+int write_buf(char** buf, size_t * buflen, const char* val)
+{
+ /* write_buf will not write outside of (*buf)[0..(*buflen-1)]
+ * if *buflen > strlen(val), then write_buf will ensure that *buf points to a null-terminated string.
+ * on success, write_buf will advance *buf to the next free char,
+ * set *buflen to indicate the quantity of remaining space,
+ * and return 0.
+ * on error, write_buf will set errno, return 1, and will not modify *buf or *buflen.
+ * *buf and val should not overlap.
+ */
+
+ LET(size_t copy_amt = strlen(val) + 1, copy_amt == 0,
+ "Integer overflow.", out_err_overflow);
+
+ if (*buflen < copy_amt)
+ goto out_err_range;
+
+ memcpy(*buf, val, copy_amt);
+
+ *buf += copy_amt;
+ *buflen -= copy_amt;
+ return 0;
+
+out_err_overflow:
+ errno = EOVERFLOW;
+ return 1;
+
+out_err_range:
+ errno = ERANGE;
+ return 1;
+}
+
+int format_buf(char** buf, size_t* buflen, const char* fmt, ...)
+{
+ /* format_buf will not write outside of (*buf)[0..(*buflen-1)]
+ * if *buflen > 0, format_buf will ensure that *buf points to a null-terminated string.
+ * on success, format_buf will advance *buf to the next free char,
+ * set *buflen to indicate the quantity of remaining space,
+ * and return 0.
+ * on error, format_buf will set errno, return 1, and will not modify *buf or *buflen.
+ * *buf should not overlap with fmt or any optional arguments.
+ */
+ if (*buflen < 1)
+ {
+ errno = ERANGE;
+ return 1;
+ }
+
+ va_list ap;
+ va_start(ap, fmt);
+
+ int status = vsnprintf(*buf, *buflen, fmt, ap);
+ int err = errno;
+
+ va_end(ap);
+
+ if (status < 0)
+ goto out_err;
+
+ size_t written = (size_t) status;
+
+ err = ERANGE;
+ if (written >= *buflen)
+ goto out_err;
+
+ err = EOVERFLOW;
+ LET(written = written + 1, written == 0,
+ "Integer overflow.", out_err);
+
+ *buf += written;
+ *buflen -= written;
+ return 0;
+
+out_err:
+ (*buf)[*buflen-1] = '\0';
+ errno = err;
+ return 1;
+}
diff --git a/nss/buf.h b/nss/buf.h
new file mode 100644
index 0000000..0767df1
--- /dev/null
+++ b/nss/buf.h
@@ -0,0 +1,2 @@
+int write_buf(char** buf, size_t * buflen, const char* val);
+int format_buf(char** buf, size_t* buflen, const char* fmt, ...);
diff --git a/nss/cgen.h b/nss/cgen.h
new file mode 100644
index 0000000..cd5a275
--- /dev/null
+++ b/nss/cgen.h
@@ -0,0 +1,10 @@
+#define STATIC_ASSERT(expr) extern char __static_assertion_failed [(expr) ? 1 : -1]
+#define SAVE_ERR(EXPR) {int __errno_save = errno; EXPR; errno = __errno_save;}
+#define __XSTRING(X) __STRING(X)
+/* #define PERROR(msg) {int __errno_cache = errno; if (getenv(DEBUG) != NULL) {fprintf(stderr, "%s|%d| %s: %s\n", __FILE__, __LINE__, __func__, msg); if (__errno_cache) {fprintf(stderr, "Error %d: %s\n", __errno_cache, strerror(__errno_cache));}};} */
+#define PERROR(msg)
+#define CHK(EXPR, MSG, ERR_LABEL) {if(EXPR) { PERROR(MSG); goto ERR_LABEL;}}
+#define LET(LETEXPR, CONDEXPR, MSG, ERR_LABEL) LETEXPR; if (CONDEXPR) { PERROR(MSG); goto ERR_LABEL;}
+#define TST(EXPR, TRUE, MSG, ERR_LABEL) {if (EXPR) {(TRUE); PERROR(MSG); goto ERR_LABEL;}}
+#define INIT(BUF, LEN, TYPE, INIT, MSG, ERR_LABEL) { CHK(*(LEN) < sizeof(TYPE), (MSG), ERR_LABEL); (*(TYPE*)(*(BUF))) = (INIT); (*(LEN)) -= sizeof(TYPE); (*(BUF)) += sizeof(TYPE); }
+#define COPY(BUF, LEN, TYPE, SRC, MSG, ERR_LABEL) { CHK(*(LEN) < sizeof(TYPE), (MSG), ERR_LABEL); memcpy(*(BUF), (SRC), sizeof(TYPE)); (*(LEN)) -= sizeof(TYPE); (*(BUF)) += sizeof(TYPE); }
diff --git a/nss/config/debug.h b/nss/config/debug.h
new file mode 100644
index 0000000..a4e10bf
--- /dev/null
+++ b/nss/config/debug.h
@@ -0,0 +1,3 @@
+/* Name of the environment variable whose definition will trigger nss-rainbow
+ * debug output. */
+#define DEBUG "NSS_RAINBOW_DEBUG"
diff --git a/nss/config/shell.h b/nss/config/shell.h
new file mode 100644
index 0000000..d88348b
--- /dev/null
+++ b/nss/config/shell.h
@@ -0,0 +1,2 @@
+/* Default shell for rainbow-created uids. */
+#define SHELL "/bin/bash"
diff --git a/nss/config/spool.h b/nss/config/spool.h
new file mode 100644
index 0000000..44c685e
--- /dev/null
+++ b/nss/config/spool.h
@@ -0,0 +1,2 @@
+/* Location where the rainbow user/group database is stored. */
+#define SPOOL "/var/spool/rainbow/2"
diff --git a/nss/gids.c b/nss/gids.c
new file mode 100644
index 0000000..81d7d69
--- /dev/null
+++ b/nss/gids.c
@@ -0,0 +1,33 @@
+#define _GNU_SOURCE
+#include <grp.h>
+#include <stdio.h>
+#include <string.h>
+#define BUFLEN 4096
+
+int main() {
+ struct group gr, *grp;
+ char buf[BUFLEN];
+ int i, cnt;
+
+ cnt = 0;
+
+ setgrent();
+ while (1) {
+ i = getgrent_r(&gr, buf, BUFLEN, &grp);
+ if (i) {
+ printf("status: %d\n", i);
+ break;
+ }
+ int num_members = 0;
+ char** mem_ptr = grp->gr_mem;
+ while (mem_ptr && *mem_ptr) {num_members++; mem_ptr++;}
+ printf("\ncnt %d: name %s gid (%d) : num_members %d\n", cnt, grp->gr_name, grp->gr_gid, num_members);
+ for (i = 0; i < num_members; i++)
+ printf("%s ", grp->gr_mem[i]);
+ if (num_members) printf("\n");
+ cnt++;
+ }
+ endgrent();
+ return 0;
+}
+
diff --git a/nss/group.c b/nss/group.c
new file mode 100644
index 0000000..e7a79f6
--- /dev/null
+++ b/nss/group.c
@@ -0,0 +1,56 @@
+/*
+enum nss_status
+_nss_rainbow_initgroups_dyn(const char *user, gid_t gid, long int *start, long int *size, gid_t **groupsp, long int limit, int *errnop) {
+ openlog("nss-rainbow", LOG_PID, LOG_LOCAL0);
+ PERROR("Hallo!");
+ return NSS_STATUS_SUCCESS;
+}
+
+ if (uid < 10000)
+ return NSS_STATUS_NOTFOUND;
+
+ int pathlen = 1024;
+ char pathdata[1024], *pathbuf = pathdata;
+
+
+ result->pw_dir = buf;
+ CHK(format_buf(&pathbuf, &pathlen, "/home/olpc/isolation/1/uid_to_home_dir/%d", uid) == 0,
+ "Unable to calculate home dir.", out_error_errno);
+
+ struct stat st;
+ CHK(stat(result->pw_dir, &st) == -1,
+ "Stat failed for homebuf.", out_error_errno);
+ result->pw_uid = uid;
+ result->pw_gid = st.st_gid;
+
+ (*groupsp)[*start] =
+}
+
+enum nss_status _nss_rainbow_setgrent(void) {
+ openlog("nss-rainbow", LOG_PID, LOG_LOCAL0);
+ PERROR("Hallo!");
+ return NSS_STATUS_SUCCESS; }
+enum nss_status _nss_rainbow_endgrent(void) {
+ openlog("nss-rainbow", LOG_PID, LOG_LOCAL0);
+ PERROR("Hallo!");
+ return NSS_STATUS_SUCCESS; }
+
+enum nss_status
+_nss_rainbow_getgrent_r(struct group *result, char *buf, size_t buflen, int *errnop) {
+ openlog("nss-rainbow", LOG_PID, LOG_LOCAL0);
+ PERROR("Hallo!");
+ return NSS_STATUS_NOTFOUND;
+ //Do we really need grent/grid/grnam?
+}
+
+
+enum nss_status
+_nss_rainbow_getgrgid_r(gid_t gid, struct group *gbuf, char *buf, size_t buflen, int *errnop) {
+ return NSS_STATUS_NOTFOUND;
+}
+
+enum nss_status
+_nss_rainbow_getgrnam_r(const char* name, struct group *gbuf, char *buf, size_t buflen, int *errnop) {
+ return NSS_STATUS_NOTFOUND;
+}
+*/
diff --git a/nss/nat.c b/nss/nat.c
new file mode 100644
index 0000000..dc6b648
--- /dev/null
+++ b/nss/nat.c
@@ -0,0 +1,50 @@
+#define _GNU_SOURCE
+#include <errno.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "cgen.h"
+#include "nat.h"
+
+#include "config/debug.h"
+
+int nat_add(unsigned long* result, unsigned long arg)
+{
+ unsigned long tmp = *result + arg;
+ if (tmp < arg) {
+ errno = EOVERFLOW;
+ return 1;
+ }
+ *result = tmp;
+ return 0;
+}
+
+int nat_mult(unsigned long* result, unsigned long long arg)
+{
+ unsigned long long tmp = arg * (*result);
+ if (tmp > (unsigned long)-1) {
+ errno = EOVERFLOW;
+ return 1;
+ }
+ *result = ((unsigned long) -1) & tmp;
+ return 0;
+}
+
+int parse_nat(unsigned long* result, const char* buf, size_t buflen) {
+ unsigned long total = 0;
+ for (size_t i = 0; i < buflen; i++)
+ {
+ LET(char num = buf[i] - '0', num < 0 || num > 9,
+ "Non-decimal character in string.", out_error_inval);
+ CHK(nat_mult(&total, 10) == 1, "Multiplication overflow.", out_error_errno);
+ CHK(nat_add(&total, num) == 1, "Addition overflow.", out_error_errno);
+ }
+ *result = total;
+ return 0;
+out_error_inval:
+ errno = EINVAL;
+out_error_errno:
+ return 1;
+}
diff --git a/nss/nat.h b/nss/nat.h
new file mode 100644
index 0000000..ab04a89
--- /dev/null
+++ b/nss/nat.h
@@ -0,0 +1,3 @@
+int nat_add(unsigned long* result, unsigned long arg);
+int nat_mult(unsigned long* result, unsigned long long arg);
+int parse_nat(unsigned long* result, const char* buf, size_t buflen);
diff --git a/nss/nss-rainbow.c b/nss/nss-rainbow.c
new file mode 100644
index 0000000..f3e646b
--- /dev/null
+++ b/nss/nss-rainbow.c
@@ -0,0 +1,478 @@
+#define _GNU_SOURCE
+#define _SVID_SOURCE
+#include <nss.h>
+#include <stdio.h>
+#include <pwd.h>
+#include <grp.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <dirent.h>
+
+/* code generators */
+#include "cgen.h"
+
+#include "buf.h"
+#include "nat.h"
+#include "slist.h"
+
+/* constants */
+#include "config/shell.h"
+#include "config/debug.h"
+#include "config/spool.h"
+
+static unsigned char g_uids[65536];
+static unsigned short g_uid = 0;
+
+/* I'm going to assume that every appropriately named dirent represents a user.
+ * We may need to revise this, e.g. by checking for the existence of home dir.
+ * We're also going to be evil and use the scandir() traversal to directly
+ * record allocated uids. <MS> */
+int
+uid_scanner(const struct dirent* d) {
+ unsigned long val;
+ if(parse_nat(&val, d->d_name, strlen(d->d_name)) == 1) return 0;
+ if(val < 10000 || val > 60000) return 0;
+ g_uids[val] = 1;
+ return 0;
+}
+
+/* implementation */
+enum nss_status
+_nss_rainbow_endpwent(void) {
+ return NSS_STATUS_SUCCESS;
+}
+
+enum nss_status
+_nss_rainbow_setpwent(void) {
+ for (unsigned short i = 10000; i < 60000; i++)
+ g_uids[i] = 0;
+ g_uid = 0;
+ struct dirent** ents;
+ CHK(scandir(SPOOL "/uid_pool", &ents, uid_scanner, versionsort) == -1,
+ "Unable to extract uids from $RAINBOW_SPOOL.", out_error_spool);
+ return NSS_STATUS_SUCCESS;
+out_error_spool:
+ return NSS_STATUS_UNAVAIL;
+}
+
+int check_gid_in_range(gid_t gid)
+{
+ TST(gid < 10000 || gid > 60000, errno = ENOENT,
+ "Gid outside [10000, 60000].", out_error);
+ return 0;
+out_error:
+ return 1;
+}
+
+
+int
+read_gid_for_uid(unsigned long uid, unsigned long* gid) {
+ char gid_path[NAME_MAX+1];
+ char gid_name[NAME_MAX+1];
+ size_t gid_path_len;
+ ssize_t gid_name_len;
+ char* gid_path_str;
+ unsigned long val;
+
+ gid_path_len = gid_name_len = NAME_MAX+1;
+ gid_path_str = gid_path;
+
+ CHK(format_buf(&gid_path_str, &gid_path_len, SPOOL "/uid_to_gid/%d", uid) == 1,
+ "Unable to calculate gid-path.", out_err);
+ LET(gid_name_len = readlink(gid_path, gid_name, gid_name_len), gid_name_len == -1,
+ "Unable to read gid-path.", out_err);
+ CHK(parse_nat(&val, gid_name, gid_name_len) == 1,
+ "Unable to parse gid_buf into a gid.", out_err);
+ CHK(check_gid_in_range(val) == 1,
+ "gid not in valid range.", out_err);
+
+ *gid = val;
+ return 0;
+out_err:
+ return 1;
+}
+
+enum nss_status
+_nss_rainbow_getpwent_r(struct passwd *result, char* buf, size_t buflen, int *errnop) {
+ /* Getting errno and the return code correct in this function is a pain
+ * because we need to return
+ *
+ * NSS_STATUS_TRYAGAIN + ERANGE if we run out of space in buf
+ * NSS_STATUS_NOTFOUND + ENOENT if we run out of uids
+ * NSS_STATUS_SUCCESS + _____, if we win
+ * or try another uid, if we lose
+ */
+
+begin:
+ errno = 0;
+
+ /* Locate the next reserved uid. */
+ while (true){
+ g_uid++;
+ TST(!g_uid, errno = ENOENT,
+ "No more uids to check.", out_error);
+ if (g_uids[g_uid]) break;
+ }
+
+ result->pw_uid = g_uid;
+
+ unsigned long gid;
+ CHK(read_gid_for_uid(g_uid, &gid) == 1,
+ "Unable to calculate gid for uid.", begin);
+ result->pw_gid = gid;
+
+ result->pw_dir = buf;
+ CHK(format_buf(&buf, &buflen, SPOOL "/uid_to_home_dir/%d", g_uid) == 1,
+ "Unable to calculate home dir.", maybe_error);
+
+ result->pw_gecos = result->pw_name = result->pw_passwd = buf;
+ CHK(format_buf(&buf, &buflen, "%d", g_uid) == 1,
+ "Not enough buffer for $USER.", maybe_error);
+
+ result->pw_shell = buf;
+ CHK(write_buf(&buf, &buflen, SHELL) == 1,
+ "Shell string constant too long.", maybe_error);
+
+ return NSS_STATUS_SUCCESS;
+
+maybe_error:
+ if(errno == ERANGE) { g_uid--; goto out_error; }
+ goto begin;
+
+out_error:
+ *errnop = errno;
+ if (errno == ERANGE) return NSS_STATUS_TRYAGAIN;
+ if (errno == ENOENT) return NSS_STATUS_NOTFOUND;
+ return NSS_STATUS_UNAVAIL;
+}
+
+enum nss_status _nss_rainbow_getpwuid_r(uid_t uid, struct passwd *result, char* buf, size_t buflen, int *errnop) {
+ if (uid < 10000)
+ return NSS_STATUS_NOTFOUND;
+
+ result->pw_dir = buf;
+ CHK(format_buf(&buf, &buflen, SPOOL "/uid_to_home_dir/%d", uid) == 1,
+ "Unable to calculate home dir.", out_error_errno);
+
+ struct stat st;
+ CHK(stat(result->pw_dir, &st) == -1,
+ "Stat failed for homebuf.", out_error_errno);
+ result->pw_uid = uid;
+
+ unsigned long gid;
+ CHK(read_gid_for_uid(uid, &gid) == 1,
+ "Unable to read gid for uid.", out_error_errno);
+ result->pw_gid = gid;
+
+ result->pw_gecos = result->pw_name = result->pw_passwd = buf;
+ CHK(format_buf(&buf, &buflen, "%d", uid) == 1,
+ "Unable to calculate user name.", out_error_errno);
+
+ result->pw_shell = buf;
+ CHK(write_buf(&buf, &buflen, SHELL) == 1,
+ "Shell string constant too long.", out_error_errno);
+
+ return NSS_STATUS_SUCCESS;
+
+out_error_errno:
+ *errnop = errno;
+ return NSS_STATUS_TRYAGAIN;
+}
+
+enum nss_status _nss_rainbow_getpwnam_r(const char * name, struct passwd *result, char* buf, size_t buflen, int *errnop) {
+ unsigned long val;
+ CHK(parse_nat(&val, name, strlen(name)) == 1,
+ "Unable to parse name into a uid.", out_error_name);
+
+ TST(val < 10000 || val > 60000, errno = EINVAL,
+ "Name specifies a uid outside [10000, 60000].", out_error_name);
+
+ return _nss_rainbow_getpwuid_r((uid_t)val, result, buf, buflen, errnop);
+
+out_error_name:
+ *errnop = errno;
+ return NSS_STATUS_NOTFOUND;
+}
+
+struct member_row {
+ struct slist list;
+ char* name;
+};
+struct gid_row {
+ struct slist list;
+ struct member_row *members;
+ gid_t gid;
+};
+
+struct gid_row* g_gids;
+struct gid_row* g_cur_gid;
+
+void member_row_init(struct member_row* self) {
+ self->name = NULL;
+ slist_init(&self->list);
+}
+
+void gid_row_init(struct gid_row* self) {
+ slist_init(&self->list);
+ self->members = NULL;
+ self->gid = -1;
+}
+
+void gid_row_add_member(struct gid_row* self, struct member_row* mr) {
+ slist_extend(&self->members->list, &mr->list);
+}
+
+int members_scanner(const struct dirent* d) {
+ size_t member_len = strlen(d->d_name);
+ const char* member_str = d->d_name;
+
+ CHK(member_len == 0
+ || (member_len == 1 && d->d_name[0] == '.')
+ || (member_len == 2 && d->d_name[0] == '.' && d->d_name[1] == '.'),
+ "Invalid data-gid-to-member member path.", exit);
+
+ /* g_cur_gid now points to the appropriate gid_row node.
+ * There's no way that we could already have our current (uid, gid) row.
+ * I think. :)
+ * Therefore, we'll immediately insert it. */
+ struct member_row* mr;
+
+ LET(mr = calloc(1, sizeof(struct member_row)), !mr,
+ "Unable to allocate new member_row node.", exit);
+ member_row_init(mr);
+
+ LET(mr->name = malloc(member_len+1), !mr->name,
+ "Unable to allocate new member_row->name.", exit);
+ strncpy(mr->name, member_str, member_len+1);
+
+ gid_row_add_member(g_cur_gid, mr);
+
+exit:
+ return 0;
+}
+
+int
+gid_to_members_scanner(const struct dirent* d) {
+ unsigned long gid;
+ struct dirent** ents;
+ char path[NAME_MAX+1];
+ size_t path_len = NAME_MAX + 1;
+ char* path_str = path;
+ struct gid_row* gr;
+
+
+ CHK(path_len == 0
+ || (path_len == 1 && d->d_name[0] == '.')
+ || (path_len == 2 && d->d_name[0] == '.' && d->d_name[1] == '.'),
+ "Invalid data-gid-to-member gid dir.", exit);
+
+ CHK(parse_nat(&gid, d->d_name, strlen(d->d_name)) == 1,
+ "Unable to parse d->d_name into a gid.", exit);
+ CHK(gid < 10000 || gid > 60000,
+ "gid not in valid range.", exit);
+
+ CHK(format_buf(&path_str, &path_len, SPOOL "/gid_to_members/%d", gid) == 1,
+ "Unable to calculate data-gid-to-members path.", exit);
+
+ /* We might have already allocated a gid_row for our gid.
+ * Therefore, search for it. */
+ bool find_gid(struct gid_row* iter) {
+ if (iter->gid == gid) { g_cur_gid = iter; return 0; } else return 1;
+ }
+ slist_search(struct gid_row, list, g_gids, found, not_found, find_gid);
+
+not_found:
+ LET(gr = calloc(1, sizeof(struct gid_row)), !gr,
+ "Unable to allocate new gid_row node.", exit);
+ gid_row_init(gr);
+ gr->gid = gid;
+
+ LET(gr->members = calloc(1, sizeof(struct member_row)), !gr->members,
+ "Unable to allocate new gid_row->members node.", exit);
+ member_row_init(gr->members);
+
+ slist_extend(&g_gids->list, &gr->list);
+ g_cur_gid = gr;
+
+found:
+ /* g_cur_gid now points to an appropriate gid_row for us to fill in. */
+ CHK(scandir(path, &ents, members_scanner, versionsort) == -1,
+ "Unable to scan members dir from $RAINBOW_SPOOL/gid_to_members/$GID.", exit);
+exit:
+ return 0;
+}
+
+enum nss_status _nss_rainbow_setgrent(void) {
+ struct dirent** ents;
+ LET(g_gids = calloc(1, sizeof(struct gid_row)), !g_gids,
+ "Unable to allocate new g_gids node.", out_error);
+ gid_row_init(g_gids);
+ CHK(scandir(SPOOL "/gid_to_members", &ents, gid_to_members_scanner, versionsort) == -1,
+ "Unable to extract gid->members dirs from $RAINBOW_SPOOL.", out_error);
+
+ g_cur_gid = container_of(g_gids->list.next, struct gid_row, list);
+ return NSS_STATUS_SUCCESS;
+out_error:
+ return NSS_STATUS_UNAVAIL;
+}
+
+enum nss_status _nss_rainbow_endgrent(void) {
+ void free_gid(struct gid_row* gid_iter) {
+ void free_member(struct member_row* member_iter) {
+ free(member_iter->name);
+ free(member_iter);
+ }
+ slist_for(struct member_row, list, gid_iter->members, free_member);
+ free(gid_iter);
+ }
+ slist_for(struct gid_row, list, g_gids, free_gid);
+ gid_row_init(g_gids);
+
+ return NSS_STATUS_SUCCESS;
+}
+/*
+struct group
+{
+ char *gr_name;
+ char *gr_passwd;
+ __gid_t gr_gid;
+ char **gr_mem;
+};
+*/
+int fill_member(struct member_row* member_iter, struct group* result, char** buf, size_t* buflen, size_t* member_count) {
+ result->gr_mem[*member_count] = *buf;
+ CHK(format_buf(buf, buflen, "%s", member_iter->name) == 1,
+ "Unable to write a group member.", out_error);
+ *member_count += 1;
+
+ return 0;
+out_error:
+ return 1;
+}
+
+int
+fill_group(struct group* result, char* buf, size_t buflen, struct gid_row* gid_iter) {
+ result->gr_gid = gid_iter->gid;
+
+ result->gr_name = result->gr_passwd = buf;
+ CHK(format_buf(&buf, &buflen, "%d", gid_iter->gid) == 1,
+ "Unable to calculate group name.", out_error);
+
+ /* count the number of members */
+ size_t member_count = 0;
+ void count_member(struct member_row* iter) { (void) iter; member_count++; }
+ slist_for(struct member_row, list, gid_iter->members, count_member);
+
+ /* reserve space for the member-pointers */
+ size_t member_size = (member_count+1)* sizeof(char*);
+ result->gr_mem = (char**)buf;
+ TST(buflen < member_size, errno = ERANGE,
+ "Not enough space for group member pointers.", out_error);
+ result->gr_mem[member_count] = 0;
+ buf += member_size;
+ buflen -= member_size;
+
+ /* fill in the members and swing the pointers */
+ member_count = 0;
+ slist_while(struct member_row, list, gid_iter->members, out_error,
+ fill_member, result, &buf, &buflen, &member_count);
+
+ return 0;
+
+out_error:
+ return 1;
+}
+
+enum nss_status
+_nss_rainbow_getgrent_r(struct group *result, char *buf, size_t buflen, int *errnop) {
+ if (&g_cur_gid->list == &g_gids->list)
+ return NSS_STATUS_NOTFOUND;
+
+ CHK(fill_group(result, buf, buflen, g_cur_gid) == 1,
+ "Unable to fill in group struct.", out_error_errno);
+ g_cur_gid = container_of(g_cur_gid->list.next, struct gid_row, list);
+
+ return NSS_STATUS_SUCCESS;
+
+out_error_errno:
+ *errnop = errno;
+ return NSS_STATUS_TRYAGAIN;
+}
+
+enum nss_status
+_nss_rainbow_getgrgid_r(gid_t gid, struct group *result, char *buf, size_t buflen, int *errnop) {
+ /* Getting errno and the return code exactly right is a pain.
+ * The problem is that we need
+ *
+ * NSS_STATUS_NOTFOUND + ENOENT if we can't find the result
+ * NSS_STATUS_TRYAGAIN + ERANGE if buf isn't big enough
+ * NSS_STATUS_??? + ____ if we can't setgrent()
+ * NSS_STATUS_NOTFOUND + ____ if we have bogus data
+ * NSS_SUCCESS + ____ otherwise.
+ */
+
+ enum nss_status ret, tmp;
+
+ LET(ret = _nss_rainbow_setgrent(), ret != NSS_STATUS_SUCCESS,
+ "Unable to setgrent().", out_error);
+
+ TST(check_gid_in_range(gid) == 1, ret = NSS_STATUS_NOTFOUND,
+ "Gid outside [10000, 60000].", out_error);
+
+ /* look up group data */
+ struct gid_row* gid_iter = NULL;
+ bool find_gid(struct gid_row* iter, struct gid_row** result) {
+ if (iter->gid == gid) { *result = iter; return 0; } else return 1;
+ }
+
+ slist_search(struct gid_row, list, g_gids, resume, resume,
+ find_gid, &gid_iter);
+resume:
+
+ TST(&gid_iter->list == NULL, ret = NSS_STATUS_NOTFOUND,
+ "Gid not found.", out_dealloc);
+
+ CHK(fill_group(result, buf, buflen, gid_iter) == 1,
+ "Unable to fill in group struct.", maybe_range);
+
+ ret = NSS_STATUS_SUCCESS;
+ goto out_dealloc;
+
+maybe_range:
+ ret = (errno == ERANGE) ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND;
+
+out_error:
+ *errnop = errno;
+
+out_dealloc:
+ tmp = _nss_rainbow_endgrent();
+ TST(ret == NSS_STATUS_SUCCESS && tmp != NSS_STATUS_SUCCESS, ret = tmp,
+ "Unable to deallocate internal group data.", out);
+
+out:
+ return ret;
+}
+
+enum nss_status
+_nss_rainbow_getgrnam_r(const char* name, struct group *result, char *buf, size_t buflen, int *errnop) {
+ unsigned long val;
+
+ CHK(parse_nat(&val, name, strlen(name)) == 1,
+ "Unable to parse name into a gid.", out_error);
+
+ CHK(check_gid_in_range(val) == 1,
+ "Name specifies a gid outside [10000, 60000].", out_error);
+
+ return _nss_rainbow_getgrgid_r((gid_t)val, result, buf, buflen, errnop);
+out_error:
+ *errnop = errno;
+ return NSS_STATUS_NOTFOUND;
+}
diff --git a/nss/slist.c b/nss/slist.c
new file mode 100644
index 0000000..160c17e
--- /dev/null
+++ b/nss/slist.c
@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "slist.h"
+
+void slist_init(struct slist* self) {
+ self->next = self;
+}
+
+bool slist_null(struct slist* self) {
+ return self == self->next;
+}
+
+bool slist_non_null(struct slist* self) {
+ return self != self->next;
+}
+
+void slist_extend(struct slist* self, struct slist* rhs) {
+ rhs->next = self->next;
+ self->next = rhs;
+}
+
+size_t slist_length(struct slist* self) {
+ /* XXX: Arithmetic overflow! */
+ size_t result = 0;
+ while (slist_non_null(self)) {
+ result++;
+ self = self->next;
+ }
+ return result;
+}
diff --git a/nss/slist.h b/nss/slist.h
new file mode 100644
index 0000000..cd031ee
--- /dev/null
+++ b/nss/slist.h
@@ -0,0 +1,39 @@
+
+struct slist { struct slist *next; };
+
+void slist_init(struct slist* self);
+bool slist_null(struct slist* self);
+bool slist_non_null(struct slist* self);
+size_t slist_length(struct slist* self);
+void slist_extend(struct slist* self, struct slist* rhs);
+
+#define container_of(PTR, TYPE, MEMBER) \
+ (TYPE *)((char *)(PTR) - ((ptrdiff_t) &((TYPE*)0)->MEMBER))
+
+/* FUN should return false to break iteration. */
+/* NB: Comma-swallowing paste of gnu-style named variadic macro argument. */
+#define slist_metafor(TYPE, MEMBER, PTR, BODY) { \
+ struct slist *__slist_head = &((PTR)->MEMBER); \
+ struct slist *__slist_iter = __slist_head->next; \
+ struct slist *__slist_next; \
+ while(__slist_iter != __slist_head) { \
+ __slist_next = __slist_iter->next; \
+ TYPE* __value_ptr = container_of(__slist_iter, TYPE, MEMBER); \
+ BODY; \
+ __slist_iter = __slist_next; \
+ } \
+}
+
+#define slist_search(TYPE, MEMBER, PTR, FOUND_LABEL, NOTFOUND_LABEL, FUN, \
+ COOKIE...) \
+ slist_metafor(TYPE, MEMBER, PTR, \
+ { if(!(FUN)(__value_ptr, ##COOKIE)) goto FOUND_LABEL; } \
+ ) \
+ goto NOTFOUND_LABEL;
+
+#define slist_while(TYPE, MEMBER, PTR, ERR_LABEL, FUN, COOKIE...) \
+ slist_metafor(TYPE, MEMBER, PTR, \
+ { if((FUN)(__value_ptr, ##COOKIE)) goto ERR_LABEL; }) \
+
+#define slist_for(TYPE, MEMBER, PTR, FUN, COOKIE...) \
+ slist_metafor(TYPE, MEMBER, PTR, { (FUN)(__value_ptr, ##COOKIE); })
diff --git a/nss/test_endgrent.c b/nss/test_endgrent.c
new file mode 100644
index 0000000..d5e82bc
--- /dev/null
+++ b/nss/test_endgrent.c
@@ -0,0 +1,9 @@
+#define _GNU_SOURCE
+#include <grp.h>
+
+int main() {
+ setgrent();
+ endgrent();
+ return 0;
+}
+
diff --git a/nss/test_format_buf.c b/nss/test_format_buf.c
new file mode 100644
index 0000000..40577ba
--- /dev/null
+++ b/nss/test_format_buf.c
@@ -0,0 +1,78 @@
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "cgen.h"
+#include "buf.h"
+#include "config/debug.h"
+
+int main() {
+ char init[] = "aaaa";
+ char buf[] = "aaaa";
+ char good5[] = "b\0aa";
+ char good2[] = "b\0aa";
+ char good1[] = "\0aaa";
+ char good0[] = "aaaa";
+
+ char* ptr;
+ size_t len, old_len, i;
+ int ret;
+
+ ptr = buf;
+ old_len = len = sizeof(buf);
+ ret = format_buf(&ptr, &len, "b");
+
+ assert(ret == 0 && errno == 0);
+ assert(len == old_len - 2);
+ assert(ptr == buf+2);
+ for(i = 0; i < sizeof(buf); i++)
+ assert(buf[i] == good5[i]);
+
+ for(i = 0; i < sizeof(buf); i++)
+ buf[i] = init[i];
+
+ ptr = buf;
+ old_len = len = 2;
+ ret = format_buf(&ptr, &len, "b");
+
+ assert(ret == 0 && errno == 0);
+ assert(len == old_len - 2);
+ assert(ptr == buf+2);
+ for(i = 0; i < sizeof(buf); i++)
+ assert(buf[i] == good2[i]);
+
+ for(i = 0; i < sizeof(buf); i++)
+ buf[i] = init[i];
+
+ ptr = buf;
+ old_len = len = 1;
+ ret = format_buf(&ptr, &len, "b");
+
+ assert(ret == 1 && errno == ERANGE);
+ assert(len == old_len);
+ assert(ptr == buf);
+ for(i = 0; i < sizeof(buf); i++)
+ assert(buf[i] == good1[i]);
+
+ for(i = 0; i < sizeof(buf); i++)
+ buf[i] = init[i];
+
+ ptr = buf;
+ old_len = len = 0;
+ ret = format_buf(&ptr, &len, "b");
+
+ assert(ret == 1 && errno == ERANGE);
+ assert(len == old_len);
+ assert(ptr == buf);
+ for(i = 0; i < sizeof(buf); i++)
+ assert(buf[i] == good0[i]);
+
+ for(i = 0; i < sizeof(buf); i++)
+ buf[i] = init[i];
+
+ return 0;
+}
+
diff --git a/nss/test_nat.c b/nss/test_nat.c
new file mode 100644
index 0000000..b7a01cb
--- /dev/null
+++ b/nss/test_nat.c
@@ -0,0 +1,24 @@
+#define _GNU_SOURCE
+#include <errno.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "cgen.h"
+#include "nat.h"
+
+#include "config/debug.h"
+
+int main()
+{
+ openlog("nat", LOG_PERROR, LOG_LOCAL0);
+ unsigned long data;
+ char buf[] = "12345";
+ CHK(parse_nat(&data, buf, strlen(buf)) == 1, "Death", out_death);
+ printf("%ld\n", data);
+ return 0;
+out_death:
+ printf("Death.");
+ return -1;
+}
diff --git a/nss/test_slist.c b/nss/test_slist.c
new file mode 100644
index 0000000..138adf4
--- /dev/null
+++ b/nss/test_slist.c
@@ -0,0 +1,46 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#include "slist.h"
+
+struct node {
+ int id;
+ struct slist list;
+};
+
+void node_init(struct node* self) {
+ self->id = 0;
+ slist_init(&self->list);
+}
+
+void node_print(struct node* self) {
+ printf("node %d\n", self->id);
+}
+
+int find_two(struct node* self) {
+ if (self->id == 2) return 0; else return 1;
+}
+
+int main() {
+ struct node h, a, b, c;
+ node_init(&h); a.id = 0;
+ node_init(&a); a.id = 1;
+ node_init(&b); b.id = 2;
+ node_init(&c); c.id = 3;
+ slist_extend(&h.list, &a.list);
+ slist_extend(&a.list, &b.list);
+ slist_extend(&b.list, &c.list);
+
+ slist_for(struct node, list, &h, &node_print);
+ slist_search(struct node, list, &h, found, not_found, &find_two);
+
+found:
+ printf("found two\n");
+ return 0;
+
+not_found:
+ printf("two not found\n");
+ return 1;
+}
diff --git a/nss/uids.c b/nss/uids.c
new file mode 100644
index 0000000..dbe17d0
--- /dev/null
+++ b/nss/uids.c
@@ -0,0 +1,28 @@
+#define _GNU_SOURCE
+#include <pwd.h>
+#include <stdio.h>
+#define BUFLEN 4096
+
+int main() {
+ struct passwd pw, *pwp;
+ char buf[BUFLEN];
+ int i, cnt;
+
+ cnt = 0;
+
+ setpwent();
+ while (1) {
+ i = getpwent_r(&pw, buf, BUFLEN, &pwp);
+ if (i) {
+ printf("status: %d\n", i);
+ break;
+ }
+ printf("%d: %s (%d)\tHOME %s\tSHELL %s\n", cnt,
+ pwp->pw_name, pwp->pw_uid,
+ pwp->pw_dir, pwp->pw_shell);
+ cnt++;
+ }
+ endpwent();
+ return 0;
+}
+