Back to index

glibc  2.9
getnetgrent_r.c
Go to the documentation of this file.
00001 /* Copyright (C) 1996, 1997, 1998, 1999, 2002, 2004, 2005, 2007
00002    Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <assert.h>
00021 #include <atomic.h>
00022 #include <bits/libc-lock.h>
00023 #include <errno.h>
00024 #include <netdb.h>
00025 #include <stdbool.h>
00026 #include <stdlib.h>
00027 #include <string.h>
00028 #include "netgroup.h"
00029 #include "nsswitch.h"
00030 #include <sysdep.h>
00031 
00032 
00033 /* Protect above variable against multiple uses at the same time.  */
00034 __libc_lock_define_initialized (static, lock)
00035 
00036 /* The whole information for the set/get/endnetgrent functions are
00037    kept in this structure.  */
00038 static struct __netgrent dataset;
00039 
00040 /* The lookup function for the first entry of this service.  */
00041 extern int __nss_netgroup_lookup (service_user **nipp, const char *name,
00042                               void **fctp) internal_function;
00043 
00044 /* Set up NIP to run through the services.  Return nonzero if there are no
00045    services (left).  */
00046 static int
00047 setup (void **fctp, service_user **nipp)
00048 {
00049   /* Remember the first service_entry, it's always the same.  */
00050   static bool startp_initialized;
00051   static service_user *startp;
00052   int no_more;
00053 
00054   if (!startp_initialized)
00055     {
00056       /* Executing this more than once at the same time must yield the
00057         same result every time.  So we need no locking.  */
00058       no_more = __nss_netgroup_lookup (nipp, "setnetgrent", fctp);
00059       startp = no_more ? (service_user *) -1 : *nipp;
00060       PTR_MANGLE (startp);
00061       atomic_write_barrier ();
00062       startp_initialized = true;
00063     }
00064   else
00065     {
00066       service_user *nip = startp;
00067       PTR_DEMANGLE (nip);
00068       if (nip == (service_user *) -1)
00069        /* No services at all.  */
00070        return 1;
00071 
00072       /* Reset to the beginning of the service list.  */
00073       *nipp = nip;
00074       /* Look up the first function.  */
00075       no_more = __nss_lookup (nipp, "setnetgrent", NULL, fctp);
00076     }
00077   return no_more;
00078 }
00079 
00080 /* Free used memory.  */
00081 static void
00082 free_memory (struct __netgrent *data)
00083 {
00084   while (data->known_groups != NULL)
00085     {
00086       struct name_list *tmp = data->known_groups;
00087       data->known_groups = data->known_groups->next;
00088       free (tmp);
00089     }
00090 
00091   while (data->needed_groups != NULL)
00092     {
00093       struct name_list *tmp = data->needed_groups;
00094       data->needed_groups = data->needed_groups->next;
00095       free (tmp);
00096     }
00097 }
00098 
00099 static void
00100 endnetgrent_hook (struct __netgrent *datap)
00101 {
00102   enum nss_status (*endfct) (struct __netgrent *);
00103 
00104   if (datap->nip == NULL)
00105     return;
00106 
00107   endfct = __nss_lookup_function (datap->nip, "endnetgrent");
00108   if (endfct != NULL)
00109     (void) (*endfct) (datap);
00110   datap->nip = NULL;
00111 }
00112 
00113 static int
00114 internal_function
00115 __internal_setnetgrent_reuse (const char *group, struct __netgrent *datap,
00116                            int *errnop)
00117 {
00118   union
00119   {
00120     enum nss_status (*f) (const char *, struct __netgrent *);
00121     void *ptr;
00122   } fct;
00123   enum nss_status status = NSS_STATUS_UNAVAIL;
00124   struct name_list *new_elem;
00125 
00126   /* Free data from previous service.  */
00127   endnetgrent_hook (datap);
00128 
00129   /* Cycle through all the services and run their setnetgrent functions.  */
00130   int no_more = setup (&fct.ptr, &datap->nip);
00131   while (! no_more)
00132     {
00133       assert (datap->data == NULL);
00134 
00135       /* Ignore status, we force check in `__nss_next2'.  */
00136       status = (*fct.f) (group, datap);
00137 
00138       service_user *old_nip = datap->nip;
00139       no_more = __nss_next2 (&datap->nip, "setnetgrent", NULL, &fct.ptr,
00140                           status, 0);
00141 
00142       if (status == NSS_STATUS_SUCCESS && ! no_more)
00143        {
00144          enum nss_status (*endfct) (struct __netgrent *);
00145 
00146          endfct = __nss_lookup_function (old_nip, "endnetgrent");
00147          if (endfct != NULL)
00148            (void) (*endfct) (datap);
00149        }
00150     }
00151 
00152   /* Add the current group to the list of known groups.  */
00153   size_t group_len = strlen (group) + 1;
00154   new_elem = (struct name_list *) malloc (sizeof (struct name_list)
00155                                      + group_len);
00156   if (new_elem == NULL)
00157     {
00158       *errnop = errno;
00159       status = NSS_STATUS_TRYAGAIN;
00160     }
00161   else
00162     {
00163       new_elem->next = datap->known_groups;
00164       memcpy (new_elem->name, group, group_len);
00165       datap->known_groups = new_elem;
00166     }
00167 
00168   return status == NSS_STATUS_SUCCESS;
00169 }
00170 
00171 int internal_setnetgrent (const char *group, struct __netgrent *datap);
00172 libc_hidden_proto (internal_setnetgrent)
00173 
00174 int
00175 internal_setnetgrent (const char *group, struct __netgrent *datap)
00176 {
00177   /* Free list of all netgroup names from last run.  */
00178   free_memory (datap);
00179 
00180   return __internal_setnetgrent_reuse (group, datap, &errno);
00181 }
00182 libc_hidden_def (internal_setnetgrent)
00183 strong_alias (internal_setnetgrent, __internal_setnetgrent)
00184 
00185 int
00186 setnetgrent (const char *group)
00187 {
00188   int result;
00189 
00190   __libc_lock_lock (lock);
00191 
00192   result = internal_setnetgrent (group, &dataset);
00193 
00194   __libc_lock_unlock (lock);
00195 
00196   return result;
00197 }
00198 
00199 void internal_endnetgrent (struct __netgrent *datap);
00200 libc_hidden_proto (internal_endnetgrent)
00201 
00202 void
00203 internal_endnetgrent (struct __netgrent *datap)
00204 {
00205   endnetgrent_hook (datap);
00206   /* Now free list of all netgroup names from last run.  */
00207   free_memory (datap);
00208 }
00209 libc_hidden_def (internal_endnetgrent)
00210 strong_alias (internal_endnetgrent, __internal_endnetgrent)
00211 
00212 
00213 void
00214 endnetgrent (void)
00215 {
00216   __libc_lock_lock (lock);
00217 
00218   internal_endnetgrent (&dataset);
00219 
00220   __libc_lock_unlock (lock);
00221 }
00222 
00223 
00224 int internal_getnetgrent_r (char **hostp, char **userp, char **domainp,
00225                          struct __netgrent *datap,
00226                          char *buffer, size_t buflen, int *errnop);
00227 libc_hidden_proto (internal_getnetgrent_r)
00228 
00229 int
00230 internal_getnetgrent_r (char **hostp, char **userp, char **domainp,
00231                        struct __netgrent *datap,
00232                        char *buffer, size_t buflen, int *errnop)
00233 {
00234   enum nss_status (*fct) (struct __netgrent *, char *, size_t, int *);
00235 
00236   /* Initialize status to return if no more functions are found.  */
00237   enum nss_status status = NSS_STATUS_NOTFOUND;
00238 
00239   /* Run through available functions, starting with the same function last
00240      run.  We will repeat each function as long as it succeeds, and then go
00241      on to the next service action.  */
00242   int no_more = (datap->nip == NULL
00243                || (fct = __nss_lookup_function (datap->nip, "getnetgrent_r"))
00244                   == NULL);
00245   while (! no_more)
00246     {
00247       status = (*fct) (datap, buffer, buflen, &errno);
00248 
00249       if (status == NSS_STATUS_RETURN)
00250        {
00251          /* This was the last one for this group.  Look at next group
00252             if available.  */
00253          int found = 0;
00254          while (datap->needed_groups != NULL && ! found)
00255            {
00256              struct name_list *tmp = datap->needed_groups;
00257              datap->needed_groups = datap->needed_groups->next;
00258              tmp->next = datap->known_groups;
00259              datap->known_groups = tmp;
00260 
00261              found = __internal_setnetgrent_reuse (datap->known_groups->name,
00262                                               datap, errnop);
00263            }
00264 
00265          if (found && datap->nip != NULL)
00266            {
00267              fct = __nss_lookup_function (datap->nip, "getnetgrent_r");
00268              if (fct != NULL)
00269               continue;
00270            }
00271        }
00272       else if (status == NSS_STATUS_SUCCESS && datap->type == group_val)
00273        {
00274          /* The last entry was a name of another netgroup.  */
00275          struct name_list *namep;
00276 
00277          /* Ignore if we've seen the name before.  */
00278          for (namep = datap->known_groups; namep != NULL;
00279               namep = namep->next)
00280            if (strcmp (datap->val.group, namep->name) == 0)
00281              break;
00282          if (namep != NULL)
00283            /* Really ignore.  */
00284            continue;
00285 
00286          size_t group_len = strlen (datap->val.group) + 1;
00287          namep = (struct name_list *) malloc (sizeof (struct name_list)
00288                                           + group_len);
00289          if (namep == NULL)
00290            /* We are out of memory.  */
00291            status = NSS_STATUS_RETURN;
00292          else
00293            {
00294              namep->next = datap->needed_groups;
00295              memcpy (namep->name, datap->val.group, group_len);
00296              datap->needed_groups = namep;
00297              /* And get the next entry.  */
00298              continue;
00299            }
00300        }
00301 
00302       break;
00303     }
00304 
00305   if (status == NSS_STATUS_SUCCESS)
00306     {
00307       *hostp = (char *) datap->val.triple.host;
00308       *userp = (char *) datap->val.triple.user;
00309       *domainp = (char *) datap->val.triple.domain;
00310     }
00311 
00312   return status == NSS_STATUS_SUCCESS ? 1 : 0;
00313 }
00314 libc_hidden_def (internal_getnetgrent_r)
00315 strong_alias (internal_getnetgrent_r, __internal_getnetgrent_r)
00316 
00317 /* The real entry point.  */
00318 int
00319 __getnetgrent_r (char **hostp, char **userp, char **domainp,
00320                char *buffer, size_t buflen)
00321 {
00322   enum nss_status status;
00323 
00324   __libc_lock_lock (lock);
00325 
00326   status = internal_getnetgrent_r (hostp, userp, domainp, &dataset,
00327                                buffer, buflen, &errno);
00328 
00329   __libc_lock_unlock (lock);
00330 
00331   return status;
00332 }
00333 weak_alias (__getnetgrent_r, getnetgrent_r)
00334 
00335 /* Test whether given (host,user,domain) triple is in NETGROUP.  */
00336 int
00337 innetgr (const char *netgroup, const char *host, const char *user,
00338         const char *domain)
00339 {
00340   union
00341   {
00342     int (*f) (const char *, struct __netgrent *);
00343     void *ptr;
00344   } setfct;
00345   void (*endfct) (struct __netgrent *);
00346   int (*getfct) (struct __netgrent *, char *, size_t, int *);
00347   struct __netgrent entry;
00348   int result = 0;
00349   const char *current_group = netgroup;
00350   int real_entry = 0;
00351 
00352   memset (&entry, '\0', sizeof (entry));
00353 
00354   /* Walk through the services until we found an answer or we shall
00355      not work further.  We can do some optimization here.  Since all
00356      services must provide the `setnetgrent' function we can do all
00357      the work during one walk through the service list.  */
00358   while (1)
00359     {
00360       int no_more = setup (&setfct.ptr, &entry.nip);
00361       while (! no_more)
00362        {
00363          assert (entry.data == NULL);
00364 
00365          /* Open netgroup.  */
00366          enum nss_status status = (*setfct.f) (current_group, &entry);
00367 
00368          if (status == NSS_STATUS_SUCCESS
00369              && (getfct = __nss_lookup_function (entry.nip, "getnetgrent_r"))
00370                != NULL)
00371            {
00372              char buffer[1024];
00373 
00374              while ((*getfct) (&entry, buffer, sizeof buffer, &errno)
00375                    == NSS_STATUS_SUCCESS)
00376               {
00377                 if (entry.type == group_val)
00378                   {
00379                     /* Make sure we haven't seen the name before.  */
00380                     struct name_list *namep;
00381 
00382                     for (namep = entry.known_groups; namep != NULL;
00383                         namep = namep->next)
00384                      if (strcmp (entry.val.group, namep->name) == 0)
00385                        break;
00386                     if (namep == NULL
00387                        && strcmp (netgroup, entry.val.group) != 0)
00388                      {
00389                        size_t group_len = strlen (entry.val.group) + 1;
00390                        namep =
00391                          (struct name_list *) malloc (sizeof (*namep)
00392                                                   + group_len);
00393                        if (namep == NULL)
00394                          {
00395                            /* Out of memory, simply return.  */
00396                            result = -1;
00397                            break;
00398                          }
00399 
00400                        namep->next = entry.needed_groups;
00401                        memcpy (namep->name, entry.val.group, group_len);
00402                        entry.needed_groups = namep;
00403                      }
00404                   }
00405                 else
00406                   {
00407                     real_entry = 1;
00408 
00409                     if ((entry.val.triple.host == NULL || host == NULL
00410                         || __strcasecmp (entry.val.triple.host, host) == 0)
00411                        && (entry.val.triple.user == NULL || user == NULL
00412                            || strcmp (entry.val.triple.user, user) == 0)
00413                        && (entry.val.triple.domain == NULL || domain == NULL
00414                            || __strcasecmp (entry.val.triple.domain,
00415                                           domain) == 0))
00416                      {
00417                        result = 1;
00418                        break;
00419                      }
00420                   }
00421               }
00422 
00423              /* If we found one service which does know the given
00424                netgroup we don't try further.  */
00425              status = NSS_STATUS_RETURN;
00426            }
00427 
00428          /* Free all resources of the service.  */
00429          endfct = __nss_lookup_function (entry.nip, "endnetgrent");
00430          if (endfct != NULL)
00431            (*endfct) (&entry);
00432 
00433          if (result != 0)
00434            break;
00435 
00436          /* Look for the next service.  */
00437          no_more = __nss_next2 (&entry.nip, "setnetgrent", NULL,
00438                              &setfct.ptr, status, 0);
00439        }
00440 
00441       if (result == 0 && entry.needed_groups != NULL)
00442        {
00443          struct name_list *tmp = entry.needed_groups;
00444          entry.needed_groups = tmp->next;
00445          tmp->next = entry.known_groups;
00446          entry.known_groups = tmp;
00447          current_group = entry.known_groups->name;
00448          continue;
00449        }
00450 
00451       /* No way out.  */
00452       break;
00453     }
00454 
00455   /* Free the memory.  */
00456   free_memory (&entry);
00457 
00458   return result == 1;
00459 }
00460 libc_hidden_def (innetgr)