summaryrefslogtreecommitdiff
path: root/usr/src/lib
diff options
context:
space:
mode:
authorBaban Kenkre <Baban.Kenkre@Sun.COM>2008-11-07 12:09:53 -0800
committerBaban Kenkre <Baban.Kenkre@Sun.COM>2008-11-07 12:09:53 -0800
commit2b4a78020b9c38d1b95e2f3fefa6d6e4be382d1f (patch)
treeb9f0bc817d950cefb1af4653dad8de547a17e061 /usr/src/lib
parent0a2b1d27cac02f57e17b310f8baeb1dda082c83a (diff)
downloadillumos-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/lib')
-rw-r--r--usr/src/lib/Makefile3
-rw-r--r--usr/src/lib/libadutils/Makefile49
-rw-r--r--usr/src/lib/libadutils/Makefile.com50
-rw-r--r--usr/src/lib/libadutils/amd64/Makefile29
-rw-r--r--usr/src/lib/libadutils/common/adutils.c1693
-rw-r--r--usr/src/lib/libadutils/common/adutils_impl.h146
-rw-r--r--usr/src/lib/libadutils/common/libadutils.h177
-rw-r--r--usr/src/lib/libadutils/common/llib-ladutils29
-rw-r--r--usr/src/lib/libadutils/common/mapfile-vers48
-rw-r--r--usr/src/lib/libadutils/i386/Makefile28
-rw-r--r--usr/src/lib/libadutils/sparc/Makefile28
-rw-r--r--usr/src/lib/libadutils/sparcv9/Makefile29
-rw-r--r--usr/src/lib/libc/port/gen/getgrnam_r.c12
-rw-r--r--usr/src/lib/libc/port/gen/getpwnam_r.c26
-rw-r--r--usr/src/lib/libc/port/gen/putpwent.c4
-rw-r--r--usr/src/lib/libidmap/common/idmap_api.c23
-rw-r--r--usr/src/lib/nsswitch/Makefile6
-rw-r--r--usr/src/lib/nsswitch/ad/Makefile46
-rw-r--r--usr/src/lib/nsswitch/ad/Makefile.com39
-rw-r--r--usr/src/lib/nsswitch/ad/amd64/Makefile32
-rw-r--r--usr/src/lib/nsswitch/ad/common/ad_common.c534
-rw-r--r--usr/src/lib/nsswitch/ad/common/ad_common.h132
-rw-r--r--usr/src/lib/nsswitch/ad/common/getgrent.c186
-rw-r--r--usr/src/lib/nsswitch/ad/common/getpwnam.c518
-rw-r--r--usr/src/lib/nsswitch/ad/common/getspent.c146
-rw-r--r--usr/src/lib/nsswitch/ad/common/mapfile-vers33
-rw-r--r--usr/src/lib/nsswitch/ad/i386/Makefile31
-rw-r--r--usr/src/lib/nsswitch/ad/sparc/Makefile31
-rw-r--r--usr/src/lib/nsswitch/ad/sparcv9/Makefile32
-rw-r--r--usr/src/lib/nsswitch/compat/common/compat_common.c55
-rw-r--r--usr/src/lib/nsswitch/compat/common/compat_common.h10
-rw-r--r--usr/src/lib/nsswitch/compat/common/getgrent.c53
-rw-r--r--usr/src/lib/nsswitch/compat/common/getpwent.c65
-rw-r--r--usr/src/lib/nsswitch/files/common/files_common.c50
-rw-r--r--usr/src/lib/nsswitch/files/common/files_common.h8
-rw-r--r--usr/src/lib/nsswitch/files/common/getgrent.c95
-rw-r--r--usr/src/lib/nsswitch/files/common/getpwnam.c100
-rw-r--r--usr/src/lib/nsswitch/ldap/common/getgrent.c14
-rw-r--r--usr/src/lib/nsswitch/ldap/common/getpwnam.c45
-rw-r--r--usr/src/lib/nsswitch/ldap/common/ldap_common.h14
-rw-r--r--usr/src/lib/nsswitch/nis/common/getgrent.c71
-rw-r--r--usr/src/lib/nsswitch/nis/common/getpwnam.c77
-rw-r--r--usr/src/lib/nsswitch/nis/common/nis_common.c13
-rw-r--r--usr/src/lib/nsswitch/nis/common/nis_common.h8
-rw-r--r--usr/src/lib/nsswitch/nisplus/common/getgrent.c17
-rw-r--r--usr/src/lib/nsswitch/nisplus/common/getpwnam.c26
-rw-r--r--usr/src/lib/nsswitch/nisplus/common/nisplus_common.h5
-rw-r--r--usr/src/lib/passwdutil/switch_utils.c40
48 files changed, 4763 insertions, 143 deletions
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 {