]> ruderich.org/simon Gitweb - nsscash/nsscash.git/blob - nss/tests/pw.c
nss/tests: add missing headers for WIFEXITED/WEXITSTATUS
[nsscash/nsscash.git] / nss / tests / pw.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_getpwent(void) {
34     struct passwd p;
35     enum nss_status s;
36     char tmp[1024];
37     char tmp_small[10];
38     int errnop = 0;
39
40     // Test one setpwent/getpwent/endpwent round
41
42     s = _nss_cash_setpwent(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_getpwent_r(&p, tmp_small, sizeof(tmp_small), &errnop);
47     assert(s == NSS_STATUS_TRYAGAIN);
48     assert(errnop == ERANGE);
49     s = _nss_cash_getpwent_r(&p, tmp_small, sizeof(tmp_small), &errnop);
50     assert(s == NSS_STATUS_TRYAGAIN);
51     assert(errnop == ERANGE);
52     s = _nss_cash_getpwent_r(&p, tmp_small, sizeof(tmp_small), &errnop);
53     assert(s == NSS_STATUS_TRYAGAIN);
54     assert(errnop == ERANGE);
55
56     s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
57     assert(s == NSS_STATUS_SUCCESS);
58     assert(!strcmp(p.pw_name, "root"));
59     assert(!strcmp(p.pw_passwd, "x"));
60     assert(p.pw_uid == 0);
61     assert(p.pw_gid == 0);
62     assert(!strcmp(p.pw_gecos, "root"));
63     assert(!strcmp(p.pw_dir, "/root"));
64     assert(!strcmp(p.pw_shell, "/bin/bash"));
65
66     s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
67     assert(s == NSS_STATUS_SUCCESS);
68     assert(!strcmp(p.pw_name, "daemon"));
69     for (int i = 0; i < 10; i++) {
70         s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
71         assert(s == NSS_STATUS_SUCCESS);
72     }
73     s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
74     assert(s == NSS_STATUS_SUCCESS);
75     assert(!strcmp(p.pw_name, "www-data"));
76     for (int i = 0; i < 12; i++) {
77         s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
78         assert(s == NSS_STATUS_SUCCESS);
79     }
80     s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
81     assert(s == NSS_STATUS_SUCCESS);
82     assert(!strcmp(p.pw_name, "_rpc"));
83     s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
84     assert(s == NSS_STATUS_SUCCESS);
85     assert(!strcmp(p.pw_name, "postfix"));
86     s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
87     assert(s == NSS_STATUS_NOTFOUND);
88     assert(errnop == ENOENT);
89
90     s = _nss_cash_endpwent();
91     assert(s == NSS_STATUS_SUCCESS);
92
93
94     // Test proper reset
95
96     s = _nss_cash_setpwent(0);
97     assert(s == NSS_STATUS_SUCCESS);
98
99     s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
100     assert(s == NSS_STATUS_SUCCESS);
101     assert(!strcmp(p.pw_name, "root"));
102     assert(!strcmp(p.pw_passwd, "x"));
103     assert(p.pw_uid == 0);
104     assert(p.pw_gid == 0);
105     assert(!strcmp(p.pw_gecos, "root"));
106     assert(!strcmp(p.pw_dir, "/root"));
107     assert(!strcmp(p.pw_shell, "/bin/bash"));
108
109     s = _nss_cash_endpwent();
110     assert(s == NSS_STATUS_SUCCESS);
111
112
113     // Test proper reset the 2nd
114
115     s = _nss_cash_setpwent(0);
116     assert(s == NSS_STATUS_SUCCESS);
117
118     s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
119     assert(s == NSS_STATUS_SUCCESS);
120     assert(!strcmp(p.pw_name, "root"));
121     assert(!strcmp(p.pw_passwd, "x"));
122     assert(p.pw_uid == 0);
123     assert(p.pw_gid == 0);
124     assert(!strcmp(p.pw_gecos, "root"));
125     assert(!strcmp(p.pw_dir, "/root"));
126     assert(!strcmp(p.pw_shell, "/bin/bash"));
127
128     s = _nss_cash_endpwent();
129     assert(s == NSS_STATUS_SUCCESS);
130
131
132     // Test many rounds to check for open file leaks
133     for (int i = 0; i < 10000; i++) {
134         s = _nss_cash_setpwent(0);
135         assert(s == NSS_STATUS_SUCCESS);
136
137         s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
138         assert(s == NSS_STATUS_SUCCESS);
139         assert(!strcmp(p.pw_name, "root"));
140
141         s = _nss_cash_endpwent();
142         assert(s == NSS_STATUS_SUCCESS);
143     }
144
145
146     // Test with cash file is not present
147
148     assert(rename("tests/passwd.nsscash", "tests/passwd.nsscash.tmp") == 0);
149     s = _nss_cash_setpwent(0);
150     assert(s == NSS_STATUS_SUCCESS);
151     s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
152     assert(s == NSS_STATUS_UNAVAIL);
153     assert(errnop == ENOENT);
154     s = _nss_cash_getpwent_r(&p, tmp, sizeof(tmp), &errnop);
155     assert(s == NSS_STATUS_UNAVAIL);
156     assert(errnop == ENOENT);
157     s = _nss_cash_endpwent();
158     assert(s == NSS_STATUS_SUCCESS);
159     assert(rename("tests/passwd.nsscash.tmp", "tests/passwd.nsscash") == 0);
160 }
161
162 static void test_getpwuid(void) {
163     struct passwd p;
164     enum nss_status s;
165     char tmp[1024];
166     char tmp_small[10];
167     int errnop = 0;
168
169     s = _nss_cash_getpwuid_r(0, &p, tmp_small, sizeof(tmp_small), &errnop);
170     assert(s == NSS_STATUS_TRYAGAIN);
171     assert(errnop == ERANGE);
172     s = _nss_cash_getpwuid_r(42, &p, tmp_small, sizeof(tmp_small), &errnop);
173     assert(s == NSS_STATUS_NOTFOUND); // 42 does not exist
174     assert(errnop == ENOENT);
175     s = _nss_cash_getpwuid_r(65534, &p, tmp_small, sizeof(tmp_small), &errnop);
176     assert(s == NSS_STATUS_TRYAGAIN);
177     assert(errnop == ERANGE);
178
179     s = _nss_cash_getpwuid_r(0, &p, tmp, sizeof(tmp), &errnop);
180     assert(s == NSS_STATUS_SUCCESS);
181     assert(!strcmp(p.pw_name, "root"));
182     assert(!strcmp(p.pw_passwd, "x"));
183     assert(p.pw_uid == 0);
184     assert(p.pw_gid == 0);
185     assert(!strcmp(p.pw_gecos, "root"));
186     assert(!strcmp(p.pw_dir, "/root"));
187     assert(!strcmp(p.pw_shell, "/bin/bash"));
188
189     s = _nss_cash_getpwuid_r(1, &p, tmp, sizeof(tmp), &errnop);
190     assert(s == NSS_STATUS_SUCCESS);
191     assert(!strcmp(p.pw_name, "daemon"));
192     assert(!strcmp(p.pw_passwd, "x"));
193     assert(p.pw_uid == 1);
194     assert(p.pw_gid == 1);
195     assert(!strcmp(p.pw_gecos, "daemon"));
196     assert(!strcmp(p.pw_dir, "/usr/sbin"));
197     assert(!strcmp(p.pw_shell, "/usr/sbin/nologin"));
198
199     s = _nss_cash_getpwuid_r(11, &p, tmp, sizeof(tmp), &errnop);
200     assert(s == NSS_STATUS_NOTFOUND);
201     assert(errnop == ENOENT);
202
203     s = _nss_cash_getpwuid_r(102, &p, tmp, sizeof(tmp), &errnop);
204     assert(s == NSS_STATUS_SUCCESS);
205     assert(!strcmp(p.pw_name, "systemd-network"));
206     assert(!strcmp(p.pw_passwd, "x"));
207     assert(p.pw_uid == 102);
208     assert(p.pw_gid == 103);
209     assert(!strcmp(p.pw_gecos, "systemd Network Management,,,"));
210     assert(!strcmp(p.pw_dir, "/run/systemd"));
211     assert(!strcmp(p.pw_shell, "/usr/sbin/nologin"));
212
213     s = _nss_cash_getpwuid_r(107, &p, tmp, sizeof(tmp), &errnop);
214     assert(s == NSS_STATUS_SUCCESS);
215     assert(!strcmp(p.pw_name, "postfix"));
216     assert(!strcmp(p.pw_passwd, "x"));
217     assert(p.pw_uid == 107);
218     assert(p.pw_gid == 114);
219     assert(!strcmp(p.pw_gecos, ""));
220     assert(!strcmp(p.pw_dir, "/var/spool/postfix"));
221     assert(!strcmp(p.pw_shell, "/usr/sbin/nologin"));
222
223     s = _nss_cash_getpwuid_r(INT_MAX, &p, tmp, sizeof(tmp), &errnop);
224     assert(s == NSS_STATUS_NOTFOUND);
225     assert(errnop == ENOENT);
226
227
228     // Test with cash file is not present
229
230     assert(rename("tests/passwd.nsscash", "tests/passwd.nsscash.tmp") == 0);
231     s = _nss_cash_getpwuid_r(0, &p, tmp, sizeof(tmp), &errnop);
232     assert(s == NSS_STATUS_UNAVAIL);
233     assert(errnop == ENOENT);
234     s = _nss_cash_getpwuid_r(42, &p, tmp, sizeof(tmp), &errnop);
235     assert(s == NSS_STATUS_UNAVAIL);
236     assert(errnop == ENOENT);
237     assert(rename("tests/passwd.nsscash.tmp", "tests/passwd.nsscash") == 0);
238 }
239
240 static void test_getpwnam(void) {
241     struct passwd p;
242     enum nss_status s;
243     char tmp[1024];
244     char tmp_small[10];
245     int errnop = 0;
246
247     s = _nss_cash_getpwnam_r("root", &p, tmp_small, sizeof(tmp_small), &errnop);
248     assert(s == NSS_STATUS_TRYAGAIN);
249     assert(errnop == ERANGE);
250     s = _nss_cash_getpwnam_r("nope", &p, tmp_small, sizeof(tmp_small), &errnop);
251     assert(s == NSS_STATUS_NOTFOUND); // does not exist
252     assert(errnop == ENOENT);
253     s = _nss_cash_getpwnam_r("nobody", &p, tmp_small, sizeof(tmp_small), &errnop);
254     assert(s == NSS_STATUS_TRYAGAIN);
255     assert(errnop == ERANGE);
256
257     s = _nss_cash_getpwnam_r("root", &p, tmp, sizeof(tmp), &errnop);
258     assert(s == NSS_STATUS_SUCCESS);
259     assert(!strcmp(p.pw_name, "root"));
260     assert(!strcmp(p.pw_passwd, "x"));
261     assert(p.pw_uid == 0);
262     assert(p.pw_gid == 0);
263     assert(!strcmp(p.pw_gecos, "root"));
264     assert(!strcmp(p.pw_dir, "/root"));
265     assert(!strcmp(p.pw_shell, "/bin/bash"));
266
267     s = _nss_cash_getpwnam_r("daemon", &p, tmp, sizeof(tmp), &errnop);
268     assert(s == NSS_STATUS_SUCCESS);
269     assert(!strcmp(p.pw_name, "daemon"));
270     assert(!strcmp(p.pw_passwd, "x"));
271     assert(p.pw_uid == 1);
272     assert(p.pw_gid == 1);
273     assert(!strcmp(p.pw_gecos, "daemon"));
274     assert(!strcmp(p.pw_dir, "/usr/sbin"));
275     assert(!strcmp(p.pw_shell, "/usr/sbin/nologin"));
276
277     s = _nss_cash_getpwnam_r("nope2", &p, tmp, sizeof(tmp), &errnop);
278     assert(s == NSS_STATUS_NOTFOUND);
279     assert(errnop == ENOENT);
280
281     s = _nss_cash_getpwnam_r("systemd-network", &p, tmp, sizeof(tmp), &errnop);
282     assert(s == NSS_STATUS_SUCCESS);
283     assert(!strcmp(p.pw_name, "systemd-network"));
284     assert(!strcmp(p.pw_passwd, "x"));
285     assert(p.pw_uid == 102);
286     assert(p.pw_gid == 103);
287     assert(!strcmp(p.pw_gecos, "systemd Network Management,,,"));
288     assert(!strcmp(p.pw_dir, "/run/systemd"));
289     assert(!strcmp(p.pw_shell, "/usr/sbin/nologin"));
290
291     s = _nss_cash_getpwnam_r("postfix", &p, tmp, sizeof(tmp), &errnop);
292     assert(s == NSS_STATUS_SUCCESS);
293     assert(!strcmp(p.pw_name, "postfix"));
294     assert(!strcmp(p.pw_passwd, "x"));
295     assert(p.pw_uid == 107);
296     assert(p.pw_gid == 114);
297     assert(!strcmp(p.pw_gecos, ""));
298     assert(!strcmp(p.pw_dir, "/var/spool/postfix"));
299     assert(!strcmp(p.pw_shell, "/usr/sbin/nologin"));
300
301     s = _nss_cash_getpwnam_r("", &p, tmp, sizeof(tmp), &errnop);
302     assert(s == NSS_STATUS_NOTFOUND);
303     assert(errnop == ENOENT);
304
305
306     // Test with cash file is not present
307
308     assert(rename("tests/passwd.nsscash", "tests/passwd.nsscash.tmp") == 0);
309     s = _nss_cash_getpwnam_r("root", &p, tmp, sizeof(tmp), &errnop);
310     assert(s == NSS_STATUS_UNAVAIL);
311     assert(errnop == ENOENT);
312     s = _nss_cash_getpwnam_r("nope", &p, tmp, sizeof(tmp), &errnop);
313     assert(s == NSS_STATUS_UNAVAIL);
314     assert(errnop == ENOENT);
315     assert(rename("tests/passwd.nsscash.tmp", "tests/passwd.nsscash") == 0);
316 }
317
318 static void test_limits(void) {
319     char gecos[65508];
320     memset(gecos, 'X', sizeof(gecos));
321     gecos[sizeof(gecos)-1] = '\0';
322
323     int r;
324     FILE *fh;
325
326     const char *nsscash_cmd = "../nsscash convert passwd "
327         "tests/limits tests/limits.nsscash 2> /dev/null";
328
329     // Entries which will not fit in uint16_t, nsscash must abort
330
331     fh = fopen("tests/limits", "w");
332     assert(fh != NULL);
333     r = fprintf(fh, "test:xx:42:4711:%s:/home/test:/bin/zsh\n", gecos);
334     assert(r == 65544);
335     r = fclose(fh);
336     assert(r == 0);
337
338     r = system(nsscash_cmd);
339     assert(r != -1);
340     assert(WIFEXITED(r) && WEXITSTATUS(r) == 1);
341
342     fh = fopen("tests/limits", "w");
343     assert(fh != NULL);
344     r = fprintf(fh, "test:%s:42:4711:%s:/home/test:/bin/zsh\n", gecos, gecos);
345     assert(r == 131049);
346     r = fclose(fh);
347     assert(r == 0);
348
349     r = system(nsscash_cmd);
350     assert(r != -1);
351     assert(WIFEXITED(r) && WEXITSTATUS(r) == 1);
352
353     // Largest entry which will fit
354
355     fh = fopen("tests/limits", "w");
356     assert(fh != NULL);
357     r = fprintf(fh, "test:x:42:4711:%s:/home/test:/bin/zsh\n", gecos);
358     assert(r == 65543);
359     r = fclose(fh);
360     assert(r == 0);
361
362     r = system(nsscash_cmd);
363     assert(r != -1);
364     assert(WIFEXITED(r) && WEXITSTATUS(r) == 0);
365
366     r = rename("tests/passwd.nsscash", "tests/passwd.nsscash.tmp");
367     assert(r == 0);
368     r = rename("tests/limits.nsscash", "tests/passwd.nsscash");
369     assert(r == 0);
370
371     // Check if the entry can be retrieved
372
373     struct passwd p;
374     enum nss_status s;
375     char tmp[4+1 + 1+1 + 65508 + 10+1 + 8+1];
376     int errnop = 0;
377
378     s = _nss_cash_getpwuid_r(42, &p, tmp, sizeof(tmp), &errnop);
379     assert(s == NSS_STATUS_SUCCESS);
380     assert(!strcmp(p.pw_name, "test"));
381     assert(!strcmp(p.pw_passwd, "x"));
382     assert(p.pw_uid == 42);
383     assert(p.pw_gid == 4711);
384     assert(!strcmp(p.pw_gecos, gecos));
385     assert(!strcmp(p.pw_dir, "/home/test"));
386     assert(!strcmp(p.pw_shell, "/bin/zsh"));
387
388     r = rename("tests/passwd.nsscash.tmp", "tests/passwd.nsscash");
389     assert(r == 0);
390
391     r = unlink("tests/limits");
392     assert(r == 0);
393 }
394
395 int main(void) {
396     test_getpwent();
397     test_getpwuid();
398     test_getpwnam();
399
400     test_limits();
401
402     return EXIT_SUCCESS;
403 }