diff options
Diffstat (limited to 'usr/src/lib/nsswitch')
| -rw-r--r-- | usr/src/lib/nsswitch/Makefile.com | 8 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/Makefile.targ | 7 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/compat/Makefile.com | 5 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/dns/Makefile.com | 4 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/files/Makefile.com | 4 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/ldap/Makefile | 4 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/ldap/Makefile.com | 8 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/ldap/Makefile.targ | 20 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/ldap/amd64/Makefile | 5 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/ldap/common/getexecattr.c | 5 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/ldap/common/getgrent.c | 6 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/ldap/common/getnetgrent.c | 2016 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/ldap/common/getspent.c | 7 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/ldap/common/ldap_common.h | 4 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/ldap/common/provider.d | 50 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/ldap/i386/Makefile | 6 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/ldap/sparc/Makefile | 6 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/ldap/sparcv9/Makefile | 6 | ||||
| -rw-r--r-- | usr/src/lib/nsswitch/nis/Makefile.com | 5 |
19 files changed, 1777 insertions, 399 deletions
diff --git a/usr/src/lib/nsswitch/Makefile.com b/usr/src/lib/nsswitch/Makefile.com index 570ae3a035..df29c223a5 100644 --- a/usr/src/lib/nsswitch/Makefile.com +++ b/usr/src/lib/nsswitch/Makefile.com @@ -22,7 +22,7 @@ # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# Copyright (c) 2018, Joyent, Inc. +# Copyright 2019 Joyent, Inc. include $(SRC)/lib/Makefile.lib @@ -36,12 +36,6 @@ LINTOUT = lint.out CPPFLAGS += -D_REENTRANT -CERRWARN += -_gcc=-Wno-switch -CERRWARN += -_gcc=-Wno-uninitialized -CERRWARN += -_gcc=-Wno-parentheses -CERRWARN += -_gcc=-Wno-unused-variable -CERRWARN += -_gcc=-Wno-address - # not linted SMATCH=off diff --git a/usr/src/lib/nsswitch/Makefile.targ b/usr/src/lib/nsswitch/Makefile.targ index 399d9be82c..d1fc6976c4 100644 --- a/usr/src/lib/nsswitch/Makefile.targ +++ b/usr/src/lib/nsswitch/Makefile.targ @@ -21,15 +21,14 @@ # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. -# -# ident "%Z%%M% %I% %E% SMI" +# Copyright 2019 Joyent, Inc. # all: $(LIBS) # include global library targets. include $(SRC)/lib/Makefile.targ - +include $(SRC)/lib/Makefile.usdt $(ROOT32DYNLIB) := FILEMODE= 755 $(ROOT64DYNLIB) := FILEMODE= 755 @@ -39,7 +38,7 @@ $(DYNLIB1) := CTFMERGE_POST = $(CTFMERGE_LIB) # DYNLIB1 is used rather than DYNLIB to prevent the automagic expansion and # creation of a libXXXX.so$(VERS) target. -$(DYNLIB1): pics .WAIT $$(PICS) +$(DYNLIB1): pics .WAIT $$(PICS) .WAIT $(USDT_PICS) $(BUILD.SO) $(POST_PROCESS_SO) diff --git a/usr/src/lib/nsswitch/compat/Makefile.com b/usr/src/lib/nsswitch/compat/Makefile.com index 5b600c5588..c78d532734 100644 --- a/usr/src/lib/nsswitch/compat/Makefile.com +++ b/usr/src/lib/nsswitch/compat/Makefile.com @@ -22,8 +22,7 @@ # # Copyright 1993,2001-2003 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. -# -# ident "%Z%%M% %I% %E% SMI" +# Copyright 2019 Joyent, Inc. # # lib/nsswitch/compat/Makefile.com @@ -37,6 +36,8 @@ OBJECTS = getpwent.o \ getuserattr.o \ getauuser.o +pics/compat_common.o := CERRWARN += -_gcc=-Wno-uninitialized + # include common nsswitch library definitions. include ../../Makefile.com diff --git a/usr/src/lib/nsswitch/dns/Makefile.com b/usr/src/lib/nsswitch/dns/Makefile.com index 0366633c0c..ceff67bced 100644 --- a/usr/src/lib/nsswitch/dns/Makefile.com +++ b/usr/src/lib/nsswitch/dns/Makefile.com @@ -21,6 +21,7 @@ # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2019 Joyent, Inc. # # lib/nsswitch/dns/Makefile.com @@ -44,5 +45,8 @@ CPPFLAGS += -DNSS_DNS_LIBRESOLV=\"libresolv.so.2\" LINTFLAGS += -erroff=E_GLOBAL_COULD_BE_STATIC2 +pics/dns_common.o := CERRWARN += -_gcc=-Wno-uninitialized +pics/gethostent6.o := CERRWARN += -_gcc=-Wno-uninitialized + LDLIBS += -lnsl -lresolv_joy -lsocket DYNLIB1 = nss_dns.so$(VERS) diff --git a/usr/src/lib/nsswitch/files/Makefile.com b/usr/src/lib/nsswitch/files/Makefile.com index 1489badee6..86cd43b071 100644 --- a/usr/src/lib/nsswitch/files/Makefile.com +++ b/usr/src/lib/nsswitch/files/Makefile.com @@ -21,6 +21,7 @@ # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2019 Joyent, Inc. # LIBRARY = libnss_files.a @@ -59,6 +60,9 @@ CPPFLAGS += -I../../../common/inc LINTFLAGS += -erroff=E_GLOBAL_COULD_BE_STATIC2 LINTFLAGS64 += -erroff=E_GLOBAL_COULD_BE_STATIC2 +pics/gethostent.o := CERRWARN += -_gcc=-Wno-switch +pics/gethostent.o := CERRWARN += -_gcc=-Wno-uninitialized + LDLIBS += -lnsl DYNLIB1 = nss_files.so$(VERS) diff --git a/usr/src/lib/nsswitch/ldap/Makefile b/usr/src/lib/nsswitch/ldap/Makefile index eabc270b02..835e2945a3 100644 --- a/usr/src/lib/nsswitch/ldap/Makefile +++ b/usr/src/lib/nsswitch/ldap/Makefile @@ -19,11 +19,9 @@ # # CDDL HEADER END # -# -#ident "%Z%%M% %I% %E% SMI" -# # Copyright (c) 1999 by Sun Microsystems, Inc. # All rights reserved. +# Copyright 2019 Joyent, Inc. # # lib/nsswitch/ldap/Makefile # diff --git a/usr/src/lib/nsswitch/ldap/Makefile.com b/usr/src/lib/nsswitch/ldap/Makefile.com index 694bd5f023..0abf9bd4c9 100644 --- a/usr/src/lib/nsswitch/ldap/Makefile.com +++ b/usr/src/lib/nsswitch/ldap/Makefile.com @@ -21,6 +21,7 @@ # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2019 Joyent, Inc. # LIBRARY = libnss_ldap.a @@ -50,13 +51,16 @@ OBJECTS = getauthattr.o \ tsol_getrhent.o \ tsol_gettpent.o \ ldap_common.o \ - ldap_utils.o + ldap_utils.o \ + list.o + +USDT_PROVIDERS = provider.d # include common nsswitch library definitions. include ../../Makefile.com CPPFLAGS += -I../../../libsldap/common -LDLIBS += -lsldap -lnsl -lldap +LDLIBS += -lsldap -lnsl -lldap -lavl -lscf LINTFLAGS += -erroff=E_GLOBAL_COULD_BE_STATIC2 LINTFLAGS64 += -erroff=E_GLOBAL_COULD_BE_STATIC2 DYNLIB1 = nss_ldap.so$(VERS) diff --git a/usr/src/lib/nsswitch/ldap/Makefile.targ b/usr/src/lib/nsswitch/ldap/Makefile.targ new file mode 100644 index 0000000000..2352c0892e --- /dev/null +++ b/usr/src/lib/nsswitch/ldap/Makefile.targ @@ -0,0 +1,20 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2019 Joyent, Inc. +# + +include $(SRC)/lib/nsswitch/Makefile.targ + +pics/%.o: $(SRC)/common/list/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) diff --git a/usr/src/lib/nsswitch/ldap/amd64/Makefile b/usr/src/lib/nsswitch/ldap/amd64/Makefile index 1334471b25..ef375387db 100644 --- a/usr/src/lib/nsswitch/ldap/amd64/Makefile +++ b/usr/src/lib/nsswitch/ldap/amd64/Makefile @@ -22,8 +22,7 @@ # # Copyright 2004 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. -# -# ident "%Z%%M% %I% %E% SMI" +# Copyright 2019 Joyent, Inc. # include ../Makefile.com @@ -31,6 +30,6 @@ include $(SRC)/lib/Makefile.lib.64 LIBS = $(DYNLIB1) -include ../../Makefile.targ +include ../Makefile.targ install: all $(ROOT64DYNLIB) diff --git a/usr/src/lib/nsswitch/ldap/common/getexecattr.c b/usr/src/lib/nsswitch/ldap/common/getexecattr.c index abd22908e0..8ec4197428 100644 --- a/usr/src/lib/nsswitch/ldap/common/getexecattr.c +++ b/usr/src/lib/nsswitch/ldap/common/getexecattr.c @@ -21,6 +21,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2019 Joyent, Inc. */ #include <secdb.h> @@ -421,7 +422,7 @@ get_wild(ldap_backend_ptr be, nss_XbyY_args_t *argp, int getby_flag) const char *type = _priv_exec->type; if (strpbrk(policy, "*()\\") != NULL || - type != NULL && strpbrk(type, "*()\\") != NULL) + (type != NULL && strpbrk(type, "*()\\") != NULL)) return ((nss_status_t)NSS_NOTFOUND); if (_priv_exec->id != NULL) @@ -545,7 +546,7 @@ getbynam(ldap_backend_ptr be, void *a) const char *type = _priv_exec->type; if (strpbrk(policy, "*()\\") != NULL || - type != NULL && strpbrk(type, "*()\\") != NULL || + (type != NULL && strpbrk(type, "*()\\") != NULL) || _ldap_filter_name(name, _priv_exec->name, sizeof (name)) != 0) return ((nss_status_t)NSS_NOTFOUND); ret = snprintf(searchfilter, sizeof (searchfilter), diff --git a/usr/src/lib/nsswitch/ldap/common/getgrent.c b/usr/src/lib/nsswitch/ldap/common/getgrent.c index 291d16dbc6..bbfa049bcd 100644 --- a/usr/src/lib/nsswitch/ldap/common/getgrent.c +++ b/usr/src/lib/nsswitch/ldap/common/getgrent.c @@ -23,6 +23,7 @@ * Use is subject to license terms. * * Copyright 2017 Nexenta Systems, Inc. All rights reserved. + * Copyright 2019 Joyent, Inc. */ #include <grp.h> @@ -161,7 +162,7 @@ _nss_ldap_group2str(ldap_backend_ptr be, nss_XbyY_args_t *argp) * If we find an '=' in the member attribute value, treat it as * a DN, otherwise as a username. */ - if (member_str = strchr(members->attrvalue[i], '=')) { + if ((member_str = strchr(members->attrvalue[i], '=')) != NULL) { member_str++; /* skip over the '=' */ /* Fail if we can't pull a username out of the RDN */ if (! (member_str = strtok_r(member_str, @@ -367,7 +368,8 @@ getbymember(ldap_backend_ptr be, void *a) * value, treat it as a DN, otherwise as a * username. */ - if (member_str = strchr(membervalue[j], '=')) { + if ((member_str = strchr(membervalue[j], '=')) != + NULL) { member_str++; /* skip over the '=' */ member_str = strtok_r(member_str, ",", &strtok_state); diff --git a/usr/src/lib/nsswitch/ldap/common/getnetgrent.c b/usr/src/lib/nsswitch/ldap/common/getnetgrent.c index c2ff7466ec..e832574ac2 100644 --- a/usr/src/lib/nsswitch/ldap/common/getnetgrent.c +++ b/usr/src/lib/nsswitch/ldap/common/getnetgrent.c @@ -21,18 +21,182 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2019 Joyent, Inc. */ +/* + * nss_ldap netgroup support + * + * Like other name service switch modules, this code may run in nscd or + * arbitrary processes that call getnetgrent(3C), getnetgrent_r(3C), + * setnetgrent(3C), endnetgrent(3C), and innetgr(3C). For a reason that is not + * entirely clear ("due to backend knowledge"), libc's nss_pack() instructs + * callers of all netgroup functions other than innetgr(3C) to NSS_TRYLOCAL + * rather than allowing the query to be passed to nscd. This is probably of + * negligible impact, as modern use of netgroup lookups is almost exclusively + * through innetgr(3C). + * + * Whether being called from libc or nscd, initialization happens via a call to + * _nss_ldap_netgroup_constr(). There is no destructor - rather the library is + * closed with dlclose(), which will trigger ngc_fini(). The most likely + * consumer, nscd, restarts itself when it sees a configuration change. + * + * + * Netgroup Caching + * + * Caching of netgroups is done within this module because the type of caching + * that is most useful for netgroups is a poor fit for nscd. nscd is focused on + * caching individual results, with the assumption that one request doesn't + * perform a high cost operation from which a subsequent operation may benefit. + * The poster child for this problem is the most common case: innetgr(). The + * LDAP NIS schema is poorly designed for innetgr because it forces a full + * object transfer for every innetgr call. Native NIS avoids this with the + * revnetgroup map. + * + * Netgroups are cached only when running as part of the + * svc:/system/name-service-cache:default service. nscd.conf(4) can be used to + * override defaults with enable-cache, positive-time-to-live, and + * negative-time-to-live. While enable-cache defaults to "yes", lookups that + * bypass nscd still are not cached. + * + * The cache is implemented as an AVL tree (ngc_cache), with each node of the + * tree representing one netgroup. Nested netgroups are not flattened, implying + * that if there are two netgroups that each include a common third netgroup, + * there will be three tree nodes. Expired netgroups that still have references + * are migrated from ngc_cache (AVL tree) to ngc_graveyard (list). + * + * The following functions are intended to be called by those functions that + * implement setnetgrent(), endnetgrent(), and innetgr(). + * + * netgroup_get() On success, a reference counted pointer to a cached + * netgroup is returned. A call to this function may + * trigger the netgroup to be loaded from LDAP. + * The context that is passed is used to ensure that + * use of memberNisNetgroup does not lead to infinite + * loops. + * netgroup_rele() Releases the reference returned by netgroup_get. + * + * + * Locking + * + * There is one big lock, ngc_lock. It must be held while accessing ngc_cache, + * or any of the lists that may reference netgroup_t objects. It must also be + * held while accessing the ng_refcnt field of any netgroup_t that is in any of + * the ngc_* lists or tree. + * + * ngc_*() functions operate on netgroup_t objects. ngc_*_locked() functions + * must be called with ngc_lock held and the others expect ngc_lock not to be + * held. + * + * + * Expiry + * + * Clearly, it is not OK to cache data forever without checking to ensure that + * it is current. When a netgroup is loaded from LDAP, its expiration time is + * set to ngc_pos_ttl (default NGC_POS_TTL) seconds from the time that it is + * loaded. Any queries that start after the expiration time will trigger the + * netgroup to be loaded from LDAP again. Negative (NSS_NOTFOUND) results are + * also cached for ngc_neg_ttl seconds. As mentioned above, the defaults can be + * overridden by positive-time-to-live and negative-time-to-live in nscd.conf. + * + * To avoid full reloads of netgroups on a regular basis, a netgroup_get() that + * occurs in the final 25% of a netgroup's expiry period will trigger a worker + * thread to make an LDAP query to check the modifyTimestamp of the relevant + * netgroup object. If the modifyTimestamp has not changed from its value + * stored in the cache, the netgroup's timeout is reset to ngc_pos_ttl seconds + * in the future. If the modifyTimestamp has changed to a later time, the + * netgroup will be reloaded and replaced in or removed from the cache. In + * addition to reducing the load on the LDAP server(s), this approach leads to + * lower latency for frequent lookups. + * + * Each netgroup in the cache is also in the ngc_pos_expire_queue or + * ngc_neg_expire_queue list_t. These lists are ordered by expiry time, with + * each new or renewed netgroup being placed at the end of the appropriate list. + * Whether triggered by a lookup or a periodic reap (see ngc_reap_locked()), + * when an expired netgroup is removed from ngc_cache, it is also removed from + * the expire queue. See ngc_dispose_locked(). + * + * When setnetgrent() is called, it calls netgroup_get() and holds the reference + * in the context that lives until endnetgrent() is called or the netgroup is + * fully consumed. To avoid problems with a netgroup reference that is returned + * in the instant before the netgroup expires, the reference is valid for at + * least EXPIRE_SECONDS seconds. + * + * When nscd sees a configuration change, it restarts itself. Thus, there is no + * need for any finalization code to free state. Applications that call + * setnetgrent() should call endnetgrent() to free resources held by this module + * and libslap. + * + * + * Example + * + * In this example, the cache has 6 netgroups: admins, bastion, blah, devs, ops, + * and qa. In ngc_cache, the solid lines represent the tree structure, the + * dotted lines represent expiration queues (lists), and the hash lines + * represent the warm queue. + * + * - Five of them are not expired: admins, bastion, blah, devs, qa + * - Three of the non-expired netgroups exist: admins, bastion, devs + * - Two of these are active and near their expiration time so a refresh + * has been queued: (admins, bastion) + * - Two of the recently queried netgroups do not exist: blah, qa + * - One netgroup is expired but is still referenced: ops + * + * ngc_cache ngc_graveyard + * | | + * V V + * +----------+ +---------+ + * | @blah | | @ops | + * | negative |<.............. | expired | + * +----------+ : +---------+ + * / \ : + * +----------+ +----------+ : + * ....>| @bastion |....>| @devs | : + * : | positive | | positive | : + * : +----------+ +----------+ : + * : / ^ \ : + * +----------+ # +----------+ + * | @admins |### | @qa | + * | positive |<########## | negative | + * +----------+ # +----------+ + * ^ # ^ + * : # : + * ngc_pos_expire_queue ngc_warm_queue ngc_neg_expire_queue + * + * When the thread that has a hold on ops releases it, ops will be removed from + * ngc_graveyard and will be freed. + * + * When ngc_warmer wakes up, it will walk far enough into each of + * ngc_neg_expire_queue and ngc_pos_expire_queue to see the first non-expired + * netgroup on each list or the end of the list. Those netgroups that it + * encounters that are expired will be moved to ngc_graveyard or freed, + * depending on whether they have references. Both admins and bastion will have + * their modifyTimestamp attribute queried, causing each of those netgroups to + * be renewed or expired and replaced. + */ + +#include <assert.h> +#include <errno.h> +#include <libscf.h> +#include <locale.h> +#include <stddef.h> #include <syslog.h> +#include <sys/avl.h> +#include <sys/debug.h> +#include <sys/list.h> +#include <sys/sdt.h> +#include <sys/sysmacros.h> +#include <thread.h> #include "ldap_common.h" /* netgroup attributes filters */ #define _N_TRIPLE "nisnetgrouptriple" #define _N_MEMBER "membernisnetgroup" +#define _N_MODIFYSTAMP "modifyTimestamp" -#define PRINT_VAL(a) (((a).argc == 0) || ((a).argv == NULL) || \ - ((a).argv[0] == NULL)) ? "*" : (a).argv[0] +#define PRINT_VAL(a) ((((a).argc == 0) || ((a).argv == NULL) || \ + ((a).argv[0] == NULL)) ? "*" : (a).argv[0]) #define ISNULL(a) (a == NULL ? "<NULL>" : a) #define MAX_DOMAIN_LEN 1024 #define MAX_TRIPLE_LEN (MAXHOSTNAMELEN + LOGNAME_MAX + \ @@ -44,41 +208,1001 @@ #define N_HASH 257 #define COMMA ',' +/* These are in seconds. Be careful: ngc_expire and ng_refresh are in nsec. */ +#define NGC_POS_TTL 3600 /* or nscd.conf positive-time-to-live */ +#define NGC_NEG_TTL 5 /* or nscd.conf negative-time-to-live */ + +#define NGC_DATESTR_LEN 24 + +#define NSCD_FMRI "svc:/system/name-service-cache:default" +#define NSCD_CONF "/etc/nscd.conf" + static const char *netgrent_attrs[] = { _N_TRIPLE, _N_MEMBER, + _N_MODIFYSTAMP, (char *)NULL }; +static const char *netgrent_stamp[] = { + _N_MODIFYSTAMP, + (char *)NULL +}; + +/* + * Each of these will reference strings in ng_result. Per split_triple(), NULL + * is treated as a wild card. + */ +typedef struct { + const char *ngt_host; + const char *ngt_user; + const char *ngt_domain; +} ngc_triple_t; + +typedef enum { + NGC_FLAG_NEGATIVE = 0x01, + NGC_FLAG_INCACHE = 0x02, + NGC_FLAG_INWARMER = 0x04, + NGC_FLAG_INEXPQUEUE = 0x08, +} ngc_flags_t; + +#define NGC_NEGATIVE(ng) (!!((ng)->ng_flags & NGC_FLAG_NEGATIVE)) +#define NGC_INCACHE(ng) (!!((ng)->ng_flags & NGC_FLAG_INCACHE)) +#define NGC_INWARMER(ng) (!!((ng)->ng_flags & NGC_FLAG_INWARMER)) +#define NGC_INEXPQUEUE(ng) (!!((ng)->ng_flags & NGC_FLAG_INEXPQUEUE)) + +#define NGC_CLEAR(ng, flags) ((ng)->ng_flags &= ~(flags)) +#define NGC_SET(ng, flags) ((ng)->ng_flags |= (flags)) + +/* + * A cached netgroup. + * + * Each netgroup_t is in ng_cache (with NGC_FLAG_INCACHE) or ng_graveyard + * (without NGC_FLAG_INCACHE). Those that are in ng_cache are also in + * ngc_pos_expire_queue or ngc_neg_expire_queue, roughly sorted by ng_expire + * (soonest at head). References in ng_cache, ng_graveyard, + * ng_pos_expire_queue, and ng_neg_expire_queue do not cause ng_refcnt to + * increase. Any other reference while not also holding ng_lock (including + * those for ng_warm_queue) do increase ng_refcnt. + * + * This structure is arranged such that it is as friendly as possible to dtrace + * scripts that need to work across 32-bit and 64-bit executables. In + * particular, pointers come late in the structure and any 64-bit fields are + * 64-bit aligned to avoid strange offsets. + */ +typedef struct { + /* Monotonic seconds */ + uint32_t ng_birth; /* For cache debugging */ + uint32_t ng_expire; /* Do not use after */ + uint32_t ng_refresh; /* Time to update ahead of expiry */ + + uint32_t ng_refcnt; + uint64_t ng_lastchange; /* Tenths of second since epoch */ + ngc_flags_t ng_flags; + ns_ldap_result_t *ng_result; + ngc_triple_t *ng_triples; + uint32_t ng_triplecnt; + union { + avl_node_t ng_cache; /* if NGC_INCACHE(), in ngc_cache */ + list_node_t ng_tombstone; /* !NGC_INCACHE(), in ngc_graveyard */ + } ng_linkage; + list_node_t ng_warm_linkage; /* ngc_warm_queue */ + list_node_t ng_expire_linkage; /* ngc_{pos,neg}_expire_queue */ + const char *ng_name; /* Will reference space after struct */ +} netgroup_t; + +/* + * While iterating a netgroup we must keep track of which memberNisNetgroups + * have been seen and avoid visiting the same nested netgroup multiple times. + * This is particularly important for netgroups that form circular references. + * + * This is handled with a netgroup_table_t. Each iterator establishes a + * netgroup_table_t containing a hash table of netgroup_name_t elements. + */ typedef struct netgroup_name { - char *name; - struct netgroup_name *next; - struct netgroup_name *next_hash; + char *ngn_name; + struct netgroup_name *ngn_next; + struct netgroup_name *ngn_next_hash; } netgroup_name_t; typedef struct { - netgroup_name_t *hash_list[N_HASH]; - netgroup_name_t *to_do; - netgroup_name_t *done; + netgroup_name_t *ngt_hash_list[N_HASH]; + netgroup_name_t *ngt_to_do; + netgroup_name_t *ngt_done; } netgroup_table_t; +typedef unsigned int hash_t; + +/* + * This cookie is used across setnetgrent()/getnetgrent()/endnetgrent(). + */ typedef struct { - ns_ldap_result_t *results; - ns_ldap_entry_t *entry; - char **attrs; - char *netgroup; - netgroup_table_t tab; + netgroup_t *gnc_netgroup; /* cached netgroup */ + ns_ldap_entry_t *gnc_entry; /* entry in netgroup->ng_result */ + char *gnc_name; /* netgroup name */ + uint32_t gnc_curtriple; /* index in netgroup->ng_triples */ + netgroup_table_t gnc_tab; } getnetgrent_cookie_t; -typedef struct { - struct nss_innetgr_args *ia; - const char *ssd_filter; - const char *netgrname; - const char *membername; - netgroup_table_t tab; -} innetgr_cookie_t; +/* These hold netgroup_t nodes in the cache. */ +static avl_tree_t ngc_cache; +static list_t ngc_graveyard; +static list_t ngc_warm_queue; -typedef unsigned int hash_t; +/* + * ngc_lock must be held while modifying ngc_cache, ngc_graveyard, or adjusting + * ng_refcnt in any netgroup in ngc_cache or ngc_graveyard. + */ +static mutex_t ngc_lock = ERRORCHECKMUTEX; +static boolean_t ngc_initialized = B_FALSE; + +/* + * The warmer thread performs asynchronous refreshes of active netgroups that + * are approaching their expiration. Activity is defined as having at least one + * use in the final 25% of ngc_pos_ttl. + * + * Don't think the warmer thread is all rainbows and unicorns: it is also the + * grim reaper for expired netgroups. It wakes up every ngc_reap_interval + * seconds and clears the cache of expired netgroups. + */ +static cond_t ngc_warm_cv = DEFAULTCV; +static thread_t ngc_warmer_tid; +static boolean_t ngc_warmer_die = B_FALSE; +static list_t ngc_pos_expire_queue; +static list_t ngc_neg_expire_queue; +static uint32_t ngc_reap_interval = 313; /* Arbitrary, but not N * 60 */ + +/* + * The positive cache size is naturally limited by the size of all the netgroups + * in the LDAP server. The negative cache is limited only by imagination, + * unless we have an explicit limit on the size. + */ +static uint32_t ngc_neg_max = 200; /* Arbitrary */ +static volatile uint32_t ngc_neg_count = 0; + +/* Initialized in read_nscd_conf() */ +static boolean_t ngc_enable; +static int ngc_pos_ttl; +static int ngc_neg_ttl; + +static int split_triple(char *, const char **, const char **, const char **); +static void ngc_dispose_locked(netgroup_t *); + +/* + * We don't really care about high-precision timers for cache expiration, but we + * do need a monotonic clock. ngc_first_tick is initialized by ngc_init() to + * the number of seconds returned by gethrtime(). ngc_last_tick is the number + * of seconds since ngc_first_tick. + * + * ngc_first_tick is a global to allow ngc_init() and ngc_time() to cooperate. + * ngc_last_tick is a global so it is available during post-mortem analysis. + */ +static uint32_t ngc_first_tick = 0; +static uint32_t ngc_last_tick = 0; + +static uint32_t +ngc_time(void) +{ + uint32_t tick; + + tick = NSEC2SEC(gethrtime()) - ngc_first_tick; + DTRACE_PROBE1(nss_ldap, ngc__tick, tick); + + /* + * For post-mortem analysis only; the winner of a race will update all + * 32-bits at once. + */ + ngc_last_tick = tick; + + return (tick); +} + +static netgroup_t * +ngc_alloc(const char *name) +{ + netgroup_t *ng; + size_t len = strlen(name) + 1; + + /* + * Use one allocation for the structure and the variable length name. + * Not using flexible array member because we need to be able to assign + * a value to ng_name before avl_find() without doing a heap allocation. + */ + if ((ng = calloc(1, sizeof (*ng) + len)) == NULL) { + return (NULL); + } + + ng->ng_birth = ngc_time(); + ng->ng_name = (const char *)ng + sizeof (*ng); + (void) strlcpy((char *)ng->ng_name, name, len); + + return (ng); +} + +static void +ngc_free(netgroup_t *ng) +{ + DTRACE_PROBE2(nss_ldap, netgroup__cache__free, ng->ng_name, ng); + + VERIFY0(ng->ng_refcnt); + free(ng->ng_triples); + (void) __ns_ldap_freeResult(&ng->ng_result); + free(ng); +} + +static void +ngc_hold_locked(netgroup_t *ng) +{ + VERIFY(MUTEX_HELD(&ngc_lock)); + DTRACE_PROBE3(nss_ldap, netgroup__cache__hold, ng->ng_name, ng, + ng->ng_refcnt); + ng->ng_refcnt++; +} + +static void +ngc_rele_locked(netgroup_t *ng) +{ + uint32_t now = ngc_time(); + + VERIFY(MUTEX_HELD(&ngc_lock)); + VERIFY3S(ng->ng_refcnt, >, 0); + + DTRACE_PROBE3(nss_ldap, netgroup__cache__rele, ng->ng_name, ng, + ng->ng_refcnt); + + ng->ng_refcnt--; + if (ng->ng_expire <= now || !NGC_INCACHE(ng)) { + ngc_dispose_locked(ng); + } +} + +static void +netgroup_rele(netgroup_t *ng) +{ + mutex_enter(&ngc_lock); + + ngc_rele_locked(ng); + + mutex_exit(&ngc_lock); +} + +/* + * Free or otherwise properly dispose of a netgroup that is evicted from the + * cache or may be ready to be evicted from the graveyard. Does not alter the + * reference count. + */ +static void +ngc_dispose_locked(netgroup_t *ng) +{ + VERIFY(MUTEX_HELD(&ngc_lock)); + + DTRACE_PROBE4(nss_ldap, netgroup__cache__dispose, ng->ng_name, ng, + ng->ng_refcnt, ng->ng_flags); + + if (NGC_INCACHE(ng)) { + avl_remove(&ngc_cache, ng); + + /* + * If it is in the cache, it is also in an expire queue. + */ + if (NGC_NEGATIVE(ng)) { + list_remove(&ngc_neg_expire_queue, ng); + ngc_neg_count--; + } else { + list_remove(&ngc_pos_expire_queue, ng); + } + + if (ng->ng_refcnt == 0) { + ngc_free(ng); + } else { + NGC_CLEAR(ng, NGC_FLAG_INEXPQUEUE); + NGC_CLEAR(ng, NGC_FLAG_INCACHE); + DTRACE_PROBE2(nss_ldap, netgroup__cache__to__graveyard, + ng->ng_name, ng); + list_insert_tail(&ngc_graveyard, ng); + } + return; + } + + /* + * If it's not in the cache, it's in the graveyard but may still have + * references. + */ + if (ng->ng_refcnt == 0) { + list_remove(&ngc_graveyard, ng); + ngc_free(ng); + return; + } +} + +static void +ngc_set_expire_locked(netgroup_t *ng) +{ + list_t *expire_queue; + int expire_ttl; + int refresh_ttl; + + VERIFY(MUTEX_HELD(&ngc_lock)); + + if (NGC_NEGATIVE(ng)) { + expire_queue = &ngc_neg_expire_queue; + ng->ng_expire = ngc_enable ? (ngc_time() + ngc_neg_ttl) : 0; + ng->ng_refresh = 0; + } else { + expire_queue = &ngc_pos_expire_queue; + ng->ng_expire = ngc_enable ? + (ngc_time() + ngc_pos_ttl) : 0; + /* Refresh when 1/4 or less of ngc_pos_ttl remains */ + ng->ng_refresh = ngc_enable ? + (ng->ng_expire - (ngc_pos_ttl / 4)) : 0; + } + + if (NGC_INEXPQUEUE(ng)) { + if (list_tail(expire_queue) != ng) { + list_remove(expire_queue, ng); + list_insert_tail(expire_queue, ng); + } + } else { + NGC_SET(ng, NGC_FLAG_INEXPQUEUE); + list_insert_tail(expire_queue, ng); + } +} + +/* + * This parses the netgroup triples in ng->ng_result, storing them in + * ng->ng_triples. The process of parsing them overwrites at least some of the + * white space, commas, and parentheses in ng_result so they are not usable + * after this. ng->ng_result must live as long as ng->ng_triples. + */ +static int +ngc_parse_triples(netgroup_t *ng) +{ + ns_ldap_entry_t *entry; + char **attr; + uint32_t i = 0; + uint32_t entries = 0; + + VERIFY0(ng->ng_triplecnt); + + /* First, we need a count of nisNetgroupTriple attributes */ + for (entry = ng->ng_result->entry; entry != NULL; entry = entry->next) { + entries++; + for (attr = __ns_ldap_getAttr(entry, _N_TRIPLE); + attr != NULL && *attr != NULL; attr++) { + i++; + } + } +#ifdef DEBUG + syslog(LOG_DEBUG, "ngc_parse_triples parsing %u triples from " + "%u entries", i, entries); +#endif + if (i == 0) { + return (0); + } + + /* Allocate the triples */ + ng->ng_triples = calloc(i, sizeof (*ng->ng_triples)); + if (ng->ng_triples == NULL) { + ng->ng_triplecnt = 0; + return (-1); + } + ng->ng_triplecnt = i; + i = 0; + + /* + * Parse the triples. Parse errors lead to not all of the allocated + * slots being used. + */ + for (entry = ng->ng_result->entry; entry != NULL; entry = entry->next) { + for (attr = __ns_ldap_getAttr(entry, _N_TRIPLE); + *attr != NULL; attr++) { + ngc_triple_t *ngt = &ng->ng_triples[i]; + + if (split_triple(*attr, &ngt->ngt_host, + &ngt->ngt_user, &ngt->ngt_domain) == 0) { + i++; + DTRACE_PROBE5(nss_ldap, netgroup__cache__triple, + ng->ng_name, ng, ngt->ngt_host, + ngt->ngt_user, ngt->ngt_domain); + } + } + } + ng->ng_triplecnt = i; + + return (0); +} + +/* + * Get a base10 number that is exactly `digits` long from the string at `*bufp`. + * Verify it is between `min` and `max`, inclusive. + * + * On success `*valp` is updated with the value and `*bufp` is advanced by + * `digits` characters. + */ +static int +parse_num(const char **bufp, int *valp, uint32_t digits, int min, int max) +{ + uint32_t i; + int val = 0; + const char *buf = *bufp; + + VERIFY3U(digits, >, 0); + for (i = 0; i < digits; i++, buf++) { + int newval; + + /* + * We avoid isdigit() and isdigit_l() because we are constrained + * by RFC 4517 to the ASCII digits, we don't have control over + * which locale is currently being used, and newlocale() could + * fail. + */ + if (*buf < '0' || *buf > '9') { + return (-1); + } + newval = val * 10 + *buf - '0'; + if (newval < val) { + return (-1); + } + val = newval; + } + if (val < min || val > max) { + return (-1); + } + *valp = val; + *bufp = buf; + return (0); +} + +#ifdef DEBUG +#define FAILOFF ((uintptr_t)next - (uintptr_t)gtime + 1) +#define DBG_PARSE_GENTIME_FAIL() \ + (void) fprintf(stderr, "%s:%d: parse failed at character %lu\n", \ + __func__, __LINE__, FAILOFF); \ + (void) fprintf(stderr, " %s\n", gtime); \ + (void) fprintf(stderr, " %*s\n", FAILOFF, "^"); +#else +#define DBG_PARSE_GENTIME_FAIL() +#endif + +/* + * See RFC 4517 Section 3.3.13 + * + * Tries to find an RFC 4517 compliant Generalized Time in gtime, returning via + * *whenp the tenths of seconds since the epoch UTC. + * + * Times before the epoch are not supported. + * + * At first blush, it would seem that strptime(3C) should be able to handle this + * task. Sadly, that is not the case. In particular, it does not handle + * fractional seconds, does not document the support it has for offsets (%z), + * and does not support two digit offsets. The one-digit fractional part + * specified by RFC 4517 is unlikely to be useful outside of this use case, + * so fixing strptime() to be useful here is not reasonable. Even absent the + * fractional units issue, the optional components would force many trips + * through strptime() trying to guess which format may be the right one. + */ +static int +parse_generalized_time(const char *gtime, uint64_t *whenp) +{ + const char *next; + struct tm tm = { 0 }; + uint32_t frac_tenths = 0; + int frac = 0; + time_t secs; + uint64_t tsecs; /* tenths of a second */ + + for (next = gtime; *next != '\0'; next++) { + if (!isascii(*next)) { + DBG_PARSE_GENTIME_FAIL(); + return (-1); + } + } + next = gtime; + + /* Year, month, day, hour are required */ + if (parse_num(&next, &tm.tm_year, 4, 0, 9999) != 0 || + parse_num(&next, &tm.tm_mon, 2, 1, 12) != 0 || + parse_num(&next, &tm.tm_mday, 2, 1, 31) != 0 || + parse_num(&next, &tm.tm_hour, 2, 0, 23) != 0) { + DBG_PARSE_GENTIME_FAIL(); + return (-1); + } + + /* + * Minutes and seconds are optional. The meaning of the fractional part + * varies with its position: it may be a fraction of an hour, a minute, + * or a second. + */ + frac_tenths = 60 * 60; + if (parse_num(&next, &tm.tm_min, 2, 0, 59) == 0) { + frac_tenths = 60; + if (parse_num(&next, &tm.tm_sec, 2, 0, 60) == 0) { + frac_tenths = 1; + } + } + + /* + * (time_t) -1 is an error per timegm(3C); other negative times are + * unsupported because we don't realistically expect this to be used + * before 1970. If time_t is signed 32-bits, it shouldn't pretend to + * parse time properly when the offset from the epoch no longer fits in + * 31 bits. + */ + tm.tm_year -= 1900; + tm.tm_mon--; + if ((secs = timegm(&tm)) < 0) { +#ifdef DEBUG + (void) fprintf(stderr, "%s:%d: timegm failed for <%s>\n", + __func__, __LINE__, gtime); +#endif + return (-1); + } + tsecs = 10 * (uint64_t)secs; + + if (*next == '.' || *next == ',') { + next++; + if (parse_num(&next, &frac, 1, 0, 9) != 0) { + DBG_PARSE_GENTIME_FAIL(); + return (-1); + } + tsecs += frac_tenths * frac; + } + + if (*next == '+' || *next == '-') { + int hrs, mins = 0; + int sign = *next == '+' ? -1 : 1; + + next++; + /* Get offset hours */ + if (parse_num(&next, &hrs, 2, 0, 23) != 0) { + DBG_PARSE_GENTIME_FAIL(); + return (-1); + } + /* + * Get offset minutes, which are optional in GeneralizedTime. + * + * There are three scenarios that may happen now: + * + * - parse_num() may find two digits that form a number in + * [0, 59]. In that case, it will update mins, return + * success, and advance the next pointer. + * + * - parse_num() may find that the next character is a nul + * character. This error is rightly ignored because minutes + * are optional. The next pointer still references a nul + * character. + * + * - parse_num() may find an invalid value in the next two + * characters. In this case, it returns an error without + * advancing the next pointer. The nul character check that + * follows will detect this situation, causing this function + * to return an error. + */ + (void) parse_num(&next, &mins, 2, 0, 59); + if (*next != '\0') { + DBG_PARSE_GENTIME_FAIL(); + return (-1); + } + tsecs += 10 * sign * ((hrs * 60) + mins) * 60; + } else if (*next != '\0' && strcmp(next, "Z") != 0) { + DBG_PARSE_GENTIME_FAIL(); + return (-1); + } + + *whenp = tsecs; + + return (0); +} + +/* + * Get the value of modifyTimestamp and return any valid value in the supplied + * buffer. + * + * RFC 4512 section 3.4.4 defines modifyTimestamp as a GeneralizedTime. RFC + * 4517 section 3.3.13 specifies GeneralizedTime as an ISO 8601 time that + * may or may not have a fractional component and may or may not have timezone + * information. The fractional component can be fractions of seconds, minutes, + * or hours, depending on context. + */ +static uint64_t +get_modify_timestamp(ns_ldap_result_t *result) +{ + ns_ldap_entry_t *entry; + char **attr; + uint64_t when; + + for (entry = result->entry; entry != NULL; entry = entry->next) { + attr = __ns_ldap_getAttr(entry, _N_MODIFYSTAMP); + if (*attr == NULL) { + continue; + } + if (parse_generalized_time(*attr, &when) == 0) { + return (when); + } + } + return (0); +} + +static nss_status_t +ngc_ldap_search(const char *ngname, const char **attrs, + ns_ldap_result_t **result) +{ + char filter[SEARCHFILTERLEN]; + char name[SEARCHFILTERLEN]; + char userdata[SEARCHFILTERLEN]; + ns_ldap_error_t *error = NULL; + int rc; + + /* Escape special characters */ + if (_ldap_filter_name(name, ngname, sizeof (name)) != 0) + return (NSS_NOTFOUND); + /* Form "(&(objectClass=nisNetGroup)(cn=<name>))" */ + rc = snprintf(filter, sizeof (filter), _F_SETMEMBER, name); + if (rc >= sizeof (filter) || rc < 0) + return (NSS_NOTFOUND); + + /* Form "(&(%s)(cn=<name>))" - including literal %s */ + rc = snprintf(userdata, sizeof (userdata), _F_SETMEMBER_SSD, name); + if (rc >= sizeof (userdata) || rc < 0) { + return (NSS_NOTFOUND); + } + + /* Perform the search */ + rc = __ns_ldap_list(_NETGROUP, filter, _merge_SSD_filter, attrs, NULL, + 0, result, &error, NULL, userdata); + if (error != NULL && switch_err(rc, error) == NSS_TRYAGAIN) { + /* + * Return NSS_TRYAGAIN (rather than looping here) so that the + * nscd or the name service switch frontend can manage the + * retries. + */ + (void) __ns_ldap_freeError(&error); + return (NSS_TRYAGAIN); + } + (void) __ns_ldap_freeError(&error); + if (rc != NS_LDAP_SUCCESS) { + return (NSS_NOTFOUND); + } + + return (NSS_SUCCESS); +} + +/* + * Get the specified netgroup from LDAP. Only to be called by netgroup_get() or + * ng_refresh(); + * + * On successful read from ldap, the netgroup is added to the cache and a held + * reference is returned. If the cache is enabled, a lookup that returns + * NSS_NOTFOUND will lead to a negative cache entry. + */ +static nss_status_t +ngc_get_from_ldap(const char *ngname, netgroup_t **ngp) +{ + char filter[SEARCHFILTERLEN]; + char name[SEARCHFILTERLEN]; + char userdata[SEARCHFILTERLEN]; + ns_ldap_result_t *result = NULL; + ns_ldap_error_t *error = NULL; + int rc; + nss_status_t status; + netgroup_t *ng, *ngc; + + VERIFY(!MUTEX_HELD(&ngc_lock)); + + status = ngc_ldap_search(ngname, netgrent_attrs, &result); + if (status == NSS_NOTFOUND && ngc_enable && ngc_neg_ttl > 0) { + /* Add a negative entry, being careful not to allow too many */ + mutex_enter(&ngc_lock); + if (ngc_neg_count >= ngc_neg_max) { + mutex_exit(&ngc_lock); + return (NSS_NOTFOUND); + } + ngc_neg_count++; + mutex_exit(&ngc_lock); + + if ((ng = ngc_alloc(ngname)) == NULL) { + return (status); + } + NGC_SET(ng, NGC_FLAG_NEGATIVE | NGC_FLAG_INCACHE); + } else if (status != NSS_SUCCESS) { + return (status); + } else { + /* We got a result, cache it. */ + if ((ng = ngc_alloc(ngname)) == NULL) { + int err = errno; + (void) __ns_ldap_freeResult(&result); + errno = err; + return (NSS_ERROR); + } + + ng->ng_result = result; + if (ngc_parse_triples(ng) != 0) { + return (NSS_ERROR); + } + + ng->ng_lastchange = get_modify_timestamp(result); + + NGC_SET(ng, NGC_FLAG_INCACHE); + *ngp = ng; + } + + mutex_enter(&ngc_lock); + + if ((ngc = avl_find(&ngc_cache, ng, NULL)) != NULL) { + /* Someone else just slipped one in. This one is newer? */ + DTRACE_PROBE3(nss_ldap, netgroup__cache__add__collision, + ng->ng_name, ng, ngc); + ngc_rele_locked(ngc); + } + + ngc_set_expire_locked(ng); + avl_add(&ngc_cache, ng); + + status = NGC_NEGATIVE(ng) ? NSS_NOTFOUND : NSS_SUCCESS; + if (status == NSS_SUCCESS) { + ngc_hold_locked(ng); + *ngp = ng; + } + + mutex_exit(&ngc_lock); + + DTRACE_PROBE3(nss_ldap, netgroup__cache__add, ng->ng_name, ng, status); + return (status); +} + +static void +ngc_queue_refresh_locked(netgroup_t *ng) +{ + VERIFY(MUTEX_HELD(&ngc_lock)); + + DTRACE_PROBE2(nss_ldap, netgroup__warmer__enqueue, ng->ng_name, ng); + + NGC_SET(ng, NGC_FLAG_INWARMER); + list_insert_tail(&ngc_warm_queue, ng); + + VERIFY0(cond_signal(&ngc_warm_cv)); +} + +/* + * Get the specified netgroup from the cache. + */ +static nss_status_t +netgroup_get(const char *name, netgroup_t **ngp) +{ + netgroup_t *ng; + netgroup_t find = { .ng_name = name }; + uint32_t now; + nss_status_t status; + + mutex_enter(&ngc_lock); + + if (!ngc_initialized) { + /* + * A poorly behaved application may be trying lookups while + * simultaneously calling dlclose(). + */ + mutex_exit(&ngc_lock); + errno = ENOSYS; + return (NSS_ERROR); + } + + ng = avl_find(&ngc_cache, &find, NULL); + if (ng == NULL) { + /* not in cache, get it from LDAP */ + mutex_exit(&ngc_lock); + return (ngc_get_from_ldap(name, ngp)); + } + VERIFY(NGC_INCACHE(ng)); + + /* + * If the netgroup has expired, get it out of the cache and get it fresh + * from LDAP. + */ + now = ngc_time(); + if (ng->ng_expire <= now) { + ngc_dispose_locked(ng); + mutex_exit(&ngc_lock); + return (ngc_get_from_ldap(name, ngp)); + } + + /* + * If a refresh is needed, grab one ref for the return and another for + * the refresh. Set the refresh time forward so that we don't end up + * with concurrent refreshes. + */ + if (ngc_enable && ng->ng_refresh < now && !NGC_INWARMER(ng) && + !NGC_NEGATIVE(ng)) { + ng->ng_refresh = ng->ng_expire; + ngc_hold_locked(ng); + ngc_queue_refresh_locked(ng); + } + status = NGC_NEGATIVE(ng) ? NSS_NOTFOUND : NSS_SUCCESS; + if (status == NSS_SUCCESS) { + ngc_hold_locked(ng); + *ngp = ng; + } + mutex_exit(&ngc_lock); + + DTRACE_PROBE3(nss_ldap, netgroup__get__from__cache, ng->ng_name, ng, + status); + return (status); +} + +/* + * Dispose of all expired netgroups that are in the cache. + */ +static void +ngc_reap_locked(void) +{ + list_t *queues[] = { + &ngc_neg_expire_queue, + &ngc_pos_expire_queue, + }; + netgroup_t *ng; + uint32_t now = ngc_time(); + uint32_t i; + + VERIFY(MUTEX_HELD(&ngc_lock)); + + for (i = 0; i < ARRAY_SIZE(queues); i++) { + for (ng = list_head(queues[i]); + ng != NULL && now >= ng->ng_expire; + ng = list_head(queues[i])) { + DTRACE_PROBE3(nss_ldap, netgroup__reap, ng->ng_name, ng, + queues[i]); + VERIFY(NGC_INCACHE(ng)); + ngc_dispose_locked(ng); + VERIFY3P(list_head(queues[i]), !=, ng); + } + } +} + +/* + * This worker thread picks up netgroups that need to be refreshed from + * ngc_warm_queue. It also wakes up from time to time (ngc_reap_interval) to + * clear cruft from the cache. + * + * Several things can happen to a netgroup that is in this queue. + * + * - There could be a delay in queue processing and the netgroup may have + * already been evicted from the cache. In this case it is not refreshed. + * - The LDAP server may not provide a modifyTimestamp attr. In this case, the + * netgroup will be fully reloaded on demand. + * - Most commonly, the current modifyTimestamp value matches the value found in + * the cache. The expire and refresh times are updated as though the entire + * netgroup was just loaded. + * - A newer modifyTimestamp may be seen. This causes the netgroup to be + * expired from the cache. + * + * As each netgroup was placed in ngc_warm_queue, a reference was taken. That + * reference is released as this thread processes the renewal. + */ +static void * +ngc_warmer(void *data __unused) +{ + netgroup_t *ng; + netgroup_t *newng; + nss_status_t status; + ns_ldap_result_t *result = NULL; + avl_index_t where; + uint64_t lastchange; + timestruc_t reltime; + int err; + + mutex_enter(&ngc_lock); + + reltime.tv_sec = ngc_reap_interval; + reltime.tv_nsec = 0; + + for (;;) { + err = cond_reltimedwait(&ngc_warm_cv, &ngc_lock, &reltime); + VERIFY(err == 0 || err == ETIME || err == EINTR); + + if (ngc_warmer_die) { + mutex_exit(&ngc_lock); + break; + } + + /* + * First, do a little housekeeping. + */ + ngc_reap_locked(); + + ng = list_remove_head(&ngc_warm_queue); + if (ng != NULL) { + NGC_CLEAR(ng, NGC_FLAG_INWARMER); + } + + if (ng == NULL) { + /* Timeout or interrupted by a signal */ + continue; + } + + mutex_exit(&ngc_lock); + status = ngc_ldap_search(ng->ng_name, netgrent_stamp, &result); + mutex_enter(&ngc_lock); + if (status != NSS_SUCCESS) { + /* + * Either the server does not support modifyTimestamp or + * something worse happened. Since we aren't certain of + * the reason, do not evict it from the cache. + */ + DTRACE_PROBE3(nss_ldap, netgroup__warmer__ldap__fail, + ng->ng_name, ng, status); + ngc_rele_locked(ng); + continue; + } + lastchange = get_modify_timestamp(result); + (void) __ns_ldap_freeResult(&result); + if (lastchange == 0) { + DTRACE_PROBE2(nss_ldap, netgroup__warmer__no__stamp, + ng->ng_name, ng); + ngc_rele_locked(ng); + continue; + } + + if (lastchange == ng->ng_lastchange && !NGC_INCACHE(ng) && + avl_find(&ngc_cache, ng, &where) == NULL) { + /* + * The netgroup has not changed, but it has been + * expired. Rip it from the jaws of death. + */ + + DTRACE_PROBE2(nss_ldap, netgroup__warmer__resurrection, + ng->ng_name, ng); + list_remove(&ngc_graveyard, ng); + avl_insert(&ngc_cache, ng, where); + NGC_SET(ng, NGC_FLAG_INCACHE); + ngc_set_expire_locked(ng); + + ngc_rele_locked(ng); + continue; + } + if (lastchange == ng->ng_lastchange) { + /* Netgroup has not changed, move expiry ahead */ + + DTRACE_PROBE2(nss_ldap, netgroup__warmer__renewal, + ng->ng_name, ng); + ngc_set_expire_locked(ng); + ngc_rele_locked(ng); + continue; + } + + /* + * The netgroup has been changed. Expire the current netgroup + * then fetch a fresh copy. That order is important so that the + * netgroup is not in the cache when ngc_get_from_ldap() tries + * to add the fresh copy. Keep the hold on ng until after + * ngc_get_from_ldap() completes to ensure that ng->ng_name does + * not get freed while it is still needed. + */ + DTRACE_PROBE2(nss_ldap, netgroup__warmer__expire, ng->ng_name, + ng); + ngc_dispose_locked(ng); + + mutex_exit(&ngc_lock); + status = ngc_get_from_ldap(ng->ng_name, &newng); + mutex_enter(&ngc_lock); + + if (status == NSS_SUCCESS) { + DTRACE_PROBE3(nss_ldap, + netgroup__warmer__reload__success, newng->ng_name, + newng, ng); + ngc_rele_locked(newng); + } else { + DTRACE_PROBE3(nss_ldap, netgroup__warmer__reload__fail, + ng->ng_name, ng, status); + } + ngc_rele_locked(ng); + } + + return (NULL); +} + +/* + * Netgroup table management. This is used during a query to handle nested + * netgroups while avoiding loops. + */ static hash_t get_hash(const char *s) @@ -121,12 +1245,12 @@ add_netgroup_name(const char *name, netgroup_table_t *tab) } h = get_hash(name); - ng = tab->hash_list[h]; + ng = tab->ngt_hash_list[h]; while (ng != NULL) { - if (strcmp(name, ng->name) == 0) + if (strcmp(name, ng->ngn_name) == 0) break; - ng = ng->next_hash; + ng = ng->ngn_next_hash; } if (ng == NULL) { @@ -134,15 +1258,15 @@ add_netgroup_name(const char *name, netgroup_table_t *tab) calloc(1, sizeof (netgroup_name_t)); if (ng_new == NULL) return (-1); - ng_new->name = strdup(name); - if (ng_new->name == NULL) { + ng_new->ngn_name = strdup(name); + if (ng_new->ngn_name == NULL) { free(ng_new); return (-1); } - ng_new->next_hash = tab->hash_list[h]; - tab->hash_list[h] = ng_new; - ng_new->next = tab->to_do; - tab->to_do = ng_new; + ng_new->ngn_next_hash = tab->ngt_hash_list[h]; + tab->ngt_hash_list[h] = ng_new; + ng_new->ngn_next = tab->ngt_to_do; + tab->ngt_to_do = ng_new; } return (0); } @@ -155,11 +1279,11 @@ get_next_netgroup(netgroup_table_t *tab) if (tab == NULL) return (NULL); - ng = tab->to_do; + ng = tab->ngt_to_do; if (ng != NULL) { - tab->to_do = ng->next; - ng->next = tab->done; - tab->done = ng; + tab->ngt_to_do = ng->ngn_next; + ng->ngn_next = tab->ngt_done; + tab->ngt_done = ng; } return (ng); } @@ -172,17 +1296,17 @@ free_netgroup_table(netgroup_table_t *tab) if (tab == NULL) return; - for (ng = tab->to_do; ng != NULL; ng = next) { - if (ng->name != NULL) - free(ng->name); - next = ng->next; + for (ng = tab->ngt_to_do; ng != NULL; ng = next) { + if (ng->ngn_name != NULL) + free(ng->ngn_name); + next = ng->ngn_next; free(ng); } - for (ng = tab->done; ng != NULL; ng = next) { - if (ng->name != NULL) - free(ng->name); - next = ng->next; + for (ng = tab->ngt_done; ng != NULL; ng = next) { + if (ng->ngn_name != NULL) + free(ng->ngn_name); + next = ng->ngn_next; free(ng); } (void) memset(tab, 0, sizeof (*tab)); @@ -190,8 +1314,8 @@ free_netgroup_table(netgroup_table_t *tab) /* * domain comparing routine - * n1: See if n1 is n2 or an ancestor of it - * n2: (in string terms, n1 is a suffix of n2) + * n1: See if n1 is n2 or an ancestor of it + * n2: (in string terms, n1 is a suffix of n2) * Returns ZERO for success, -1 for failure. */ static int @@ -233,14 +1357,15 @@ domcmp(const char *n1, const char *n2) } static int -split_triple(char *triple, char **hostname, char **username, char **domain) +split_triple(char *triple, const char **hostname, const char **username, + const char **domain) { int i, syntax_err; char *splittriple[3]; char *p = triple; #ifdef DEBUG - (void) fprintf(stdout, "\n[getnetgrent.c: split_triple]\n"); + (void) fprintf(stderr, "\n[getnetgrent.c: split_triple]\n"); #endif /* DEBUG */ if (triple == NULL) @@ -308,7 +1433,7 @@ split_triple(char *triple, char **hostname, char **username, char **domain) */ static int -match_triple_entry(struct nss_innetgr_args *ia, const ns_ldap_entry_t *entry) +match_triple(struct nss_innetgr_args *ia, netgroup_t *ng) { int ndomains; char **pdomains; @@ -316,13 +1441,9 @@ match_triple_entry(struct nss_innetgr_args *ia, const ns_ldap_entry_t *entry) char **phost; int nusers; char **pusers; - char **attr; - char triple[MAX_TRIPLE_LEN]; - char *tuser, *thost, *tdomain; - int i; - char *current, *limit; - int pulen, phlen; - char *pusers0, *phost0; + const char *tuser, *thost, *tdomain; + uint32_t i, trip; + char *pusers0 = NULL, *phost0 = NULL; nhost = ia->arg[NSS_NETGR_MACHINE].argc; phost = (char **)ia->arg[NSS_NETGR_MACHINE].argv; @@ -330,7 +1451,6 @@ match_triple_entry(struct nss_innetgr_args *ia, const ns_ldap_entry_t *entry) nhost = 0; } else { phost0 = phost[0]; - phlen = strlen(phost0); #ifdef DEBUG syslog(LOG_DEBUG, "nss_ldap: match_triple_entry: " "entering with host: %s", phost0 ? phost0 : ""); @@ -342,7 +1462,6 @@ match_triple_entry(struct nss_innetgr_args *ia, const ns_ldap_entry_t *entry) nusers = 0; } else { pusers0 = pusers[0]; - pulen = strlen(pusers0); #ifdef DEBUG syslog(LOG_DEBUG, "nss_ldap: match_triple_entry: " "entering with user: %s", pusers0 ? pusers0 : ""); @@ -358,10 +1477,6 @@ match_triple_entry(struct nss_innetgr_args *ia, const ns_ldap_entry_t *entry) "entering with domain: %s", pdomains[0] ? pdomains[0] : ""); #endif - attr = __ns_ldap_getAttr(entry, _N_TRIPLE); - if (attr == NULL || *attr == NULL) - return (0); - #ifdef DEBUG syslog(LOG_DEBUG, "nss_ldap: match_triple_entry: " "(nusers: %d, nhost:%d, ndomains: %d)", @@ -369,104 +1484,56 @@ match_triple_entry(struct nss_innetgr_args *ia, const ns_ldap_entry_t *entry) #endif /* Special cases for speedup */ - if (nusers == 1 && nhost == 0 && ndomains == 0) { + if (nusers == 1 && nhost == 0 && ndomains == 0 && pusers0 != NULL) { /* Special case for finding a single user in a netgroup */ - for (; *attr; attr++) { - /* jump to first comma and check next character */ - current = *attr; + for (trip = 0; trip < ng->ng_triplecnt; trip++) { + ngc_triple_t *ngt = &ng->ng_triples[trip]; + #ifdef DEBUG syslog(LOG_DEBUG, "nss_ldap: match_triple_entry: " - "current is: %s", current); + "current user is: %s", ngt->ngt_user); #endif - if ((current = strchr(current, COMMA)) == NULL) - continue; - current++; - - /* skip whitespaces */ - while (isspace(*current)) - current++; - /* if user part is null, then treat as wildcard */ - if (*current == COMMA) + if (ngt->ngt_user == NULL) { return (1); - - /* compare first character */ - if (*pusers0 != *current) - continue; - - /* limit username to COMMA */ - if ((limit = strchr(current, COMMA)) == NULL) - continue; - *limit = '\0'; - - /* remove blanks before COMMA */ - if ((limit = strpbrk(current, " \t")) != NULL) - *limit = '\0'; - - /* compare size of username */ - if (pulen != strlen(current)) { - continue; } /* do actual compare */ - if (strncmp(pusers0, current, pulen) == 0) { + if (strcmp(pusers0, ngt->ngt_user) == 0) { return (1); - } else { - continue; } } - } else if (nusers == 0 && nhost == 1 && ndomains == 0) { + } else if (nusers == 0 && nhost == 1 && ndomains == 0 && + phost0 != NULL) { /* Special case for finding a single host in a netgroup */ - for (; *attr; attr++) { + for (trip = 0; trip < ng->ng_triplecnt; trip++) { + ngc_triple_t *ngt = &ng->ng_triples[trip]; + - /* jump to first character and check */ - current = *attr; #ifdef DEBUG syslog(LOG_DEBUG, "nss_ldap: match_triple_entry: " - "current is: %s", current); + "current host is: %s", ngt->ngt_host); #endif - current++; - - /* skip whitespaces */ - while (isspace(*current)) - current++; /* if host part is null, then treat as wildcard */ - if (*current == COMMA) + if (ngt->ngt_host == NULL) { return (1); - - /* limit hostname to COMMA */ - if ((limit = strchr(current, COMMA)) == NULL) - continue; - *limit = '\0'; - - /* remove blanks before COMMA */ - if ((limit = strpbrk(current, " \t")) != NULL) - *limit = '\0'; - - /* compare size of hostname */ - if (phlen != strlen(current)) { - continue; } /* do actual compare */ - if (strncasecmp(phost0, current, phlen) == 0) { + if (strcasecmp(phost0, ngt->ngt_host) == 0) { return (1); - } else { - continue; } } } else { - for (; *attr; attr++) { - if (strlcpy(triple, *attr, - sizeof (triple)) >= sizeof (triple)) - continue; + for (trip = 0; trip < ng->ng_triplecnt; trip++) { + thost = ng->ng_triples[trip].ngt_host; + tuser = ng->ng_triples[trip].ngt_user; + tdomain = ng->ng_triples[trip].ngt_domain; #ifdef DEBUG syslog(LOG_DEBUG, "nss_ldap: match_triple_entry: " - "triple is: %s", triple); + "triple is: (%s,%s,%s)", thost, tuser, tdomain); #endif - if (split_triple(triple, &thost, &tuser, &tdomain) != 0) - continue; if (thost != NULL && *thost != '\0' && nhost != 0) { for (i = 0; i < nhost; i++) if (strcasecmp(thost, phost[i]) == 0) @@ -497,18 +1564,6 @@ match_triple_entry(struct nss_innetgr_args *ia, const ns_ldap_entry_t *entry) } static int -match_triple(struct nss_innetgr_args *ia, ns_ldap_result_t *result) -{ - ns_ldap_entry_t *entry; - - for (entry = result->entry; entry != NULL; entry = entry->next) - if (match_triple_entry(ia, entry) == 1) - return (1); - - return (0); -} - -static int add_netgroup_member_entry(ns_ldap_entry_t *entry, netgroup_table_t *tab) { char **attrs; @@ -548,83 +1603,59 @@ add_netgroup_member(ns_ldap_result_t *result, netgroup_table_t *tab) static nss_status_t top_down_search(struct nss_innetgr_args *ia, char *netgrname) { - char searchfilter[SEARCHFILTERLEN]; - char name[SEARCHFILTERLEN]; - char userdata[SEARCHFILTERLEN]; - ns_ldap_result_t *result = NULL; - ns_ldap_error_t *error = NULL; - int rc; - nss_status_t status = NSS_NOTFOUND; - nss_status_t status1; - netgroup_table_t tab; - netgroup_name_t *ng; - int ret; + netgroup_table_t tab; + netgroup_name_t *ngn; + netgroup_t *ng; + int rc; + int serrno; (void) memset(&tab, 0, sizeof (tab)); if (add_netgroup_name(netgrname, &tab) != 0) return ((nss_status_t)NSS_NOTFOUND); - while ((ng = get_next_netgroup(&tab)) != NULL) { + while ((ngn = get_next_netgroup(&tab)) != NULL) { #ifdef DEBUG syslog(LOG_DEBUG, "nss_ldap: top_down_search: netgroup loop " - "(ng->name: %s)", ng->name ? ng->name : "null !"); + "(ngn->ngn_name: %s)", + ngn->ngn_name ? ngn->ngn_name : "null !"); #endif - if (_ldap_filter_name(name, ng->name, sizeof (name)) != 0) - break; - ret = snprintf(searchfilter, sizeof (searchfilter), - _F_SETMEMBER, name); - if (ret >= sizeof (searchfilter) || ret < 0) + switch (netgroup_get(ngn->ngn_name, &ng)) { + case NSS_SUCCESS: break; - - ret = snprintf(userdata, sizeof (userdata), _F_SETMEMBER_SSD, - name); - if (ret >= sizeof (userdata) || ret < 0) - break; - - /* searching for current netgroup name entry */ - rc = __ns_ldap_list(_NETGROUP, searchfilter, - _merge_SSD_filter, netgrent_attrs, NULL, 0, &result, - &error, NULL, userdata); - - if (error != NULL) { - status1 = switch_err(rc, error); - if (status1 == NSS_TRYAGAIN) { - (void) __ns_ldap_freeError(&error); - free_netgroup_table(&tab); - return (status1); - } + case NSS_TRYAGAIN: + free_netgroup_table(&tab); + return (NSS_TRYAGAIN); + case NSS_ERROR: + serrno = errno; + free_netgroup_table(&tab); + errno = serrno; + return (NSS_ERROR); + default: + continue; } - (void) __ns_ldap_freeError(&error); - if (rc == NS_LDAP_SUCCESS) { - if (match_triple(ia, result) == 1) { - /* We found a match */ - ia->status = NSS_NETGR_FOUND; - status = NSS_SUCCESS; + if (match_triple(ia, ng) == 1) { + /* We found a match */ + ia->status = NSS_NETGR_FOUND; + free_netgroup_table(&tab); + netgroup_rele(ng); #ifdef DEBUG - syslog(LOG_DEBUG, "nss_ldap: top_down_search: " - "found match"); + syslog(LOG_DEBUG, "nss_ldap: top_down_search: " + "found match\n"); #endif - break; - } + return (NSS_SUCCESS); + } - /* - * No match found. Check for membernisnetgroup - * in result and if yes, start again with those. - */ - rc = add_netgroup_member(result, &tab); - if (rc != 0) - break; - } else if (rc != NS_LDAP_NOTFOUND) { + rc = add_netgroup_member(ng->ng_result, &tab); + netgroup_rele(ng); + if (rc != 0) { break; } - (void) __ns_ldap_freeResult(&result); } - (void) __ns_ldap_freeResult(&result); free_netgroup_table(&tab); - return (status); + return (NSS_NOTFOUND); } /* @@ -637,8 +1668,8 @@ __netgr_in(void *a, char *netgrname) nss_status_t status = NSS_NOTFOUND; #ifdef DEBUG - (void) fprintf(stdout, "\n[getnetgrent.c: netgr_in]\n"); - (void) fprintf(stdout, "\tmachine: argc[%d]='%s' user: " + (void) fprintf(stderr, "\n[getnetgrent.c: netgr_in]\n"); + (void) fprintf(stderr, "\tmachine: argc[%d]='%s' user: " "argc[%d]='%s',\n\tdomain:argc[%d]='%s' " "netgroup: argc[%d]='%s'\n", NSS_NETGR_MACHINE, @@ -649,7 +1680,7 @@ __netgr_in(void *a, char *netgrname) PRINT_VAL(ia->arg[NSS_NETGR_DOMAIN]), NSS_NETGR_N, PRINT_VAL(ia->arg[NSS_NETGR_N])); - (void) fprintf(stdout, "\tgroups='%s'\n", netgrname); + (void) fprintf(stderr, "\tgroups='%s'\n", netgrname); #endif /* DEBUG */ ia->status = NSS_NETGR_NO; @@ -657,7 +1688,12 @@ __netgr_in(void *a, char *netgrname) if (netgrname == NULL) return (status); - return (top_down_search(ia, netgrname)); + status = top_down_search(ia, netgrname); + DTRACE_PROBE5(nss_ldap, innetgr, netgrname, + PRINT_VAL(ia->arg[NSS_NETGR_MACHINE]), + PRINT_VAL(ia->arg[NSS_NETGR_USER]), + PRINT_VAL(ia->arg[NSS_NETGR_DOMAIN]), status); + return (status); } /*ARGSUSED0*/ @@ -669,6 +1705,7 @@ netgr_in(ldap_backend_ptr be, void *a) nss_status_t rc = (nss_status_t)NSS_NOTFOUND; ia->status = NSS_NETGR_NO; + for (i = 0; i < ia->groups.argc; i++) { rc = __netgr_in(a, ia->groups.argv[i]); if (ia->status == NSS_NETGR_FOUND) @@ -677,45 +1714,23 @@ netgr_in(ldap_backend_ptr be, void *a) return (rc); } -/* - * - */ - -static nss_status_t -getnetgr_ldap_setent(ldap_backend_ptr be, void *a) -{ - const char *netgroup = (const char *) a; - getnetgrent_cookie_t *cookie; - -#ifdef DEBUG - (void) fprintf(stdout, "\n[getnetgrent.c: getnetgr_ldap_setent]\n"); -#endif /* DEBUG */ - - cookie = (getnetgrent_cookie_t *)be->netgroup_cookie; - if (cookie != NULL && cookie->netgroup != NULL) { - /* is this another set on the same netgroup */ - if (strcmp(cookie->netgroup, netgroup) == 0) - return ((nss_status_t)NSS_SUCCESS); - } - - return (NSS_NOTFOUND); -} - static void free_getnetgrent_cookie(getnetgrent_cookie_t **cookie) { getnetgrent_cookie_t *p = *cookie; #ifdef DEBUG - (void) fprintf(stdout, "\n[getnetgrent.c: free_getnetgrent_cookie]\n"); + (void) fprintf(stderr, "\n[getnetgrent.c: free_getnetgrent_cookie]\n"); #endif /* DEBUG */ if (p == NULL) return; - (void) __ns_ldap_freeResult(&p->results); - free_netgroup_table(&p->tab); - free(p->netgroup); + if (p->gnc_netgroup != NULL) { + netgroup_rele(p->gnc_netgroup); + } + free_netgroup_table(&p->gnc_tab); + free(p->gnc_name); free(p); *cookie = NULL; } @@ -726,7 +1741,7 @@ getnetgr_ldap_endent(ldap_backend_ptr be, void *a) { #ifdef DEBUG - (void) fprintf(stdout, "\n[getnetgrent.c: getnetgr_ldap_endent]\n"); + (void) fprintf(stderr, "\n[getnetgrent.c: getnetgr_ldap_endent]\n"); #endif /* DEBUG */ free_getnetgrent_cookie((getnetgrent_cookie_t **)&be->netgroup_cookie); @@ -741,7 +1756,7 @@ getnetgr_ldap_destr(ldap_backend_ptr be, void *a) { #ifdef DEBUG - (void) fprintf(stdout, "\n[getnetgrent.c: getnetgr_ldap_destr]\n"); + (void) fprintf(stderr, "\n[getnetgrent.c: getnetgr_ldap_destr]\n"); #endif /* DEBUG */ free_getnetgrent_cookie((getnetgrent_cookie_t **)&be->netgroup_cookie); @@ -750,27 +1765,81 @@ getnetgr_ldap_destr(ldap_backend_ptr be, void *a) return ((nss_status_t)NSS_NOTFOUND); } +/* + * Copies results from a buffer that may be about to be freed into a long-lived + * general-purpose buffer. + * + * val IN: The return value that needs to be copied. + * + * *bufferp IN: On the first call of this function for a particular nss + * call, this should be the address of `buffer` element of a + * nss_getnetgrent_args structure (`args`). On subsequent + * calls, it should be the value that was returned by + * reference from the previous call. Do not pass + * `&args->buffer`, rather pass a reference to a copy of + * `&args->buffer`. + * OUT: Advanced to the next unused space in args->buffer. + * + * *leftp IN: The amount of space in `args->buffer` that remains unused + * and available for copying `val` into `args->buffer`. + * The first call should pass a reference to a copy of + * `args->buflen` and subsequent calls should use the value + * returned by the previous call. + * OUT: The amount of space that remains after copying `val`. + * + * *retbufp OUT: Will be updated to reference the location in args->buffer + * that contains a copy of val. Typically will be one of + * args->retp[]. + * + * After the following calls (plus error checking of function returns) + * + * char *buf = args->result; + * size_t left = args->buflen; + * set_retbuf("host", &buf, &left, &args->retp[NSS_NETGR_MACHINE]); + * set_retbuf("user", &buf, &left, &args->retp[NSS_NETGR_USER]); + * set_retbuf("domain", &buf, &left, &args->retp[NSS_NETGR_DOMAIN]); + * + * `args` looks like: + * + * buffer = "host\0user\0domain\0" + * ^ ^ ^ + * | | retp[NSS_NETGR_DOMAIN] + * | retp[NSS_NETGR_USER] + * retp[NSS_NETGR_HOST] + */ +static int +set_retbuf(const char *val, char **bufferp, size_t *leftp, char **retbufp) +{ + char *buffer = *bufferp; + size_t left = *leftp; + size_t len; + + if (val == NULL) { + *retbufp = NULL; + return (0); + } + len = strlcpy(buffer, val, left); + if (len >= left) { + return (-1); + } + *retbufp = buffer; + *bufferp = buffer + len; + *leftp = left - len; + return (0); +} static nss_status_t getnetgr_ldap_getent(ldap_backend_ptr be, void *a) { struct nss_getnetgrent_args *args; getnetgrent_cookie_t *p; - char searchfilter[SEARCHFILTERLEN]; - char userdata[SEARCHFILTERLEN]; - char name[SEARCHFILTERLEN]; - int rc; - ns_ldap_result_t *result = NULL; - ns_ldap_error_t *error = NULL; - char **attrs; - char *hostname, *username, *domain; - char *buffer; nss_status_t status = NSS_SUCCESS; - netgroup_name_t *ng; + netgroup_name_t *ngn; int ret; + ns_ldap_result_t *results = NULL; #ifdef DEBUG - (void) fprintf(stdout, "\n[getnetgrent.c: getnetgr_ldap_getent]\n"); + (void) fprintf(stderr, "\n[getnetgrent.c: getnetgr_ldap_getent]\n"); #endif /* DEBUG */ args = (struct nss_getnetgrent_args *)a; @@ -778,8 +1847,9 @@ getnetgr_ldap_getent(ldap_backend_ptr be, void *a) args->status = NSS_NETGR_NO; p = (getnetgrent_cookie_t *)be->netgroup_cookie; - if (p == NULL) + if (p == NULL) { return ((nss_status_t)NSS_SUCCESS); + } for (;;) { /* @@ -788,130 +1858,96 @@ getnetgr_ldap_getent(ldap_backend_ptr be, void *a) * processed. * Needed for nested netgroup (memberNisNetgroup attributes). */ - if (p->results == NULL) { - if ((ng = get_next_netgroup(&p->tab)) != NULL) { - if (_ldap_filter_name(name, ng->name, - sizeof (name)) != 0) - break; - - ret = snprintf(searchfilter, - sizeof (searchfilter), - _F_SETMEMBER, name); - if (ret >= sizeof (searchfilter) || ret < 0) - break; - + if (p->gnc_netgroup == NULL) { + if ((ngn = get_next_netgroup(&p->gnc_tab)) == NULL) { + /* No more netgroups to process */ #ifdef DEBUG syslog(LOG_DEBUG, "nss_ldap: " - "getnetgr_ldap_getent: " - "netgroup name: %s", name); -#endif - ret = snprintf(userdata, sizeof (userdata), - _F_SETMEMBER_SSD, name); - if (ret >= sizeof (userdata) || ret < 0) - break; - - result = NULL; - rc = __ns_ldap_list(_NETGROUP, searchfilter, - _merge_SSD_filter, netgrent_attrs, NULL, - 0, &result, &error, NULL, userdata); - (void) __ns_ldap_freeError(&error); - - if (rc == NS_LDAP_SUCCESS && result != NULL) { - p->results = result; - } else { -#ifdef DEBUG - syslog(LOG_DEBUG, "nss_ldap: " - "getnetgr_ldap_getent: " - "__ns_ldap_list() returned %d " - "(result: 0x%x)", rc, result); + "getnetgr_ldap_getent: no more netgroup " + "to process.\n"); #endif - /* - * Will exit when no more netgroup - * to search and no more p->results - * to process. - */ - (void) __ns_ldap_freeResult(&result); - } - } else { /* no more netgroup to process */ + break; /* from loop */ + } + + switch (netgroup_get(ngn->ngn_name, &p->gnc_netgroup)) { + case NSS_SUCCESS: + break; /* from switch */ + case NSS_TRYAGAIN: + return (NSS_TRYAGAIN); + default: /* - * If no more results to process, and since - * there's no more netgroup to process either, - * then it's time to break and exit the for - * loop. + * Likely a nested netgroup that doesn't exist, + * but there may be more to try. */ -#ifdef DEBUG - syslog(LOG_DEBUG, "nss_ldap: " - "getnetgr_ldap_getent: no more netgroup " - "to process, p->results: 0x%x", - p->results); -#endif - if (p->results == NULL) - break; + continue; } + + p->gnc_entry = NULL; } - if (p->results == NULL) - continue; - if (p->entry == NULL) - p->entry = p->results->entry; + results = p->gnc_netgroup->ng_result; - if (p->entry == NULL) + /* Empty or missing netgroup */ + if (results == NULL) { continue; + } - if (p->attrs == NULL) { - attrs = __ns_ldap_getAttr(p->entry, _N_TRIPLE); - if (attrs != NULL && *attrs != NULL) - p->attrs = attrs; + if (p->gnc_entry == NULL) { + p->gnc_entry = results->entry; + if (p->gnc_entry == NULL) { + continue; + } } - if (p->attrs != NULL) { - attrs = p->attrs; - buffer = args->buffer; + if (p->gnc_curtriple < p->gnc_netgroup->ng_triplecnt) { + ngc_triple_t *ngt; + char *buffer = args->buffer; + size_t left = args->buflen; - if (strlcpy(buffer, *attrs, args->buflen) >= - args->buflen) { + ngt = &p->gnc_netgroup->ng_triples[p->gnc_curtriple]; + p->gnc_curtriple++; + + /* + * The triple (ngt) may be freed before args->retp[] are + * consumed. Copy the components from the cache into + * args->buffer. + */ + if (set_retbuf(ngt->ngt_host, &buffer, &left, + &args->retp[NSS_NETGR_MACHINE]) != 0 || + set_retbuf(ngt->ngt_user, &buffer, &left, + &args->retp[NSS_NETGR_USER]) != 0 || + set_retbuf(ngt->ngt_domain, &buffer, &left, + &args->retp[NSS_NETGR_DOMAIN]) != 0) { status = NSS_STR_PARSE_ERANGE; break; } + args->status = NSS_NETGR_FOUND; - rc = split_triple(buffer, &hostname, &username, - &domain); - attrs++; - if (attrs != NULL && *attrs != NULL) - p->attrs = attrs; - else - p->attrs = NULL; - if (rc == 0) { - args->retp[NSS_NETGR_MACHINE] = hostname; - args->retp[NSS_NETGR_USER] = username; - args->retp[NSS_NETGR_DOMAIN] = domain; - args->status = NSS_NETGR_FOUND; #ifdef DEBUG - syslog(LOG_DEBUG, "nss_ldap: " - "getnetgr_ldap_getent: found triple " - "(%s, %s, %s), 0x%x to process", - hostname ? hostname : "", - username ? username : "", - domain ? domain : "", - p->attrs); + syslog(LOG_DEBUG, "nss_ldap: getnetgr_ldap_getent: " + "found triple (%s,%s,%s), %d more to process", + args->retp[NSS_NETGR_MACHINE] ? + args->retp[NSS_NETGR_MACHINE] : "", + args->retp[NSS_NETGR_USER] ? + args->retp[NSS_NETGR_USER] : "", + args->retp[NSS_NETGR_DOMAIN] ? + args->retp[NSS_NETGR_DOMAIN] : "", + p->gnc_netgroup->ng_triplecnt - p->gnc_curtriple); #endif - if (p->attrs != NULL) - break; - } + break; } - if (p->attrs == NULL) { - rc = add_netgroup_member_entry(p->entry, &p->tab); - if (rc != 0) { - args->status = NSS_NETGR_NO; - break; - } + /* Despite its name, this adds all members on this entry. */ + if (add_netgroup_member_entry(p->gnc_entry, &p->gnc_tab) != 0) { + args->status = NSS_NETGR_NO; + break; + } - p->entry = p->entry->next; - if (p->entry == NULL) - (void) __ns_ldap_freeResult(&p->results); - if (args->status == NSS_NETGR_FOUND) - break; + p->gnc_entry = p->gnc_entry->next; + if (p->gnc_entry == NULL) { + netgroup_rele(p->gnc_netgroup); + p->gnc_netgroup = NULL; + p->gnc_curtriple = 0; } } @@ -921,14 +1957,13 @@ getnetgr_ldap_getent(ldap_backend_ptr be, void *a) static ldap_backend_op_t getnetgroup_ops[] = { getnetgr_ldap_destr, getnetgr_ldap_endent, - getnetgr_ldap_setent, + NULL, getnetgr_ldap_getent, }; /* - * + * setnetgrent() backend, at least for non-nscd case. */ - static nss_status_t netgr_set(ldap_backend_ptr be, void *a) { @@ -938,8 +1973,8 @@ netgr_set(ldap_backend_ptr be, void *a) getnetgrent_cookie_t *p; #ifdef DEBUG - (void) fprintf(stdout, "\n[getnetgrent.c: netgr_set]\n"); - (void) fprintf(stdout, + (void) fprintf(stderr, "\n[getnetgrent.c: netgr_set]\n"); + (void) fprintf(stderr, "\targs->netgroup: %s\n", ISNULL(args->netgroup)); #endif /* DEBUG */ @@ -950,12 +1985,12 @@ netgr_set(ldap_backend_ptr be, void *a) p = (getnetgrent_cookie_t *)calloc(1, sizeof (getnetgrent_cookie_t)); if (p == NULL) return ((nss_status_t)NSS_NOTFOUND); - p->netgroup = strdup(args->netgroup); - if (p->netgroup == NULL) { + p->gnc_name = strdup(args->netgroup); + if (p->gnc_name == NULL) { free(p); return ((nss_status_t)NSS_NOTFOUND); } - if (add_netgroup_name(args->netgroup, &p->tab) == -1) { + if (add_netgroup_name(args->netgroup, &p->gnc_tab) == -1) { free_getnetgrent_cookie(&p); return ((nss_status_t)NSS_NOTFOUND); } @@ -976,11 +2011,275 @@ netgr_set(ldap_backend_ptr be, void *a) get_be->netgroup_cookie = p; args->iterator = (nss_backend_t *)get_be; - (void) __ns_ldap_freeResult(&be->result); - return (NSS_SUCCESS); } +/* + * Initialization and configuration + */ + +static int +ngc_compare(const void *l, const void *r) +{ + const netgroup_t *ngl = l, *ngr = r; + int ret; + + ret = strcmp(ngl->ng_name, ngr->ng_name); + if (ret < 0) + return (-1); + if (ret > 0) + return (1); + return (0); +} + +static int +yntoi(const char *yorn, int minval __unused, int maxval __unused, + int *retval, char *errbuf, size_t errbufsz) +{ + if (strcasecmp(yorn, "yes") == 0) { + *retval = 1; + return (0); + } + if (strcasecmp(yorn, "no") == 0) { + *retval = 0; + return (0); + } + (void) snprintf(errbuf, errbufsz, + "invalid value '%s': expected 'yes' or 'no'", yorn); + return (-1); +} + +static int +safestrtoi(const char *str, int minval, int maxval, int *retval, + char *errbuf, size_t errbufsz) +{ + long val; + char *end; + + errno = 0; + val = strtol(str, &end, 10); + if (errno != 0 || *end != '\0' || val < minval || val > maxval) { + (void) snprintf(errbuf, errbufsz, + "invalid value '%s': expected integer between %d and %d", + str, minval, maxval); + return (-1); + } + *retval = (int)val; + return (0); +} + +/* + * nscd does not provide us with the config or an easy way to get it. We'll + * fetch it ourselves. + */ +static void +read_nscd_conf(void) +{ + const int week = 60 * 60 * 24 * 7; + struct { + char *key; + int *valp; + int defval; + int minval; + int maxval; + int (*toi)(const char *, int, int, int *, char *, size_t); + } config[] = { + { "enable-cache", (int *)&ngc_enable, 1, 0, 1, yntoi }, + { "positive-time-to-live", &ngc_pos_ttl, NGC_POS_TTL, 0, week, + safestrtoi }, + { "negative-time-to-live", &ngc_neg_ttl, NGC_NEG_TTL, 0, week, + safestrtoi } + }; + uint32_t i; + FILE *cfg; + char buf[1024]; + uint32_t line = 0; + + /* + * Set values back to their defaults in case they were removed from + * nscd.conf. + */ + for (i = 0; i < ARRAY_SIZE(config); i++) { + *config[i].valp = config[i].defval; + }; + + if ((cfg = fopen(NSCD_CONF, "rF")) == NULL) { + syslog(LOG_ERR, "nss_ldap: unable to read nscd.conf: %m"); + return; + } + + while (fgets(buf, sizeof (buf), cfg) != NULL) { + char *key, *db, *strval, *junk, *last = NULL; + char errmsg[1024]; + + line++; + + if ((key = strchr(buf, '#')) != NULL) { + *key = '\0'; + } + if ((key = strtok_r(buf, "\n\t ", &last)) == NULL) { + continue; + } + if ((db = strtok_r(NULL, "\n\t ", &last)) == NULL) { + continue; + } + if (strcmp(db, "netgroup") != 0) { + continue; + } + + strval = strtok_r(NULL, "\n\t ", &last); + junk = strtok_r(NULL, "\n\t ", &last); + + for (i = 0; i < ARRAY_SIZE(config); i++) { + if (strcmp(config[i].key, key) == 0) { + break; + } + } + if (i == ARRAY_SIZE(config)) { + syslog(LOG_ERR, "nss_ldap: %s:%d: " + "netgroup attribute '%s' invalid", NSCD_CONF, + line, key); + continue; + } + if (strval == NULL) { + syslog(LOG_ERR, "nss_ldap: %s:%d: " + "netgroup attribute '%s' missing value", NSCD_CONF, + line, key); + continue; + } + if (junk != NULL) { + syslog(LOG_ERR, "nss_ldap: %s:%d " + "netgroup attribute '%s' has too many values", + NSCD_CONF, line, key); + continue; + } + if (config[i].toi(strval, config[i].minval, config[i].maxval, + config[i].valp, errmsg, sizeof (errmsg)) != 0) { + syslog(LOG_ERR, "nss_ldap: %s:%d: %s", NSCD_CONF, line, + key, errmsg); + continue; + } + } + VERIFY0(fclose(cfg)); +} + +static void +ngc_init(void) +{ + char fmri[sizeof (NSCD_FMRI) + 1]; /* space for extra char */ + scf_handle_t *scf = NULL; + + mutex_enter(&ngc_lock); + + DTRACE_PROBE1(nss_ldap, ngc__init, ngc_initialized); + + VERIFY0(ngc_initialized); + + /* See ngc_time() */ + ngc_first_tick = NSEC2SEC(gethrtime()); + + read_nscd_conf(); + + /* + * Even when not caching, the caching structures are used - netgroups + * just expire immediately and are pruned when ng_refcnt drops to zero. + */ + avl_create(&ngc_cache, ngc_compare, sizeof (netgroup_t), + offsetof(netgroup_t, ng_linkage)); + list_create(&ngc_graveyard, sizeof (netgroup_t), + offsetof(netgroup_t, ng_linkage)); + list_create(&ngc_neg_expire_queue, sizeof (netgroup_t), + offsetof(netgroup_t, ng_expire_linkage)); + list_create(&ngc_pos_expire_queue, sizeof (netgroup_t), + offsetof(netgroup_t, ng_expire_linkage)); + + /* + * Name service backends may run under nscd or as part of some other + * process that is making a request. Keep things as light as possible + * while not running under nscd. + */ + if (ngc_enable && (scf = scf_handle_create(SCF_VERSION)) != NULL && + scf_handle_bind(scf) == 0 && + scf_myname(scf, fmri, sizeof (fmri)) == (sizeof (NSCD_FMRI) - 1) && + strcmp(fmri, NSCD_FMRI) == 0) { + char *env; + + /* For testing */ + if ((env = getenv("NSS_LDAP_REAP_INTERVAL")) != NULL) { + ngc_reap_interval = atoi(env); + VERIFY3S(ngc_reap_interval, >, 0); + } + + ngc_warmer_die = B_FALSE; + list_create(&ngc_warm_queue, sizeof (netgroup_t), + offsetof(netgroup_t, ng_warm_linkage)); + VERIFY0(cond_init(&ngc_warm_cv, USYNC_THREAD, NULL)); + if (thr_create(NULL, 0, ngc_warmer, NULL, 0, + &ngc_warmer_tid) != 0) { + ngc_warmer_tid = 0; + } + } else { + ngc_enable = B_FALSE; + ngc_warmer_tid = 0; + } + if (scf != NULL) { + scf_handle_destroy(scf); + } + + ngc_initialized = B_TRUE; + mutex_exit(&ngc_lock); +} + +/* + * This performs an orderly cleanup when the nss_ldap is unloaded. The name + * service switch (with nscd or arbitrary libc consumer) doesn't intend for + * backends to keep state, so we rely on a little help from the dynamic linker + * on unload. + */ +#pragma fini(ngc_fini) +void +ngc_fini(void) +{ + netgroup_t *ng, *next; + + mutex_enter(&ngc_lock); + + DTRACE_PROBE1(nss_ldap, ngc__fini, ngc_initialized); + + if (!ngc_initialized || !ngc_enable) { + mutex_exit(&ngc_lock); + return; + } + + ngc_initialized = B_FALSE; + + if (ngc_warmer_tid != 0) { + ngc_warmer_die = B_TRUE; + cond_signal(&ngc_warm_cv); + + mutex_exit(&ngc_lock); + (void) thr_join(ngc_warmer_tid, NULL, NULL); + mutex_enter(&ngc_lock); + } + + for (ng = avl_first(&ngc_cache); ng != NULL; ng = next) { + next = AVL_NEXT(&ngc_cache, ng); + ng->ng_refcnt = 0; + ngc_dispose_locked(ng); + } + avl_destroy(&ngc_cache); + list_destroy(&ngc_neg_expire_queue); + list_destroy(&ngc_pos_expire_queue); + + for (ng = list_head(&ngc_graveyard); ng != NULL; ng = next) { + next = list_next(&ngc_graveyard, ng); + ng->ng_refcnt = 0; + ngc_dispose_locked(ng); + } + list_destroy(&ngc_graveyard); + + mutex_exit(&ngc_lock); +} /*ARGSUSED1*/ static nss_status_t @@ -988,7 +2287,7 @@ netgr_ldap_destr(ldap_backend_ptr be, void *a) { #ifdef DEBUG - (void) fprintf(stdout, "\n[getnetgrent.c: netgr_ldap_destr]\n"); + (void) fprintf(stderr, "\n[getnetgrent.c: netgr_ldap_destr]\n"); #endif /* DEBUG */ (void) _clean_ldap_backend(be); @@ -997,8 +2296,6 @@ netgr_ldap_destr(ldap_backend_ptr be, void *a) } - - static ldap_backend_op_t netgroup_ops[] = { netgr_ldap_destr, 0, @@ -1022,10 +2319,13 @@ _nss_ldap_netgroup_constr(const char *dummy1, const char *dummy2, { #ifdef DEBUG - (void) fprintf(stdout, + (void) fprintf(stderr, "\n[getnetgrent.c: _nss_ldap_netgroup_constr]\n"); #endif /* DEBUG */ + /* Initialize the cache. */ + ngc_init(); + return ((nss_backend_t *)_nss_ldap_constr(netgroup_ops, sizeof (netgroup_ops)/sizeof (netgroup_ops[0]), _NETGROUP, netgrent_attrs, NULL)); diff --git a/usr/src/lib/nsswitch/ldap/common/getspent.c b/usr/src/lib/nsswitch/ldap/common/getspent.c index bcdba2998c..1557d5e550 100644 --- a/usr/src/lib/nsswitch/ldap/common/getspent.c +++ b/usr/src/lib/nsswitch/ldap/common/getspent.c @@ -21,6 +21,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2019 Joyent, Inc. */ #include <shadow.h> @@ -77,8 +78,10 @@ _nss_ldap_shadow2str(ldap_backend_ptr be, nss_XbyY_args_t *argp) ns_ldap_result_t *result = be->result; char **uid, **passwd, **last, **smin, **smax; char **warning, **inactive, **expire, **flag; - char *last_str, *min_str, *max_str, *warning_str; - char *inactive_str, *expire_str, *flag_str; + char *last_str = _NO_VALUE, *min_str = _NO_VALUE; + char *max_str = _NO_VALUE, *warning_str = _NO_VALUE; + char *inactive_str = _NO_VALUE, *expire_str = _NO_VALUE; + char *flag_str = _NO_VALUE; if (result == NULL) return (NSS_STR_PARSE_PARSE); diff --git a/usr/src/lib/nsswitch/ldap/common/ldap_common.h b/usr/src/lib/nsswitch/ldap/common/ldap_common.h index 690dd15adc..1a8fe55dd9 100644 --- a/usr/src/lib/nsswitch/ldap/common/ldap_common.h +++ b/usr/src/lib/nsswitch/ldap/common/ldap_common.h @@ -20,6 +20,7 @@ */ /* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2019 Joyent, Inc. */ #ifndef _LDAP_COMMON_H @@ -70,8 +71,7 @@ extern "C" { #define NSS_STR_PARSE_NO_ADDR (NSS_STR_PARSE_ERANGE + 100) #define NSS_STR_PARSE_NO_RESULT (NSS_STR_PARSE_ERANGE + 101) -#define DOTTEDSUBDOMAIN(string) \ - ((string != NULL) && (strchr(string, '.') != NULL)) +#define DOTTEDSUBDOMAIN(string) (strchr(string, '.') != NULL) #define SEARCHFILTERLEN 256 #define _NO_VALUE "" diff --git a/usr/src/lib/nsswitch/ldap/common/provider.d b/usr/src/lib/nsswitch/ldap/common/provider.d new file mode 100644 index 0000000000..1f5a1f271e --- /dev/null +++ b/usr/src/lib/nsswitch/ldap/common/provider.d @@ -0,0 +1,50 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2019 Joyent, Inc. + */ + +provider nss_ldap { + /* netgroup-* probes all start with string, netgroup_t */ + probe netgroup__cache__add__collision(string, uintptr_t, uintptr_t); + probe netgroup__cache__add(string, uintptr_t, int); + probe netgroup__cache__dispose(string, uintptr_t, int, int); + probe netgroup__cache__hold(string, uintptr_t, int); + probe netgroup__cache__free(string, uintptr_t); + probe netgroup__cache__rele(string, uintptr_t, int); + probe netgroup__cache__to__graveyard(string, uintptr_t); + probe netgroup__cache__triple(string, uintptr_t, string, string, + string); + probe netgroup__get__from__cache(string, uintptr_t, int); + probe netgroup__reap(string, uintptr_t, uintptr_t); + probe netgroup__warmer__backwards(string, uintptr_t); + probe netgroup__warmer__enqueue(string, uintptr_t); + probe netgroup__warmer__expire(string, uintptr_t); + probe netgroup__warmer__ldap__fail(string, uintptr_t); + probe netgroup__warmer__no__stamp(string, uintptr_t); + probe netgroup__warmer__reload__fail(string, uintptr_t, int); + probe netgroup__warmer__reload__success(string, uintptr_t, uintptr_t); + probe netgroup__warmer__renewal(string, uintptr_t); + probe netgroup__warmer__resurrection(string, uintptr_t); + + /* probes not starting with netgroup-* can be more diverse */ + probe innetgr(string, string, string, string, int); + probe ngc__fini(int); + probe ngc__init(int); + probe ngc__tick(uint32_t); +}; + +#pragma D attributes Evolving/Evolving/Common provider nss_ldap provider +#pragma D attributes Private/Private/Unknown provider nss_ldap module +#pragma D attributes Private/Private/Unknown provider nss_ldap function +#pragma D attributes Evolving/Evolving/Common provider nss_ldap name +#pragma D attributes Evolving/Evolving/Common provider nss_ldap args diff --git a/usr/src/lib/nsswitch/ldap/i386/Makefile b/usr/src/lib/nsswitch/ldap/i386/Makefile index ee9fdcccc5..b947d468f2 100644 --- a/usr/src/lib/nsswitch/ldap/i386/Makefile +++ b/usr/src/lib/nsswitch/ldap/i386/Makefile @@ -19,11 +19,9 @@ # # CDDL HEADER END # -# -#ident "%Z%%M% %I% %E% SMI" -# # Copyright (c) 1999,2001 by Sun Microsystems, Inc. # All rights reserved. +# Copyright 2019 Joyent, Inc. # # lib/nsswitch/ldap/i386/Makefile @@ -31,6 +29,6 @@ include ../Makefile.com LIBS = $(DYNLIB1) -include ../../Makefile.targ +include ../Makefile.targ install: all $(ROOTLIBS) diff --git a/usr/src/lib/nsswitch/ldap/sparc/Makefile b/usr/src/lib/nsswitch/ldap/sparc/Makefile index a87617b860..eea0b48f93 100644 --- a/usr/src/lib/nsswitch/ldap/sparc/Makefile +++ b/usr/src/lib/nsswitch/ldap/sparc/Makefile @@ -19,11 +19,9 @@ # # CDDL HEADER END # -# -#ident "%Z%%M% %I% %E% SMI" -# # Copyright (c) 1999,2001 by Sun Microsystems, Inc. # All rights reserved. +# Copyright 2019 Joyent, Inc. # # lib/nsswitch/ldap/sparc/Makefile @@ -31,6 +29,6 @@ include ../Makefile.com LIBS = $(DYNLIB1) -include ../../Makefile.targ +include ../Makefile.targ install: all $(ROOTLIBS) diff --git a/usr/src/lib/nsswitch/ldap/sparcv9/Makefile b/usr/src/lib/nsswitch/ldap/sparcv9/Makefile index 938a17187a..c38b660d8b 100644 --- a/usr/src/lib/nsswitch/ldap/sparcv9/Makefile +++ b/usr/src/lib/nsswitch/ldap/sparcv9/Makefile @@ -19,11 +19,9 @@ # # CDDL HEADER END # -# -#ident "%Z%%M% %I% %E% SMI" -# # Copyright (c) 1999,2001 by Sun Microsystems, Inc. # All rights reserved. +# Copyright 2019 Joyent, Inc. # # lib/nsswitch/ldap/sparcv9/Makefile @@ -32,6 +30,6 @@ include $(SRC)/lib/Makefile.lib.64 LIBS = $(DYNLIB1) -include ../../Makefile.targ +include ../Makefile.targ install: all $(ROOT64DYNLIB) diff --git a/usr/src/lib/nsswitch/nis/Makefile.com b/usr/src/lib/nsswitch/nis/Makefile.com index cbccdfb764..8e87e78e0d 100644 --- a/usr/src/lib/nsswitch/nis/Makefile.com +++ b/usr/src/lib/nsswitch/nis/Makefile.com @@ -21,6 +21,7 @@ # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2019 Joyent, Inc. # LIBRARY = libnss_nis.a @@ -57,5 +58,9 @@ include ../../../Makefile.rootfs LINTFLAGS += -erroff=E_GLOBAL_COULD_BE_STATIC2 LINTFLAGS64 += -erroff=E_GLOBAL_COULD_BE_STATIC2 +pics/getgrent.o := CERRWARN += -_gcc=-Wno-switch +pics/getnetgrent.o := CERRWARN += -_gcc=-Wno-parentheses +pics/nis_common.o := CERRWARN += -_gcc=-Wno-uninitialized + LDLIBS += -lnsl DYNLIB1 = nss_nis.so$(VERS) |
