1 | /* Test that nsswitch.conf reloading actually works. |
2 | Copyright (C) 2020-2024 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <nss.h> |
20 | #include <stdio.h> |
21 | #include <stdlib.h> |
22 | #include <string.h> |
23 | #include <sys/types.h> |
24 | #include <errno.h> |
25 | #include <pwd.h> |
26 | |
27 | #include <support/support.h> |
28 | #include <support/check.h> |
29 | |
30 | #include "nss_test.h" |
31 | |
32 | /* Size of buffers used by *_r functions. */ |
33 | #define TESTBUFLEN 4096 |
34 | |
35 | static struct passwd pwd_table_1[] = { |
36 | PWD (100), |
37 | PWD (30), |
38 | PWD (200), |
39 | PWD (60), |
40 | PWD (20000), |
41 | PWD_LAST () |
42 | }; |
43 | |
44 | static const char *hostaddr_5[] = |
45 | { |
46 | "ABCd" , "ABCD" , "ABC4" , NULL |
47 | }; |
48 | |
49 | static const char *hostaddr_15[] = |
50 | { |
51 | "4321" , "4322" , NULL |
52 | }; |
53 | |
54 | static const char *hostaddr_25[] = |
55 | { |
56 | "WXYZ" , NULL |
57 | }; |
58 | |
59 | |
60 | static struct hostent host_table_1[] = { |
61 | HOST (5), |
62 | HOST (15), |
63 | HOST (25), |
64 | HOST_LAST () |
65 | }; |
66 | |
67 | void |
68 | _nss_test1_init_hook(test_tables *t) |
69 | { |
70 | t->pwd_table = pwd_table_1; |
71 | t->host_table = host_table_1; |
72 | } |
73 | |
74 | /* The first of these must not appear in pwd_table_1. */ |
75 | static struct passwd pwd_table_2[] = { |
76 | PWD (5), |
77 | PWD_N(200, "name30" ), |
78 | PWD (16), |
79 | PWD_LAST () |
80 | }; |
81 | |
82 | static const char *hostaddr_6[] = |
83 | { |
84 | "mnop" , NULL |
85 | }; |
86 | |
87 | static const char *hostaddr_16[] = |
88 | { |
89 | "7890" , "7891" , NULL |
90 | }; |
91 | |
92 | static const char *hostaddr_26[] = |
93 | { |
94 | "qwer" , "qweR" , NULL |
95 | }; |
96 | |
97 | static struct hostent host_table_2[] = { |
98 | HOST (6), |
99 | HOST (16), |
100 | HOST (26), |
101 | HOST_LAST () |
102 | }; |
103 | |
104 | void |
105 | _nss_test2_init_hook(test_tables *t) |
106 | { |
107 | t->pwd_table = pwd_table_2; |
108 | t->host_table = host_table_2; |
109 | } |
110 | |
111 | static void |
112 | must_be_tests (struct passwd *pt, struct hostent *ht) |
113 | { |
114 | int i; |
115 | struct hostent *h; |
116 | |
117 | struct passwd *p; |
118 | for (i = 0; !PWD_ISLAST (&pt[i]); ++i) |
119 | { |
120 | p = getpwuid (uid: pt[i].pw_uid); |
121 | TEST_VERIFY (p != NULL); |
122 | if (p != NULL) |
123 | { |
124 | TEST_COMPARE_STRING (p->pw_name, pt[i].pw_name); |
125 | } |
126 | } |
127 | |
128 | setpwent (); |
129 | for (i = 0; !PWD_ISLAST (&pt[i]); ++i) |
130 | { |
131 | p = getpwent (); |
132 | TEST_VERIFY (p != NULL); |
133 | if (p != NULL) |
134 | { |
135 | TEST_COMPARE_STRING (p->pw_name, pt[i].pw_name); |
136 | TEST_COMPARE (p->pw_uid, pt[i].pw_uid); |
137 | } |
138 | } |
139 | endpwent (); |
140 | |
141 | for (i = 0; !HOST_ISLAST (&ht[i]); ++i) |
142 | { |
143 | h = gethostbyname (name: ht[i].h_name); |
144 | TEST_VERIFY (h != NULL); |
145 | if (h != NULL) |
146 | { |
147 | TEST_COMPARE_STRING (h->h_name, ht[i].h_name); |
148 | TEST_COMPARE (h->h_addrtype, AF_INET); |
149 | TEST_VERIFY (h->h_addr_list[0] != NULL); |
150 | if (h->h_addr_list[0] != NULL) |
151 | TEST_COMPARE_BLOB (h->h_addr_list[0], h->h_length, |
152 | ht[i].h_addr_list[0], ht[i].h_length); |
153 | } |
154 | } |
155 | |
156 | for (i = 0; !HOST_ISLAST (&ht[i]); ++i) |
157 | { |
158 | struct hostent r, *rp; |
159 | char buf[TESTBUFLEN]; |
160 | int herrno, res; |
161 | |
162 | res = gethostbyname2_r (name: ht[i].h_name, AF_INET, |
163 | result_buf: &r, buf: buf, TESTBUFLEN, result: &rp, h_errnop: &herrno); |
164 | TEST_COMPARE (res, 0); |
165 | if (res == 0) |
166 | { |
167 | TEST_COMPARE_STRING (r.h_name, ht[i].h_name); |
168 | TEST_COMPARE (r.h_addrtype, AF_INET); |
169 | TEST_VERIFY (r.h_addr_list[0] != NULL); |
170 | if (r.h_addr_list[0] != NULL) |
171 | TEST_COMPARE_BLOB (r.h_addr_list[0], r.h_length, |
172 | ht[i].h_addr_list[0], ht[i].h_length); |
173 | } |
174 | } |
175 | |
176 | for (i = 0; !HOST_ISLAST (&ht[i]); ++i) |
177 | { |
178 | h = gethostbyaddr (addr: ht[i].h_addr, len: 4, AF_INET); |
179 | TEST_VERIFY (h != NULL); |
180 | if (h != NULL) |
181 | { |
182 | TEST_COMPARE_STRING (h->h_name, ht[i].h_name); |
183 | TEST_VERIFY (h->h_addr_list[0] != NULL); |
184 | if (h->h_addr_list[0] != NULL) |
185 | TEST_COMPARE_BLOB (h->h_addr_list[0], h->h_length, |
186 | ht[i].h_addr_list[0], ht[i].h_length); |
187 | } |
188 | } |
189 | |
190 | /* getaddrinfo */ |
191 | |
192 | for (i = 0; !HOST_ISLAST (&ht[i]); ++i) |
193 | { |
194 | struct addrinfo *ap; |
195 | struct addrinfo hint; |
196 | int res, j; |
197 | |
198 | memset (&hint, 0, sizeof (hint)); |
199 | hint.ai_family = AF_INET; |
200 | hint.ai_socktype = SOCK_STREAM; |
201 | hint.ai_protocol = 0; |
202 | hint.ai_flags = 0; |
203 | |
204 | ap = NULL; |
205 | res = getaddrinfo (ht[i].h_name, NULL, &hint, &ap); |
206 | TEST_COMPARE (res, 0); |
207 | TEST_VERIFY (ap != NULL); |
208 | if (res == 0 && ap != NULL) |
209 | { |
210 | j = 0; /* which address in the list */ |
211 | while (ap) |
212 | { |
213 | TEST_COMPARE (ap->ai_family, AF_INET); |
214 | |
215 | struct sockaddr_in *in = (struct sockaddr_in *)ap->ai_addr; |
216 | unsigned char *up = (unsigned char *)&in->sin_addr; |
217 | |
218 | TEST_COMPARE_BLOB (up, 4, ht[i].h_addr_list[j], 4); |
219 | |
220 | ap = ap->ai_next; |
221 | ++j; |
222 | } |
223 | } |
224 | } |
225 | |
226 | /* getnameinfo */ |
227 | |
228 | for (i = 0; !HOST_ISLAST (&ht[i]); ++i) |
229 | { |
230 | struct sockaddr_in addr; |
231 | int res; |
232 | char host_buf[NI_MAXHOST]; |
233 | |
234 | memset (&addr, 0, sizeof (addr)); |
235 | addr.sin_family = AF_INET; |
236 | addr.sin_port = 80; |
237 | memcpy (& addr.sin_addr, ht[i].h_addr_list[0], 4); |
238 | |
239 | res = getnameinfo ((struct sockaddr *) &addr, sizeof(addr), |
240 | host_buf, sizeof(host_buf), |
241 | NULL, 0, NI_NOFQDN); |
242 | |
243 | TEST_COMPARE (res, 0); |
244 | if (res == 0) |
245 | TEST_VERIFY (strcmp (ht[i].h_name, host_buf) == 0); |
246 | else |
247 | printf (format: "error %s\n" , gai_strerror (res)); |
248 | } |
249 | } |
250 | |
251 | static void |
252 | must_be_1 (void) |
253 | { |
254 | struct passwd *p; |
255 | |
256 | must_be_tests (pt: pwd_table_1, ht: host_table_1); |
257 | p = getpwnam(name: "name5" ); |
258 | TEST_VERIFY (p == NULL); |
259 | } |
260 | |
261 | static void |
262 | must_be_2 (void) |
263 | { |
264 | struct passwd *p; |
265 | |
266 | must_be_tests (pt: pwd_table_2, ht: host_table_2); |
267 | p = getpwnam(name: "name100" ); |
268 | TEST_VERIFY (p == NULL); |
269 | } |
270 | |
271 | static void |
272 | xrename (const char *a, const char *b) |
273 | { |
274 | int i = rename (old: a, new: b); |
275 | if (i != 0) |
276 | FAIL_EXIT1 ("rename(%s,%s) failed: %s\n" , a, b, strerror(errno)); |
277 | } |
278 | |
279 | /* If the actions change while in the midst of doing a series of |
280 | lookups, make sure they're consistent. */ |
281 | static void |
282 | test_cross_switch_consistency (void) |
283 | { |
284 | int i; |
285 | struct passwd *p; |
286 | |
287 | /* We start by initiating a set/get/end loop on conf1. */ |
288 | setpwent (); |
289 | for (i = 0; !PWD_ISLAST (&pwd_table_1[i]); ++i) |
290 | { |
291 | p = getpwent (); |
292 | TEST_VERIFY (p != NULL); |
293 | if (p != NULL) |
294 | { |
295 | TEST_COMPARE_STRING (p->pw_name, pwd_table_1[i].pw_name); |
296 | TEST_COMPARE (p->pw_uid, pwd_table_1[i].pw_uid); |
297 | } |
298 | |
299 | /* After the first lookup, switch to conf2 and verify */ |
300 | if (i == 0) |
301 | { |
302 | xrename (a: "/etc/nsswitch.conf" , b: "/etc/nsswitch.conf1" ); |
303 | xrename (a: "/etc/nsswitch.conf2" , b: "/etc/nsswitch.conf" ); |
304 | |
305 | p = getpwnam (name: pwd_table_2[0].pw_name); |
306 | TEST_COMPARE (p->pw_uid, pwd_table_2[0].pw_uid); |
307 | } |
308 | |
309 | /* But the original loop should still be on conf1. */ |
310 | } |
311 | endpwent (); |
312 | |
313 | /* Make sure the set/get/end loop sees conf2 now. */ |
314 | setpwent (); |
315 | for (i = 0; !PWD_ISLAST (&pwd_table_2[i]); ++i) |
316 | { |
317 | p = getpwent (); |
318 | TEST_VERIFY (p != NULL); |
319 | if (p != NULL) |
320 | { |
321 | TEST_COMPARE_STRING (p->pw_name, pwd_table_2[i].pw_name); |
322 | TEST_COMPARE (p->pw_uid, pwd_table_2[i].pw_uid); |
323 | } |
324 | } |
325 | endpwent (); |
326 | |
327 | } |
328 | |
329 | static int |
330 | do_test (void) |
331 | { |
332 | /* The test1 module was configured at program start. */ |
333 | must_be_1 (); |
334 | |
335 | xrename (a: "/etc/nsswitch.conf" , b: "/etc/nsswitch.conf1" ); |
336 | xrename (a: "/etc/nsswitch.conf2" , b: "/etc/nsswitch.conf" ); |
337 | must_be_2 (); |
338 | |
339 | xrename (a: "/etc/nsswitch.conf" , b: "/etc/nsswitch.conf2" ); |
340 | xrename (a: "/etc/nsswitch.conf1" , b: "/etc/nsswitch.conf" ); |
341 | must_be_1 (); |
342 | |
343 | test_cross_switch_consistency (); |
344 | |
345 | return 0; |
346 | } |
347 | |
348 | #include <support/test-driver.c> |
349 | |