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_getgrent(void) {
40 // Test one setgrent/getgrent/endgrent round
42 s = _nss_cash_setgrent(0);
43 assert(s == NSS_STATUS_SUCCESS);
45 // Multiple calls with too small buffer don't advance any internal indices
46 s = _nss_cash_getgrent_r(&g, tmp_small, sizeof(tmp_small), &errnop);
47 assert(s == NSS_STATUS_TRYAGAIN);
48 assert(errnop == ERANGE);
49 s = _nss_cash_getgrent_r(&g, tmp_small, sizeof(tmp_small), &errnop);
50 assert(s == NSS_STATUS_TRYAGAIN);
51 assert(errnop == ERANGE);
52 s = _nss_cash_getgrent_r(&g, tmp_small, sizeof(tmp_small), &errnop);
53 assert(s == NSS_STATUS_TRYAGAIN);
54 assert(errnop == ERANGE);
56 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
57 assert(s == NSS_STATUS_SUCCESS);
58 assert(!strcmp(g.gr_name, "root"));
59 assert(!strcmp(g.gr_passwd, "x"));
60 assert(g.gr_gid == 0);
61 assert(g.gr_mem != NULL);
62 assert(g.gr_mem[0] == NULL);
64 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
65 assert(s == NSS_STATUS_SUCCESS);
66 assert(!strcmp(g.gr_name, "daemon"));
67 assert(g.gr_gid == 1);
68 assert(g.gr_mem != NULL);
69 assert(!strcmp(g.gr_mem[0], "andariel"));
70 assert(!strcmp(g.gr_mem[1], "duriel"));
71 assert(!strcmp(g.gr_mem[2], "mephisto"));
72 assert(!strcmp(g.gr_mem[3], "diablo"));
73 assert(!strcmp(g.gr_mem[4], "baal"));
74 assert(g.gr_mem[5] == NULL);
75 for (int i = 0; i < 21; i++) {
76 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
77 assert(s == NSS_STATUS_SUCCESS);
79 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
80 assert(s == NSS_STATUS_SUCCESS);
81 assert(!strcmp(g.gr_name, "www-data"));
82 assert(!strcmp(g.gr_passwd, "x"));
83 assert(g.gr_gid == 33);
84 assert(g.gr_mem != NULL);
85 assert(!strcmp(g.gr_mem[0], "nobody"));
86 assert(g.gr_mem[1] == NULL);
87 for (int i = 0; i < 29; i++) {
88 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
89 assert(s == NSS_STATUS_SUCCESS);
91 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
92 assert(s == NSS_STATUS_SUCCESS);
93 assert(!strcmp(g.gr_name, "postfix"));
94 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
95 assert(s == NSS_STATUS_SUCCESS);
96 assert(!strcmp(g.gr_name, "postdrop"));
97 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
98 assert(s == NSS_STATUS_NOTFOUND);
99 assert(errnop == ENOENT);
101 s = _nss_cash_endgrent();
102 assert(s == NSS_STATUS_SUCCESS);
107 s = _nss_cash_setgrent(0);
108 assert(s == NSS_STATUS_SUCCESS);
110 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
111 assert(s == NSS_STATUS_SUCCESS);
112 assert(!strcmp(g.gr_name, "root"));
113 assert(!strcmp(g.gr_passwd, "x"));
114 assert(g.gr_gid == 0);
115 assert(g.gr_mem != NULL);
116 assert(g.gr_mem[0] == NULL);
118 s = _nss_cash_endgrent();
119 assert(s == NSS_STATUS_SUCCESS);
122 // Test proper reset the 2nd
124 s = _nss_cash_setgrent(0);
125 assert(s == NSS_STATUS_SUCCESS);
127 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
128 assert(s == NSS_STATUS_SUCCESS);
129 assert(!strcmp(g.gr_name, "root"));
130 assert(!strcmp(g.gr_passwd, "x"));
131 assert(g.gr_gid == 0);
132 assert(g.gr_mem != NULL);
133 assert(g.gr_mem[0] == NULL);
135 s = _nss_cash_endgrent();
136 assert(s == NSS_STATUS_SUCCESS);
139 // Test many rounds to check for open file leaks
140 for (int i = 0; i < 10000; i++) {
141 s = _nss_cash_setgrent(0);
142 assert(s == NSS_STATUS_SUCCESS);
144 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
145 assert(s == NSS_STATUS_SUCCESS);
146 assert(!strcmp(g.gr_name, "root"));
148 s = _nss_cash_endgrent();
149 assert(s == NSS_STATUS_SUCCESS);
153 // Test with cash file is not present
155 assert(rename("tests/group.nsscash", "tests/group.nsscash.tmp") == 0);
156 s = _nss_cash_setgrent(0);
157 assert(s == NSS_STATUS_SUCCESS);
158 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
159 assert(s == NSS_STATUS_UNAVAIL);
160 assert(errnop == ENOENT);
161 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
162 assert(s == NSS_STATUS_UNAVAIL);
163 assert(errnop == ENOENT);
164 s = _nss_cash_endgrent();
165 assert(s == NSS_STATUS_SUCCESS);
166 assert(rename("tests/group.nsscash.tmp", "tests/group.nsscash") == 0);
169 static void test_getgrgid(void) {
176 s = _nss_cash_getgrgid_r(0, &g, tmp_small, sizeof(tmp_small), &errnop);
177 assert(s == NSS_STATUS_TRYAGAIN);
178 assert(errnop == ERANGE);
179 s = _nss_cash_getgrgid_r(14, &g, tmp_small, sizeof(tmp_small), &errnop);
180 assert(s == NSS_STATUS_NOTFOUND); // 14 does not exist
181 assert(errnop == ENOENT);
182 s = _nss_cash_getgrgid_r(65534, &g, tmp_small, sizeof(tmp_small), &errnop);
183 assert(s == NSS_STATUS_TRYAGAIN);
184 assert(errnop == ERANGE);
186 s = _nss_cash_getgrgid_r(0, &g, tmp, sizeof(tmp), &errnop);
187 assert(s == NSS_STATUS_SUCCESS);
188 assert(!strcmp(g.gr_name, "root"));
189 assert(!strcmp(g.gr_passwd, "x"));
190 assert(g.gr_gid == 0);
191 assert(g.gr_mem != NULL);
192 assert(g.gr_mem[0] == NULL);
194 s = _nss_cash_getgrgid_r(1, &g, tmp, sizeof(tmp), &errnop);
195 assert(s == NSS_STATUS_SUCCESS);
196 assert(!strcmp(g.gr_name, "daemon"));
197 assert(g.gr_gid == 1);
198 assert(g.gr_mem != NULL);
199 assert(!strcmp(g.gr_mem[0], "andariel"));
200 assert(!strcmp(g.gr_mem[1], "duriel"));
201 assert(!strcmp(g.gr_mem[2], "mephisto"));
202 assert(!strcmp(g.gr_mem[3], "diablo"));
203 assert(!strcmp(g.gr_mem[4], "baal"));
204 assert(g.gr_mem[5] == NULL);
206 s = _nss_cash_getgrgid_r(11, &g, tmp, sizeof(tmp), &errnop);
207 assert(s == NSS_STATUS_NOTFOUND);
208 assert(errnop == ENOENT);
210 s = _nss_cash_getgrgid_r(103, &g, tmp, sizeof(tmp), &errnop);
211 assert(s == NSS_STATUS_SUCCESS);
212 assert(!strcmp(g.gr_name, "systemd-network"));
213 assert(!strcmp(g.gr_passwd, "x"));
214 assert(g.gr_gid == 103);
215 assert(g.gr_mem != NULL);
216 assert(g.gr_mem[0] == NULL);
218 s = _nss_cash_getgrgid_r(107, &g, tmp, sizeof(tmp), &errnop);
219 assert(s == NSS_STATUS_SUCCESS);
220 assert(!strcmp(g.gr_name, "kvm"));
221 assert(!strcmp(g.gr_passwd, "x"));
222 assert(g.gr_gid == 107);
223 assert(g.gr_mem != NULL);
224 assert(g.gr_mem[0] == NULL);
226 s = _nss_cash_getgrgid_r(65534, &g, tmp, sizeof(tmp), &errnop);
227 assert(s == NSS_STATUS_SUCCESS);
228 assert(!strcmp(g.gr_name, "nogroup"));
229 assert(!strcmp(g.gr_passwd, "x"));
230 assert(g.gr_gid == 65534);
231 assert(g.gr_mem != NULL);
232 assert(g.gr_mem[0] == NULL);
234 s = _nss_cash_getgrgid_r(INT_MAX, &g, tmp, sizeof(tmp), &errnop);
235 assert(s == NSS_STATUS_NOTFOUND);
236 assert(errnop == ENOENT);
239 // Test with cash file is not present
241 assert(rename("tests/group.nsscash", "tests/group.nsscash.tmp") == 0);
242 s = _nss_cash_getgrgid_r(0, &g, tmp, sizeof(tmp), &errnop);
243 assert(s == NSS_STATUS_UNAVAIL);
244 assert(errnop == ENOENT);
245 s = _nss_cash_getgrgid_r(14, &g, tmp, sizeof(tmp), &errnop);
246 assert(s == NSS_STATUS_UNAVAIL);
247 assert(errnop == ENOENT);
248 assert(rename("tests/group.nsscash.tmp", "tests/group.nsscash") == 0);
251 static void test_getgrnam(void) {
258 s = _nss_cash_getgrnam_r("root", &g, tmp_small, sizeof(tmp_small), &errnop);
259 assert(s == NSS_STATUS_TRYAGAIN);
260 assert(errnop == ERANGE);
261 s = _nss_cash_getgrnam_r("nope", &g, tmp_small, sizeof(tmp_small), &errnop);
262 assert(s == NSS_STATUS_NOTFOUND); // does not exist
263 assert(errnop == ENOENT);
264 s = _nss_cash_getgrnam_r("nogroup", &g, tmp_small, sizeof(tmp_small), &errnop);
265 assert(s == NSS_STATUS_TRYAGAIN);
266 assert(errnop == ERANGE);
268 s = _nss_cash_getgrnam_r("root", &g, tmp, sizeof(tmp), &errnop);
269 assert(s == NSS_STATUS_SUCCESS);
270 assert(!strcmp(g.gr_name, "root"));
271 assert(!strcmp(g.gr_passwd, "x"));
272 assert(g.gr_gid == 0);
273 assert(g.gr_mem != NULL);
274 assert(g.gr_mem[0] == NULL);
276 s = _nss_cash_getgrnam_r("daemon", &g, tmp, sizeof(tmp), &errnop);
277 assert(s == NSS_STATUS_SUCCESS);
278 assert(!strcmp(g.gr_name, "daemon"));
279 assert(g.gr_gid == 1);
280 assert(g.gr_mem != NULL);
281 assert(!strcmp(g.gr_mem[0], "andariel"));
282 assert(!strcmp(g.gr_mem[1], "duriel"));
283 assert(!strcmp(g.gr_mem[2], "mephisto"));
284 assert(!strcmp(g.gr_mem[3], "diablo"));
285 assert(!strcmp(g.gr_mem[4], "baal"));
286 assert(g.gr_mem[5] == NULL);
288 s = _nss_cash_getgrnam_r("nope2", &g, tmp, sizeof(tmp), &errnop);
289 assert(s == NSS_STATUS_NOTFOUND);
290 assert(errnop == ENOENT);
292 s = _nss_cash_getgrnam_r("systemd-network", &g, tmp, sizeof(tmp), &errnop);
293 assert(s == NSS_STATUS_SUCCESS);
294 assert(!strcmp(g.gr_name, "systemd-network"));
295 assert(!strcmp(g.gr_passwd, "x"));
296 assert(g.gr_gid == 103);
297 assert(g.gr_mem != NULL);
298 assert(g.gr_mem[0] == NULL);
300 s = _nss_cash_getgrnam_r("postfix", &g, tmp, sizeof(tmp), &errnop);
301 assert(s == NSS_STATUS_SUCCESS);
302 assert(!strcmp(g.gr_name, "postfix"));
303 assert(!strcmp(g.gr_passwd, "x"));
304 assert(g.gr_gid == 114);
305 assert(g.gr_mem != NULL);
306 assert(g.gr_mem[0] == NULL);
308 s = _nss_cash_getgrnam_r("", &g, tmp, sizeof(tmp), &errnop);
309 assert(s == NSS_STATUS_NOTFOUND);
310 assert(errnop == ENOENT);
313 // Test with cash file is not present
315 assert(rename("tests/group.nsscash", "tests/group.nsscash.tmp") == 0);
316 s = _nss_cash_getgrnam_r("root", &g, tmp, sizeof(tmp), &errnop);
317 assert(s == NSS_STATUS_UNAVAIL);
318 assert(errnop == ENOENT);
319 s = _nss_cash_getgrnam_r("nope", &g, tmp, sizeof(tmp), &errnop);
320 assert(s == NSS_STATUS_UNAVAIL);
321 assert(errnop == ENOENT);
322 assert(rename("tests/group.nsscash.tmp", "tests/group.nsscash") == 0);
325 static void test_limits(void) {
326 char large_member[65525];
327 memset(large_member, 'X', sizeof(large_member));
328 large_member[sizeof(large_member)-1] = '\0';
330 char many_members[54603]; // 5461 members
331 memset(many_members, 'X', sizeof(many_members));
332 for (int i = 9; i < (int)sizeof(many_members); i += 10) {
333 many_members[i-1] = (char)('A' + i % ('Z' - 'A'));
334 many_members[i] = ',';
336 many_members[sizeof(many_members)-1] = '\0';
341 const char *nsscash_cmd = "../nsscash convert group "
342 "tests/limits tests/limits.nsscash 2> /dev/null";
344 // Entries which will not fit in uint16_t, nsscash must abort
346 fh = fopen("tests/limits", "w");
348 r = fprintf(fh, "test:x:42:A%s\n", large_member);
353 r = system(nsscash_cmd);
355 assert(WIFEXITED(r) && WEXITSTATUS(r) == 1);
357 fh = fopen("tests/limits", "w");
359 r = fprintf(fh, "many:x:4711:%s%s\n", many_members, many_members);
364 r = system(nsscash_cmd);
366 assert(WIFEXITED(r) && WEXITSTATUS(r) == 1);
368 // Largest entries which will fit
370 fh = fopen("tests/limits", "w");
372 r = fprintf(fh, "test:x:42:%s\n", large_member);
374 r = fprintf(fh, "many:x:4711:%s\n", many_members);
379 r = system(nsscash_cmd);
381 assert(WIFEXITED(r) && WEXITSTATUS(r) == 0);
383 r = rename("tests/group.nsscash", "tests/group.nsscash.tmp");
385 r = rename("tests/limits.nsscash", "tests/group.nsscash");
388 // Check if the entry can be retrieved
392 char tmp[sizeof(char **) + 1*sizeof(char *) + 1*sizeof(uint16_t) +
393 4+1 + 1+1 + 65525 + 1];
394 char tmp2[sizeof(char **) + 5462*sizeof(char *) + 5462*sizeof(uint16_t) +
395 4+1 + 1+1 + 54603 + 1];
398 s = _nss_cash_getgrgid_r(42, &g, tmp, sizeof(tmp), &errnop);
399 assert(s == NSS_STATUS_SUCCESS);
400 assert(!strcmp(g.gr_name, "test"));
401 assert(g.gr_gid == 42);
402 assert(g.gr_mem != NULL);
403 assert(!strcmp(g.gr_mem[0], large_member));
404 assert(g.gr_mem[1] == NULL);
406 s = _nss_cash_getgrgid_r(4711, &g, tmp2, sizeof(tmp2), &errnop);
407 assert(s == NSS_STATUS_SUCCESS);
408 assert(!strcmp(g.gr_name, "many"));
409 assert(g.gr_gid == 4711);
410 assert(g.gr_mem != NULL);
411 for (int i = 0; i < 5461-1; i++) {
413 memset(x, 'X', sizeof(x));
414 x[8] = (char)('A' + (i * 10 + 9) % ('Z' - 'A'));
416 assert(!strcmp(g.gr_mem[i], x));
418 assert(!strcmp(g.gr_mem[5461-1], "XX"));
419 assert(g.gr_mem[5461] == NULL);
421 r = rename("tests/group.nsscash.tmp", "tests/group.nsscash");
424 r = unlink("tests/limits");