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