]> ruderich.org/simon Gitweb - nsscash/nsscash.git/blob - nss/tests/gr.c
487e673b2d84f3363f8363f50ed9a9d9893a289a
[nsscash/nsscash.git] / nss / tests / gr.c
1 /*
2  * Tests for the NSS cash module
3  *
4  * Copyright (C) 2019  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 <unistd.h>
27
28 #include "../cash_nss.h"
29
30
31 static void test_getgrent(void) {
32     struct group g;
33     enum nss_status s;
34     char tmp[1024];
35     char tmp_small[10];
36     int errnop = 0;
37
38     // Test one setgrent/getgrent/endgrent round
39
40     s = _nss_cash_setgrent(0);
41     assert(s == NSS_STATUS_SUCCESS);
42
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);
53
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);
61
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);
76     }
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);
88     }
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);
98
99     s = _nss_cash_endgrent();
100     assert(s == NSS_STATUS_SUCCESS);
101
102
103     // Test proper reset
104
105     s = _nss_cash_setgrent(0);
106     assert(s == NSS_STATUS_SUCCESS);
107
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);
115
116     s = _nss_cash_endgrent();
117     assert(s == NSS_STATUS_SUCCESS);
118
119
120     // Test proper reset the 2nd
121
122     s = _nss_cash_setgrent(0);
123     assert(s == NSS_STATUS_SUCCESS);
124
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);
132
133     s = _nss_cash_endgrent();
134     assert(s == NSS_STATUS_SUCCESS);
135
136
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);
141
142         s = _nss_cash_getgrent_r(&g, tmp, sizeof(tmp), &errnop);
143         assert(s == NSS_STATUS_SUCCESS);
144         assert(!strcmp(g.gr_name, "root"));
145
146         s = _nss_cash_endgrent();
147         assert(s == NSS_STATUS_SUCCESS);
148     }
149
150
151     // Test with cash file is not present
152
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);
165 }
166
167 static void test_getgrgid(void) {
168     struct group g;
169     enum nss_status s;
170     char tmp[1024];
171     char tmp_small[10];
172     int errnop = 0;
173
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);
183
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);
191
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);
203
204     s = _nss_cash_getgrgid_r(11, &g, tmp, sizeof(tmp), &errnop);
205     assert(s == NSS_STATUS_NOTFOUND);
206     assert(errnop == ENOENT);
207
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);
215
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);
223
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);
231
232     s = _nss_cash_getgrgid_r(INT_MAX, &g, tmp, sizeof(tmp), &errnop);
233     assert(s == NSS_STATUS_NOTFOUND);
234     assert(errnop == ENOENT);
235
236
237     // Test with cash file is not present
238
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);
247 }
248
249 static void test_getgrnam(void) {
250     struct group g;
251     enum nss_status s;
252     char tmp[1024];
253     char tmp_small[10];
254     int errnop = 0;
255
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);
265
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);
273
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);
285
286     s = _nss_cash_getgrnam_r("nope2", &g, tmp, sizeof(tmp), &errnop);
287     assert(s == NSS_STATUS_NOTFOUND);
288     assert(errnop == ENOENT);
289
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);
297
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);
305
306     s = _nss_cash_getgrnam_r("", &g, tmp, sizeof(tmp), &errnop);
307     assert(s == NSS_STATUS_NOTFOUND);
308     assert(errnop == ENOENT);
309
310
311     // Test with cash file is not present
312
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);
321 }
322
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';
327
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] = ',';
333     }
334     many_members[sizeof(many_members)-1] = '\0';
335
336     int r;
337     FILE *fh;
338
339     const char *nsscache_cmd = "../nsscash convert group "
340         "tests/limits tests/limits.nsscash 2> /dev/null";
341
342     // Entries which will not fit in uint16_t, nsscash must abort
343
344     fh = fopen("tests/limits", "w");
345     assert(fh != NULL);
346     r = fprintf(fh, "test:x:42:A%s\n", large_member);
347     assert(r == 65536);
348     r = fclose(fh);
349     assert(r == 0);
350
351     r = system(nsscache_cmd);
352     assert(r != -1);
353     assert(WIFEXITED(r) && WEXITSTATUS(r) == 1);
354
355     fh = fopen("tests/limits", "w");
356     assert(fh != NULL);
357     r = fprintf(fh, "many:x:4711:%s%s\n", many_members, many_members);
358     assert(r == 109217);
359     r = fclose(fh);
360     assert(r == 0);
361
362     r = system(nsscache_cmd);
363     assert(r != -1);
364     assert(WIFEXITED(r) && WEXITSTATUS(r) == 1);
365
366     // Largest entries which will fit
367
368     fh = fopen("tests/limits", "w");
369     assert(fh != NULL);
370     r = fprintf(fh, "test:x:42:%s\n", large_member);
371     assert(r == 65535);
372     r = fprintf(fh, "many:x:4711:%s\n", many_members);
373     assert(r == 54615);
374     r = fclose(fh);
375     assert(r == 0);
376
377     r = system(nsscache_cmd);
378     assert(r != -1);
379     assert(WIFEXITED(r) && WEXITSTATUS(r) == 0);
380
381     r = rename("tests/group.nsscash", "tests/group.nsscash.tmp");
382     assert(r == 0);
383     r = rename("tests/limits.nsscash", "tests/group.nsscash");
384     assert(r == 0);
385
386     // Check if the entry can be retrieved
387
388     struct group g;
389     enum nss_status s;
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];
394     int errnop = 0;
395
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);
403
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++) {
410         char x[9+1];
411         memset(x, 'X', sizeof(x));
412         x[8] = (char)('A' + (i * 10 + 9) % ('Z' - 'A'));
413         x[9] = '\0';
414         assert(!strcmp(g.gr_mem[i], x));
415     }
416     assert(!strcmp(g.gr_mem[5461-1], "XX"));
417     assert(g.gr_mem[5461] == NULL);
418
419     r = rename("tests/group.nsscash.tmp", "tests/group.nsscash");
420     assert(r == 0);
421
422     r = unlink("tests/limits");
423     assert(r == 0);
424 }
425
426 int main(void) {
427     test_getgrent();
428     test_getgrgid();
429     test_getgrnam();
430
431     test_limits();
432
433     return EXIT_SUCCESS;
434 }