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