diff --git a/.gitignore b/.gitignore index 8638111..7e915b1 100644 --- a/.gitignore +++ b/.gitignore @@ -28,5 +28,5 @@ debian/debhelper-build-stamp /tmpconfig.yml /.ghi.yml /libnss_cache.so -/gen_getent.c.gcov +/*.c.gcov /build/ diff --git a/Makefile b/Makefile index 1743eb5..2325186 100644 --- a/Makefile +++ b/Makefile @@ -13,8 +13,16 @@ LIBNSSCACHE = nss_cache.o compat/getpwent_r.o compat/getgrent_r.o SOURCES = Makefile gen_getent.c lookup.c nss_cache.c nss_cache.h nss_test.h COPYING version libnss-cache.spec VERSION = $(shell git describe --tags --always | sed -E 's@.*/([^-]*).*@\1@') +GETENT_DATA_TOUCH = $(TESTDATA)/.touch.getent_data +LOOKUP_DATA_TOUCH = $(TESTDATA)/.touch.lookup_data +INDEX_DATA_TOUCH = $(TESTDATA)/.touch.index_data + +.PHONY: all all: $(LIBRARY) +.PHONY: test +test: test_getent time_lookups + .PHONY: check check: CFLAGS += -O0 -g --coverage check: LDFLAGS += --coverage @@ -23,41 +31,43 @@ check: test_getent time_lookups lookup: lookup.o $(LIBNSSCACHE) .PHONY: time_lookups -time_lookups: $(TESTDATA) lookup_data lookup +time_lookups: lookup_data index_data lookup @echo Linear username lookups rm -f $(TESTDATA)/passwd.cache.ixname time -f %E ./lookup -c getpwnam -f $(TESTDATA)/rand_pwnames @echo Binary username lookups - ./scripts/index.sh $(TESTDATA)/passwd.cache 1 $(TESTDATA)/passwd.cache.ixname + ln -sf passwd_cache_ixname_disabled $(TESTDATA)/passwd.cache.ixname time -f %E ./lookup -c getpwnam -f $(TESTDATA)/rand_pwnames - @echo Linear UID lookups rm -f $(TESTDATA)/passwd.cache.ixuid time -f %E ./lookup -c getpwuid -f $(TESTDATA)/rand_pwuids @echo Binary UID lookups - ./scripts/index.sh $(TESTDATA)/passwd.cache 3 $(TESTDATA)/passwd.cache.ixuid + ln -sf passwd_cache_ixuid_disabled $(TESTDATA)/passwd.cache.ixuid time -f %E ./lookup -c getpwuid -f $(TESTDATA)/rand_pwuids - @echo Linear groupname lookups rm -f $(TESTDATA)/group.cache.ixname time -f %E ./lookup -c getgrnam -f $(TESTDATA)/rand_grnames @echo Binary groupname lookups - ./scripts/index.sh $(TESTDATA)/group.cache 1 $(TESTDATA)/group.cache.ixname + ln -sf group_cache_ixname_disabled $(TESTDATA)/group.cache.ixname time -f %E ./lookup -c getgrnam -f $(TESTDATA)/rand_grnames - @echo Linear GID lookups rm -f $(TESTDATA)/group.cache.ixgid time -f %E ./lookup -c getgrgid -f $(TESTDATA)/rand_grgids @echo Binary GID lookups - ./scripts/index.sh $(TESTDATA)/group.cache 3 $(TESTDATA)/group.cache.ixgid + ln -sf group_cache_ixuid_disabled $(TESTDATA)/group.cache.ixuid time -f %E ./lookup -c getgrgid -f $(TESTDATA)/rand_grgids - @echo Linear shadow lookups rm -f $(TESTDATA)/shadow.cache.ixname time -f %E ./lookup -c getspnam -f $(TESTDATA)/rand_spnames @echo Binary shadow lookups - ./scripts/index.sh $(TESTDATA)/shadow.cache 1 $(TESTDATA)/shadow.cache.ixname + ln -sf shadow_cache_ixname_disabled $(TESTDATA)/shadow.cache.ixname time -f %E ./lookup -c getspnam -f $(TESTDATA)/rand_spnames + @echo Linear gshadow lookups + rm -f $(TESTDATA)/gshadow.cache.ixname + time -f %E ./lookup -c getsgnam -f $(TESTDATA)/rand_sgnames + @echo Binary gshadow lookups + ln -sf gshadow_cache_ixname_disabled $(TESTDATA)/gshadow.cache.ixname + time -f %E ./lookup -c getsgnam -f $(TESTDATA)/rand_sgnames gcov --all-blocks --branch-probabilities --branch-counts --function-summaries --unconditional-branches ./lookup gen_getent: gen_getent.o $(LIBNSSCACHE) @@ -69,10 +79,25 @@ test_getent: getent_data gen_getent nss_cache.c diff $(TESTDATA)/passwd.cache $(TESTDATA)/passwd.cache.out diff $(TESTDATA)/group.cache $(TESTDATA)/group.cache.out diff $(TESTDATA)/shadow.cache $(TESTDATA)/shadow.cache.out + diff $(TESTDATA)/gshadow.cache $(TESTDATA)/gshadow.cache.out gcov --all-blocks --branch-probabilities --branch-counts --function-summaries --unconditional-branches ./gen_getent + +.PHONY: index_data +index_data: $(INDEX_DATA_TOUCH) + +$(INDEX_DATA_TOUCH): $(LOOKUP_DATA_TOUCH) + ./scripts/index.sh $(TESTDATA)/passwd.cache 1 $(TESTDATA)/passwd_cache_ixname_disabled + ./scripts/index.sh $(TESTDATA)/passwd.cache 3 $(TESTDATA)/passwd_cache_ixuid_disabled + ./scripts/index.sh $(TESTDATA)/group.cache 1 $(TESTDATA)/group_cache_ixname_disabled + ./scripts/index.sh $(TESTDATA)/group.cache 3 $(TESTDATA)/group_cache_ixgid_disabled + ./scripts/index.sh $(TESTDATA)/shadow.cache 1 $(TESTDATA)/shadow_cache_ixname_disabled + ./scripts/index.sh $(TESTDATA)/gshadow.cache 1 $(TESTDATA)/gshadow_cache_ixname_disabled + .PHONY: lookup_data -lookup_data: getent_data +lookup_data: $(LOOKUP_DATA_TOUCH) + +$(LOOKUP_DATA_TOUCH): $(GETENT_DATA_TOUCH) cut -d : -f 1 $(TESTDATA)/passwd.cache |\ sort -R | head -500 > $(TESTDATA)/rand_pwnames cut -d : -f 3 $(TESTDATA)/passwd.cache |\ @@ -83,16 +108,20 @@ lookup_data: getent_data sort -R | head -500 > $(TESTDATA)/rand_grgids cut -d : -f 1 $(TESTDATA)/shadow.cache |\ sort -R | head -500 > $(TESTDATA)/rand_spnames + cut -d : -f 1 $(TESTDATA)/gshadow.cache |\ + sort -R | head -500 > $(TESTDATA)/rand_sgnames + touch $@ .PHONY: getent_data -getent_data: $(TESTDATA) +getent_data: $(GETENT_DATA_TOUCH) + +$(GETENT_DATA_TOUCH): scripts/gentestdata.sh + mkdir -p $(TESTDATA) ./scripts/gentestdata.sh $(TESTDATA) + touch $@ last_pw_errno_test: test/last_pw_errno_test.c -$(TESTDATA): - mkdir -p $(TESTDATA) - $(LIBRARY): LDFLAGS += -shared $(LD_SONAME) $(LIBRARY): $(LIBNSSCACHE) $(CC) $(CFLAGS) $(LDFLAGS) -o $(LIBRARY) $+ @@ -110,7 +139,7 @@ install: $(SONAME) .PHONY: clean clean: - rm -f $(LIBRARY)* *.o compat/*.o *.gcda *.gcno compat/*.gcda compat/*.gcno lookup gen_getent last_pw_errno_test + rm -f $(LIBRARY)* *.o compat/*.o *.gcov *.gcda *.gcno compat/*.gcda compat/*.gcno lookup gen_getent last_pw_errno_test .PHONY: veryclean veryclean: clean diff --git a/gen_getent.c b/gen_getent.c index b67b7b2..3fe1869 100644 --- a/gen_getent.c +++ b/gen_getent.c @@ -86,12 +86,12 @@ static int getgrent_to_file(FILE *output) { result.gr_gid); // unroll **gr_mem for (idx = 0; result.gr_mem[idx] != NULL; idx++) { - fprintf(output, "%s", result.gr_mem[idx]); - if (result.gr_mem[idx + 1] != NULL) { - fprintf(output, ","); + if (idx != 0) { + fputs(",", output); } + fputs(result.gr_mem[idx], output); } - fprintf(output, "\n"); + fputs("\n", output); } if (ret == NSS_STATUS_TRYAGAIN) { buflen = buflen * 2; @@ -134,37 +134,37 @@ static int getspent_to_file(FILE *output) { if (result.sp_lstchg != -1) { fprintf(output, "%ld:", result.sp_lstchg); } else { - fprintf(output, ":"); + fputs(":", output); } if (result.sp_min != -1) { fprintf(output, "%ld:", result.sp_min); } else { - fprintf(output, ":"); + fputs(":", output); } if (result.sp_max != -1) { fprintf(output, "%ld:", result.sp_max); } else { - fprintf(output, ":"); + fputs(":", output); } if (result.sp_warn != -1) { fprintf(output, "%ld:", result.sp_warn); } else { - fprintf(output, ":"); + fputs(":", output); } if (result.sp_inact != -1) { fprintf(output, "%ld:", result.sp_inact); } else { - fprintf(output, ":"); + fputs(":", output); } if (result.sp_expire != -1) { fprintf(output, "%ld:", result.sp_expire); } else { - fprintf(output, ":"); + fputs(":", output); } if (result.sp_flag != -1) { fprintf(output, "%ld", result.sp_flag); } - fprintf(output, "\n"); + fputs("\n", output); } if (ret == NSS_STATUS_TRYAGAIN) { buflen = buflen * 2; @@ -181,6 +181,59 @@ static int getspent_to_file(FILE *output) { return 0; } + +// getsgent_to_file() +// Call the nss_cache getsgent function to dump the shadow store to a +// file. + +static int getsgent_to_file(FILE *output) { + struct sgrp result; + char *buffer; + size_t buflen = 1024; + int errnop; + enum nss_status ret; + int idx; + + _nss_cache_setsgent_path(GSHADOW_FILE); + + buffer = malloc(buflen); + + do { + ret = _nss_cache_getsgent_r(&result, buffer, buflen, &errnop); + if (ret == NSS_STATUS_SUCCESS) { + fprintf(output, "%s:%s:", result.sg_namp, result.sg_passwd); + // unroll **sg_adm + for (idx = 0; result.sg_adm[idx] != NULL; idx++) { + if (idx != 0) { + fputs(",", output); + } + fputs(result.sg_adm[idx], output); + } + fputs(":", output); + // unroll **sg_mem + for (idx = 0; result.sg_mem[idx] != NULL; idx++) { + if (idx != 0) { + fputs(",", output); + } + fputs(result.sg_mem[idx], output); + } + fputs("\n", output); + } + if (ret == NSS_STATUS_TRYAGAIN) { + buflen = buflen * 2; + buffer = realloc(buffer, buflen); + } + if (ret == NSS_STATUS_UNAVAIL) { + perror("ERROR: failed to access gshadow test data"); + free(buffer); + return 1; + } + } while (ret == NSS_STATUS_SUCCESS || ret == NSS_STATUS_TRYAGAIN); + + free(buffer); + + return 0; +} #endif // ifndef BSD // gen_getpwent_data() @@ -258,6 +311,31 @@ static int gen_getspent_data(void) { return ret; } + +// gen_getsgent_data() +// +// creates a copy of the shadow map as read by nss_cache.c + +static int gen_getsgent_data(void) { + char filename[NSS_CACHE_PATH_LENGTH]; + FILE *output; + int ret; + + strncpy(filename, GSHADOW_FILE, NSS_CACHE_PATH_LENGTH - 4); + int n = strlen(filename); + strncat(filename, ".out", NSS_CACHE_PATH_LENGTH - n - 1); + output = fopen(filename, "w"); + + if (output == NULL) { + fprintf(stderr, "failed to open %s!\n", filename); + return 255; + } + + ret = getsgent_to_file(output); + fclose(output); + + return ret; +} #endif // ifndef BSD // main() @@ -277,14 +355,20 @@ int main(void) { ret = gen_getgrent_data(); if (ret != 0) { - fprintf(stderr, "Failed to generate password file.\n"); + fprintf(stderr, "Failed to generate group file.\n"); failed_tests = failed_tests + 1; } #ifndef BSD ret = gen_getspent_data(); if (ret != 0) { - fprintf(stderr, "Failed to generate password file.\n"); + fprintf(stderr, "Failed to generate shadow file.\n"); + failed_tests = failed_tests + 1; + } + + ret = gen_getsgent_data(); + if (ret != 0) { + fprintf(stderr, "Failed to generate gshadow file.\n"); failed_tests = failed_tests + 1; } #endif diff --git a/lookup.c b/lookup.c index 09f1377..c122cfa 100644 --- a/lookup.c +++ b/lookup.c @@ -243,6 +243,46 @@ static int getspnam_wrapper(char *name) { return found; } + +// getsgnam_wrapper() +// +// perform a getsgnam() lookup via nss_cache.c directly + +static int getsgnam_wrapper(char *name) { + struct sgrp result; + char *buffer = NULL; + size_t buflen = 1024; + int found = 255; + int errnop; + int ret; + + _nss_cache_setsgent_path(GSHADOW_FILE); + + buffer = malloc(buflen); + + do { + ret = _nss_cache_getsgnam_r(name, &result, buffer, buflen, &errnop); + if (ret == NSS_STATUS_SUCCESS) { + // printf("found %s, %s\n", result.sg_namp, result.sg_pwdp); + found = 0; + } + if (ret == NSS_STATUS_NOTFOUND) { + found = 1; + } + if (ret == NSS_STATUS_TRYAGAIN) { + buflen = buflen * 2; + buffer = realloc(buffer, buflen); + } + if (ret == NSS_STATUS_UNAVAIL) { + fprintf(stderr, "ERROR: failed to access passwd test data\n"); + return 2; + } + } while (ret == NSS_STATUS_TRYAGAIN); + + free(buffer); + + return found; +} #endif // ifndef BSD // lookup_getpwnam() @@ -395,6 +435,36 @@ static int lookup_getspnam(FILE *input) { return ret; } + +// lookup_getsgnam() +// +// call getsgnam() from nss_cache.c on each line of a file + +static int lookup_getsgnam(FILE *input) { + int lines = 0; + char line[255]; + int ret; + + while (fgets(line, sizeof(line), input)) { + lines += 1; + // strip trailing newline + if (line[strlen(line) - 1] == '\n') { + line[strlen(line) - 1] = '\0'; + } + ret = getsgnam_wrapper(line); + if (ret != 0) { + break; + } + } + + if (ret == 0) { + printf("successfully completed %d lookups\n", lines); + } else { + fprintf(stderr, "failed at line %d: %s\n", lines, line); + } + + return ret; +} #endif // ifndef BSD // nss_lookup() @@ -415,6 +485,8 @@ static int nss_lookup(char *call, FILE *input) { #ifndef BSD } else if (strncmp(call, "getspnam", 8) == 0) { ret = lookup_getspnam(input); + } else if (strncmp(call, "getsgnam", 8) == 0) { + ret = lookup_getsgnam(input); #endif // ifndef BSD } else { fprintf(stderr, "unknown nss function: %s\n", call); diff --git a/nss_cache.c b/nss_cache.c index bcb4b96..43c27b3 100644 --- a/nss_cache.c +++ b/nss_cache.c @@ -23,6 +23,7 @@ #include "nss_cache.h" #include +#include /* Locking implementation: use pthreads. */ #include @@ -36,13 +37,15 @@ static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_unlock(&mutex); \ } while (0) -static FILE *p_file = NULL; -static FILE *g_file = NULL; -static char p_filename[NSS_CACHE_PATH_LENGTH] = "/etc/passwd.cache"; -static char g_filename[NSS_CACHE_PATH_LENGTH] = "/etc/group.cache"; +static FILE *pw_file = NULL; +static FILE *gr_file = NULL; +static char pw_filename[NSS_CACHE_PATH_LENGTH] = "/etc/passwd.cache"; +static char gr_filename[NSS_CACHE_PATH_LENGTH] = "/etc/group.cache"; #ifndef BSD -static FILE *s_file = NULL; -static char s_filename[NSS_CACHE_PATH_LENGTH] = "/etc/shadow.cache"; +static FILE *sp_file = NULL; +static FILE *sg_file = NULL; +static char sp_filename[NSS_CACHE_PATH_LENGTH] = "/etc/shadow.cache"; +static char sg_filename[NSS_CACHE_PATH_LENGTH] = "/etc/gshadow.cache"; #else extern int fgetpwent_r(FILE *, struct passwd *, char *, size_t, struct passwd **); @@ -192,8 +195,8 @@ enum nss_status _nss_cache_bsearch2(struct nss_cache_args *args, int *errnop) { // Helper function for testing extern char *_nss_cache_setpwent_path(const char *path) { - DEBUG("%s %s\n", "Setting p_filename to", path); - return strncpy(p_filename, path, NSS_CACHE_PATH_LENGTH - 1); + DEBUG("%s %s\n", "Setting pw_filename to", path); + return strncpy(pw_filename, path, NSS_CACHE_PATH_LENGTH - 1); } // _nss_cache_pwuid_wrap() @@ -250,10 +253,10 @@ static enum nss_cache_match _nss_cache_pwnam_wrap(FILE *file, // Internal setup routine static enum nss_status _nss_cache_setpwent_locked(void) { - DEBUG("%s %s\n", "Opening", p_filename); - p_file = fopen(p_filename, "r"); + DEBUG("%s %s\n", "Opening", pw_filename); + pw_file = fopen(pw_filename, "r"); - if (p_file) { + if (pw_file) { return NSS_STATUS_SUCCESS; } else { return NSS_STATUS_UNAVAIL; @@ -277,9 +280,9 @@ enum nss_status _nss_cache_setpwent(int stayopen) { static enum nss_status _nss_cache_endpwent_locked(void) { DEBUG("Closing passwd.cache\n"); - if (p_file) { - fclose(p_file); - p_file = NULL; + if (pw_file) { + fclose(pw_file); + pw_file = NULL; } return NSS_STATUS_SUCCESS; } @@ -303,13 +306,13 @@ static enum nss_status _nss_cache_getpwent_r_locked(struct passwd *result, int *errnop) { enum nss_status ret = NSS_STATUS_SUCCESS; - if (p_file == NULL) { - DEBUG("p_file == NULL, going to setpwent\n"); + if (pw_file == NULL) { + DEBUG("pw_file == NULL, going to setpwent\n"); ret = _nss_cache_setpwent_locked(); } if (ret == NSS_STATUS_SUCCESS) { - if (fgetpwent_r(p_file, result, buffer, buflen, &result) == 0) { + if (fgetpwent_r(pw_file, result, buffer, buflen, &result) == 0) { DEBUG("Returning user %d:%s\n", result->pw_uid, result->pw_name); } else { if (errno == ENOENT) { @@ -345,7 +348,7 @@ enum nss_status _nss_cache_getpwuid_r(uid_t uid, struct passwd *result, struct nss_cache_args args; enum nss_status ret; - strncpy(filename, p_filename, NSS_CACHE_PATH_LENGTH - 1); + strncpy(filename, pw_filename, NSS_CACHE_PATH_LENGTH - 1); int n = strlen(filename); if (n > NSS_CACHE_PATH_LENGTH - 7) { DEBUG("filename too long\n"); @@ -354,7 +357,7 @@ enum nss_status _nss_cache_getpwuid_r(uid_t uid, struct passwd *result, strncat(filename, ".ixuid", NSS_CACHE_PATH_LENGTH - n - 1); args.sorted_filename = filename; - args.system_filename = p_filename; + args.system_filename = pw_filename; args.lookup_function = _nss_cache_pwuid_wrap; args.lookup_value = &uid; args.lookup_result = result; @@ -408,7 +411,7 @@ enum nss_status _nss_cache_getpwnam_r(const char *name, struct passwd *result, } strncpy(pw_name, name, strlen(name) + 1); - strncpy(filename, p_filename, NSS_CACHE_PATH_LENGTH - 1); + strncpy(filename, pw_filename, NSS_CACHE_PATH_LENGTH - 1); int n = strlen(filename); if (n > NSS_CACHE_PATH_LENGTH - 8) { DEBUG("filename too long\n"); @@ -418,7 +421,7 @@ enum nss_status _nss_cache_getpwnam_r(const char *name, struct passwd *result, strncat(filename, ".ixname", NSS_CACHE_PATH_LENGTH - n - 1); args.sorted_filename = filename; - args.system_filename = p_filename; + args.system_filename = pw_filename; args.lookup_function = _nss_cache_pwnam_wrap; args.lookup_value = pw_name; args.lookup_result = result; @@ -457,18 +460,18 @@ enum nss_status _nss_cache_getpwnam_r(const char *name, struct passwd *result, // Helper function for testing extern char *_nss_cache_setgrent_path(const char *path) { - DEBUG("%s %s\n", "Setting g_filename to", path); - return strncpy(g_filename, path, NSS_CACHE_PATH_LENGTH - 1); + DEBUG("%s %s\n", "Setting gr_filename to", path); + return strncpy(gr_filename, path, NSS_CACHE_PATH_LENGTH - 1); } // _nss_cache_setgrent_locked() // Internal setup routine static enum nss_status _nss_cache_setgrent_locked(void) { - DEBUG("%s %s\n", "Opening", g_filename); - g_file = fopen(g_filename, "r"); + DEBUG("%s %s\n", "Opening", gr_filename); + gr_file = fopen(gr_filename, "r"); - if (g_file) { + if (gr_file) { return NSS_STATUS_SUCCESS; } else { return NSS_STATUS_UNAVAIL; @@ -542,9 +545,9 @@ enum nss_status _nss_cache_setgrent(int stayopen) { static enum nss_status _nss_cache_endgrent_locked(void) { DEBUG("Closing group.cache\n"); - if (g_file) { - fclose(g_file); - g_file = NULL; + if (gr_file) { + fclose(gr_file); + gr_file = NULL; } return NSS_STATUS_SUCCESS; } @@ -568,16 +571,16 @@ static enum nss_status _nss_cache_getgrent_r_locked(struct group *result, int *errnop) { enum nss_status ret = NSS_STATUS_SUCCESS; - if (g_file == NULL) { - DEBUG("g_file == NULL, going to setgrent\n"); + if (gr_file == NULL) { + DEBUG("gr_file == NULL, going to setgrent\n"); ret = _nss_cache_setgrent_locked(); } if (ret == NSS_STATUS_SUCCESS) { fpos_t position; - fgetpos(g_file, &position); - if (fgetgrent_r(g_file, result, buffer, buflen, &result) == 0) { + fgetpos(gr_file, &position); + if (fgetgrent_r(gr_file, result, buffer, buflen, &result) == 0) { DEBUG("Returning group %s (%d)\n", result->gr_name, result->gr_gid); } else { /* Rewind back to where we were just before, otherwise the data read @@ -590,7 +593,7 @@ static enum nss_status _nss_cache_getgrent_r_locked(struct group *result, if (errno == ENOENT) { errno = 0; } else { - fsetpos(g_file, &position); + fsetpos(gr_file, &position); } *errnop = errno; ret = _nss_cache_ent_bad_return_code(*errnop); @@ -630,7 +633,7 @@ enum nss_status _nss_cache_getgrgid_r(gid_t gid, struct group *result, return NSS_STATUS_TRYAGAIN; } - strncpy(filename, g_filename, NSS_CACHE_PATH_LENGTH - 1); + strncpy(filename, gr_filename, NSS_CACHE_PATH_LENGTH - 1); int n = strlen(filename); if (n > NSS_CACHE_PATH_LENGTH - 7) { DEBUG("filename too long\n"); @@ -639,7 +642,7 @@ enum nss_status _nss_cache_getgrgid_r(gid_t gid, struct group *result, strncat(filename, ".ixgid", NSS_CACHE_PATH_LENGTH - n - 1); args.sorted_filename = filename; - args.system_filename = g_filename; + args.system_filename = gr_filename; args.lookup_function = _nss_cache_grgid_wrap; args.lookup_value = &gid; args.lookup_result = result; @@ -693,7 +696,7 @@ enum nss_status _nss_cache_getgrnam_r(const char *name, struct group *result, } strncpy(gr_name, name, strlen(name) + 1); - strncpy(filename, g_filename, NSS_CACHE_PATH_LENGTH - 1); + strncpy(filename, gr_filename, NSS_CACHE_PATH_LENGTH - 1); int n = strlen(filename); if (n > NSS_CACHE_PATH_LENGTH - 8) { DEBUG("filename too long\n"); @@ -703,7 +706,7 @@ enum nss_status _nss_cache_getgrnam_r(const char *name, struct group *result, strncat(filename, ".ixname", NSS_CACHE_PATH_LENGTH - n - 1); args.sorted_filename = filename; - args.system_filename = g_filename; + args.system_filename = gr_filename; args.lookup_function = _nss_cache_grnam_wrap; args.lookup_value = gr_name; args.lookup_result = result; @@ -746,18 +749,18 @@ enum nss_status _nss_cache_getgrnam_r(const char *name, struct group *result, // Helper function for testing extern char *_nss_cache_setspent_path(const char *path) { - DEBUG("%s %s\n", "Setting s_filename to", path); - return strncpy(s_filename, path, NSS_CACHE_PATH_LENGTH - 1); + DEBUG("%s %s\n", "Setting sp_filename to", path); + return strncpy(sp_filename, path, NSS_CACHE_PATH_LENGTH - 1); } // _nss_cache_setspent_locked() // Internal setup routine static enum nss_status _nss_cache_setspent_locked(void) { - DEBUG("%s %s\n", "Opening", s_filename); - s_file = fopen(s_filename, "r"); + DEBUG("%s %s\n", "Opening", sp_filename); + sp_file = fopen(sp_filename, "r"); - if (s_file) { + if (sp_file) { return NSS_STATUS_SUCCESS; } else { return NSS_STATUS_UNAVAIL; @@ -807,9 +810,9 @@ enum nss_status _nss_cache_setspent(int stayopen) { static enum nss_status _nss_cache_endspent_locked(void) { DEBUG("Closing shadow.cache\n"); - if (s_file) { - fclose(s_file); - s_file = NULL; + if (sp_file) { + fclose(sp_file); + sp_file = NULL; } return NSS_STATUS_SUCCESS; } @@ -833,13 +836,13 @@ static enum nss_status _nss_cache_getspent_r_locked(struct spwd *result, int *errnop) { enum nss_status ret = NSS_STATUS_SUCCESS; - if (s_file == NULL) { - DEBUG("s_file == NULL, going to setspent\n"); + if (sp_file == NULL) { + DEBUG("sp_file == NULL, going to setspent\n"); ret = _nss_cache_setspent_locked(); } if (ret == NSS_STATUS_SUCCESS) { - if (fgetspent_r(s_file, result, buffer, buflen, &result) == 0) { + if (fgetspent_r(sp_file, result, buffer, buflen, &result) == 0) { DEBUG("Returning shadow entry %s\n", result->sp_namp); } else { if (errno == ENOENT) { @@ -886,7 +889,7 @@ enum nss_status _nss_cache_getspnam_r(const char *name, struct spwd *result, } strncpy(sp_namp, name, strlen(name) + 1); - strncpy(filename, s_filename, NSS_CACHE_PATH_LENGTH - 1); + strncpy(filename, sp_filename, NSS_CACHE_PATH_LENGTH - 1); int n = strlen(filename); if (n > NSS_CACHE_PATH_LENGTH - 8) { DEBUG("filename too long\n"); @@ -896,7 +899,7 @@ enum nss_status _nss_cache_getspnam_r(const char *name, struct spwd *result, strncat(filename, ".ixname", NSS_CACHE_PATH_LENGTH - n - 1); args.sorted_filename = filename; - args.system_filename = s_filename; + args.system_filename = sp_filename; args.lookup_function = _nss_cache_spnam_wrap; args.lookup_value = sp_namp; args.lookup_result = result; @@ -926,6 +929,191 @@ enum nss_status _nss_cache_getspnam_r(const char *name, struct spwd *result, return ret; } + +// _nss_cache_setsgent_path() +// Helper function for testing + +extern char *_nss_cache_setsgent_path(const char *path) { + DEBUG("%s %s\n", "Setting sg_filename to", path); + return strncpy(sg_filename, path, NSS_CACHE_PATH_LENGTH - 1); +} + +// _nss_cache_setsgent_locked() +// Internal setup routine + +static enum nss_status _nss_cache_setsgent_locked(void) { + DEBUG("%s %s\n", "Opening", sg_filename); + sg_file = fopen(sg_filename, "r"); + + if (sg_file) { + return NSS_STATUS_SUCCESS; + } else { + return NSS_STATUS_UNAVAIL; + } +} + +// _nss_cache_sgnam_wrap() +// Internal wrapper for binary searches, using shadow-specific calls. + +static enum nss_cache_match _nss_cache_sgnam_wrap(FILE *file, + struct nss_cache_args *args) { + struct sgrp *result = args->lookup_result; + char *name = args->lookup_value; + int ret; + + if (fgetsgent_r(file, result, args->buffer, args->buflen, &result) == 0) { + ret = strcoll(result->sg_namp, name); + if (ret == 0) { + DEBUG("SUCCESS: found user %s\n", result->sg_namp); + return NSS_CACHE_EXACT; + } + DEBUG("Failed match at name %s\n", result->sg_namp); + if (ret > 0) { + return NSS_CACHE_HIGH; + } else { + return NSS_CACHE_LOW; + } + } + + return NSS_CACHE_ERROR; +} + +// _nss_cache_setsgent() +// Called by NSS to open the shadow file +// 'stayopen' parameter is ignored. + +enum nss_status _nss_cache_setsgent(int stayopen) { + enum nss_status ret; + NSS_CACHE_LOCK(); + ret = _nss_cache_setsgent_locked(); + NSS_CACHE_UNLOCK(); + return ret; +} + +// _nss_cache_endsgent_locked() +// Internal close routine + +static enum nss_status _nss_cache_endsgent_locked(void) { + DEBUG("Closing gshadow.cache\n"); + if (sg_file) { + fclose(sg_file); + sg_file = NULL; + } + return NSS_STATUS_SUCCESS; +} + +// _nss_cache_endsgent() +// Called by NSS to close the shadow file + +enum nss_status _nss_cache_endsgent(void) { + enum nss_status ret; + NSS_CACHE_LOCK(); + ret = _nss_cache_endsgent_locked(); + NSS_CACHE_UNLOCK(); + return ret; +} + +// _nss_cache_getsgent_r_locked() +// Called internally to return the next entry from the shadow file + +static enum nss_status _nss_cache_getsgent_r_locked(struct sgrp *result, + char *buffer, size_t buflen, + int *errnop) { + enum nss_status ret = NSS_STATUS_SUCCESS; + + if (sg_file == NULL) { + DEBUG("sg_file == NULL, going to setsgent\n"); + ret = _nss_cache_setsgent_locked(); + } + + if (ret == NSS_STATUS_SUCCESS) { + if (fgetsgent_r(sg_file, result, buffer, buflen, &result) == 0) { + DEBUG("Returning gshadow entry %s\n", result->sg_namp); + } else { + if (errno == ENOENT) { + errno = 0; + } + *errnop = errno; + ret = _nss_cache_ent_bad_return_code(*errnop); + } + } + + return ret; +} + +// _nss_cache_getsgent_r() +// Called by NSS to look up next entry in the shadow file + +enum nss_status _nss_cache_getsgent_r(struct sgrp *result, char *buffer, + size_t buflen, int *errnop) { + enum nss_status ret; + NSS_CACHE_LOCK(); + ret = _nss_cache_getsgent_r_locked(result, buffer, buflen, errnop); + NSS_CACHE_UNLOCK(); + return ret; +} + +// _nss_cache_getsgnam_r() +// Find a user by name + +enum nss_status _nss_cache_getsgnam_r(const char *name, struct sgrp *result, + char *buffer, size_t buflen, + int *errnop) { + char *sg_namp; + char filename[NSS_CACHE_PATH_LENGTH]; + struct nss_cache_args args; + enum nss_status ret; + + NSS_CACHE_LOCK(); + + // name is a const char, we need a non-const copy + sg_namp = malloc(strlen(name) + 1); + if (sg_namp == NULL) { + DEBUG("malloc error\n"); + return NSS_STATUS_UNAVAIL; + } + strncpy(sg_namp, name, strlen(name) + 1); + + strncpy(filename, sg_filename, NSS_CACHE_PATH_LENGTH - 1); + int n = strlen(filename); + if (n > NSS_CACHE_PATH_LENGTH - 8) { + DEBUG("filename too long\n"); + free(sg_namp); + return NSS_STATUS_UNAVAIL; + } + strncat(filename, ".ixname", NSS_CACHE_PATH_LENGTH - n - 1); + + args.sorted_filename = filename; + args.system_filename = sg_filename; + args.lookup_function = _nss_cache_sgnam_wrap; + args.lookup_value = sg_namp; + args.lookup_result = result; + args.buffer = buffer; + args.buflen = buflen; + args.lookup_key = sg_namp; + args.lookup_key_length = strlen(sg_namp); + + DEBUG("Binary search for user %s\n", sg_namp); + ret = _nss_cache_bsearch2(&args, errnop); + + if (ret == NSS_STATUS_UNAVAIL) { + DEBUG("Binary search failed, falling back to full linear search\n"); + ret = _nss_cache_setsgent_locked(); + + if (ret == NSS_STATUS_SUCCESS) { + while ((ret = _nss_cache_getsgent_r_locked( + result, buffer, buflen, errnop)) == NSS_STATUS_SUCCESS) { + if (!strcmp(result->sg_namp, name)) break; + } + } + } + + free(sg_namp); + _nss_cache_endsgent_locked(); + NSS_CACHE_UNLOCK(); + + return ret; +} #endif #ifdef BSD diff --git a/nss_cache.h b/nss_cache.h index d1067a0..460b278 100644 --- a/nss_cache.h +++ b/nss_cache.h @@ -31,6 +31,7 @@ #ifndef BSD #include +#include #endif /* ifndef BSD */ #ifndef NSS_CACHE_H @@ -53,6 +54,7 @@ extern char *_nss_cache_setpwent_path(const char *path); extern char *_nss_cache_setgrent_path(const char *path); #ifndef BSD extern char *_nss_cache_setspent_path(const char *path); +extern char *_nss_cache_setsgent_path(const char *path); #endif /* ifndef BSD */ enum nss_cache_match { diff --git a/nss_test.h b/nss_test.h index d6ccbe7..161c54a 100644 --- a/nss_test.h +++ b/nss_test.h @@ -24,6 +24,7 @@ #ifndef BSD #include +#include #endif // ifndef BSD #ifndef NSS_TEST_H @@ -33,6 +34,7 @@ const char *PASSWD_FILE = ".testdata/passwd.cache"; const char *GROUP_FILE = ".testdata/group.cache"; #ifndef BSD const char *SHADOW_FILE = ".testdata/shadow.cache"; +const char *GSHADOW_FILE = ".testdata/gshadow.cache"; #endif // ifndef BSD extern enum nss_status _nss_cache_getpwent_r(struct passwd *result, @@ -59,6 +61,11 @@ extern enum nss_status _nss_cache_getspnam_r(const char *name, size_t buflen, int *errnop); extern enum nss_status _nss_cache_getspent_r(struct spwd *result, char *buffer, size_t buflen, int *errnop); +extern enum nss_status _nss_cache_getsgnam_r(const char *name, + struct sgrp *result, char *buffer, + size_t buflen, int *errnop); +extern enum nss_status _nss_cache_getsgent_r(struct sgrp *result, char *buffer, + size_t buflen, int *errnop); extern char *_nss_cache_setpwent_path(const char *path); #endif // ifndef BSD diff --git a/scripts/gentestdata.sh b/scripts/gentestdata.sh index b14d70f..ee6f76f 100755 --- a/scripts/gentestdata.sh +++ b/scripts/gentestdata.sh @@ -1,21 +1,28 @@ #!/bin/bash +set -e -u export LC_ALL=C.UTF-8 +outdir="$1" for _i in `seq 1 2500` ; do uid=$(tr -cd [:alpha:] < /dev/urandom | head -c8) name=$(tr -cd [:alpha:] < /dev/urandom | head -c32) printf "%s:x:%d:1000:%s:/home/%s:/bin/nologin\n" "$uid" $_i "$name" "$uid" -done > $1/passwd.cache +done > "${outdir}/passwd.cache" for _i in `seq 1 2500` ; do gid=$(tr -cd [:alpha:] < /dev/urandom | head -c8) name=$(tr -cd [:alpha:] < /dev/urandom | head -c32) printf "%s:x:%d:\n" "$gid" $_i -done > $1/group.cache +done > "${outdir}/group.cache" while read line ; do uid=$(echo $line | cut -f1 -d:) printf "%s::::::::\n" $uid -done < $1/passwd.cache > $1/shadow.cache +done < "${outdir}/passwd.cache" > "${outdir}/shadow.cache" + +while read line ; do + gid=$(echo $line | cut -f1 -d:) + printf "%s:::\n" $gid +done < "${outdir}/group.cache" > "${outdir}/gshadow.cache"