(NEW, NEW1): Define.
[kopensolaris-gnu/glibc.git] / nss / nsswitch.c
1 /* Copyright (C) 1996,1997,1998,1999,2001,2002 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
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, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <ctype.h>
21 #include <dlfcn.h>
22 #include <errno.h>
23 #include <netdb.h>
24 #include <bits/libc-lock.h>
25 #include <search.h>
26 #include <stdio.h>
27 #include <stdio_ext.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include <aliases.h>
32 #include <grp.h>
33 #include <netinet/ether.h>
34 #include <pwd.h>
35 #include <shadow.h>
36
37 #if !defined DO_STATIC_NSS || defined SHARED
38 # include <gnu/lib-names.h>
39 #endif
40
41 #include "nsswitch.h"
42
43 /* Prototypes for the local functions.  */
44 static name_database *nss_parse_file (const char *fname) internal_function;
45 static name_database_entry *nss_getline (char *line) internal_function;
46 static service_user *nss_parse_service_list (const char *line)
47      internal_function;
48 static service_library *nss_new_service (name_database *database,
49                                          const char *name) internal_function;
50
51
52 /* Declare external database variables.  */
53 #define DEFINE_DATABASE(name)                                                 \
54   extern service_user *__nss_##name##_database attribute_hidden;              \
55   weak_extern (__nss_##name##_database)
56 #include "databases.def"
57 #undef DEFINE_DATABASE
58
59 /* Structure to map database name to variable.  */
60 static struct
61 {
62   const char *name;
63   service_user **dbp;
64 } databases[] =
65 {
66 #define DEFINE_DATABASE(name)                                                 \
67   { #name, &__nss_##name##_database },
68 #include "databases.def"
69 #undef DEFINE_DATABASE
70 };
71
72
73 __libc_lock_define_initialized (static, lock)
74
75 #if !defined DO_STATIC_NSS || defined SHARED
76 /* String with revision number of the shared object files.  */
77 static const char *const __nss_shlib_revision = LIBNSS_FILES_SO + 15;
78 #endif
79
80 /* The root of the whole data base.  */
81 static name_database *service_table;
82
83
84 /* -1 == database not found
85     0 == database entry pointer stored */
86 int
87 __nss_database_lookup (const char *database, const char *alternate_name,
88                        const char *defconfig, service_user **ni)
89 {
90   /* Prevent multiple threads to change the service table.  */
91   __libc_lock_lock (lock);
92
93   /* Reconsider database variable in case some other thread called
94      `__nss_configure_lookup' while we waited for the lock.  */
95   if (*ni != NULL)
96     {
97       __libc_lock_unlock (lock);
98       return 0;
99     }
100
101   /* Are we initialized yet?  */
102   if (service_table == NULL)
103     /* Read config file.  */
104     service_table = nss_parse_file (_PATH_NSSWITCH_CONF);
105
106   /* Test whether configuration data is available.  */
107   if (service_table != NULL)
108     {
109       /* Return first `service_user' entry for DATABASE.  */
110       name_database_entry *entry;
111
112       /* XXX Could use some faster mechanism here.  But each database is
113          only requested once and so this might not be critical.  */
114       for (entry = service_table->entry; entry != NULL; entry = entry->next)
115         if (strcmp (database, entry->name) == 0)
116           *ni = entry->service;
117
118       if (*ni == NULL && alternate_name != NULL)
119         /* We haven't found an entry so far.  Try to find it with the
120            alternative name.  */
121         for (entry = service_table->entry; entry != NULL; entry = entry->next)
122           if (strcmp (alternate_name, entry->name) == 0)
123             *ni = entry->service;
124     }
125
126   /* No configuration data is available, either because nsswitch.conf
127      doesn't exist or because it doesn't has a line for this database.
128
129      DEFCONFIG specifies the default service list for this database,
130      or null to use the most common default.  */
131   if (*ni == NULL)
132     *ni = nss_parse_service_list (defconfig
133                                   ?: "nis [NOTFOUND=return] files");
134
135   __libc_lock_unlock (lock);
136
137   return 0;
138 }
139
140
141 /* -1 == not found
142     0 == function found
143     1 == finished */
144 int
145 __nss_lookup (service_user **ni, const char *fct_name, void **fctp)
146 {
147   *fctp = __nss_lookup_function (*ni, fct_name);
148
149   while (*fctp == NULL
150          && nss_next_action (*ni, NSS_STATUS_UNAVAIL) == NSS_ACTION_CONTINUE
151          && (*ni)->next != NULL)
152     {
153       *ni = (*ni)->next;
154
155       *fctp = __nss_lookup_function (*ni, fct_name);
156     }
157
158   return *fctp != NULL ? 0 : (*ni)->next == NULL ? 1 : -1;
159 }
160
161
162 /* -1 == not found
163     0 == adjusted for next function
164     1 == finished */
165 int
166 __nss_next (service_user **ni, const char *fct_name, void **fctp, int status,
167             int all_values)
168 {
169   if (all_values)
170     {
171       if (nss_next_action (*ni, NSS_STATUS_TRYAGAIN) == NSS_ACTION_RETURN
172           && nss_next_action (*ni, NSS_STATUS_UNAVAIL) == NSS_ACTION_RETURN
173           && nss_next_action (*ni, NSS_STATUS_NOTFOUND) == NSS_ACTION_RETURN
174           && nss_next_action (*ni, NSS_STATUS_SUCCESS) == NSS_ACTION_RETURN)
175         return 1;
176     }
177   else
178     {
179       /* This is really only for debugging.  */
180        if (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_RETURN)
181          __libc_fatal ("illegal status in __nss_next");
182
183        if (nss_next_action (*ni, status) == NSS_ACTION_RETURN)
184          return 1;
185     }
186
187   if ((*ni)->next == NULL)
188     return -1;
189
190   do
191     {
192       *ni = (*ni)->next;
193
194       *fctp = __nss_lookup_function (*ni, fct_name);
195     }
196   while (*fctp == NULL
197          && nss_next_action (*ni, NSS_STATUS_UNAVAIL) == NSS_ACTION_CONTINUE
198          && (*ni)->next != NULL);
199
200   return *fctp != NULL ? 0 : -1;
201 }
202
203
204 int
205 __nss_configure_lookup (const char *dbname, const char *service_line)
206 {
207   service_user *new_db;
208   size_t cnt;
209
210   for (cnt = 0; cnt < sizeof databases; ++cnt)
211     {
212       int cmp = strcmp (dbname, databases[cnt].name);
213       if (cmp == 0)
214         break;
215       if (cmp < 0)
216         {
217           __set_errno (EINVAL);
218           return -1;
219         }
220     }
221
222   if (cnt == sizeof databases)
223     {
224       __set_errno (EINVAL);
225       return -1;
226     }
227
228   /* Test whether it is really used.  */
229   if (databases[cnt].dbp == NULL)
230     /* Nothing to do, but we could do.  */
231     return 0;
232
233   /* Try to generate new data.  */
234   new_db = nss_parse_service_list (service_line);
235   if (new_db == NULL)
236     {
237       /* Illegal service specification.  */
238       __set_errno (EINVAL);
239       return -1;
240     }
241
242   /* Prevent multiple threads to change the service table.  */
243   __libc_lock_lock (lock);
244
245   /* Install new rules.  */
246   *databases[cnt].dbp = new_db;
247
248   __libc_lock_unlock (lock);
249
250   return 0;
251 }
252
253
254 /* Comparison function for searching NI->known tree.  */
255 static int
256 known_compare (const void *p1, const void *p2)
257 {
258   return p1 == p2 ? 0 : strcmp (*(const char *const *) p1,
259                                 *(const char *const *) p2);
260 }
261
262
263 void *
264 __nss_lookup_function (service_user *ni, const char *fct_name)
265 {
266   void **found, *result;
267
268   /* We now modify global data.  Protect it.  */
269   __libc_lock_lock (lock);
270
271   /* Search the tree of functions previously requested.  Data in the
272      tree are `known_function' structures, whose first member is a
273      `const char *', the lookup key.  The search returns a pointer to
274      the tree node structure; the first member of the is a pointer to
275      our structure (i.e. what will be a `known_function'); since the
276      first member of that is the lookup key string, &FCT_NAME is close
277      enough to a pointer to our structure to use as a lookup key that
278      will be passed to `known_compare' (above).  */
279
280   found = __tsearch (&fct_name, (void **) &ni->known, &known_compare);
281   if (*found != &fct_name)
282     /* The search found an existing structure in the tree.  */
283     result = ((known_function *) *found)->fct_ptr;
284   else
285     {
286       /* This name was not known before.  Now we have a node in the tree
287          (in the proper sorted position for FCT_NAME) that points to
288          &FCT_NAME instead of any real `known_function' structure.
289          Allocate a new structure and fill it in.  */
290
291       known_function *known = malloc (sizeof *known);
292       if (! known)
293         {
294         remove_from_tree:
295           /* Oops.  We can't instantiate this node properly.
296              Remove it from the tree.  */
297           __tdelete (&fct_name, (void **) &ni->known, &known_compare);
298           result = NULL;
299         }
300       else
301         {
302           /* Point the tree node at this new structure.  */
303           *found = known;
304           known->fct_name = fct_name;
305
306           if (ni->library == NULL)
307             {
308               /* This service has not yet been used.  Fetch the service
309                  library for it, creating a new one if need be.  If there
310                  is no service table from the file, this static variable
311                  holds the head of the service_library list made from the
312                  default configuration.  */
313               static name_database default_table;
314               ni->library = nss_new_service (service_table ?: &default_table,
315                                              ni->name);
316               if (ni->library == NULL)
317                 {
318                   /* This only happens when out of memory.  */
319                   free (known);
320                   goto remove_from_tree;
321                 }
322             }
323
324 #if !defined DO_STATIC_NSS || defined SHARED
325           if (ni->library->lib_handle == NULL)
326             {
327               /* Load the shared library.  */
328               size_t shlen = (7 + strlen (ni->library->name) + 3
329                               + strlen (__nss_shlib_revision) + 1);
330               int saved_errno = errno;
331               char shlib_name[shlen];
332
333               /* Construct shared object name.  */
334               __stpcpy (__stpcpy (__stpcpy (__stpcpy (shlib_name,
335                                                       "libnss_"),
336                                             ni->library->name),
337                                   ".so"),
338                         __nss_shlib_revision);
339
340               ni->library->lib_handle = __libc_dlopen (shlib_name);
341               if (ni->library->lib_handle == NULL)
342                 {
343                   /* Failed to load the library.  */
344                   ni->library->lib_handle = (void *) -1l;
345                   __set_errno (saved_errno);
346                 }
347             }
348
349           if (ni->library->lib_handle == (void *) -1l)
350             /* Library not found => function not found.  */
351             result = NULL;
352           else
353             {
354               /* Get the desired function.  */
355               size_t namlen = (5 + strlen (ni->library->name) + 1
356                                + strlen (fct_name) + 1);
357               char name[namlen];
358
359               /* Construct the function name.  */
360               __stpcpy (__stpcpy (__stpcpy (__stpcpy (name, "_nss_"),
361                                             ni->library->name),
362                                   "_"),
363                         fct_name);
364
365               /* Look up the symbol.  */
366               result = __libc_dlsym (ni->library->lib_handle, name);
367             }
368 #else
369           /* We can't get function address dynamically in static linking. */
370           {
371 # define DEFINE_ENT(h,nm)                                                     \
372             { #h"_get"#nm"ent_r", _nss_##h##_get##nm##ent_r },                \
373             { #h"_end"#nm"ent", _nss_##h##_end##nm##ent },                    \
374             { #h"_set"#nm"ent", _nss_##h##_set##nm##ent },
375 # define DEFINE_GET(h,nm)                                                     \
376             { #h"_get"#nm"_r", _nss_##h##_get##nm##_r },
377 # define DEFINE_GETBY(h,nm,ky)                                                \
378             { #h"_get"#nm"by"#ky"_r", _nss_##h##_get##nm##by##ky##_r },
379             static struct fct_tbl { const char *fname; void *fp; } *tp, tbl[] =
380               {
381 # include "function.def"
382                 { NULL, NULL }
383               };
384             size_t namlen = (5 + strlen (ni->library->name) + 1
385                              + strlen (fct_name) + 1);
386             char name[namlen];
387
388             /* Construct the function name.  */
389             __stpcpy (__stpcpy (__stpcpy (name, ni->library->name),
390                                 "_"),
391                       fct_name);
392
393             result = NULL;
394             for (tp = &tbl[0]; tp->fname; tp++)
395               if (strcmp (tp->fname, name) == 0)
396                 {
397                   result = tp->fp;
398                   break;
399                 }
400           }
401 #endif
402
403           /* Remember function pointer for later calls.  Even if null, we
404              record it so a second try needn't search the library again.  */
405           known->fct_ptr = result;
406         }
407     }
408
409   /* Remove the lock.  */
410   __libc_lock_unlock (lock);
411
412   return result;
413 }
414
415
416 static name_database *
417 internal_function
418 nss_parse_file (const char *fname)
419 {
420   FILE *fp;
421   name_database *result;
422   name_database_entry *last;
423   char *line;
424   size_t len;
425
426   /* Open the configuration file.  */
427   fp = fopen (fname, "r");
428   if (fp == NULL)
429     return NULL;
430
431   /* No threads use this stream.  */
432   __fsetlocking (fp, FSETLOCKING_BYCALLER);
433
434   result = (name_database *) malloc (sizeof (name_database));
435   if (result == NULL)
436     return NULL;
437
438   result->entry = NULL;
439   result->library = NULL;
440   last = NULL;
441   line = NULL;
442   len = 0;
443   do
444     {
445       name_database_entry *this;
446       ssize_t n;
447
448       n = __getline (&line, &len, fp);
449       if (n < 0)
450         break;
451       if (line[n - 1] == '\n')
452         line[n - 1] = '\0';
453
454       /* Because the file format does not know any form of quoting we
455          can search forward for the next '#' character and if found
456          make it terminating the line.  */
457       *__strchrnul (line, '#') = '\0';
458
459       /* If the line is blank it is ignored.  */
460       if (line[0] == '\0')
461         continue;
462
463       /* Each line completely specifies the actions for a database.  */
464       this = nss_getline (line);
465       if (this != NULL)
466         {
467           if (last != NULL)
468             last->next = this;
469           else
470             result->entry = this;
471
472           last = this;
473         }
474     }
475   while (!feof_unlocked (fp));
476
477   /* Free the buffer.  */
478   free (line);
479   /* Close configuration file.  */
480   fclose (fp);
481
482   return result;
483 }
484
485
486 /* Read the source names:
487         `( <source> ( "[" "!"? (<status> "=" <action> )+ "]" )? )*'
488    */
489 static service_user *
490 internal_function
491 nss_parse_service_list (const char *line)
492 {
493   service_user *result = NULL, **nextp = &result;
494
495   while (1)
496     {
497       service_user *new_service;
498       const char *name;
499
500       while (isspace (line[0]))
501         ++line;
502       if (line[0] == '\0')
503         /* No source specified.  */
504         return result;
505
506       /* Read <source> identifier.  */
507       name = line;
508       while (line[0] != '\0' && !isspace (line[0]) && line[0] != '[')
509         ++line;
510       if (name == line)
511         return result;
512
513
514       new_service = (service_user *) malloc (sizeof (service_user)
515                                              + (line - name + 1));
516       if (new_service == NULL)
517         return result;
518
519       *((char *) __mempcpy (new_service->name, name, line - name)) = '\0';
520
521       /* Set default actions.  */
522       new_service->actions[2 + NSS_STATUS_TRYAGAIN] = NSS_ACTION_CONTINUE;
523       new_service->actions[2 + NSS_STATUS_UNAVAIL] = NSS_ACTION_CONTINUE;
524       new_service->actions[2 + NSS_STATUS_NOTFOUND] = NSS_ACTION_CONTINUE;
525       new_service->actions[2 + NSS_STATUS_SUCCESS] = NSS_ACTION_RETURN;
526       new_service->actions[2 + NSS_STATUS_RETURN] = NSS_ACTION_RETURN;
527       new_service->library = NULL;
528       new_service->known = NULL;
529       new_service->next = NULL;
530
531       while (isspace (line[0]))
532         ++line;
533
534       if (line[0] == '[')
535         {
536           /* Read criterions.  */
537           do
538             ++line;
539           while (line[0] != '\0' && isspace (line[0]));
540
541           do
542             {
543               int not;
544               enum nss_status status;
545               lookup_actions action;
546
547               /* Grok ! before name to mean all statii but that one.  */
548               not = line[0] == '!';
549               if (not)
550                 ++line;
551
552               /* Read status name.  */
553               name = line;
554               while (line[0] != '\0' && !isspace (line[0]) && line[0] != '='
555                      && line[0] != ']')
556                 ++line;
557
558               /* Compare with known statii.  */
559               if (line - name == 7)
560                 {
561                   if (__strncasecmp (name, "SUCCESS", 7) == 0)
562                     status = NSS_STATUS_SUCCESS;
563                   else if (__strncasecmp (name, "UNAVAIL", 7) == 0)
564                     status = NSS_STATUS_UNAVAIL;
565                   else
566                     return result;
567                 }
568               else if (line - name == 8)
569                 {
570                   if (__strncasecmp (name, "NOTFOUND", 8) == 0)
571                     status = NSS_STATUS_NOTFOUND;
572                   else if (__strncasecmp (name, "TRYAGAIN", 8) == 0)
573                     status = NSS_STATUS_TRYAGAIN;
574                   else
575                     return result;
576                 }
577               else
578                 return result;
579
580               while (isspace (line[0]))
581                 ++line;
582               if (line[0] != '=')
583                 return result;
584               do
585                 ++line;
586               while (isspace (line[0]));
587
588               name = line;
589               while (line[0] != '\0' && !isspace (line[0]) && line[0] != '='
590                      && line[0] != ']')
591                 ++line;
592
593               if (line - name == 6 && __strncasecmp (name, "RETURN", 6) == 0)
594                 action = NSS_ACTION_RETURN;
595               else if (line - name == 8
596                        && __strncasecmp (name, "CONTINUE", 8) == 0)
597                 action = NSS_ACTION_CONTINUE;
598               else
599                 return result;
600
601               if (not)
602                 {
603                   /* Save the current action setting for this status,
604                      set them all to the given action, and reset this one.  */
605                   const lookup_actions save = new_service->actions[2 + status];
606                   new_service->actions[2 + NSS_STATUS_TRYAGAIN] = action;
607                   new_service->actions[2 + NSS_STATUS_UNAVAIL] = action;
608                   new_service->actions[2 + NSS_STATUS_NOTFOUND] = action;
609                   new_service->actions[2 + NSS_STATUS_SUCCESS] = action;
610                   new_service->actions[2 + status] = save;
611                 }
612               else
613                 new_service->actions[2 + status] = action;
614
615               /* Skip white spaces.  */
616               while (isspace (line[0]))
617                 ++line;
618             }
619           while (line[0] != ']');
620
621           /* Skip the ']'.  */
622           ++line;
623         }
624
625       *nextp = new_service;
626       nextp = &new_service->next;
627     }
628 }
629
630 static name_database_entry *
631 internal_function
632 nss_getline (char *line)
633 {
634   const char *name;
635   name_database_entry *result;
636   size_t len;
637
638   /* Ignore leading white spaces.  ATTENTION: this is different from
639      what is implemented in Solaris.  The Solaris man page says a line
640      beginning with a white space character is ignored.  We regard
641      this as just another misfeature in Solaris.  */
642   while (isspace (line[0]))
643     ++line;
644
645   /* Recognize `<database> ":"'.  */
646   name = line;
647   while (line[0] != '\0' && !isspace (line[0]) && line[0] != ':')
648     ++line;
649   if (line[0] == '\0' || name == line)
650     /* Syntax error.  */
651     return NULL;
652   *line++ = '\0';
653
654   len = strlen (name) + 1;
655
656   result = (name_database_entry *) malloc (sizeof (name_database_entry) + len);
657   if (result == NULL)
658     return NULL;
659
660   /* Save the database name.  */
661   memcpy (result->name, name, len);
662
663   /* Parse the list of services.  */
664   result->service = nss_parse_service_list (line);
665
666   result->next = NULL;
667   return result;
668 }
669
670
671 static service_library *
672 internal_function
673 nss_new_service (name_database *database, const char *name)
674 {
675   service_library **currentp = &database->library;
676
677   while (*currentp != NULL)
678     {
679       if (strcmp ((*currentp)->name, name) == 0)
680         return *currentp;
681       currentp = &(*currentp)->next;
682     }
683
684   /* We have to add the new service.  */
685   *currentp = (service_library *) malloc (sizeof (service_library));
686   if (*currentp == NULL)
687     return NULL;
688
689   (*currentp)->name = name;
690   (*currentp)->lib_handle = NULL;
691   (*currentp)->next = NULL;
692
693   return *currentp;
694 }
695
696
697 /* Free all resources if necessary.  */
698 static void __attribute__ ((unused))
699 free_mem (void)
700 {
701   name_database *top = service_table;
702   name_database_entry *entry;
703   service_library *library;
704
705   if (top == NULL)
706     /* Maybe we have not read the nsswitch.conf file.  */
707     return;
708
709   /* Don't disturb ongoing other threads (if there are any).  */
710   service_table = NULL;
711
712   entry = top->entry;
713   while (entry != NULL)
714     {
715       name_database_entry *olde = entry;
716       service_user *service = entry->service;
717
718       while (service != NULL)
719         {
720           service_user *olds = service;
721
722           if (service->known != NULL)
723             __tdestroy (service->known, free);
724
725           service = service->next;
726           free (olds);
727         }
728
729       entry = entry->next;
730       free (olde);
731     }
732
733   library = top->library;
734   while (library != NULL)
735     {
736       service_library *oldl = library;
737
738       __libc_dlclose (library->lib_handle);
739
740       library = library->next;
741       free (oldl);
742     }
743
744   free (top);
745 }
746
747 text_set_element (__libc_subfreeres, free_mem);