1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
  23  *
  24  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  25  * Use is subject to license terms.
  26  */
  27 
  28 /*
  29  *      files/getnetgrent.c -- "files" backend for nsswitch "netgroup" database
  30  *
  31  *      The API for netgroups differs sufficiently from that for the average
  32  *      getXXXbyYYY function that we use very few of the support routines in
  33  *      files_common.h.
  34  *
  35  *      The implementation of setnetgrent()/getnetgrent() here follows the
  36  *      the 4.x code, inasmuch as the setnetgrent() routine does all the work
  37  *      of traversing the netgroup graph and building a (potentially large)
  38  *      list in memory, and getnetgrent() just steps down the list.
  39  *
  40  *      An alternative, and probably better, implementation would lazy-eval
  41  *      the netgroup graph in response to getnetgrent() calls (though
  42  *      setnetgrent() should still check for the top-level netgroup name
  43  *      and return NSS_SUCCESS / NSS_NOTFOUND).
  44  */
  45 
  46 #include "files_common.h"
  47 #include <ctype.h>
  48 #include <rpcsvc/ypclnt.h>
  49 #include <malloc.h>
  50 #include <string.h>
  51 #include <ctype.h>
  52 
  53 /*
  54  * Tricky debug support
  55  */
  56 
  57 #pragma weak __nss_files_netgr_debug
  58 #pragma weak __nss_files_netgr_error
  59 extern void __nss_files_netgr_debug(const char *, ...);
  60 extern void __nss_files_netgr_error(const char *, ...);
  61 
  62 /*
  63  * Start of stuff borrowed from getgrent.c
  64  */
  65 static uint_t
  66 hash_netgrname(nss_XbyY_args_t *argp, int keyhash, const char *line,
  67         int linelen)
  68 {
  69         const char      *name;
  70         int             namelen, i;
  71         uint_t          hash = 0;
  72 
  73         if (keyhash) {
  74                 name = argp->key.name;
  75                 namelen = strlen(name);
  76         } else {
  77                 name = line;
  78                 namelen = 0;
  79                 while (linelen-- && !isspace(*line)) {
  80                         line++;
  81                         namelen++;
  82                 }
  83         }
  84 
  85         for (i = 0; i < namelen; i++)
  86                 hash = hash * 15 + name[i];
  87         return (hash);
  88 }
  89 
  90 static files_hash_func hash_netgr[1] = { hash_netgrname };
  91 
  92 static files_hash_t hashinfo = {
  93         DEFAULTMUTEX,
  94         sizeof (struct nss_netgrent),
  95         NSS_LINELEN_NETGROUP,
  96         1,
  97         hash_netgr
  98 };
  99 
 100 static int
 101 check_netgrname(nss_XbyY_args_t *argp, const char *line, int linelen)
 102 {
 103         const char *linep, *limit;
 104         const char *keyp = argp->key.name;
 105 
 106         linep = line;
 107         limit = line + linelen;
 108 
 109         /* +/- entries valid for compat source only */
 110         if (linelen == 0 || *line == '+' || *line == '-')
 111                 return (0);
 112         while (*keyp && linep < limit && *keyp == *linep) {
 113                 keyp++;
 114                 linep++;
 115         }
 116         return (linep < limit && *keyp == '\0' && isspace(*linep));
 117 }
 118 
 119 static nss_status_t
 120 getbyname(be, a)
 121         files_backend_ptr_t     be;
 122         void                    *a;
 123 {
 124         return (_nss_files_XY_hash(be, a, 1, &hashinfo, 0, check_netgrname));
 125 }
 126 
 127 /*
 128  * End of stuff borrowed from getgrent.c
 129  *
 130  * Now some "glue" functions based loosely on
 131  *   lib/libc/port/gen/getgrnam_r.c
 132  */
 133 
 134 
 135 /*
 136  * This is a special purpose str2ent (parse) function used only in
 137  * the _nss_files_getbyname() below.  A general-purpose version of
 138  * this parser would copy the incoming line buffer to the passed
 139  * temporary buffer, and fill in the passed struct nss_netgrent with
 140  * pointers into that temporary buffer.  Our caller only needs the
 141  * list of members of this netgroup, and since that string already
 142  * exists in ready-to-use form in the incoming line buffer, we just
 143  * use that.  Also special here is the fact that we allocate a copy
 144  * of the member list, both because the caller wants it allocated,
 145  * and because the buffer at *instr will change after we return.
 146  * The caller passes null for a temporary buffer, which we ignore.
 147  *
 148  * See the test program: cmd/nsstest/netgr_get.c
 149  * for a more generic version of this function.
 150  */
 151 /* ARGSUSED */
 152 static int
 153 str2netgr(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
 154 {
 155         const char sep[] = " \t\n";
 156         struct nss_netgrent *netgr = ent;
 157         const char      *p;
 158 
 159         /* skip leading space */
 160         p = instr;
 161         while (isspace(*p))
 162                 p++;
 163 
 164         /* should be at the key */
 165         if (*p == '\0')
 166                 return (NSS_STR_PARSE_PARSE);
 167         /* Full parser would set netgr_name = p here. */
 168 
 169         /* skip the key ... */
 170         p = strpbrk(p, sep);
 171         if (p == NULL)
 172                 return (NSS_STR_PARSE_PARSE);
 173         /* Full parser would store a null at *p here. */
 174 
 175         /* skip separators */
 176         while (isspace(*p))
 177                 p++;
 178 
 179         /*
 180          * Should be at the members list, which is the
 181          * rest of the input line.
 182          */
 183         if (*p == '\0')
 184                 return (NSS_STR_PARSE_PARSE);
 185 
 186         /*
 187          * Caller wants this allocated.  Do it now,
 188          * before the inbuf gets re-used.
 189          */
 190         netgr->netgr_members = strdup(p);
 191 
 192         return (NSS_STR_PARSE_SUCCESS);
 193 }
 194 
 195 /*
 196  * This is a compatibility "shim" used by top_down() to get
 197  * the list of members for some netgroup.  On success, the
 198  * list of members is returned in allocated memory via valp.
 199  */
 200 static nss_status_t
 201 netgr_get_members(struct files_backend *be,
 202         const char              *name,
 203         char                    **valp)
 204 {
 205         struct nss_netgrent netgr;
 206         nss_XbyY_args_t args;
 207         nss_status_t result;
 208 
 209         if (name == (const char *)NULL)
 210                 return (NSS_ERROR);
 211 
 212         (void) memset(&netgr, 0, sizeof (netgr));
 213         (void) memset(&args, 0, sizeof (args));
 214         args.buf.result = &netgr;
 215         args.str2ent = str2netgr;
 216         args.key.name = name;
 217         result = getbyname(be, &args);
 218 
 219         if (result == NSS_SUCCESS) {
 220                 /* Note: allocated memory. */
 221                 *valp = netgr.netgr_members;
 222                 if (*valp == NULL)
 223                         result = NSS_UNAVAIL;
 224         }
 225 
 226         return (result);
 227 }
 228 
 229 
 230 /*
 231  * End "glue" functions
 232  *
 233  * The rest of this is based on:
 234  *  lib/nsswitch/nis/common/getnetgrent.c
 235  */
 236 
 237 
 238 /*
 239  * The nss_backend_t for a getnetgrent() sequence;  we actually give the
 240  *   netgroup frontend a pointer to one of these structures in response to
 241  *   a (successful) setnetgrent() call on the files_backend backend
 242  *   described further down in this file.
 243  */
 244 
 245 struct files_getnetgr_be;
 246 typedef nss_status_t    (*files_getnetgr_op_t)(
 247         struct files_getnetgr_be *, void *);
 248 
 249 struct files_getnetgr_be {
 250         files_getnetgr_op_t     *ops;
 251         nss_dbop_t              n_ops;
 252         /*
 253          * State for set/get/endnetgrent()
 254          */
 255         char                    *netgroup;
 256         struct grouplist        *all_members;
 257         struct grouplist        *next_member;
 258 };
 259 
 260 struct grouplist {  /* One element of the list generated by a setnetgrent() */
 261         char                    *triple[NSS_NETGR_N];
 262         struct  grouplist       *gl_nxt;
 263 };
 264 
 265 static nss_status_t
 266 getnetgr_set(be, a)
 267         struct files_getnetgr_be        *be;
 268         void                    *a;
 269 {
 270         const char              *netgroup = (const char *) a;
 271 
 272         if (be->netgroup != 0 &&
 273             strcmp(be->netgroup, netgroup) == 0) {
 274                 /* We already have the member-list;  regurgitate it */
 275                 be->next_member = be->all_members;
 276                 return (NSS_SUCCESS);
 277         }
 278         return (NSS_NOTFOUND);
 279 }
 280 
 281 static nss_status_t
 282 getnetgr_get(be, a)
 283         struct files_getnetgr_be        *be;
 284         void                    *a;
 285 {
 286         struct nss_getnetgrent_args *args = (struct nss_getnetgrent_args *)a;
 287         struct grouplist        *mem;
 288 
 289         if ((mem = be->next_member) == 0) {
 290                 args->status = NSS_NETGR_NO;
 291         } else {
 292                 char                    *buffer = args->buffer;
 293                 int                     buflen  = args->buflen;
 294                 enum nss_netgr_argn     i;
 295 
 296                 args->status = NSS_NETGR_FOUND;
 297 
 298                 for (i = 0;  i < NSS_NETGR_N;  i++) {
 299                         const char      *str;
 300                         ssize_t len;
 301 
 302                         if ((str = mem->triple[i]) == 0) {
 303                                 args->retp[i] = 0;
 304                         } else if ((len = strlen(str) + 1) <= buflen) {
 305                                 args->retp[i] = buffer;
 306                                 (void) memcpy(buffer, str, len);
 307                                 buffer += len;
 308                                 buflen -= len;
 309                         } else {
 310                                 args->status = NSS_NETGR_NOMEM;
 311                                 break;
 312                         }
 313                 }
 314                 be->next_member      = mem->gl_nxt;
 315         }
 316         return (NSS_SUCCESS);   /* Yup, even for end-of-list, i.e. */
 317                                 /* do NOT advance to next backend. */
 318 }
 319 
 320 /*ARGSUSED*/
 321 static nss_status_t
 322 getnetgr_end(be, dummy)
 323         struct files_getnetgr_be        *be;
 324         void                    *dummy;
 325 {
 326         struct grouplist        *gl;
 327         struct grouplist        *next;
 328 
 329         for (gl = be->all_members; gl != NULL; gl = next) {
 330                 enum nss_netgr_argn     i;
 331 
 332                 next = gl->gl_nxt;
 333                 for (i = NSS_NETGR_MACHINE;  i < NSS_NETGR_N;  i++) {
 334                         if (gl->triple[i] != 0) {
 335                                 free(gl->triple[i]);
 336                         }
 337                 }
 338                 free(gl);
 339         }
 340         be->all_members = 0;
 341         be->next_member = 0;
 342         if (be->netgroup != 0) {
 343                 free(be->netgroup);
 344                 be->netgroup = 0;
 345         }
 346         return (NSS_SUCCESS);
 347 }
 348 
 349 /*ARGSUSED*/
 350 static nss_status_t
 351 getnetgr_destr(be, dummy)
 352         struct files_getnetgr_be        *be;
 353         void                    *dummy;
 354 {
 355         if (be != 0) {
 356                 (void) getnetgr_end(be, (void *)0);
 357                 free(be);
 358         }
 359         return (NSS_SUCCESS);
 360 }
 361 
 362 static files_getnetgr_op_t getnetgr_ops[] = {
 363         getnetgr_destr,
 364         getnetgr_end,
 365         getnetgr_set,
 366         getnetgr_get,   /* getnetgrent_r() */
 367 };
 368 
 369 
 370 /*
 371  * The nss_backend_t for innetgr() and setnetgrent().
 372  * Also getbyname(), but that's only for testing.
 373  */
 374 
 375 
 376 
 377 /*
 378  * Code to do top-down search in the graph defined by the 'netgroup' YP map
 379  */
 380 
 381 /*
 382  * ===> This code is now used for setnetgrent(), not just innetgr().
 383  *
 384  * If the easy way doesn't pan out, recursively search the 'netgroup' map.
 385  * In order to do this, we:
 386  *
 387  *    - remember all the netgroup names we've seen during this search,
 388  *      whether or not we've expanded them yet (we want fast insertion
 389  *      with duplicate-detection, so use yet another chained hash table),
 390  *
 391  *    - keep a list of all the netgroups we haven't expanded yet (we just
 392  *      want fast insertion and pop-first, so a linked list will do fine).
 393  *      If we insert at the head, we get a depth-first search;  insertion
 394  *      at the tail gives breadth-first (?), which seems preferable (?).
 395  *
 396  * A netgrnam struct contains pointers for both the hash-table and the list.
 397  * It also contains the netgroup name;  note that we embed the name at the
 398  * end of the structure rather than holding a pointer to yet another
 399  * malloc()ed region.
 400  *
 401  * A netgrtab structure contains the hash-chain heads and the head/tail
 402  * pointers for the expansion list.
 403  */
 404 
 405 struct netgrnam {
 406         struct netgrnam *hash_chain;
 407         struct netgrnam *expand_next;
 408         char            name[1];        /* Really [strlen(name) + 1] */
 409 };
 410 
 411 #define HASHMOD 113
 412 
 413 struct netgrtab {
 414         struct netgrnam *expand_first;
 415         struct netgrnam **expand_lastp;
 416         struct netgrnam *hash_heads[HASHMOD];
 417 };
 418 
 419 static void
 420 ngt_init(ngt)
 421         struct netgrtab *ngt;
 422 {
 423         (void) memset((void *)ngt, 0, sizeof (*ngt));
 424         ngt->expand_lastp = &ngt->expand_first;
 425 }
 426 
 427 /* === ? Change ngt_init() and ngt_destroy() to malloc/free struct netgrtab */
 428 
 429 static void
 430 /* ==> ? Should return 'failed' (out-of-memory) status ? */
 431 ngt_insert(ngt, name, namelen)
 432         struct netgrtab *ngt;
 433         const char      *name;
 434         size_t          namelen;
 435 {
 436         unsigned        hashval;
 437         size_t          i;
 438         struct netgrnam *cur;
 439         struct netgrnam **head;
 440 
 441 #define dummy           ((struct netgrnam *)0)
 442 
 443         if (__nss_files_netgr_debug != NULL)
 444                 __nss_files_netgr_debug(
 445                     "ngt_insert: ngt=%p names=%s", ngt, name);
 446 
 447         for (hashval = 0, i = 0;  i < namelen;  i++) {
 448                 hashval = (hashval << 2) + hashval +
 449                         ((const unsigned char *)name)[i];
 450         }
 451         head = &ngt->hash_heads[hashval % HASHMOD];
 452         for (cur = *head;  cur != 0;  cur = cur->hash_chain) {
 453                 if (strncmp(cur->name, name, namelen) == 0 &&
 454                     cur->name[namelen] == 0) {
 455                         return;         /* Already in table, do nothing */
 456                 }
 457         }
 458         /* Create new netgrnam struct */
 459         cur = (struct netgrnam *)
 460                 malloc(namelen + 1 + (char *)&dummy->name[0] - (char *)dummy);
 461         if (cur == 0) {
 462                 return;                 /* Out of memory, too bad */
 463         }
 464         (void) memcpy(cur->name, name, namelen);
 465         cur->name[namelen] = 0;
 466 
 467         /* Insert in hash table */
 468         cur->hash_chain = *head;
 469         *head = cur;
 470 
 471         /* Insert in expansion list (insert at end for breadth-first search */
 472         cur->expand_next = 0;
 473         *ngt->expand_lastp = cur;
 474         ngt->expand_lastp = &cur->expand_next;
 475 
 476 #undef  dummy
 477 }
 478 
 479 static const char *
 480 ngt_next(ngt)
 481         struct netgrtab *ngt;
 482 {
 483         struct netgrnam *first;
 484 
 485         if ((first = ngt->expand_first) == 0) {
 486                 return (0);
 487         }
 488         if ((ngt->expand_first = first->expand_next) == 0) {
 489                 ngt->expand_lastp = &ngt->expand_first;
 490         }
 491         return (first->name);
 492 }
 493 
 494 static void
 495 ngt_destroy(ngt)
 496         struct netgrtab *ngt;
 497 {
 498         struct netgrnam *cur;
 499         struct netgrnam *next;
 500         int             i;
 501 
 502         for (i = 0;  i < HASHMOD;  i++) {
 503                 for (cur = ngt->hash_heads[i];  cur != 0; /* cstyle */) {
 504                         next = cur->hash_chain;
 505                         free(cur);
 506                         cur = next;
 507                 }
 508         }
 509         /* Don't bother zeroing pointers;  must do init if we want to reuse */
 510 }
 511 
 512 typedef const char *ccp;
 513 
 514 static nss_status_t
 515 top_down(struct files_backend *be, const char **groups, int ngroups,
 516     int (*func)(ccp triple[3], void *iter_args, nss_status_t *return_val),
 517     void *iter_args)
 518 {
 519         struct netgrtab         *ngt;
 520         /* netgrtab goes on the heap, not the stack, because it's large and */
 521         /* stacks may not be all that big in multi-threaded programs. */
 522 
 523         const char              *group;
 524         int                     nfound;
 525         int                     done;
 526         nss_status_t            result;
 527 
 528         if ((ngt = (struct netgrtab *)malloc(sizeof (*ngt))) == 0) {
 529                 return (NSS_UNAVAIL);
 530         }
 531         ngt_init(ngt);
 532 
 533         while (ngroups > 0) {
 534                 ngt_insert(ngt, *groups, strlen(*groups));
 535                 groups++;
 536                 ngroups--;
 537         }
 538 
 539         done    = 0;    /* Set to 1 to indicate that we cut the iteration  */
 540                         /*   short (and 'result' holds the return value)   */
 541         nfound  = 0;    /* Number of successful netgroup getbyname calls   */
 542 
 543         while (!done && (group = ngt_next(ngt)) != 0) {
 544                 char            *val = NULL;
 545                 char            *p;
 546 
 547                 result = netgr_get_members(be, group, &val);
 548                 if (result != NSS_SUCCESS) {
 549                         /*LINTED E_NOP_IF_STMT*/
 550                         if (result == NSS_NOTFOUND) {
 551                                 if (__nss_files_netgr_error != NULL)
 552                                         __nss_files_netgr_error(
 553                             "files netgroup lookup: %s doesn't exist",
 554                                             group);
 555                         } else {
 556                                 if (__nss_files_netgr_error != NULL)
 557                                         __nss_files_netgr_error(
 558                         "files netgroup lookup: getbyname returned [%s]",
 559                                             strerror(errno));
 560                                 done = 1;       /* Give up, return result */
 561                         }
 562                         /* Don't need to clean up anything */
 563                         continue;
 564                 }
 565 
 566                 if (__nss_files_netgr_debug != NULL)
 567                         __nss_files_netgr_debug(
 568                             "ngt_top: ngt=%p grp=%s members=\"%s\"",
 569                             ngt, group, val);
 570 
 571                 nfound++;
 572 
 573                 if ((p = strpbrk(val, "#\n")) != 0) {
 574                         *p = '\0';
 575                 }
 576                 p = val;
 577 
 578                 /* Parse val into triples and recursive netgroup references */
 579                 /*CONSTCOND*/
 580                 while (1) {
 581                         ccp                     triple[NSS_NETGR_N];
 582                         int                     syntax_err;
 583                         enum nss_netgr_argn     i;
 584 
 585                         while (isspace(*p)) {
 586                                 p++;
 587                         }
 588                         if (*p == '\0') {
 589                                 /* Finished processing this particular val */
 590                                 break;
 591                         }
 592                         if (*p != '(') {
 593                                 /* Doesn't look like the start of a triple, */
 594                                 /*   so assume it's a recursive netgroup.   */
 595                                 char *start = p;
 596                                 p = strpbrk(start, " \t");
 597                                 if (p == 0) {
 598                                         /* Point p at the final '\0' */
 599                                         p = start + strlen(start);
 600                                 }
 601                                 ngt_insert(ngt, start, (size_t)(p - start));
 602                                 continue;
 603                         }
 604 
 605                         /* Main case:  a (machine, user, domain) triple */
 606                         p++;
 607                         syntax_err = 0;
 608                         for (i = NSS_NETGR_MACHINE; i < NSS_NETGR_N; i++) {
 609                                 char            *start;
 610                                 char            *limit;
 611                                 const char      *terminators = ",) \t";
 612 
 613                                 if (i == NSS_NETGR_DOMAIN) {
 614                                         /* Don't allow comma */
 615                                         terminators++;
 616                                 }
 617                                 while (isspace(*p)) {
 618                                         p++;
 619                                 }
 620                                 start = p;
 621                                 limit = strpbrk(start, terminators);
 622                                 if (limit == 0) {
 623                                         syntax_err++;
 624                                         break;
 625                                 }
 626                                 p = limit;
 627                                 while (isspace(*p)) {
 628                                         p++;
 629                                 }
 630                                 if (*p == terminators[0]) {
 631                                         /*
 632                                          * Successfully parsed this name and
 633                                          *   the separator after it (comma or
 634                                          *   right paren); leave p ready for
 635                                          *   next parse.
 636                                          */
 637                                         p++;
 638                                         if (start == limit) {
 639                                                 /* Wildcard */
 640                                                 triple[i] = 0;
 641                                         } else {
 642                                                 *limit = '\0';
 643                                                 triple[i] = start;
 644                                         }
 645                                 } else {
 646                                         syntax_err++;
 647                                         break;
 648                                 }
 649                         }
 650 
 651                         if (syntax_err) {
 652 /*
 653  * ===> log it;
 654  * ===> try skipping past next ')';  failing that, abandon the line;
 655  */
 656                                 break;  /* Abandon this line */
 657                         } else if (!(*func)(triple, iter_args, &result)) {
 658                                 /* Return result, good or bad */
 659                                 done = 1;
 660                                 break;
 661                         }
 662                 }
 663                 /* End of inner loop over val[] */
 664                 free(val);
 665                 val = NULL;
 666         }
 667         /* End of outer loop (!done && ngt_next(ngt) != 0) */
 668 
 669         ngt_destroy(ngt);
 670         free(ngt);
 671 
 672         if (done) {
 673                 return (result);
 674         } else if (nfound > 0) {
 675                 /* ==== ? Should only do this if all the top-level groups */
 676                 /*        exist in YP?                                    */
 677                 return (NSS_SUCCESS);
 678         } else {
 679                 return (NSS_NOTFOUND);
 680         }
 681 }
 682 
 683 
 684 /*
 685  * Code for setnetgrent()
 686  */
 687 
 688 /*
 689  * Iterator function for setnetgrent():  copy triple, add to be->all_members
 690  */
 691 static int
 692 save_triple(ccp trippp[NSS_NETGR_N], void *headp_arg,
 693     nss_status_t *return_val)
 694 {
 695         struct grouplist        **headp = headp_arg;
 696         struct grouplist        *gl;
 697         enum nss_netgr_argn     i;
 698 
 699         if (__nss_files_netgr_debug != NULL)
 700                 __nss_files_netgr_debug(
 701                     "save_tripple: h=%s u=%s d=%s",
 702                     trippp[0] ? trippp[0] : "*",
 703                     trippp[1] ? trippp[1] : "*",
 704                     trippp[2] ? trippp[2] : "*");
 705 
 706         if ((gl = (struct grouplist *)malloc(sizeof (*gl))) == 0) {
 707                 /* Out of memory */
 708                 *return_val = NSS_UNAVAIL;
 709                 return (0);
 710         }
 711         for (i = NSS_NETGR_MACHINE;  i < NSS_NETGR_N;  i++) {
 712                 if (trippp[i] == 0) {
 713                         /* Wildcard */
 714                         gl->triple[i] = 0;
 715                 } else if ((gl->triple[i] = strdup(trippp[i])) == 0) {
 716                         /* Out of memory.  Free any we've allocated */
 717                         enum nss_netgr_argn     j;
 718 
 719                         for (j = NSS_NETGR_MACHINE;  j < i;  j++) {
 720                                 if (gl->triple[j] != 0) {
 721                                         free(gl->triple[j]);
 722                                 }
 723                         }
 724                         *return_val = NSS_UNAVAIL;
 725                         return (0);
 726                 }
 727         }
 728         gl->gl_nxt = *headp;
 729         *headp = gl;
 730         return (1);     /* Tell top_down() to keep iterating */
 731 }
 732 
 733 static nss_status_t
 734 netgr_set(be, a)
 735         struct files_backend    *be;
 736         void                    *a;
 737 {
 738         struct nss_setnetgrent_args *args = (struct nss_setnetgrent_args *)a;
 739         struct files_getnetgr_be        *get_be;
 740         nss_status_t            res;
 741 
 742         get_be = (struct files_getnetgr_be *)malloc(sizeof (*get_be));
 743         if (get_be == 0) {
 744                 return (NSS_UNAVAIL);
 745         }
 746 
 747         get_be->all_members = 0;
 748         res = top_down(be, &args->netgroup, 1, save_triple,
 749                 &get_be->all_members);
 750 
 751         if (res == NSS_SUCCESS) {
 752                 get_be->ops          = getnetgr_ops;
 753                 get_be->n_ops                = sizeof (getnetgr_ops) /
 754                                                 sizeof (getnetgr_ops[0]);
 755                 get_be->netgroup     = strdup(args->netgroup);
 756                 get_be->next_member  = get_be->all_members;
 757 
 758                 args->iterator               = (nss_backend_t *)get_be;
 759         } else {
 760                 args->iterator               = 0;
 761                 free(get_be);
 762         }
 763         return (res);
 764 }
 765 
 766 
 767 /*
 768  * Code for innetgr()
 769  */
 770 
 771 /*
 772  * Iterator function for innetgr():  Check whether triple matches args
 773  */
 774 static int
 775 match_triple(ccp triple[NSS_NETGR_N], void *ia_arg, nss_status_t *return_val)
 776 {
 777         struct nss_innetgr_args *ia = ia_arg;
 778         enum nss_netgr_argn     i;
 779 
 780         if (__nss_files_netgr_debug != NULL)
 781                 __nss_files_netgr_debug(
 782                     "match_tripple: h=%s u=%s d=%s",
 783                     triple[0] ? triple[0] : "*",
 784                     triple[1] ? triple[1] : "*",
 785                     triple[2] ? triple[2] : "*");
 786 
 787         for (i = NSS_NETGR_MACHINE;  i < NSS_NETGR_N;  i++) {
 788                 int             (*cmpf)(const char *, const char *);
 789                 char            **argv;
 790                 int             n;
 791                 const char      *name = triple[i];
 792                 int             argc = ia->arg[i].argc;
 793 
 794                 if (argc == 0 || name == 0) {
 795                         /* Wildcarded on one side or t'other */
 796                         continue;
 797                 }
 798                 argv = ia->arg[i].argv;
 799                 cmpf = (i == NSS_NETGR_MACHINE) ? strcasecmp : strcmp;
 800                 for (n = 0;  n < argc;  n++) {
 801                         if ((*cmpf)(argv[n], name) == 0) {
 802                                 break;
 803                         }
 804                 }
 805                 if (n >= argc) {
 806                         /* Match failed, tell top_down() to keep looking */
 807                         return (1);
 808                 }
 809         }
 810         /* Matched on all three, so quit looking and declare victory */
 811 
 812         if (__nss_files_netgr_debug != NULL)
 813                 __nss_files_netgr_debug("match_tripple: found");
 814 
 815         ia->status = NSS_NETGR_FOUND;
 816         *return_val = NSS_SUCCESS;
 817         return (0);
 818 }
 819 
 820 /*
 821  * Used to have easy_way() and it's support functions here.
 822  */
 823 
 824 static nss_status_t
 825 netgr_in(be, a)
 826         struct files_backend    *be;
 827         void                    *a;
 828 {
 829         struct nss_innetgr_args *ia = (struct nss_innetgr_args *)a;
 830         nss_status_t            res;
 831 
 832         ia->status = NSS_NETGR_NO;
 833 
 834         /*
 835          * Used to have "easy_way" calls here for the cases
 836          * where we have just a user, or just a machine.
 837          *
 838          * That was important for NIS, where getting the list of
 839          * members for some netgroup was a yp_match call that may
 840          * need to go over-the-wire.  Here in the "files" backend,
 841          * getting the members of a group (getbyname) is a strictly
 842          * local operation, and is cached (see hashinfo above) so
 843          * it can normally complete with just memory operations.
 844          *
 845          * With a low-cost getbyname operation, the simple
 846          * top_down algorithm has acceptable performance.
 847          */
 848 
 849         /* Nope, try the slow way */
 850         ia->status = NSS_NETGR_NO;
 851         res = top_down(be, (const char **)ia->groups.argv, ia->groups.argc,
 852             match_triple, ia);
 853         return (res);
 854 }
 855 
 856 
 857 /*
 858  * (Almost) boilerplate for a switch backend
 859  */
 860 
 861 /*ARGSUSED*/
 862 static nss_status_t
 863 netgr_destr(be, dummy)
 864         struct files_backend    *be;
 865         void                    *dummy;
 866 {
 867         if (be != 0) {
 868                 free(be);
 869         }
 870         return (NSS_SUCCESS);
 871 }
 872 
 873 static files_backend_op_t netgroup_ops[] = {
 874         netgr_destr,
 875         0,              /* No endent, because no setent/getent */
 876         0,              /* No setent;  setnetgrent() is really a getXbyY() */
 877         0,              /* No getent in the normal sense */
 878 
 879         netgr_in,       /* innetgr(), via NSS_DBOP_NETGROUP_IN */
 880         netgr_set,      /* setnetgrent(), via NSS_DBOP_NETGROUP_SET */
 881         getbyname,      /* For testing, via NSS_DBOP_NETGROUP_BYNAME */
 882 };
 883 
 884 /*
 885  * This is the one-and-only external entry point in this file.
 886  * It's called by the NSS framework when loading this backend.
 887  */
 888 /*ARGSUSED*/
 889 nss_backend_t *
 890 _nss_files_netgroup_constr(dummy1, dummy2, dummy3)
 891         const char      *dummy1, *dummy2, *dummy3;
 892 {
 893         nss_backend_t   *be;
 894 
 895         be = _nss_files_constr(netgroup_ops,
 896             sizeof (netgroup_ops) / sizeof (netgroup_ops[0]),
 897             "/etc/netgroup",
 898             NSS_LINELEN_NETGROUP,
 899             &hashinfo);
 900 
 901         return (be);
 902 }