2 * Tests for the NSS cash module
4 * Copyright (C) 2019 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/>.
28 #include "../cash_nss.h"
31 static void test_getpwent(void) {
38 // Test one setpwent/getpwent/endpwent round
40 s = _nss_cash_setpwent(0);
41 assert(s == NSS_STATUS_SUCCESS);
43 // Multiple calls with too small buffer don't advance any internal indices
44 s = _nss_cash_getpwent_r(&p, tmp_small, sizeof(tmp_small), &errnop);
45 assert(s == NSS_STATUS_TRYAGAIN);
46 assert(errnop == ERANGE);
47 s = _nss_cash_getpwent_r(&p, tmp_small, sizeof(tmp_small), &errnop);
48 assert(s == NSS_STATUS_TRYAGAIN);
49 assert(errnop == ERANGE);
50 s = _nss_cash_getpwent_r(&p, tmp_small, sizeof(tmp_small), &errnop);
51 assert(s == NSS_STATUS_TRYAGAIN);
52 assert(errnop == ERANGE);
54 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
55 assert(s == NSS_STATUS_SUCCESS);
56 assert(!strcmp(p.pw_name, "root"));
57 assert(!strcmp(p.pw_passwd, "x"));
58 assert(p.pw_uid == 0);
59 assert(p.pw_gid == 0);
60 assert(!strcmp(p.pw_gecos, "root"));
61 assert(!strcmp(p.pw_dir, "/root"));
62 assert(!strcmp(p.pw_shell, "/bin/bash"));
64 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
65 assert(s == NSS_STATUS_SUCCESS);
66 assert(!strcmp(p.pw_name, "daemon"));
67 for (int i = 0; i < 10; i++) {
68 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
69 assert(s == NSS_STATUS_SUCCESS);
71 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
72 assert(s == NSS_STATUS_SUCCESS);
73 assert(!strcmp(p.pw_name, "www-data"));
74 for (int i = 0; i < 12; i++) {
75 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
76 assert(s == NSS_STATUS_SUCCESS);
78 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
79 assert(s == NSS_STATUS_SUCCESS);
80 assert(!strcmp(p.pw_name, "_rpc"));
81 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
82 assert(s == NSS_STATUS_SUCCESS);
83 assert(!strcmp(p.pw_name, "postfix"));
84 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
85 assert(s == NSS_STATUS_NOTFOUND);
86 assert(errnop == ENOENT);
88 s = _nss_cash_endpwent();
89 assert(s == NSS_STATUS_SUCCESS);
94 s = _nss_cash_setpwent(0);
95 assert(s == NSS_STATUS_SUCCESS);
97 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
98 assert(s == NSS_STATUS_SUCCESS);
99 assert(!strcmp(p.pw_name, "root"));
100 assert(!strcmp(p.pw_passwd, "x"));
101 assert(p.pw_uid == 0);
102 assert(p.pw_gid == 0);
103 assert(!strcmp(p.pw_gecos, "root"));
104 assert(!strcmp(p.pw_dir, "/root"));
105 assert(!strcmp(p.pw_shell, "/bin/bash"));
107 s = _nss_cash_endpwent();
108 assert(s == NSS_STATUS_SUCCESS);
111 // Test proper reset the 2nd
113 s = _nss_cash_setpwent(0);
114 assert(s == NSS_STATUS_SUCCESS);
116 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
117 assert(s == NSS_STATUS_SUCCESS);
118 assert(!strcmp(p.pw_name, "root"));
119 assert(!strcmp(p.pw_passwd, "x"));
120 assert(p.pw_uid == 0);
121 assert(p.pw_gid == 0);
122 assert(!strcmp(p.pw_gecos, "root"));
123 assert(!strcmp(p.pw_dir, "/root"));
124 assert(!strcmp(p.pw_shell, "/bin/bash"));
126 s = _nss_cash_endpwent();
127 assert(s == NSS_STATUS_SUCCESS);
130 // Test many rounds to check for open file leaks
131 for (int i = 0; i < 10000; i++) {
132 s = _nss_cash_setpwent(0);
133 assert(s == NSS_STATUS_SUCCESS);
135 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
136 assert(s == NSS_STATUS_SUCCESS);
137 assert(!strcmp(p.pw_name, "root"));
139 s = _nss_cash_endpwent();
140 assert(s == NSS_STATUS_SUCCESS);
144 // Test with cash file is not present
146 assert(rename("tests/passwd.nsscash", "tests/passwd.nsscash.tmp") == 0);
147 s = _nss_cash_setpwent(0);
148 assert(s == NSS_STATUS_SUCCESS);
149 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
150 assert(s == NSS_STATUS_UNAVAIL);
151 assert(errnop == ENOENT);
152 s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
153 assert(s == NSS_STATUS_UNAVAIL);
154 assert(errnop == ENOENT);
155 s = _nss_cash_endpwent();
156 assert(s == NSS_STATUS_SUCCESS);
157 assert(rename("tests/passwd.nsscash.tmp", "tests/passwd.nsscash") == 0);
160 static void test_getpwuid(void) {
167 s = _nss_cash_getpwuid_r(0, &p, tmp_small, sizeof(tmp_small), &errnop);
168 assert(s == NSS_STATUS_TRYAGAIN);
169 assert(errnop == ERANGE);
170 s = _nss_cash_getpwuid_r(42, &p, tmp_small, sizeof(tmp_small), &errnop);
171 assert(s == NSS_STATUS_NOTFOUND); // 42 does not exist
172 assert(errnop == ENOENT);
173 s = _nss_cash_getpwuid_r(65534, &p, tmp_small, sizeof(tmp_small), &errnop);
174 assert(s == NSS_STATUS_TRYAGAIN);
175 assert(errnop == ERANGE);
177 s = _nss_cash_getpwuid_r(0, &p, tmp, sizeof(tmp), &errnop);
178 assert(s == NSS_STATUS_SUCCESS);
179 assert(!strcmp(p.pw_name, "root"));
180 assert(!strcmp(p.pw_passwd, "x"));
181 assert(p.pw_uid == 0);
182 assert(p.pw_gid == 0);
183 assert(!strcmp(p.pw_gecos, "root"));
184 assert(!strcmp(p.pw_dir, "/root"));
185 assert(!strcmp(p.pw_shell, "/bin/bash"));
187 s = _nss_cash_getpwuid_r(1, &p, tmp, sizeof(tmp), &errnop);
188 assert(s == NSS_STATUS_SUCCESS);
189 assert(!strcmp(p.pw_name, "daemon"));
190 assert(!strcmp(p.pw_passwd, "x"));
191 assert(p.pw_uid == 1);
192 assert(p.pw_gid == 1);
193 assert(!strcmp(p.pw_gecos, "daemon"));
194 assert(!strcmp(p.pw_dir, "/usr/sbin"));
195 assert(!strcmp(p.pw_shell, "/usr/sbin/nologin"));
197 s = _nss_cash_getpwuid_r(11, &p, tmp, sizeof(tmp), &errnop);
198 assert(s == NSS_STATUS_NOTFOUND);
199 assert(errnop == ENOENT);
201 s = _nss_cash_getpwuid_r(102, &p, tmp, sizeof(tmp), &errnop);
202 assert(s == NSS_STATUS_SUCCESS);
203 assert(!strcmp(p.pw_name, "systemd-network"));
204 assert(!strcmp(p.pw_passwd, "x"));
205 assert(p.pw_uid == 102);
206 assert(p.pw_gid == 103);
207 assert(!strcmp(p.pw_gecos, "systemd Network Management,,,"));
208 assert(!strcmp(p.pw_dir, "/run/systemd"));
209 assert(!strcmp(p.pw_shell, "/usr/sbin/nologin"));
211 s = _nss_cash_getpwuid_r(107, &p, tmp, sizeof(tmp), &errnop);
212 assert(s == NSS_STATUS_SUCCESS);
213 assert(!strcmp(p.pw_name, "postfix"));
214 assert(!strcmp(p.pw_passwd, "x"));
215 assert(p.pw_uid == 107);
216 assert(p.pw_gid == 114);
217 assert(!strcmp(p.pw_gecos, ""));
218 assert(!strcmp(p.pw_dir, "/var/spool/postfix"));
219 assert(!strcmp(p.pw_shell, "/usr/sbin/nologin"));
221 s = _nss_cash_getpwuid_r(INT_MAX, &p, tmp, sizeof(tmp), &errnop);
222 assert(s == NSS_STATUS_NOTFOUND);
223 assert(errnop == ENOENT);
226 // Test with cash file is not present
228 assert(rename("tests/passwd.nsscash", "tests/passwd.nsscash.tmp") == 0);
229 s = _nss_cash_getpwuid_r(0, &p, tmp, sizeof(tmp), &errnop);
230 assert(s == NSS_STATUS_UNAVAIL);
231 assert(errnop == ENOENT);
232 s = _nss_cash_getpwuid_r(42, &p, tmp, sizeof(tmp), &errnop);
233 assert(s == NSS_STATUS_UNAVAIL);
234 assert(errnop == ENOENT);
235 assert(rename("tests/passwd.nsscash.tmp", "tests/passwd.nsscash") == 0);
238 static void test_getpwnam(void) {
245 s = _nss_cash_getpwnam_r("root", &p, tmp_small, sizeof(tmp_small), &errnop);
246 assert(s == NSS_STATUS_TRYAGAIN);
247 assert(errnop == ERANGE);
248 s = _nss_cash_getpwnam_r("nope", &p, tmp_small, sizeof(tmp_small), &errnop);
249 assert(s == NSS_STATUS_NOTFOUND); // does not exist
250 assert(errnop == ENOENT);
251 s = _nss_cash_getpwnam_r("nobody", &p, tmp_small, sizeof(tmp_small), &errnop);
252 assert(s == NSS_STATUS_TRYAGAIN);
253 assert(errnop == ERANGE);
255 s = _nss_cash_getpwnam_r("root", &p, tmp, sizeof(tmp), &errnop);
256 assert(s == NSS_STATUS_SUCCESS);
257 assert(!strcmp(p.pw_name, "root"));
258 assert(!strcmp(p.pw_passwd, "x"));
259 assert(p.pw_uid == 0);
260 assert(p.pw_gid == 0);
261 assert(!strcmp(p.pw_gecos, "root"));
262 assert(!strcmp(p.pw_dir, "/root"));
263 assert(!strcmp(p.pw_shell, "/bin/bash"));
265 s = _nss_cash_getpwnam_r("daemon", &p, tmp, sizeof(tmp), &errnop);
266 assert(s == NSS_STATUS_SUCCESS);
267 assert(!strcmp(p.pw_name, "daemon"));
268 assert(!strcmp(p.pw_passwd, "x"));
269 assert(p.pw_uid == 1);
270 assert(p.pw_gid == 1);
271 assert(!strcmp(p.pw_gecos, "daemon"));
272 assert(!strcmp(p.pw_dir, "/usr/sbin"));
273 assert(!strcmp(p.pw_shell, "/usr/sbin/nologin"));
275 s = _nss_cash_getpwnam_r("nope2", &p, tmp, sizeof(tmp), &errnop);
276 assert(s == NSS_STATUS_NOTFOUND);
277 assert(errnop == ENOENT);
279 s = _nss_cash_getpwnam_r("systemd-network", &p, tmp, sizeof(tmp), &errnop);
280 assert(s == NSS_STATUS_SUCCESS);
281 assert(!strcmp(p.pw_name, "systemd-network"));
282 assert(!strcmp(p.pw_passwd, "x"));
283 assert(p.pw_uid == 102);
284 assert(p.pw_gid == 103);
285 assert(!strcmp(p.pw_gecos, "systemd Network Management,,,"));
286 assert(!strcmp(p.pw_dir, "/run/systemd"));
287 assert(!strcmp(p.pw_shell, "/usr/sbin/nologin"));
289 s = _nss_cash_getpwnam_r("postfix", &p, tmp, sizeof(tmp), &errnop);
290 assert(s == NSS_STATUS_SUCCESS);
291 assert(!strcmp(p.pw_name, "postfix"));
292 assert(!strcmp(p.pw_passwd, "x"));
293 assert(p.pw_uid == 107);
294 assert(p.pw_gid == 114);
295 assert(!strcmp(p.pw_gecos, ""));
296 assert(!strcmp(p.pw_dir, "/var/spool/postfix"));
297 assert(!strcmp(p.pw_shell, "/usr/sbin/nologin"));
299 s = _nss_cash_getpwnam_r("", &p, tmp, sizeof(tmp), &errnop);
300 assert(s == NSS_STATUS_NOTFOUND);
301 assert(errnop == ENOENT);
304 // Test with cash file is not present
306 assert(rename("tests/passwd.nsscash", "tests/passwd.nsscash.tmp") == 0);
307 s = _nss_cash_getpwnam_r("root", &p, tmp, sizeof(tmp), &errnop);
308 assert(s == NSS_STATUS_UNAVAIL);
309 assert(errnop == ENOENT);
310 s = _nss_cash_getpwnam_r("nope", &p, tmp, sizeof(tmp), &errnop);
311 assert(s == NSS_STATUS_UNAVAIL);
312 assert(errnop == ENOENT);
313 assert(rename("tests/passwd.nsscash.tmp", "tests/passwd.nsscash") == 0);
316 static void test_limits(void) {
318 memset(gecos, 'X', sizeof(gecos));
319 gecos[sizeof(gecos)-1] = '\0';
324 const char *nsscache_cmd = "../nsscash convert passwd "
325 "tests/limits tests/limits.nsscash 2> /dev/null";
327 // Entries which will not fit in uint16_t, nsscash must abort
329 fh = fopen("tests/limits", "w");
331 r = fprintf(fh, "test:xx:42:4711:%s:/home/test:/bin/zsh\n", gecos);
336 r = system(nsscache_cmd);
338 assert(WIFEXITED(r) && WEXITSTATUS(r) == 1);
340 fh = fopen("tests/limits", "w");
342 r = fprintf(fh, "test:%s:42:4711:%s:/home/test:/bin/zsh\n", gecos, gecos);
347 r = system(nsscache_cmd);
349 assert(WIFEXITED(r) && WEXITSTATUS(r) == 1);
351 // Largest entry which will fit
353 fh = fopen("tests/limits", "w");
355 r = fprintf(fh, "test:x:42:4711:%s:/home/test:/bin/zsh\n", gecos);
360 r = system(nsscache_cmd);
362 assert(WIFEXITED(r) && WEXITSTATUS(r) == 0);
364 r = rename("tests/passwd.nsscash", "tests/passwd.nsscash.tmp");
366 r = rename("tests/limits.nsscash", "tests/passwd.nsscash");
369 // Check if the entry can be retrieved
373 char tmp[4+1 + 1+1 + 65508 + 10+1 + 8+1];
376 s = _nss_cash_getpwuid_r(42, &p, tmp, sizeof(tmp), &errnop);
377 assert(s == NSS_STATUS_SUCCESS);
378 assert(!strcmp(p.pw_name, "test"));
379 assert(!strcmp(p.pw_passwd, "x"));
380 assert(p.pw_uid == 42);
381 assert(p.pw_gid == 4711);
382 assert(!strcmp(p.pw_gecos, gecos));
383 assert(!strcmp(p.pw_dir, "/home/test"));
384 assert(!strcmp(p.pw_shell, "/bin/zsh"));
386 r = rename("tests/passwd.nsscash.tmp", "tests/passwd.nsscash");
389 r = unlink("tests/limits");