1 /* Template generic NSS service provider.  See nss_test.h for usage.
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 #include <errno.h>
20 #include <nss.h>
21 #include <pthread.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <alloc_buffer.h>
25 
26 
27 /* We need to be able to handle NULLs "properly" within the testsuite,
28    to test known bad data.  */
29 #define alloc_buffer_maybe_copy_string(b,s) s ? alloc_buffer_copy_string (b, s) : NULL;
30 
31 /* This file is the master template.  Other instances of this test
32    module should define NAME(x) to have their name instead of "test1",
33    then include this file.
34 */
35 #define NAME_(x,n) _nss_##n##_##x
36 #ifndef NAME
37 #define NAME(x) NAME_(x,test1)
38 #endif
39 #define NAMESTR__(x) #x
40 #define NAMESTR_(x) NAMESTR__(x)
41 #define NAMESTR(x) NAMESTR_(NAME(x))
42 
43 #include "nss_test.h"
44 
45 /* -------------------------------------------------- */
46 /* Default Data.  */
47 
48 static struct passwd default_pwd_data[] =
49   {
50 #define PWD(u) \
51     { .pw_name = (char *) "name" #u, .pw_passwd = (char *) "*", .pw_uid = u,  \
52       .pw_gid = 100, .pw_gecos = (char *) "*", .pw_dir = (char *) "*",	      \
53       .pw_shell = (char *) "*" }
54     PWD (30),
55     PWD (100),
56     PWD (200),
57     PWD (60),
58     PWD (20000)
59   };
60 #define default_npwd_data \
61   (sizeof (default_pwd_data) / sizeof (default_pwd_data[0]))
62 
63 static struct passwd *pwd_data = default_pwd_data;
64 static int npwd_data = default_npwd_data;
65 
66 static struct group *grp_data = NULL;
67 static int ngrp_data = 0;
68 
69 static struct spwd *spwd_data = NULL;
70 static int nspwd_data = 0;
71 
72 static struct hostent *host_data = NULL;
73 static int nhost_data = 0;
74 
75 /* This function will get called, and once per session, look back into
76    the test case's executable for an init hook function, and call
77    it.  */
78 
79 static int initted = 0;
80 static void
init(void)81 init(void)
82 {
83   test_tables t;
84   int i;
85 
86   if (initted)
87     return;
88   if (NAME(init_hook))
89     {
90       memset (&t, 0, sizeof (t));
91       NAME(init_hook)(&t);
92 
93       if (t.pwd_table)
94 	{
95 	  pwd_data = t.pwd_table;
96 	  for (i=0; ! PWD_ISLAST(& pwd_data[i]); i++)
97 	    ;
98 	  npwd_data = i;
99 	}
100 
101       if (t.grp_table)
102 	{
103 	  grp_data = t.grp_table;
104 	  for (i=0; ! GRP_ISLAST(& grp_data[i]); i++)
105 	    ;
106 	  ngrp_data = i;
107 	}
108       if (t.spwd_table)
109 	{
110 	  spwd_data = t.spwd_table;
111 	  for (i=0; ! SPWD_ISLAST(& spwd_data[i]); i++)
112 	    ;
113 	  nspwd_data = i;
114 	}
115       if (t.host_table)
116 	{
117 	  host_data = t.host_table;
118 	  for (i=0; ! HOST_ISLAST(& host_data[i]); i++)
119 	    ;
120 	  nhost_data = i;
121 	}
122     }
123   initted = 1;
124 }
125 
126 /* -------------------------------------------------- */
127 /* Password handling.  */
128 
129 static size_t pwd_iter;
130 #define CURPWD pwd_data[pwd_iter]
131 
132 static pthread_mutex_t pwd_lock = PTHREAD_MUTEX_INITIALIZER;
133 
134 enum nss_status
NAME(setpwent)135 NAME(setpwent) (int stayopen)
136 {
137   init();
138   pwd_iter = 0;
139   return NSS_STATUS_SUCCESS;
140 }
141 
142 
143 enum nss_status
NAME(endpwent)144 NAME(endpwent) (void)
145 {
146   init();
147   return NSS_STATUS_SUCCESS;
148 }
149 
150 static enum nss_status
copy_passwd(struct passwd * result,struct passwd * local,char * buffer,size_t buflen,int * errnop)151 copy_passwd (struct passwd *result, struct passwd *local,
152 	    char *buffer, size_t buflen, int *errnop)
153 {
154   struct alloc_buffer buf = alloc_buffer_create (buffer, buflen);
155 
156   result->pw_name = alloc_buffer_maybe_copy_string (&buf, local->pw_name);
157   result->pw_passwd = alloc_buffer_maybe_copy_string (&buf, local->pw_passwd);
158   result->pw_uid = local->pw_uid;
159   result->pw_gid = local->pw_gid;
160   result->pw_gecos = alloc_buffer_maybe_copy_string (&buf, local->pw_gecos);
161   result->pw_dir = alloc_buffer_maybe_copy_string (&buf, local->pw_dir);
162   result->pw_shell = alloc_buffer_maybe_copy_string (&buf, local->pw_shell);
163 
164   if (alloc_buffer_has_failed (&buf))
165     {
166       *errnop = ERANGE;
167       return NSS_STATUS_TRYAGAIN;
168     }
169 
170   return NSS_STATUS_SUCCESS;
171 }
172 
173 enum nss_status
NAME(getpwent_r)174 NAME(getpwent_r) (struct passwd *result, char *buffer, size_t buflen,
175 		       int *errnop)
176 {
177   int res = NSS_STATUS_SUCCESS;
178 
179   init();
180   pthread_mutex_lock (&pwd_lock);
181 
182   if (pwd_iter >= npwd_data)
183     res = NSS_STATUS_NOTFOUND;
184   else
185     {
186       res = copy_passwd (result, &CURPWD, buffer, buflen, errnop);
187       ++pwd_iter;
188     }
189 
190   pthread_mutex_unlock (&pwd_lock);
191 
192   return res;
193 }
194 
195 
196 enum nss_status
NAME(getpwuid_r)197 NAME(getpwuid_r) (uid_t uid, struct passwd *result, char *buffer,
198 		       size_t buflen, int *errnop)
199 {
200   init();
201   for (size_t idx = 0; idx < npwd_data; ++idx)
202     if (pwd_data[idx].pw_uid == uid)
203       return copy_passwd (result, &pwd_data[idx], buffer, buflen, errnop);
204 
205   return NSS_STATUS_NOTFOUND;
206 }
207 
208 
209 enum nss_status
NAME(getpwnam_r)210 NAME(getpwnam_r) (const char *name, struct passwd *result, char *buffer,
211 		       size_t buflen, int *errnop)
212 {
213   init();
214   for (size_t idx = 0; idx < npwd_data; ++idx)
215     if (strcmp (pwd_data[idx].pw_name, name) == 0)
216       return copy_passwd (result, &pwd_data[idx], buffer, buflen, errnop);
217 
218   return NSS_STATUS_NOTFOUND;
219 }
220 
221 /* -------------------------------------------------- */
222 /* Group handling.  */
223 
224 static size_t grp_iter;
225 #define CURGRP grp_data[grp_iter]
226 
227 static pthread_mutex_t grp_lock = PTHREAD_MUTEX_INITIALIZER;
228 
229 enum nss_status
NAME(setgrent)230 NAME(setgrent) (int stayopen)
231 {
232   init();
233   grp_iter = 0;
234   return NSS_STATUS_SUCCESS;
235 }
236 
237 
238 enum nss_status
NAME(endgrent)239 NAME(endgrent) (void)
240 {
241   init();
242   return NSS_STATUS_SUCCESS;
243 }
244 
245 static enum nss_status
copy_group(struct group * result,struct group * local,char * buffer,size_t buflen,int * errnop)246 copy_group (struct group *result, struct group *local,
247 	    char *buffer, size_t buflen, int *errnop)
248 {
249   struct alloc_buffer buf = alloc_buffer_create (buffer, buflen);
250   char **memlist;
251   int i;
252 
253   if (local->gr_mem)
254     {
255       i = 0;
256       while (local->gr_mem[i])
257 	++i;
258 
259       memlist = alloc_buffer_alloc_array (&buf, char *, i + 1);
260 
261       if (memlist) {
262 	for (i = 0; local->gr_mem[i]; ++i)
263 	  memlist[i] = alloc_buffer_maybe_copy_string (&buf, local->gr_mem[i]);
264 	memlist[i] = NULL;
265       }
266 
267       result->gr_mem = memlist;
268     }
269   else
270     result->gr_mem = NULL;
271 
272   result->gr_name = alloc_buffer_maybe_copy_string (&buf, local->gr_name);
273   result->gr_passwd = alloc_buffer_maybe_copy_string (&buf, local->gr_passwd);
274   result->gr_gid = local->gr_gid;
275 
276   if (alloc_buffer_has_failed (&buf))
277     {
278       *errnop = ERANGE;
279       return NSS_STATUS_TRYAGAIN;
280     }
281 
282   return NSS_STATUS_SUCCESS;
283 }
284 
285 
286 enum nss_status
NAME(getgrent_r)287 NAME(getgrent_r) (struct group *result, char *buffer, size_t buflen,
288 		       int *errnop)
289 {
290   int res = NSS_STATUS_SUCCESS;
291 
292   init();
293   pthread_mutex_lock (&grp_lock);
294 
295   if (grp_iter >= ngrp_data)
296     res = NSS_STATUS_NOTFOUND;
297   else
298     {
299       res = copy_group (result, &CURGRP, buffer, buflen, errnop);
300       ++grp_iter;
301     }
302 
303   pthread_mutex_unlock (&grp_lock);
304 
305   return res;
306 }
307 
308 
309 enum nss_status
NAME(getgrgid_r)310 NAME(getgrgid_r) (gid_t gid, struct group *result, char *buffer,
311 		  size_t buflen, int *errnop)
312 {
313   init();
314   for (size_t idx = 0; idx < ngrp_data; ++idx)
315     if (grp_data[idx].gr_gid == gid)
316       return copy_group (result, &grp_data[idx], buffer, buflen, errnop);
317 
318   return NSS_STATUS_NOTFOUND;
319 }
320 
321 
322 enum nss_status
NAME(getgrnam_r)323 NAME(getgrnam_r) (const char *name, struct group *result, char *buffer,
324 		       size_t buflen, int *errnop)
325 {
326   init();
327   for (size_t idx = 0; idx < ngrp_data; ++idx)
328     if (strcmp (pwd_data[idx].pw_name, name) == 0)
329       {
330 	return copy_group (result, &grp_data[idx], buffer, buflen, errnop);
331       }
332 
333   return NSS_STATUS_NOTFOUND;
334 }
335 
336 /* -------------------------------------------------- */
337 /* Shadow password handling.  */
338 
339 static size_t spwd_iter;
340 #define CURSPWD spwd_data[spwd_iter]
341 
342 static pthread_mutex_t spwd_lock = PTHREAD_MUTEX_INITIALIZER;
343 
344 enum nss_status
NAME(setspent)345 NAME(setspent) (int stayopen)
346 {
347   init();
348   spwd_iter = 0;
349   return NSS_STATUS_SUCCESS;
350 }
351 
352 
353 enum nss_status
NAME(endspwent)354 NAME(endspwent) (void)
355 {
356   init();
357   return NSS_STATUS_SUCCESS;
358 }
359 
360 static enum nss_status
copy_shadow(struct spwd * result,struct spwd * local,char * buffer,size_t buflen,int * errnop)361 copy_shadow (struct spwd *result, struct spwd *local,
362 	    char *buffer, size_t buflen, int *errnop)
363 {
364   struct alloc_buffer buf = alloc_buffer_create (buffer, buflen);
365 
366   result->sp_namp = alloc_buffer_maybe_copy_string (&buf, local->sp_namp);
367   result->sp_pwdp = alloc_buffer_maybe_copy_string (&buf, local->sp_pwdp);
368   result->sp_lstchg = local->sp_lstchg;
369   result->sp_min = local->sp_min;
370   result->sp_max = local->sp_max;
371   result->sp_warn = local->sp_warn;
372   result->sp_inact = local->sp_inact;
373   result->sp_expire = local->sp_expire;
374   result->sp_flag = local->sp_flag;
375 
376   if (alloc_buffer_has_failed (&buf))
377     {
378       *errnop = ERANGE;
379       return NSS_STATUS_TRYAGAIN;
380     }
381 
382   return NSS_STATUS_SUCCESS;
383 }
384 
385 enum nss_status
NAME(getspent_r)386 NAME(getspent_r) (struct spwd *result, char *buffer, size_t buflen,
387 		  int *errnop)
388 {
389   int res = NSS_STATUS_SUCCESS;
390 
391   init();
392   pthread_mutex_lock (&spwd_lock);
393 
394   if (spwd_iter >= nspwd_data)
395     res = NSS_STATUS_NOTFOUND;
396   else
397     {
398       res = copy_shadow (result, &CURSPWD, buffer, buflen, errnop);
399       ++spwd_iter;
400     }
401 
402   pthread_mutex_unlock (&spwd_lock);
403 
404   return res;
405 }
406 
407 enum nss_status
NAME(getspnam_r)408 NAME(getspnam_r) (const char *name, struct spwd *result, char *buffer,
409 		  size_t buflen, int *errnop)
410 {
411   init();
412   for (size_t idx = 0; idx < nspwd_data; ++idx)
413     if (strcmp (spwd_data[idx].sp_namp, name) == 0)
414       return copy_shadow (result, &spwd_data[idx], buffer, buflen, errnop);
415 
416   return NSS_STATUS_NOTFOUND;
417 }
418 
419 /* -------------------------------------------------- */
420 /* Host handling.  */
421 
422 static size_t host_iter;
423 #define CURHOST host_data[host_iter]
424 
425 static pthread_mutex_t host_lock = PTHREAD_MUTEX_INITIALIZER;
426 
427 enum nss_status
NAME(sethostent)428 NAME(sethostent) (int stayopen)
429 {
430   init();
431   host_iter = 0;
432   return NSS_STATUS_SUCCESS;
433 }
434 
435 
436 enum nss_status
NAME(endhostent)437 NAME(endhostent) (void)
438 {
439   init();
440   return NSS_STATUS_SUCCESS;
441 }
442 
443 static enum nss_status
copy_host(struct hostent * result,struct hostent * local,char * buffer,size_t buflen,int * errnop)444 copy_host (struct hostent *result, struct hostent *local,
445 	    char *buffer, size_t buflen, int *errnop)
446 {
447   struct alloc_buffer buf = alloc_buffer_create (buffer, buflen);
448   char **memlist;
449   int i, j;
450 
451   if (local->h_addr_list)
452     {
453       i = 0;
454       while (local->h_addr_list[i])
455 	++i;
456 
457       memlist = alloc_buffer_alloc_array (&buf, char *, i + 1);
458 
459       if (memlist) {
460 	for (j = 0; j < i; ++j)
461 	  memlist[j] = alloc_buffer_maybe_copy_string (&buf, local->h_addr_list[j]);
462 	memlist[j] = NULL;
463       }
464 
465       result->h_addr_list = memlist;
466     }
467   else
468     {
469       result->h_addr_list = NULL;
470     }
471 
472   result->h_aliases = NULL;
473   result->h_addrtype = AF_INET;
474   result->h_length = 4;
475   result->h_name = alloc_buffer_maybe_copy_string (&buf, local->h_name);
476 
477   if (alloc_buffer_has_failed (&buf))
478     {
479       *errnop = ERANGE;
480       return NSS_STATUS_TRYAGAIN;
481     }
482 
483   return NSS_STATUS_SUCCESS;
484 }
485 
486 
487 enum nss_status
NAME(gethostent_r)488 NAME(gethostent_r) (struct hostent *ret, char *buffer, size_t buflen,
489 		    struct hostent **result, int *errnop)
490 {
491   int res = NSS_STATUS_SUCCESS;
492 
493   init();
494   pthread_mutex_lock (&host_lock);
495 
496   if (host_iter >= nhost_data)
497     {
498       res = NSS_STATUS_NOTFOUND;
499       *result = NULL;
500     }
501   else
502     {
503       res = copy_host (ret, &CURHOST, buffer, buflen, errnop);
504       *result = ret;
505       ++host_iter;
506     }
507 
508   pthread_mutex_unlock (&host_lock);
509 
510   return res;
511 }
512 
513 enum nss_status
NAME(gethostbyname3_r)514 NAME(gethostbyname3_r) (const char *name, int af, struct hostent *ret,
515 			     char *buffer, size_t buflen, int *errnop,
516 			     int *h_errnop, int32_t *ttlp, char **canonp)
517 {
518   init();
519 
520   for (size_t idx = 0; idx < nhost_data; ++idx)
521     if (strcmp (host_data[idx].h_name, name) == 0)
522       return copy_host (ret, & host_data[idx], buffer, buflen, h_errnop);
523 
524   return NSS_STATUS_NOTFOUND;
525 }
526 
527 enum nss_status
NAME(gethostbyname_r)528 NAME(gethostbyname_r) (const char *name, struct hostent *result,
529 		       char *buffer, size_t buflen,
530 		       int *errnop, int *h_errnop)
531 {
532   return NAME(gethostbyname3_r) (name, AF_INET, result, buffer, buflen,
533 				 errnop, h_errnop, NULL, NULL);
534 }
535 
536 enum nss_status
NAME(gethostbyname2_r)537 NAME(gethostbyname2_r) (const char *name, int af, struct hostent *result,
538 			char *buffer, size_t buflen,
539 			int *errnop, int *h_errnop)
540 {
541   return NAME(gethostbyname3_r) (name, af, result, buffer, buflen,
542 				 errnop, h_errnop, NULL, NULL);
543 }
544 
545 enum nss_status
NAME(gethostbyaddr2_r)546 NAME(gethostbyaddr2_r) (const void *addr, socklen_t len, int af,
547 			struct hostent *result, char *buffer, size_t buflen,
548 			int *errnop, int *h_errnop, int32_t *ttlp)
549 {
550   init();
551 
552   /* Support this later.  */
553   if (len != 4)
554     return NSS_STATUS_NOTFOUND;
555 
556   for (size_t idx = 0; idx < nhost_data; ++idx)
557     if (memcmp (host_data[idx].h_addr, addr, len) == 0)
558       return copy_host (result, & host_data[idx], buffer, buflen, h_errnop);
559 
560   return NSS_STATUS_NOTFOUND;
561 }
562 
563 /* Note: only the first address is supported, intentionally.  */
564 enum nss_status
NAME(gethostbyaddr_r)565 NAME(gethostbyaddr_r) (const void *addr, socklen_t len, int af,
566 		       struct hostent *result, char *buffer, size_t buflen,
567 		       int *errnop, int *h_errnop)
568 {
569   return NAME(gethostbyaddr2_r) (addr, len, af, result, buffer, buflen,
570 				 errnop, h_errnop, NULL);
571 }
572