diff options
author | Baban Kenkre <Baban.Kenkre@Sun.COM> | 2008-11-07 12:09:53 -0800 |
---|---|---|
committer | Baban Kenkre <Baban.Kenkre@Sun.COM> | 2008-11-07 12:09:53 -0800 |
commit | 2b4a78020b9c38d1b95e2f3fefa6d6e4be382d1f (patch) | |
tree | b9f0bc817d950cefb1af4653dad8de547a17e061 /usr/src | |
parent | 0a2b1d27cac02f57e17b310f8baeb1dda082c83a (diff) | |
download | illumos-joyent-2b4a78020b9c38d1b95e2f3fefa6d6e4be382d1f.tar.gz |
PSARC/2008/441 Active Directory name service module (nss_ad)
6722476 name service switch module for AD (nss_ad) needed
Diffstat (limited to 'usr/src')
69 files changed, 5028 insertions, 1540 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint index 6cffcc986f..0b6a7705aa 100644 --- a/usr/src/Makefile.lint +++ b/usr/src/Makefile.lint @@ -325,6 +325,7 @@ COMMON_SUBDIRS = \ lib/cfgadm_plugins/sdcard \ lib/crypt_modules \ lib/extendedFILE \ + lib/libadutils \ lib/libadt_jni \ lib/libaio \ lib/libavl \ diff --git a/usr/src/cmd/getent/dogetgr.c b/usr/src/cmd/getent/dogetgr.c index fcd7b64f43..740a46846e 100644 --- a/usr/src/cmd/getent/dogetgr.c +++ b/usr/src/cmd/getent/dogetgr.c @@ -20,12 +20,10 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <grp.h> #include <stdlib.h> @@ -43,10 +41,10 @@ putgrent(const struct group *grp, FILE *fp) return (1); } - if (fprintf(fp, "%s:%s:%d:", - grp->gr_name != NULL ? grp->gr_name : "", - grp->gr_passwd != NULL ? grp->gr_passwd : "", - grp->gr_gid) == EOF) + if (fprintf(fp, "%s:%s:%u:", + grp->gr_name != NULL ? grp->gr_name : "", + grp->gr_passwd != NULL ? grp->gr_passwd : "", + grp->gr_gid) == EOF) rc = 1; mem = grp ->gr_mem; @@ -89,7 +87,7 @@ dogetgr(const char **list) * If the argument passed is not numeric, then * we take it as the group name and proceed. */ - gid = strtol(*list, &ptr, 10); + gid = strtoul(*list, &ptr, 10); if (!(*ptr == '\0' && errno == 0) || ((grp = getgrgid(gid)) == NULL)) { grp = getgrnam(*list); diff --git a/usr/src/cmd/getent/dogetpw.c b/usr/src/cmd/getent/dogetpw.c index 19257ad210..03a37bf134 100644 --- a/usr/src/cmd/getent/dogetpw.c +++ b/usr/src/cmd/getent/dogetpw.c @@ -19,10 +19,8 @@ * CDDL HEADER END */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -60,7 +58,7 @@ dogetpw(const char **list) * If the argument passed is not numeric, then * we take it as the user name and proceed. */ - uid = strtol(*list, &ptr, 10); + uid = strtoul(*list, &ptr, 10); if (!(*ptr == '\0' && errno == 0) || ((pwp = getpwuid(uid)) == NULL)) { pwp = getpwnam(*list); diff --git a/usr/src/cmd/idmap/idmapd/Makefile b/usr/src/cmd/idmap/idmapd/Makefile index a1af3b3852..bb724b95db 100644 --- a/usr/src/cmd/idmap/idmapd/Makefile +++ b/usr/src/cmd/idmap/idmapd/Makefile @@ -22,8 +22,6 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" -# PROG = idmapd MANIFEST = idmap.xml @@ -54,14 +52,15 @@ ROOTMANIFESTDIR = $(ROOTSVCSYSTEM) $(ROOTMANIFEST) := FILEMODE= 444 INCS += -I. -I../../../lib/libidmap/common -I$(IDMAP_PROT_DIR)\ - -I../../../lib/libsldap/common + -I../../../lib/libsldap/common\ + -I../../../lib/libadutils/common $(OBJS) := CPPFLAGS += $(INCS) -D_REENTRANT $(POFILE) := CPPFLAGS += $(INCS) CLOBBERFILES += $(IDMAP_PROT_H) CFLAGS += -v -LDLIBS += -lsecdb -lsocket -lnsl -lidmap -lscf -lsldap -lldap -luuid +LDLIBS += -lsecdb -lsocket -lnsl -lidmap -lscf -lsldap -lldap -luuid -ladutils $(PROG) := MAPFILES = $(MAPFILE.INT) $(MAPFILE.NGB) $(PROG) := LDFLAGS += $(MAPFILES:%=-M%) diff --git a/usr/src/cmd/idmap/idmapd/adutils.c b/usr/src/cmd/idmap/idmapd/adutils.c index d22ce169c9..f19706822e 100644 --- a/usr/src/cmd/idmap/idmapd/adutils.c +++ b/usr/src/cmd/idmap/idmapd/adutils.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Processes name2sid & sid2name batched lookups for a given user or * computer from an AD Directory server using GSSAPI authentication @@ -47,14 +45,12 @@ #include <errno.h> #include <assert.h> #include <limits.h> +#include <time.h> #include <sys/u8_textprep.h> +#include "libadutils.h" #include "nldaputils.h" #include "idmapd.h" -/* - * Internal data structures for this code - */ - /* Attribute names and filter format strings */ #define SAN "sAMAccountName" #define OBJSID "objectSid" @@ -62,58 +58,8 @@ #define SANFILTER "(sAMAccountName=%.*s)" #define OBJSIDFILTER "(objectSid=%s)" -/* - * This should really be in some <sys/sid.h> file or so; we have a - * private version of sid_t, and so must other components of ON until we - * rationalize this. - */ -typedef struct sid { - uchar_t version; - uchar_t sub_authority_count; - uint64_t authority; /* really, 48-bits */ - rid_t sub_authorities[SID_MAX_SUB_AUTHORITIES]; -} sid_t; - -/* A single DS */ -typedef struct ad_host { - struct ad_host *next; - ad_t *owner; /* ad_t to which this belongs */ - pthread_mutex_t lock; - LDAP *ld; /* LDAP connection */ - uint32_t ref; /* ref count */ - time_t idletime; /* time since last activity */ - int dead; /* error on LDAP connection */ - /* - * Used to distinguish between different instances of LDAP - * connections to this same DS. We need this so we never mix up - * results for a given msgID from one connection with those of - * another earlier connection where two batch state structures - * share this ad_host object but used different LDAP connections - * to send their LDAP searches. - */ - uint64_t generation; - - /* LDAP DS info */ - char *host; - int port; - - /* hardwired to SASL GSSAPI only for now */ - char *saslmech; - unsigned saslflags; - - /* Number of outstanding search requests */ - uint32_t max_requests; - uint32_t num_requests; -} ad_host_t; - -/* A set of DSs for a given AD partition; ad_t typedef comes from adutil.h */ -struct ad { - char *dflt_w2k_dom; /* used to qualify bare names */ - pthread_mutex_t lock; - uint32_t ref; - ad_host_t *last_adh; - idmap_ad_partition_t partition; /* Data or global catalog? */ -}; +void idmap_ldap_res_search_cb(LDAP *ld, LDAPMessage **res, int rc, + int qid, void *argp); /* * A place to put the results of a batched (async) query @@ -140,9 +86,9 @@ typedef struct idmap_q { char **attr; /* Attr for name mapping */ char **value; /* value for name mapping */ idmap_retcode *rc; + adutils_rc ad_rc; + adutils_result_t *result; - /* lookup state */ - int msgid; /* LDAP message ID */ /* * The LDAP search entry result is placed here to be processed * when the search done result is received. @@ -152,185 +98,21 @@ typedef struct idmap_q { /* Batch context structure; typedef is in header file */ struct idmap_query_state { - idmap_query_state_t *next; + adutils_query_state_t *qs; int qcount; /* how many queries */ - int ref_cnt; /* reference count */ - pthread_cond_t cv; /* Condition wait variable */ uint32_t qlastsent; - uint32_t qinflight; /* how many queries in flight */ - uint16_t qdead; /* oops, lost LDAP connection */ - ad_host_t *qadh; /* LDAP connection */ - uint64_t qadh_gen; /* same as qadh->generation */ const char *ad_unixuser_attr; const char *ad_unixgroup_attr; idmap_q_t queries[1]; /* array of query results */ }; -/* - * List of query state structs -- needed so we can "route" LDAP results - * to the right context if multiple threads should be using the same - * connection concurrently - */ -static idmap_query_state_t *qstatehead = NULL; -static pthread_mutex_t qstatelock = PTHREAD_MUTEX_INITIALIZER; - -/* - * List of DSs, needed by the idle connection reaper thread - */ -static ad_host_t *host_head = NULL; static pthread_t reaperid = 0; -static pthread_mutex_t adhostlock = PTHREAD_MUTEX_INITIALIZER; - - -static void -idmap_lookup_unlock_batch(idmap_query_state_t **state); - -static void -delete_ds(ad_t *ad, const char *host, int port); - -/*ARGSUSED*/ -static int -idmap_saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts) -{ - sasl_interact_t *interact; - - if (prompts == NULL || flags != LDAP_SASL_INTERACTIVE) - return (LDAP_PARAM_ERROR); - - /* There should be no extra arguemnts for SASL/GSSAPI authentication */ - for (interact = prompts; interact->id != SASL_CB_LIST_END; - interact++) { - interact->result = NULL; - interact->len = 0; - } - return (LDAP_SUCCESS); -} - - -/* - * Turn "dc=foo,dc=bar,dc=com" into "foo.bar.com"; ignores any other - * attributes (CN, etc...). We don't need the reverse, for now. - */ -static -char * -dn2dns(const char *dn) -{ - char **rdns = NULL; - char **attrs = NULL; - char **labels = NULL; - char *dns = NULL; - char **rdn, **attr, **label; - int maxlabels = 5; - int nlabels = 0; - int dnslen; - - /* - * There is no reverse of ldap_dns_to_dn() in our libldap, so we - * have to do the hard work here for now. - */ - - /* - * This code is much too liberal: it looks for "dc" attributes - * in all RDNs of the DN. In theory this could cause problems - * if people were to use "dc" in nodes other than the root of - * the tree, but in practice noone, least of all Active - * Directory, does that. - * - * On the other hand, this code is much too conservative: it - * does not make assumptions about ldap_explode_dn(), and _that_ - * is the true for looking at every attr of every RDN. - * - * Since we only ever look at dc and those must be DNS labels, - * at least until we get around to supporting IDN here we - * shouldn't see escaped labels from AD nor from libldap, though - * the spec (RFC2253) does allow libldap to escape things that - * don't need escaping -- if that should ever happen then - * libldap will need a spanking, and we can take care of that. - */ - - /* Explode a DN into RDNs */ - if ((rdns = ldap_explode_dn(dn, 0)) == NULL) - return (NULL); - - labels = calloc(maxlabels + 1, sizeof (char *)); - label = labels; - - for (rdn = rdns; *rdn != NULL; rdn++) { - if (attrs != NULL) - ldap_value_free(attrs); - - /* Explode each RDN, look for DC attr, save val as DNS label */ - if ((attrs = ldap_explode_rdn(rdn[0], 0)) == NULL) - goto done; - - for (attr = attrs; *attr != NULL; attr++) { - if (strncasecmp(*attr, "dc=", 3) != 0) - continue; - - /* Found a DNS label */ - labels[nlabels++] = strdup((*attr) + 3); - - if (nlabels == maxlabels) { - char **tmp; - tmp = realloc(labels, - sizeof (char *) * (maxlabels + 1)); - - if (tmp == NULL) - goto done; - - labels = tmp; - labels[nlabels] = NULL; - } - - /* There should be just one DC= attr per-RDN */ - break; - } - } - - /* - * Got all the labels, now join with '.' - * - * We need room for nlabels - 1 periods ('.'), one nul - * terminator, and the strlen() of each label. - */ - dnslen = nlabels; - for (label = labels; *label != NULL; label++) - dnslen += strlen(*label); - - if ((dns = malloc(dnslen)) == NULL) - goto done; - - *dns = '\0'; - - for (label = labels; *label != NULL; label++) { - (void) strlcat(dns, *label, dnslen); - /* - * NOTE: the last '.' won't be appended -- there's no room - * for it! - */ - (void) strlcat(dns, ".", dnslen); - } - -done: - if (labels != NULL) { - for (label = labels; *label != NULL; label++) - free(*label); - free(labels); - } - if (attrs != NULL) - ldap_value_free(attrs); - if (rdns != NULL) - ldap_value_free(rdns); - - return (dns); -} /* * Keep connection management simple for now, extend or replace later * with updated libsldap code. */ #define ADREAPERSLEEP 60 -#define ADCONN_TIME 300 /* * Idle connection reaping side of connection management @@ -343,8 +125,6 @@ static void adreaper(void *arg) { - ad_host_t *adh; - time_t now; timespec_t ts; ts.tv_sec = ADREAPERSLEEP; @@ -356,270 +136,8 @@ adreaper(void *arg) * portable than usleep(3C) */ (void) nanosleep(&ts, NULL); - (void) pthread_mutex_lock(&adhostlock); - now = time(NULL); - for (adh = host_head; adh != NULL; adh = adh->next) { - (void) pthread_mutex_lock(&adh->lock); - if (adh->ref == 0 && adh->idletime != 0 && - adh->idletime + ADCONN_TIME < now) { - if (adh->ld) { - (void) ldap_unbind(adh->ld); - adh->ld = NULL; - adh->idletime = 0; - adh->ref = 0; - } - } - (void) pthread_mutex_unlock(&adh->lock); - } - (void) pthread_mutex_unlock(&adhostlock); - } -} - -int -idmap_ad_alloc(ad_t **new_ad, const char *default_domain, - idmap_ad_partition_t part) -{ - ad_t *ad; - - *new_ad = NULL; - - if ((default_domain == NULL || *default_domain == '\0') && - part != IDMAP_AD_GLOBAL_CATALOG) - return (-1); - - if ((ad = calloc(1, sizeof (ad_t))) == NULL) - return (-1); - - ad->ref = 1; - ad->partition = part; - - if (default_domain == NULL) - default_domain = ""; - - if ((ad->dflt_w2k_dom = strdup(default_domain)) == NULL) - goto err; - - if (pthread_mutex_init(&ad->lock, NULL) != 0) - goto err; - - *new_ad = ad; - - return (0); -err: - if (ad->dflt_w2k_dom != NULL) - free(ad->dflt_w2k_dom); - free(ad); - return (-1); -} - - -void -idmap_ad_free(ad_t **ad) -{ - ad_host_t *p; - ad_host_t *prev; - - if (ad == NULL || *ad == NULL) - return; - - (void) pthread_mutex_lock(&(*ad)->lock); - - if (atomic_dec_32_nv(&(*ad)->ref) > 0) { - (void) pthread_mutex_unlock(&(*ad)->lock); - *ad = NULL; - return; - } - - (void) pthread_mutex_lock(&adhostlock); - prev = NULL; - p = host_head; - while (p != NULL) { - if (p->owner != (*ad)) { - prev = p; - p = p->next; - continue; - } else { - delete_ds((*ad), p->host, p->port); - if (prev == NULL) - p = host_head; - else - p = prev->next; - } - } - (void) pthread_mutex_unlock(&adhostlock); - - (void) pthread_mutex_unlock(&(*ad)->lock); - (void) pthread_mutex_destroy(&(*ad)->lock); - - free((*ad)->dflt_w2k_dom); - free(*ad); - - *ad = NULL; -} - - -static -int -idmap_open_conn(ad_host_t *adh, int timeoutsecs) -{ - int zero = 0; - int ldversion, rc; - int timeoutms = timeoutsecs * 1000; - - if (adh == NULL) - return (0); - - (void) pthread_mutex_lock(&adh->lock); - - if (!adh->dead && adh->ld != NULL) - /* done! */ - goto out; - - if (adh->ld != NULL) { - (void) ldap_unbind(adh->ld); - adh->ld = NULL; - } - adh->num_requests = 0; - - atomic_inc_64(&adh->generation); - - /* Open and bind an LDAP connection */ - adh->ld = ldap_init(adh->host, adh->port); - if (adh->ld == NULL) { - idmapdlog(LOG_INFO, "ldap_init() to server " - "%s port %d failed. (%s)", adh->host, - adh->port, strerror(errno)); - goto out; - } - ldversion = LDAP_VERSION3; - (void) ldap_set_option(adh->ld, LDAP_OPT_PROTOCOL_VERSION, &ldversion); - (void) ldap_set_option(adh->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); - (void) ldap_set_option(adh->ld, LDAP_OPT_TIMELIMIT, &zero); - (void) ldap_set_option(adh->ld, LDAP_OPT_SIZELIMIT, &zero); - (void) ldap_set_option(adh->ld, LDAP_X_OPT_CONNECT_TIMEOUT, &timeoutms); - (void) ldap_set_option(adh->ld, LDAP_OPT_RESTART, LDAP_OPT_ON); - rc = ldap_sasl_interactive_bind_s(adh->ld, "" /* binddn */, - adh->saslmech, NULL, NULL, adh->saslflags, &idmap_saslcallback, - NULL); - - if (rc != LDAP_SUCCESS) { - (void) ldap_unbind(adh->ld); - adh->ld = NULL; - idmapdlog(LOG_INFO, "ldap_sasl_interactive_bind_s() to server " - "%s port %d failed. (%s)", adh->host, adh->port, - ldap_err2string(rc)); + adutils_reap_idle_connections(); } - - idmapdlog(LOG_DEBUG, "Using global catalog server %s:%d", - adh->host, adh->port); - -out: - if (adh->ld != NULL) { - atomic_inc_32(&adh->ref); - adh->idletime = time(NULL); - adh->dead = 0; - (void) pthread_mutex_unlock(&adh->lock); - return (1); - } - - (void) pthread_mutex_unlock(&adh->lock); - return (0); -} - - -/* - * Connection management: find an open connection or open one - */ -static -ad_host_t * -idmap_get_conn(ad_t *ad) -{ - ad_host_t *adh = NULL; - int tries; - int dscount = 0; - int timeoutsecs = IDMAPD_LDAP_OPEN_TIMEOUT; - -retry: - (void) pthread_mutex_lock(&adhostlock); - - if (host_head == NULL) { - (void) pthread_mutex_unlock(&adhostlock); - goto out; - } - - if (dscount == 0) { - /* - * First try: count the number of DSes. - * - * Integer overflow is not an issue -- we can't have so many - * DSes because they won't fit even DNS over TCP, and SMF - * shouldn't let you set so many. - */ - for (adh = host_head, tries = 0; adh != NULL; adh = adh->next) { - if (adh->owner == ad) - dscount++; - } - - if (dscount == 0) { - (void) pthread_mutex_unlock(&adhostlock); - goto out; - } - - tries = dscount * 3; /* three tries per-ds */ - - /* - * Begin round-robin at the next DS in the list after the last - * one that we had a connection to, else start with the first - * DS in the list. - */ - adh = ad->last_adh; - } - - /* - * Round-robin -- pick the next one on the list; if the list - * changes on us, no big deal, we'll just potentially go - * around the wrong number of times. - */ - for (;;) { - if (adh != NULL && adh->ld != NULL && !adh->dead) - break; - if (adh == NULL || (adh = adh->next) == NULL) - adh = host_head; - if (adh->owner == ad) - break; - } - - ad->last_adh = adh; - (void) pthread_mutex_unlock(&adhostlock); - - - /* Found suitable DS, open it if not already opened */ - if (idmap_open_conn(adh, timeoutsecs)) - return (adh); - - tries--; - - if ((tries % dscount) == 0) - timeoutsecs *= 2; - - if (tries > 0) - goto retry; - -out: - idmapdlog(LOG_NOTICE, "Couldn't open an LDAP connection to any global " - "catalog server!"); - - return (NULL); -} - -static -void -idmap_release_conn(ad_host_t *adh) -{ - (void) pthread_mutex_lock(&adh->lock); - if (atomic_dec_32_nv(&adh->ref) == 0) - adh->idletime = time(NULL); - (void) pthread_mutex_unlock(&adh->lock); } /* @@ -628,415 +146,69 @@ idmap_release_conn(ad_host_t *adh) */ int -idmap_add_ds(ad_t *ad, const char *host, int port) +idmap_add_ds(adutils_ad_t *ad, const char *host, int port) { - ad_host_t *p; - ad_host_t *new = NULL; - int ret = -1; - - if (port == 0) - port = (int)ad->partition; - - (void) pthread_mutex_lock(&adhostlock); - for (p = host_head; p != NULL; p = p->next) { - if (p->owner != ad) - continue; - - if (strcmp(host, p->host) == 0 && p->port == port) { - /* already added */ - ret = 0; - goto err; - } - } - - /* add new entry */ - new = (ad_host_t *)calloc(1, sizeof (ad_host_t)); - if (new == NULL) - goto err; - new->owner = ad; - new->port = port; - new->dead = 0; - new->max_requests = 80; - new->num_requests = 0; - if ((new->host = strdup(host)) == NULL) - goto err; - - /* default to SASL GSSAPI only for now */ - new->saslflags = LDAP_SASL_INTERACTIVE; - new->saslmech = "GSSAPI"; - - if ((ret = pthread_mutex_init(&new->lock, NULL)) != 0) { - free(new->host); - new->host = NULL; - errno = ret; - ret = -1; - goto err; - } + int ret = -1; - /* link in */ - new->next = host_head; - host_head = new; + if (adutils_add_ds(ad, host, port) == ADUTILS_SUCCESS) + ret = 0; /* Start reaper if it doesn't exist */ - if (reaperid == 0) + if (ret == 0 && reaperid == 0) (void) pthread_create(&reaperid, NULL, (void *(*)(void *))adreaper, (void *)NULL); - -err: - (void) pthread_mutex_unlock(&adhostlock); - - if (ret != 0 && new != NULL) { - if (new->host != NULL) { - (void) pthread_mutex_destroy(&new->lock); - free(new->host); - } - free(new); - } - return (ret); } -/* - * Free a DS configuration. - * Caller must lock the adhostlock mutex - */ -static void -delete_ds(ad_t *ad, const char *host, int port) -{ - ad_host_t **p, *q; - - for (p = &host_head; *p != NULL; p = &((*p)->next)) { - if ((*p)->owner != ad || strcmp(host, (*p)->host) != 0 || - (*p)->port != port) - continue; - /* found */ - if ((*p)->ref > 0) - break; /* still in use */ - - q = *p; - *p = (*p)->next; - - (void) pthread_mutex_destroy(&q->lock); - - if (q->ld) - (void) ldap_unbind(q->ld); - if (q->host) - free(q->host); - free(q); - break; - } - -} - - -/* - * Convert a binary SID in a BerValue to a sid_t - */ -static -int -idmap_getsid(BerValue *bval, sid_t *sidp) -{ - int i, j; - uchar_t *v; - uint32_t a; - - /* - * The binary format of a SID is as follows: - * - * byte #0: version, always 0x01 - * byte #1: RID count, always <= 0x0f - * bytes #2-#7: SID authority, big-endian 48-bit unsigned int - * - * followed by RID count RIDs, each a little-endian, unsigned - * 32-bit int. - */ - /* - * Sanity checks: must have at least one RID, version must be - * 0x01, and the length must be 8 + rid count * 4 - */ - if (bval->bv_len > 8 && bval->bv_val[0] == 0x01 && - bval->bv_len == 1 + 1 + 6 + bval->bv_val[1] * 4) { - v = (uchar_t *)bval->bv_val; - sidp->version = v[0]; - sidp->sub_authority_count = v[1]; - sidp->authority = - /* big endian -- so start from the left */ - ((u_longlong_t)v[2] << 40) | - ((u_longlong_t)v[3] << 32) | - ((u_longlong_t)v[4] << 24) | - ((u_longlong_t)v[5] << 16) | - ((u_longlong_t)v[6] << 8) | - (u_longlong_t)v[7]; - for (i = 0; i < sidp->sub_authority_count; i++) { - j = 8 + (i * 4); - /* little endian -- so start from the right */ - a = (v[j + 3] << 24) | (v[j + 2] << 16) | - (v[j + 1] << 8) | (v[j]); - sidp->sub_authorities[i] = a; - } - return (0); - } - return (-1); -} - -/* - * Convert a sid_t to S-1-... - */ -static -char * -idmap_sid2txt(sid_t *sidp) -{ - int rlen, i, len; - char *str, *cp; - - if (sidp->version != 1) - return (NULL); - - len = sizeof ("S-1-") - 1; - - /* - * We could optimize like so, but, why? - * if (sidp->authority < 10) - * len += 2; - * else if (sidp->authority < 100) - * len += 3; - * else - * len += snprintf(NULL, 0"%llu", sidp->authority); - */ - len += snprintf(NULL, 0, "%llu", sidp->authority); - - /* Max length of a uint32_t printed out in ASCII is 10 bytes */ - len += 1 + (sidp->sub_authority_count + 1) * 10; - - if ((cp = str = malloc(len)) == NULL) - return (NULL); - - rlen = snprintf(str, len, "S-1-%llu", sidp->authority); - - cp += rlen; - len -= rlen; - - for (i = 0; i < sidp->sub_authority_count; i++) { - assert(len > 0); - rlen = snprintf(cp, len, "-%u", sidp->sub_authorities[i]); - cp += rlen; - len -= rlen; - assert(len >= 0); - } - - return (str); -} - -/* - * Convert a sid_t to on-the-wire encoding - */ static -int -idmap_sid2binsid(sid_t *sid, uchar_t *binsid, int binsidlen) -{ - uchar_t *p; - int i; - uint64_t a; - uint32_t r; - - if (sid->version != 1 || - binsidlen != (1 + 1 + 6 + sid->sub_authority_count * 4)) - return (-1); - - p = binsid; - *p++ = 0x01; /* version */ - /* sub authority count */ - *p++ = sid->sub_authority_count; - /* Authority */ - a = sid->authority; - /* big-endian -- start from left */ - *p++ = (a >> 40) & 0xFF; - *p++ = (a >> 32) & 0xFF; - *p++ = (a >> 24) & 0xFF; - *p++ = (a >> 16) & 0xFF; - *p++ = (a >> 8) & 0xFF; - *p++ = a & 0xFF; - - /* sub-authorities */ - for (i = 0; i < sid->sub_authority_count; i++) { - r = sid->sub_authorities[i]; - /* little-endian -- start from right */ - *p++ = (r & 0x000000FF); - *p++ = (r & 0x0000FF00) >> 8; - *p++ = (r & 0x00FF0000) >> 16; - *p++ = (r & 0xFF000000) >> 24; - } - - return (0); -} - -/* - * Convert a stringified SID (S-1-...) into a hex-encoded version of the - * on-the-wire encoding, but with each pair of hex digits pre-pended - * with a '\', so we can pass this to libldap. - */ -static -int -idmap_txtsid2hexbinsid(const char *txt, const rid_t *rid, - char *hexbinsid, int hexbinsidlen) +idmap_retcode +map_adrc2idmaprc(adutils_rc adrc) { - sid_t sid = { 0 }; - int i, j; - const char *cp; - char *ecp; - u_longlong_t a; - unsigned long r; - uchar_t *binsid, b, hb; - - /* Only version 1 SIDs please */ - if (strncmp(txt, "S-1-", strlen("S-1-")) != 0) - return (-1); - - if (strlen(txt) < (strlen("S-1-") + 1)) - return (-1); - - /* count '-'s */ - for (j = 0, cp = strchr(txt, '-'); - cp != NULL && *cp != '\0'; - j++, cp = strchr(cp + 1, '-')) { - /* can't end on a '-' */ - if (*(cp + 1) == '\0') - return (-1); - } - - /* Adjust count for version and authority */ - j -= 2; - - /* we know the version number and RID count */ - sid.version = 1; - sid.sub_authority_count = (rid != NULL) ? j + 1 : j; - - /* must have at least one RID, but not too many */ - if (sid.sub_authority_count < 1 || - sid.sub_authority_count > SID_MAX_SUB_AUTHORITIES) - return (-1); - - /* check that we only have digits and '-' */ - if (strspn(txt + 1, "0123456789-") < (strlen(txt) - 1)) - return (-1); - - cp = txt + strlen("S-1-"); - - /* 64-bit safe parsing of unsigned 48-bit authority value */ - errno = 0; - a = strtoull(cp, &ecp, 10); - - /* errors parsing the authority or too many bits */ - if (cp == ecp || (a == 0 && errno == EINVAL) || - (a == ULLONG_MAX && errno == ERANGE) || - (a & 0x0000ffffffffffffULL) != a) - return (-1); - - cp = ecp; - - sid.authority = (uint64_t)a; - - for (i = 0; i < j; i++) { - if (*cp++ != '-') - return (-1); - /* 64-bit safe parsing of unsigned 32-bit RID */ - errno = 0; - r = strtoul(cp, &ecp, 10); - /* errors parsing the RID or too many bits */ - if (cp == ecp || (r == 0 && errno == EINVAL) || - (r == ULONG_MAX && errno == ERANGE) || - (r & 0xffffffffUL) != r) - return (-1); - sid.sub_authorities[i] = (uint32_t)r; - cp = ecp; - } - - /* check that all of the string SID has been consumed */ - if (*cp != '\0') - return (-1); - - if (rid != NULL) - sid.sub_authorities[j] = *rid; - - j = 1 + 1 + 6 + sid.sub_authority_count * 4; - - if (hexbinsidlen < (j * 3)) - return (-2); - - /* binary encode the SID */ - binsid = (uchar_t *)alloca(j); - (void) idmap_sid2binsid(&sid, binsid, j); - - /* hex encode, with a backslash before each byte */ - for (ecp = hexbinsid, i = 0; i < j; i++) { - b = binsid[i]; - *ecp++ = '\\'; - hb = (b >> 4) & 0xF; - *ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A'); - hb = b & 0xF; - *ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A'); + switch (adrc) { + case ADUTILS_SUCCESS: + return (IDMAP_SUCCESS); + case ADUTILS_ERR_NOTFOUND: + return (IDMAP_ERR_NOTFOUND); + case ADUTILS_ERR_MEMORY: + return (IDMAP_ERR_MEMORY); + case ADUTILS_ERR_DOMAIN: + return (IDMAP_ERR_DOMAIN); + case ADUTILS_ERR_OTHER: + return (IDMAP_ERR_OTHER); + case ADUTILS_ERR_RETRIABLE_NET_ERR: + return (IDMAP_ERR_RETRIABLE_NET_ERR); + default: + return (IDMAP_ERR_INTERNAL); } - *ecp = '\0'; - - return (0); + /* NOTREACHED */ } -static -char * -convert_bval2sid(BerValue *bval, rid_t *rid) -{ - sid_t sid; - - if (idmap_getsid(bval, &sid) < 0) - return (NULL); - - /* - * If desired and if the SID is what should be a domain/computer - * user or group SID (i.e., S-1-5-w-x-y-z-<user/group RID>) then - * save the last RID and truncate the SID - */ - if (rid != NULL && sid.authority == 5 && sid.sub_authority_count == 5) - *rid = sid.sub_authorities[--sid.sub_authority_count]; - return (idmap_sid2txt(&sid)); -} - - idmap_retcode -idmap_lookup_batch_start(ad_t *ad, int nqueries, idmap_query_state_t **state) +idmap_lookup_batch_start(adutils_ad_t *ad, int nqueries, + idmap_query_state_t **state) { - idmap_query_state_t *new_state; - ad_host_t *adh = NULL; + idmap_query_state_t *new_state; + adutils_rc rc; *state = NULL; if (ad == NULL) return (IDMAP_ERR_INTERNAL); - adh = idmap_get_conn(ad); - if (adh == NULL) - return (IDMAP_ERR_RETRIABLE_NET_ERR); - new_state = calloc(1, sizeof (idmap_query_state_t) + (nqueries - 1) * sizeof (idmap_q_t)); - if (new_state == NULL) return (IDMAP_ERR_MEMORY); - new_state->ref_cnt = 1; - new_state->qadh = adh; - new_state->qcount = nqueries; - new_state->qadh_gen = adh->generation; - /* should be -1, but the atomic routines want unsigned */ - new_state->qlastsent = 0; - (void) pthread_cond_init(&new_state->cv, NULL); - - (void) pthread_mutex_lock(&qstatelock); - new_state->next = qstatehead; - qstatehead = new_state; - (void) pthread_mutex_unlock(&qstatelock); + if ((rc = adutils_lookup_batch_start(ad, nqueries, + idmap_ldap_res_search_cb, new_state, &new_state->qs)) + != ADUTILS_SUCCESS) { + free(new_state); + return (map_adrc2idmaprc(rc)); + } + new_state->qcount = nqueries; *state = new_state; - return (IDMAP_SUCCESS); } @@ -1052,83 +224,6 @@ idmap_lookup_batch_set_unixattr(idmap_query_state_t *state, } /* - * Find the idmap_query_state_t to which a given LDAP result msgid on a - * given connection belongs. This routine increaments the reference count - * so that the object can not be freed. idmap_lookup_unlock_batch() - * must be called to decreament the reference count. - */ -static -int -idmap_msgid2query(ad_host_t *adh, int msgid, - idmap_query_state_t **state, int *qid) -{ - idmap_query_state_t *p; - int i; - int ret; - - (void) pthread_mutex_lock(&qstatelock); - for (p = qstatehead; p != NULL; p = p->next) { - if (p->qadh != adh || adh->generation != p->qadh_gen) - continue; - for (i = 0; i < p->qcount; i++) { - if ((p->queries[i]).msgid == msgid) { - if (!p->qdead) { - p->ref_cnt++; - *state = p; - *qid = i; - ret = 1; - } else - ret = 0; - (void) pthread_mutex_unlock(&qstatelock); - return (ret); - } - } - } - (void) pthread_mutex_unlock(&qstatelock); - return (0); -} - -/* - * Put the the search result onto the correct idmap_q_t given the LDAP result - * msgid - * Returns: 0 success - * -1 already has a search result - * -2 cant find message id - */ -static -int -idmap_quesearchresbymsgid(ad_host_t *adh, int msgid, LDAPMessage *search_res) -{ - idmap_query_state_t *p; - int i; - int res; - - (void) pthread_mutex_lock(&qstatelock); - for (p = qstatehead; p != NULL; p = p->next) { - if (p->qadh != adh || adh->generation != p->qadh_gen) - continue; - for (i = 0; i < p->qcount; i++) { - if ((p->queries[i]).msgid == msgid) { - if (p->queries[i].search_res == NULL) { - if (!p->qdead) { - p->queries[i].search_res = - search_res; - res = 0; - } else - res = -2; - } else - res = -1; - (void) pthread_mutex_unlock(&qstatelock); - return (res); - } - } - } - (void) pthread_mutex_unlock(&qstatelock); - return (-2); -} - - -/* * Take parsed attribute values from a search result entry and check if * it is the result that was desired and, if so, set the result fields * of the given idmap_q_t. @@ -1145,7 +240,7 @@ idmap_setqresults(idmap_q_t *q, char *san, char *dn, const char *attr, assert(dn != NULL); - if ((domain = dn2dns(dn)) == NULL) + if ((domain = adutils_dn2dns(dn)) == NULL) goto out; if (q->ecanonname != NULL && san != NULL) { @@ -1201,8 +296,7 @@ idmap_setqresults(idmap_q_t *q, char *san, char *dn, const char *attr, san = NULL; } - /* Always have q->rc; idmap_extract_object() asserts this */ - *q->rc = IDMAP_SUCCESS; + q->ad_rc = ADUTILS_SUCCESS; out: /* Free unused attribute values */ @@ -1212,56 +306,6 @@ out: free(unixname); } -/* - * The following three functions extract objectSid, sAMAccountName and - * objectClass attribute values and, in the case of objectSid and - * objectClass, parse them. - * - * idmap_setqresults() takes care of dealing with the result entry's DN. - */ - -/* - * Return a NUL-terminated stringified SID from the value of an - * objectSid attribute and put the last RID in *rid. - */ -static -char * -idmap_bv_objsid2sidstr(BerValue **bvalues, rid_t *rid) -{ - char *sid; - - if (bvalues == NULL) - return (NULL); - /* objectSid is single valued */ - if ((sid = convert_bval2sid(bvalues[0], rid)) == NULL) - return (NULL); - return (sid); -} - -/* - * Return a NUL-terminated string from the value of a sAMAccountName - * or unixname attribute. - */ -static -char * -idmap_bv_name2str(BerValue **bvalues) -{ - char *s; - - if (bvalues == NULL || bvalues[0] == NULL || - bvalues[0]->bv_val == NULL) - return (NULL); - - if ((s = malloc(bvalues[0]->bv_len + 1)) == NULL) - return (NULL); - - (void) snprintf(s, bvalues[0]->bv_len + 1, "%.*s", bvalues[0]->bv_len, - bvalues[0]->bv_val); - - return (s); -} - - #define BVAL_CASEEQ(bv, str) \ (((*(bv))->bv_len == (sizeof (str) - 1)) && \ strncasecmp((*(bv))->bv_val, str, (*(bv))->bv_len) == 0) @@ -1309,12 +353,11 @@ idmap_bv_objclass2sidtype(BerValue **bvalues, int *sid_type) */ static void -idmap_extract_object(idmap_query_state_t *state, int qid, LDAPMessage *res) +idmap_extract_object(idmap_query_state_t *state, idmap_q_t *q, + LDAPMessage *res, LDAP *ld) { BerElement *ber = NULL; BerValue **bvalues; - ad_host_t *adh; - idmap_q_t *q; char *attr; const char *unixuser_attr = NULL; const char *unixgroup_attr = NULL; @@ -1327,24 +370,11 @@ idmap_extract_object(idmap_query_state_t *state, int qid, LDAPMessage *res) int sid_type = _IDMAP_T_UNDEF; int has_class, has_san, has_sid; int has_unixuser, has_unixgroup; - int num; - - adh = state->qadh; - - (void) pthread_mutex_lock(&adh->lock); - - q = &(state->queries[qid]); assert(q->rc != NULL); - if (adh->dead || (dn = ldap_get_dn(adh->ld, res)) == NULL) { - num = adh->num_requests; - (void) pthread_mutex_unlock(&adh->lock); - idmapdlog(LOG_DEBUG, - "AD error decoding search result - %d queued requests", - num); + if ((dn = ldap_get_dn(ld, res)) == NULL) return; - } assert(q->domain == NULL || *q->domain == NULL); @@ -1384,8 +414,8 @@ idmap_extract_object(idmap_query_state_t *state, int qid, LDAPMessage *res) } has_class = has_san = has_sid = has_unixuser = has_unixgroup = 0; - for (attr = ldap_first_attribute(adh->ld, res, &ber); attr != NULL; - attr = ldap_next_attribute(adh->ld, res, ber)) { + for (attr = ldap_first_attribute(ld, res, &ber); attr != NULL; + attr = ldap_next_attribute(ld, res, ber)) { bvalues = NULL; /* for memory management below */ /* @@ -1394,15 +424,20 @@ idmap_extract_object(idmap_query_state_t *state, int qid, LDAPMessage *res) */ if (q->sid != NULL && !has_sid && strcasecmp(attr, OBJSID) == 0) { - bvalues = ldap_get_values_len(adh->ld, res, attr); - sid = idmap_bv_objsid2sidstr(bvalues, &rid); - has_sid = (sid != NULL); + bvalues = ldap_get_values_len(ld, res, attr); + if (bvalues != NULL) { + sid = adutils_bv_objsid2sidstr( + bvalues[0], &rid); + has_sid = (sid != NULL); + } } else if (!has_san && strcasecmp(attr, SAN) == 0) { - bvalues = ldap_get_values_len(adh->ld, res, attr); - san = idmap_bv_name2str(bvalues); - has_san = (san != NULL); + bvalues = ldap_get_values_len(ld, res, attr); + if (bvalues != NULL) { + san = adutils_bv_name2str(bvalues[0]); + has_san = (san != NULL); + } } else if (!has_class && strcasecmp(attr, OBJCLASS) == 0) { - bvalues = ldap_get_values_len(adh->ld, res, attr); + bvalues = ldap_get_values_len(ld, res, attr); has_class = idmap_bv_objclass2sidtype(bvalues, &sid_type); if (has_class && q->unixname != NULL && @@ -1431,15 +466,19 @@ idmap_extract_object(idmap_query_state_t *state, int qid, LDAPMessage *res) } } else if (!has_unixuser && unixuser_attr != NULL && strcasecmp(attr, unixuser_attr) == 0) { - bvalues = ldap_get_values_len(adh->ld, res, attr); - unixuser = idmap_bv_name2str(bvalues); - has_unixuser = (unixuser != NULL); + bvalues = ldap_get_values_len(ld, res, attr); + if (bvalues != NULL) { + unixuser = adutils_bv_name2str(bvalues[0]); + has_unixuser = (unixuser != NULL); + } } else if (!has_unixgroup && unixgroup_attr != NULL && strcasecmp(attr, unixgroup_attr) == 0) { - bvalues = ldap_get_values_len(adh->ld, res, attr); - unixgroup = idmap_bv_name2str(bvalues); - has_unixgroup = (unixgroup != NULL); + bvalues = ldap_get_values_len(ld, res, attr); + if (bvalues != NULL) { + unixgroup = adutils_bv_name2str(bvalues[0]); + has_unixgroup = (unixgroup != NULL); + } } if (bvalues != NULL) @@ -1455,8 +494,6 @@ idmap_extract_object(idmap_query_state_t *state, int qid, LDAPMessage *res) } } - (void) pthread_mutex_unlock(&adh->lock); - if (!has_class) { /* * Didn't find objectclass. Something's wrong with our @@ -1485,140 +522,31 @@ idmap_extract_object(idmap_query_state_t *state, int qid, LDAPMessage *res) ldap_memfree(dn); } -/* - * Try to get a result; if there is one, find the corresponding - * idmap_q_t and process the result. - * - * Returns: 0 success - * -1 error - * -2 queue empty - */ -static -int -idmap_get_adobject_batch(ad_host_t *adh, struct timeval *timeout) +void +idmap_ldap_res_search_cb(LDAP *ld, LDAPMessage **res, int rc, int qid, + void *argp) { - idmap_query_state_t *query_state; - LDAPMessage *res = NULL; - int rc, ret, msgid, qid; - idmap_q_t *que; - int num; - - (void) pthread_mutex_lock(&adh->lock); - if (adh->dead || adh->num_requests == 0) { - if (adh->dead) - ret = -1; - else - ret = -2; - (void) pthread_mutex_unlock(&adh->lock); - return (ret); - } + idmap_query_state_t *state = (idmap_query_state_t *)argp; + idmap_q_t *q = &(state->queries[qid]); - /* Get one result */ - rc = ldap_result(adh->ld, LDAP_RES_ANY, 0, - timeout, &res); - if ((timeout != NULL && timeout->tv_sec > 0 && rc == LDAP_SUCCESS) || - rc < 0) - adh->dead = 1; - - if (rc == LDAP_RES_SEARCH_RESULT && adh->num_requests > 0) - adh->num_requests--; - - if (adh->dead) { - num = adh->num_requests; - (void) pthread_mutex_unlock(&adh->lock); - idmapdlog(LOG_DEBUG, - "AD ldap_result error - %d queued requests", num); - return (-1); - } switch (rc) { case LDAP_RES_SEARCH_RESULT: - /* We should have the LDAP replies for some search... */ - msgid = ldap_msgid(res); - if (idmap_msgid2query(adh, msgid, &query_state, &qid)) { - (void) pthread_mutex_unlock(&adh->lock); - que = &(query_state->queries[qid]); - if (que->search_res != NULL) { - idmap_extract_object(query_state, qid, - que->search_res); - (void) ldap_msgfree(que->search_res); - que->search_res = NULL; - } else - *que->rc = IDMAP_ERR_NOTFOUND; - /* ...so we can decrement qinflight */ - atomic_dec_32(&query_state->qinflight); - idmap_lookup_unlock_batch(&query_state); - } else { - num = adh->num_requests; - (void) pthread_mutex_unlock(&adh->lock); - idmapdlog(LOG_DEBUG, - "AD cannot find message ID - %d queued requests", - num); - } - (void) ldap_msgfree(res); - ret = 0; + if (q->search_res != NULL) { + idmap_extract_object(state, q, q->search_res, ld); + (void) ldap_msgfree(q->search_res); + q->search_res = NULL; + } else + q->ad_rc = ADUTILS_ERR_NOTFOUND; break; - - case LDAP_RES_SEARCH_REFERENCE: - /* - * We have no need for these at the moment. Eventually, - * when we query things that we can't expect to find in - * the Global Catalog then we'll need to learn to follow - * references. - */ - (void) pthread_mutex_unlock(&adh->lock); - (void) ldap_msgfree(res); - ret = 0; - break; - case LDAP_RES_SEARCH_ENTRY: - /* Got a result - queue it */ - msgid = ldap_msgid(res); - rc = idmap_quesearchresbymsgid(adh, msgid, res); - num = adh->num_requests; - (void) pthread_mutex_unlock(&adh->lock); - if (rc == -1) { - idmapdlog(LOG_DEBUG, - "AD already has search result - %d queued requests", - num); - (void) ldap_msgfree(res); - } else if (rc == -2) { - idmapdlog(LOG_DEBUG, - "AD cannot queue by message ID " - "- %d queued requests", num); - (void) ldap_msgfree(res); + if (q->search_res == NULL) { + q->search_res = *res; + *res = NULL; } - ret = 0; break; - default: - /* timeout or error; treat the same */ - (void) pthread_mutex_unlock(&adh->lock); - ret = -1; break; } - - return (ret); -} - -/* - * This routine decreament the reference count of the - * idmap_query_state_t - */ -static void -idmap_lookup_unlock_batch(idmap_query_state_t **state) -{ - /* - * Decrement reference count with qstatelock locked - */ - (void) pthread_mutex_lock(&qstatelock); - (*state)->ref_cnt--; - /* - * If there are no references wakup the allocating thread - */ - if ((*state)->ref_cnt <= 1) - (void) pthread_cond_signal(&(*state)->cv); - (void) pthread_mutex_unlock(&qstatelock); - *state = NULL; } static @@ -1639,96 +567,38 @@ idmap_cleanup_batch(idmap_query_state_t *batch) /* * This routine frees the idmap_query_state_t structure - * If the reference count is greater than 1 it waits - * for the other threads to finish using it. */ void idmap_lookup_release_batch(idmap_query_state_t **state) { - idmap_query_state_t **p; - - /* - * Set state to dead to stop further operations. - * Wait for reference count with qstatelock locked - * to get to one. - */ - (void) pthread_mutex_lock(&qstatelock); - (*state)->qdead = 1; - while ((*state)->ref_cnt > 1) { - (void) pthread_cond_wait(&(*state)->cv, &qstatelock); - } - - /* Remove this state struct from the list of state structs */ - for (p = &qstatehead; *p != NULL; p = &(*p)->next) { - if (*p == (*state)) { - *p = (*state)->next; - break; - } - } - (void) pthread_mutex_unlock(&qstatelock); - + if (state == NULL || *state == NULL) + return; + adutils_lookup_batch_release(&(*state)->qs); idmap_cleanup_batch(*state); - - (void) pthread_cond_destroy(&(*state)->cv); - - idmap_release_conn((*state)->qadh); - free(*state); *state = NULL; } - -/* - * This routine waits for other threads using the - * idmap_query_state_t structure to finish. - * If the reference count is greater than 1 it waits - * for the other threads to finish using it. - */ -static -void -idmap_lookup_wait_batch(idmap_query_state_t *state) -{ - /* - * Set state to dead to stop further operation. - * stating. - * Wait for reference count to get to one - * with qstatelock locked. - */ - (void) pthread_mutex_lock(&qstatelock); - state->qdead = 1; - while (state->ref_cnt > 1) { - (void) pthread_cond_wait(&state->cv, &qstatelock); - } - (void) pthread_mutex_unlock(&qstatelock); -} - - idmap_retcode idmap_lookup_batch_end(idmap_query_state_t **state) { - int rc = LDAP_SUCCESS; - idmap_retcode retcode = IDMAP_SUCCESS; - struct timeval timeout; + adutils_rc ad_rc; + int i; + idmap_query_state_t *id_qs = *state; - timeout.tv_sec = IDMAPD_SEARCH_TIMEOUT; - timeout.tv_usec = 0; + ad_rc = adutils_lookup_batch_end(&id_qs->qs); - /* Process results until done or until timeout, if given */ - while ((*state)->qinflight > 0) { - if ((rc = idmap_get_adobject_batch((*state)->qadh, - &timeout)) != 0) - break; + /* + * Map adutils rc to idmap_retcode in each + * query because consumers in dbutils.c + * expects idmap_retcode. + */ + for (i = 0; i < id_qs->qcount; i++) { + *id_qs->queries[i].rc = + map_adrc2idmaprc(id_qs->queries[i].ad_rc); } - (*state)->qdead = 1; - /* Wait for other threads proceesing search result to finish */ - idmap_lookup_wait_batch(*state); - - if (rc == -1 || (*state)->qinflight != 0) - retcode = IDMAP_ERR_RETRIABLE_NET_ERR; - idmap_lookup_release_batch(state); - - return (retcode); + return (map_adrc2idmaprc(ad_rc)); } /* @@ -1744,11 +614,8 @@ idmap_batch_add1(idmap_query_state_t *state, const char *filter, char **sid, rid_t *rid, int *sid_type, char **unixname, idmap_retcode *rc) { - idmap_retcode retcode = IDMAP_SUCCESS; - int lrc, qid, i; - int num; - int dead; - struct timeval tv; + adutils_rc ad_rc; + int qid, i; idmap_q_t *q; static char *attrs[] = { SAN, @@ -1760,12 +627,11 @@ idmap_batch_add1(idmap_query_state_t *state, const char *filter, }; qid = atomic_inc_32_nv(&state->qlastsent) - 1; - q = &(state->queries[qid]); /* - * Remember the expected canonname so we can check the results - * agains it + * Remember the expected canonname, domainname and unix type + * so we can check the results * against it */ q->ecanonname = ecanonname; q->edomain = edomain; @@ -1821,69 +687,19 @@ idmap_batch_add1(idmap_query_state_t *state, const char *filter, if (value != NULL) *value = NULL; - /* Check the number of queued requests first */ - tv.tv_sec = IDMAPD_SEARCH_TIMEOUT; - tv.tv_usec = 0; - while (!state->qadh->dead && - state->qadh->num_requests > state->qadh->max_requests) { - if (idmap_get_adobject_batch(state->qadh, &tv) != 0) - break; - } - /* * Don't set *canonname to NULL because it may be pointing to the * given winname. Later on if we get a canonical name from AD the * old name if any will be freed before assigning the new name. */ - /* Send this lookup, don't wait for a result here */ - lrc = LDAP_SUCCESS; - (void) pthread_mutex_lock(&state->qadh->lock); - - if (!state->qadh->dead) { - state->qadh->idletime = time(NULL); - lrc = ldap_search_ext(state->qadh->ld, "", - LDAP_SCOPE_SUBTREE, filter, attrs, 0, NULL, NULL, - NULL, -1, &q->msgid); - - if (lrc == LDAP_SUCCESS) { - state->qadh->num_requests++; - } else if (lrc == LDAP_BUSY || lrc == LDAP_UNAVAILABLE || - lrc == LDAP_CONNECT_ERROR || lrc == LDAP_SERVER_DOWN || - lrc == LDAP_UNWILLING_TO_PERFORM) { - retcode = IDMAP_ERR_RETRIABLE_NET_ERR; - state->qadh->dead = 1; - } else { - retcode = IDMAP_ERR_OTHER; - state->qadh->dead = 1; - } - } - dead = state->qadh->dead; - num = state->qadh->num_requests; - (void) pthread_mutex_unlock(&state->qadh->lock); - - if (dead) { - if (lrc != LDAP_SUCCESS) - idmapdlog(LOG_DEBUG, - "AD ldap_search_ext error (%s) " - "- %d queued requests", - ldap_err2string(lrc), num); - return (retcode); - } - - atomic_inc_32(&state->qinflight); - /* - * Reap as many requests as we can _without_ waiting - * - * We do this to prevent any possible TCP socket buffer - * starvation deadlocks. + * Invoke the mother of all APIs i.e. the adutils API */ - (void) memset(&tv, 0, sizeof (tv)); - while (idmap_get_adobject_batch(state->qadh, &tv) == 0) - ; - - return (IDMAP_SUCCESS); + ad_rc = adutils_lookup_batch_add(state->qs, filter, + (const char **)attrs, + edomain, &q->result, &q->ad_rc); + return (map_adrc2idmaprc(ad_rc)); } idmap_retcode @@ -1928,17 +744,15 @@ idmap_name2sid_batch_add1(idmap_query_state_t *state, } *strchr(ecanonname, '@') = '\0'; } else { - /* - * 'name' not qualified and dname not given - * - * Note: ad->dflt_w2k_dom cannot be NULL - see - * idmap_ad_alloc() - */ - if (*state->qadh->owner->dflt_w2k_dom == '\0') { + /* 'name' not qualified and dname not given */ + dname = adutils_lookup_batch_getdefdomain( + state->qs); + assert(dname != NULL); + if (*dname == '\0') { free(ecanonname); return (IDMAP_ERR_DOMAIN); } - edomain = strdup(state->qadh->owner->dflt_w2k_dom); + edomain = strdup(dname); if (edomain == NULL) { free(ecanonname); return (IDMAP_ERR_MEMORY); @@ -1990,7 +804,7 @@ idmap_sid2name_batch_add1(idmap_query_state_t *state, idmap_retcode retcode; int flen, ret; char *filter = NULL; - char cbinsid[MAXHEXBINSID + 1]; + char cbinsid[ADUTILS_MAXHEXBINSID + 1]; /* * Strategy: search [the global catalog] for user/group by @@ -2000,7 +814,7 @@ idmap_sid2name_batch_add1(idmap_query_state_t *state, * computer. */ - ret = idmap_txtsid2hexbinsid(sid, rid, &cbinsid[0], sizeof (cbinsid)); + ret = adutils_txtsid2hexbinsid(sid, rid, &cbinsid[0], sizeof (cbinsid)); if (ret != 0) return (IDMAP_ERR_SID); diff --git a/usr/src/cmd/idmap/idmapd/adutils.h b/usr/src/cmd/idmap/idmapd/adutils.h index c81c0a8dab..6b722749ac 100644 --- a/usr/src/cmd/idmap/idmapd/adutils.h +++ b/usr/src/cmd/idmap/idmapd/adutils.h @@ -27,8 +27,6 @@ #ifndef _ADUTILS_H #define _ADUTILS_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -51,6 +49,7 @@ extern "C" { #include <thread.h> #include <synch.h> #include "idmap_prot.h" +#include "libadutils.h" #include <sys/idmap.h> /* @@ -68,49 +67,12 @@ extern "C" { #define _IDMAP_T_DOMAIN -1006 #define _IDMAP_T_COMPUTER -1007 -#define SID_MAX_SUB_AUTHORITIES 15 -#define MAXBINSID (1 + 1 + 6 + (SID_MAX_SUB_AUTHORITIES * 4)) -#define MAXHEXBINSID (MAXBINSID * 3) - typedef uint32_t rid_t; -/* - * We use the port numbers for normal LDAP and global catalog LDAP as - * the enum values for this enumeration. Clever? Silly? You decide. - * Although we never actually use these enum values as port numbers and - * never will, so this is just cute. - */ -typedef enum idmap_ad_partition { - IDMAP_AD_DATA = 389, - IDMAP_AD_GLOBAL_CATALOG = 3268 -} idmap_ad_partition_t; - -typedef struct ad ad_t; typedef struct idmap_query_state idmap_query_state_t; -/* - * Idmap interfaces: - * - * - an ad_t represents an AD partition - * - a DS (hostname + port, if port != 0) can be added/removed from an ad_t - * - and because libldap supports space-separated lists of servers, a - * single hostname value can actually be a set of hostnames. - * - an ad_t can be allocated, ref'ed and released; last release - * releases resources - * - * - lookups are batched; see below. - * - * See below. - */ - -/* Allocate/release ad_t objects */ -int idmap_ad_alloc(ad_t **new_ad, const char *default_domain, - idmap_ad_partition_t part); -void idmap_ad_free(ad_t **ad); +int idmap_add_ds(adutils_ad_t *ad, const char *host, int port); -/* Add/remove a DS to/from an ad_t */ -int idmap_add_ds(ad_t *ad, const char *host, int port); -void idmap_delete_ds(ad_t *ad, const char *host, int port); /* * Batch lookups @@ -132,7 +94,7 @@ void idmap_delete_ds(ad_t *ad, const char *host, int port); */ /* Start a batch of lookups */ -idmap_retcode idmap_lookup_batch_start(ad_t *ad, int nqueries, +idmap_retcode idmap_lookup_batch_start(adutils_ad_t *ad, int nqueries, idmap_query_state_t **state); /* End a batch and release its idmap_query_state_t object */ diff --git a/usr/src/cmd/idmap/idmapd/dbutils.c b/usr/src/cmd/idmap/idmapd/dbutils.c index 6420344b00..771eaf8250 100644 --- a/usr/src/cmd/idmap/idmapd/dbutils.c +++ b/usr/src/cmd/idmap/idmapd/dbutils.c @@ -67,6 +67,9 @@ static idmap_retcode lookup_cache_name2sid(sqlite *, const char *, #define AVOID_NAMESERVICE(req)\ (req->flag & IDMAP_REQ_FLG_NO_NAMESERVICE) +#define ALLOW_WK_OR_LOCAL_SIDS_ONLY(req)\ + (req->flag & IDMAP_REQ_FLG_WK_OR_LOCAL_SIDS_ONLY) + #define IS_EPHEMERAL(pid) (pid > INT32_MAX && pid != SENTINEL_PID) #define LOCALRID_MIN 1000 @@ -1844,10 +1847,13 @@ ad_lookup_batch(lookup_state_t *state, idmap_mapping_batch *batch, } retry: + RDLOCK_CONFIG(); retcode = idmap_lookup_batch_start(_idmapdstate.ad, state->ad_nqueries, &qs); + UNLOCK_CONFIG(); if (retcode != IDMAP_SUCCESS) { - if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && retries++ < 2) + if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && + retries++ < ADUTILS_DEF_NUM_RETRIES) goto retry; degrade_svc(1, "failed to create batch for AD lookup"); goto out; @@ -2019,7 +2025,8 @@ retry: idmap_lookup_release_batch(&qs); } - if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && retries++ < 2) + if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && + retries++ < ADUTILS_DEF_NUM_RETRIES) goto retry; else if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR) degrade_svc(1, "some AD lookups timed out repeatedly"); @@ -2224,11 +2231,16 @@ sid2pid_first_pass(lookup_state_t *state, idmap_mapping *req, if (retcode != IDMAP_ERR_NOTFOUND) goto out; - /* Check if this is a localsid */ if (!wksid) { + /* Check if this is a localsid */ retcode = lookup_localsid2pid(req, res); if (retcode != IDMAP_ERR_NOTFOUND) goto out; + + if (ALLOW_WK_OR_LOCAL_SIDS_ONLY(req)) { + retcode = IDMAP_ERR_NONEGENERATED; + goto out; + } } /* Lookup cache */ @@ -3581,9 +3593,12 @@ ad_lookup_by_winname(lookup_state_t *state, idmap_retcode rc, retcode; retry: + RDLOCK_CONFIG(); retcode = idmap_lookup_batch_start(_idmapdstate.ad, 1, &qs); + UNLOCK_CONFIG(); if (retcode != IDMAP_SUCCESS) { - if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && retries++ < 2) + if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && + retries++ < ADUTILS_DEF_NUM_RETRIES) goto retry; degrade_svc(1, "failed to create request for AD lookup " "by winname"); @@ -3604,7 +3619,8 @@ retry: else retcode = idmap_lookup_batch_end(&qs); - if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && retries++ < 2) + if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR && + retries++ < ADUTILS_DEF_NUM_RETRIES) goto retry; else if (retcode == IDMAP_ERR_RETRIABLE_NET_ERR) degrade_svc(1, "some AD lookups timed out repeatedly"); diff --git a/usr/src/cmd/idmap/idmapd/idmapd.h b/usr/src/cmd/idmap/idmapd/idmapd.h index 1fc0f696ed..b4cab338c4 100644 --- a/usr/src/cmd/idmap/idmapd/idmapd.h +++ b/usr/src/cmd/idmap/idmapd/idmapd.h @@ -26,8 +26,6 @@ #ifndef _IDMAPD_H #define _IDMAPD_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <stdlib.h> #include <stdarg.h> @@ -41,6 +39,7 @@ #include "idmap_prot.h" #include "adutils.h" #include "idmap_config.h" +#include "libadutils.h" #ifdef __cplusplus extern "C" { @@ -80,7 +79,7 @@ typedef struct idmapd_state { gid_t limit_gid; int new_eph_db; /* was the ephem ID db [re-]created? */ bool_t eph_map_unres_sids; - ad_t *ad; + adutils_ad_t *ad; } idmapd_state_t; extern idmapd_state_t _idmapdstate; diff --git a/usr/src/cmd/idmap/idmapd/init.c b/usr/src/cmd/idmap/idmapd/init.c index 5ccb6553a3..f1eb9d9aaf 100644 --- a/usr/src/cmd/idmap/idmapd/init.c +++ b/usr/src/cmd/idmap/idmapd/init.c @@ -23,8 +23,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Initialization routines */ @@ -105,9 +103,9 @@ load_config() void reload_ad() { - int i; - ad_t *old; - ad_t *new; + int i; + adutils_ad_t *old; + adutils_ad_t *new; idmap_pg_config_t *pgcfg = &_idmapdstate.cfg->pgcfg; @@ -126,8 +124,8 @@ reload_ad() old = _idmapdstate.ad; - if (idmap_ad_alloc(&new, pgcfg->default_domain, - IDMAP_AD_GLOBAL_CATALOG) != 0) { + if (adutils_ad_alloc(&new, pgcfg->default_domain, + ADUTILS_AD_GLOBAL_CATALOG) != ADUTILS_SUCCESS) { degrade_svc(0, "could not initialize AD context"); return; } @@ -136,7 +134,7 @@ reload_ad() if (idmap_add_ds(new, pgcfg->global_catalog[i].host, pgcfg->global_catalog[i].port) != 0) { - idmap_ad_free(&new); + adutils_ad_free(&new); degrade_svc(0, "could not initialize AD GC context"); return; } @@ -145,7 +143,7 @@ reload_ad() _idmapdstate.ad = new; if (old != NULL) - idmap_ad_free(&old); + adutils_ad_free(&old); } diff --git a/usr/src/cmd/netfiles/Makefile b/usr/src/cmd/netfiles/Makefile index 6e7e66f805..4f21e795c8 100644 --- a/usr/src/cmd/netfiles/Makefile +++ b/usr/src/cmd/netfiles/Makefile @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -19,10 +18,7 @@ # # CDDL HEADER END # -# -#ident "%Z%%M% %I% %E% SMI" -# -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # @@ -33,7 +29,7 @@ include ../Makefile.cmd FILES= hosts services ETCFILES= netconfig nsswitch.conf nsswitch.files nsswitch.nis nsswitch.nisplus \ - nsswitch.dns nsswitch.ldap + nsswitch.dns nsswitch.ldap nsswitch.ad ROOTNET= $(ROOTETC)/net TICLTS= $(ROOTNET)/ticlts diff --git a/usr/src/cmd/netfiles/nsswitch.ad b/usr/src/cmd/netfiles/nsswitch.ad new file mode 100644 index 0000000000..e869c6bd32 --- /dev/null +++ b/usr/src/cmd/netfiles/nsswitch.ad @@ -0,0 +1,77 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# /etc/nsswitch.ad: +# +# An example file that could be copied over to /etc/nsswitch.conf; it +# uses Microsoft's Active Directory (AD) for passwd and group; DNS for +# hosts lookups; and files for the remaining databases. +# +# AD name service currently only supports passwd(4) and group(4) +# databases. +# +# Other name service databases such as audit_user(4) and user_attr(4) +# that follow passwd entry in nsswitch.conf are not supported by AD. +# If the AD backend is processed (based on the configuration) it will +# return NOT FOUND for these databases. +# +# Since AD name service is not yet a complete Solaris solution, please +# add other name service(s) as appropriate to the lines in this file. +# +# "hosts:" and "services:" in this file are used only if the +# /etc/netconfig file has a "-" for nametoaddr_libs of "inet" transports. + +# DNS service expects that an instance of svc:/network/dns/client be +# enabled and online. + +passwd: files ad +group: files ad + +# Note that IPv4 addresses are searched for in all of the ipnodes databases +# before searching the hosts databases. +hosts: files dns +ipnodes: files dns + +networks: files +protocols: files +rpc: files +ethers: files +netmasks: files +bootparams: files +publickey: files +# At present there isn't a 'files' backend for netgroup; the system will +# figure it out pretty quickly, and won't use netgroups at all. +netgroup: files +automount: files +aliases: files +services: files +printers: user files + +auth_attr: files +prof_attr: files +project: files + +tnrhtp: files +tnrhdb: files diff --git a/usr/src/cmd/nscd/nscd_cfgdef.h b/usr/src/cmd/nscd/nscd_cfgdef.h index f503dfbfea..6acdec9feb 100644 --- a/usr/src/cmd/nscd/nscd_cfgdef.h +++ b/usr/src/cmd/nscd/nscd_cfgdef.h @@ -26,8 +26,6 @@ #ifndef _NSCD_CFGDEF_H #define _NSCD_CFGDEF_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -126,6 +124,7 @@ nscd_cfg_id_t _nscd_cfg_nsw_src[] = { { 5, "dns" }, { 6, "compat" }, { 7, "user" }, + { 8, "ad" }, { -1, NULL } }; diff --git a/usr/src/cmd/nscd/nscd_smfmonitor.c b/usr/src/cmd/nscd/nscd_smfmonitor.c index a0874e394d..bd2ff958ba 100644 --- a/usr/src/cmd/nscd/nscd_smfmonitor.c +++ b/usr/src/cmd/nscd/nscd_smfmonitor.c @@ -23,8 +23,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdlib.h> #include <libscf.h> #include <string.h> @@ -164,9 +162,10 @@ _nscd_get_smf_state(int srci, int dbi, int recheck) n = NSCD_NSW_SRC_NAME(srci); /* the files, compat, and dns backends are always available */ - if ((*n == 'f' || *n == 'c' || *n == 'd') && + if ((*n == 'f' || *n == 'c' || *n == 'd' || *n == 'a') && (strcmp(NSCD_NSW_SRC_NAME(srci), "files") == 0 || strcmp(NSCD_NSW_SRC_NAME(srci), "compat") == 0 || + strcmp(NSCD_NSW_SRC_NAME(srci), "ad") == 0 || strcmp(NSCD_NSW_SRC_NAME(srci), "dns") == 0)) { return (SCF_STATE_ONLINE); } diff --git a/usr/src/head/rpcsvc/idmap_prot.x b/usr/src/head/rpcsvc/idmap_prot.x index 23f62685cd..84893f290b 100644 --- a/usr/src/head/rpcsvc/idmap_prot.x +++ b/usr/src/head/rpcsvc/idmap_prot.x @@ -148,12 +148,16 @@ const IDMAP_REQ_FLG_NO_NAMESERVICE = 0x00000004; /* Request how a mapping was formed */ const IDMAP_REQ_FLG_MAPPING_INFO = 0x00000008; + /* * This libidmap only flag is defined in idmap.h * It enables use of the libidmap cache * const IDMAP_REQ_FLG_USE_CACHE = 0x00000010; */ +/* Request mapping for well-known or local SIDs only */ +const IDMAP_REQ_FLG_WK_OR_LOCAL_SIDS_ONLY = 0x00000020; + /* * Mapping direction definitions diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile index 94e5f1d7f3..7a22e5a065 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -243,6 +243,7 @@ SUBDIRS += \ libshare \ libsqlite \ libidmap \ + libadutils \ libipmi \ libexacct/demo \ libvscan \ @@ -527,6 +528,8 @@ gss_mechs/mech_krb5: libgss libnsl libsocket libresolv pkcs11 libadt_jni: libbsm $(CLOSED_BUILD)libc: $(CLOSED)/lib/libc_i18n libast: libsocket +libadutils: libldap5 libidmap +nsswitch: libadutils libbsm: libtsol libcmd: libast libsocket libnsl libcmdutils: libavl diff --git a/usr/src/lib/libadutils/Makefile b/usr/src/lib/libadutils/Makefile new file mode 100644 index 0000000000..cded41b662 --- /dev/null +++ b/usr/src/lib/libadutils/Makefile @@ -0,0 +1,49 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include $(SRC)/lib/Makefile.lib + +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint + +.KEEP_STATE: + +all clean clobber install lint: $(SUBDIRS) + +check: $(CHECKHDRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include $(SRC)/Makefile.msg.targ +include $(SRC)/lib/Makefile.targ diff --git a/usr/src/lib/libadutils/Makefile.com b/usr/src/lib/libadutils/Makefile.com new file mode 100644 index 0000000000..bdb5668bd8 --- /dev/null +++ b/usr/src/lib/libadutils/Makefile.com @@ -0,0 +1,50 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +LIBRARY = libadutils.a +VERS = .1 +OBJECTS = adutils.o +LINT_OBJECTS = adutils.o + +include ../../Makefile.lib + +LIBS = $(DYNLIB) $(LINTLIB) +LDLIBS += -lc -lldap -lidmap +SRCDIR = ../common +$(LINTLIB):= SRCS = $(SRCDIR)/$(LINTSRC) +IDMAP_PROT_DIR = $(SRC)/head/rpcsvc +CFLAGS += $(CCVERBOSE) +CPPFLAGS += -D_REENTRANT -I$(SRCDIR) -I$(SRC)/lib/libidmap/common \ + -I$(IDMAP_PROT_DIR) + +lint := OBJECTS = $(LINT_OBJECTS) + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +include ../../Makefile.targ diff --git a/usr/src/lib/libadutils/amd64/Makefile b/usr/src/lib/libadutils/amd64/Makefile new file mode 100644 index 0000000000..39ae44e24b --- /dev/null +++ b/usr/src/lib/libadutils/amd64/Makefile @@ -0,0 +1,29 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/libadutils/common/adutils.c b/usr/src/lib/libadutils/common/adutils.c new file mode 100644 index 0000000000..d838858671 --- /dev/null +++ b/usr/src/lib/libadutils/common/adutils.c @@ -0,0 +1,1693 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <alloca.h> +#include <string.h> +#include <strings.h> +#include <lber.h> +#include <sasl/sasl.h> +#include <string.h> +#include <ctype.h> +#include <synch.h> +#include <atomic.h> +#include <errno.h> +#include <assert.h> +#include <limits.h> +#include <sys/u8_textprep.h> +#include <sys/varargs.h> +#include "libadutils.h" +#include "adutils_impl.h" + +/* List of DSs, needed by the idle connection reaper thread */ +static pthread_mutex_t adhostlock = PTHREAD_MUTEX_INITIALIZER; +static adutils_host_t *host_head = NULL; + +/* + * List of query state structs -- needed so we can "route" LDAP results + * to the right context if multiple threads should be using the same + * connection concurrently + */ +static pthread_mutex_t qstatelock = PTHREAD_MUTEX_INITIALIZER; +static adutils_query_state_t *qstatehead = NULL; + +static char *adutils_sid_ber2str(BerValue *bvalues); +static void adutils_lookup_batch_unlock(adutils_query_state_t **state); +static void delete_ds(adutils_ad_t *ad, const char *host, int port); + +typedef struct binary_attrs { + const char *name; + char *(*ber2str)(BerValue *bvalues); +} binary_attrs_t; + +static binary_attrs_t binattrs[] = { + {"objectSID", adutils_sid_ber2str}, + {NULL, NULL} +}; + +void +adutils_set_log(int pri, bool_t syslog, bool_t degraded) +{ + idmap_log_stderr(pri); + idmap_log_syslog(syslog); + idmap_log_degraded(degraded); +} + +/* + * Turn "foo.bar.com" into "dc=foo,dc=bar,dc=com" + */ +static +char * +adutils_dns2dn(const char *dns) +{ + int nameparts; + return (ldap_dns_to_dn((char *)dns, &nameparts)); +} + +/* + * Turn "dc=foo,dc=bar,dc=com" into "foo.bar.com"; ignores any other + * attributes (CN, etc...). + */ +char * +adutils_dn2dns(const char *dn) +{ + char **rdns = NULL; + char **attrs = NULL; + char **labels = NULL; + char *dns = NULL; + char **rdn, **attr, **label; + int maxlabels = 5; + int nlabels = 0; + int dnslen; + + /* + * There is no reverse of ldap_dns_to_dn() in our libldap, so we + * have to do the hard work here for now. + */ + + /* + * This code is much too liberal: it looks for "dc" attributes + * in all RDNs of the DN. In theory this could cause problems + * if people were to use "dc" in nodes other than the root of + * the tree, but in practice noone, least of all Active + * Directory, does that. + * + * On the other hand, this code is much too conservative: it + * does not make assumptions about ldap_explode_dn(), and _that_ + * is the true for looking at every attr of every RDN. + * + * Since we only ever look at dc and those must be DNS labels, + * at least until we get around to supporting IDN here we + * shouldn't see escaped labels from AD nor from libldap, though + * the spec (RFC2253) does allow libldap to escape things that + * don't need escaping -- if that should ever happen then + * libldap will need a spanking, and we can take care of that. + */ + + /* Explode a DN into RDNs */ + if ((rdns = ldap_explode_dn(dn, 0)) == NULL) + return (NULL); + + labels = calloc(maxlabels + 1, sizeof (char *)); + label = labels; + + for (rdn = rdns; *rdn != NULL; rdn++) { + if (attrs != NULL) + ldap_value_free(attrs); + + /* Explode each RDN, look for DC attr, save val as DNS label */ + if ((attrs = ldap_explode_rdn(rdn[0], 0)) == NULL) + goto done; + + for (attr = attrs; *attr != NULL; attr++) { + if (strncasecmp(*attr, "dc=", 3) != 0) + continue; + + /* Found a DNS label */ + labels[nlabels++] = strdup((*attr) + 3); + + if (nlabels == maxlabels) { + char **tmp; + tmp = realloc(labels, + sizeof (char *) * (maxlabels + 1)); + + if (tmp == NULL) + goto done; + + labels = tmp; + labels[nlabels] = NULL; + } + + /* There should be just one DC= attr per-RDN */ + break; + } + } + + /* + * Got all the labels, now join with '.' + * + * We need room for nlabels - 1 periods ('.'), one nul + * terminator, and the strlen() of each label. + */ + dnslen = nlabels; + for (label = labels; *label != NULL; label++) + dnslen += strlen(*label); + + if ((dns = malloc(dnslen)) == NULL) + goto done; + + *dns = '\0'; + + for (label = labels; *label != NULL; label++) { + (void) strlcat(dns, *label, dnslen); + /* + * NOTE: the last '.' won't be appended -- there's no room + * for it! + */ + (void) strlcat(dns, ".", dnslen); + } + +done: + if (labels != NULL) { + for (label = labels; *label != NULL; label++) + free(*label); + free(labels); + } + if (attrs != NULL) + ldap_value_free(attrs); + if (rdns != NULL) + ldap_value_free(rdns); + + return (dns); +} + +/* + * Convert a binary SID in a BerValue to a adutils_sid_t + */ +static +int +getsid(BerValue *bval, adutils_sid_t *sidp) +{ + int i, j; + uchar_t *v; + uint32_t a; + + /* + * The binary format of a SID is as follows: + * + * byte #0: version, always 0x01 + * byte #1: RID count, always <= 0x0f + * bytes #2-#7: SID authority, big-endian 48-bit unsigned int + * + * followed by RID count RIDs, each a little-endian, unsigned + * 32-bit int. + */ + /* + * Sanity checks: must have at least one RID, version must be + * 0x01, and the length must be 8 + rid count * 4 + */ + if (bval->bv_len > 8 && bval->bv_val[0] == 0x01 && + bval->bv_len == 1 + 1 + 6 + bval->bv_val[1] * 4) { + v = (uchar_t *)bval->bv_val; + sidp->version = v[0]; + sidp->sub_authority_count = v[1]; + sidp->authority = + /* big endian -- so start from the left */ + ((u_longlong_t)v[2] << 40) | + ((u_longlong_t)v[3] << 32) | + ((u_longlong_t)v[4] << 24) | + ((u_longlong_t)v[5] << 16) | + ((u_longlong_t)v[6] << 8) | + (u_longlong_t)v[7]; + for (i = 0; i < sidp->sub_authority_count; i++) { + j = 8 + (i * 4); + /* little endian -- so start from the right */ + a = (v[j + 3] << 24) | (v[j + 2] << 16) | + (v[j + 1] << 8) | (v[j]); + sidp->sub_authorities[i] = a; + } + return (0); + } + return (-1); +} + +/* + * Convert a adutils_sid_t to S-1-... + */ +static +char * +sid2txt(adutils_sid_t *sidp) +{ + int rlen, i, len; + char *str, *cp; + + if (sidp->version != 1) + return (NULL); + + len = sizeof ("S-1-") - 1; + + /* + * We could optimize like so, but, why? + * if (sidp->authority < 10) + * len += 2; + * else if (sidp->authority < 100) + * len += 3; + * else + * len += snprintf(NULL, 0"%llu", sidp->authority); + */ + len += snprintf(NULL, 0, "%llu", sidp->authority); + + /* Max length of a uint32_t printed out in ASCII is 10 bytes */ + len += 1 + (sidp->sub_authority_count + 1) * 10; + + if ((cp = str = malloc(len)) == NULL) + return (NULL); + + rlen = snprintf(str, len, "S-1-%llu", sidp->authority); + + cp += rlen; + len -= rlen; + + for (i = 0; i < sidp->sub_authority_count; i++) { + assert(len > 0); + rlen = snprintf(cp, len, "-%u", sidp->sub_authorities[i]); + cp += rlen; + len -= rlen; + assert(len >= 0); + } + + return (str); +} + +/* + * Convert a adutils_sid_t to on-the-wire encoding + */ +static +int +sid2binsid(adutils_sid_t *sid, uchar_t *binsid, int binsidlen) +{ + uchar_t *p; + int i; + uint64_t a; + uint32_t r; + + if (sid->version != 1 || + binsidlen != (1 + 1 + 6 + sid->sub_authority_count * 4)) + return (-1); + + p = binsid; + *p++ = 0x01; /* version */ + /* sub authority count */ + *p++ = sid->sub_authority_count; + /* Authority */ + a = sid->authority; + /* big-endian -- start from left */ + *p++ = (a >> 40) & 0xFF; + *p++ = (a >> 32) & 0xFF; + *p++ = (a >> 24) & 0xFF; + *p++ = (a >> 16) & 0xFF; + *p++ = (a >> 8) & 0xFF; + *p++ = a & 0xFF; + + /* sub-authorities */ + for (i = 0; i < sid->sub_authority_count; i++) { + r = sid->sub_authorities[i]; + /* little-endian -- start from right */ + *p++ = (r & 0x000000FF); + *p++ = (r & 0x0000FF00) >> 8; + *p++ = (r & 0x00FF0000) >> 16; + *p++ = (r & 0xFF000000) >> 24; + } + + return (0); +} + +/* + * Convert a stringified SID (S-1-...) into a hex-encoded version of the + * on-the-wire encoding, but with each pair of hex digits pre-pended + * with a '\', so we can pass this to libldap. + */ +int +adutils_txtsid2hexbinsid(const char *txt, const uint32_t *rid, + char *hexbinsid, int hexbinsidlen) +{ + adutils_sid_t sid = { 0 }; + int i, j; + const char *cp; + char *ecp; + u_longlong_t a; + unsigned long r; + uchar_t *binsid, b, hb; + + /* Only version 1 SIDs please */ + if (strncmp(txt, "S-1-", strlen("S-1-")) != 0) + return (-1); + + if (strlen(txt) < (strlen("S-1-") + 1)) + return (-1); + + /* count '-'s */ + for (j = 0, cp = strchr(txt, '-'); + cp != NULL && *cp != '\0'; + j++, cp = strchr(cp + 1, '-')) { + /* can't end on a '-' */ + if (*(cp + 1) == '\0') + return (-1); + } + + /* Adjust count for version and authority */ + j -= 2; + + /* we know the version number and RID count */ + sid.version = 1; + sid.sub_authority_count = (rid != NULL) ? j + 1 : j; + + /* must have at least one RID, but not too many */ + if (sid.sub_authority_count < 1 || + sid.sub_authority_count > ADUTILS_SID_MAX_SUB_AUTHORITIES) + return (-1); + + /* check that we only have digits and '-' */ + if (strspn(txt + 1, "0123456789-") < (strlen(txt) - 1)) + return (-1); + + cp = txt + strlen("S-1-"); + + /* 64-bit safe parsing of unsigned 48-bit authority value */ + errno = 0; + a = strtoull(cp, &ecp, 10); + + /* errors parsing the authority or too many bits */ + if (cp == ecp || (a == 0 && errno == EINVAL) || + (a == ULLONG_MAX && errno == ERANGE) || + (a & 0x0000ffffffffffffULL) != a) + return (-1); + + cp = ecp; + + sid.authority = (uint64_t)a; + + for (i = 0; i < j; i++) { + if (*cp++ != '-') + return (-1); + /* 64-bit safe parsing of unsigned 32-bit RID */ + errno = 0; + r = strtoul(cp, &ecp, 10); + /* errors parsing the RID or too many bits */ + if (cp == ecp || (r == 0 && errno == EINVAL) || + (r == ULONG_MAX && errno == ERANGE) || + (r & 0xffffffffUL) != r) + return (-1); + sid.sub_authorities[i] = (uint32_t)r; + cp = ecp; + } + + /* check that all of the string SID has been consumed */ + if (*cp != '\0') + return (-1); + + if (rid != NULL) + sid.sub_authorities[j] = *rid; + + j = 1 + 1 + 6 + sid.sub_authority_count * 4; + + if (hexbinsidlen < (j * 3)) + return (-2); + + /* binary encode the SID */ + binsid = (uchar_t *)alloca(j); + (void) sid2binsid(&sid, binsid, j); + + /* hex encode, with a backslash before each byte */ + for (ecp = hexbinsid, i = 0; i < j; i++) { + b = binsid[i]; + *ecp++ = '\\'; + hb = (b >> 4) & 0xF; + *ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A'); + hb = b & 0xF; + *ecp++ = (hb <= 0x9 ? hb + '0' : hb - 10 + 'A'); + } + *ecp = '\0'; + + return (0); +} + +static +char * +convert_bval2sid(BerValue *bval, uint32_t *rid) +{ + adutils_sid_t sid; + + if (getsid(bval, &sid) < 0) + return (NULL); + + /* + * If desired and if the SID is what should be a domain/computer + * user or group SID (i.e., S-1-5-w-x-y-z-<user/group RID>) then + * save the last RID and truncate the SID + */ + if (rid != NULL && sid.authority == 5 && sid.sub_authority_count == 5) + *rid = sid.sub_authorities[--sid.sub_authority_count]; + return (sid2txt(&sid)); +} + + +/* + * Return a NUL-terminated stringified SID from the value of an + * objectSid attribute and put the last RID in *rid. + */ +char * +adutils_bv_objsid2sidstr(BerValue *bval, uint32_t *rid) +{ + char *sid; + + if (bval == NULL) + return (NULL); + /* objectSid is single valued */ + if ((sid = convert_bval2sid(bval, rid)) == NULL) + return (NULL); + return (sid); +} + +static +char * +adutils_sid_ber2str(BerValue *bval) +{ + return (adutils_bv_objsid2sidstr(bval, NULL)); +} + + +/* Return a NUL-terminated string from the Ber value */ +char * +adutils_bv_name2str(BerValue *bval) +{ + char *s; + + if (bval == NULL || bval->bv_val == NULL) + return (NULL); + if ((s = malloc(bval->bv_len + 1)) == NULL) + return (NULL); + (void) snprintf(s, bval->bv_len + 1, "%.*s", bval->bv_len, + bval->bv_val); + return (s); +} + +/*ARGSUSED*/ +static +int +saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts) +{ + sasl_interact_t *interact; + + if (prompts == NULL || flags != LDAP_SASL_INTERACTIVE) + return (LDAP_PARAM_ERROR); + + /* There should be no extra arguemnts for SASL/GSSAPI authentication */ + for (interact = prompts; interact->id != SASL_CB_LIST_END; + interact++) { + interact->result = NULL; + interact->len = 0; + } + return (LDAP_SUCCESS); +} + + +#define ADCONN_TIME 300 + +/* + * Idle connection reaping side of connection management + */ +void +adutils_reap_idle_connections() +{ + adutils_host_t *adh; + time_t now; + + (void) pthread_mutex_lock(&adhostlock); + now = time(NULL); + for (adh = host_head; adh != NULL; adh = adh->next) { + (void) pthread_mutex_lock(&adh->lock); + if (adh->ref == 0 && adh->idletime != 0 && + adh->idletime + ADCONN_TIME < now) { + if (adh->ld) { + (void) ldap_unbind(adh->ld); + adh->ld = NULL; + adh->idletime = 0; + adh->ref = 0; + } + } + (void) pthread_mutex_unlock(&adh->lock); + } + (void) pthread_mutex_unlock(&adhostlock); +} + + +adutils_rc +adutils_ad_alloc(adutils_ad_t **new_ad, const char *default_domain, + adutils_ad_partition_t part) +{ + adutils_ad_t *ad; + + *new_ad = NULL; + + if ((default_domain == NULL || *default_domain == '\0') && + part != ADUTILS_AD_GLOBAL_CATALOG) + return (ADUTILS_ERR_DOMAIN); + if ((ad = calloc(1, sizeof (*ad))) == NULL) + return (ADUTILS_ERR_MEMORY); + ad->ref = 1; + ad->partition = part; + if (default_domain == NULL) + default_domain = ""; + if ((ad->dflt_w2k_dom = strdup(default_domain)) == NULL) + goto err; + if (pthread_mutex_init(&ad->lock, NULL) != 0) + goto err; + *new_ad = ad; + return (ADUTILS_SUCCESS); + +err: + if (ad->dflt_w2k_dom != NULL) + free(ad->dflt_w2k_dom); + free(ad); + return (ADUTILS_ERR_MEMORY); +} + +void +adutils_ad_free(adutils_ad_t **ad) +{ + adutils_host_t *p; + adutils_host_t *prev; + + if (ad == NULL || *ad == NULL) + return; + + (void) pthread_mutex_lock(&(*ad)->lock); + + if (atomic_dec_32_nv(&(*ad)->ref) > 0) { + (void) pthread_mutex_unlock(&(*ad)->lock); + *ad = NULL; + return; + } + + (void) pthread_mutex_lock(&adhostlock); + prev = NULL; + p = host_head; + while (p != NULL) { + if (p->owner != (*ad)) { + prev = p; + p = p->next; + continue; + } else { + delete_ds((*ad), p->host, p->port); + if (prev == NULL) + p = host_head; + else + p = prev->next; + } + } + (void) pthread_mutex_unlock(&adhostlock); + + (void) pthread_mutex_unlock(&(*ad)->lock); + (void) pthread_mutex_destroy(&(*ad)->lock); + + free((*ad)->dflt_w2k_dom); + free(*ad); + + *ad = NULL; +} + +static +int +open_conn(adutils_host_t *adh, int timeoutsecs) +{ + int zero = 0; + int ldversion, rc; + int timeoutms = timeoutsecs * 1000; + + if (adh == NULL) + return (0); + + (void) pthread_mutex_lock(&adh->lock); + + if (!adh->dead && adh->ld != NULL) + /* done! */ + goto out; + + if (adh->ld != NULL) { + (void) ldap_unbind(adh->ld); + adh->ld = NULL; + } + adh->num_requests = 0; + + atomic_inc_64(&adh->generation); + + /* Open and bind an LDAP connection */ + adh->ld = ldap_init(adh->host, adh->port); + if (adh->ld == NULL) { + idmapdlog(LOG_INFO, "ldap_init() to server " + "%s port %d failed. (%s)", adh->host, + adh->port, strerror(errno)); + goto out; + } + ldversion = LDAP_VERSION3; + (void) ldap_set_option(adh->ld, LDAP_OPT_PROTOCOL_VERSION, &ldversion); + (void) ldap_set_option(adh->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); + (void) ldap_set_option(adh->ld, LDAP_OPT_TIMELIMIT, &zero); + (void) ldap_set_option(adh->ld, LDAP_OPT_SIZELIMIT, &zero); + (void) ldap_set_option(adh->ld, LDAP_X_OPT_CONNECT_TIMEOUT, &timeoutms); + (void) ldap_set_option(adh->ld, LDAP_OPT_RESTART, LDAP_OPT_ON); + rc = ldap_sasl_interactive_bind_s(adh->ld, "" /* binddn */, + adh->saslmech, NULL, NULL, adh->saslflags, &saslcallback, + NULL); + + if (rc != LDAP_SUCCESS) { + (void) ldap_unbind(adh->ld); + adh->ld = NULL; + idmapdlog(LOG_INFO, "ldap_sasl_interactive_bind_s() to server " + "%s port %d failed. (%s)", adh->host, adh->port, + ldap_err2string(rc)); + } + + idmapdlog(LOG_DEBUG, "Using global catalog server %s:%d", + adh->host, adh->port); + +out: + if (adh->ld != NULL) { + atomic_inc_32(&adh->ref); + adh->idletime = time(NULL); + adh->dead = 0; + (void) pthread_mutex_unlock(&adh->lock); + return (1); + } + + (void) pthread_mutex_unlock(&adh->lock); + return (0); +} + + +/* + * Connection management: find an open connection or open one + */ +static +adutils_host_t * +get_conn(adutils_ad_t *ad) +{ + adutils_host_t *adh = NULL; + int tries; + int dscount = 0; + int timeoutsecs = ADUTILS_LDAP_OPEN_TIMEOUT; + +retry: + (void) pthread_mutex_lock(&adhostlock); + + if (host_head == NULL) { + (void) pthread_mutex_unlock(&adhostlock); + goto out; + } + + if (dscount == 0) { + /* + * First try: count the number of DSes. + * + * Integer overflow is not an issue -- we can't have so many + * DSes because they won't fit even DNS over TCP, and SMF + * shouldn't let you set so many. + */ + for (adh = host_head, tries = 0; adh != NULL; adh = adh->next) { + if (adh->owner == ad) + dscount++; + } + + if (dscount == 0) { + (void) pthread_mutex_unlock(&adhostlock); + goto out; + } + + tries = dscount * 3; /* three tries per-ds */ + + /* + * Begin round-robin at the next DS in the list after the last + * one that we had a connection to, else start with the first + * DS in the list. + */ + adh = ad->last_adh; + } + + /* + * Round-robin -- pick the next one on the list; if the list + * changes on us, no big deal, we'll just potentially go + * around the wrong number of times. + */ + for (;;) { + if (adh != NULL && adh->ld != NULL && !adh->dead) + break; + if (adh == NULL || (adh = adh->next) == NULL) + adh = host_head; + if (adh->owner == ad) + break; + } + + ad->last_adh = adh; + (void) pthread_mutex_unlock(&adhostlock); + + /* Found suitable DS, open it if not already opened */ + if (open_conn(adh, timeoutsecs)) + return (adh); + + tries--; + if ((tries % dscount) == 0) + timeoutsecs *= 2; + if (tries > 0) + goto retry; + +out: + idmapdlog(LOG_NOTICE, "Couldn't open an LDAP connection to any global " + "catalog server!"); + return (NULL); +} + +static +void +release_conn(adutils_host_t *adh) +{ + int delete = 0; + + (void) pthread_mutex_lock(&adh->lock); + if (atomic_dec_32_nv(&adh->ref) == 0) { + if (adh->owner == NULL) + delete = 1; + adh->idletime = time(NULL); + } + (void) pthread_mutex_unlock(&adh->lock); + + /* Free this host if its owner no longer exists. */ + if (delete) { + (void) pthread_mutex_lock(&adhostlock); + delete_ds(NULL, adh->host, adh->port); + (void) pthread_mutex_unlock(&adhostlock); + } +} + +/* + * Create a adutils_host_t, populate it and add it to the list of hosts. + */ +adutils_rc +adutils_add_ds(adutils_ad_t *ad, const char *host, int port) +{ + adutils_host_t *p; + adutils_host_t *new = NULL; + int ret; + adutils_rc rc; + + (void) pthread_mutex_lock(&adhostlock); + for (p = host_head; p != NULL; p = p->next) { + if (p->owner != ad) + continue; + + if (strcmp(host, p->host) == 0 && p->port == port) { + /* already added */ + rc = ADUTILS_SUCCESS; + goto err; + } + } + + rc = ADUTILS_ERR_MEMORY; + + /* add new entry */ + new = (adutils_host_t *)calloc(1, sizeof (*new)); + if (new == NULL) + goto err; + new->owner = ad; + new->port = port; + new->dead = 0; + new->max_requests = 80; + new->num_requests = 0; + if ((new->host = strdup(host)) == NULL) + goto err; + new->saslflags = LDAP_SASL_INTERACTIVE; + new->saslmech = "GSSAPI"; + + if ((ret = pthread_mutex_init(&new->lock, NULL)) != 0) { + free(new->host); + new->host = NULL; + errno = ret; + rc = ADUTILS_ERR_INTERNAL; + goto err; + } + + /* link in */ + rc = ADUTILS_SUCCESS; + new->next = host_head; + host_head = new; + +err: + (void) pthread_mutex_unlock(&adhostlock); + + if (rc != 0 && new != NULL) { + if (new->host != NULL) { + (void) pthread_mutex_destroy(&new->lock); + free(new->host); + } + free(new); + } + + return (rc); +} + +/* + * Free a DS configuration. + * Caller must lock the adhostlock mutex + */ +static +void +delete_ds(adutils_ad_t *ad, const char *host, int port) +{ + adutils_host_t **p, *q; + + for (p = &host_head; *p != NULL; p = &((*p)->next)) { + if ((*p)->owner != ad || strcmp(host, (*p)->host) != 0 || + (*p)->port != port) + continue; + /* found */ + + (void) pthread_mutex_lock(&((*p)->lock)); + if ((*p)->ref > 0) { + /* + * Still in use. Set its owner to NULL so + * that it can be freed when its ref count + * becomes 0. + */ + (*p)->owner = NULL; + (void) pthread_mutex_unlock(&((*p)->lock)); + break; + } + (void) pthread_mutex_unlock(&((*p)->lock)); + + q = *p; + *p = (*p)->next; + + (void) pthread_mutex_destroy(&q->lock); + + if (q->ld) + (void) ldap_unbind(q->ld); + if (q->host) + free(q->host); + free(q); + break; + } + +} + +adutils_rc +adutils_lookup_batch_start(adutils_ad_t *ad, int nqueries, + adutils_ldap_res_search_cb ldap_res_search_cb, + void *ldap_res_search_argp, + adutils_query_state_t **state) +{ + adutils_query_state_t *new_state; + adutils_host_t *adh = NULL; + + if (ad == NULL) + return (ADUTILS_ERR_INTERNAL); + + *state = NULL; + adh = get_conn(ad); + if (adh == NULL) + return (ADUTILS_ERR_RETRIABLE_NET_ERR); + + new_state = calloc(1, sizeof (adutils_query_state_t) + + (nqueries - 1) * sizeof (adutils_q_t)); + if (new_state == NULL) + return (ADUTILS_ERR_MEMORY); + + /* + * Save default domain from the ad object so that we don't + * have to access the 'ad' object later. + */ + new_state->default_domain = strdup(adh->owner->dflt_w2k_dom); + if (new_state->default_domain == NULL) { + free(new_state); + return (ADUTILS_ERR_MEMORY); + } + + if (ad->partition == ADUTILS_AD_DATA) + new_state->basedn = adutils_dns2dn(new_state->default_domain); + else + new_state->basedn = strdup(""); + if (new_state->basedn == NULL) { + free(new_state->default_domain); + free(new_state); + return (ADUTILS_ERR_MEMORY); + } + + new_state->ref_cnt = 1; + new_state->qadh = adh; + new_state->qcount = nqueries; + new_state->qadh_gen = adh->generation; + new_state->qlastsent = 0; + new_state->ldap_res_search_cb = ldap_res_search_cb; + new_state->ldap_res_search_argp = ldap_res_search_argp; + (void) pthread_cond_init(&new_state->cv, NULL); + + (void) pthread_mutex_lock(&qstatelock); + new_state->next = qstatehead; + qstatehead = new_state; + (void) pthread_mutex_unlock(&qstatelock); + *state = new_state; + + return (ADUTILS_SUCCESS); +} + +/* + * Find the adutils_query_state_t to which a given LDAP result msgid on a + * given connection belongs. This routine increaments the reference count + * so that the object can not be freed. adutils_lookup_batch_unlock() + * must be called to decreament the reference count. + */ +static +int +msgid2query(adutils_host_t *adh, int msgid, + adutils_query_state_t **state, int *qid) +{ + adutils_query_state_t *p; + int i; + int ret; + + (void) pthread_mutex_lock(&qstatelock); + for (p = qstatehead; p != NULL; p = p->next) { + if (p->qadh != adh || adh->generation != p->qadh_gen) + continue; + for (i = 0; i < p->qcount; i++) { + if ((p->queries[i]).msgid == msgid) { + if (!p->qdead) { + p->ref_cnt++; + *state = p; + *qid = i; + ret = 1; + } else + ret = 0; + (void) pthread_mutex_unlock(&qstatelock); + return (ret); + } + } + } + (void) pthread_mutex_unlock(&qstatelock); + return (0); +} + +static +int +check_for_binary_attrs(const char *attr) +{ + int i; + for (i = 0; binattrs[i].name != NULL; i++) { + if (strcasecmp(binattrs[i].name, attr) == 0) + return (i); + } + return (-1); +} + +static +void +free_entry(adutils_entry_t *entry) +{ + int i, j; + adutils_attr_t *ap; + + if (entry == NULL) + return; + if (entry->attr_nvpairs == NULL) { + free(entry); + return; + } + for (i = 0; i < entry->num_nvpairs; i++) { + ap = &entry->attr_nvpairs[i]; + if (ap->attr_name == NULL) { + ldap_value_free(ap->attr_values); + continue; + } + if (check_for_binary_attrs(ap->attr_name) >= 0) { + free(ap->attr_name); + if (ap->attr_values == NULL) + continue; + for (j = 0; j < ap->num_values; j++) + free(ap->attr_values[j]); + free(ap->attr_values); + } else if (strcasecmp(ap->attr_name, "dn") == 0) { + free(ap->attr_name); + ldap_memfree(ap->attr_values[0]); + free(ap->attr_values); + } else { + free(ap->attr_name); + ldap_value_free(ap->attr_values); + } + } + free(entry->attr_nvpairs); + free(entry); +} + +void +adutils_freeresult(adutils_result_t **result) +{ + adutils_entry_t *e, *next; + + if (result == NULL || *result == NULL) + return; + if ((*result)->entries == NULL) { + free(*result); + *result = NULL; + return; + } + for (e = (*result)->entries; e != NULL; e = next) { + next = e->next; + free_entry(e); + } + free(*result); + *result = NULL; +} + +const adutils_entry_t * +adutils_getfirstentry(adutils_result_t *result) +{ + if (result != NULL) + return (result->entries); + return (NULL); +} + + +char ** +adutils_getattr(const adutils_entry_t *entry, const char *attrname) +{ + int i; + adutils_attr_t *ap; + + if (entry == NULL || entry->attr_nvpairs == NULL) + return (NULL); + for (i = 0; i < entry->num_nvpairs; i++) { + ap = &entry->attr_nvpairs[i]; + if (ap->attr_name != NULL && + strcasecmp(ap->attr_name, attrname) == 0) + return (ap->attr_values); + } + return (NULL); +} + + +/* + * Queue LDAP result for the given query. + * + * Return values: + * 0 success + * -1 ignore result + * -2 error + */ +static +int +make_entry(adutils_q_t *q, adutils_host_t *adh, LDAPMessage *search_res, + adutils_entry_t **entry) +{ + BerElement *ber = NULL; + BerValue **bvalues = NULL; + char **strvalues; + char *attr = NULL, *dn = NULL, *domain = NULL; + adutils_entry_t *ep; + adutils_attr_t *ap; + int i, j, b, err = 0, ret = -2; + + *entry = NULL; + + /* Check that this is the domain that we were looking for */ + if ((dn = ldap_get_dn(adh->ld, search_res)) == NULL) + return (-2); + if ((domain = adutils_dn2dns(dn)) == NULL) { + ldap_memfree(dn); + return (-2); + } + if (q->edomain != NULL) { + if (u8_strcmp(q->edomain, domain, 0, U8_STRCMP_CI_LOWER, + U8_UNICODE_LATEST, &err) != 0 || err != 0) { + ldap_memfree(dn); + free(domain); + return (-1); + } + } + free(domain); + + /* Allocate memory for the entry */ + if ((ep = calloc(1, sizeof (*ep))) == NULL) + goto out; + + /* For 'dn' */ + ep->num_nvpairs = 1; + + /* Count the number of name-value pairs for this entry */ + for (attr = ldap_first_attribute(adh->ld, search_res, &ber); + attr != NULL; + attr = ldap_next_attribute(adh->ld, search_res, ber)) { + ep->num_nvpairs++; + ldap_memfree(attr); + } + ber_free(ber, 0); + ber = NULL; + + /* Allocate array for the attribute name-value pairs */ + ep->attr_nvpairs = calloc(ep->num_nvpairs, sizeof (*ep->attr_nvpairs)); + if (ep->attr_nvpairs == NULL) { + ep->num_nvpairs = 0; + goto out; + } + + /* For dn */ + ap = &ep->attr_nvpairs[0]; + if ((ap->attr_name = strdup("dn")) == NULL) + goto out; + ap->num_values = 1; + ap->attr_values = calloc(ap->num_values, sizeof (*ap->attr_values)); + if (ap->attr_values == NULL) { + ap->num_values = 0; + goto out; + } + ap->attr_values[0] = dn; + dn = NULL; + + for (attr = ldap_first_attribute(adh->ld, search_res, &ber), i = 1; + attr != NULL; + ldap_memfree(attr), i++, + attr = ldap_next_attribute(adh->ld, search_res, ber)) { + ap = &ep->attr_nvpairs[i]; + if ((ap->attr_name = strdup(attr)) == NULL) + goto out; + + if ((b = check_for_binary_attrs(attr)) >= 0) { + bvalues = + ldap_get_values_len(adh->ld, search_res, attr); + if (bvalues == NULL) + continue; + ap->num_values = ldap_count_values_len(bvalues); + if (ap->num_values == 0) { + ldap_value_free_len(bvalues); + bvalues = NULL; + continue; + } + ap->attr_values = calloc(ap->num_values, + sizeof (*ap->attr_values)); + if (ap->attr_values == NULL) { + ap->num_values = 0; + goto out; + } + for (j = 0; j < ap->num_values; j++) { + ap->attr_values[j] = + binattrs[b].ber2str(bvalues[j]); + if (ap->attr_values[j] == NULL) + goto out; + } + ldap_value_free_len(bvalues); + bvalues = NULL; + continue; + } + + strvalues = ldap_get_values(adh->ld, search_res, attr); + if (strvalues == NULL) + continue; + ap->num_values = ldap_count_values(strvalues); + if (ap->num_values == 0) { + ldap_value_free(strvalues); + continue; + } + ap->attr_values = strvalues; + } + + ret = 0; +out: + ldap_memfree(attr); + ldap_memfree(dn); + ber_free(ber, 0); + ldap_value_free_len(bvalues); + if (ret < 0) + free_entry(ep); + else + *entry = ep; + return (ret); +} + +/* + * Put the search result onto the given adutils_q_t. + * Returns: 0 success + * < 0 error + */ +static +int +add_entry(adutils_host_t *adh, adutils_q_t *q, LDAPMessage *search_res) +{ + int ret = -1; + adutils_entry_t *entry = NULL; + adutils_result_t *res; + + ret = make_entry(q, adh, search_res, &entry); + if (ret < -1) { + *q->rc = ADUTILS_ERR_MEMORY; + goto out; + } else if (ret == -1) { + /* ignore result */ + goto out; + } + if (*q->result == NULL) { + res = calloc(1, sizeof (*res)); + if (res == NULL) { + *q->rc = ADUTILS_ERR_MEMORY; + goto out; + } + res->num_entries = 1; + res->entries = entry; + *q->result = res; + } else { + res = *q->result; + entry->next = res->entries; + res->entries = entry; + res->num_entries++; + } + *q->rc = ADUTILS_SUCCESS; + entry = NULL; + ret = 0; + +out: + free_entry(entry); + return (ret); +} + +/* + * Try to get a result; if there is one, find the corresponding + * adutils_q_t and process the result. + * + * Returns: 0 success + * -1 error + */ +static +int +get_adobject_batch(adutils_host_t *adh, struct timeval *timeout) +{ + adutils_query_state_t *query_state; + LDAPMessage *res = NULL; + int rc, ret, msgid, qid; + adutils_q_t *que; + int num; + + (void) pthread_mutex_lock(&adh->lock); + if (adh->dead || adh->num_requests == 0) { + ret = (adh->dead) ? -1 : -2; + (void) pthread_mutex_unlock(&adh->lock); + return (ret); + } + + /* Get one result */ + rc = ldap_result(adh->ld, LDAP_RES_ANY, 0, timeout, &res); + if ((timeout != NULL && timeout->tv_sec > 0 && rc == LDAP_SUCCESS) || + rc < 0) + adh->dead = 1; + + if (rc == LDAP_RES_SEARCH_RESULT && adh->num_requests > 0) + adh->num_requests--; + if (adh->dead) { + num = adh->num_requests; + (void) pthread_mutex_unlock(&adh->lock); + idmapdlog(LOG_DEBUG, + "AD ldap_result error - %d queued requests", num); + return (-1); + } + + switch (rc) { + case LDAP_RES_SEARCH_RESULT: + msgid = ldap_msgid(res); + if (msgid2query(adh, msgid, &query_state, &qid)) { + if (query_state->ldap_res_search_cb != NULL) { + /* + * We use the caller-provided callback + * to process the result. + */ + query_state->ldap_res_search_cb( + adh->ld, &res, rc, qid, + query_state->ldap_res_search_argp); + (void) pthread_mutex_unlock(&adh->lock); + } else { + /* + * No callback. We fallback to our + * default behaviour. All the entries + * gotten from this search have been + * added to the result list during + * LDAP_RES_SEARCH_ENTRY (see below). + * Here we set the return status to + * notfound if the result is still empty. + */ + (void) pthread_mutex_unlock(&adh->lock); + que = &(query_state->queries[qid]); + if (*que->result == NULL) + *que->rc = ADUTILS_ERR_NOTFOUND; + } + atomic_dec_32(&query_state->qinflight); + adutils_lookup_batch_unlock(&query_state); + } else { + num = adh->num_requests; + (void) pthread_mutex_unlock(&adh->lock); + idmapdlog(LOG_DEBUG, + "AD cannot find message ID (%d) " + "- %d queued requests", + msgid, num); + } + (void) ldap_msgfree(res); + ret = 0; + break; + + case LDAP_RES_SEARCH_ENTRY: + msgid = ldap_msgid(res); + if (msgid2query(adh, msgid, &query_state, &qid)) { + if (query_state->ldap_res_search_cb != NULL) { + /* + * We use the caller-provided callback + * to process the entry. + */ + query_state->ldap_res_search_cb( + adh->ld, &res, rc, qid, + query_state->ldap_res_search_argp); + (void) pthread_mutex_unlock(&adh->lock); + } else { + /* + * No callback. We fallback to our + * default behaviour. This entry + * will be added to the result list. + */ + que = &(query_state->queries[qid]); + rc = add_entry(adh, que, res); + (void) pthread_mutex_unlock(&adh->lock); + if (rc < 0) { + idmapdlog(LOG_DEBUG, + "Failed to queue entry by " + "message ID (%d) " + "- %d queued requests", + msgid, num); + } + } + adutils_lookup_batch_unlock(&query_state); + } else { + num = adh->num_requests; + (void) pthread_mutex_unlock(&adh->lock); + idmapdlog(LOG_DEBUG, + "AD cannot find message ID (%d) " + "- %d queued requests", + msgid, num); + } + (void) ldap_msgfree(res); + ret = 0; + break; + + case LDAP_RES_SEARCH_REFERENCE: + /* + * We have no need for these at the moment. Eventually, + * when we query things that we can't expect to find in + * the Global Catalog then we'll need to learn to follow + * references. + */ + (void) pthread_mutex_unlock(&adh->lock); + (void) ldap_msgfree(res); + ret = 0; + break; + + default: + /* timeout or error; treat the same */ + (void) pthread_mutex_unlock(&adh->lock); + ret = -1; + break; + } + + return (ret); +} + +/* + * This routine decreament the reference count of the + * adutils_query_state_t + */ +static void +adutils_lookup_batch_unlock(adutils_query_state_t **state) +{ + /* + * Decrement reference count with qstatelock locked + */ + (void) pthread_mutex_lock(&qstatelock); + (*state)->ref_cnt--; + /* + * If there are no references wakup the allocating thread + */ + if ((*state)->ref_cnt <= 1) + (void) pthread_cond_signal(&(*state)->cv); + (void) pthread_mutex_unlock(&qstatelock); + *state = NULL; +} + +/* + * This routine frees the adutils_query_state_t structure + * If the reference count is greater than 1 it waits + * for the other threads to finish using it. + */ +void +adutils_lookup_batch_release(adutils_query_state_t **state) +{ + adutils_query_state_t **p; + int i; + + if (state == NULL || *state == NULL) + return; + + /* + * Set state to dead to stop further operations. + * Wait for reference count with qstatelock locked + * to get to one. + */ + (void) pthread_mutex_lock(&qstatelock); + (*state)->qdead = 1; + while ((*state)->ref_cnt > 1) { + (void) pthread_cond_wait(&(*state)->cv, &qstatelock); + } + + /* Remove this state struct from the list of state structs */ + for (p = &qstatehead; *p != NULL; p = &(*p)->next) { + if (*p == (*state)) { + *p = (*state)->next; + break; + } + } + (void) pthread_mutex_unlock(&qstatelock); + (void) pthread_cond_destroy(&(*state)->cv); + release_conn((*state)->qadh); + + /* Clear results for queries that failed */ + for (i = 0; i < (*state)->qcount; i++) { + if (*(*state)->queries[i].rc != ADUTILS_SUCCESS) { + adutils_freeresult((*state)->queries[i].result); + } + } + free((*state)->default_domain); + free((*state)->basedn); + free(*state); + *state = NULL; +} + + +/* + * This routine waits for other threads using the + * adutils_query_state_t structure to finish. + * If the reference count is greater than 1 it waits + * for the other threads to finish using it. + */ +static +void +adutils_lookup_batch_wait(adutils_query_state_t *state) +{ + /* + * Set state to dead to stop further operation. + * stating. + * Wait for reference count to get to one + * with qstatelock locked. + */ + (void) pthread_mutex_lock(&qstatelock); + state->qdead = 1; + while (state->ref_cnt > 1) { + (void) pthread_cond_wait(&state->cv, &qstatelock); + } + (void) pthread_mutex_unlock(&qstatelock); +} + +/* + * Process active queries in the AD lookup batch and then finalize the + * result. + */ +adutils_rc +adutils_lookup_batch_end(adutils_query_state_t **state) +{ + int rc = LDAP_SUCCESS; + adutils_rc ad_rc = ADUTILS_SUCCESS; + struct timeval tv; + + tv.tv_sec = ADUTILS_SEARCH_TIMEOUT; + tv.tv_usec = 0; + + /* Process results until done or until timeout, if given */ + while ((*state)->qinflight > 0) { + if ((rc = get_adobject_batch((*state)->qadh, + &tv)) != 0) + break; + } + (*state)->qdead = 1; + /* Wait for other threads processing search result to finish */ + adutils_lookup_batch_wait(*state); + if (rc == -1 || (*state)->qinflight != 0) + ad_rc = ADUTILS_ERR_RETRIABLE_NET_ERR; + adutils_lookup_batch_release(state); + return (ad_rc); +} + +const char * +adutils_lookup_batch_getdefdomain(adutils_query_state_t *state) +{ + return (state->default_domain); +} + +/* + * Send one prepared search, queue up msgid, process what results are + * available + */ +adutils_rc +adutils_lookup_batch_add(adutils_query_state_t *state, + const char *filter, const char **attrs, const char *edomain, + adutils_result_t **result, adutils_rc *rc) +{ + adutils_rc retcode = ADUTILS_SUCCESS; + int lrc, qid; + int num; + int dead; + struct timeval tv; + adutils_q_t *q; + + qid = atomic_inc_32_nv(&state->qlastsent) - 1; + q = &(state->queries[qid]); + + /* + * Remember the expected domain so we can check the results + * against it + */ + q->edomain = edomain; + + /* Remember where to put the results */ + q->result = result; + q->rc = rc; + + /* + * Provide sane defaults for the results in case we never hear + * back from the DS before closing the connection. + */ + *rc = ADUTILS_ERR_RETRIABLE_NET_ERR; + if (result != NULL) + *result = NULL; + + /* Check the number of queued requests first */ + tv.tv_sec = ADUTILS_SEARCH_TIMEOUT; + tv.tv_usec = 0; + while (!state->qadh->dead && + state->qadh->num_requests > state->qadh->max_requests) { + if (get_adobject_batch(state->qadh, &tv) != 0) + break; + } + + /* Send this lookup, don't wait for a result here */ + lrc = LDAP_SUCCESS; + (void) pthread_mutex_lock(&state->qadh->lock); + + if (!state->qadh->dead) { + state->qadh->idletime = time(NULL); + lrc = ldap_search_ext(state->qadh->ld, state->basedn, + LDAP_SCOPE_SUBTREE, filter, (char **)attrs, + 0, NULL, NULL, NULL, -1, &q->msgid); + + if (lrc == LDAP_SUCCESS) { + state->qadh->num_requests++; + } else if (lrc == LDAP_BUSY || lrc == LDAP_UNAVAILABLE || + lrc == LDAP_CONNECT_ERROR || lrc == LDAP_SERVER_DOWN || + lrc == LDAP_UNWILLING_TO_PERFORM) { + retcode = ADUTILS_ERR_RETRIABLE_NET_ERR; + state->qadh->dead = 1; + } else { + retcode = ADUTILS_ERR_OTHER; + state->qadh->dead = 1; + } + } + dead = state->qadh->dead; + num = state->qadh->num_requests; + (void) pthread_mutex_unlock(&state->qadh->lock); + + if (dead) { + if (lrc != LDAP_SUCCESS) + idmapdlog(LOG_DEBUG, + "AD ldap_search_ext error (%s) " + "- %d queued requests", + ldap_err2string(lrc), num); + return (retcode); + } + + atomic_inc_32(&state->qinflight); + + /* + * Reap as many requests as we can _without_ waiting to prevent + * any possible TCP socket buffer starvation deadlocks. + */ + (void) memset(&tv, 0, sizeof (tv)); + while (get_adobject_batch(state->qadh, &tv) == 0) + ; + + return (ADUTILS_SUCCESS); +} + +/* + * Single AD lookup request implemented on top of the batch API. + */ +adutils_rc +adutils_lookup(adutils_ad_t *ad, const char *filter, const char **attrs, + const char *domain, adutils_result_t **result) +{ + adutils_rc rc, brc; + adutils_query_state_t *qs; + + rc = adutils_lookup_batch_start(ad, 1, NULL, NULL, &qs); + if (rc != ADUTILS_SUCCESS) + return (rc); + + rc = adutils_lookup_batch_add(qs, filter, attrs, domain, result, &brc); + if (rc != ADUTILS_SUCCESS) { + adutils_lookup_batch_release(&qs); + return (rc); + } + + rc = adutils_lookup_batch_end(&qs); + if (rc != ADUTILS_SUCCESS) + return (rc); + return (brc); +} diff --git a/usr/src/lib/libadutils/common/adutils_impl.h b/usr/src/lib/libadutils/common/adutils_impl.h new file mode 100644 index 0000000000..847d5e384d --- /dev/null +++ b/usr/src/lib/libadutils/common/adutils_impl.h @@ -0,0 +1,146 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _ADUTILS_IMPL_H +#define _ADUTILS_IMPL_H + +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <ldap.h> +#include <pthread.h> +#include "addisc.h" +#include "idmap_priv.h" +#include "idmap_prot.h" +#include "libadutils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ADUTILS_SEARCH_TIMEOUT 3 +#define ADUTILS_LDAP_OPEN_TIMEOUT 1 + +typedef struct adutils_sid { + uchar_t version; + uchar_t sub_authority_count; + uint64_t authority; /* really, 48-bits */ + uint32_t sub_authorities[ADUTILS_SID_MAX_SUB_AUTHORITIES]; +} adutils_sid_t; + +struct adutils_host; + + +/* A set of DSs for a given AD partition */ +struct adutils_ad { + char *dflt_w2k_dom; /* used to qualify bare names */ + pthread_mutex_t lock; + uint32_t ref; + struct adutils_host *last_adh; + adutils_ad_partition_t partition; /* Data or global catalog? */ +}; + +typedef struct adutils_attr { + char *attr_name; + uint_t num_values; + char **attr_values; +} adutils_attr_t; + +/* typedef in libadutils.h */ +struct adutils_entry { + uint_t num_nvpairs; + adutils_attr_t *attr_nvpairs; + struct adutils_entry *next; +}; + +/* typedef in libadutils.h */ +struct adutils_result { + uint_t num_entries; + adutils_entry_t *entries; +}; + +/* A single DS */ +typedef struct adutils_host { + struct adutils_host *next; + struct adutils_ad *owner; /* ad_t to which this belongs */ + pthread_mutex_t lock; + LDAP *ld; /* LDAP connection */ + uint32_t ref; /* ref count */ + time_t idletime; /* time since last activity */ + int dead; /* error on LDAP connection */ + /* + * Used to distinguish between different instances of LDAP + * connections to this same DS. We need this so we never mix up + * results for a given msgID from one connection with those of + * another earlier connection where two batch state structures + * share this adutils_host object but used different LDAP connections + * to send their LDAP searches. + */ + uint64_t generation; + + /* LDAP DS info */ + char *host; + int port; + + /* hardwired to SASL GSSAPI only for now */ + char *saslmech; + unsigned saslflags; + + /* Number of outstanding search requests */ + uint32_t max_requests; + uint32_t num_requests; +} adutils_host_t; + +/* A place to put the results of a batched (async) query */ +typedef struct adutils_q { + const char *edomain; /* expected domain name */ + struct adutils_result **result; /* The LDAP search result */ + adutils_rc *rc; + int msgid; /* LDAP message ID */ +} adutils_q_t; + +/* Batch context structure */ +struct adutils_query_state { + struct adutils_query_state *next; + int qcount; /* how many queries */ + int ref_cnt; /* reference count */ + pthread_cond_t cv; /* Condition wait variable */ + uint32_t qlastsent; + uint32_t qinflight; /* how many queries in flight */ + uint16_t qdead; /* oops, lost LDAP connection */ + adutils_host_t *qadh; /* LDAP connection */ + uint64_t qadh_gen; /* same as qadh->generation */ + adutils_ldap_res_search_cb ldap_res_search_cb; + void *ldap_res_search_argp; + char *default_domain; + char *basedn; + adutils_q_t queries[1]; /* array of query results */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _ADUTILS_IMPL_H */ diff --git a/usr/src/lib/libadutils/common/libadutils.h b/usr/src/lib/libadutils/common/libadutils.h new file mode 100644 index 0000000000..9a6d82a0b2 --- /dev/null +++ b/usr/src/lib/libadutils/common/libadutils.h @@ -0,0 +1,177 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LIBADUTILS_H +#define _LIBADUTILS_H + +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <rpc/rpc.h> +#include <ldap.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define ADUTILS_DEF_NUM_RETRIES 2 +#define ADUTILS_SID_MAX_SUB_AUTHORITIES 15 +#define ADUTILS_MAXBINSID\ + (1 + 1 + 6 + (ADUTILS_SID_MAX_SUB_AUTHORITIES * 4)) +#define ADUTILS_MAXHEXBINSID (ADUTILS_MAXBINSID * 3) + +typedef struct adutils_ad adutils_ad_t; +typedef struct adutils_entry adutils_entry_t; +typedef struct adutils_result adutils_result_t; +typedef struct adutils_ctx adutils_ctx_t; +typedef struct adutils_query_state adutils_query_state_t; + +/* + * Typedef for callback routine for adutils_lookup_batch_start. + * This callback routine is used to process the result of + * ldap_result(3LDAP). + * ld - LDAP handle used by ldap_result(3LDAP) + * res - Entry returned by ldap_result(3LDAP) + * rc - Return value of ldap_result(3LDAP) + * qid - Query ID that corresponds to the result. + * argp - Argument passed by the caller at the time + * of adutils_lookup_batch_start. + */ +typedef void (*adutils_ldap_res_search_cb)(LDAP *ld, LDAPMessage **res, + int rc, int qid, void *argp); + +typedef enum { + ADUTILS_SUCCESS = 0, + ADUTILS_ERR_INTERNAL = -10000, + ADUTILS_ERR_OTHER, + ADUTILS_ERR_NOTFOUND, + ADUTILS_ERR_RETRIABLE_NET_ERR, + ADUTILS_ERR_MEMORY, + ADUTILS_ERR_DOMAIN +} adutils_rc; + +/* + * We use the port numbers for normal LDAP and global catalog LDAP as + * the enum values for this enumeration. Clever? Silly? You decide. + * Although we never actually use these enum values as port numbers and + * never will, so this is just cute. + */ +typedef enum adutils_ad_partition { + ADUTILS_AD_DATA = 389, + ADUTILS_AD_GLOBAL_CATALOG = 3268 +} adutils_ad_partition_t; + + +/* + * adutils interfaces: + * + * - an adutils_ad_t represents an AD partition + * - a DS (hostname + port, if port != 0) can be added/removed from an + * adutils_ad_t + * - an adutils_ad_t can be allocated, ref'ed and released; last release + * releases resources + * + * + * adutils_lookup_batch_xxx interfaces: + * + * These interfaces allow the caller to batch AD lookup requests. The + * batched requests are processed asynchronously. The actual lookup + * is currently implement using libldap's ldap_search_ext(3LDAP) and + * ldap_result(3LDAP) APIs. + * + * Example: + * adutils_query_state_t *qs; + * adutils_lookup_batch_start(..., &qs); + * for each request { + * rc = adutils_lookup_batch_add(qs, ...); + * if (rc != success) + * break; + * } + * if (rc == success) + * adutils_lookup_batch_end(&qs); + * else + * adutils_lookup_batch_release(&qs); + * + * The adutils_lookup_batch_start interface allows the caller to pass + * in a callback function that's invoked when ldap_result() returns + * LDAP_RES_SEARCH_RESULT and LDAP_RES_SEARCH_ENTRY for each request. + * + * If no callback is provided then adutils batch API falls back to its + * default behaviour which is: + * For LDAP_RES_SEARCH_ENTRY, add the entry to the entry set. + * For LDAP_RES_SEARCH_RESULT, set return code to + * ADUTILS_ERR_NOTFOUND if the entry set is empty. + * + * See $SRC/cmd/idmap/idmapd/adutils.c for an example of + * non-default callback routine. + * + */ + +extern adutils_rc adutils_ad_alloc(adutils_ad_t **new_ad, + const char *default_domain, + adutils_ad_partition_t part); +extern void adutils_ad_free(adutils_ad_t **ad); +extern adutils_rc adutils_add_ds(adutils_ad_t *ad, + const char *host, int port); +extern void adutils_set_log(int pri, bool_t syslog, + bool_t degraded); +extern void adutils_freeresult(adutils_result_t **result); +extern adutils_rc adutils_lookup(adutils_ad_t *ad, + const char *searchfilter, + const char **attrs, const char *domain, + adutils_result_t **result); +extern char **adutils_getattr(const adutils_entry_t *entry, + const char *attrname); +extern const adutils_entry_t *adutils_getfirstentry( + adutils_result_t *result); +extern int adutils_txtsid2hexbinsid(const char *txt, + const uint32_t *rid, + char *hexbinsid, int hexbinsidlen); +extern char *adutils_bv_name2str(BerValue *bval); +extern char *adutils_bv_objsid2sidstr(BerValue *bval, + uint32_t *rid); +extern void adutils_reap_idle_connections(void); +extern char *adutils_dn2dns(const char *dn); +extern adutils_rc adutils_lookup_batch_start(adutils_ad_t *ad, + int nqueries, + adutils_ldap_res_search_cb ldap_res_search_cb, + void *ldap_res_search_argp, + adutils_query_state_t **state); +extern adutils_rc adutils_lookup_batch_add(adutils_query_state_t *state, + const char *filter, const char **attrs, + const char *edomain, adutils_result_t **result, + adutils_rc *rc); +extern adutils_rc adutils_lookup_batch_end( + adutils_query_state_t **state); +extern void adutils_lookup_batch_release( + adutils_query_state_t **state); +extern const char *adutils_lookup_batch_getdefdomain( + adutils_query_state_t *state); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBADUTILS_H */ diff --git a/usr/src/lib/libadutils/common/llib-ladutils b/usr/src/lib/libadutils/common/llib-ladutils new file mode 100644 index 0000000000..e0f09dfe25 --- /dev/null +++ b/usr/src/lib/libadutils/common/llib-ladutils @@ -0,0 +1,29 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +#include "adutils_impl.h" diff --git a/usr/src/lib/libadutils/common/mapfile-vers b/usr/src/lib/libadutils/common/mapfile-vers new file mode 100644 index 0000000000..07d0065906 --- /dev/null +++ b/usr/src/lib/libadutils/common/mapfile-vers @@ -0,0 +1,48 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +SUNWprivate { + global: + adutils_txtsid2hexbinsid; + adutils_bv_name2str; + adutils_bv_objsid2sidstr; + adutils_getattr; + adutils_getfirstentry; + adutils_freeresult; + adutils_lookup; + adutils_lookup_batch_start; + adutils_lookup_batch_add; + adutils_lookup_batch_end; + adutils_lookup_batch_release; + adutils_lookup_batch_getdefdomain; + adutils_dn2dns; + adutils_reap_idle_connections; + adutils_ad_alloc; + adutils_ad_free; + adutils_add_ds; + adutils_set_log; + local: + *; +}; diff --git a/usr/src/lib/libadutils/i386/Makefile b/usr/src/lib/libadutils/i386/Makefile new file mode 100644 index 0000000000..c7cca61bea --- /dev/null +++ b/usr/src/lib/libadutils/i386/Makefile @@ -0,0 +1,28 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libadutils/sparc/Makefile b/usr/src/lib/libadutils/sparc/Makefile new file mode 100644 index 0000000000..c7cca61bea --- /dev/null +++ b/usr/src/lib/libadutils/sparc/Makefile @@ -0,0 +1,28 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libadutils/sparcv9/Makefile b/usr/src/lib/libadutils/sparcv9/Makefile new file mode 100644 index 0000000000..39ae44e24b --- /dev/null +++ b/usr/src/lib/libadutils/sparcv9/Makefile @@ -0,0 +1,29 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/libc/port/gen/getgrnam_r.c b/usr/src/lib/libc/port/gen/getgrnam_r.c index 52e1b6d0e7..7e610cd8f2 100644 --- a/usr/src/lib/libc/port/gen/getgrnam_r.c +++ b/usr/src/lib/libc/port/gen/getgrnam_r.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include "lint.h" #include <mtlib.h> #include <sys/types.h> @@ -318,6 +316,7 @@ str2group(const char *instr, int lenstr, void *ent, char *buffer, int buflen) char *p, *next; int black_magic; /* "+" or "-" entry */ char **memlist, **limit; + ulong_t tmp; if (lenstr + 1 > buflen) return (NSS_STR_PARSE_ERANGE); @@ -378,16 +377,15 @@ str2group(const char *instr, int lenstr, void *ent, char *buffer, int buflen) return (NSS_STR_PARSE_PARSE); } if (!black_magic) { - group->gr_gid = (gid_t)strtol(p, &next, 10); + tmp = strtoul(p, &next, 10); if (next == p) { /* gid field should be nonempty */ return (NSS_STR_PARSE_PARSE); } - /* - * gids should be in the range 0 .. MAXUID - */ - if (group->gr_gid > MAXUID) + if (group->gr_gid >= UINT32_MAX) group->gr_gid = GID_NOBODY; + else + group->gr_gid = (gid_t)tmp; } if (*next++ != ':') { /* Parse error, even for a '+' entry (which should have */ diff --git a/usr/src/lib/libc/port/gen/getpwnam_r.c b/usr/src/lib/libc/port/gen/getpwnam_r.c index 7ff4d21f0d..7b7c417de5 100644 --- a/usr/src/lib/libc/port/gen/getpwnam_r.c +++ b/usr/src/lib/libc/port/gen/getpwnam_r.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include "lint.h" #include <sys/types.h> #include <pwd.h> @@ -244,6 +242,7 @@ str2passwd(const char *instr, int lenstr, void *ent, char *buffer, int buflen) struct passwd *passwd = (struct passwd *)ent; char *p, *next; int black_magic; /* "+" or "-" entry */ + ulong_t tmp; if (lenstr + 1 > buflen) return (NSS_STR_PARSE_ERANGE); @@ -310,7 +309,14 @@ str2passwd(const char *instr, int lenstr, void *ent, char *buffer, int buflen) return (NSS_STR_PARSE_PARSE); } if (!black_magic) { - passwd->pw_uid = (uid_t)strtol(p, &next, 10); + /* + * strtoul returns unsigned long which is + * 8 bytes on a 64-bit system. We don't want + * to assign it directly to passwd->pw_uid + * which is 4 bytes or else we will end up + * truncating the value. + */ + tmp = strtoul(p, &next, 10); if (next == p) { /* uid field should be nonempty */ return (NSS_STR_PARSE_PARSE); @@ -321,11 +327,13 @@ str2passwd(const char *instr, int lenstr, void *ent, char *buffer, int buflen) * than 60001 (the rfs limit). If it met either of * these conditions, the uid was translated to 60001. * - * Now we just check for negative uids; anything else + * Now we just check for -1 (UINT32_MAX); anything else * is administrative policy */ - if (passwd->pw_uid > MAXUID) + if (tmp >= UINT32_MAX) passwd->pw_uid = UID_NOBODY; + else + passwd->pw_uid = (uid_t)tmp; } if (*next++ != ':') { if (black_magic) @@ -341,17 +349,19 @@ str2passwd(const char *instr, int lenstr, void *ent, char *buffer, int buflen) return (NSS_STR_PARSE_PARSE); } if (!black_magic) { - passwd->pw_gid = (gid_t)strtol(p, &next, 10); + tmp = strtoul(p, &next, 10); if (next == p) { /* gid field should be nonempty */ return (NSS_STR_PARSE_PARSE); } /* - * gid should be non-negative; anything else + * gid should not be -1; anything else * is administrative policy. */ - if (passwd->pw_gid > MAXUID) + if (passwd->pw_gid >= UINT32_MAX) passwd->pw_gid = GID_NOBODY; + else + passwd->pw_gid = (gid_t)tmp; } if (*next++ != ':') { if (black_magic) diff --git a/usr/src/lib/libc/port/gen/putpwent.c b/usr/src/lib/libc/port/gen/putpwent.c index efc482c7f4..b4c1928902 100644 --- a/usr/src/lib/libc/port/gen/putpwent.c +++ b/usr/src/lib/libc/port/gen/putpwent.c @@ -27,8 +27,6 @@ /* Copyright (c) 1988 AT&T */ /* All Rights Reserved */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * format a password file entry */ @@ -55,7 +53,7 @@ putpwent(const struct passwd *p, FILE *f) p->pw_dir ? p->pw_dir : "", p->pw_shell ? p->pw_shell : ""); } else { /* "normal case" */ - (void) fprintf(f, ":%d:%d:%s:%s:%s", + (void) fprintf(f, ":%u:%u:%s:%s:%s", p->pw_uid, p->pw_gid, p->pw_gecos, diff --git a/usr/src/lib/libidmap/common/idmap_api.c b/usr/src/lib/libidmap/common/idmap_api.c index ef6079f0c0..69ed433d98 100644 --- a/usr/src/lib/libidmap/common/idmap_api.c +++ b/usr/src/lib/libidmap/common/idmap_api.c @@ -124,7 +124,7 @@ idmap_free(void *ptr) } -#define MIN_STACK_NEEDS 16384 +#define MIN_STACK_NEEDS 65536 /* * Create and Initialize idmap client handle for rpc/doors @@ -163,16 +163,19 @@ idmap_init(idmap_handle_t **handle) /* stack grows down */ sendsz = ((char *)&sendsz - (char *)st.ss_sp); - /* - * Take much of the stack space left, divided by two, - * but leave enough for our needs (just a guess!), and - * if we can't, then roll the dice. - */ - sendsz = RNDUP(sendsz / 2); - if (sendsz < MIN_STACK_NEEDS) + if (sendsz <= MIN_STACK_NEEDS) { sendsz = 0; /* RPC call may fail */ - else if (sendsz > IDMAP_MAX_DOOR_RPC) - sendsz = IDMAP_MAX_DOOR_RPC; + } else { + /* Leave 64Kb (just a guess) for our needs */ + sendsz -= MIN_STACK_NEEDS; + + /* Divide the stack space left by two */ + sendsz = RNDUP(sendsz / 2); + + /* Limit sendsz to 256KB */ + if (sendsz > IDMAP_MAX_DOOR_RPC) + sendsz = IDMAP_MAX_DOOR_RPC; + } } clnt = clnt_door_create(IDMAP_PROG, IDMAP_V1, sendsz); diff --git a/usr/src/lib/nsswitch/Makefile b/usr/src/lib/nsswitch/Makefile index dda261d132..76c882d0f9 100644 --- a/usr/src/lib/nsswitch/Makefile +++ b/usr/src/lib/nsswitch/Makefile @@ -19,9 +19,7 @@ # CDDL HEADER END # # -#ident "%Z%%M% %I% %E% SMI" -# -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # lib/nsswitch/Makefile @@ -29,7 +27,7 @@ include $(SRC)/Makefile.master -SUBDIRS= files nis nisplus compat dns ldap user mdns +SUBDIRS= files nis nisplus compat dns ldap user mdns ad all:= TARGET= all clean:= TARGET= clean diff --git a/usr/src/lib/nsswitch/ad/Makefile b/usr/src/lib/nsswitch/ad/Makefile new file mode 100644 index 0000000000..284c347307 --- /dev/null +++ b/usr/src/lib/nsswitch/ad/Makefile @@ -0,0 +1,46 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END + + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# lib/nsswitch/ad/Makefile +# + +include ../../../Makefile.master + +FILES_SUBDIRS= $(MACH) $(BUILD64) $(MACH64) + +all:= TARGET= all +clean:= TARGET= clean +clobber:= TARGET= clobber +install:= TARGET= install +lint:= TARGET= lint + +.KEEP_STATE: + +all clean clobber install lint: $(FILES_SUBDIRS) + +$(MACH) $(MACH64): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/nsswitch/ad/Makefile.com b/usr/src/lib/nsswitch/ad/Makefile.com new file mode 100644 index 0000000000..0b50f606ac --- /dev/null +++ b/usr/src/lib/nsswitch/ad/Makefile.com @@ -0,0 +1,39 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# lib/nsswitch/ad/Makefile.com + +LIBRARY = libnss_ad.a +VERS = .1 +OBJECTS = getpwnam.o getspent.o getgrent.o ad_common.o +IDMAP_PROT_DIR = $(SRC)/head/rpcsvc + +# include common nsswitch library definitions. +include ../../Makefile.com + +CPPFLAGS += -I../../../libadutils/common -I../../../libidmap/common \ + -I$(IDMAP_PROT_DIR) +LDLIBS += -ladutils -lidmap +DYNLIB1 = nss_ad.so$(VERS) diff --git a/usr/src/lib/nsswitch/ad/amd64/Makefile b/usr/src/lib/nsswitch/ad/amd64/Makefile new file mode 100644 index 0000000000..6e71c56dcc --- /dev/null +++ b/usr/src/lib/nsswitch/ad/amd64/Makefile @@ -0,0 +1,32 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com +include $(SRC)/lib/Makefile.lib.64 + +LIBS = $(DYNLIB1) + +include ../../Makefile.targ + +install: all $(ROOT64DYNLIB) diff --git a/usr/src/lib/nsswitch/ad/common/ad_common.c b/usr/src/lib/nsswitch/ad/common/ad_common.c new file mode 100644 index 0000000000..9265defe13 --- /dev/null +++ b/usr/src/lib/nsswitch/ad/common/ad_common.c @@ -0,0 +1,534 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <malloc.h> +#include <synch.h> +#include <syslog.h> +#include <rpcsvc/ypclnt.h> +#include <rpcsvc/yp_prot.h> +#include <pthread.h> +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include <sys/stat.h> +#include <assert.h> +#include "ad_common.h" + +static pthread_mutex_t statelock = PTHREAD_MUTEX_INITIALIZER; +static nssad_state_t state = {0}; + +static void +nssad_cfg_free_props(nssad_prop_t *props) +{ + if (props->domain_name != NULL) { + free(props->domain_name); + props->domain_name = NULL; + } + if (props->domain_controller != NULL) { + free(props->domain_controller); + props->domain_controller = NULL; + } +} + +static int +nssad_cfg_discover_props(const char *domain, ad_disc_t ad_ctx, + nssad_prop_t *props) +{ + ad_disc_refresh(ad_ctx); + if (ad_disc_set_DomainName(ad_ctx, domain) != 0) + return (-1); + if (props->domain_controller == NULL) + props->domain_controller = + ad_disc_get_DomainController(ad_ctx, AD_DISC_PREFER_SITE); + return (0); +} + +static int +nssad_cfg_reload_ad(nssad_prop_t *props, adutils_ad_t **ad) +{ + int i; + adutils_ad_t *new; + + if (props->domain_controller == NULL || + props->domain_controller[0].host[0] == '\0') + return (0); + if (adutils_ad_alloc(&new, props->domain_name, + ADUTILS_AD_DATA) != ADUTILS_SUCCESS) + return (-1); + for (i = 0; props->domain_controller[i].host[0] != '\0'; i++) { + if (adutils_add_ds(new, + props->domain_controller[i].host, + props->domain_controller[i].port) != ADUTILS_SUCCESS) { + adutils_ad_free(&new); + return (-1); + } + } + + if (*ad != NULL) + adutils_ad_free(ad); + *ad = new; + return (0); +} + +static +int +update_dirs(idmap_ad_disc_ds_t **value, idmap_ad_disc_ds_t **new) +{ + if (*value == *new) + return (0); + + if (*value != NULL && *new != NULL && + ad_disc_compare_ds(*value, *new) == 0) { + free(*new); + *new = NULL; + return (0); + } + + if (*value) + free(*value); + *value = *new; + *new = NULL; + return (1); +} + +static +int +nssad_cfg_refresh(nssad_cfg_t *cp) +{ + nssad_prop_t props; + + (void) ad_disc_SubnetChanged(cp->ad_ctx); + (void) memset(&props, 0, sizeof (props)); + if (nssad_cfg_discover_props(cp->props.domain_name, cp->ad_ctx, + &props) < 0) + return (-1); + if (update_dirs(&cp->props.domain_controller, + &props.domain_controller)) { + if (cp->props.domain_controller != NULL && + cp->props.domain_controller[0].host[0] != '\0') + (void) nssad_cfg_reload_ad(&cp->props, &cp->ad); + } + return (0); +} + +static void +nssad_cfg_destroy(nssad_cfg_t *cp) +{ + if (cp != NULL) { + (void) pthread_rwlock_destroy(&cp->lock); + ad_disc_fini(cp->ad_ctx); + nssad_cfg_free_props(&cp->props); + adutils_ad_free(&cp->ad); + free(cp); + } +} + +static nssad_cfg_t * +nssad_cfg_create(const char *domain) +{ + nssad_cfg_t *cp; + + if ((cp = calloc(1, sizeof (*cp))) == NULL) + return (NULL); + if (pthread_rwlock_init(&cp->lock, NULL) != 0) { + free(cp); + return (NULL); + } + adutils_set_log(-1, TRUE, FALSE); + if ((cp->ad_ctx = ad_disc_init()) == NULL) + goto errout; + if ((cp->props.domain_name = strdup(domain)) == NULL) + goto errout; + if (nssad_cfg_discover_props(domain, cp->ad_ctx, &cp->props) < 0) + goto errout; + if (nssad_cfg_reload_ad(&cp->props, &cp->ad) < 0) + goto errout; + return (cp); +errout: + nssad_cfg_destroy(cp); + return (NULL); +} + +#define hex_char(n) "0123456789abcdef"[n & 0xf] + +int +_ldap_filter_name(char *filter_name, const char *name, int filter_name_size) +{ + char *end = filter_name + filter_name_size; + char c; + + for (; *name; name++) { + c = *name; + switch (c) { + case '*': + case '(': + case ')': + case '\\': + if (end <= filter_name + 3) + return (-1); + *filter_name++ = '\\'; + *filter_name++ = hex_char(c >> 4); + *filter_name++ = hex_char(c & 0xf); + break; + default: + if (end <= filter_name + 1) + return (-1); + *filter_name++ = c; + break; + } + } + if (end <= filter_name) + return (-1); + *filter_name = '\0'; + return (0); +} + +static +nss_status_t +map_adrc2nssrc(adutils_rc adrc) +{ + if (adrc == ADUTILS_SUCCESS) + return ((nss_status_t)NSS_SUCCESS); + if (adrc == ADUTILS_ERR_NOTFOUND) + errno = 0; + return ((nss_status_t)NSS_NOTFOUND); +} + +/* ARGSUSED */ +nss_status_t +_nss_ad_marshall_data(ad_backend_ptr be, nss_XbyY_args_t *argp) +{ + int stat; + + if (argp->buf.result == NULL) { + /* + * This suggests that the process (e.g. nscd) expects + * nssad to return the data in native file format in + * argp->buf.buffer i.e. no need to marshall the data. + */ + argp->returnval = argp->buf.buffer; + argp->returnlen = strlen(argp->buf.buffer); + return ((nss_status_t)NSS_STR_PARSE_SUCCESS); + } + + if (argp->str2ent == NULL) + return ((nss_status_t)NSS_STR_PARSE_PARSE); + + stat = (*argp->str2ent)(be->buffer, be->buflen, + argp->buf.result, argp->buf.buffer, argp->buf.buflen); + + if (stat == NSS_STR_PARSE_SUCCESS) { + argp->returnval = argp->buf.result; + argp->returnlen = 1; /* irrelevant */ + } + return ((nss_status_t)stat); +} + +nss_status_t +_nss_ad_sanitize_status(ad_backend_ptr be, nss_XbyY_args_t *argp, + nss_status_t stat) +{ + if (be->buffer != NULL) { + free(be->buffer); + be->buffer = NULL; + be->buflen = 0; + be->db_type = NSS_AD_DB_NONE; + } + + if (stat == NSS_STR_PARSE_SUCCESS) { + return ((nss_status_t)NSS_SUCCESS); + } else if (stat == NSS_STR_PARSE_PARSE) { + argp->returnval = 0; + return ((nss_status_t)NSS_NOTFOUND); + } else if (stat == NSS_STR_PARSE_ERANGE) { + argp->erange = 1; + return ((nss_status_t)NSS_NOTFOUND); + } + return ((nss_status_t)NSS_UNAVAIL); +} + +/* ARGSUSED */ +static +nssad_cfg_t * +get_cfg(const char *domain) +{ + nssad_cfg_t *cp, *lru, *prev; + + /* + * Note about the queue: + * + * The queue is used to hold our per domain + * configs. The queue is limited to CFG_QUEUE_MAX_SIZE. + * If the queue increases beyond that point we toss + * out the LRU entry. The entries are inserted into + * the queue at state.qtail and the LRU entry is + * removed from state.qhead. state.qnext points + * from the qtail to the qhead. Everytime a config + * is accessed it is moved to qtail. + */ + + (void) pthread_mutex_lock(&statelock); + + for (cp = state.qtail, prev = NULL; cp != NULL; + prev = cp, cp = cp->qnext) { + if (cp->props.domain_name == NULL || + strcasecmp(cp->props.domain_name, domain) != 0) + continue; + + /* Found config for the given domain. */ + + if (state.qtail != cp) { + /* + * Move the entry to the tail of the queue. + * This way the LRU entry can be found at + * the head of the queue. + */ + prev->qnext = cp->qnext; + if (state.qhead == cp) + state.qhead = prev; + cp->qnext = state.qtail; + state.qtail = cp; + } + + if (ad_disc_get_TTL(cp->ad_ctx) == 0) { + /* + * If there are expired items in the + * config, grab the write lock and + * refresh the config. + */ + (void) pthread_rwlock_wrlock(&cp->lock); + if (nssad_cfg_refresh(cp) < 0) { + (void) pthread_rwlock_unlock(&cp->lock); + (void) pthread_mutex_unlock(&statelock); + return (NULL); + } + (void) pthread_rwlock_unlock(&cp->lock); + } + + /* Return the config found */ + (void) pthread_rwlock_rdlock(&cp->lock); + (void) pthread_mutex_unlock(&statelock); + return (cp); + } + + /* Create new config entry for the domain */ + if ((cp = nssad_cfg_create(domain)) == NULL) { + (void) pthread_mutex_unlock(&statelock); + return (NULL); + } + + /* Add it to the queue */ + state.qcount++; + if (state.qtail == NULL) { + state.qtail = state.qhead = cp; + (void) pthread_rwlock_rdlock(&cp->lock); + (void) pthread_mutex_unlock(&statelock); + return (cp); + } + cp->qnext = state.qtail; + state.qtail = cp; + + /* If the queue has exceeded its size, remove the LRU entry */ + if (state.qcount >= CFG_QUEUE_MAX_SIZE) { + /* Detach the lru entry and destroy */ + lru = state.qhead; + if (pthread_rwlock_trywrlock(&lru->lock) == 0) { + for (prev = state.qtail; prev != NULL; + prev = prev->qnext) { + if (prev->qnext != lru) + continue; + state.qhead = prev; + prev->qnext = NULL; + state.qcount--; + (void) pthread_rwlock_unlock(&lru->lock); + nssad_cfg_destroy(lru); + break; + } + (void) assert(prev != NULL); + } + } + + (void) pthread_rwlock_rdlock(&cp->lock); + (void) pthread_mutex_unlock(&statelock); + return (cp); +} + + +/* ARGSUSED */ +static +nss_status_t +ad_lookup(const char *filter, const char **attrs, + const char *domain, adutils_result_t **result) +{ + int retries = 0; + adutils_rc rc, brc; + adutils_query_state_t *qs; + nssad_cfg_t *cp; + +retry: + if ((cp = get_cfg(domain)) == NULL) + return ((nss_status_t)NSS_NOTFOUND); + + rc = adutils_lookup_batch_start(cp->ad, 1, NULL, NULL, &qs); + (void) pthread_rwlock_unlock(&cp->lock); + if (rc != ADUTILS_SUCCESS) + goto out; + + rc = adutils_lookup_batch_add(qs, filter, attrs, domain, result, &brc); + if (rc != ADUTILS_SUCCESS) { + adutils_lookup_batch_release(&qs); + goto out; + } + + rc = adutils_lookup_batch_end(&qs); + if (rc != ADUTILS_SUCCESS) + goto out; + rc = brc; + +out: + if (rc == ADUTILS_ERR_RETRIABLE_NET_ERR && + retries++ < ADUTILS_DEF_NUM_RETRIES) + goto retry; + return (map_adrc2nssrc(rc)); +} + + +/* ARGSUSED */ +nss_status_t +_nss_ad_lookup(ad_backend_ptr be, nss_XbyY_args_t *argp, + const char *database, const char *searchfilter, + const char *dname, int *try_idmap) +{ + nss_status_t stat; + + *try_idmap = 0; + + /* Clear up results if any */ + (void) adutils_freeresult(&be->result); + + /* Lookup AD */ + stat = ad_lookup(searchfilter, be->attrs, dname, &be->result); + if (stat != NSS_SUCCESS) { + argp->returnval = 0; + *try_idmap = 1; + return (stat); + } + + /* Map AD object(s) to string in native file format */ + stat = be->adobj2str(be, argp); + if (stat == NSS_STR_PARSE_SUCCESS) + stat = _nss_ad_marshall_data(be, argp); + return (_nss_ad_sanitize_status(be, argp, stat)); +} + +static +void +clean_state() +{ + nssad_cfg_t *cp, *curr; + + (void) pthread_mutex_lock(&statelock); + for (cp = state.qtail; cp != NULL; ) { + curr = cp; + cp = cp->qnext; + nssad_cfg_destroy(curr); + } + (void) memset(&state, 0, sizeof (state)); + (void) pthread_mutex_unlock(&statelock); +} + +static +void +_clean_ad_backend(ad_backend_ptr be) +{ + if (be->tablename != NULL) + free(be->tablename); + if (be->buffer != NULL) { + free(be->buffer); + be->buffer = NULL; + } + free(be); +} + + +/* + * _nss_ad_destr frees allocated memory before exiting this nsswitch shared + * backend library. This function is called before returning control back to + * nsswitch. + */ +/*ARGSUSED*/ +nss_status_t +_nss_ad_destr(ad_backend_ptr be, void *a) +{ + (void) _clean_ad_backend(be); + clean_state(); + return ((nss_status_t)NSS_SUCCESS); +} + + +/*ARGSUSED*/ +nss_status_t +_nss_ad_setent(ad_backend_ptr be, void *a) +{ + return ((nss_status_t)NSS_UNAVAIL); +} + + +/*ARGSUSED*/ +nss_status_t +_nss_ad_endent(ad_backend_ptr be, void *a) +{ + return ((nss_status_t)NSS_UNAVAIL); +} + + +/*ARGSUSED*/ +nss_status_t +_nss_ad_getent(ad_backend_ptr be, void *a) +{ + return ((nss_status_t)NSS_UNAVAIL); +} + + +nss_backend_t * +_nss_ad_constr(ad_backend_op_t ops[], int nops, char *tablename, + const char **attrs, fnf adobj2str) +{ + ad_backend_ptr be; + + if ((be = (ad_backend_ptr) calloc(1, sizeof (*be))) == NULL) + return (NULL); + if ((be->tablename = (char *)strdup(tablename)) == NULL) { + free(be); + return (NULL); + } + be->ops = ops; + be->nops = (nss_dbop_t)nops; + be->attrs = attrs; + be->adobj2str = adobj2str; + (void) memset(&state, 0, sizeof (state)); + return ((nss_backend_t *)be); +} diff --git a/usr/src/lib/nsswitch/ad/common/ad_common.h b/usr/src/lib/nsswitch/ad/common/ad_common.h new file mode 100644 index 0000000000..a6c74fba02 --- /dev/null +++ b/usr/src/lib/nsswitch/ad/common/ad_common.h @@ -0,0 +1,132 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _AD_COMMON_H +#define _AD_COMMON_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <ctype.h> +#include <nss_dbdefs.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <signal.h> +#include <idmap.h> +#include <sys/idmap.h> +#include <idmap_prot.h> +#include <idmap_priv.h> +#include "addisc.h" +#include "libadutils.h" + +#define _GROUP "group" +#define _PASSWD "passwd" +#define _SHADOW "shadow" + +#define WK_DOMAIN "BUILTIN" +#define CFG_QUEUE_MAX_SIZE 15 + +#define SEARCHFILTERLEN 256 +#define RESET_ERRNO()\ + if (errno == EINVAL)\ + errno = 0; + +/* + * Superset the nss_backend_t abstract data type. This ADT has + * been extended to include AD associated data structures. + */ + +typedef struct ad_backend *ad_backend_ptr; +typedef nss_status_t (*ad_backend_op_t)(ad_backend_ptr, void *); +typedef int (*fnf)(ad_backend_ptr be, nss_XbyY_args_t *argp); + +typedef enum { + NSS_AD_DB_NONE = 0, + NSS_AD_DB_PASSWD_BYNAME = 1, + NSS_AD_DB_PASSWD_BYUID = 2, + NSS_AD_DB_GROUP_BYNAME = 3, + NSS_AD_DB_GROUP_BYGID = 4, + NSS_AD_DB_SHADOW_BYNAME = 5 +} nss_ad_db_type_t; + +struct ad_backend { + ad_backend_op_t *ops; + nss_dbop_t nops; + char *tablename; + const char **attrs; + fnf adobj2str; + char *buffer; + int buflen; + idmap_handle_t *ih; + uid_t uid; + adutils_result_t *result; + nss_ad_db_type_t db_type; +}; + +typedef struct nssad_prop { + char *domain_name; + idmap_ad_disc_ds_t *domain_controller; +} nssad_prop_t; + +typedef struct nssad_cfg { + pthread_rwlock_t lock; + nssad_prop_t props; + ad_disc_t ad_ctx; + adutils_ad_t *ad; + struct nssad_cfg *qnext; +} nssad_cfg_t; + +typedef struct nssad_state { + nssad_cfg_t *qhead; + nssad_cfg_t *qtail; + uint32_t qcount; +} nssad_state_t; + +extern nss_status_t _nss_ad_destr(ad_backend_ptr be, void *a); +extern nss_status_t _nss_ad_endent(ad_backend_ptr be, void *a); +extern nss_status_t _nss_ad_setent(ad_backend_ptr be, void *a); +extern nss_status_t _nss_ad_getent(ad_backend_ptr be, void *a); +nss_backend_t *_nss_ad_constr(ad_backend_op_t ops[], int nops, + char *tablename, const char **attrs, fnf ldapobj2str); +extern nss_status_t _nss_ad_lookup(ad_backend_ptr be, + nss_XbyY_args_t *argp, const char *database, + const char *searchfilter, const char *dname, + int *try_idmap); +extern nss_status_t _nss_ad_marshall_data(ad_backend_ptr be, + nss_XbyY_args_t *argp); +extern nss_status_t _nss_ad_sanitize_status(ad_backend_ptr be, + nss_XbyY_args_t *argp, nss_status_t stat); +extern int _ldap_filter_name(char *filter_name, const char *name, + int filter_name_size); + + +#ifdef __cplusplus +} +#endif + +#endif /* _AD_COMMON_H */ diff --git a/usr/src/lib/nsswitch/ad/common/getgrent.c b/usr/src/lib/nsswitch/ad/common/getgrent.c new file mode 100644 index 0000000000..b5f70666ba --- /dev/null +++ b/usr/src/lib/nsswitch/ad/common/getgrent.c @@ -0,0 +1,186 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <grp.h> +#include <idmap.h> +#include "ad_common.h" + +static int +update_buffer(ad_backend_ptr be, nss_XbyY_args_t *argp, + const char *name, const char *domain, gid_t gid) +{ + int buflen; + char *buffer; + + if (domain == NULL) + domain = WK_DOMAIN; + + buflen = snprintf(NULL, 0, "%s@%s::%u:", name, domain, gid) + 1; + + if (argp->buf.result != NULL) { + buffer = be->buffer = malloc(buflen); + if (be->buffer == NULL) + return (-1); + be->buflen = buflen; + } else { + if (buflen > argp->buf.buflen) + return (-1); + buflen = argp->buf.buflen; + buffer = argp->buf.buffer; + } + + (void) snprintf(buffer, buflen, "%s@%s::%u:", name, domain, gid); + return (0); +} + +/* + * getbynam gets a group entry by name. This function constructs an ldap + * search filter using the name invocation parameter and the getgrnam search + * filter defined. Once the filter is constructed, we search for a matching + * entry and marshal the data results into struct group for the frontend + * process. The function _nss_ad_group2ent performs the data marshaling. + */ +static nss_status_t +getbynam(ad_backend_ptr be, void *a) +{ + nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; + char name[SEARCHFILTERLEN]; + char *dname; + nss_status_t stat; + idmap_stat idmaprc; + gid_t gid; + int is_user, is_wuser; + idmap_handle_t *ih; + + be->db_type = NSS_AD_DB_GROUP_BYNAME; + + /* Sanitize name so that it can be used in our LDAP filter */ + if (_ldap_filter_name(name, argp->key.name, sizeof (name)) != 0) + return ((nss_status_t)NSS_NOTFOUND); + + if ((dname = strchr(name, '@')) == NULL) + return ((nss_status_t)NSS_NOTFOUND); + + *dname = '\0'; + dname++; + + /* + * Map the name to gid using idmap service. + */ + idmaprc = idmap_init(&ih); + if (idmaprc != IDMAP_SUCCESS) + return ((nss_status_t)NSS_NOTFOUND); + is_wuser = -1; + is_user = 0; /* Map name to gid */ + idmaprc = idmap_get_w2u_mapping(ih, NULL, NULL, name, dname, + 0, &is_user, &is_wuser, &gid, NULL, NULL, NULL); + (void) idmap_fini(ih); + if (idmaprc != IDMAP_SUCCESS) { + RESET_ERRNO(); + return ((nss_status_t)NSS_NOTFOUND); + } + + /* Create group(4) style string */ + if (update_buffer(be, argp, name, dname, gid) < 0) + return ((nss_status_t)NSS_NOTFOUND); + + /* Marshall the data, sanitize the return status and return */ + stat = _nss_ad_marshall_data(be, argp); + return (_nss_ad_sanitize_status(be, argp, stat)); +} + +/* + * getbygid gets a group entry by number. This function constructs an ldap + * search filter using the name invocation parameter and the getgrgid search + * filter defined. Once the filter is constructed, we searche for a matching + * entry and marshal the data results into struct group for the frontend + * process. The function _nss_ad_group2ent performs the data marshaling. + */ +static nss_status_t +getbygid(ad_backend_ptr be, void *a) +{ + nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; + char *winname = NULL, *windomain = NULL; + nss_status_t stat; + + be->db_type = NSS_AD_DB_GROUP_BYGID; + + stat = (nss_status_t)NSS_NOTFOUND; + + /* nss_ad does not support non ephemeral gids */ + if (argp->key.gid <= MAXUID) + goto out; + + /* Map the given GID to a SID using the idmap service */ + if (idmap_init(&be->ih) != 0) + goto out; + if (idmap_get_u2w_mapping(be->ih, &argp->key.gid, NULL, 0, + 0, NULL, NULL, NULL, &winname, &windomain, + NULL, NULL) != 0) { + RESET_ERRNO(); + goto out; + } + + /* + * NULL winname implies a local SID or unresolvable SID both of + * which cannot be used to generated group(4) entry + */ + if (winname == NULL) + goto out; + + /* Create group(4) style string */ + if (update_buffer(be, argp, winname, windomain, argp->key.gid) < 0) + goto out; + + /* Marshall the data, sanitize the return status and return */ + stat = _nss_ad_marshall_data(be, argp); + stat = _nss_ad_sanitize_status(be, argp, stat); + +out: + idmap_free(winname); + idmap_free(windomain); + (void) idmap_fini(be->ih); + be->ih = NULL; + return (stat); +} + +static ad_backend_op_t gr_ops[] = { + _nss_ad_destr, + _nss_ad_endent, + _nss_ad_setent, + _nss_ad_getent, + getbynam, + getbygid +}; + +/*ARGSUSED0*/ +nss_backend_t * +_nss_ad_group_constr(const char *dummy1, const char *dummy2, + const char *dummy3) +{ + + return ((nss_backend_t *)_nss_ad_constr(gr_ops, + sizeof (gr_ops)/sizeof (gr_ops[0]), _GROUP, NULL, NULL)); +} diff --git a/usr/src/lib/nsswitch/ad/common/getpwnam.c b/usr/src/lib/nsswitch/ad/common/getpwnam.c new file mode 100644 index 0000000000..ece50a54b0 --- /dev/null +++ b/usr/src/lib/nsswitch/ad/common/getpwnam.c @@ -0,0 +1,518 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <pwd.h> +#include <idmap.h> +#include <ctype.h> +#include "ad_common.h" + +/* passwd attributes and filters */ +#define _PWD_DN "dn" +#define _PWD_SAN "sAMAccountName" +#define _PWD_OBJSID "objectSid" +#define _PWD_PRIMARYGROUPID "primaryGroupID" +#define _PWD_CN "cn" +#define _PWD_HOMEDIRECTORY "homedirectory" +#define _PWD_LOGINSHELL "loginshell" +#define _PWD_OBJCLASS "objectClass" + +#define _F_GETPWNAM "(sAMAccountName=%.*s)" +#define _F_GETPWUID "(objectSid=%s)" + +static const char *pwd_attrs[] = { + _PWD_SAN, + _PWD_OBJSID, + _PWD_PRIMARYGROUPID, + _PWD_CN, + _PWD_HOMEDIRECTORY, + _PWD_LOGINSHELL, + _PWD_OBJCLASS, + (char *)NULL +}; + +static int +update_buffer(ad_backend_ptr be, nss_XbyY_args_t *argp, + const char *name, const char *domain, + uid_t uid, gid_t gid, const char *gecos, + const char *homedir, const char *shell) +{ + int buflen; + char *buffer; + + if (be->db_type == NSS_AD_DB_PASSWD_BYNAME) { + /* + * The canonical name obtained from AD lookup may not match + * the case of the name (i.e. key) in the request. Therefore, + * use the name from the request to construct the result. + */ + buflen = snprintf(NULL, 0, "%s:%s:%u:%u:%s:%s:%s", + argp->key.name, "x", uid, gid, gecos, homedir, shell) + 1; + } else { + if (domain == NULL) + domain = WK_DOMAIN; + buflen = snprintf(NULL, 0, "%s@%s:%s:%u:%u:%s:%s:%s", + name, domain, "x", uid, gid, gecos, homedir, shell) + 1; + } + + + if (argp->buf.result != NULL) { + buffer = be->buffer = malloc(buflen); + if (be->buffer == NULL) + return (-1); + be->buflen = buflen; + } else { + if (buflen > argp->buf.buflen) + return (-1); + buflen = argp->buf.buflen; + buffer = argp->buf.buffer; + } + + if (be->db_type == NSS_AD_DB_PASSWD_BYNAME) + (void) snprintf(buffer, buflen, "%s:%s:%u:%u:%s:%s:%s", + argp->key.name, "x", uid, gid, gecos, homedir, shell); + else + (void) snprintf(buffer, buflen, "%s@%s:%s:%u:%u:%s:%s:%s", + name, domain, "x", uid, gid, gecos, homedir, shell); + return (0); +} + + +#define NET_SCHEME "/net" + +/* + * 1) If the homeDirectory string is in UNC format then convert it into + * a /net format. This needs to be revisited later but is fine for now + * because Solaris does not support -hosts automount map for CIFS yet. + * + * 2) If homeDirectory contains ':' then return NULL because ':' is the + * delimiter in passwd entries and may break apps that parse these entries. + * + * 3) For all other cases return the same string that was passed to + * this function. + */ +static +char * +process_homedir(char *homedir) +{ + size_t len, smb_len; + char *smb_homedir; + int i, slash = 0; + + len = strlen(homedir); + + if (strchr(homedir, ':') != NULL) + /* + * Ignore paths that have colon ':' because ':' is a + * delimiter for the passwd entry. + */ + return (NULL); + + if (!(len > 1 && homedir[0] == '\\' && homedir[1] == '\\')) + /* Keep homedir intact if not in UNC format */ + return (homedir); + + /* + * Convert UNC string into /net format + * Example: \\server\abc -> /net/server/abc + */ + smb_len = len + 1 + sizeof (NET_SCHEME); + if ((smb_homedir = calloc(1, smb_len)) == NULL) + return (NULL); + (void) strlcpy(smb_homedir, NET_SCHEME, smb_len); + for (i = strlen(smb_homedir); *homedir != '\0'; homedir++) { + if (*homedir == '\\') { + /* Reduce double backslashes into one */ + if (slash) + slash = 0; + else { + slash = 1; + smb_homedir[i++] = '/'; + } + } else { + smb_homedir[i++] = *homedir; + slash = 0; + } + } + return (smb_homedir); +} + +/* + * _nss_ad_passwd2str is the data marshaling method for the passwd getXbyY + * (e.g., getbyuid(), getbyname(), getpwent()) backend processes. This method is + * called after a successful AD search has been performed. This method will + * parse the AD search values into the file format. + * e.g. + * + * blue@whale:x:123456:10:Blue Whale:/: + * + */ +static int +_nss_ad_passwd2str(ad_backend_ptr be, nss_XbyY_args_t *argp) +{ + int nss_result; + adutils_result_t *result = be->result; + const adutils_entry_t *entry; + char **sid_v, *ptr, **pgid_v, *end; + ulong_t tmp; + uint32_t urid, grid; + uid_t uid; + gid_t gid; + idmap_stat gstat; + idmap_get_handle_t *ig = NULL; + char **name_v, **dn_v, *domain = NULL; + char **gecos_v, **shell_v; + char **homedir_v = NULL, *homedir = NULL; + char *NULL_STR = ""; + + if (result == NULL) + return (NSS_STR_PARSE_PARSE); + entry = adutils_getfirstentry(result); + nss_result = NSS_STR_PARSE_PARSE; + + /* Create handles for idmap service */ + if (be->ih == NULL && idmap_init(&be->ih) != 0) + goto result_pwd2str; + if (idmap_get_create(be->ih, &ig) != 0) + goto result_pwd2str; + + /* Get name */ + name_v = adutils_getattr(entry, _PWD_SAN); + if (name_v == NULL || name_v[0] == NULL || *name_v[0] == '\0') + goto result_pwd2str; + + /* Get domain */ + dn_v = adutils_getattr(entry, _PWD_DN); + if (dn_v == NULL || dn_v[0] == NULL || *dn_v[0] == '\0') + goto result_pwd2str; + domain = adutils_dn2dns(dn_v[0]); + + /* Get objectSID (in text format) */ + sid_v = adutils_getattr(entry, _PWD_OBJSID); + if (sid_v == NULL || sid_v[0] == NULL || *sid_v[0] == '\0') + goto result_pwd2str; + + /* Break SID into prefix and rid */ + if ((ptr = strrchr(sid_v[0], '-')) == NULL) + goto result_pwd2str; + *ptr = '\0'; + end = ++ptr; + tmp = strtoul(ptr, &end, 10); + if (end == ptr || tmp > UINT32_MAX) + goto result_pwd2str; + urid = (uint32_t)tmp; + + /* We already have uid -- no need to call idmapd */ + if (be->db_type == NSS_AD_DB_PASSWD_BYUID) + uid = argp->key.uid; + else + uid = be->uid; + + /* Get primaryGroupID */ + pgid_v = adutils_getattr(entry, _PWD_PRIMARYGROUPID); + if (pgid_v == NULL || pgid_v[0] == NULL || *pgid_v[0] == '\0') + /* + * If primaryGroupID is not found then we request + * a GID to be mapped to the given user's objectSID + * (diagonal mapping) and use this GID as the primary + * GID for the entry. + */ + grid = urid; + else { + end = pgid_v[0]; + tmp = strtoul(pgid_v[0], &end, 10); + if (end == pgid_v[0] || tmp > UINT32_MAX) + goto result_pwd2str; + grid = (uint32_t)tmp; + } + + /* Map group SID to GID using idmap service */ + if (idmap_get_gidbysid(ig, sid_v[0], grid, 0, &gid, &gstat) != 0) + goto result_pwd2str; + if (idmap_get_mappings(ig) != 0 || gstat != 0) { + RESET_ERRNO(); + goto result_pwd2str; + } + + /* Get gecos, homedirectory and shell information if available */ + gecos_v = adutils_getattr(entry, _PWD_CN); + if (gecos_v == NULL || gecos_v[0] == NULL || *gecos_v[0] == '\0') + gecos_v = &NULL_STR; + + homedir_v = adutils_getattr(entry, _PWD_HOMEDIRECTORY); + if (homedir_v == NULL || homedir_v[0] == NULL || *homedir_v[0] == '\0') + homedir = NULL_STR; + else if ((homedir = process_homedir(homedir_v[0])) == NULL) + homedir = NULL_STR; + + shell_v = adutils_getattr(entry, _PWD_LOGINSHELL); + if (shell_v == NULL || shell_v[0] == NULL || *shell_v[0] == '\0') + shell_v = &NULL_STR; + + if (update_buffer(be, argp, name_v[0], domain, uid, gid, + gecos_v[0], homedir, shell_v[0]) < 0) + nss_result = NSS_STR_PARSE_ERANGE; + else + nss_result = NSS_STR_PARSE_SUCCESS; + +result_pwd2str: + idmap_get_destroy(ig); + (void) idmap_fini(be->ih); + be->ih = NULL; + (void) adutils_freeresult(&be->result); + free(domain); + if (homedir != NULL_STR && homedir_v != NULL && + homedir != homedir_v[0]) + free(homedir); + return ((int)nss_result); +} + +/* + * getbyname gets a passwd entry by winname. This function constructs an ldap + * search filter using the name invocation parameter and the getpwnam search + * filter defined. Once the filter is constructed, we search for a matching + * entry and marshal the data results into struct passwd for the frontend + * process. The function _nss_ad_passwd2ent performs the data marshaling. + */ + +static nss_status_t +getbyname(ad_backend_ptr be, void *a) +{ + nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; + char *searchfilter; + char name[SEARCHFILTERLEN]; + char *dname; + int filterlen, namelen; + int flag; + nss_status_t stat; + idmap_stat idmaprc; + uid_t uid; + gid_t gid; + int is_user, is_wuser, try_idmap; + idmap_handle_t *ih; + + be->db_type = NSS_AD_DB_PASSWD_BYNAME; + + /* Sanitize name so that it can be used in our LDAP filter */ + if (_ldap_filter_name(name, argp->key.name, sizeof (name)) != 0) + return ((nss_status_t)NSS_NOTFOUND); + + if ((dname = strchr(name, '@')) == NULL) + return ((nss_status_t)NSS_NOTFOUND); + + *dname = '\0'; + dname++; + + /* + * Map the given name to UID using idmap service. If idmap + * call fails then this will save us doing AD discovery and + * AD lookup here. + */ + if (idmap_init(&be->ih) != IDMAP_SUCCESS) + return ((nss_status_t)NSS_NOTFOUND); + flag = (strcasecmp(dname, WK_DOMAIN) == 0) ? + IDMAP_REQ_FLG_WK_OR_LOCAL_SIDS_ONLY : 0; + is_wuser = -1; + is_user = 1; + if (idmap_get_w2u_mapping(be->ih, NULL, NULL, name, + dname, flag, &is_user, &is_wuser, &be->uid, NULL, + NULL, NULL) != IDMAP_SUCCESS) { + (void) idmap_fini(be->ih); + be->ih = NULL; + RESET_ERRNO(); + return ((nss_status_t)NSS_NOTFOUND); + } + + /* If this is not a Well-Known SID then try AD lookup. */ + if (strcasecmp(dname, WK_DOMAIN) != 0) { + /* Assemble filter using the given name */ + namelen = strlen(name); + filterlen = snprintf(NULL, 0, _F_GETPWNAM, namelen, name) + 1; + if ((searchfilter = (char *)malloc(filterlen)) == NULL) + return ((nss_status_t)NSS_NOTFOUND); + (void) snprintf(searchfilter, filterlen, _F_GETPWNAM, + namelen, name); + stat = _nss_ad_lookup(be, argp, _PASSWD, searchfilter, + dname, &try_idmap); + free(searchfilter); + + if (!try_idmap) { + (void) idmap_fini(be->ih); + be->ih = NULL; + return (stat); + } + + } + + /* + * Either this is a Well-Known SID or AD lookup failed. Map + * the given name to GID using idmap service and construct + * the passwd entry. + */ + is_wuser = -1; + is_user = 0; /* Map name to primary gid */ + idmaprc = idmap_get_w2u_mapping(be->ih, NULL, NULL, name, dname, + flag, &is_user, &is_wuser, &gid, NULL, NULL, NULL); + (void) idmap_fini(be->ih); + be->ih = NULL; + if (idmaprc != IDMAP_SUCCESS) { + RESET_ERRNO(); + return ((nss_status_t)NSS_NOTFOUND); + } + + /* Create passwd(4) style string */ + if (update_buffer(be, argp, name, dname, + be->uid, gid, "", "", "") < 0) + return ((nss_status_t)NSS_NOTFOUND); + + /* Marshall the data, sanitize the return status and return */ + stat = _nss_ad_marshall_data(be, argp); + return (_nss_ad_sanitize_status(be, argp, stat)); +} + + +/* + * getbyuid gets a passwd entry by uid number. This function constructs an ldap + * search filter using the uid invocation parameter and the getpwuid search + * filter defined. Once the filter is constructed, we search for a matching + * entry and marshal the data results into struct passwd for the frontend + * process. The function _nss_ad_passwd2ent performs the data marshaling. + */ + +static nss_status_t +getbyuid(ad_backend_ptr be, void *a) +{ + nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; + char searchfilter[ADUTILS_MAXHEXBINSID + 14]; + char *sidprefix = NULL; + idmap_rid_t rid; + char cbinsid[ADUTILS_MAXHEXBINSID + 1]; + char *winname = NULL, *windomain = NULL; + int is_user, is_wuser; + gid_t gid; + idmap_stat idmaprc; + int ret, try_idmap; + nss_status_t stat; + + be->db_type = NSS_AD_DB_PASSWD_BYUID; + + stat = (nss_status_t)NSS_NOTFOUND; + + /* nss_ad does not support non ephemeral uids */ + if (argp->key.uid <= MAXUID) + goto out; + + /* Map the given UID to a SID using the idmap service */ + if (idmap_init(&be->ih) != 0) + goto out; + if (idmap_get_u2w_mapping(be->ih, &argp->key.uid, NULL, 0, + 1, NULL, &sidprefix, &rid, &winname, &windomain, + NULL, NULL) != 0) { + RESET_ERRNO(); + goto out; + } + + /* + * NULL winname implies a local SID or unresolvable SID both of + * which cannot be used to generated passwd(4) entry + */ + if (winname == NULL) + goto out; + + /* If this is not a Well-Known SID try AD lookup */ + if (windomain != NULL && strcasecmp(windomain, WK_DOMAIN) != 0) { + if (adutils_txtsid2hexbinsid(sidprefix, &rid, + &cbinsid[0], sizeof (cbinsid)) != 0) + goto out; + + ret = snprintf(searchfilter, sizeof (searchfilter), + _F_GETPWUID, cbinsid); + if (ret >= sizeof (searchfilter) || ret < 0) + goto out; + + stat = _nss_ad_lookup(be, argp, _PASSWD, searchfilter, + windomain, &try_idmap); + + if (!try_idmap) + goto out; + } + + /* Map winname to primary gid using idmap service */ + is_user = 0; + is_wuser = -1; + idmaprc = idmap_get_w2u_mapping(be->ih, NULL, NULL, + winname, windomain, 0, &is_user, &is_wuser, &gid, + NULL, NULL, NULL); + + (void) idmap_fini(be->ih); + be->ih = NULL; + + if (idmaprc != IDMAP_SUCCESS) { + RESET_ERRNO(); + goto out; + } + + /* Create passwd(4) style string */ + if (update_buffer(be, argp, winname, windomain, + argp->key.uid, gid, "", "", "") < 0) + goto out; + + /* Marshall the data, sanitize the return status and return */ + stat = _nss_ad_marshall_data(be, argp); + stat = _nss_ad_sanitize_status(be, argp, stat); + +out: + idmap_free(sidprefix); + idmap_free(winname); + idmap_free(windomain); + (void) idmap_fini(be->ih); + be->ih = NULL; + return (stat); +} + +static ad_backend_op_t passwd_ops[] = { + _nss_ad_destr, + _nss_ad_endent, + _nss_ad_setent, + _nss_ad_getent, + getbyname, + getbyuid +}; + +/* + * _nss_ad_passwd_constr is where life begins. This function calls the + * generic AD constructor function to define and build the abstract + * data types required to support AD operations. + */ + +/*ARGSUSED0*/ +nss_backend_t * +_nss_ad_passwd_constr(const char *dummy1, const char *dummy2, + const char *dummy3) +{ + + return ((nss_backend_t *)_nss_ad_constr(passwd_ops, + sizeof (passwd_ops)/sizeof (passwd_ops[0]), + _PASSWD, pwd_attrs, _nss_ad_passwd2str)); +} diff --git a/usr/src/lib/nsswitch/ad/common/getspent.c b/usr/src/lib/nsswitch/ad/common/getspent.c new file mode 100644 index 0000000000..257540c8db --- /dev/null +++ b/usr/src/lib/nsswitch/ad/common/getspent.c @@ -0,0 +1,146 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <shadow.h> +#include <stdlib.h> +#include "ad_common.h" + +static int +update_buffer(ad_backend_ptr be, nss_XbyY_args_t *argp, + const char *name, const char *domain) +{ + int buflen; + char *buffer; + + /* + * The user password is not available in the AD object and therefore + * sp_pwdp will be "*NP*". + * + * nss_ad will leave aging fields empty (i.e. The front end + * marshaller will set sp_lstchgst, sp_min, sp_max, sp_warn, + * sp_inact, and sp_expire to -1 and sp_flag to 0) because shadow + * fields are irrevalent with AD and krb5. + */ + + buflen = snprintf(NULL, 0, "%s@%s:*NP*:::::::", name, domain) + 1; + + if (argp->buf.result != NULL) { + buffer = be->buffer = malloc(buflen); + if (be->buffer == NULL) + return (-1); + be->buflen = buflen; + } else { + if (buflen > argp->buf.buflen) + return (-1); + buflen = argp->buf.buflen; + buffer = argp->buf.buffer; + } + + buflen = snprintf(buffer, buflen, "%s@%s:*NP*:::::::", + name, domain) + 1; + return (0); +} + +/* + * getbynam gets a shadow entry by winname. This function constructs an ldap + * search filter using the name invocation parameter and the getspnam search + * filter defined. Once the filter is constructed we search for a matching + * entry and marshal the data results into struct shadow for the frontend + * process. The function _nss_ad_shadow2ent performs the data marshaling. + */ +static nss_status_t +getbynam(ad_backend_ptr be, void *a) +{ + nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; + char name[SEARCHFILTERLEN + 1]; + char *dname; + nss_status_t stat; + idmap_stat idmaprc; + uid_t uid; + int is_user, is_wuser; + idmap_handle_t *ih; + + be->db_type = NSS_AD_DB_SHADOW_BYNAME; + + /* Sanitize name so that it can be used in our LDAP filter */ + if (_ldap_filter_name(name, argp->key.name, sizeof (name)) != 0) + return ((nss_status_t)NSS_NOTFOUND); + + if ((dname = strchr(name, '@')) == NULL) + return ((nss_status_t)NSS_NOTFOUND); + + *dname = '\0'; + dname++; + + /* + * Use idmap service to verify that the given + * name is a valid Windows name. + */ + idmaprc = idmap_init(&ih); + if (idmaprc != IDMAP_SUCCESS) + return ((nss_status_t)NSS_NOTFOUND); + is_wuser = -1; + is_user = 1; + idmaprc = idmap_get_w2u_mapping(ih, NULL, NULL, name, dname, + 0, &is_user, &is_wuser, &uid, NULL, NULL, NULL); + (void) idmap_fini(ih); + if (idmaprc != IDMAP_SUCCESS) { + RESET_ERRNO(); + return ((nss_status_t)NSS_NOTFOUND); + } + + /* Create shadow(4) style string */ + if (update_buffer(be, argp, name, dname) < 0) + return ((nss_status_t)NSS_NOTFOUND); + + /* Marshall the data, sanitize the return status and return */ + stat = _nss_ad_marshall_data(be, argp); + return (_nss_ad_sanitize_status(be, argp, stat)); +} + +static ad_backend_op_t sp_ops[] = { + _nss_ad_destr, + _nss_ad_endent, + _nss_ad_setent, + _nss_ad_getent, + getbynam +}; + + +/* + * _nss_ad_passwd_constr is where life begins. This function calls the + * generic ldap constructor function to define and build the abstract + * data types required to support ldap operations. + */ +/*ARGSUSED0*/ +nss_backend_t * +_nss_ad_shadow_constr(const char *dummy1, const char *dummy2, + const char *dummy3) +{ + + return ((nss_backend_t *)_nss_ad_constr(sp_ops, + sizeof (sp_ops)/sizeof (sp_ops[0]), + _SHADOW, NULL, NULL)); +} diff --git a/usr/src/lib/nsswitch/ad/common/mapfile-vers b/usr/src/lib/nsswitch/ad/common/mapfile-vers new file mode 100644 index 0000000000..a42ab674cb --- /dev/null +++ b/usr/src/lib/nsswitch/ad/common/mapfile-vers @@ -0,0 +1,33 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +SUNWprivate { + global: + _nss_ad_passwd_constr; + _nss_ad_shadow_constr; + _nss_ad_group_constr; + local: + *; +}; diff --git a/usr/src/lib/nsswitch/ad/i386/Makefile b/usr/src/lib/nsswitch/ad/i386/Makefile new file mode 100644 index 0000000000..710f624b5c --- /dev/null +++ b/usr/src/lib/nsswitch/ad/i386/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com + +LIBS = $(DYNLIB1) + +include ../../Makefile.targ + +install: all $(ROOTLIBS) diff --git a/usr/src/lib/nsswitch/ad/sparc/Makefile b/usr/src/lib/nsswitch/ad/sparc/Makefile new file mode 100644 index 0000000000..710f624b5c --- /dev/null +++ b/usr/src/lib/nsswitch/ad/sparc/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com + +LIBS = $(DYNLIB1) + +include ../../Makefile.targ + +install: all $(ROOTLIBS) diff --git a/usr/src/lib/nsswitch/ad/sparcv9/Makefile b/usr/src/lib/nsswitch/ad/sparcv9/Makefile new file mode 100644 index 0000000000..6e71c56dcc --- /dev/null +++ b/usr/src/lib/nsswitch/ad/sparcv9/Makefile @@ -0,0 +1,32 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com +include $(SRC)/lib/Makefile.lib.64 + +LIBS = $(DYNLIB1) + +include ../../Makefile.targ + +install: all $(ROOT64DYNLIB) diff --git a/usr/src/lib/nsswitch/compat/common/compat_common.c b/usr/src/lib/nsswitch/compat/common/compat_common.c index d696f80b3c..82975b3e37 100644 --- a/usr/src/lib/nsswitch/compat/common/compat_common.c +++ b/usr/src/lib/nsswitch/compat/common/compat_common.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Common code and structures used by name-service-switch "compat" backends. @@ -28,8 +28,6 @@ * the "files" backend; this file is no exception. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -692,6 +690,44 @@ _attrdb_compat_XY_all(be, argp, netdb, check, op_num) return (res); } +static int +validate_ids(compat_backend_ptr_t be, nss_XbyY_args_t *argp, + char *line, int *linelenp, int buflen, int extra_chars) +{ + if (be->return_string_data != 1) { + struct passwd *p; + struct group *g; + /* + * The data is already marshalled into + * struct passwd or group. + */ + if (strcmp(be->filename, PASSWD) == 0) { + p = (struct passwd *)argp->returnval; + if (p->pw_uid > MAXUID) + p->pw_uid = UID_NOBODY; + if (p->pw_gid > MAXUID) + p->pw_gid = GID_NOBODY; + } else if (strcmp(be->filename, GF_PATH) == 0) { + g = (struct group *)argp->returnval; + if (g->gr_gid > MAXUID) + g->gr_gid = GID_NOBODY; + } + return (NSS_STR_PARSE_SUCCESS); + } + + /* + * The data needs to be returned in string format therefore + * validate the return string. + */ + if (strcmp(be->filename, PASSWD) == 0) + return (validate_passwd_ids(line, linelenp, buflen, + extra_chars)); + else if (strcmp(be->filename, GF_PATH) == 0) + return (validate_group_ids(line, linelenp, buflen, + extra_chars)); + return (NSS_STR_PARSE_SUCCESS); +} + nss_status_t _nss_compat_XY_all(be, args, check, op_num) compat_backend_ptr_t be; @@ -760,6 +796,19 @@ _nss_compat_XY_all(be, args, check, op_num) args->returnval = args->buf.result; if ((*check)(args) != 0) { int len; + + parsestat = validate_ids(be, args, + instr, &linelen, be->minbuf, 1); + if (parsestat == + NSS_STR_PARSE_ERANGE) { + args->erange = 1; + res = NSS_NOTFOUND; + break; + } else if (parsestat != + NSS_STR_PARSE_SUCCESS) { + continue; + } + if (be->return_string_data != 1) { res = NSS_SUCCESS; break; diff --git a/usr/src/lib/nsswitch/compat/common/compat_common.h b/usr/src/lib/nsswitch/compat/common/compat_common.h index 041dc0f322..5761651636 100644 --- a/usr/src/lib/nsswitch/compat/common/compat_common.h +++ b/usr/src/lib/nsswitch/compat/common/compat_common.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* @@ -29,8 +29,6 @@ #ifndef _COMPAT_COMMON_H #define _COMPAT_COMMON_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <nss_common.h> #include <nss_dbdefs.h> #include <stdio.h> @@ -131,6 +129,12 @@ extern nss_status_t _nss_compat_XY_all(); extern nss_status_t _attrdb_compat_XY_all(); #endif +/* functions to validate passwd and group ids */ +extern int validate_passwd_ids(char *line, int *linelenp, int buflen, + int extra_chars); +extern int validate_group_ids(char *line, int *linelenp, int buflen, + int extra_chars); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/nsswitch/compat/common/getgrent.c b/usr/src/lib/nsswitch/compat/common/getgrent.c index 0457392e59..6f9c81b1e0 100644 --- a/usr/src/lib/nsswitch/compat/common/getgrent.c +++ b/usr/src/lib/nsswitch/compat/common/getgrent.c @@ -21,7 +21,7 @@ /* * getgrent.c * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * lib/nsswitch/compat/getgrent.c -- name-service-switch backend for getgrnam() @@ -43,8 +43,6 @@ * - People who recursively specify "compat" deserve what they get. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <grp.h> #include <stdlib.h> #include <unistd.h> /* for GF_PATH */ @@ -62,6 +60,51 @@ _nss_initf_group_compat(p) p->default_config = NSS_DEFCONF_GROUP_COMPAT; } +/* + * Validates group entry replacing gid > MAXUID by GID_NOBODY. + */ +int +validate_group_ids(char *line, int *linelenp, int buflen, int extra_chars) +{ + char *linep, *limit, *gidp; + ulong_t gid; + int oldgidlen, idlen; + int linelen = *linelenp, newlinelen; + + if (linelen == 0 || *line == '+' || *line == '-') + return (NSS_STR_PARSE_SUCCESS); + + linep = line; + limit = line + linelen; + + while (linep < limit && *linep++ != ':') /* skip groupname */ + continue; + while (linep < limit && *linep++ != ':') /* skip password */ + continue; + if (linep == limit) + return (NSS_STR_PARSE_PARSE); + + gidp = linep; + gid = strtoul(gidp, (char **)&linep, 10); /* grab gid */ + oldgidlen = linep - gidp; + if (linep >= limit || oldgidlen == 0) + return (NSS_STR_PARSE_PARSE); + + if (gid <= MAXUID) + return (NSS_STR_PARSE_SUCCESS); + + idlen = snprintf(NULL, 0, "%u", GID_NOBODY); + newlinelen = linelen + idlen - oldgidlen; + if (newlinelen + extra_chars > buflen) + return (NSS_STR_PARSE_ERANGE); + + (void) bcopy(linep, gidp + idlen, limit - linep + extra_chars); + (void) snprintf(gidp, idlen + 1, "%u", GID_NOBODY); + *(gidp + idlen) = ':'; + *linelenp = newlinelen; + return (NSS_STR_PARSE_SUCCESS); +} + static const char * get_grname(argp) nss_XbyY_args_t *argp; @@ -107,6 +150,8 @@ getbygid(be, a) { nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; + if (argp->key.gid > MAXUID) + return (NSS_NOTFOUND); return (_nss_compat_XY_all(be, argp, check_grgid, NSS_DBOP_GROUP_BYGID)); } @@ -211,7 +256,7 @@ merge_grents(be, argp, fields) /* Really "out of memory", but PARSE_PARSE will have to do */ } s = buf; - (void) snprintf(s, NSS_LINELEN_GROUP, "%s:%s:%d:", + (void) snprintf(s, NSS_LINELEN_GROUP, "%s:%s:%u:", g->gr_name, fields[1] != 0 ? fields[1] : g->gr_passwd, g->gr_gid); diff --git a/usr/src/lib/nsswitch/compat/common/getpwent.c b/usr/src/lib/nsswitch/compat/common/getpwent.c index 1219dab7fa..f7e5961ebe 100644 --- a/usr/src/lib/nsswitch/compat/common/getpwent.c +++ b/usr/src/lib/nsswitch/compat/common/getpwent.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * getpwent.c @@ -49,8 +49,6 @@ * confusing. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <pwd.h> #include <shadow.h> /* For PASSWD (pathname to passwd file) */ #include <stdlib.h> @@ -68,6 +66,63 @@ _nss_initf_passwd_compat(p) p->default_config = NSS_DEFCONF_PASSWD_COMPAT; } +/* + * Validates passwd entry replacing uid/gid > MAXUID by ID_NOBODY. + */ +int +validate_passwd_ids(char *line, int *linelenp, int buflen, int extra_chars) +{ + char *linep, *limit, *uidp, *gidp; + uid_t uid; + gid_t gid; + ulong_t uidl, gidl; + int olduidlen, oldgidlen, idlen; + int linelen = *linelenp, newlinelen; + + if (linelen == 0 || *line == '+' || *line == '-') + return (NSS_STR_PARSE_SUCCESS); + + linep = line; + limit = line + linelen; + + while (linep < limit && *linep++ != ':') /* skip username */ + continue; + while (linep < limit && *linep++ != ':') /* skip password */ + continue; + if (linep == limit) + return (NSS_STR_PARSE_PARSE); + + uidp = linep; + uidl = strtoul(uidp, (char **)&linep, 10); /* grab uid */ + olduidlen = linep - uidp; + if (++linep >= limit || olduidlen == 0) + return (NSS_STR_PARSE_PARSE); + + gidp = linep; + gidl = strtoul(gidp, (char **)&linep, 10); /* grab gid */ + oldgidlen = linep - gidp; + if (linep >= limit || oldgidlen == 0) + return (NSS_STR_PARSE_PARSE); + + if (uidl <= MAXUID && gidl <= MAXUID) + return (NSS_STR_PARSE_SUCCESS); + uid = (uidl > MAXUID) ? UID_NOBODY : (uid_t)uidl; + gid = (gidl > MAXUID) ? GID_NOBODY : (gid_t)gidl; + + /* Check if we have enough space in the buffer */ + idlen = snprintf(NULL, 0, "%u:%u", uid, gid); + newlinelen = linelen + idlen - olduidlen - oldgidlen - 1; + if (newlinelen + extra_chars > buflen) + return (NSS_STR_PARSE_ERANGE); + + /* Replace ephemeral ids by ID_NOBODY */ + (void) bcopy(linep, uidp + idlen, limit - linep + extra_chars); + (void) snprintf(uidp, idlen + 1, "%u:%u", uid, gid); + *(uidp + idlen) = ':'; /* restore : that was overwritten by snprintf */ + *linelenp = newlinelen; + return (NSS_STR_PARSE_SUCCESS); +} + static const char * get_pwname(argp) nss_XbyY_args_t *argp; @@ -113,6 +168,8 @@ getbyuid(be, a) { nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; + if (argp->key.uid > MAXUID) + return (NSS_NOTFOUND); return (_nss_compat_XY_all(be, argp, check_pwuid, NSS_DBOP_PASSWD_BYUID)); } @@ -163,7 +220,7 @@ merge_pwents(be, argp, fields) s += len; buflen -= len; - len = snprintf(s, buflen, ":%ld:%ld:%s:%s:%s", + len = snprintf(s, buflen, ":%u:%u:%s:%s:%s", pw->pw_uid, pw->pw_gid, fields[4] != 0 ? fields[4] : pw->pw_gecos, diff --git a/usr/src/lib/nsswitch/files/common/files_common.c b/usr/src/lib/nsswitch/files/common/files_common.c index 74d47f7c8c..4b7b6753c1 100644 --- a/usr/src/lib/nsswitch/files/common/files_common.c +++ b/usr/src/lib/nsswitch/files/common/files_common.c @@ -25,8 +25,6 @@ * Common code and structures used by name-service-switch "files" backends. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * An implementation that used mmap() sensibly would be a wonderful thing, * but this here is just yer standard fgets() thang. @@ -299,9 +297,27 @@ _nss_files_XY_all(be, args, netdb, filter, check) if (check != NULL && (*check)(args, instr, linelen) == 0) continue; - func = args->str2ent; - parsestat = (*func)(instr, linelen, args->buf.result, - args->buf.buffer, args->buf.buflen); + parsestat = NSS_STR_PARSE_SUCCESS; + if (be->filename != NULL) { + /* + * Special case for passwd and group wherein we + * replace uids/gids > MAXUID by ID_NOBODY + * because files backend does not support + * ephemeral ids. + */ + if (strcmp(be->filename, PF_PATH) == 0) + parsestat = validate_passwd_ids(instr, + &linelen, be->minbuf, 2); + else if (strcmp(be->filename, GF_PATH) == 0) + parsestat = validate_group_ids(instr, + &linelen, be->minbuf, 2, check); + } + + if (parsestat == NSS_STR_PARSE_SUCCESS) { + func = args->str2ent; + parsestat = (*func)(instr, linelen, args->buf.result, + args->buf.buffer, args->buf.buflen); + } if (parsestat == NSS_STR_PARSE_SUCCESS) { args->returnval = (args->buf.result != NULL)? @@ -366,7 +382,7 @@ _nss_files_XY_hash(files_backend_ptr_t be, nss_XbyY_args_t *args, int netdb, files_hash_t *fhp, int hashop, files_XY_check_func check) { /* LINTED E_FUNC_VAR_UNUSED */ - int fd, retries, ht; + int fd, retries, ht, stat; /* LINTED E_FUNC_VAR_UNUSED */ uint_t hash, line, f; /* LINTED E_FUNC_VAR_UNUSED */ @@ -412,6 +428,28 @@ retry: if ((*check)(args, fhp->fh_line[line].l_start, fhp->fh_line[line].l_len) == 0) continue; + + if (be->filename != NULL) { + stat = NSS_STR_PARSE_SUCCESS; + if (strcmp(be->filename, PF_PATH) == 0) + stat = validate_passwd_ids( + fhp->fh_line[line].l_start, + &fhp->fh_line[line].l_len, + fhp->fh_line[line].l_len + 1, + 1); + else if (strcmp(be->filename, GF_PATH) == 0) + stat = validate_group_ids( + fhp->fh_line[line].l_start, + &fhp->fh_line[line].l_len, + fhp->fh_line[line].l_len + 1, + 1, check); + if (stat != NSS_STR_PARSE_SUCCESS) { + if (stat == NSS_STR_PARSE_ERANGE) + args->erange = 1; + continue; + } + } + if ((*args->str2ent)(fhp->fh_line[line].l_start, fhp->fh_line[line].l_len, args->buf.result, args->buf.buffer, args->buf.buflen) == diff --git a/usr/src/lib/nsswitch/files/common/files_common.h b/usr/src/lib/nsswitch/files/common/files_common.h index b526833022..2138d7180e 100644 --- a/usr/src/lib/nsswitch/files/common/files_common.h +++ b/usr/src/lib/nsswitch/files/common/files_common.h @@ -31,8 +31,6 @@ #ifndef _FILES_COMMON_H #define _FILES_COMMON_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <nss_common.h> #include <nss_dbdefs.h> #include <stdio.h> @@ -136,6 +134,12 @@ extern nss_status_t _nss_files_XY_hash(); int _nss_files_check_name_aliases(nss_XbyY_args_t *, const char *, int); int _nss_files_check_name_colon(nss_XbyY_args_t *, const char *, int); +/* passwd and group validation functions */ +extern int validate_group_ids(char *line, int *linelenp, int buflen, + int extra_chars, files_XY_check_func check); +extern int validate_passwd_ids(char *line, int *linelenp, int buflen, + int extra_chars); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/nsswitch/files/common/getgrent.c b/usr/src/lib/nsswitch/files/common/getgrent.c index 6f45136f16..455e070da4 100644 --- a/usr/src/lib/nsswitch/files/common/getgrent.c +++ b/usr/src/lib/nsswitch/files/common/getgrent.c @@ -19,14 +19,12 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * files/getgrent.c -- "files" backend for nsswitch "group" database */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <grp.h> #include <unistd.h> /* for GF_PATH */ #include <stdlib.h> /* for GF_PATH */ @@ -69,16 +67,16 @@ hash_grgid(nss_XbyY_args_t *argp, int keyhash, const char *line, if (keyhash) return ((uint_t)argp->key.gid); - /* skip groupname */ - while (linep < limit && *linep++ != ':'); - /* skip password */ - while (linep < limit && *linep++ != ':'); + while (linep < limit && *linep++ != ':') /* skip groupname */ + continue; + while (linep < limit && *linep++ != ':') /* skip password */ + continue; if (linep == limit) return (GID_NOBODY); /* gid */ end = linep; - id = (uint_t)strtol(linep, (char **)&end, 10); + id = (uint_t)strtoul(linep, (char **)&end, 10); /* empty gid */ if (linep == end) return (GID_NOBODY); @@ -127,7 +125,7 @@ static int check_grgid(nss_XbyY_args_t *argp, const char *line, int linelen) { const char *linep, *limit, *end; - gid_t gr_gid; + ulong_t gr_gid; linep = line; limit = line + linelen; @@ -136,22 +134,22 @@ check_grgid(nss_XbyY_args_t *argp, const char *line, int linelen) if (linelen == 0 || *line == '+' || *line == '-') return (0); - /* skip username */ - while (linep < limit && *linep++ != ':'); - /* skip password */ - while (linep < limit && *linep++ != ':'); + while (linep < limit && *linep++ != ':') /* skip groupname */ + continue; + while (linep < limit && *linep++ != ':') /* skip password */ + continue; if (linep == limit) return (0); - /* uid */ + /* gid */ end = linep; - gr_gid = (gid_t)strtol(linep, (char **)&end, 10); + gr_gid = strtoul(linep, (char **)&end, 10); - /* empty gid is not valid */ - if (linep == end) + /* check if gid is empty or overflows */ + if (linep == end || gr_gid > UINT32_MAX) return (0); - return (gr_gid == argp->key.gid); + return ((gid_t)gr_gid == argp->key.gid); } static nss_status_t @@ -159,7 +157,66 @@ getbygid(be, a) files_backend_ptr_t be; void *a; { - return (_nss_files_XY_hash(be, a, 0, &hashinfo, 1, check_grgid)); + nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; + + if (argp->key.gid > MAXUID) + return (NSS_NOTFOUND); + return (_nss_files_XY_hash(be, argp, 0, &hashinfo, 1, check_grgid)); +} + +/* + * Validates group entry replacing gid > MAXUID by GID_NOBODY. + */ +int +validate_group_ids(char *line, int *linelenp, int buflen, int extra_chars, + files_XY_check_func check) +{ + char *linep, *limit, *gidp; + ulong_t gid; + int oldgidlen, idlen; + int linelen = *linelenp, newlinelen; + + /* + * getbygid() rejects searching by ephemeral gid therefore + * no need to validate because the matched entry won't have + * an ephemeral gid. + */ + if (check != NULL && check == check_grgid) + return (NSS_STR_PARSE_SUCCESS); + + /* +/- entries valid for compat source only */ + if (linelen == 0 || *line == '+' || *line == '-') + return (NSS_STR_PARSE_SUCCESS); + + linep = line; + limit = line + linelen; + + while (linep < limit && *linep++ != ':') /* skip groupname */ + continue; + while (linep < limit && *linep++ != ':') /* skip password */ + continue; + if (linep == limit) + return (NSS_STR_PARSE_PARSE); + + gidp = linep; + gid = strtoul(gidp, (char **)&linep, 10); /* grab gid */ + oldgidlen = linep - gidp; + if (linep >= limit || oldgidlen == 0) + return (NSS_STR_PARSE_PARSE); + + if (gid <= MAXUID) + return (NSS_STR_PARSE_SUCCESS); + + idlen = snprintf(NULL, 0, "%u", GID_NOBODY); + newlinelen = linelen + idlen - oldgidlen; + if (newlinelen + extra_chars > buflen) + return (NSS_STR_PARSE_ERANGE); + + (void) bcopy(linep, gidp + idlen, limit - linep + extra_chars); + (void) snprintf(gidp, idlen + 1, "%u", GID_NOBODY); + *(gidp + idlen) = ':'; + *linelenp = newlinelen; + return (NSS_STR_PARSE_SUCCESS); } static nss_status_t diff --git a/usr/src/lib/nsswitch/files/common/getpwnam.c b/usr/src/lib/nsswitch/files/common/getpwnam.c index 8faa2014eb..570eec84ad 100644 --- a/usr/src/lib/nsswitch/files/common/getpwnam.c +++ b/usr/src/lib/nsswitch/files/common/getpwnam.c @@ -19,14 +19,12 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * files/getpwnam.c -- "files" backend for nsswitch "passwd" database */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <pwd.h> #include <shadow.h> #include <unistd.h> /* for PF_PATH */ @@ -70,16 +68,16 @@ hash_pwuid(nss_XbyY_args_t *argp, int keyhash, const char *line, if (keyhash) return ((uint_t)argp->key.uid); - /* skip username */ - while (linep < limit && *linep++ != ':'); - /* skip password */ - while (linep < limit && *linep++ != ':'); + while (linep < limit && *linep++ != ':') /* skip username */ + continue; + while (linep < limit && *linep++ != ':') /* skip password */ + continue; if (linep == limit) return (UID_NOBODY); /* uid */ end = linep; - id = (uint_t)strtol(linep, (char **)&end, 10); + id = (uint_t)strtoul(linep, (char **)&end, 10); /* empty uid */ if (linep == end) @@ -129,7 +127,7 @@ static int check_pwuid(nss_XbyY_args_t *argp, const char *line, int linelen) { const char *linep, *limit, *end; - uid_t pw_uid; + ulong_t pw_uid; linep = line; limit = line + linelen; @@ -138,22 +136,22 @@ check_pwuid(nss_XbyY_args_t *argp, const char *line, int linelen) if (linelen == 0 || *line == '+' || *line == '-') return (0); - /* skip username */ - while (linep < limit && *linep++ != ':'); - /* skip password */ - while (linep < limit && *linep++ != ':'); + while (linep < limit && *linep++ != ':') /* skip username */ + continue; + while (linep < limit && *linep++ != ':') /* skip password */ + continue; if (linep == limit) return (0); /* uid */ end = linep; - pw_uid = (uid_t)strtol(linep, (char **)&end, 10); + pw_uid = strtoul(linep, (char **)&end, 10); - /* empty uid is not valid */ - if (linep == end) + /* check if the uid is empty or overflows */ + if (linep == end || pw_uid > UINT32_MAX) return (0); - return (pw_uid == argp->key.uid); + return ((uid_t)pw_uid == argp->key.uid); } static nss_status_t @@ -161,7 +159,73 @@ getbyuid(be, a) files_backend_ptr_t be; void *a; { - return (_nss_files_XY_hash(be, a, 0, &hashinfo, 1, check_pwuid)); + nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; + + if (argp->key.uid > MAXUID) + return (NSS_NOTFOUND); + return (_nss_files_XY_hash(be, argp, 0, &hashinfo, 1, check_pwuid)); +} + +/* + * Validates passwd entry replacing uid/gid > MAXUID by ID_NOBODY. + */ +int +validate_passwd_ids(char *line, int *linelenp, int buflen, int extra_chars) +{ + char *linep, *limit, *uidp, *gidp; + uid_t uid; + gid_t gid; + ulong_t uidl, gidl; + int olduidlen, oldgidlen, idlen; + int linelen = *linelenp, newlinelen; + + /* + * +name entries in passwd(4) do not override uid and gid + * values. Therefore no need to validate the ids in these + * entries. + */ + if (linelen == 0 || *line == '+' || *line == '-') + return (NSS_STR_PARSE_SUCCESS); + + linep = line; + limit = line + linelen; + + while (linep < limit && *linep++ != ':') /* skip username */ + continue; + while (linep < limit && *linep++ != ':') /* skip password */ + continue; + if (linep == limit) + return (NSS_STR_PARSE_PARSE); + + uidp = linep; + uidl = strtoul(uidp, (char **)&linep, 10); /* grab uid */ + olduidlen = linep - uidp; + if (++linep >= limit || olduidlen == 0) + return (NSS_STR_PARSE_PARSE); + + gidp = linep; + gidl = strtoul(gidp, (char **)&linep, 10); /* grab gid */ + oldgidlen = linep - gidp; + if (linep >= limit || oldgidlen == 0) + return (NSS_STR_PARSE_PARSE); + + if (uidl <= MAXUID && gidl <= MAXUID) + return (NSS_STR_PARSE_SUCCESS); + uid = (uidl > MAXUID) ? UID_NOBODY : (uid_t)uidl; + gid = (gidl > MAXUID) ? GID_NOBODY : (gid_t)gidl; + + /* Check if we have enough space in the buffer */ + idlen = snprintf(NULL, 0, "%u:%u", uid, gid); + newlinelen = linelen + idlen - olduidlen - oldgidlen - 1; + if (newlinelen + extra_chars > buflen) + return (NSS_STR_PARSE_ERANGE); + + /* Replace ephemeral ids by ID_NOBODY */ + (void) bcopy(linep, uidp + idlen, limit - linep + extra_chars); + (void) snprintf(uidp, idlen + 1, "%u:%u", uid, gid); + *(uidp + idlen) = ':'; /* restore : that was overwritten by snprintf */ + *linelenp = newlinelen; + return (NSS_STR_PARSE_SUCCESS); } static files_backend_op_t passwd_ops[] = { diff --git a/usr/src/lib/nsswitch/ldap/common/getgrent.c b/usr/src/lib/nsswitch/ldap/common/getgrent.c index f9d92227dd..bfcec21fbb 100644 --- a/usr/src/lib/nsswitch/ldap/common/getgrent.c +++ b/usr/src/lib/nsswitch/ldap/common/getgrent.c @@ -23,8 +23,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <grp.h> #include "ldap_common.h" @@ -74,9 +72,13 @@ _nss_ldap_group2str(ldap_backend_ptr be, nss_XbyY_args_t *argp) int firstime = 1; char *buffer = NULL; ns_ldap_result_t *result = be->result; - char **gname, **passwd, **gid, *password; + char **gname, **passwd, **gid, *password, *end; + char gid_nobody[NOBODY_STR_LEN]; + char *gid_nobody_v[1]; ns_ldap_attr_t *members; + (void) snprintf(gid_nobody, sizeof (gid_nobody), "%u", GID_NOBODY); + gid_nobody_v[0] = gid_nobody; if (result == NULL) return (NSS_STR_PARSE_PARSE); @@ -121,6 +123,9 @@ _nss_ldap_group2str(ldap_backend_ptr be, nss_XbyY_args_t *argp) nss_result = NSS_STR_PARSE_PARSE; goto result_grp2str; } + /* Validate GID */ + if (strtoul(gid[0], &end, 10) > MAXUID) + gid = gid_nobody_v; len = snprintf(buffer, buflen, "%s:%s:%s:", gname[0], password, gid[0]); TEST_AND_ADJUST(len, buffer, buflen, result_grp2str); @@ -206,6 +211,9 @@ getbygid(ldap_backend_ptr be, void *a) char userdata[SEARCHFILTERLEN]; int ret; + if (argp->key.uid > MAXUID) + return ((nss_status_t)NSS_NOTFOUND); + ret = snprintf(searchfilter, sizeof (searchfilter), _F_GETGRGID, argp->key.uid); if (ret >= sizeof (searchfilter) || ret < 0) diff --git a/usr/src/lib/nsswitch/ldap/common/getpwnam.c b/usr/src/lib/nsswitch/ldap/common/getpwnam.c index a2c9ff40c9..4b98ada4d5 100644 --- a/usr/src/lib/nsswitch/ldap/common/getpwnam.c +++ b/usr/src/lib/nsswitch/ldap/common/getpwnam.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <pwd.h> #include "ldap_common.h" @@ -79,6 +77,14 @@ _nss_ldap_passwd2str(ldap_backend_ptr be, nss_XbyY_args_t *argp) char **uid_v, **uidn_v, **gidn_v; char **gecos_v, **homedir_v, **shell_v; char *NULL_STR = ""; + char uid_nobody[NOBODY_STR_LEN]; + char gid_nobody[NOBODY_STR_LEN], *end; + char *uid_nobody_v[1], *gid_nobody_v[1]; + + (void) snprintf(uid_nobody, sizeof (uid_nobody), "%u", UID_NOBODY); + uid_nobody_v[0] = uid_nobody; + (void) snprintf(gid_nobody, sizeof (gid_nobody), "%u", GID_NOBODY); + gid_nobody_v[0] = gid_nobody; if (result == NULL) return (NSS_STR_PARSE_PARSE); @@ -98,10 +104,15 @@ _nss_ldap_passwd2str(ldap_backend_ptr be, nss_XbyY_args_t *argp) uidn_v = __ns_ldap_getAttr(entry, _PWD_UIDNUMBER); gidn_v = __ns_ldap_getAttr(entry, _PWD_GIDNUMBER); if (uid_v == NULL || uidn_v == NULL || gidn_v == NULL || - uid_v[0] == NULL || uidn_v[0] == NULL || gidn_v[0] == NULL) { + uid_v[0] == NULL || uidn_v[0] == NULL || gidn_v[0] == NULL) { nss_result = NSS_STR_PARSE_PARSE; goto result_pwd2str; } + /* Validate UID and GID */ + if (strtoul(uidn_v[0], &end, 10) > MAXUID) + uidn_v = uid_nobody_v; + if (strtoul(gidn_v[0], &end, 10) > MAXUID) + gidn_v = gid_nobody_v; str_len = strlen(uid_v[0]) + strlen(uidn_v[0]) + strlen(gidn_v[0]); if (str_len > buflen) { nss_result = NSS_STR_PARSE_ERANGE; @@ -140,15 +151,14 @@ _nss_ldap_passwd2str(ldap_backend_ptr be, nss_XbyY_args_t *argp) } (void) snprintf(be->buffer, be->buflen, - "%s:%s:%s:%s:%s:%s:%s", - uid_v[0], "x", uidn_v[0], gidn_v[0], - gecos_v[0], homedir_v[0], shell_v[0]); + "%s:%s:%s:%s:%s:%s:%s", + uid_v[0], "x", uidn_v[0], gidn_v[0], + gecos_v[0], homedir_v[0], shell_v[0]); } else { (void) snprintf(argp->buf.buffer, (str_len + 8), - "%s:%s:%s:%s:%s:%s:%s", - uid_v[0], "x", uidn_v[0], gidn_v[0], - gecos_v[0], homedir_v[0], shell_v[0]); - + "%s:%s:%s:%s:%s:%s:%s", + uid_v[0], "x", uidn_v[0], gidn_v[0], + gecos_v[0], homedir_v[0], shell_v[0]); } result_pwd2str: @@ -186,8 +196,7 @@ getbyname(ldap_backend_ptr be, void *a) return ((nss_status_t)NSS_NOTFOUND); return ((nss_status_t)_nss_ldap_lookup(be, argp, - _PASSWD, searchfilter, NULL, - _merge_SSD_filter, userdata)); + _PASSWD, searchfilter, NULL, _merge_SSD_filter, userdata)); } @@ -207,6 +216,9 @@ getbyuid(ldap_backend_ptr be, void *a) char userdata[SEARCHFILTERLEN]; int ret; + if (argp->key.uid > MAXUID) + return ((nss_status_t)NSS_NOTFOUND); + ret = snprintf(searchfilter, sizeof (searchfilter), _F_GETPWUID, (long)argp->key.uid); if (ret >= sizeof (searchfilter) || ret < 0) @@ -218,8 +230,7 @@ getbyuid(ldap_backend_ptr be, void *a) return ((nss_status_t)NSS_NOTFOUND); return ((nss_status_t)_nss_ldap_lookup(be, argp, - _PASSWD, searchfilter, NULL, - _merge_SSD_filter, userdata)); + _PASSWD, searchfilter, NULL, _merge_SSD_filter, userdata)); } static ldap_backend_op_t passwd_ops[] = { @@ -245,6 +256,6 @@ _nss_ldap_passwd_constr(const char *dummy1, const char *dummy2, { return ((nss_backend_t *)_nss_ldap_constr(passwd_ops, - sizeof (passwd_ops)/sizeof (passwd_ops[0]), - _PASSWD, pwd_attrs, _nss_ldap_passwd2str)); + sizeof (passwd_ops)/sizeof (passwd_ops[0]), + _PASSWD, pwd_attrs, _nss_ldap_passwd2str)); } diff --git a/usr/src/lib/nsswitch/ldap/common/ldap_common.h b/usr/src/lib/nsswitch/ldap/common/ldap_common.h index d6e08680c4..7d9b56613a 100644 --- a/usr/src/lib/nsswitch/ldap/common/ldap_common.h +++ b/usr/src/lib/nsswitch/ldap/common/ldap_common.h @@ -19,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _LDAP_COMMON_H #define _LDAP_COMMON_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -88,6 +86,16 @@ extern "C" { buffer += len; \ buflen -= len; +/* + * We need to use UID_NOBODY and GID_NOBODY as strings. Therefore we use + * snprintf to convert [U|G]ID_NOBODY into a string. The target buffer + * size was chosen as 21 to allow the largest 64-bit number to be stored + * as string in it. Right now uid_t and gid_t are 32-bit so we don't + * really need 21 characters but it does allow for future expansion + * without having to modify this code. + */ +#define NOBODY_STR_LEN 21 + /* * Superset the nss_backend_t abstract data type. This ADT has diff --git a/usr/src/lib/nsswitch/nis/common/getgrent.c b/usr/src/lib/nsswitch/nis/common/getgrent.c index f6447a9d4c..45fe104a9a 100644 --- a/usr/src/lib/nsswitch/nis/common/getgrent.c +++ b/usr/src/lib/nsswitch/nis/common/getgrent.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -27,8 +27,6 @@ * nis/getgrent.c -- "nis" backend for nsswitch "group" database */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <grp.h> #include <pwd.h> #include "nis_common.h" @@ -58,10 +56,70 @@ getbygid(be, a) nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; char gidstr[12]; /* More than enough */ + if (argp->key.gid > MAXUID) + return (NSS_NOTFOUND); (void) snprintf(gidstr, 12, "%d", argp->key.gid); return (_nss_nis_lookup(be, argp, 0, "group.bygid", gidstr, 0)); } +/* + * Validates group entry replacing gid > MAXUID by GID_NOBODY. + */ +int +validate_group_ids(char **linepp, int *linelenp, int allocbuf) +{ + char *linep, *limit, *gidp, *newline; + ulong_t gid; + int oldgidlen, idlen; + int linelen = *linelenp, newlinelen; + + linep = *linepp; + limit = linep + linelen; + + /* +/- entries valid for compat source only */ + if (linelen == 0 || *linep == '+' || *linep == '-') + return (NSS_STR_PARSE_SUCCESS); + + while (linep < limit && *linep++ != ':') /* skip groupname */ + continue; + while (linep < limit && *linep++ != ':') /* skip password */ + continue; + if (linep == limit) + return (NSS_STR_PARSE_PARSE); + + gidp = linep; + gid = strtoul(gidp, (char **)&linep, 10); /* grab gid */ + oldgidlen = linep - gidp; + if (linep >= limit || oldgidlen == 0) + return (NSS_STR_PARSE_PARSE); + + if (gid <= MAXUID) + return (NSS_STR_PARSE_SUCCESS); + + idlen = snprintf(NULL, 0, "%u", GID_NOBODY); + newlinelen = linelen + idlen - oldgidlen; + if (newlinelen > linelen) { + /* need a larger buffer */ + if (!allocbuf || (newline = malloc(newlinelen + 1)) == NULL) + return (NSS_STR_PARSE_ERANGE); + /* Replace ephemeral ids by ID_NOBODY in the new buffer */ + *(gidp - 1) = '\0'; + (void) snprintf(newline, newlinelen + 1, "%s:%u%s", + *linepp, GID_NOBODY, linep); + free(*linepp); + *linepp = newline; + *linelenp = newlinelen; + return (NSS_STR_PARSE_SUCCESS); + } + + /* Replace ephemeral gid by GID_NOBODY in the same buffer */ + (void) bcopy(linep, gidp + idlen, limit - linep + 1); + (void) snprintf(gidp, idlen + 1, "%u", GID_NOBODY); + *(gidp + idlen) = ':'; + *linelenp = newlinelen; + return (NSS_STR_PARSE_SUCCESS); +} + static nss_status_t getbymember(be, a) nis_backend_ptr_t be; @@ -225,16 +283,15 @@ netid_lookup(struct nss_groupsbymem *argp) } if ((res = _nss_nis_ypmatch(domain, "netid.byname", netname, - &val, &vallen, 0)) != NSS_SUCCESS) { + &val, &vallen, 0)) != NSS_SUCCESS) { return (res); } (void) strtok_r(val, "#", &lasts); parse_res = parse_netid(val, argp->gid_array, argp->maxgids, - &argp->numgids); + &argp->numgids); free(val); return ((parse_res == NSS_STR_PARSE_SUCCESS) - ? NSS_SUCCESS - : NSS_NOTFOUND); + ? NSS_SUCCESS : NSS_NOTFOUND); } diff --git a/usr/src/lib/nsswitch/nis/common/getpwnam.c b/usr/src/lib/nsswitch/nis/common/getpwnam.c index a23ee8af5c..ad55f9e5ca 100644 --- a/usr/src/lib/nsswitch/nis/common/getpwnam.c +++ b/usr/src/lib/nsswitch/nis/common/getpwnam.c @@ -19,14 +19,12 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * nis/getpwnam.c -- "nis" backend for nsswitch "passwd" database */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <pwd.h> #include "nis_common.h" @@ -49,10 +47,81 @@ getbyuid(be, a) nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; char uidstr[12]; /* More than enough */ - (void) snprintf(uidstr, 12, "%ld", argp->key.uid); + if (argp->key.uid > MAXUID) + return (NSS_NOTFOUND); + (void) snprintf(uidstr, 12, "%u", argp->key.uid); return (_nss_nis_lookup(be, argp, 0, "passwd.byuid", uidstr, 0)); } +/* + * Validates passwd entry replacing uid/gid > MAXUID by ID_NOBODY. + */ +int +validate_passwd_ids(char **linepp, int *linelenp, int allocbuf) +{ + char *linep, *limit, *uidp, *gidp, *newline; + uid_t uid; + gid_t gid; + ulong_t uidl, gidl; + int olduidlen, oldgidlen, idlen; + int linelen = *linelenp, newlinelen; + + linep = *linepp; + limit = linep + linelen; + + /* +/- entries valid for compat source only */ + if (linelen == 0 || *linep == '+' || *linep == '-') + return (NSS_STR_PARSE_SUCCESS); + + while (linep < limit && *linep++ != ':') /* skip username */ + continue; + while (linep < limit && *linep++ != ':') /* skip password */ + continue; + if (linep == limit) + return (NSS_STR_PARSE_PARSE); + + uidp = linep; + uidl = strtoul(uidp, (char **)&linep, 10); /* grab uid */ + olduidlen = linep - uidp; + if (++linep >= limit || olduidlen == 0) + return (NSS_STR_PARSE_PARSE); + + gidp = linep; + gidl = strtoul(gidp, (char **)&linep, 10); /* grab gid */ + oldgidlen = linep - gidp; + if (linep >= limit || oldgidlen == 0) + return (NSS_STR_PARSE_PARSE); + + if (uidl <= MAXUID && gidl <= MAXUID) + return (NSS_STR_PARSE_SUCCESS); + uid = (uidl > MAXUID) ? UID_NOBODY : (uid_t)uidl; + gid = (gidl > MAXUID) ? GID_NOBODY : (gid_t)gidl; + + /* Check if we have enough space in the buffer */ + idlen = snprintf(NULL, 0, "%u:%u", uid, gid); + newlinelen = linelen + idlen - olduidlen - oldgidlen - 1; + if (newlinelen > linelen) { + /* need a larger buffer */ + if (!allocbuf || (newline = malloc(newlinelen + 1)) == NULL) + return (NSS_STR_PARSE_ERANGE); + /* Replace ephemeral ids by ID_NOBODY in the new buffer */ + *(uidp - 1) = '\0'; + (void) snprintf(newline, newlinelen + 1, "%s:%u:%u%s", + *linepp, uid, gid, linep); + free(*linepp); + *linepp = newline; + *linelenp = newlinelen; + return (NSS_STR_PARSE_SUCCESS); + } + + /* Replace ephemeral ids by ID_NOBODY in the same buffer */ + (void) bcopy(linep, uidp + idlen, limit - linep + 1); + (void) snprintf(uidp, idlen + 1, "%u:%u", uid, gid); + *(uidp + idlen) = ':'; /* restore : that was overwritten by snprintf */ + *linelenp = newlinelen; + return (NSS_STR_PARSE_SUCCESS); +} + static nis_backend_op_t passwd_ops[] = { _nss_nis_destr, _nss_nis_endent, diff --git a/usr/src/lib/nsswitch/nis/common/nis_common.c b/usr/src/lib/nsswitch/nis/common/nis_common.c index 6a4e6b4c13..a7294a85fb 100644 --- a/usr/src/lib/nsswitch/nis/common/nis_common.c +++ b/usr/src/lib/nsswitch/nis/common/nis_common.c @@ -30,8 +30,6 @@ * Common code and structures used by name-service-switch "nis" backends. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include "nis_common.h" #include <string.h> #include <synch.h> @@ -235,6 +233,17 @@ _nss_nis_lookup(be, args, netdb, map, key, ypstatusp) return (res); } + parsestat = NSS_STR_PARSE_SUCCESS; + if (strcmp(map, "passwd.byname") == 0 || + strcmp(map, "passwd.byuid") == 0) { + parsestat = validate_passwd_ids(&val, &vallen, 1); + } else if (strcmp(map, "group.byname") == 0) + parsestat = validate_group_ids(&val, &vallen, 1); + if (parsestat != NSS_STR_PARSE_SUCCESS) { + free(val); + return (NSS_NOTFOUND); + } + free_ptr = val; if (netdb) { diff --git a/usr/src/lib/nsswitch/nis/common/nis_common.h b/usr/src/lib/nsswitch/nis/common/nis_common.h index 7aaf6c4989..aabac9ab98 100644 --- a/usr/src/lib/nsswitch/nis/common/nis_common.h +++ b/usr/src/lib/nsswitch/nis/common/nis_common.h @@ -33,9 +33,9 @@ #ifndef _NIS_COMMON_H #define _NIS_COMMON_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <nss_dbdefs.h> +#include <stdlib.h> +#include <strings.h> #include <signal.h> #include <rpcsvc/ypclnt.h> #include <rpcsvc/yp_prot.h> @@ -131,6 +131,10 @@ extern int __yp_next_cflookup(char *, char *, char *, int, char **, int *, extern int __yp_all_cflookup(char *, char *, struct ypall_callback *, int); +/* functions to validate passwd and group ids */ +extern int validate_passwd_ids(char **linepp, int *linelenp, int allocbuf); +extern int validate_group_ids(char **linepp, int *linelenp, int allocbuf); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/nsswitch/nisplus/common/getgrent.c b/usr/src/lib/nsswitch/nisplus/common/getgrent.c index c5c76514b6..1c8cdeb349 100644 --- a/usr/src/lib/nsswitch/nisplus/common/getgrent.c +++ b/usr/src/lib/nsswitch/nisplus/common/getgrent.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -27,8 +27,6 @@ * nisplus/getgrent.c -- NIS+ backend for nsswitch "group" database */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <grp.h> #include <string.h> #include <stdlib.h> @@ -71,6 +69,9 @@ getbygid(be, a) nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; char gidstr[12]; /* More than enough */ + if (argp->key.gid > MAXUID) + return (NSS_NOTFOUND); + (void) snprintf(gidstr, 12, "%u", argp->key.gid); return (_nss_nisplus_lookup(be, argp, GR_TAG_GID, gidstr)); } @@ -194,6 +195,8 @@ nis_object2str(nobj, obj, be, argp) nss_XbyY_args_t *argp; { char *buffer, *name, *passwd, *gid, *members; + ulong_t gidl; + char gid_nobody[NOBODY_STR_LEN]; int buflen, namelen, passwdlen, gidlen, memberslen; char *endnum; struct entry_col *ecol; @@ -224,9 +227,15 @@ nis_object2str(nobj, obj, be, argp) /* gid: group id */ __NISPLUS_GETCOL_OR_RETURN(ecol, GR_NDX_GID, gidlen, gid); - (void) strtol(gid, &endnum, 10); + gidl = strtoul(gid, &endnum, 10); if (*endnum != 0 || gid == endnum) return (NSS_STR_PARSE_PARSE); + if (gidl > MAXUID) { + (void) snprintf(gid_nobody, sizeof (gid_nobody), + "%u", GID_NOBODY); + gid = gid_nobody; + gidlen = strlen(gid); + } /* members: gid list */ __NISPLUS_GETCOL_OR_EMPTY(ecol, GR_NDX_MEM, memberslen, members); diff --git a/usr/src/lib/nsswitch/nisplus/common/getpwnam.c b/usr/src/lib/nsswitch/nisplus/common/getpwnam.c index 19807456e5..c0d3d1bd4f 100644 --- a/usr/src/lib/nsswitch/nisplus/common/getpwnam.c +++ b/usr/src/lib/nsswitch/nisplus/common/getpwnam.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -27,8 +27,6 @@ * nisplus/getpwnam.c -- NIS+ backend for nsswitch "passwd" database */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <pwd.h> #include <stdlib.h> @@ -54,6 +52,9 @@ getbyuid(be, a) nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; char uidstr[12]; /* More than enough */ + if (argp->key.uid > MAXUID) + return (NSS_NOTFOUND); + (void) snprintf(uidstr, 12, "%ld", argp->key.uid); return (_nss_nisplus_lookup(be, argp, PW_TAG_UID, uidstr)); } @@ -73,6 +74,9 @@ nis_object2str(nobj, obj, be, argp) nss_XbyY_args_t *argp; { char *buffer, *name, *uid, *gid, *gecos; + ulong_t uidl, gidl; + char uid_nobody[NOBODY_STR_LEN]; + char gid_nobody[NOBODY_STR_LEN]; char *dir, *shell, *endnum; int buflen, namelen, uidlen, gidlen, gecoslen; int dirlen, shelllen; @@ -100,15 +104,27 @@ nis_object2str(nobj, obj, be, argp) /* uid: user id. Must be numeric */ __NISPLUS_GETCOL_OR_RETURN(ecol, PW_NDX_UID, uidlen, uid); - (void) strtol(uid, &endnum, 10); + uidl = strtoul(uid, &endnum, 10); if (*endnum != 0 || uid == endnum) return (NSS_STR_PARSE_PARSE); + if (uidl > MAXUID) { + (void) snprintf(uid_nobody, sizeof (uid_nobody), + "%u", UID_NOBODY); + uid = uid_nobody; + uidlen = strlen(uid); + } /* gid: primary group id. Must be numeric */ __NISPLUS_GETCOL_OR_RETURN(ecol, PW_NDX_GID, gidlen, gid); - (void) strtol(gid, &endnum, 10); + gidl = strtoul(gid, &endnum, 10); if (*endnum != 0 || gid == endnum) return (NSS_STR_PARSE_PARSE); + if (gidl > MAXUID) { + (void) snprintf(gid_nobody, sizeof (gid_nobody), + "%u", GID_NOBODY); + gid = gid_nobody; + gidlen = strlen(gid); + } /* gecos: user's real name */ __NISPLUS_GETCOL_OR_EMPTY(ecol, PW_NDX_GCOS, gecoslen, gecos); diff --git a/usr/src/lib/nsswitch/nisplus/common/nisplus_common.h b/usr/src/lib/nsswitch/nisplus/common/nisplus_common.h index b446c0b23a..2af5717262 100644 --- a/usr/src/lib/nsswitch/nisplus/common/nisplus_common.h +++ b/usr/src/lib/nsswitch/nisplus/common/nisplus_common.h @@ -28,8 +28,6 @@ * Common code used by name-service-switch "nisplus" backends */ -#pragma ident "%Z%%M% %I% %E% SMI" - #ifndef _NISPLUS_COMMON_H #define _NISPLUS_COMMON_H @@ -42,6 +40,9 @@ */ #define NIS_LIST_COMMON (FOLLOW_LINKS | FOLLOW_PATH) +/* See the comment in $SRC/lib/nsswitch/ldap/common/ldap_common.h */ +#define NOBODY_STR_LEN 21 + typedef struct nisplus_backend *nisplus_backend_ptr_t; typedef nss_status_t (*nisplus_backend_op_t)(nisplus_backend_ptr_t, void *); diff --git a/usr/src/lib/passwdutil/switch_utils.c b/usr/src/lib/passwdutil/switch_utils.c index 3b2221bff7..8145ed360e 100644 --- a/usr/src/lib/passwdutil/switch_utils.c +++ b/usr/src/lib/passwdutil/switch_utils.c @@ -23,7 +23,6 @@ * Use is subject to license terms. */ - #include <sys/types.h> #include <nsswitch.h> #include <stdlib.h> @@ -134,6 +133,8 @@ get_ns(pwu_repository_t *rep, int accesstype) enum __nsw_parse_err pserr; struct __nsw_lookup *lkp; struct __nsw_lookup *lkp2; + struct __nsw_lookup *lkp3; + struct __nsw_lookup *lkpn; int result = REP_NOREP; if (rep != PWU_DEFAULT_REP) { @@ -150,7 +151,7 @@ get_ns(pwu_repository_t *rep, int accesstype) * find the name service switch entry. (Backward compat) */ syslog(LOG_ERR, "passwdutil.so: nameservice switch entry for " - "passwd not found."); + "passwd not found."); result = REP_FILES | REP_NIS; return (result); } @@ -158,9 +159,13 @@ get_ns(pwu_repository_t *rep, int accesstype) lkp = conf->lookups; /* - * Supported nsswitch.conf can have a maximum of 2 repositories. + * Supported nsswitch.conf can have a maximum of 3 repositories. * If we encounter an unsupported nsswitch.conf, we return REP_NSS * to fall back to the nsswitch backend. + * + * Note that specifying 'ad' in the configuration is acceptable + * though changing AD users' passwords through passwd(1) is not. + * Therefore "ad" will be silently ignored. */ if (conf->num_lookups == 1) { /* files or compat */ @@ -185,6 +190,35 @@ get_ns(pwu_repository_t *rep, int accesstype) result |= REP_NIS; else if (strcmp(lkp2->service_name, "nisplus") == 0) result |= REP_NISPLUS; + else if (strcmp(lkp2->service_name, "ad") != 0) + result = REP_NSS; + /* AD is ignored */ + } else { + result = REP_NSS; + } + } else if (conf->num_lookups == 3) { + /* + * Valid configurations with 3 repositories are: + * files ad [nis | ldap | nisplus] OR + * files [nis | ldap | nisplus] ad + */ + lkp2 = lkp->next; + lkp3 = lkp2->next; + if (strcmp(lkp2->service_name, "ad") == 0) + lkpn = lkp3; + else if (strcmp(lkp3->service_name, "ad") == 0) + lkpn = lkp2; + else + lkpn = NULL; + if (strcmp(lkp->service_name, "files") == 0 && + lkpn != NULL) { + result = REP_FILES; + if (strcmp(lkpn->service_name, "ldap") == 0) + result |= REP_LDAP; + else if (strcmp(lkpn->service_name, "nis") == 0) + result |= REP_NIS; + else if (strcmp(lkpn->service_name, "nisplus") == 0) + result |= REP_NISPLUS; else result = REP_NSS; } else { diff --git a/usr/src/pkgdefs/SUNWarc/prototype_com b/usr/src/pkgdefs/SUNWarc/prototype_com index 61f806d12c..293d707cca 100644 --- a/usr/src/pkgdefs/SUNWarc/prototype_com +++ b/usr/src/pkgdefs/SUNWarc/prototype_com @@ -23,8 +23,6 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# # # This required package information file contains a list of package contents. @@ -72,6 +70,8 @@ s none usr/ccs/lib/values-Xt.o=../../lib/values-Xt.o d none usr/lib 755 root bin s none usr/lib/llib-ladm=../../lib/llib-ladm s none usr/lib/llib-ladm.ln=../../lib/llib-ladm.ln +f none usr/lib/llib-ladutils 644 root bin +f none usr/lib/llib-ladutils.ln 644 root bin s none usr/lib/llib-laio=../../lib/llib-laio s none usr/lib/llib-laio.ln=../../lib/llib-laio.ln s none usr/lib/llib-lpam=../../lib/llib-lpam diff --git a/usr/src/pkgdefs/SUNWarc/prototype_i386 b/usr/src/pkgdefs/SUNWarc/prototype_i386 index 924d200125..d08b072f5e 100644 --- a/usr/src/pkgdefs/SUNWarc/prototype_i386 +++ b/usr/src/pkgdefs/SUNWarc/prototype_i386 @@ -23,8 +23,6 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# # # This required package information file contains a list of package contents. @@ -68,6 +66,7 @@ f none usr/lib/amd64/values-Xt.o 644 root bin f none usr/lib/amd64/values-xpg4.o 644 root bin f none usr/lib/amd64/values-xpg6.o 644 root bin s none usr/lib/amd64/llib-ladm.ln=../../../lib/amd64/llib-ladm.ln +f none usr/lib/amd64/llib-ladutils.ln 644 root bin s none usr/lib/amd64/llib-laio.ln=../../../lib/amd64/llib-laio.ln f none usr/lib/amd64/llib-lbsdmalloc.ln 644 root bin s none usr/lib/amd64/llib-lbsm.ln=../../../lib/amd64/llib-lbsm.ln diff --git a/usr/src/pkgdefs/SUNWarc/prototype_sparc b/usr/src/pkgdefs/SUNWarc/prototype_sparc index fe4b089836..23b7473759 100644 --- a/usr/src/pkgdefs/SUNWarc/prototype_sparc +++ b/usr/src/pkgdefs/SUNWarc/prototype_sparc @@ -23,8 +23,6 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# # # This required package information file contains a list of package contents. @@ -64,6 +62,7 @@ f none usr/lib/sparcv9/values-Xt.o 644 root bin f none usr/lib/sparcv9/values-xpg4.o 644 root bin f none usr/lib/sparcv9/values-xpg6.o 644 root bin s none usr/lib/sparcv9/llib-ladm.ln=../../../lib/sparcv9/llib-ladm.ln +f none usr/lib/sparcv9/llib-ladutils.ln 644 root bin s none usr/lib/sparcv9/llib-laio.ln=../../../lib/sparcv9/llib-laio.ln f none usr/lib/sparcv9/llib-lbsdmalloc.ln 644 root bin s none usr/lib/sparcv9/llib-lbsm.ln=../../../lib/sparcv9/llib-lbsm.ln diff --git a/usr/src/pkgdefs/SUNWcsl/prototype_com b/usr/src/pkgdefs/SUNWcsl/prototype_com index 6c8a7e5e0e..cde1a248cf 100644 --- a/usr/src/pkgdefs/SUNWcsl/prototype_com +++ b/usr/src/pkgdefs/SUNWcsl/prototype_com @@ -69,6 +69,8 @@ f none usr/lib/extendedFILE.so.1 755 root bin f none usr/lib/lib.b 444 root bin s none usr/lib/libadm.so=../../lib/libadm.so.1 s none usr/lib/libadm.so.1=../../lib/libadm.so.1 +f none usr/lib/libadutils.so.1 755 root bin +s none usr/lib/libadutils.so=./libadutils.so.1 s none usr/lib/libaio.so=../../lib/libaio.so.1 s none usr/lib/libaio.so.1=../../lib/libaio.so.1 f none usr/lib/libast.so.1 755 root bin @@ -298,6 +300,7 @@ s none usr/lib/nss_compat.so.1=../../lib/nss_compat.so.1 s none usr/lib/nss_dns.so.1=../../lib/nss_dns.so.1 s none usr/lib/nss_files.so.1=../../lib/nss_files.so.1 f none usr/lib/nss_ldap.so.1 755 root bin +f none usr/lib/nss_ad.so.1 755 root bin s none usr/lib/nss_nis.so.1=../../lib/nss_nis.so.1 s none usr/lib/nss_nisplus.so.1=../../lib/nss_nisplus.so.1 s none usr/lib/nss_user.so.1=../../lib/nss_user.so.1 diff --git a/usr/src/pkgdefs/SUNWcsl/prototype_i386 b/usr/src/pkgdefs/SUNWcsl/prototype_i386 index 4b376fba9d..542bea8cdf 100644 --- a/usr/src/pkgdefs/SUNWcsl/prototype_i386 +++ b/usr/src/pkgdefs/SUNWcsl/prototype_i386 @@ -156,6 +156,8 @@ f none usr/lib/security/amd64/pkcs11_softtoken.so.1 755 root bin s none usr/lib/security/amd64/pkcs11_softtoken.so=./pkcs11_softtoken.so.1 s none usr/lib/64=amd64 d none usr/lib/amd64 755 root bin +f none usr/lib/amd64/libadutils.so.1 755 root bin +s none usr/lib/amd64/libadutils.so=./libadutils.so.1 f none usr/lib/amd64/libbsdmalloc.so.1 755 root bin s none usr/lib/amd64/libbsdmalloc.so=libbsdmalloc.so.1 s none usr/lib/amd64/libbsm.so.1=../../../lib/amd64/libbsm.so.1 @@ -361,6 +363,7 @@ s none usr/lib/amd64/nss_compat.so.1=../../../lib/amd64/nss_compat.so.1 s none usr/lib/amd64/nss_dns.so.1=../../../lib/amd64/nss_dns.so.1 s none usr/lib/amd64/nss_files.so.1=../../../lib/amd64/nss_files.so.1 f none usr/lib/amd64/nss_ldap.so.1 755 root bin +f none usr/lib/amd64/nss_ad.so.1 755 root bin s none usr/lib/amd64/nss_nis.so.1=../../../lib/amd64/nss_nis.so.1 s none usr/lib/amd64/nss_nisplus.so.1=../../../lib/amd64/nss_nisplus.so.1 s none usr/lib/amd64/nss_user.so.1=../../../lib/amd64/nss_user.so.1 diff --git a/usr/src/pkgdefs/SUNWcsl/prototype_sparc b/usr/src/pkgdefs/SUNWcsl/prototype_sparc index e8117dff2c..407cbed963 100644 --- a/usr/src/pkgdefs/SUNWcsl/prototype_sparc +++ b/usr/src/pkgdefs/SUNWcsl/prototype_sparc @@ -149,6 +149,8 @@ f none usr/lib/security/sparcv9/pkcs11_softtoken.so.1 755 root bin s none usr/lib/security/sparcv9/pkcs11_softtoken.so=./pkcs11_softtoken.so.1 s none usr/lib/64=sparcv9 d none usr/lib/sparcv9 755 root bin +f none usr/lib/sparcv9/libadutils.so.1 755 root bin +s none usr/lib/sparcv9/libadutils.so=./libadutils.so.1 f none usr/lib/sparcv9/libbsdmalloc.so.1 755 root bin s none usr/lib/sparcv9/libbsdmalloc.so=libbsdmalloc.so.1 s none usr/lib/sparcv9/libbsm.so.1=../../../lib/sparcv9/libbsm.so.1 @@ -352,6 +354,7 @@ s none usr/lib/sparcv9/nss_compat.so.1=../../../lib/sparcv9/nss_compat.so.1 s none usr/lib/sparcv9/nss_dns.so.1=../../../lib/sparcv9/nss_dns.so.1 s none usr/lib/sparcv9/nss_files.so.1=../../../lib/sparcv9/nss_files.so.1 f none usr/lib/sparcv9/nss_ldap.so.1 755 root bin +f none usr/lib/sparcv9/nss_ad.so.1 755 root bin s none usr/lib/sparcv9/nss_nis.so.1=../../../lib/sparcv9/nss_nis.so.1 s none usr/lib/sparcv9/nss_nisplus.so.1=../../../lib/sparcv9/nss_nisplus.so.1 s none usr/lib/sparcv9/nss_user.so.1=../../../lib/sparcv9/nss_user.so.1 diff --git a/usr/src/pkgdefs/SUNWcsr/prototype_com b/usr/src/pkgdefs/SUNWcsr/prototype_com index 6682b68886..0938a05481 100644 --- a/usr/src/pkgdefs/SUNWcsr/prototype_com +++ b/usr/src/pkgdefs/SUNWcsr/prototype_com @@ -225,6 +225,7 @@ e nsswitch etc/nsswitch.conf 644 root sys f none etc/nsswitch.files 644 root sys f none etc/nsswitch.ldap 644 root sys f none etc/nsswitch.dns 644 root sys +f none etc/nsswitch.ad 644 root sys d none etc/opt 755 root sys e passwd etc/passwd 644 root sys e etcprofile etc/profile 644 root sys |