]> ruderich.org/simon Gitweb - nsscash/nsscash.git/blob - nss/tests/gr.c
nss/tests: add missing headers for WIFEXITED/WEXITSTATUS
[nsscash/nsscash.git] / nss / tests / gr.c
1 /*
2  * Tests for the NSS cash module
3  *
4  * Copyright (C) 2019-2020  Simon Ruderich
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 #include <assert.h>
21 #include <errno.h>
22 #include <limits.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/types.h>
27 #include <sys/wait.h>
28 #include <unistd.h>
29
30 #include "../cash_nss.h"
31
32
33 static void test_getgrent(void) {
34     struct group g;
35     enum nss_status s;
36     char tmp[1024];
37     char tmp_small[10];
38     int errnop = 0;
39
40     // Test one setgrent/getgrent/endgrent round
41
42     s = _nss_cash_setgrent(0);
43     assert(s == NSS_STATUS_SUCCESS);
44
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);
55
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);
63
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);
78     }
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);
90     }
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);
100
101     s = _nss_cash_endgrent();
102     assert(s == NSS_STATUS_SUCCESS);
103
104
105     // Test proper reset
106
107     s = _nss_cash_setgrent(0);
108     assert(s == NSS_STATUS_SUCCESS);
109
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);
117
118     s = _nss_cash_endgrent();
119     assert(s == NSS_STATUS_SUCCESS);
120
121
122     // Test proper reset the 2nd
123
124     s = _nss_cash_setgrent(0);
125     assert(s == NSS_STATUS_SUCCESS);
126
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);
134
135     s = _nss_cash_endgrent();
136     assert(s == NSS_STATUS_SUCCESS);
137
138
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);
143
144         s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
145         assert(s == NSS_STATUS_SUCCESS);
146         assert(!strcmp(g.gr_name, "root"));
147
148         s = _nss_cash_endgrent();
149         assert(s == NSS_STATUS_SUCCESS);
150     }
151
152
153     // Test with cash file is not present
154
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);
167 }
168
169 static void test_getgrgid(void) {
170     struct group g;
171     enum nss_status s;
172     char tmp[1024];
173     char tmp_small[10];
174     int errnop = 0;
175
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);
185
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);
193
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);
205
206     s = _nss_cash_getgrgid_r(11, &g, tmp, sizeof(tmp), &errnop);
207     assert(s == NSS_STATUS_NOTFOUND);
208     assert(errnop == ENOENT);
209
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);
217
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);
225
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);
233
234     s = _nss_cash_getgrgid_r(INT_MAX, &g, tmp, sizeof(tmp), &errnop);
235     assert(s == NSS_STATUS_NOTFOUND);
236     assert(errnop == ENOENT);
237
238
239     // Test with cash file is not present
240
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);
249 }
250
251 static void test_getgrnam(void) {
252     struct group g;
253     enum nss_status s;
254     char tmp[1024];
255     char tmp_small[10];
256     int errnop = 0;
257
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);
267
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);
275
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);
287
288     s = _nss_cash_getgrnam_r("nope2", &g, tmp, sizeof(tmp), &errnop);
289     assert(s == NSS_STATUS_NOTFOUND);
290     assert(errnop == ENOENT);
291
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);
299
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);
307
308     s = _nss_cash_getgrnam_r("", &g, tmp, sizeof(tmp), &errnop);
309     assert(s == NSS_STATUS_NOTFOUND);
310     assert(errnop == ENOENT);
311
312
313     // Test with cash file is not present
314
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);
323 }
324
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';
329
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] = ',';
335     }
336     many_members[sizeof(many_members)-1] = '\0';
337
338     int r;
339     FILE *fh;
340
341     const char *nsscash_cmd = "../nsscash convert group "
342         "tests/limits tests/limits.nsscash 2> /dev/null";
343
344     // Entries which will not fit in uint16_t, nsscash must abort
345
346     fh = fopen("tests/limits", "w");
347     assert(fh != NULL);
348     r = fprintf(fh, "test:x:42:A%s\n", large_member);
349     assert(r == 65536);
350     r = fclose(fh);
351     assert(r == 0);
352
353     r = system(nsscash_cmd);
354     assert(r != -1);
355     assert(WIFEXITED(r) && WEXITSTATUS(r) == 1);
356
357     fh = fopen("tests/limits", "w");
358     assert(fh != NULL);
359     r = fprintf(fh, "many:x:4711:%s%s\n", many_members, many_members);
360     assert(r == 109217);
361     r = fclose(fh);
362     assert(r == 0);
363
364     r = system(nsscash_cmd);
365     assert(r != -1);
366     assert(WIFEXITED(r) && WEXITSTATUS(r) == 1);
367
368     // Largest entries which will fit
369
370     fh = fopen("tests/limits", "w");
371     assert(fh != NULL);
372     r = fprintf(fh, "test:x:42:%s\n", large_member);
373     assert(r == 65535);
374     r = fprintf(fh, "many:x:4711:%s\n", many_members);
375     assert(r == 54615);
376     r = fclose(fh);
377     assert(r == 0);
378
379     r = system(nsscash_cmd);
380     assert(r != -1);
381     assert(WIFEXITED(r) && WEXITSTATUS(r) == 0);
382
383     r = rename("tests/group.nsscash", "tests/group.nsscash.tmp");
384     assert(r == 0);
385     r = rename("tests/limits.nsscash", "tests/group.nsscash");
386     assert(r == 0);
387
388     // Check if the entry can be retrieved
389
390     struct group g;
391     enum nss_status s;
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];
396     int errnop = 0;
397
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);
405
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++) {
412         char x[9+1];
413         memset(x, 'X', sizeof(x));
414         x[8] = (char)('A' + (i * 10 + 9) % ('Z' - 'A'));
415         x[9] = '\0';
416         assert(!strcmp(g.gr_mem[i], x));
417     }
418     assert(!strcmp(g.gr_mem[5461-1], "XX"));
419     assert(g.gr_mem[5461] == NULL);
420
421     r = rename("tests/group.nsscash.tmp", "tests/group.nsscash");
422     assert(r == 0);
423
424     r = unlink("tests/limits");
425     assert(r == 0);
426 }
427
428 int main(void) {
429     test_getgrent();
430     test_getgrgid();
431     test_getgrnam();
432
433     test_limits();
434
435     return EXIT_SUCCESS;
436 }