2 * Tests for the NSS cash module
4 * Copyright (C) 2019-2020 Simon Ruderich
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
26 #include <sys/types.h>
30 #include "../cash_nss.h"
33 static void test_getpwent(void) {
40 // Test one setpwent/getpwent/endpwent round
42 s = _nss_cash_setpwent(0);
43 assert(s == NSS_STATUS_SUCCESS);
45 // Multiple calls with too small buffer don't advance any internal indices
46 s = _nss_cash_getpwent_r(&p, tmp_small, sizeof(tmp_small), &errnop);
47 assert(s == NSS_STATUS_TRYAGAIN);
48 assert(errnop == ERANGE);
49 s = _nss_cash_getpwent_r(&p, tmp_small, sizeof(tmp_small), &errnop);
50 assert(s == NSS_STATUS_TRYAGAIN);
51 assert(errnop == ERANGE);
52 s = _nss_cash_getpwent_r(&p, tmp_small, sizeof(tmp_small), &errnop);
53 assert(s == NSS_STATUS_TRYAGAIN);
54 assert(errnop == ERANGE);
56 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
57 assert(s == NSS_STATUS_SUCCESS);
58 assert(!strcmp(p.pw_name, "root"));
59 assert(!strcmp(p.pw_passwd, "x"));
60 assert(p.pw_uid == 0);
61 assert(p.pw_gid == 0);
62 assert(!strcmp(p.pw_gecos, "root"));
63 assert(!strcmp(p.pw_dir, "/root"));
64 assert(!strcmp(p.pw_shell, "/bin/bash"));
66 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
67 assert(s == NSS_STATUS_SUCCESS);
68 assert(!strcmp(p.pw_name, "daemon"));
69 for (int i = 0; i < 10; i++) {
70 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
71 assert(s == NSS_STATUS_SUCCESS);
73 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
74 assert(s == NSS_STATUS_SUCCESS);
75 assert(!strcmp(p.pw_name, "www-data"));
76 for (int i = 0; i < 12; i++) {
77 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
78 assert(s == NSS_STATUS_SUCCESS);
80 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
81 assert(s == NSS_STATUS_SUCCESS);
82 assert(!strcmp(p.pw_name, "_rpc"));
83 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
84 assert(s == NSS_STATUS_SUCCESS);
85 assert(!strcmp(p.pw_name, "postfix"));
86 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
87 assert(s == NSS_STATUS_NOTFOUND);
88 assert(errnop == ENOENT);
90 s = _nss_cash_endpwent();
91 assert(s == NSS_STATUS_SUCCESS);
96 s = _nss_cash_setpwent(0);
97 assert(s == NSS_STATUS_SUCCESS);
99 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
100 assert(s == NSS_STATUS_SUCCESS);
101 assert(!strcmp(p.pw_name, "root"));
102 assert(!strcmp(p.pw_passwd, "x"));
103 assert(p.pw_uid == 0);
104 assert(p.pw_gid == 0);
105 assert(!strcmp(p.pw_gecos, "root"));
106 assert(!strcmp(p.pw_dir, "/root"));
107 assert(!strcmp(p.pw_shell, "/bin/bash"));
109 s = _nss_cash_endpwent();
110 assert(s == NSS_STATUS_SUCCESS);
113 // Test proper reset the 2nd
115 s = _nss_cash_setpwent(0);
116 assert(s == NSS_STATUS_SUCCESS);
118 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
119 assert(s == NSS_STATUS_SUCCESS);
120 assert(!strcmp(p.pw_name, "root"));
121 assert(!strcmp(p.pw_passwd, "x"));
122 assert(p.pw_uid == 0);
123 assert(p.pw_gid == 0);
124 assert(!strcmp(p.pw_gecos, "root"));
125 assert(!strcmp(p.pw_dir, "/root"));
126 assert(!strcmp(p.pw_shell, "/bin/bash"));
128 s = _nss_cash_endpwent();
129 assert(s == NSS_STATUS_SUCCESS);
132 // Test many rounds to check for open file leaks
133 for (int i = 0; i < 10000; i++) {
134 s = _nss_cash_setpwent(0);
135 assert(s == NSS_STATUS_SUCCESS);
137 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
138 assert(s == NSS_STATUS_SUCCESS);
139 assert(!strcmp(p.pw_name, "root"));
141 s = _nss_cash_endpwent();
142 assert(s == NSS_STATUS_SUCCESS);
146 // Test with cash file is not present
148 assert(rename("tests/passwd.nsscash", "tests/passwd.nsscash.tmp") == 0);
149 s = _nss_cash_setpwent(0);
150 assert(s == NSS_STATUS_SUCCESS);
151 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
152 assert(s == NSS_STATUS_UNAVAIL);
153 assert(errnop == ENOENT);
154 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
155 assert(s == NSS_STATUS_UNAVAIL);
156 assert(errnop == ENOENT);
157 s = _nss_cash_endpwent();
158 assert(s == NSS_STATUS_SUCCESS);
159 assert(rename("tests/passwd.nsscash.tmp", "tests/passwd.nsscash") == 0);
162 static void test_getpwuid(void) {
169 s = _nss_cash_getpwuid_r(0, &p, tmp_small, sizeof(tmp_small), &errnop);
170 assert(s == NSS_STATUS_TRYAGAIN);
171 assert(errnop == ERANGE);
172 s = _nss_cash_getpwuid_r(42, &p, tmp_small, sizeof(tmp_small), &errnop);
173 assert(s == NSS_STATUS_NOTFOUND); // 42 does not exist
174 assert(errnop == ENOENT);
175 s = _nss_cash_getpwuid_r(65534, &p, tmp_small, sizeof(tmp_small), &errnop);
176 assert(s == NSS_STATUS_TRYAGAIN);
177 assert(errnop == ERANGE);
179 s = _nss_cash_getpwuid_r(0, &p, tmp, sizeof(tmp), &errnop);
180 assert(s == NSS_STATUS_SUCCESS);
181 assert(!strcmp(p.pw_name, "root"));
182 assert(!strcmp(p.pw_passwd, "x"));
183 assert(p.pw_uid == 0);
184 assert(p.pw_gid == 0);
185 assert(!strcmp(p.pw_gecos, "root"));
186 assert(!strcmp(p.pw_dir, "/root"));
187 assert(!strcmp(p.pw_shell, "/bin/bash"));
189 s = _nss_cash_getpwuid_r(1, &p, tmp, sizeof(tmp), &errnop);
190 assert(s == NSS_STATUS_SUCCESS);
191 assert(!strcmp(p.pw_name, "daemon"));
192 assert(!strcmp(p.pw_passwd, "x"));
193 assert(p.pw_uid == 1);
194 assert(p.pw_gid == 1);
195 assert(!strcmp(p.pw_gecos, "daemon"));
196 assert(!strcmp(p.pw_dir, "/usr/sbin"));
197 assert(!strcmp(p.pw_shell, "/usr/sbin/nologin"));
199 s = _nss_cash_getpwuid_r(11, &p, tmp, sizeof(tmp), &errnop);
200 assert(s == NSS_STATUS_NOTFOUND);
201 assert(errnop == ENOENT);
203 s = _nss_cash_getpwuid_r(102, &p, tmp, sizeof(tmp), &errnop);
204 assert(s == NSS_STATUS_SUCCESS);
205 assert(!strcmp(p.pw_name, "systemd-network"));
206 assert(!strcmp(p.pw_passwd, "x"));
207 assert(p.pw_uid == 102);
208 assert(p.pw_gid == 103);
209 assert(!strcmp(p.pw_gecos, "systemd Network Management,,,"));
210 assert(!strcmp(p.pw_dir, "/run/systemd"));
211 assert(!strcmp(p.pw_shell, "/usr/sbin/nologin"));
213 s = _nss_cash_getpwuid_r(107, &p, tmp, sizeof(tmp), &errnop);
214 assert(s == NSS_STATUS_SUCCESS);
215 assert(!strcmp(p.pw_name, "postfix"));
216 assert(!strcmp(p.pw_passwd, "x"));
217 assert(p.pw_uid == 107);
218 assert(p.pw_gid == 114);
219 assert(!strcmp(p.pw_gecos, ""));
220 assert(!strcmp(p.pw_dir, "/var/spool/postfix"));
221 assert(!strcmp(p.pw_shell, "/usr/sbin/nologin"));
223 s = _nss_cash_getpwuid_r(INT_MAX, &p, tmp, sizeof(tmp), &errnop);
224 assert(s == NSS_STATUS_NOTFOUND);
225 assert(errnop == ENOENT);
228 // Test with cash file is not present
230 assert(rename("tests/passwd.nsscash", "tests/passwd.nsscash.tmp") == 0);
231 s = _nss_cash_getpwuid_r(0, &p, tmp, sizeof(tmp), &errnop);
232 assert(s == NSS_STATUS_UNAVAIL);
233 assert(errnop == ENOENT);
234 s = _nss_cash_getpwuid_r(42, &p, tmp, sizeof(tmp), &errnop);
235 assert(s == NSS_STATUS_UNAVAIL);
236 assert(errnop == ENOENT);
237 assert(rename("tests/passwd.nsscash.tmp", "tests/passwd.nsscash") == 0);
240 static void test_getpwnam(void) {
247 s = _nss_cash_getpwnam_r("root", &p, tmp_small, sizeof(tmp_small), &errnop);
248 assert(s == NSS_STATUS_TRYAGAIN);
249 assert(errnop == ERANGE);
250 s = _nss_cash_getpwnam_r("nope", &p, tmp_small, sizeof(tmp_small), &errnop);
251 assert(s == NSS_STATUS_NOTFOUND); // does not exist
252 assert(errnop == ENOENT);
253 s = _nss_cash_getpwnam_r("nobody", &p, tmp_small, sizeof(tmp_small), &errnop);
254 assert(s == NSS_STATUS_TRYAGAIN);
255 assert(errnop == ERANGE);
257 s = _nss_cash_getpwnam_r("root", &p, tmp, sizeof(tmp), &errnop);
258 assert(s == NSS_STATUS_SUCCESS);
259 assert(!strcmp(p.pw_name, "root"));
260 assert(!strcmp(p.pw_passwd, "x"));
261 assert(p.pw_uid == 0);
262 assert(p.pw_gid == 0);
263 assert(!strcmp(p.pw_gecos, "root"));
264 assert(!strcmp(p.pw_dir, "/root"));
265 assert(!strcmp(p.pw_shell, "/bin/bash"));
267 s = _nss_cash_getpwnam_r("daemon", &p, tmp, sizeof(tmp), &errnop);
268 assert(s == NSS_STATUS_SUCCESS);
269 assert(!strcmp(p.pw_name, "daemon"));
270 assert(!strcmp(p.pw_passwd, "x"));
271 assert(p.pw_uid == 1);
272 assert(p.pw_gid == 1);
273 assert(!strcmp(p.pw_gecos, "daemon"));
274 assert(!strcmp(p.pw_dir, "/usr/sbin"));
275 assert(!strcmp(p.pw_shell, "/usr/sbin/nologin"));
277 s = _nss_cash_getpwnam_r("nope2", &p, tmp, sizeof(tmp), &errnop);
278 assert(s == NSS_STATUS_NOTFOUND);
279 assert(errnop == ENOENT);
281 s = _nss_cash_getpwnam_r("systemd-network", &p, tmp, sizeof(tmp), &errnop);
282 assert(s == NSS_STATUS_SUCCESS);
283 assert(!strcmp(p.pw_name, "systemd-network"));
284 assert(!strcmp(p.pw_passwd, "x"));
285 assert(p.pw_uid == 102);
286 assert(p.pw_gid == 103);
287 assert(!strcmp(p.pw_gecos, "systemd Network Management,,,"));
288 assert(!strcmp(p.pw_dir, "/run/systemd"));
289 assert(!strcmp(p.pw_shell, "/usr/sbin/nologin"));
291 s = _nss_cash_getpwnam_r("postfix", &p, tmp, sizeof(tmp), &errnop);
292 assert(s == NSS_STATUS_SUCCESS);
293 assert(!strcmp(p.pw_name, "postfix"));
294 assert(!strcmp(p.pw_passwd, "x"));
295 assert(p.pw_uid == 107);
296 assert(p.pw_gid == 114);
297 assert(!strcmp(p.pw_gecos, ""));
298 assert(!strcmp(p.pw_dir, "/var/spool/postfix"));
299 assert(!strcmp(p.pw_shell, "/usr/sbin/nologin"));
301 s = _nss_cash_getpwnam_r("", &p, tmp, sizeof(tmp), &errnop);
302 assert(s == NSS_STATUS_NOTFOUND);
303 assert(errnop == ENOENT);
306 // Test with cash file is not present
308 assert(rename("tests/passwd.nsscash", "tests/passwd.nsscash.tmp") == 0);
309 s = _nss_cash_getpwnam_r("root", &p, tmp, sizeof(tmp), &errnop);
310 assert(s == NSS_STATUS_UNAVAIL);
311 assert(errnop == ENOENT);
312 s = _nss_cash_getpwnam_r("nope", &p, tmp, sizeof(tmp), &errnop);
313 assert(s == NSS_STATUS_UNAVAIL);
314 assert(errnop == ENOENT);
315 assert(rename("tests/passwd.nsscash.tmp", "tests/passwd.nsscash") == 0);
318 static void test_limits(void) {
320 memset(gecos, 'X', sizeof(gecos));
321 gecos[sizeof(gecos)-1] = '\0';
326 const char *nsscash_cmd = "../nsscash convert passwd "
327 "tests/limits tests/limits.nsscash 2> /dev/null";
329 // Entries which will not fit in uint16_t, nsscash must abort
331 fh = fopen("tests/limits", "w");
333 r = fprintf(fh, "test:xx:42:4711:%s:/home/test:/bin/zsh\n", gecos);
338 r = system(nsscash_cmd);
340 assert(WIFEXITED(r) && WEXITSTATUS(r) == 1);
342 fh = fopen("tests/limits", "w");
344 r = fprintf(fh, "test:%s:42:4711:%s:/home/test:/bin/zsh\n", gecos, gecos);
349 r = system(nsscash_cmd);
351 assert(WIFEXITED(r) && WEXITSTATUS(r) == 1);
353 // Largest entry which will fit
355 fh = fopen("tests/limits", "w");
357 r = fprintf(fh, "test:x:42:4711:%s:/home/test:/bin/zsh\n", gecos);
362 r = system(nsscash_cmd);
364 assert(WIFEXITED(r) && WEXITSTATUS(r) == 0);
366 r = rename("tests/passwd.nsscash", "tests/passwd.nsscash.tmp");
368 r = rename("tests/limits.nsscash", "tests/passwd.nsscash");
371 // Check if the entry can be retrieved
375 char tmp[4+1 + 1+1 + 65508 + 10+1 + 8+1];
378 s = _nss_cash_getpwuid_r(42, &p, tmp, sizeof(tmp), &errnop);
379 assert(s == NSS_STATUS_SUCCESS);
380 assert(!strcmp(p.pw_name, "test"));
381 assert(!strcmp(p.pw_passwd, "x"));
382 assert(p.pw_uid == 42);
383 assert(p.pw_gid == 4711);
384 assert(!strcmp(p.pw_gecos, gecos));
385 assert(!strcmp(p.pw_dir, "/home/test"));
386 assert(!strcmp(p.pw_shell, "/bin/zsh"));
388 r = rename("tests/passwd.nsscash.tmp", "tests/passwd.nsscash");
391 r = unlink("tests/limits");