1/* Common code for NSS test cases.
2 Copyright (C) 2017-2022 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
20/* There are two (or more) NSS test modules named nss_test1,
21 nss_test2, etc. Each one will call a function IN THE TEST CASE
22 called _nss_test1_init_hook(test_tables *) (or _nss_test2_*, etc).
23
24 In your copy of the hook function, you may change the *_table
25 pointers in the passed struct to point to static tables in your
26 test case, and the test modules will use that table instead.
27
28 Your tables MUST end with an entry that has a *_LAST() macro.
29 Use the *_ISLAST() macro to test for end of list.
30
31 Use __nss_configure_lookup("passwd", "test1 test2") (for example) to
32 configure NSS to use the test modules. */
33
34#include <pwd.h>
35#include <grp.h>
36#include <shadow.h>
37#include <netdb.h>
38
39typedef struct test_tables {
40 struct passwd *pwd_table;
41 struct group *grp_table;
42 struct spwd *spwd_table;
43 struct hostent *host_table;
44} test_tables;
45
46extern void _nss_test1_init_hook (test_tables *) __attribute__((weak));
47extern void _nss_test2_init_hook (test_tables *) __attribute__((weak));
48
49#define PWD_LAST() { .pw_name = NULL, .pw_uid = 0 }
50#define GRP_LAST() { .gr_name = NULL, .gr_gid = 0 }
51#define SPWD_LAST() { .sp_namp = NULL, .sp_pwdp = NULL }
52#define HOST_LAST() { .h_name = NULL, .h_aliases = NULL, .h_length = 0, .h_addr_list = NULL }
53
54#define PWD_ISLAST(p) ((p)->pw_name == NULL && (p)->pw_uid == 0)
55#define GRP_ISLAST(g) ((g)->gr_name == NULL && (g)->gr_gid == 0)
56#define SPWD_ISLAST(s) ((s)->sp_namp == NULL && (s)->sp_pwdp == 0)
57#define HOST_ISLAST(h) ((h)->h_name == NULL && (h)->h_length == 0)
58
59/* Macros to fill in the tables easily. */
60
61/* Note that the "unparameterized" fields are not magic; they're just
62 arbitrary values. Tests which need to verify those fields should
63 fill them in explicitly. */
64
65#define PWD(u) \
66 { .pw_name = (char *) "name" #u, .pw_passwd = (char *) "*", .pw_uid = u, \
67 .pw_gid = 100, .pw_gecos = (char *) "*", .pw_dir = (char *) "*", \
68 .pw_shell = (char *) "*" }
69
70#define PWD_N(u,n) \
71 { .pw_name = (char *) n, .pw_passwd = (char *) "*", .pw_uid = u, \
72 .pw_gid = 100, .pw_gecos = (char *) "*", .pw_dir = (char *) "*", \
73 .pw_shell = (char *) "*" }
74
75#define GRP(u) \
76 { .gr_name = (char *) "name" #u, .gr_passwd = (char *) "*", .gr_gid = u, \
77 .gr_mem = (char **) group_##u }
78
79#define GRP_N(u,n,m) \
80 { .gr_name = (char *) n, .gr_passwd = (char *) "*", .gr_gid = u, \
81 .gr_mem = (char **) m }
82
83#define SPWD(u) \
84 { .sp_namp = (char *) "name" #u, .sp_pwdp = (char *) "passwd" #u }
85
86#define HOST(u) \
87 { .h_name = (char *) "name" #u, .h_aliases = NULL, .h_addrtype = u, \
88 .h_length = 4, \
89 .h_addr_list = (char **) hostaddr_##u }
90
91/*------------------------------------------------------------*/
92
93/* Helper functions for testing passwd entries. Call
94 compare_passwds() passing a test index, the passwd entry you got,
95 and the expected passwd entry. The function will return the number
96 of mismatches, or zero of the two records are the same. */
97
98static void __attribute__((used))
99print_passwd (struct passwd *p)
100{
101 printf (format: " passwd %u.%s (%s) :", p->pw_uid, p->pw_name, p->pw_passwd);
102 printf (format: " %u, %s, %s, %s\n", p->pw_gid, p->pw_gecos, p->pw_dir, p->pw_shell);
103 printf (format: "\n");
104}
105
106static int __attribute__((used))
107compare_passwd_field (int i, struct passwd *p, const char *got,
108 const char *exp, const char *name)
109{
110 /* Does the entry have a value? */
111 if (got == NULL)
112 {
113 printf (format: "[%d] passwd %s for %u.%s was (null)\n",
114 i, name,
115 p->pw_uid, p->pw_name);
116 return 1;
117 }
118 /* Does the entry have an unexpected name? */
119 else if (exp == NULL)
120 {
121 printf (format: "[%d] passwd %s for %u.(null) was %s\n",
122 i, name,
123 p->pw_uid, got);
124 return 1;
125 }
126 /* And is it correct? */
127 else if (got && strcmp (got, exp) != 0)
128 {
129 printf(format: "[%d] passwd entry %u.%s had %s \"%s\" (expected \"%s\") \n",
130 i,
131 p->pw_uid, p->pw_name, name,
132 got, exp);
133 return 1;
134 }
135 return 0;
136}
137
138#define COMPARE_PWD_FIELD(f) \
139 retval += compare_passwd_field (i, e, p->f, e->f, #f)
140
141/* Compare passwd to expected passwd, return number of "problems".
142 "I" is the index into the testcase data. */
143static int __attribute__((used))
144compare_passwds (int i, struct passwd *p, struct passwd *e)
145{
146 int retval = 0;
147
148 /* Did we get the expected uid? */
149 if (p->pw_uid != e->pw_uid)
150 {
151 printf(format: "[%d] passwd entry %u.%s had uid %u\n", i,
152 e->pw_uid, e->pw_name,
153 p->pw_uid);
154 ++retval;
155 }
156
157 /* Did we get the expected gid? */
158 if (p->pw_gid != e->pw_gid)
159 {
160 printf(format: "[%d] passwd entry %u.%s had gid %u (expected %u)\n", i,
161 e->pw_uid, e->pw_name,
162 p->pw_gid, e->pw_gid);
163 ++retval;
164 }
165
166 COMPARE_PWD_FIELD (pw_name);
167 COMPARE_PWD_FIELD (pw_passwd);
168 COMPARE_PWD_FIELD (pw_gecos);
169 COMPARE_PWD_FIELD (pw_dir);
170 COMPARE_PWD_FIELD (pw_shell);
171
172 if (retval > 0)
173 {
174 /* Left in for debugging later, if needed. */
175 print_passwd (p);
176 print_passwd (p: e);
177 }
178
179 return retval;
180}
181
182/*------------------------------------------------------------*/
183
184/* Helpers for checking group entries. See passwd helper comment
185 above for details. */
186
187static void __attribute__((used))
188print_group (struct group *g)
189{
190 int j;
191
192 printf (format: " group %u.%s (%s) :", g->gr_gid, g->gr_name, g->gr_passwd);
193 if (g->gr_mem)
194 for (j=0; g->gr_mem[j]; j++)
195 printf (format: "%s%s", j==0 ? " " : ", ", g->gr_mem[j]);
196 printf (format: "\n");
197}
198
199/* Compare group to expected group, return number of "problems". "I"
200 is the index into the testcase data. */
201static int __attribute__((used))
202compare_groups (int i, struct group *g, struct group *e)
203{
204 int j;
205 int retval = 0;
206
207 /* Did we get the expected gid? */
208 if (g->gr_gid != e->gr_gid)
209 {
210 printf(format: "[%d] group entry %u.%s had gid %u\n", i,
211 e->gr_gid, e->gr_name,
212 g->gr_gid);
213 ++retval;
214 }
215
216 /* Does the entry have a name? */
217 if (g->gr_name == NULL)
218 {
219 printf (format: "[%d] group name for %u.%s was (null)\n", i,
220 e->gr_gid, e->gr_name);
221 ++retval;
222 }
223 /* Does the entry have an unexpected name? */
224 else if (e->gr_name == NULL)
225 {
226 printf (format: "[%d] group name for %u.(null) was %s\n", i,
227 e->gr_gid, g->gr_name);
228 ++retval;
229 }
230 /* And is it correct? */
231 else if (strcmp (g->gr_name, e->gr_name) != 0)
232 {
233 printf(format: "[%d] group entry %u.%s had name \"%s\"\n", i,
234 e->gr_gid, e->gr_name,
235 g->gr_name);
236 ++retval;
237 }
238
239 /* Does the entry have a password? */
240 if (g->gr_passwd == NULL && e->gr_passwd != NULL)
241 {
242 printf (format: "[%d] group password for %u.%s was NULL\n", i,
243 e->gr_gid, e->gr_name);
244 ++retval;
245 }
246 else if (g->gr_passwd != NULL && e->gr_passwd == NULL)
247 {
248 printf (format: "[%d] group password for %u.%s was not NULL\n", i,
249 e->gr_gid, e->gr_name);
250 ++retval;
251 }
252 /* And is it correct? */
253 else if (g->gr_passwd && strcmp (g->gr_passwd, e->gr_passwd) != 0)
254 {
255 printf(format: "[%d] group entry %u.%s had password \"%s\" (not \"%s\")\n", i,
256 e->gr_gid, e->gr_name,
257 g->gr_passwd, e->gr_passwd);
258 ++retval;
259 }
260
261 /* Now compare group members... */
262
263 if (e->gr_mem != NULL && g->gr_mem == NULL)
264 {
265 printf(format: "[%d] group entry %u.%s missing member list\n", i,
266 e->gr_gid, e->gr_name);
267 ++retval;
268 }
269 else if (e->gr_mem == NULL && g->gr_mem != NULL)
270 {
271 printf(format: "[%d] group entry %u.%s has unexpected member list\n", i,
272 e->gr_gid, e->gr_name);
273 ++retval;
274 }
275 else if (e->gr_mem == NULL && g->gr_mem == NULL)
276 {
277 /* This case is OK. */
278 }
279 else
280 {
281 /* Compare two existing lists. */
282 j = 0;
283 for (;;)
284 {
285 if (g->gr_mem[j] == NULL && e->gr_mem[j] == NULL)
286 {
287 /* Matching end-of-lists. */
288 break;
289 }
290 if (g->gr_mem[j] == NULL)
291 {
292 printf (format: "[%d] group member list for %u.%s is too short.\n", i,
293 e->gr_gid, e->gr_name);
294 ++retval;
295 break;
296 }
297 if (e->gr_mem[j] == NULL)
298 {
299 printf (format: "[%d] group member list for %u.%s is too long.\n", i,
300 e->gr_gid, e->gr_name);
301 ++retval;
302 break;
303 }
304 if (strcmp (g->gr_mem[j], e->gr_mem[j]) != 0)
305 {
306 printf (format: "[%d] group member list for %u.%s differs: %s vs %s.\n", i,
307 e->gr_gid, e->gr_name,
308 e->gr_mem[j], g->gr_mem[j]);
309 ++retval;
310 }
311
312 j++;
313 }
314 }
315
316 if (retval > 0)
317 {
318 /* Left in for debugging later, if needed. */
319 print_group (g);
320 print_group (g: e);
321 }
322
323 return retval;
324}
325

source code of glibc/nss/nss_test.h