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