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_getgrent(void) {
38 // Test one setgrent/getgrent/endgrent round
40 s = _nss_cash_setgrent(0);
41 assert(s == NSS_STATUS_SUCCESS);
43 // Multiple calls with too small buffer don't advance any internal indices
44 s = _nss_cash_getgrent_r(&g, tmp_small, sizeof(tmp_small), &errnop);
45 assert(s == NSS_STATUS_TRYAGAIN);
46 assert(errnop == ERANGE);
47 s = _nss_cash_getgrent_r(&g, tmp_small, sizeof(tmp_small), &errnop);
48 assert(s == NSS_STATUS_TRYAGAIN);
49 assert(errnop == ERANGE);
50 s = _nss_cash_getgrent_r(&g, tmp_small, sizeof(tmp_small), &errnop);
51 assert(s == NSS_STATUS_TRYAGAIN);
52 assert(errnop == ERANGE);
54 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
55 assert(s == NSS_STATUS_SUCCESS);
56 assert(!strcmp(g.gr_name, "root"));
57 assert(!strcmp(g.gr_passwd, "x"));
58 assert(g.gr_gid == 0);
59 assert(g.gr_mem != NULL);
60 assert(g.gr_mem[0] == NULL);
62 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
63 assert(s == NSS_STATUS_SUCCESS);
64 assert(!strcmp(g.gr_name, "daemon"));
65 assert(g.gr_gid == 1);
66 assert(g.gr_mem != NULL);
67 assert(!strcmp(g.gr_mem[0], "andariel"));
68 assert(!strcmp(g.gr_mem[1], "duriel"));
69 assert(!strcmp(g.gr_mem[2], "mephisto"));
70 assert(!strcmp(g.gr_mem[3], "diablo"));
71 assert(!strcmp(g.gr_mem[4], "baal"));
72 assert(g.gr_mem[5] == NULL);
73 for (int i = 0; i < 21; i++) {
74 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
75 assert(s == NSS_STATUS_SUCCESS);
77 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
78 assert(s == NSS_STATUS_SUCCESS);
79 assert(!strcmp(g.gr_name, "www-data"));
80 assert(!strcmp(g.gr_passwd, "x"));
81 assert(g.gr_gid == 33);
82 assert(g.gr_mem != NULL);
83 assert(!strcmp(g.gr_mem[0], "nobody"));
84 assert(g.gr_mem[1] == NULL);
85 for (int i = 0; i < 29; i++) {
86 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
87 assert(s == NSS_STATUS_SUCCESS);
89 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
90 assert(s == NSS_STATUS_SUCCESS);
91 assert(!strcmp(g.gr_name, "postfix"));
92 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
93 assert(s == NSS_STATUS_SUCCESS);
94 assert(!strcmp(g.gr_name, "postdrop"));
95 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
96 assert(s == NSS_STATUS_NOTFOUND);
97 assert(errnop == ENOENT);
99 s = _nss_cash_endgrent();
100 assert(s == NSS_STATUS_SUCCESS);
105 s = _nss_cash_setgrent(0);
106 assert(s == NSS_STATUS_SUCCESS);
108 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
109 assert(s == NSS_STATUS_SUCCESS);
110 assert(!strcmp(g.gr_name, "root"));
111 assert(!strcmp(g.gr_passwd, "x"));
112 assert(g.gr_gid == 0);
113 assert(g.gr_mem != NULL);
114 assert(g.gr_mem[0] == NULL);
116 s = _nss_cash_endgrent();
117 assert(s == NSS_STATUS_SUCCESS);
120 // Test proper reset the 2nd
122 s = _nss_cash_setgrent(0);
123 assert(s == NSS_STATUS_SUCCESS);
125 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
126 assert(s == NSS_STATUS_SUCCESS);
127 assert(!strcmp(g.gr_name, "root"));
128 assert(!strcmp(g.gr_passwd, "x"));
129 assert(g.gr_gid == 0);
130 assert(g.gr_mem != NULL);
131 assert(g.gr_mem[0] == NULL);
133 s = _nss_cash_endgrent();
134 assert(s == NSS_STATUS_SUCCESS);
137 // Test many rounds to check for open file leaks
138 for (int i = 0; i < 10000; i++) {
139 s = _nss_cash_setgrent(0);
140 assert(s == NSS_STATUS_SUCCESS);
142 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
143 assert(s == NSS_STATUS_SUCCESS);
144 assert(!strcmp(g.gr_name, "root"));
146 s = _nss_cash_endgrent();
147 assert(s == NSS_STATUS_SUCCESS);
151 // Test with cash file is not present
153 assert(rename("tests/group.nsscash", "tests/group.nsscash.tmp") == 0);
154 s = _nss_cash_setgrent(0);
155 assert(s == NSS_STATUS_SUCCESS);
156 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
157 assert(s == NSS_STATUS_UNAVAIL);
158 assert(errnop == ENOENT);
159 s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
160 assert(s == NSS_STATUS_UNAVAIL);
161 assert(errnop == ENOENT);
162 s = _nss_cash_endgrent();
163 assert(s == NSS_STATUS_SUCCESS);
164 assert(rename("tests/group.nsscash.tmp", "tests/group.nsscash") == 0);
167 static void test_getgrgid(void) {
174 s = _nss_cash_getgrgid_r(0, &g, tmp_small, sizeof(tmp_small), &errnop);
175 assert(s == NSS_STATUS_TRYAGAIN);
176 assert(errnop == ERANGE);
177 s = _nss_cash_getgrgid_r(14, &g, tmp_small, sizeof(tmp_small), &errnop);
178 assert(s == NSS_STATUS_NOTFOUND); // 14 does not exist
179 assert(errnop == ENOENT);
180 s = _nss_cash_getgrgid_r(65534, &g, tmp_small, sizeof(tmp_small), &errnop);
181 assert(s == NSS_STATUS_TRYAGAIN);
182 assert(errnop == ERANGE);
184 s = _nss_cash_getgrgid_r(0, &g, tmp, sizeof(tmp), &errnop);
185 assert(s == NSS_STATUS_SUCCESS);
186 assert(!strcmp(g.gr_name, "root"));
187 assert(!strcmp(g.gr_passwd, "x"));
188 assert(g.gr_gid == 0);
189 assert(g.gr_mem != NULL);
190 assert(g.gr_mem[0] == NULL);
192 s = _nss_cash_getgrgid_r(1, &g, tmp, sizeof(tmp), &errnop);
193 assert(s == NSS_STATUS_SUCCESS);
194 assert(!strcmp(g.gr_name, "daemon"));
195 assert(g.gr_gid == 1);
196 assert(g.gr_mem != NULL);
197 assert(!strcmp(g.gr_mem[0], "andariel"));
198 assert(!strcmp(g.gr_mem[1], "duriel"));
199 assert(!strcmp(g.gr_mem[2], "mephisto"));
200 assert(!strcmp(g.gr_mem[3], "diablo"));
201 assert(!strcmp(g.gr_mem[4], "baal"));
202 assert(g.gr_mem[5] == NULL);
204 s = _nss_cash_getgrgid_r(11, &g, tmp, sizeof(tmp), &errnop);
205 assert(s == NSS_STATUS_NOTFOUND);
206 assert(errnop == ENOENT);
208 s = _nss_cash_getgrgid_r(103, &g, tmp, sizeof(tmp), &errnop);
209 assert(s == NSS_STATUS_SUCCESS);
210 assert(!strcmp(g.gr_name, "systemd-network"));
211 assert(!strcmp(g.gr_passwd, "x"));
212 assert(g.gr_gid == 103);
213 assert(g.gr_mem != NULL);
214 assert(g.gr_mem[0] == NULL);
216 s = _nss_cash_getgrgid_r(107, &g, tmp, sizeof(tmp), &errnop);
217 assert(s == NSS_STATUS_SUCCESS);
218 assert(!strcmp(g.gr_name, "kvm"));
219 assert(!strcmp(g.gr_passwd, "x"));
220 assert(g.gr_gid == 107);
221 assert(g.gr_mem != NULL);
222 assert(g.gr_mem[0] == NULL);
224 s = _nss_cash_getgrgid_r(65534, &g, tmp, sizeof(tmp), &errnop);
225 assert(s == NSS_STATUS_SUCCESS);
226 assert(!strcmp(g.gr_name, "nogroup"));
227 assert(!strcmp(g.gr_passwd, "x"));
228 assert(g.gr_gid == 65534);
229 assert(g.gr_mem != NULL);
230 assert(g.gr_mem[0] == NULL);
232 s = _nss_cash_getgrgid_r(INT_MAX, &g, tmp, sizeof(tmp), &errnop);
233 assert(s == NSS_STATUS_NOTFOUND);
234 assert(errnop == ENOENT);
237 // Test with cash file is not present
239 assert(rename("tests/group.nsscash", "tests/group.nsscash.tmp") == 0);
240 s = _nss_cash_getgrgid_r(0, &g, tmp, sizeof(tmp), &errnop);
241 assert(s == NSS_STATUS_UNAVAIL);
242 assert(errnop == ENOENT);
243 s = _nss_cash_getgrgid_r(14, &g, tmp, sizeof(tmp), &errnop);
244 assert(s == NSS_STATUS_UNAVAIL);
245 assert(errnop == ENOENT);
246 assert(rename("tests/group.nsscash.tmp", "tests/group.nsscash") == 0);
249 static void test_getgrnam(void) {
256 s = _nss_cash_getgrnam_r("root", &g, tmp_small, sizeof(tmp_small), &errnop);
257 assert(s == NSS_STATUS_TRYAGAIN);
258 assert(errnop == ERANGE);
259 s = _nss_cash_getgrnam_r("nope", &g, tmp_small, sizeof(tmp_small), &errnop);
260 assert(s == NSS_STATUS_NOTFOUND); // does not exist
261 assert(errnop == ENOENT);
262 s = _nss_cash_getgrnam_r("nogroup", &g, tmp_small, sizeof(tmp_small), &errnop);
263 assert(s == NSS_STATUS_TRYAGAIN);
264 assert(errnop == ERANGE);
266 s = _nss_cash_getgrnam_r("root", &g, tmp, sizeof(tmp), &errnop);
267 assert(s == NSS_STATUS_SUCCESS);
268 assert(!strcmp(g.gr_name, "root"));
269 assert(!strcmp(g.gr_passwd, "x"));
270 assert(g.gr_gid == 0);
271 assert(g.gr_mem != NULL);
272 assert(g.gr_mem[0] == NULL);
274 s = _nss_cash_getgrnam_r("daemon", &g, tmp, sizeof(tmp), &errnop);
275 assert(s == NSS_STATUS_SUCCESS);
276 assert(!strcmp(g.gr_name, "daemon"));
277 assert(g.gr_gid == 1);
278 assert(g.gr_mem != NULL);
279 assert(!strcmp(g.gr_mem[0], "andariel"));
280 assert(!strcmp(g.gr_mem[1], "duriel"));
281 assert(!strcmp(g.gr_mem[2], "mephisto"));
282 assert(!strcmp(g.gr_mem[3], "diablo"));
283 assert(!strcmp(g.gr_mem[4], "baal"));
284 assert(g.gr_mem[5] == NULL);
286 s = _nss_cash_getgrnam_r("nope2", &g, tmp, sizeof(tmp), &errnop);
287 assert(s == NSS_STATUS_NOTFOUND);
288 assert(errnop == ENOENT);
290 s = _nss_cash_getgrnam_r("systemd-network", &g, tmp, sizeof(tmp), &errnop);
291 assert(s == NSS_STATUS_SUCCESS);
292 assert(!strcmp(g.gr_name, "systemd-network"));
293 assert(!strcmp(g.gr_passwd, "x"));
294 assert(g.gr_gid == 103);
295 assert(g.gr_mem != NULL);
296 assert(g.gr_mem[0] == NULL);
298 s = _nss_cash_getgrnam_r("postfix", &g, tmp, sizeof(tmp), &errnop);
299 assert(s == NSS_STATUS_SUCCESS);
300 assert(!strcmp(g.gr_name, "postfix"));
301 assert(!strcmp(g.gr_passwd, "x"));
302 assert(g.gr_gid == 114);
303 assert(g.gr_mem != NULL);
304 assert(g.gr_mem[0] == NULL);
306 s = _nss_cash_getgrnam_r("", &g, tmp, sizeof(tmp), &errnop);
307 assert(s == NSS_STATUS_NOTFOUND);
308 assert(errnop == ENOENT);
311 // Test with cash file is not present
313 assert(rename("tests/group.nsscash", "tests/group.nsscash.tmp") == 0);
314 s = _nss_cash_getgrnam_r("root", &g, tmp, sizeof(tmp), &errnop);
315 assert(s == NSS_STATUS_UNAVAIL);
316 assert(errnop == ENOENT);
317 s = _nss_cash_getgrnam_r("nope", &g, tmp, sizeof(tmp), &errnop);
318 assert(s == NSS_STATUS_UNAVAIL);
319 assert(errnop == ENOENT);
320 assert(rename("tests/group.nsscash.tmp", "tests/group.nsscash") == 0);
323 static void test_limits(void) {
324 char large_member[65525];
325 memset(large_member, 'X', sizeof(large_member));
326 large_member[sizeof(large_member)-1] = '\0';
328 char many_members[54603]; // 5461 members
329 memset(many_members, 'X', sizeof(many_members));
330 for (int i = 9; i < (int)sizeof(many_members); i += 10) {
331 many_members[i-1] = (char)('A' + i % ('Z' - 'A'));
332 many_members[i] = ',';
334 many_members[sizeof(many_members)-1] = '\0';
339 const char *nsscache_cmd = "../nsscash convert group "
340 "tests/limits tests/limits.nsscash 2> /dev/null";
342 // Entries which will not fit in uint16_t, nsscash must abort
344 fh = fopen("tests/limits", "w");
346 r = fprintf(fh, "test:x:42:A%s\n", large_member);
351 r = system(nsscache_cmd);
353 assert(WIFEXITED(r) && WEXITSTATUS(r) == 1);
355 fh = fopen("tests/limits", "w");
357 r = fprintf(fh, "many:x:4711:%s%s\n", many_members, many_members);
362 r = system(nsscache_cmd);
364 assert(WIFEXITED(r) && WEXITSTATUS(r) == 1);
366 // Largest entries which will fit
368 fh = fopen("tests/limits", "w");
370 r = fprintf(fh, "test:x:42:%s\n", large_member);
372 r = fprintf(fh, "many:x:4711:%s\n", many_members);
377 r = system(nsscache_cmd);
379 assert(WIFEXITED(r) && WEXITSTATUS(r) == 0);
381 r = rename("tests/group.nsscash", "tests/group.nsscash.tmp");
383 r = rename("tests/limits.nsscash", "tests/group.nsscash");
386 // Check if the entry can be retrieved
390 char tmp[sizeof(char **) + 1*sizeof(char *) + 1*sizeof(uint16_t) +
391 4+1 + 1+1 + 65525 + 1];
392 char tmp2[sizeof(char **) + 5462*sizeof(char *) + 5462*sizeof(uint16_t) +
393 4+1 + 1+1 + 54603 + 1];
396 s = _nss_cash_getgrgid_r(42, &g, tmp, sizeof(tmp), &errnop);
397 assert(s == NSS_STATUS_SUCCESS);
398 assert(!strcmp(g.gr_name, "test"));
399 assert(g.gr_gid == 42);
400 assert(g.gr_mem != NULL);
401 assert(!strcmp(g.gr_mem[0], large_member));
402 assert(g.gr_mem[1] == NULL);
404 s = _nss_cash_getgrgid_r(4711, &g, tmp2, sizeof(tmp2), &errnop);
405 assert(s == NSS_STATUS_SUCCESS);
406 assert(!strcmp(g.gr_name, "many"));
407 assert(g.gr_gid == 4711);
408 assert(g.gr_mem != NULL);
409 for (int i = 0; i < 5461-1; i++) {
411 memset(x, 'X', sizeof(x));
412 x[8] = (char)('A' + (i * 10 + 9) % ('Z' - 'A'));
414 assert(!strcmp(g.gr_mem[i], x));
416 assert(!strcmp(g.gr_mem[5461-1], "XX"));
417 assert(g.gr_mem[5461] == NULL);
419 r = rename("tests/group.nsscash.tmp", "tests/group.nsscash");
422 r = unlink("tests/limits");