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