From c8549b7770ed024dc9caac8a1c3d134532bf35c5 Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Sat, 8 Jun 2019 14:06:55 +0200 Subject: [PATCH] nss: add tests These also test large parts of the Go code. --- .gitignore | 3 + nss/Makefile | 32 +++-- nss/tests/passwd | 27 ++++ nss/tests/pw.c | 321 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 376 insertions(+), 7 deletions(-) create mode 100644 nss/tests/passwd create mode 100644 nss/tests/pw.c diff --git a/.gitignore b/.gitignore index d409a2e..3f82d1b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ /filetype_string.go +/nss/libcash_test.so /nss/libnss_cash.so.2 +/nss/tests/passwd.nsscash +/nss/tests/pw /nsscash diff --git a/nss/Makefile b/nss/Makefile index 195cdc1..0ce9093 100644 --- a/nss/Makefile +++ b/nss/Makefile @@ -5,20 +5,38 @@ CFLAGS = -Wall -Wextra -Wconversion CFLAGS += -g -O2 -Werror=array-bounds -Werror=clobbered -Werror=volatile-register-var -Werror=implicit-function-declaration -fstack-protector-strong -Wformat -Werror=format-security CPPFLAGS += -Wdate-time -D_FORTIFY_SOURCE=2 LDFLAGS += -Wl,-z,relro -Wl,-z,now - -# During development -#CFLAGS += -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -#LDFLAGS += -fsanitize=address -fsanitize=undefined +# For tests +TEST_CFLAGS += -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined +TEST_LDFLAGS += -fsanitize=address -fsanitize=undefined all: libnss_cash.so.2 clean: - rm -f libnss_cash.so.2 + rm -f libnss_cash.so.2 \ + libcash_test.so tests/pw tests/passwd.nsscash -libnss_cash.so.2: $(wildcard *.c) $(wildcard *.h) +libnss_cash.so.2 libcash_test.so: $(wildcard *.c) $(wildcard *.h) $(CC) -o $@ -shared -fPIC -Wl,-soname,$@ \ $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) \ file.c pw.c search.c \ $(LDLIBS) -.PHONY: all clean + +# Tests + +test: tests/pw tests/passwd.nsscash + LD_LIBRARY_PATH=. LD_PRELOD= ./tests/pw + +tests/pw: tests/pw.c libcash_test.so + $(CC) -o $@ $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) \ + $(TEST_CFLAGS) $(TEST_LDFLAGS) -L. \ + $< $(LDLIBS) -lcash_test -lasan + +tests/passwd.nsscash: tests/passwd + ../nsscash convert passwd $< $@ + +libcash_test.so: CFLAGS += $(TEST_CFLAGS) +libcash_test.so: CPPFLAGS += -DNSSCASH_PASSWD_FILE='"./tests/passwd.nsscash"' +libcash_test.so: LDFLAGS += $(TEST_LDFLAGS) + +.PHONY: all clean test diff --git a/nss/tests/passwd b/nss/tests/passwd new file mode 100644 index 0000000..2d1277a --- /dev/null +++ b/nss/tests/passwd @@ -0,0 +1,27 @@ +root:x:0:0:root:/root:/bin/bash +daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin +bin:x:2:2:bin:/bin:/usr/sbin/nologin +sys:x:3:3:sys:/dev:/usr/sbin/nologin +sync:x:4:65534:sync:/bin:/bin/sync +games:x:5:60:games:/usr/games:/usr/sbin/nologin +man:x:6:12:man:/var/cache/man:/usr/sbin/nologin +lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin +mail:x:8:8:mail:/var/mail:/usr/sbin/nologin +news:x:9:9:news:/var/spool/news:/usr/sbin/nologin +uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin +proxy:x:13:13:proxy:/bin:/usr/sbin/nologin +www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin +backup:x:34:34:backup:/var/backups:/usr/sbin/nologin +list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin +irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin +gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin +nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin +_apt:x:100:65534::/nonexistent:/usr/sbin/nologin +systemd-timesync:x:101:102:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin +systemd-network:x:102:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin +systemd-resolve:x:103:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin +messagebus:x:104:105::/nonexistent:/usr/sbin/nologin +sshd:x:105:65534::/run/sshd:/usr/sbin/nologin +systemd-coredump:x:999:999:systemd Core Dumper:/:/sbin/nologin +_rpc:x:106:65534::/run/rpcbind:/usr/sbin/nologin +postfix:x:107:114::/var/spool/postfix:/usr/sbin/nologin diff --git a/nss/tests/pw.c b/nss/tests/pw.c new file mode 100644 index 0000000..4a32bfd --- /dev/null +++ b/nss/tests/pw.c @@ -0,0 +1,321 @@ +/* + * Tests for the NSS cash module + * + * Copyright (C) 2019 Simon Ruderich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "../cash_nss.h" + + +static void test_getpwent(void) { + struct passwd p; + enum nss_status s; + char tmp[1024]; + char tmp_small[10]; + int errnop = 0; + + // Test one setpwent/getpwent/endpwent round + + s = _nss_cash_setpwent(0); + assert(s == NSS_STATUS_SUCCESS); + + // Multiple calls with too small buffer don't advance any internal indices + s = _nss_cash_getpwent_r(&p, tmp_small, sizeof(tmp_small), &errnop); + assert(s == NSS_STATUS_TRYAGAIN); + assert(errnop == ERANGE); + s = _nss_cash_getpwent_r(&p, tmp_small, sizeof(tmp_small), &errnop); + assert(s == NSS_STATUS_TRYAGAIN); + assert(errnop == ERANGE); + s = _nss_cash_getpwent_r(&p, tmp_small, sizeof(tmp_small), &errnop); + assert(s == NSS_STATUS_TRYAGAIN); + assert(errnop == ERANGE); + + s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_SUCCESS); + assert(!strcmp(p.pw_name, "root")); + assert(!strcmp(p.pw_passwd, "x")); + assert(p.pw_uid == 0); + assert(p.pw_gid == 0); + assert(!strcmp(p.pw_gecos, "root")); + assert(!strcmp(p.pw_dir, "/root")); + assert(!strcmp(p.pw_shell, "/bin/bash")); + + s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_SUCCESS); + assert(!strcmp(p.pw_name, "daemon")); + for (int i = 0; i < 10; i++) { + s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_SUCCESS); + } + s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_SUCCESS); + assert(!strcmp(p.pw_name, "www-data")); + for (int i = 0; i < 12; i++) { + s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_SUCCESS); + } + s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_SUCCESS); + assert(!strcmp(p.pw_name, "_rpc")); + s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_SUCCESS); + assert(!strcmp(p.pw_name, "postfix")); + s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_NOTFOUND); + assert(errnop == ENOENT); + + s = _nss_cash_endpwent(); + assert(s == NSS_STATUS_SUCCESS); + + + // Test proper reset + + s = _nss_cash_setpwent(0); + assert(s == NSS_STATUS_SUCCESS); + + s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_SUCCESS); + assert(!strcmp(p.pw_name, "root")); + assert(!strcmp(p.pw_passwd, "x")); + assert(p.pw_uid == 0); + assert(p.pw_gid == 0); + assert(!strcmp(p.pw_gecos, "root")); + assert(!strcmp(p.pw_dir, "/root")); + assert(!strcmp(p.pw_shell, "/bin/bash")); + + s = _nss_cash_endpwent(); + assert(s == NSS_STATUS_SUCCESS); + + + // Test proper reset the 2nd + + s = _nss_cash_setpwent(0); + assert(s == NSS_STATUS_SUCCESS); + + s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_SUCCESS); + assert(!strcmp(p.pw_name, "root")); + assert(!strcmp(p.pw_passwd, "x")); + assert(p.pw_uid == 0); + assert(p.pw_gid == 0); + assert(!strcmp(p.pw_gecos, "root")); + assert(!strcmp(p.pw_dir, "/root")); + assert(!strcmp(p.pw_shell, "/bin/bash")); + + s = _nss_cash_endpwent(); + assert(s == NSS_STATUS_SUCCESS); + + + // Test many rounds to check for open file leaks + for (int i = 0; i < 10000; i++) { + s = _nss_cash_setpwent(0); + assert(s == NSS_STATUS_SUCCESS); + + s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_SUCCESS); + assert(!strcmp(p.pw_name, "root")); + + s = _nss_cash_endpwent(); + assert(s == NSS_STATUS_SUCCESS); + } + + + // Test with cash file is not present + + assert(rename("tests/passwd.nsscash", "tests/passwd.nsscash.tmp") == 0); + s = _nss_cash_setpwent(0); + assert(s == NSS_STATUS_SUCCESS); + s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_UNAVAIL); + assert(errnop == ENOENT); + s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_UNAVAIL); + assert(errnop == ENOENT); + s = _nss_cash_endpwent(); + assert(s == NSS_STATUS_SUCCESS); + assert(rename("tests/passwd.nsscash.tmp", "tests/passwd.nsscash") == 0); +} + +static void test_getpwuid(void) { + struct passwd p; + enum nss_status s; + char tmp[1024]; + char tmp_small[10]; + int errnop = 0; + + s = _nss_cash_getpwuid_r(0, &p, tmp_small, sizeof(tmp_small), &errnop); + assert(s == NSS_STATUS_TRYAGAIN); + assert(errnop == ERANGE); + s = _nss_cash_getpwuid_r(42, &p, tmp_small, sizeof(tmp_small), &errnop); + assert(s == NSS_STATUS_NOTFOUND); // 42 does not exist + assert(errnop == ENOENT); + s = _nss_cash_getpwuid_r(65534, &p, tmp_small, sizeof(tmp_small), &errnop); + assert(s == NSS_STATUS_TRYAGAIN); + assert(errnop == ERANGE); + + s = _nss_cash_getpwuid_r(0, &p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_SUCCESS); + assert(!strcmp(p.pw_name, "root")); + assert(!strcmp(p.pw_passwd, "x")); + assert(p.pw_uid == 0); + assert(p.pw_gid == 0); + assert(!strcmp(p.pw_gecos, "root")); + assert(!strcmp(p.pw_dir, "/root")); + assert(!strcmp(p.pw_shell, "/bin/bash")); + + s = _nss_cash_getpwuid_r(1, &p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_SUCCESS); + assert(!strcmp(p.pw_name, "daemon")); + assert(!strcmp(p.pw_passwd, "x")); + assert(p.pw_uid == 1); + assert(p.pw_gid == 1); + assert(!strcmp(p.pw_gecos, "daemon")); + assert(!strcmp(p.pw_dir, "/usr/sbin")); + assert(!strcmp(p.pw_shell, "/usr/sbin/nologin")); + + s = _nss_cash_getpwuid_r(11, &p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_NOTFOUND); + assert(errnop == ENOENT); + + s = _nss_cash_getpwuid_r(102, &p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_SUCCESS); + assert(!strcmp(p.pw_name, "systemd-network")); + assert(!strcmp(p.pw_passwd, "x")); + assert(p.pw_uid == 102); + assert(p.pw_gid == 103); + assert(!strcmp(p.pw_gecos, "systemd Network Management,,,")); + assert(!strcmp(p.pw_dir, "/run/systemd")); + assert(!strcmp(p.pw_shell, "/usr/sbin/nologin")); + + s = _nss_cash_getpwuid_r(107, &p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_SUCCESS); + assert(!strcmp(p.pw_name, "postfix")); + assert(!strcmp(p.pw_passwd, "x")); + assert(p.pw_uid == 107); + assert(p.pw_gid == 114); + assert(!strcmp(p.pw_gecos, "")); + assert(!strcmp(p.pw_dir, "/var/spool/postfix")); + assert(!strcmp(p.pw_shell, "/usr/sbin/nologin")); + + s = _nss_cash_getpwuid_r(INT_MAX, &p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_NOTFOUND); + assert(errnop == ENOENT); + + + // Test with cash file is not present + + assert(rename("tests/passwd.nsscash", "tests/passwd.nsscash.tmp") == 0); + s = _nss_cash_getpwuid_r(0, &p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_UNAVAIL); + assert(errnop == ENOENT); + s = _nss_cash_getpwuid_r(42, &p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_UNAVAIL); + assert(errnop == ENOENT); + assert(rename("tests/passwd.nsscash.tmp", "tests/passwd.nsscash") == 0); +} + +static void test_getpwnam(void) { + struct passwd p; + enum nss_status s; + char tmp[1024]; + char tmp_small[10]; + int errnop = 0; + + s = _nss_cash_getpwnam_r("root", &p, tmp_small, sizeof(tmp_small), &errnop); + assert(s == NSS_STATUS_TRYAGAIN); + assert(errnop == ERANGE); + s = _nss_cash_getpwnam_r("nope", &p, tmp_small, sizeof(tmp_small), &errnop); + assert(s == NSS_STATUS_NOTFOUND); // 42 does not exist + assert(errnop == ENOENT); + s = _nss_cash_getpwnam_r("nobody", &p, tmp_small, sizeof(tmp_small), &errnop); + assert(s == NSS_STATUS_TRYAGAIN); + assert(errnop == ERANGE); + + s = _nss_cash_getpwnam_r("root", &p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_SUCCESS); + assert(!strcmp(p.pw_name, "root")); + assert(!strcmp(p.pw_passwd, "x")); + assert(p.pw_uid == 0); + assert(p.pw_gid == 0); + assert(!strcmp(p.pw_gecos, "root")); + assert(!strcmp(p.pw_dir, "/root")); + assert(!strcmp(p.pw_shell, "/bin/bash")); + + s = _nss_cash_getpwnam_r("daemon", &p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_SUCCESS); + assert(!strcmp(p.pw_name, "daemon")); + assert(!strcmp(p.pw_passwd, "x")); + assert(p.pw_uid == 1); + assert(p.pw_gid == 1); + assert(!strcmp(p.pw_gecos, "daemon")); + assert(!strcmp(p.pw_dir, "/usr/sbin")); + assert(!strcmp(p.pw_shell, "/usr/sbin/nologin")); + + s = _nss_cash_getpwnam_r("nope2", &p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_NOTFOUND); + assert(errnop == ENOENT); + + s = _nss_cash_getpwnam_r("systemd-network", &p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_SUCCESS); + assert(!strcmp(p.pw_name, "systemd-network")); + assert(!strcmp(p.pw_passwd, "x")); + assert(p.pw_uid == 102); + assert(p.pw_gid == 103); + assert(!strcmp(p.pw_gecos, "systemd Network Management,,,")); + assert(!strcmp(p.pw_dir, "/run/systemd")); + assert(!strcmp(p.pw_shell, "/usr/sbin/nologin")); + + s = _nss_cash_getpwnam_r("postfix", &p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_SUCCESS); + assert(!strcmp(p.pw_name, "postfix")); + assert(!strcmp(p.pw_passwd, "x")); + assert(p.pw_uid == 107); + assert(p.pw_gid == 114); + assert(!strcmp(p.pw_gecos, "")); + assert(!strcmp(p.pw_dir, "/var/spool/postfix")); + assert(!strcmp(p.pw_shell, "/usr/sbin/nologin")); + + s = _nss_cash_getpwnam_r("", &p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_NOTFOUND); + assert(errnop == ENOENT); + + + // Test with cash file is not present + + assert(rename("tests/passwd.nsscash", "tests/passwd.nsscash.tmp") == 0); + s = _nss_cash_getpwnam_r("root", &p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_UNAVAIL); + assert(errnop == ENOENT); + s = _nss_cash_getpwnam_r("nope", &p, tmp, sizeof(tmp), &errnop); + assert(s == NSS_STATUS_UNAVAIL); + assert(errnop == ENOENT); + assert(rename("tests/passwd.nsscash.tmp", "tests/passwd.nsscash") == 0); +} + +int main(void) { + test_getpwent(); + test_getpwuid(); + test_getpwnam(); + + return EXIT_SUCCESS; +} -- 2.43.2