diff options
Diffstat (limited to 'nss')
-rw-r--r-- | nss/Makefile | 73 | ||||
-rw-r--r-- | nss/buf.c | 92 | ||||
-rw-r--r-- | nss/buf.h | 2 | ||||
-rw-r--r-- | nss/cgen.h | 10 | ||||
-rw-r--r-- | nss/config/debug.h | 3 | ||||
-rw-r--r-- | nss/config/shell.h | 2 | ||||
-rw-r--r-- | nss/config/spool.h | 2 | ||||
-rw-r--r-- | nss/gids.c | 33 | ||||
-rw-r--r-- | nss/group.c | 56 | ||||
-rw-r--r-- | nss/nat.c | 50 | ||||
-rw-r--r-- | nss/nat.h | 3 | ||||
-rw-r--r-- | nss/nss-rainbow.c | 478 | ||||
-rw-r--r-- | nss/slist.c | 32 | ||||
-rw-r--r-- | nss/slist.h | 39 | ||||
-rw-r--r-- | nss/test_endgrent.c | 9 | ||||
-rw-r--r-- | nss/test_format_buf.c | 78 | ||||
-rw-r--r-- | nss/test_nat.c | 24 | ||||
-rw-r--r-- | nss/test_slist.c | 46 | ||||
-rw-r--r-- | nss/uids.c | 28 |
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; +} + |