]> ruderich.org/simon Gitweb - nsscash/nsscash.git/commitdiff
nss: add tests
authorSimon Ruderich <simon@ruderich.org>
Sat, 8 Jun 2019 12:06:55 +0000 (14:06 +0200)
committerSimon Ruderich <simon@ruderich.org>
Sat, 8 Jun 2019 12:06:55 +0000 (14:06 +0200)
These also test large parts of the Go code.

.gitignore
nss/Makefile
nss/tests/passwd [new file with mode: 0644]
nss/tests/pw.c [new file with mode: 0644]

index d409a2efb65727143150fe9fafdf71b44700124f..3f82d1b9f6c425538f8b1c83b2afe3536f792862 100644 (file)
@@ -1,3 +1,6 @@
 /filetype_string.go
+/nss/libcash_test.so
 /nss/libnss_cash.so.2
+/nss/tests/passwd.nsscash
+/nss/tests/pw
 /nsscash
index 195cdc1614e6f8aa9b11103b89e9bca8163b25c2..0ce909345aec31410edd330806f82cea50d8c325 100644 (file)
@@ -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 (file)
index 0000000..2d1277a
--- /dev/null
@@ -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 (file)
index 0000000..4a32bfd
--- /dev/null
@@ -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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+
+#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;
+}