Add nsswitch support.
authorPeter Avalos <pavalos@theshell.com>
Mon, 22 Dec 2008 02:24:07 +0000 (21:24 -0500)
committerPeter Avalos <pavalos@theshell.com>
Sun, 28 Dec 2008 04:17:05 +0000 (23:17 -0500)
The nsswitch.conf(5) manual page has a description of nsswitch.
Curiously, we already had this manual page, even though we didn't
support it.

/etc/host.conf is removed from src/, but if host.conf exists and
nsswitch.conf does not, nsswitch.conf will be created using its
contents.

Included in this commit is a framework for nsswitch caching, nscd(8),
but it relies on a few upcoming changes to our libc before it will work.
For now, it's turned off.

Also this commit includes hesiod support which is not compiled by
default.  Add WANT_HESIOD=true to make.conf to get it working.

Obtained-from: FreeBSD

133 files changed:
Makefile.inc1
etc/Makefile
etc/defaults/make.conf
etc/defaults/rc.conf
etc/host.conf [deleted file]
etc/hosts
etc/nscd.conf [new file with mode: 0644]
etc/rc.d/Makefile
etc/rc.d/nscd [new file with mode: 0644]
etc/rc.d/nsswitch [new file with mode: 0644]
include/Makefile
include/dlfcn.h
include/grp.h
include/hesiod.h [new file with mode: 0644]
include/netdb.h
include/nss.h [new file with mode: 0644]
include/nsswitch.h [new file with mode: 0644]
include/pwd.h
include/rpc/rpcent.h
include/stdlib.h
lib/libc/Makefile.inc
lib/libc/gen/Makefile.inc
lib/libc/gen/dlfunc.c [new file with mode: 0644]
lib/libc/gen/dlopen.3
lib/libc/gen/getgrent.3
lib/libc/gen/getgrent.c
lib/libc/gen/getgrouplist.c
lib/libc/gen/getpwent.3
lib/libc/gen/getpwent.c
lib/libc/gen/getusershell.3
lib/libc/gen/getusershell.c
lib/libc/gen/pw_scan.c [moved from usr.sbin/pwd_mkdb/pw_scan.c with 65% similarity]
lib/libc/gen/pw_scan.h [copied from usr.sbin/pwd_mkdb/pw_scan.h with 78% similarity]
lib/libc/gen/pwcache.3
lib/libc/gen/pwcache.c
lib/libc/include/namespace.h
lib/libc/include/nscache.h [new file with mode: 0644]
lib/libc/include/nscachedcli.h [new file with mode: 0644]
lib/libc/include/nss_tls.h [new file with mode: 0644]
lib/libc/include/un-namespace.h
lib/libc/net/Makefile.inc
lib/libc/net/getaddrinfo.c
lib/libc/net/gethostbydns.c
lib/libc/net/gethostbyht.c
lib/libc/net/gethostbyname.3
lib/libc/net/gethostbynis.c
lib/libc/net/gethostnamadr.c
lib/libc/net/getipnodebyname.3
lib/libc/net/getnetbydns.c
lib/libc/net/getnetbyht.c
lib/libc/net/getnetbynis.c
lib/libc/net/getnetent.3
lib/libc/net/getnetnamadr.c
lib/libc/net/getproto.c
lib/libc/net/getprotoent.c
lib/libc/net/getprotoname.c
lib/libc/net/getservbyname.c [deleted file]
lib/libc/net/getservent.c
lib/libc/net/hesiod.3 [new file with mode: 0644]
lib/libc/net/hesiod.c [new file with mode: 0644]
lib/libc/net/name6.c
lib/libc/net/netdb_private.h [new file with mode: 0644]
lib/libc/net/nscache.c [new file with mode: 0644]
lib/libc/net/nscachedcli.c [new file with mode: 0644]
lib/libc/net/nsdispatch.3 [new file with mode: 0644]
lib/libc/net/nsdispatch.c [new file with mode: 0644]
lib/libc/net/nslexer.l [new file with mode: 0644]
lib/libc/net/nsparser.y [new file with mode: 0644]
lib/libc/net/nss_backends.h [copied from lib/libc/net/getproto.c with 54% similarity]
lib/libc/net/nss_compat.c [new file with mode: 0644]
lib/libc/rpc/getrpcent.c
share/man/man5/Makefile
share/man/man5/group.5
share/man/man5/hesiod.conf.5 [new file with mode: 0644]
share/man/man5/hosts.5
share/man/man5/make.conf.5
share/man/man5/nsswitch.conf.5
share/man/man5/passwd.5
share/man/man8/yp.8
usr.bin/Makefile
usr.bin/chpass/chpass.c
usr.bin/chpass/edit.c
usr.bin/chpass/pw_copy.c
usr.bin/hesinfo/Makefile [new file with mode: 0644]
usr.bin/hesinfo/hesinfo.1 [new file with mode: 0644]
usr.bin/hesinfo/hesinfo.c [new file with mode: 0644]
usr.bin/passwd/Makefile
usr.sbin/Makefile
usr.sbin/nscd/Makefile [new file with mode: 0644]
usr.sbin/nscd/agent.c [new file with mode: 0644]
usr.sbin/nscd/agent.h [new file with mode: 0644]
usr.sbin/nscd/agents/Makefile.inc [new file with mode: 0644]
usr.sbin/nscd/agents/group.c [new file with mode: 0644]
usr.sbin/nscd/agents/group.h [copied from lib/libc/net/getproto.c with 57% similarity]
usr.sbin/nscd/agents/passwd.c [new file with mode: 0644]
usr.sbin/nscd/agents/passwd.h [copied from lib/libc/net/getproto.c with 57% similarity]
usr.sbin/nscd/agents/services.c [new file with mode: 0644]
usr.sbin/nscd/agents/services.h [copied from lib/libc/net/getproto.c with 57% similarity]
usr.sbin/nscd/cachelib.c [new file with mode: 0644]
usr.sbin/nscd/cachelib.h [new file with mode: 0644]
usr.sbin/nscd/cacheplcs.c [new file with mode: 0644]
usr.sbin/nscd/cacheplcs.h [new file with mode: 0644]
usr.sbin/nscd/config.c [new file with mode: 0644]
usr.sbin/nscd/config.h [new file with mode: 0644]
usr.sbin/nscd/debug.c [new file with mode: 0644]
usr.sbin/nscd/debug.h [new file with mode: 0644]
usr.sbin/nscd/hashtable.h [new file with mode: 0644]
usr.sbin/nscd/log.c [new file with mode: 0644]
usr.sbin/nscd/log.h [copied from lib/libc/net/getproto.c with 54% similarity]
usr.sbin/nscd/mp_rs_query.c [new file with mode: 0644]
usr.sbin/nscd/mp_rs_query.h [copied from lib/libc/net/getproto.c with 57% similarity]
usr.sbin/nscd/mp_ws_query.c [new file with mode: 0644]
usr.sbin/nscd/mp_ws_query.h [copied from lib/libc/net/getproto.c with 57% similarity]
usr.sbin/nscd/nscd.8 [new file with mode: 0644]
usr.sbin/nscd/nscd.c [new file with mode: 0644]
usr.sbin/nscd/nscd.conf.5 [new file with mode: 0644]
usr.sbin/nscd/nscdcli.c [new file with mode: 0644]
usr.sbin/nscd/nscdcli.h [moved from usr.sbin/pwd_mkdb/pw_scan.h with 52% similarity]
usr.sbin/nscd/parser.c [new file with mode: 0644]
usr.sbin/nscd/parser.h [moved from lib/libc/net/getservbyport.c with 50% similarity]
usr.sbin/nscd/protocol.c [new file with mode: 0644]
usr.sbin/nscd/protocol.h [new file with mode: 0644]
usr.sbin/nscd/query.c [new file with mode: 0644]
usr.sbin/nscd/query.h [new file with mode: 0644]
usr.sbin/nscd/singletons.c [copied from lib/libc/net/getproto.c with 57% similarity]
usr.sbin/nscd/singletons.h [copied from lib/libc/net/getproto.c with 57% similarity]
usr.sbin/pwd_mkdb/Makefile
usr.sbin/pwd_mkdb/pwd_mkdb.8
usr.sbin/pwd_mkdb/pwd_mkdb.c
usr.sbin/rarpd/Makefile
usr.sbin/rarpd/rarpd.8
usr.sbin/rarpd/rarpd.c
usr.sbin/ypserv/ypserv.8

index 0e35c04..5799689 100644 (file)
@@ -762,7 +762,7 @@ bootstrap-tools:
     usr.bin/cmp usr.bin/xargs usr.bin/id usr.bin/env usr.bin/dirname \
     usr.bin/tail \
     usr.sbin/chown usr.sbin/mtree usr.sbin/config \
-    usr.sbin/btxld usr.sbin/pwd_mkdb usr.sbin/zic usr.sbin/makewhatis \
+    usr.sbin/btxld usr.sbin/zic usr.sbin/makewhatis \
     gnu/usr.bin/texinfo gnu/usr.bin/grep gnu/usr.bin/sort \
     usr.bin/gzip usr.bin/bzip2 usr.bin/mkcsmapper usr.bin/mkesdb
        ${ECHODIR} "===> ${_tool} (bootstrap-tools)"; \
index d0b07c2..30d1010 100644 (file)
@@ -28,10 +28,10 @@ BIN1=       amd.map auth.conf \
        crontab csh.cshrc csh.login csh.logout \
        devd.conf devices.conf dhclient.conf dm.conf dntpd.conf \
        ftpusers group \
-       hosts hosts.allow host.conf hosts.equiv hosts.lpd \
+       hosts hosts.allow hosts.equiv hosts.lpd \
        inetd.conf login.access login.conf \
        motd modems netconfig networks newsyslog.conf \
-       pf.conf phones printcap profile \
+       nscd.conf pf.conf phones printcap profile \
        remote sensorsd.conf \
        shells sysctl.conf syslog.conf usbd.conf \
        etc.${MACHINE_ARCH}/ttys
index af0cce9..29f8d5f 100644 (file)
@@ -95,6 +95,13 @@ THREAD_LIB?= thread_xu
 # To use GNU cpio as the standard cpio.  The default is bsdcpio.
 #WITH_GCPIO=   true
 #
+# To enable Hesiod support in libc
+#WANT_HESIOD=  true
+#
+# To disable name caching in the nsswitch subsystem.  The generic caching
+# daemon, nscd(8), will not be built either if this option is set.
+#NO_NS_CACHING=        true
+#
 # To avoid building various parts of the base system:
 #NO_BIND=      true    # do not build BIND
 #NO_CRYPT=     true    # do not build crypto code
index a7ad2ae..0f7895b 100644 (file)
@@ -404,6 +404,7 @@ cron_flags=""               # Which options to pass to the cron daemon.
 lpd_enable="NO"                # Run the line printer daemon.
 lpd_program="/usr/sbin/lpd"    # path to lpd, if you want a different one.
 lpd_flags=""           # Flags to lpd (if enabled).
+nscd_enable="NO"       # Run the nsswitch caching daemon.
 usbd_enable="NO"       # Run the usbd daemon.
 usbd_flags=""          # Flags to usbd (if enabled).
 devd_enable="NO"       # Rund devd(8) daemon.
diff --git a/etc/host.conf b/etc/host.conf
deleted file mode 100644 (file)
index eed1658..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# $FreeBSD: src/etc/host.conf,v 1.6 1999/08/27 23:23:41 peter Exp $
-# $DragonFly: src/etc/host.conf,v 1.2 2003/06/17 04:24:45 dillon Exp $
-# First try the /etc/hosts file
-hosts
-# Now try the nameserver next.
-bind
-# If you have YP/NIS configured, uncomment the next line
-# nis
index 9e01843..a36591b 100644 (file)
--- a/etc/hosts
+++ b/etc/hosts
@@ -1,4 +1,4 @@
-# $FreeBSD: src/etc/hosts,v 1.11.2.4 2003/02/06 20:36:58 dbaker Exp $
+# $FreeBSD: src/etc/hosts,v 1.16 2003/01/28 21:29:23 dbaker Exp $
 # $DragonFly: src/etc/hosts,v 1.2 2003/06/17 04:24:45 dillon Exp $
 #
 # Host Database
@@ -8,7 +8,7 @@
 # machine.
 #
 # In the presence of the domain name service or NIS, this file may
-# not be consulted at all; see /etc/host.conf for the resolution order.
+# not be consulted at all; see /etc/nsswitch.conf for the resolution order.
 #
 #
 ::1                    localhost localhost.my.domain
diff --git a/etc/nscd.conf b/etc/nscd.conf
new file mode 100644 (file)
index 0000000..8706c21
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# Default caching daemon configuration file
+# $FreeBSD: src/etc/nscd.conf,v 1.2 2007/10/18 08:26:20 bushman Exp $
+#
+
+enable-cache passwd yes
+enable-cache group yes
+enable-cache hosts yes
+enable-cache services yes
+enable-cache protocols yes
+enable-cache rpc yes
+enable-cache networks yes
index 37100d7..7a0f6d4 100644 (file)
@@ -18,13 +18,14 @@ FILES=      DAEMON LOGIN NETWORKING SERVERS abi accounting addswap adjkerntz \
        mixer motd mountcritlocal mountcritremote \
        mountd moused mroute6d mrouted msgs \
        named netif netoptions newsyslog \
-       network_ipv6 nfsclient nfsd nfsserver nisdomain \
+       network_ipv6 nfsclient nfsd nfsserver nisdomain nscd nsswitch \
        dntpd othermta pf pflog ppp ppp-user pppoed pwcheck \
        quota random rarpd rcconf.sh resident rndcontrol root route6d routed \
        routing rpcbind rtadvd rtsold rwho sysdb savecore sdpd securelevel \
        sendmail sensorsd serial sppp sshd statd swap1 syscons sysctl syslogd \
        timed ttys usbd varsym vinum virecover watchdogd wpa_supplicant \
        ypbind yppasswdd ypserv ypset ypupdated ypxfrd
+
 FILESDIR=      /etc/rc.d
 FILESMODE=     ${BINMODE}
 
diff --git a/etc/rc.d/nscd b/etc/rc.d/nscd
new file mode 100644 (file)
index 0000000..13bbd85
--- /dev/null
@@ -0,0 +1,52 @@
+#!/bin/sh
+#
+# $FreeBSD: src/etc/rc.d/nscd,v 1.7 2008/07/16 19:50:29 dougb Exp $
+#
+
+# PROVIDE: nscd
+# REQUIRE: DAEMON
+# BEFORE: LOGIN
+# KEYWORD: shutdown
+
+#
+# Add the following lines to /etc/rc.conf to enable nscd:
+#
+# nscd_enable="YES"
+#
+# See nscd(8) for flags
+#
+
+. /etc/rc.subr
+
+name=nscd
+rcvar=`set_rcvar`
+
+command=/usr/sbin/nscd
+extra_commands="flush"
+flush_cmd="${command} -I all"
+
+# usage: _nscd_set_option <option name> <default value>
+#
+_nscd_set_option() {
+       local _optname _defoptval _nscd_opt_val _cached_opt_val
+       _optname=$1
+       _defoptval=$2
+
+       _nscd_opt_val=$(eval "echo \$nscd_${_optname}")
+       _cached_opt_val=$(eval "echo \$cached_${_optname}")
+
+       if [ -n "$_cached_opt_val" -a "$_nscd_opt_val" != "$_defoptval" ]; then
+               warn "You should use nscd_${_optname} instead of" \
+                   "cached_${_optname}"
+               setvar "nscd_${_optname}" "$_cached_opt_val"
+       else
+               setvar "nscd_${_optname}" "${_nscd_opt_val:-$_defoptval}"
+       fi
+}
+
+
+load_rc_config $name
+_nscd_set_option "enable" "NO"
+_nscd_set_option "pidfile" "/var/run/nscd.pid"
+_nscd_set_option "flags" ""
+run_rc_command "$1"
diff --git a/etc/rc.d/nsswitch b/etc/rc.d/nsswitch
new file mode 100644 (file)
index 0000000..78e19a1
--- /dev/null
@@ -0,0 +1,165 @@
+#!/bin/sh
+#
+# Copyright (c) 1993 - 2004 The FreeBSD Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD: src/etc/rc.d/nsswitch,v 1.10 2006/05/01 11:02:48 des Exp $
+#
+
+# PROVIDE: nsswitch
+# REQUIRE: root
+# BEFORE:  NETWORK
+
+. /etc/rc.subr
+
+name="nsswitch"
+start_cmd="nsswitch_start"
+stop_cmd=":"
+
+convert_host_conf()
+{
+    host_conf=$1; shift;
+    nsswitch_conf=$1; shift;
+
+    while read line; do
+       line=${line##[  ]}
+       case $line in
+       hosts|local|file)
+               _nsswitch="${_nsswitch}${_nsswitch+ }files"
+               ;;
+       dns|bind)
+               _nsswitch="${_nsswitch}${_nsswitch+ }dns"
+               ;;
+       nis)
+               _nsswitch="${_nsswitch}${_nsswitch+ }nis"
+               ;;
+       '#'*)
+               ;;
+       *)
+               printf "Warning: unrecognized line [%s]", $line > "/dev/stderr"
+               ;;
+
+       esac
+    done < $host_conf
+
+    echo "hosts: $_nsswitch" > $nsswitch_conf
+}
+
+generate_nsswitch_conf()
+{
+    nsswitch_conf=$1; shift;
+
+    cat >$nsswitch_conf <<EOF
+group: compat
+group_compat: nis
+hosts: files dns
+networks: files
+passwd: compat
+passwd_compat: nis
+shells: files
+services: compat
+services_compat: nis
+protocols: files
+rpc: files
+EOF
+}
+
+generate_host_conf()
+{
+    nsswitch_conf=$1; shift;
+    host_conf=$1; shift;
+
+    _cont=0
+    _sources=""
+    while read line; do
+       line=${line##[  ]}
+       case $line in
+       hosts:*)
+               ;;
+       *)
+               if [ $_cont -ne 1 ]; then
+                       continue
+               fi
+               ;;
+       esac
+       if [ "${line%\\}" = "${line}\\" ]; then
+               _cont=1
+       fi
+       line=${line#hosts:}
+       line=${line%\\}
+       line=${line%%#*}
+       _sources="${_sources}${_sources:+ }$line"
+    done < $nsswitch_conf
+
+    echo "# Auto-generated from nsswitch.conf" > $host_conf
+    for _s in ${_sources}; do
+       case $_s in
+       files)
+               echo "hosts" >> $host_conf
+               ;;
+       dns)
+               echo "dns" >> $host_conf
+               ;;
+       nis)
+               echo "nis" >> $host_conf
+               ;;
+       *=*)
+               ;;
+       *)
+               printf "Warning: unrecognized source [%s]", $_s > "/dev/stderr"
+               ;;
+       esac
+    done
+}
+
+nsswitch_start()
+{
+       # Convert host.conf to nsswitch.conf if necessary
+       #
+       if [ -f "/etc/host.conf" -a ! -f "/etc/nsswitch.conf" ]; then
+               echo ''
+               echo 'Warning: /etc/host.conf is no longer used'
+               echo '  /etc/nsswitch.conf will be created for you'
+               convert_host_conf /etc/host.conf /etc/nsswitch.conf
+       fi
+
+       # Generate default nsswitch.conf if none exists
+       #
+       if [ ! -f "/etc/nsswitch.conf" ]; then
+               echo 'Generating nsswitch.conf.'
+               generate_nsswitch_conf /etc/nsswitch.conf
+       fi
+
+       # Generate host.conf for compatibility
+       #
+       if [ ! -f "/etc/host.conf" -o \
+               "/etc/host.conf" -ot "/etc/nsswitch.conf" ]
+       then
+               echo 'Generating host.conf.'
+               generate_host_conf /etc/nsswitch.conf /etc/host.conf
+       fi
+
+}
+
+load_rc_config $name
+run_rc_command "$1"
index 9dd7873..0ad7d7d 100644 (file)
@@ -16,7 +16,7 @@ INCS= a.out.h ar.h assert.h bitstring.h complex.h cpio.h ctype.h db.h \
        iconv.h ieeefp.h ifaddrs.h iso646.h inttypes.h \
        langinfo.h libgen.h limits.h link.h locale.h malloc.h math.h memory.h \
        mpool.h monetary.h ndbm.h netconfig.h \
-       netdb.h nl_types.h nlist.h objformat.h \
+       netdb.h nl_types.h nlist.h nss.h nsswitch.h objformat.h \
        paths.h pthread.h pthread_np.h pwd.h \
        ranlib.h readpassphrase.h regex.h regexp.h resolv.h re_comp.h rmd160.h \
        search.h setjmp.h sgtty.h \
@@ -25,6 +25,10 @@ INCS=        a.out.h ar.h assert.h bitstring.h complex.h cpio.h ctype.h db.h \
        timers.h ttyent.h unistd.h ulimit.h utime.h utmp.h uuid.h vis.h \
        wchar.h wctype.h wordexp.h
 
+.if defined(WANT_HESIOD)
+INCS+= hesiod.h
+.endif
+
 MHDRS= float.h floatingpoint.h varargs.h
 
 # Only for default SHARED=copies case
index e053203..8602960 100644 (file)
@@ -65,6 +65,7 @@
 #define RTLD_DEFAULT   ((void *) -2)   /* Use default search algorithm */
 #define        RTLD_SELF       ((void *) -3)   /* Search the caller itself. */
 
+#if __BSD_VISIBLE
 /*
  * Structure filled in by dladdr().
  */
@@ -93,16 +94,38 @@ typedef struct  dl_serinfo {
         Dl_serpath     dls_serpath[1]; /* there may be more than one */
 } Dl_serinfo;
 
+/*
+ * The actual type declared by this typedef is immaterial, provided that
+ * it is a function pointer.  Its purpose is to provide a return type for
+ * dlfunc() which can be cast to a function pointer type without depending
+ * on behavior undefined by the C standard, which might trigger a compiler
+ * diagnostic.  We intentionally declare a unique type signature to force
+ * a diagnostic should the application not cast the return value of dlfunc()
+ * appropriately.
+ */
+struct __dlfunc_arg {
+       int __dlfunc_dummy;
+};
+
+typedef void (*dlfunc_t)(struct __dlfunc_arg);
+
+#endif /* __BSD_VISIBLE */
+
 __BEGIN_DECLS
-int             dladdr(const void *, Dl_info *);
+/* XSI functions first */
 int             dlclose(void *);
 const char     *dlerror(void);
-int             dlinfo(void *, int, void *);
+void           *dlopen(const char *, int);
+void           *dlsym(void * __restrict, const char * __restrict);
+
+#if __BSD_VISIBLE
+int             dladdr(const void * __restrict, Dl_info * __restrict);
+dlfunc_t        dlfunc(void * __restrict, const char * __restrict);
+int             dlinfo(void * __restrict, int, void * __restrict);
 void            dllockinit(void *, void *(*)(void *), void (*)(void *),
                            void (*)(void *), void (*)(void *),
                            void (*)(void *), void (*)(void *));
-void           *dlopen(const char *, int);
-void           *dlsym(void *, const char *);
+#endif /* __BSD_VISIBLE */
 __END_DECLS
 
 #endif /* !_DLFCN_H_ */
index 62f45ad..e6feac5 100644 (file)
@@ -36,6 +36,7 @@
  * SUCH DAMAGE.
  *
  *     @(#)grp.h       8.2 (Berkeley) 1/21/94
+ * $FreeBSD: src/include/grp.h,v 1.18 2003/04/17 14:15:25 nectar Exp $
  * $DragonFly: src/include/grp.h,v 1.5 2008/04/19 10:08:05 swildner Exp $
  */
 
 
 #include <sys/types.h>
 
-#ifndef _POSIX_SOURCE
 #define        _PATH_GROUP             "/etc/group"
+
+#ifndef _SIZE_T_DECLARED
+typedef __size_t       size_t;
+#define _SIZE_T_DECLARED
 #endif
 
 struct group {
@@ -58,13 +62,26 @@ struct group {
 #include <sys/cdefs.h>
 
 __BEGIN_DECLS
-struct group *getgrgid (gid_t);
-struct group *getgrnam (const char *);
-#ifndef _POSIX_SOURCE
-struct group *getgrent (void);
-int setgrent (void);
-void endgrent (void);
-int setgroupent (int);
+#if __BSD_VISIBLE || __POSIX_VISIBLE >= 200112 || __XSI_VISIBLE
+void            endgrent(void);
+struct group   *getgrent(void);
+#endif
+struct group   *getgrgid(gid_t);
+struct group   *getgrnam(const char *);
+#if __BSD_VISIBLE
+const char     *group_from_gid(gid_t, int);
+#endif
+#if __BSD_VISIBLE || __POSIX_VISIBLE >= 200112 || __XSI_VISIBLE
+/* XXX IEEE Std 1003.1, 2003 specifies `void setgrent(void)' */
+int             setgrent(void);
+int             getgrgid_r(gid_t, struct group *, char *, size_t,
+                   struct group **);
+int             getgrnam_r(const char *, struct group *, char *, size_t,
+                   struct group **);
+#endif
+#if __BSD_VISIBLE
+int             getgrent_r(struct group *, char *, size_t, struct group **);
+int             setgroupent(int);
 #endif
 __END_DECLS
 
diff --git a/include/hesiod.h b/include/hesiod.h
new file mode 100644 (file)
index 0000000..c7dbeee
--- /dev/null
@@ -0,0 +1,98 @@
+/*     $NetBSD: hesiod.h,v 1.3 1999/01/24 23:53:18 lukem Exp $ */
+/*     $FreeBSD: src/include/hesiod.h,v 1.2 2002/03/23 17:24:53 imp Exp $ */
+
+
+/*-
+ * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1996 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#ifndef _HESIOD_H_
+#define _HESIOD_H_
+
+       /* Application-visible indication that we have the new interfaces */
+
+#define HESIOD_INTERFACES
+
+       /* Configuration information. */
+
+#ifndef _PATH_HESIOD_CONF                      /* Configuration file. */
+#define _PATH_HESIOD_CONF      "/etc/hesiod.conf"
+#endif
+
+#define DEF_RHS                ""                      /* Defaults if HESIOD_CONF */
+#define DEF_LHS                ""                      /*    file is not present. */
+
+       /* Error codes (for backwards compatibility) */
+
+#define        HES_ER_UNINIT   -1      /* uninitialized */
+#define        HES_ER_OK       0       /* no error */
+#define        HES_ER_NOTFOUND 1       /* Hesiod name not found by server */
+#define HES_ER_CONFIG  2       /* local problem (no config file?) */
+#define HES_ER_NET     3       /* network problem */
+
+       /* Declaration of routines */
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int    hesiod_init(void **);
+char  **hesiod_resolve(void *, const char *, const char *);
+void   hesiod_free_list(void *, char **);
+char   *hesiod_to_bind(void *, const char *, const char *);
+void   hesiod_end(void *);
+
+                               /* backwards compatibility */
+int    hes_init(void);
+char   *hes_to_bind(const char *, const char *);
+char  **hes_resolve(const char *, const char *);
+int    hes_error(void);
+void   hes_free(char **);
+__END_DECLS
+
+#endif /* ! _HESIOD_H_ */
index 85b2d3f..3d8b1e0 100644 (file)
@@ -265,18 +265,8 @@ void       _setnethtent (int);
 void   _endnethtent (void);
 void   _setnetdnsent (int);
 void   _endnetdnsent (void);
-struct hostent * _gethostbyhtname  (const char *, int);
-struct hostent * _gethostbydnsname (const char *, int);
 struct hostent * _gethostbynisname (const char *, int);
-struct hostent * _gethostbyhtaddr  (const char *, int, int);
-struct hostent * _gethostbydnsaddr (const char *, int, int);
 struct hostent * _gethostbynisaddr (const char *, int, int);
-struct netent *  _getnetbyhtname  (const char *);
-struct netent *  _getnetbydnsname (const char *);
-struct netent *  _getnetbynisname (const char *);
-struct netent *  _getnetbyhtaddr  (unsigned long, int);
-struct netent *  _getnetbydnsaddr (unsigned long, int);
-struct netent *  _getnetbynisaddr (unsigned long, int);
 void _map_v4v6_address (const char *, char *);
 void _map_v4v6_hostent (struct hostent *, char **, int *);
 __END_DECLS
diff --git a/include/nss.h b/include/nss.h
new file mode 100644 (file)
index 0000000..792b16b
--- /dev/null
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 2003 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by
+ * Jacques A. Vidrine, Safeport Network Services, and Network
+ * Associates Laboratories, the Security Research Division of Network
+ * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/include/nss.h,v 1.2 2004/01/09 13:43:49 nectar Exp $
+ *
+ * Compatibility header for the GNU C Library-style nsswitch interface.
+ */
+#ifndef _NSS_H_
+#define _NSS_H_
+
+#include <nsswitch.h>
+
+enum nss_status {
+       NSS_STATUS_TRYAGAIN = -2,
+       NSS_STATUS_UNAVAIL,
+       NSS_STATUS_NOTFOUND,
+       NSS_STATUS_SUCCESS,
+       NSS_STATUS_RETURN
+};
+
+#define __nss_compat_result(rv, err)           \
+((rv == NSS_STATUS_TRYAGAIN && err == ERANGE) ? NS_RETURN : \
+ (rv == NSS_STATUS_TRYAGAIN) ? NS_TRYAGAIN :   \
+ (rv == NSS_STATUS_UNAVAIL)  ? NS_UNAVAIL  :   \
+ (rv == NSS_STATUS_NOTFOUND) ? NS_NOTFOUND :   \
+ (rv == NSS_STATUS_SUCCESS)  ? NS_SUCCESS  :   \
+ (rv == NSS_STATUS_RETURN)   ? NS_RETURN   : 0)
+
+#endif
diff --git a/include/nsswitch.h b/include/nsswitch.h
new file mode 100644 (file)
index 0000000..ff1a897
--- /dev/null
@@ -0,0 +1,250 @@
+/*     $NetBSD: nsswitch.h,v 1.6 1999/01/26 01:04:07 lukem Exp $       */
+/*     $FreeBSD: src/include/nsswitch.h,v 1.5 2007/12/12 10:08:02 bushman Exp $ */
+
+/*-
+ * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _NSSWITCH_H
+#define _NSSWITCH_H    1
+
+#include <sys/types.h>
+#include <stdarg.h>
+
+#define NSS_MODULE_INTERFACE_VERSION 1
+
+#ifndef _PATH_NS_CONF
+#define _PATH_NS_CONF  "/etc/nsswitch.conf"
+#endif
+
+/* NSS source actions */
+#define        NS_ACTION_CONTINUE      0       /* try the next source */
+#define        NS_ACTION_RETURN        1       /* look no further */
+
+#define        NS_SUCCESS      (1<<0)          /* entry was found */
+#define        NS_UNAVAIL      (1<<1)          /* source not responding, or corrupt */
+#define        NS_NOTFOUND     (1<<2)          /* source responded 'no such entry' */
+#define        NS_TRYAGAIN     (1<<3)          /* source busy, may respond to retry */
+#define NS_RETURN      (1<<4)          /* stop search, e.g. for ERANGE */
+#define NS_TERMINATE   (NS_SUCCESS|NS_RETURN) /* flags that end search */
+#define        NS_STATUSMASK   0x000000ff      /* bitmask to get the status flags */
+
+/*
+ * currently implemented sources
+ */
+#define NSSRC_FILES    "files"         /* local files */
+#define        NSSRC_DNS       "dns"           /* DNS; IN for hosts, HS for others */
+#define        NSSRC_NIS       "nis"           /* YP/NIS */
+#define        NSSRC_COMPAT    "compat"        /* passwd,group in YP compat mode */
+#define        NSSRC_CACHE     "cache"         /* nscd daemon */
+#define NSSRC_FALLBACK "__fallback"    /* internal fallback source */
+
+/*
+ * currently implemented databases
+ */
+#define NSDB_HOSTS             "hosts"
+#define NSDB_GROUP             "group"
+#define NSDB_GROUP_COMPAT      "group_compat"
+#define NSDB_NETGROUP          "netgroup"
+#define NSDB_NETWORKS          "networks"
+#define NSDB_PASSWD            "passwd"
+#define NSDB_PASSWD_COMPAT     "passwd_compat"
+#define NSDB_SHELLS            "shells"
+#define NSDB_SERVICES          "services"
+#define NSDB_SERVICES_COMPAT   "services_compat"
+#define NSDB_SSH_HOSTKEYS      "ssh_hostkeys"
+#define NSDB_PROTOCOLS         "protocols"
+#define NSDB_RPC               "rpc"
+
+/*
+ * suggested databases to implement
+ */
+#define NSDB_ALIASES           "aliases"
+#define NSDB_AUTH              "auth"
+#define NSDB_AUTOMOUNT         "automount"
+#define NSDB_BOOTPARAMS                "bootparams"
+#define NSDB_ETHERS            "ethers"
+#define NSDB_EXPORTS           "exports"
+#define NSDB_NETMASKS          "netmasks"
+#define NSDB_PHONES            "phones"
+#define NSDB_PRINTCAP          "printcap"
+#define NSDB_REMOTE            "remote"
+#define NSDB_SENDMAILVARS      "sendmailvars"
+#define NSDB_TERMCAP           "termcap"
+#define NSDB_TTYS              "ttys"
+
+/*
+ * ns_dtab `method' function signature.
+ */
+typedef int (*nss_method)(void *_retval, void *_mdata, va_list _ap);
+
+/*
+ * Macro for generating method prototypes.
+ */
+#define NSS_METHOD_PROTOTYPE(method) \
+       int method(void *, void *, va_list)
+
+/*
+ * ns_dtab - `nsswitch dispatch table'
+ * Contains an entry for each source and the appropriate function to
+ * call.  ns_dtabs are used in the nsdispatch() API in order to allow
+ * the application to override built-in actions.
+ */
+typedef struct _ns_dtab {
+       const char       *src;          /* Source this entry implements */
+       nss_method        method;       /* Method to be called */
+       void             *mdata;        /* Data passed to method */
+} ns_dtab;
+
+/*
+ * macros to help build an ns_dtab[]
+ */
+#define NS_FILES_CB(F,C)       { NSSRC_FILES,  F,      C },
+#define NS_COMPAT_CB(F,C)      { NSSRC_COMPAT, F,      C },
+#define NS_FALLBACK_CB(F)      { NSSRC_FALLBACK, F,    NULL },
+
+#ifdef HESIOD
+#   define NS_DNS_CB(F,C)      { NSSRC_DNS,    F,      C },
+#else
+#   define NS_DNS_CB(F,C)
+#endif
+
+#ifdef YP
+#   define NS_NIS_CB(F,C)      { NSSRC_NIS,    F,      C },
+#else
+#   define NS_NIS_CB(F,C)
+#endif
+
+/*
+ * ns_src - `nsswitch source'
+ * used by the nsparser routines to store a mapping between a source
+ * and its dispatch control flags for a given database.
+ */
+typedef struct _ns_src {
+       const char      *name;
+       u_int32_t        flags;
+} ns_src;
+
+
+/*
+ * default sourcelist (if nsswitch.conf is missing, corrupt,
+ * or the requested database doesn't have an entry.
+ */
+extern const ns_src __nsdefaultsrc[];
+
+/*
+ * ns_mtab - NSS method table
+ * An NSS module provides a mapping from (database name, method name)
+ * tuples to the nss_method and associated data.
+ */
+typedef struct _ns_mtab {
+       const char      *database;
+       const char      *name;
+       nss_method       method;
+       void            *mdata;
+} ns_mtab;
+
+/*
+ * NSS module de-registration, called at module unload.
+ */
+typedef void    (*nss_module_unregister_fn)(ns_mtab *, unsigned int);
+
+/*
+ * NSS module registration, called at module load.
+ */
+typedef ns_mtab *(*nss_module_register_fn)(const char *, unsigned int *,
+                      nss_module_unregister_fn *);
+
+/*
+ * Many NSS interfaces follow the getXXnam, getXXid, getXXent pattern.
+ * Developers are encouraged to use nss_lookup_type where approriate.
+ */
+enum nss_lookup_type {
+       nss_lt_name = 1,
+       nss_lt_id   = 2,
+       nss_lt_all  = 3
+};
+
+#ifdef _NS_PRIVATE
+/*
+ * private data structures for back-end nsswitch implementation
+ */
+
+/*
+ * ns_dbt - `nsswitch database thang'
+ * for each database in /etc/nsswitch.conf there is a ns_dbt, with its
+ * name and a list of ns_src's containing the source information.
+ */
+typedef struct _ns_dbt {
+       const char      *name;          /* name of database */
+       ns_src          *srclist;       /* list of sources */
+       int              srclistsize;   /* size of srclist */
+} ns_dbt;
+
+/*
+ * ns_mod - NSS module
+ */
+typedef struct _ns_mod {
+       char            *name;          /* module name */
+       void            *handle;        /* handle from dlopen */
+       ns_mtab         *mtab;          /* method table */
+       unsigned int     mtabsize;      /* count of entries in method table */
+       nss_module_unregister_fn unregister; /* called to unload module */
+} ns_mod;
+
+#endif /* _NS_PRIVATE */
+
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+extern int     nsdispatch(void *, const ns_dtab [], const char *,
+                          const char *, const ns_src [], ...);
+
+#ifdef _NS_PRIVATE
+extern void             _nsdbtaddsrc(ns_dbt *, const ns_src *);
+extern void             _nsdbtput(const ns_dbt *);
+extern void             _nsyyerror(const char *);
+extern int              _nsyylex(void);
+extern int              _nsyyparse(void);
+extern int              _nsyylineno;
+#ifdef _NSS_DEBUG
+extern void             _nsdbtdump(const ns_dbt *);
+#endif
+#endif /* _NS_PRIVATE */
+
+__END_DECLS
+
+#endif /* !_NSSWITCH_H */
index ccb5ed2..148b8b8 100644 (file)
@@ -36,6 +36,7 @@
  * SUCH DAMAGE.
  *
  *     @(#)pwd.h       8.2 (Berkeley) 1/21/94
+ * $FreeBSD: src/include/pwd.h,v 1.16 2005/01/26 17:26:54 nectar Exp $
  * $DragonFly: src/include/pwd.h,v 1.2 2003/11/14 01:01:43 dillon Exp $
  */
 
 
 #include <sys/types.h>
 
-#ifndef _POSIX_SOURCE
+#ifndef _TIME_T_DECLARED
+typedef        __time_t        time_t;
+#define        _TIME_T_DECLARED
+#endif
+
+#ifndef _SIZE_T_DECLARED
+typedef __size_t       size_t;
+#define _SIZE_T_DECLARED
+#endif
+
 #define _PATH_PWD              "/etc"
 #define        _PATH_PASSWD            "/etc/passwd"
 #define        _PASSWD                 "passwd"
 
 #define        _PATH_PWD_MKDB          "/usr/sbin/pwd_mkdb"
 
-#define        _PW_KEYBYNAME           '1'     /* stored by name */
-#define        _PW_KEYBYNUM            '2'     /* stored by entry in the "file" */
-#define        _PW_KEYBYUID            '3'     /* stored by uid */
-#define _PW_KEYYPENABLED       '4'     /* YP is enabled */
-#define        _PW_KEYYPBYNUM          '5'     /* special +@netgroup entries */
+/* Historically, the keys in _PATH_MP_DB/_PATH_SMP_DB had the format
+ * `1 octet tag | key', where the tag is one of the _PW_KEY* values
+ * listed below.  These values happen to be ASCII digits.
+ * The tag is now still a single octet, but the
+ * upper 4 bits are interpreted as a version.  Previous format
+ * entries are version `3' -- this conveniently results in the same
+ * key values as before.  The new, architecture-independent entries
+ * are version `4'.
+ * As it happens, some applications read the database directly.
+ * (Bad app, no cookie!)  Thus, we leave the _PW_KEY* symbols at their
+ * old values so these apps still work.  Consequently
+ * we have to muck around a bit more to get the correct, versioned
+ * tag, and that is what the _PW_VERSIONED macro is about.
+ */
+
+#define _PW_VERSION_MASK       '\xF0'
+#define _PW_VERSIONED(x, v)    ((unsigned char)(((x) & 0xCF) | ((v)<<4)))
+
+#define        _PW_KEYBYNAME           '\x31'  /* stored by name */
+#define        _PW_KEYBYNUM            '\x32'  /* stored by entry in the "file" */
+#define        _PW_KEYBYUID            '\x33'  /* stored by uid */
+#define _PW_KEYYPENABLED       '\x34'  /* YP is enabled */
+#define        _PW_KEYYPBYNUM          '\x35'  /* special +@netgroup entries */
+
+/* The database also contains a key to indicate the format version of
+ * the entries therein.  There may be other, older versioned entries
+ * as well.
+ */
+#define _PWD_VERSION_KEY       "\xFF" "VERSION"
+#define _PWD_CURRENT_VERSION   '\x04'
 
 #define        _PASSWORD_EFMT1         '_'     /* extended encryption format */
 
 #define        _PASSWORD_LEN           128     /* max length, not counting NULL */
-#endif
 
 struct passwd {
        char    *pw_name;               /* user name */
@@ -96,16 +130,34 @@ struct passwd {
 #define _PWF_SHELL     _PWF(8)
 #define _PWF_EXPIRE    _PWF(9)
 
+/* XXX These flags are bogus.  With nsswitch, there are many
+ * possible sources and they cannot be represented in a small integer.
+ */
+#define _PWF_SOURCE    0x3000
+#define _PWF_FILES     0x1000
+#define _PWF_NIS       0x2000
+#define _PWF_HESIOD    0x3000
+
 #include <sys/cdefs.h>
 
 __BEGIN_DECLS
-struct passwd  *getpwuid (uid_t);
-struct passwd  *getpwnam (const char *);
-#ifndef _POSIX_SOURCE
-struct passwd  *getpwent (void);
-int             setpassent (int);
-void            setpwent (void);
-void            endpwent (void);
+struct passwd  *getpwnam(const char *);
+struct passwd  *getpwuid(uid_t);
+
+#if __POSIX_VISIBLE >= 200112 || __XSI_VISIBLE >= 500
+void            endpwent(void);
+struct passwd  *getpwent(void);
+void            setpwent(void);
+int             getpwnam_r(const char *, struct passwd *, char *, size_t,
+                   struct passwd **);
+int             getpwuid_r(uid_t, struct passwd *, char *, size_t,
+                   struct passwd **);
+#endif
+
+#if __BSD_VISIBLE
+int             getpwent_r(struct passwd *, char *, size_t, struct passwd **);
+int             setpassent(int);
+const char     *user_from_uid(uid_t, int);
 #endif
 __END_DECLS
 
index 41e34e4..428d1fa 100644 (file)
@@ -29,7 +29,7 @@
  *     from: @(#)rpcent.h   1.13    94/04/25 SMI
  *     from: @(#)rpcent.h 1.1 88/12/06 SMI
  * $NetBSD: rpcent.h,v 1.1 2000/06/02 22:57:56 fvdl Exp $
- * $FreeBSD: src/include/rpc/rpcent.h,v 1.2 2002/03/23 17:24:55 imp Exp $
+ * $FreeBSD: src/include/rpc/rpcent.h,v 1.4 2006/04/29 04:26:16 ume Exp $
  * $DragonFly$
  */
 /*
@@ -53,12 +53,10 @@ struct rpcent {
 };
 
 __BEGIN_DECLS
-extern struct rpcent *getrpcbyname_r(const char *, struct rpcent *,
-                                    char *, int);
-extern struct rpcent *getrpcbynumber_r(int, struct rpcent *, char *, int);
-extern struct rpcent *getrpcent_r(struct rpcent *, char *, int);
-
-/* Old interfaces that return a pointer to a static area;  MT-unsafe */
+/*
+ * These interfaces are currently implemented through nsswitch and are
+ * MT-safe.
+ */
 extern struct rpcent *getrpcbyname(char *);
 extern struct rpcent *getrpcbynumber(int);
 extern struct rpcent *getrpcent(void);
index 12a22fc..6f54500 100644 (file)
@@ -166,7 +166,6 @@ char        *devname_r(dev_t, mode_t, char *, size_t);
 int     getloadavg(double [], int);
 const char *getprogname(void);
 
-char   *group_from_gid(gid_t, int);
 int     heapsort(void *, size_t, size_t, int (*)(const void *, const void *));
 char   *initstate(unsigned long, char *, long);
 int     mergesort(void *, size_t, size_t, int (*)(const void *, const void *));
@@ -182,7 +181,6 @@ void         setprogname(const char *);
 char   *setstate(char *);
 void    srandom(unsigned long);
 void    srandomdev(void);
-char   *user_from_uid(uid_t, int);
 
 #if !defined(__STRICT_ANSI__) && !defined(_KERNEL_VIRTUAL)
 __int64_t      strtoq(const char *, char **, int);
index f96f150..1e6f019 100644 (file)
@@ -45,6 +45,13 @@ NOASM=
 CFLAGS+= -DYP
 .include "${.CURDIR}/../libc/yp/Makefile.inc"
 .endif
+.if defined(WANT_HESIOD)
+CFLAGS+= -DHESIOD
+.endif
+# Caching isn't ready yet
+#.if !defined(NO_NS_CACHING)
+#CFLAGS+= -DNS_CACHING
+#.endif
 
 # If there are no machine dependent sources, append all the
 # machine-independent sources:
index d022eb5..b410bc1 100644 (file)
@@ -9,7 +9,7 @@ SRCS+=  _pthread_stubs.c _rand48.c _spinlock_stub.c _thread_init.c \
        alarm.c arc4random.c assert.c basename.c \
        clock.c closedir.c confstr.c \
        ctermid.c ctype.c daemon.c devname.c dirname.c disklabel.c disktab.c \
-       dlfcn.c drand48.c erand48.c err.c errlst.c \
+       dlfcn.c dlfunc.c drand48.c erand48.c err.c errlst.c \
        exec.c fmtcheck.c fmtmsg.c fnmatch.c fpclassifyd.c fpclassifyf.c \
        fstab.c ftok.c fts.c ftw.c getbootfile.c getbsize.c \
        getcap.c getcwd.c getdomainname.c getgrent.c getgrouplist.c \
@@ -24,7 +24,7 @@ SRCS+=  _pthread_stubs.c _rand48.c _spinlock_stub.c _thread_init.c \
        msgget.c msgrcv.c msgsnd.c nftw.c nice.c \
        nlist.c nrand48.c ntp_gettime.c opendir.c \
        pause.c pmadvise.c popen.c posixshm.c \
-       psignal.c pthread_fake.c pwcache.c \
+       psignal.c pthread_fake.c pw_scan.c pwcache.c \
        raise.c readdir.c readpassphrase.c rewinddir.c \
        scandir.c seed48.c seekdir.c semconfig.c semctl.c semget.c semop.c \
        setdomainname.c sethostname.c setjmperr.c setmode.c setprogname.c \
@@ -103,7 +103,9 @@ MLINKS+=getnetgrent.3 endnetgrent.3 getnetgrent.3 innetgr.3 \
        getnetgrent.3 setnetgrent.3
 MLINKS+=getprogname.3 setprogname.3
 MLINKS+=getpwent.3 endpwent.3 getpwent.3 getpwnam.3 getpwent.3 getpwuid.3 \
-       getpwent.3 setpassent.3 getpwent.3 setpwent.3 getpwent.3 setpwfile.3
+       getpwent.3 setpassent.3 getpwent.3 setpwent.3 getpwent.3 setpwfile.3 \
+       getpwent.3 getpwent_r.3 getpwent.3 getpwnam_r.3 \
+       getpwent.3 getpwuid_r.3
 MLINKS+=getttyent.3 endttyent.3 getttyent.3 getttynam.3 \
        getttyent.3 isdialuptty.3 getttyent.3 isnettty.3 \
        getttyent.3 setttyent.3
diff --git a/lib/libc/gen/dlfunc.c b/lib/libc/gen/dlfunc.c
new file mode 100644 (file)
index 0000000..b628de6
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * This source file is in the public domain.
+ * Garrett A. Wollman, 2002-05-28.
+ *
+ * $FreeBSD: src/lib/libc/gen/dlfunc.c,v 1.3 2002/09/11 05:05:48 mike Exp $
+ */
+
+#include <dlfcn.h>
+
+/*
+ * Implement the dlfunc() interface, which behaves exactly the same as
+ * dlsym() except that it returns a function pointer instead of a data
+ * pointer.  This can be used by applications to avoid compiler warnings
+ * about undefined behavior, and is intended as prior art for future
+ * POSIX standardization.  This function requires that all pointer types
+ * have the same representation, which is true on all platforms FreeBSD
+ * runs on, but is not guaranteed by the C standard.
+ */
+dlfunc_t
+dlfunc(void * __restrict handle, const char * __restrict symbol)
+{
+       union {
+               void *d;
+               dlfunc_t f;
+       } rv;
+
+       rv.d = dlsym(handle, symbol);
+       return (rv.f);
+}
index da81c6f..e0686ee 100644 (file)
@@ -50,6 +50,8 @@
 .Fn dlopen "const char *path" "int mode"
 .Ft void *
 .Fn dlsym "void *handle" "const char *symbol"
+.Ft __dlfunc_t
+.Fn dlfunc "void *handle" "const char *symbol"
 .Ft const char *
 .Fn dlerror "void"
 .Ft int
index 1ecc1cb..2133c25 100644 (file)
@@ -9,10 +9,6 @@
 .\" 2. Redistributions in binary form must reproduce the above copyright
 .\"    notice, this list of conditions and the following disclaimer in the
 .\"    documentation and/or other materials provided with the distribution.
-.\" 3. All advertising materials mentioning features or use of this software
-.\"    must display the following acknowledgement:
-.\"    This product includes software developed by the University of
-.\"    California, Berkeley and its contributors.
 .\" 4. Neither the name of the University nor the names of its contributors
 .\"    may be used to endorse or promote products derived from this software
 .\"    without specific prior written permission.
@@ -30,7 +26,7 @@
 .\" SUCH DAMAGE.
 .\"
 .\"     From: @(#)getgrent.3   8.2 (Berkeley) 4/19/94
-.\" $FreeBSD: src/lib/libc/gen/getgrent.3,v 1.12.2.4 2003/03/15 15:11:05 trhodes Exp $
+.\" $FreeBSD: src/lib/libc/gen/getgrent.3,v 1.28 2007/01/09 00:27:53 imp Exp $
 .\" $DragonFly: src/lib/libc/gen/getgrent.3,v 1.5 2008/04/19 10:08:05 swildner Exp $
 .\"
 .Dd April 19, 2008
 .Os
 .Sh NAME
 .Nm getgrent ,
+.Nm getgrent_r ,
 .Nm getgrnam ,
+.Nm getgrnam_r ,
 .Nm getgrgid ,
+.Nm getgrgid_r ,
 .Nm setgroupent ,
 .Nm setgrent ,
 .Nm endgrent
 .Sh LIBRARY
 .Lb libc
 .Sh SYNOPSIS
-.In sys/types.h
 .In grp.h
 .Ft struct group *
 .Fn getgrent void
+.Ft int
+.Fn getgrent_r "struct group *grp" "char *buffer" "size_t bufsize" "struct group **result"
 .Ft struct group *
 .Fn getgrnam "const char *name"
+.Ft int
+.Fn getgrnam_r "const char *name" "struct group *grp" "char *buffer" "size_t bufsize" "struct group **result"
 .Ft struct group *
 .Fn getgrgid "gid_t gid"
 .Ft int
+.Fn getgrgid_r "gid_t gid" "struct group *grp" "char *buffer" "size_t bufsize" "struct group **result"
+.Ft int
 .Fn setgroupent "int stayopen"
 .Ft int
 .Fn setgrent void
@@ -76,7 +80,7 @@ file
 struct group {
        char    *gr_name;       /* group name */
        char    *gr_passwd;     /* group password */
-       int     gr_gid;         /* group id */
+       gid_t   gr_gid;         /* group id */
        char    **gr_mem;       /* group members */
 };
 .Ed
@@ -89,7 +93,8 @@ search the group database for the given group name pointed to by
 .Fa name
 or the group id pointed to by
 .Fa gid ,
-respectively, returning the first one encountered.  Identical group
+respectively, returning the first one encountered.
+Identical group
 names or group gids may result in undefined behavior.
 .Pp
 The
@@ -98,17 +103,49 @@ function
 sequentially reads the group database and is intended for programs
 that wish to step through the complete list of groups.
 .Pp
-All three routines will open the group file for reading, if necessary.
+The functions
+.Fn getgrent_r ,
+.Fn getgrnam_r ,
+and
+.Fn getgrgid_r
+are thread-safe versions of
+.Fn getgrent ,
+.Fn getgrnam ,
+and
+.Fn getgrgid ,
+respectively.
+The caller must provide storage for the results of the search in
+the
+.Fa grp ,
+.Fa buffer ,
+.Fa bufsize ,
+and
+.Fa result
+arguments.
+When these functions are successful, the
+.Fa grp
+argument will be filled-in, and a pointer to that argument will be
+stored in
+.Fa result .
+If an entry is not found or an error occurs,
+.Fa result
+will be set to
+.Dv NULL .
+.Pp
+These functions will open the group file for reading, if necessary.
 .Pp
 The
 .Fn setgroupent
 function
-opens the file, or rewinds it if it is already open.  If
+opens the file, or rewinds it if it is already open.
+If
 .Fa stayopen
 is non-zero, file descriptors are left open, significantly speeding
-functions subsequent calls.  This functionality is unnecessary for
+functions subsequent calls.
+This functionality is unnecessary for
 .Fn getgrent
-as it doesn't close its file descriptors by default.  It should also
+as it does not close its file descriptors by default.
+It should also
 be noted that it is dangerous for long-running programs to use this
 functionality as the group file may be updated.
 .Pp
@@ -123,33 +160,35 @@ The
 .Fn endgrent
 function
 closes any open files.
-.Sh YP/NIS INTERACTION
-When the
-.Xr yp 8
-group database is enabled, the
-.Fn getgrnam
-and
-.Fn getgrgid
-functions use the YP maps
-.Dq Li group.byname
-and
-.Dq Li group.bygid ,
-respectively, if the requested group is not found in the local
-.Pa /etc/group
-file.  The
-.Fn getgrent
-function will step through the YP map
-.Dq Li group.byname
-if the entire map is enabled as described in
-.Xr group 5 .
 .Sh RETURN VALUES
 The functions
 .Fn getgrent ,
 .Fn getgrnam ,
 and
 .Fn getgrgid ,
-return a pointer to the group entry if successful; if end-of-file
-is reached or an error occurs a null pointer is returned.
+return a pointer to a group structure on success or
+.Dv NULL
+if the entry is not found or if an error occurs.
+If an error does occur,
+.Va errno
+will be set.
+Note that programs must explicitly set
+.Va errno
+to zero before calling any of these functions if they need to
+distinguish between a non-existent entry and an error.
+The functions
+.Fn getgrent_r ,
+.Fn getgrnam_r ,
+and
+.Fn getgrgid_r
+return 0 if no error occurred, or an error number to indicate failure.
+It is not an error if a matching entry is not found.
+(Thus, if
+.Fa result
+is set to
+.Dv NULL
+and the return value is 0, no matching entry exists.)
+.Pp
 The functions
 .Fn setgroupent
 and
@@ -172,7 +211,25 @@ been deprecated and is no longer available.
 .Sh SEE ALSO
 .Xr getpwent 3 ,
 .Xr group 5 ,
+.Xr nsswitch.conf 5 ,
 .Xr yp 8
+.Sh STANDARDS
+The
+.Fn getgrent ,
+.Fn getgrnam ,
+.Fn getgrnam_r ,
+.Fn getgrgid ,
+.Fn getgrgid_r
+and
+.Fn endgrent
+functions conform to
+.St -p1003.1-96 .
+The
+.Fn setgrent
+function differs from that standard in that its return type is
+.Vt int
+rather than
+.Vt void .
 .Sh HISTORY
 The functions
 .Fn endgrent ,
@@ -189,6 +246,15 @@ and
 .Fn setgroupent
 appeared in
 .Bx 4.3 Reno .
+The functions
+.Fn getgrent_r ,
+.Fn getgrnam_r ,
+and
+.Fn getgrgid_r
+appeared in
+.Fx 5.1
+and
+.Dx 2.1 .
 .Sh BUGS
 The functions
 .Fn getgrent ,
@@ -202,3 +268,21 @@ a pointer to that object.
 Subsequent calls to
 the same function
 will modify the same object.
+.Pp
+The functions
+.Fn getgrent ,
+.Fn getgrent_r ,
+.Fn endgrent ,
+.Fn setgroupent ,
+and
+.Fn setgrent
+are fairly useless in a networked environment and should be
+avoided, if possible.
+The
+.Fn getgrent
+and
+.Fn getgrent_r
+functions
+make no attempt to suppress duplicate information if multiple
+sources are specified in
+.Xr nsswitch.conf 5 .
index 2197f9b..9a32c38 100644 (file)
@@ -1,8 +1,12 @@
-/*     $NetBSD: getgrent.c,v 1.34.2.1 1999/04/27 14:10:58 perry Exp $  */
-/*
- * Copyright (c) 1989, 1993
- *     The Regents of the University of California.  All rights reserved.
- * Portions Copyright (c) 1994, Jason Downs. All Rights Reserved.
+/*-
+ * Copyright (c) 2003 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by
+ * Jacques A. Vidrine, Safeport Network Services, and Network
+ * Associates Laboratories, the Security Research Division of Network
+ * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
  *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * @(#)getgrent.c      8.2 (Berkeley) 3/21/94
- * $FreeBSD: src/lib/libc/gen/getgrent.c,v 1.17.6.1 2001/03/05 08:56:02 obrien Exp $
+ * $FreeBSD: src/lib/libc/gen/getgrent.c,v 1.37 2007/12/12 10:08:02 bushman Exp $
  * $DragonFly: src/lib/libc/gen/getgrent.c,v 1.5 2005/11/19 22:32:53 swildner Exp $
  */
 
+#include "namespace.h"
+#include <sys/param.h>
+#ifdef YP
+#include <rpc/rpc.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+#endif
+#include <assert.h>
+#include <ctype.h>
 #include <errno.h>
-#include <limits.h>
-#include <sys/types.h>
+#ifdef HESIOD
+#include <hesiod.h>
+#endif
+#include <grp.h>
+#include <nsswitch.h>
+#include <pthread.h>
+#include <pthread_np.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <grp.h>
 #include <syslog.h>
+#include <unistd.h>
+#include "un-namespace.h"
+#include "libc_private.h"
+#include "nss_tls.h"
+#ifdef NS_CACHING
+#include "nscache.h"
+#endif
+
+enum constants {
+       GRP_STORAGE_INITIAL     = 1 << 10, /* 1 KByte */
+       GRP_STORAGE_MAX         = 1 << 20, /* 1 MByte */
+       SETGRENT                = 1,
+       ENDGRENT                = 2,
+       HESIOD_NAME_MAX         = 256,
+};
+
+static const ns_src defaultsrc[] = {
+       { NSSRC_COMPAT, NS_SUCCESS },
+       { NULL, 0 }
+};
 
-static FILE *_gr_fp;
-static struct group _gr_group;
-static int _gr_stayopen;
+int     __gr_match_entry(const char *, size_t, enum nss_lookup_type,
+           const char *, gid_t);
+int     __gr_parse_entry(char *, size_t, struct group *, char *, size_t,
+           int *);
+
+static int      is_comment_line(const char *, size_t);
+
+union key {
+       const char      *name;
+       gid_t            gid;
+};
+static struct group *getgr(int (*)(union key, struct group *, char *, size_t,
+                   struct group **), union key);
+static int      wrap_getgrnam_r(union key, struct group *, char *, size_t,
+                   struct group **);
+static int      wrap_getgrgid_r(union key, struct group *, char *, size_t,
+                   struct group **);
+static int      wrap_getgrent_r(union key, struct group *, char *, size_t,
+                   struct group **);
+
+struct files_state {
+       FILE    *fp;
+       int      stayopen;
+};
+static void     files_endstate(void *);
+NSS_TLS_HANDLING(files);
+static int      files_setgrent(void *, void *, va_list);
+static int      files_group(void *, void *, va_list);
+
+
+#ifdef HESIOD
+struct dns_state {
+       long    counter;
+};
+static void     dns_endstate(void *);
+NSS_TLS_HANDLING(dns);
+static int      dns_setgrent(void *, void *, va_list);
+static int      dns_group(void *, void *, va_list);
+#endif
 
-static int     grscan(int, gid_t, const char *);
-static int     start_gr(void);
 
 #ifdef YP
-#include <rpc/rpc.h>
-#include <rpcsvc/yp_prot.h>
-#include <rpcsvc/ypclnt.h>
-static int _gr_stepping_yp;
-static int _gr_yp_enabled;
-static int _getypgroup(struct group *, const char *, const char *);
-static int _nextypgroup(struct group *);
+struct nis_state {
+       char     domain[MAXHOSTNAMELEN];
+       int      done;
+       char    *key;
+       int      keylen;
+};
+static void     nis_endstate(void *);
+NSS_TLS_HANDLING(nis);
+static int      nis_setgrent(void *, void *, va_list);
+static int      nis_group(void *, void *, va_list);
 #endif
 
-/* initial size for malloc and increase steps for realloc */
-#define        MAXGRP          64
-#define        MAXLINELENGTH   256
+struct compat_state {
+       FILE    *fp;
+       int      stayopen;
+       char    *name;
+       enum _compat {
+               COMPAT_MODE_OFF = 0,
+               COMPAT_MODE_ALL,
+               COMPAT_MODE_NAME
+       }        compat;
+};
+static void     compat_endstate(void *);
+NSS_TLS_HANDLING(compat);
+static int      compat_setgrent(void *, void *, va_list);
+static int      compat_group(void *, void *, va_list);
 
-static char **members;                 /* list of group members */
-static int maxgrp;              /* current length of **mebers */
-static char *line;             /* temp buffer for group line */
-static int maxlinelength;       /* current length of *line */
+static int     gr_addgid(gid_t, gid_t *, int, int *);
+static int     getgroupmembership_fallback(void *, void *, va_list);
 
-/* 
- * Lines longer than MAXLINELENGTHLIMIT will be counted as an error.
- * <= 0 disable check for maximum line length
- * 256K is enough for 64,000 uids
- */
-#define MAXLINELENGTHLIMIT     (256 * 1024)
-#define GROUP_IGNORE_COMMENTS  1       /* allow comments in /etc/group */
+#ifdef NS_CACHING
+static int      grp_id_func(char *, size_t *, va_list, void *);
+static int      grp_marshal_func(char *, size_t *, void *, va_list, void *);
+static int      grp_unmarshal_func(char *, size_t, void *, va_list, void *);
 
-struct group *
-getgrent(void)
+static int
+grp_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata)
 {
-       if (!_gr_fp && !start_gr()) {
-               return NULL;
+       char    *name;
+       gid_t   gid;
+
+       size_t  desired_size, size;
+       int     res = NS_UNAVAIL;
+       enum nss_lookup_type lookup_type;
+
+
+       lookup_type = (enum nss_lookup_type)cache_mdata;
+       switch (lookup_type) {
+       case nss_lt_name:
+               name = va_arg(ap, char *);
+               size = strlen(name);
+               desired_size = sizeof(enum nss_lookup_type) + size + 1;
+               if (desired_size > *buffer_size) {
+                       res = NS_RETURN;
+                       goto fin;
+               }
+
+               memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
+               memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1);
+
+               res = NS_SUCCESS;
+               break;
+       case nss_lt_id:
+               gid = va_arg(ap, gid_t);
+               desired_size = sizeof(enum nss_lookup_type) + sizeof(gid_t);
+               if (desired_size > *buffer_size) {
+                       res = NS_RETURN;
+                       goto fin;
+               }
+
+               memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
+               memcpy(buffer + sizeof(enum nss_lookup_type), &gid,
+                   sizeof(gid_t));
+
+               res = NS_SUCCESS;
+               break;
+       default:
+               /* should be unreachable */
+               return (NS_UNAVAIL);
        }
 
-#ifdef YP
-       if (_gr_stepping_yp) {
-               if (_nextypgroup(&_gr_group))
-                       return(&_gr_group);
+fin:
+       *buffer_size = desired_size;
+       return (res);
+}
+
+static int
+grp_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap,
+    void *cache_mdata)
+{
+       char *name;
+       gid_t gid;
+       struct group *grp;
+       char *orig_buf;
+       size_t orig_buf_size;
+
+       struct group new_grp;
+       size_t desired_size, size, mem_size;
+       char *p, **mem;
+
+       switch ((enum nss_lookup_type)cache_mdata) {
+       case nss_lt_name:
+               name = va_arg(ap, char *);
+               break;
+       case nss_lt_id:
+               gid = va_arg(ap, gid_t);
+               break;
+       case nss_lt_all:
+               break;
+       default:
+               /* should be unreachable */
+               return (NS_UNAVAIL);
        }
-tryagain:
-#endif
 
-       if (!grscan(0, 0, NULL))
-               return(NULL);
-#ifdef YP
-       if(_gr_group.gr_name[0] == '+' && _gr_group.gr_name[1]) {
-               _getypgroup(&_gr_group, &_gr_group.gr_name[1],
-                           "group.byname");
-       } else if(_gr_group.gr_name[0] == '+') {
-               if (!_nextypgroup(&_gr_group))
-                       goto tryagain;
-               else
-                       return(&_gr_group);
+       grp = va_arg(ap, struct group *);
+       orig_buf = va_arg(ap, char *);
+       orig_buf_size = va_arg(ap, size_t);
+
+       desired_size = _ALIGNBYTES + sizeof(struct group) + sizeof(char *);
+
+       if (grp->gr_name != NULL)
+               desired_size += strlen(grp->gr_name) + 1;
+       if (grp->gr_passwd != NULL)
+               desired_size += strlen(grp->gr_passwd) + 1;
+
+       if (grp->gr_mem != NULL) {
+               mem_size = 0;
+               for (mem = grp->gr_mem; *mem; ++mem) {
+                       desired_size += strlen(*mem) + 1;
+                       ++mem_size;
+               }
+
+               desired_size += _ALIGNBYTES + (mem_size + 1) * sizeof(char *);
        }
-#endif
-       return(&_gr_group);
+
+       if (desired_size > *buffer_size) {
+               /* this assignment is here for future use */
+               *buffer_size = desired_size;
+               return (NS_RETURN);
+       }
+
+       memcpy(&new_grp, grp, sizeof(struct group));
+       memset(buffer, 0, desired_size);
+
+       *buffer_size = desired_size;
+       p = buffer + sizeof(struct group) + sizeof(char *);
+       memcpy(buffer + sizeof(struct group), &p, sizeof(char *));
+       p = (char *)_ALIGN(p);
+
+       if (new_grp.gr_name != NULL) {
+               size = strlen(new_grp.gr_name);
+               memcpy(p, new_grp.gr_name, size);
+               new_grp.gr_name = p;
+               p += size + 1;
+       }
+
+       if (new_grp.gr_passwd != NULL) {
+               size = strlen(new_grp.gr_passwd);
+               memcpy(p, new_grp.gr_passwd, size);
+               new_grp.gr_passwd = p;
+               p += size + 1;
+       }
+
+       if (new_grp.gr_mem != NULL) {
+               p = (char *)_ALIGN(p);
+               memcpy(p, new_grp.gr_mem, sizeof(char *) * mem_size);
+               new_grp.gr_mem = (char **)p;
+               p += sizeof(char *) * (mem_size + 1);
+
+               for (mem = new_grp.gr_mem; *mem; ++mem) {
+                       size = strlen(*mem);
+                       memcpy(p, *mem, size);
+                       *mem = p;
+                       p += size + 1;
+               }
+       }
+
+       memcpy(buffer, &new_grp, sizeof(struct group));
+       return (NS_SUCCESS);
 }
 
-struct group *
-getgrnam(const char *name)
+static int
+grp_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
+    void *cache_mdata)
 {
-       int rval;
+       char *name;
+       gid_t gid;
+       struct group *grp;
+       char *orig_buf;
+       size_t orig_buf_size;
+       int *ret_errno;
 
-       if (!start_gr())
-               return(NULL);
-#ifdef YP
-       tryagain:
+       char *p;
+       char **mem;
+
+       switch ((enum nss_lookup_type)cache_mdata) {
+       case nss_lt_name:
+               name = va_arg(ap, char *);
+               break;
+       case nss_lt_id:
+               gid = va_arg(ap, gid_t);
+               break;
+       case nss_lt_all:
+               break;
+       default:
+               /* should be unreachable */
+               return (NS_UNAVAIL);
+       }
+
+       grp = va_arg(ap, struct group *);
+       orig_buf = va_arg(ap, char *);
+       orig_buf_size = va_arg(ap, size_t);
+       ret_errno = va_arg(ap, int *);
+
+       if (orig_buf_size <
+           buffer_size - sizeof(struct group) - sizeof(char *)) {
+               *ret_errno = ERANGE;
+               return (NS_RETURN);
+       }
+
+       memcpy(grp, buffer, sizeof(struct group));
+       memcpy(&p, buffer + sizeof(struct group), sizeof(char *));
+
+       orig_buf = (char *)_ALIGN(orig_buf);
+       memcpy(orig_buf, buffer + sizeof(struct group) + sizeof(char *) +
+           _ALIGN(p) - (size_t)p,
+           buffer_size - sizeof(struct group) - sizeof(char *) -
+           _ALIGN(p) + (size_t)p);
+       p = (char *)_ALIGN(p);
+
+       NS_APPLY_OFFSET(grp->gr_name, orig_buf, p, char *);
+       NS_APPLY_OFFSET(grp->gr_passwd, orig_buf, p, char *);
+       if (grp->gr_mem != NULL) {
+               NS_APPLY_OFFSET(grp->gr_mem, orig_buf, p, char **);
+
+               for (mem = grp->gr_mem; *mem; ++mem)
+                       NS_APPLY_OFFSET(*mem, orig_buf, p, char *);
+       }
+
+       if (retval != NULL)
+               *((struct group **)retval) = grp;
+
+       return (NS_SUCCESS);
+}
+
+NSS_MP_CACHE_HANDLING(group);
+#endif /* NS_CACHING */
+
+#ifdef NS_CACHING
+static const nss_cache_info setgrent_cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+       group, (void *)nss_lt_all,
+       NULL, NULL);
+#endif
+
+static const ns_dtab setgrent_dtab[] = {
+       { NSSRC_FILES, files_setgrent, (void *)SETGRENT },
+#ifdef HESIOD
+       { NSSRC_DNS, dns_setgrent, (void *)SETGRENT },
 #endif
-       rval = grscan(1, 0, name);
 #ifdef YP
-       if(rval == -1 && (_gr_yp_enabled < 0 || (_gr_yp_enabled &&
-                                       _gr_group.gr_name[0] == '+'))) {
-               if (!(rval = _getypgroup(&_gr_group, name, "group.byname")))
-                       goto tryagain;
-       }
+       { NSSRC_NIS, nis_setgrent, (void *)SETGRENT },
 #endif
-       if (!_gr_stayopen)
-               endgrent();
-       return (rval) ? &_gr_group : NULL;
-}
+       { NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT },
+#ifdef NS_CACHING
+       NS_CACHE_CB(&setgrent_cache_info)
+#endif
+       { NULL, NULL, NULL }
+};
 
-struct group *
-getgrgid(gid_t gid)
-{
-       int rval;
+#ifdef NS_CACHING
+static const nss_cache_info endgrent_cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+       group, (void *)nss_lt_all,
+       NULL, NULL);
+#endif
 
-       if (!start_gr())
-               return(NULL);
+static const ns_dtab endgrent_dtab[] = {
+       { NSSRC_FILES, files_setgrent, (void *)ENDGRENT },
+#ifdef HESIOD
+       { NSSRC_DNS, dns_setgrent, (void *)ENDGRENT },
+#endif
 #ifdef YP
-       tryagain:
+       { NSSRC_NIS, nis_setgrent, (void *)ENDGRENT },
+#endif
+       { NSSRC_COMPAT, compat_setgrent, (void *)ENDGRENT },
+#ifdef NS_CACHING
+       NS_CACHE_CB(&endgrent_cache_info)
+#endif
+       { NULL, NULL, NULL }
+};
+
+#ifdef NS_CACHING
+static const nss_cache_info getgrent_r_cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+       group, (void *)nss_lt_all,
+       grp_marshal_func, grp_unmarshal_func);
+#endif
+
+static const ns_dtab getgrent_r_dtab[] = {
+       { NSSRC_FILES, files_group, (void *)nss_lt_all },
+#ifdef HESIOD
+       { NSSRC_DNS, dns_group, (void *)nss_lt_all },
 #endif
-       rval = grscan(1, gid, NULL);
 #ifdef YP
-       if(rval == -1 && _gr_yp_enabled) {
-               char buf[16];
-               snprintf(buf, sizeof buf, "%d", (unsigned)gid);
-               if (!(rval = _getypgroup(&_gr_group, buf, "group.bygid")))
-                       goto tryagain;
-       }
+       { NSSRC_NIS, nis_group, (void *)nss_lt_all },
 #endif
-       if (!_gr_stayopen)
-               endgrent();
-       return (rval) ? &_gr_group : NULL;
-}
+       { NSSRC_COMPAT, compat_group, (void *)nss_lt_all },
+#ifdef NS_CACHING
+       NS_CACHE_CB(&getgrent_r_cache_info)
+#endif
+       { NULL, NULL, NULL }
+};
 
 static int
-start_gr(void)
+gr_addgid(gid_t gid, gid_t *groups, int maxgrp, int *grpcnt)
 {
-       if (_gr_fp) {
-               rewind(_gr_fp);
-               return(1);
+       int     ret, dupc;
+
+       for (dupc = 0; dupc < MIN(maxgrp, *grpcnt); dupc++) {
+               if (groups[dupc] == gid)
+                       return 1;
        }
-       _gr_fp = fopen(_PATH_GROUP, "r");
-       if(!_gr_fp) return 0;
-#ifdef YP
+
+       ret = 1;
+       if (*grpcnt < maxgrp)
+               groups[*grpcnt] = gid;
+       else
+               ret = 0;
+
+       (*grpcnt)++;
+
+       return ret;
+}
+
+static int
+getgroupmembership_fallback(void *retval, void *mdata, va_list ap)
+{
+       const ns_src src[] = {
+               { mdata, NS_SUCCESS },
+               { NULL, 0}
+       };
+       struct group    grp;
+       struct group    *grp_p;
+       char            *buf;
+       size_t          bufsize;
+       const char      *uname;
+       gid_t           *groups;
+       gid_t           agroup;
+       int             maxgrp, *grpcnt;
+       int             i, rv, ret_errno;
+
        /*
-        * This is a disgusting hack, used to determine when YP is enabled.
-        * This would be easier if we had a group database to go along with
-        * the password database.
+        * As this is a fallback method, only provided src
+        * list will be respected during methods search.
         */
-       {
-               char *my_line;
-               size_t linelen;
-               _gr_yp_enabled = 0;
-               while((my_line = fgetln(_gr_fp, &linelen)) != NULL) {
-                       if(my_line[0] == '+') {
-                               if(my_line[1] && my_line[1] != ':' && !_gr_yp_enabled) {
-                                       _gr_yp_enabled = 1;
-                               } else {
-                                       _gr_yp_enabled = -1;
-                                       break;
+       assert(src[0].name != NULL);
+
+       uname = va_arg(ap, const char *);
+       agroup = va_arg(ap, gid_t);
+       groups = va_arg(ap, gid_t *);
+       maxgrp = va_arg(ap, int);
+       grpcnt = va_arg(ap, int *);
+
+       rv = NS_UNAVAIL;
+
+       buf = malloc(GRP_STORAGE_INITIAL);
+       if (buf == NULL)
+               goto out;
+
+       bufsize = GRP_STORAGE_INITIAL;
+
+       gr_addgid(agroup, groups, maxgrp, grpcnt);
+
+       _nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", src, 0);
+       for (;;) {
+               do {
+                       ret_errno = 0;
+                       grp_p = NULL;
+                       rv = _nsdispatch(&grp_p, getgrent_r_dtab, NSDB_GROUP,
+                           "getgrent_r", src, &grp, buf, bufsize, &ret_errno);
+
+                       if (grp_p == NULL && ret_errno == ERANGE) {
+                               free(buf);
+                               if ((bufsize << 1) > GRP_STORAGE_MAX) {
+                                       buf = NULL;
+                                       errno = ERANGE;
+                                       goto out;
+                               }
+
+                               bufsize <<= 1;
+                               buf = malloc(bufsize);
+                               if (buf == NULL) {
+                                       goto out;
                                }
                        }
+               } while (grp_p == NULL && ret_errno == ERANGE);
+
+               if (ret_errno != 0) {
+                       errno = ret_errno;
+                       goto out;
                }
-               rewind(_gr_fp);
-       }
-#endif
 
-       if (maxlinelength == 0) {
-               if ((line = (char *)malloc(MAXLINELENGTH)) == NULL)
-                       return 0;
-               maxlinelength += MAXLINELENGTH;
-       }
+               if (grp_p == NULL)
+                       break;
 
-       if (maxgrp == 0) {
-               if ((members = (char **) malloc(sizeof(char **) * 
-                                              MAXGRP)) == NULL)
-                       return 0;
-               maxgrp += MAXGRP;
+               for (i = 0; grp.gr_mem[i]; i++) {
+                       if (strcmp(grp.gr_mem[i], uname) == 0)
+                           gr_addgid(grp.gr_gid, groups, maxgrp, grpcnt);
+               }
        }
 
-       return 1;
+       _nsdispatch(NULL, endgrent_dtab, NSDB_GROUP, "endgrent", src);
+out:
+       free(buf);
+       return (rv);
 }
 
+/* XXX IEEE Std 1003.1, 2003 specifies `void setgrent(void)' */
 int
 setgrent(void)
 {
-       return setgroupent(0);
+       _nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", defaultsrc, 0);
+       return (1);
 }
 
+
 int
 setgroupent(int stayopen)
 {
-       if (!start_gr())
-               return 0;
-       _gr_stayopen = stayopen;
-#ifdef YP
-       _gr_stepping_yp = 0;
-#endif
-       return 1;
+       _nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", defaultsrc,
+           stayopen);
+       return (1);
 }
 
+
 void
 endgrent(void)
 {
-#ifdef YP
-       _gr_stepping_yp = 0;
-#endif
-       if (_gr_fp) {
-               (void)fclose(_gr_fp);
-               _gr_fp = NULL;
-       }
+       _nsdispatch(NULL, endgrent_dtab, NSDB_GROUP, "endgrent", defaultsrc);
 }
 
-static int
-grscan(int search, gid_t gid, const char *name)
+
+int
+getgrent_r(struct group *grp, char *buffer, size_t bufsize,
+    struct group **result)
 {
-       char *cp, **m;
-       char *bp;
+       int     rv, ret_errno;
+
+       ret_errno = 0;
+       *result = NULL;
+       rv = _nsdispatch(result, getgrent_r_dtab, NSDB_GROUP, "getgrent_r", defaultsrc,
+           grp, buffer, bufsize, &ret_errno);
+       if (rv == NS_SUCCESS)
+               return (0);
+       else
+               return (ret_errno);
+}
 
 
-#ifdef YP
-       int _ypfound;
+int
+getgrnam_r(const char *name, struct group *grp, char *buffer, size_t bufsize,
+    struct group **result)
+{
+#ifdef NS_CACHING
+       static const nss_cache_info cache_info =
+               NS_COMMON_CACHE_INFO_INITIALIZER(
+               group, (void *)nss_lt_name,
+               grp_id_func, grp_marshal_func, grp_unmarshal_func);
+#endif
+
+       static const ns_dtab dtab[] = {
+               { NSSRC_FILES, files_group, (void *)nss_lt_name },
+#ifdef HESIOD
+               { NSSRC_DNS, dns_group, (void *)nss_lt_name },
 #endif
-       for (;;) {
 #ifdef YP
-               _ypfound = 0;
+               { NSSRC_NIS, nis_group, (void *)nss_lt_name },
 #endif
-               if (fgets(line, maxlinelength, _gr_fp) == NULL)
-                       return(0);
-
-               if (!index(line, '\n')) {
-                       do {
-                               if (feof(_gr_fp))
-                                       return(0);
-                       
-                               /* don't allocate infinite memory */
-                               if (MAXLINELENGTHLIMIT > 0 && 
-                                   maxlinelength >= MAXLINELENGTHLIMIT)
-                                       return(0);
-
-                               if ((line = reallocf(line, 
-                                    (maxlinelength + MAXLINELENGTH))) == NULL)
-                                       return(0);
-                       
-                               if (fgets(line + maxlinelength - 1, 
-                                         MAXLINELENGTH + 1, _gr_fp) == NULL)
-                                       return(0);
-
-                               maxlinelength += MAXLINELENGTH;
-                       } while (!index(line + maxlinelength - 
-                                      MAXLINELENGTH - 1, '\n'));
-               }
-
-#ifdef GROUP_IGNORE_COMMENTS
-               /* 
-                * Ignore comments: ^[ \t]*#
-                */
-               for (cp = line; *cp != '\0'; cp++)
-                       if (*cp != ' ' && *cp != '\t')
-                               break;
-               if (*cp == '#' || *cp == '\0')
-                       continue;
+               { NSSRC_COMPAT, compat_group, (void *)nss_lt_name },
+#ifdef NS_CACHING
+               NS_CACHE_CB(&cache_info)
 #endif
+               { NULL, NULL, NULL }
+       };
+       int     rv, ret_errno;
 
-               bp = line;
+       ret_errno = 0;
+       *result = NULL;
+       rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrnam_r", defaultsrc,
+           name, grp, buffer, bufsize, &ret_errno);
+       if (rv == NS_SUCCESS)
+               return (0);
+       else
+               return (ret_errno);
+}
 
-               if ((_gr_group.gr_name = strsep(&bp, ":\n")) == NULL)
-                       break;
-#ifdef YP
-               /*
-                * XXX   We need to be careful to avoid proceeding
-                * past this point under certain circumstances or
-                * we risk dereferencing null pointers down below.
-                */
-               if (_gr_group.gr_name[0] == '+') {
-                       if (strlen(_gr_group.gr_name) == 1) {
-                               switch(search) {
-                               case 0:
-                                       return(1);
-                               case 1:
-                                       return(-1);
-                               default:
-                                       return(0);
-                               }
-                       } else {
-                               cp = &_gr_group.gr_name[1];
-                               if (search && name != NULL)
-                                       if (strcmp(cp, name))
-                                               continue;
-                               if (!_getypgroup(&_gr_group, cp,
-                                               "group.byname"))
-                                       continue;
-                               if (search && name == NULL)
-                                       if (gid != _gr_group.gr_gid)
-                                               continue;
-                       /* We're going to override -- tell the world. */
-                               _ypfound++;
-                       }
-               }
-#else
-               if (_gr_group.gr_name[0] == '+')
-                       continue;
-#endif /* YP */
-               if (search && name) {
-                       if(strcmp(_gr_group.gr_name, name)) {
-                               continue;
-                       }
-               }
-#ifdef YP
-               if ((cp = strsep(&bp, ":\n")) == NULL) {
-                       if (_ypfound)
-                               return(1);
-                       else
-                               break;
-               }
-               if (strlen(cp) || !_ypfound)
-                       _gr_group.gr_passwd = cp;
-#else
-               if ((_gr_group.gr_passwd = strsep(&bp, ":\n")) == NULL)
-                       break;
+
+int
+getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize,
+    struct group **result)
+{
+#ifdef NS_CACHING
+       static const nss_cache_info cache_info =
+               NS_COMMON_CACHE_INFO_INITIALIZER(
+               group, (void *)nss_lt_id,
+               grp_id_func, grp_marshal_func, grp_unmarshal_func);
 #endif
-               if (!(cp = strsep(&bp, ":\n"))) {
-#ifdef YP
-                       if (_ypfound)
-                               return(1);
-                       else
+
+       static const ns_dtab dtab[] = {
+               { NSSRC_FILES, files_group, (void *)nss_lt_id },
+#ifdef HESIOD
+               { NSSRC_DNS, dns_group, (void *)nss_lt_id },
 #endif
-                               continue;
-               }
 #ifdef YP
-               /*
-                * Hurm. Should we be doing this? We allow UIDs to
-                * be overridden -- what about GIDs?
-                */
-               if (!_ypfound)
+               { NSSRC_NIS, nis_group, (void *)nss_lt_id },
 #endif
-               _gr_group.gr_gid = atoi(cp);
-               if (search && name == NULL && _gr_group.gr_gid != gid)
-                       continue;
-               cp = NULL;
-               if (bp == NULL) /* !!! Must check for this! */
-                       break;
-#ifdef YP
-               if ((cp = strsep(&bp, ":\n")) == NULL)
-                       break;
-
-               if (!strlen(cp) && _ypfound)
-                       return(1);
-               else
-                       members[0] = NULL;
-               bp = cp;
-               cp = NULL;
+               { NSSRC_COMPAT, compat_group, (void *)nss_lt_id },
+#ifdef NS_CACHING
+               NS_CACHE_CB(&cache_info)
 #endif
-               for (m = members; ; bp++) {
-                       if (m == (members + maxgrp - 1)) {
-                               if ((members = (char **)
-                                    reallocf(members, 
-                                            sizeof(char **) * 
-                                            (maxgrp + MAXGRP))) == NULL)
-                                       return(0);
-                               m = members + maxgrp - 1;
-                               maxgrp += MAXGRP;
+               { NULL, NULL, NULL }
+       };
+       int     rv, ret_errno;
+
+       ret_errno = 0;
+       *result = NULL;
+       rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrgid_r", defaultsrc,
+           gid, grp, buffer, bufsize, &ret_errno);
+       if (rv == NS_SUCCESS)
+               return (0);
+       else
+               return (ret_errno);
+}
+
+
+
+int
+__getgroupmembership(const char *uname, gid_t agroup, gid_t *groups,
+       int maxgrp, int *grpcnt)
+{
+       static const ns_dtab dtab[] = {
+               NS_FALLBACK_CB(getgroupmembership_fallback)
+               { NULL, NULL, NULL }
+       };
+       int rv;
+
+       assert(uname != NULL);
+       /* groups may be NULL if just sizing when invoked with maxgrp = 0 */
+       assert(grpcnt != NULL);
+
+       *grpcnt = 0;
+       rv = _nsdispatch(NULL, dtab, NSDB_GROUP, "getgroupmembership",
+           defaultsrc, uname, agroup, groups, maxgrp, grpcnt);
+
+       /* too many groups found? */
+       return (*grpcnt > maxgrp ? -1 : 0);
+}
+
+
+static struct group     grp;
+static char            *grp_storage;
+static size_t           grp_storage_size;
+
+static struct group *
+getgr(int (*fn)(union key, struct group *, char *, size_t, struct group **),
+    union key key)
+{
+       int              rv;
+       struct group    *res;
+
+       if (grp_storage == NULL) {
+               grp_storage = malloc(GRP_STORAGE_INITIAL);
+               if (grp_storage == NULL)
+                       return (NULL);
+               grp_storage_size = GRP_STORAGE_INITIAL;
+       }
+       do {
+               rv = fn(key, &grp, grp_storage, grp_storage_size, &res);
+               if (res == NULL && rv == ERANGE) {
+                       free(grp_storage);
+                       if ((grp_storage_size << 1) > GRP_STORAGE_MAX) {
+                               grp_storage = NULL;
+                               errno = ERANGE;
+                               return (NULL);
                        }
-                       if (*bp == ',') {
-                               if (cp) {
-                                       *bp = '\0';
-                                       *m++ = cp;
-                                       cp = NULL;
-                               }
-                       } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
-                               if (cp) {
-                                       *bp = '\0';
-                                       *m++ = cp;
-                               }
-                               break;
-                       } else if (cp == NULL)
-                               cp = bp;
-                       
+                       grp_storage_size <<= 1;
+                       grp_storage = malloc(grp_storage_size);
+                       if (grp_storage == NULL)
+                               return (NULL);
                }
-               _gr_group.gr_mem = members;
-               *m = NULL;
-               return(1);
-       }
-       /* NOTREACHED */
-       return (0);
+       } while (res == NULL && rv == ERANGE);
+       if (rv != 0)
+               errno = rv;
+       return (res);
 }
 
-#ifdef YP
 
 static int
-_gr_breakout_yp(struct group *gr, char *result)
+wrap_getgrnam_r(union key key, struct group *grp, char *buffer, size_t bufsize,
+    struct group **res)
 {
-       char *s, *cp;
-       char **m;
+       return (getgrnam_r(key.name, grp, buffer, bufsize, res));
+}
+
+
+static int
+wrap_getgrgid_r(union key key, struct group *grp, char *buffer, size_t bufsize,
+    struct group **res)
+{
+       return (getgrgid_r(key.gid, grp, buffer, bufsize, res));
+}
+
+
+static int
+wrap_getgrent_r(union key key __unused, struct group *grp, char *buffer,
+    size_t bufsize, struct group **res)
+{
+       return (getgrent_r(grp, buffer, bufsize, res));
+}
+
+
+struct group *
+getgrnam(const char *name)
+{
+       union key key;
+
+       key.name = name;
+       return (getgr(wrap_getgrnam_r, key));
+}
+
+
+struct group *
+getgrgid(gid_t gid)
+{
+       union key key;
+
+       key.gid = gid;
+       return (getgr(wrap_getgrgid_r, key));
+}
+
+
+struct group *
+getgrent(void)
+{
+       union key key;
+
+       key.gid = 0; /* not used */
+       return (getgr(wrap_getgrent_r, key));
+}
+
+
+static int
+is_comment_line(const char *s, size_t n)
+{
+       const char      *eom;
+
+       eom = &s[n];
+
+       for (; s < eom; s++)
+               if (*s == '#' || !isspace((unsigned char)*s))
+                       break;
+       return (*s == '#' || s == eom);
+}
 
-       /*
-        * XXX If 's' ends up being a NULL pointer, punt on this group.
-        * It means the NIS group entry is badly formatted and should
-        * be skipped.
-        */
-       if ((s = strsep(&result, ":")) == NULL) return 0; /* name */
-       gr->gr_name = s;
 
-       if ((s = strsep(&result, ":")) == NULL) return 0; /* password */
-       gr->gr_passwd = s;
+/*
+ * files backend
+ */
+static void
+files_endstate(void *p)
+{
 
-       if ((s = strsep(&result, ":")) == NULL) return 0; /* gid */
-       gr->gr_gid = atoi(s);
+       if (p == NULL)
+               return;
+       if (((struct files_state *)p)->fp != NULL)
+               fclose(((struct files_state *)p)->fp);
+       free(p);
+}
 
-       if ((s = result) == NULL) return 0;
-       cp = 0;
 
-       for (m = members; ; s++) {
-               if (m == members + maxgrp - 1) {
-                       if ((members = (char **)reallocf(members, 
-                            sizeof(char **) * (maxgrp + MAXGRP))) == NULL)
-                               return(0);
-                       m = members + maxgrp - 1;
-                       maxgrp += MAXGRP;
+static int
+files_setgrent(void *retval, void *mdata, va_list ap)
+{
+       struct files_state *st;
+       int              rv, stayopen;
+
+       rv = files_getstate(&st);
+       if (rv != 0)
+               return (NS_UNAVAIL);
+       switch ((enum constants)mdata) {
+       case SETGRENT:
+               stayopen = va_arg(ap, int);
+               if (st->fp != NULL)
+                       rewind(st->fp);
+               else if (stayopen)
+                       st->fp = fopen(_PATH_GROUP, "r");
+               break;
+       case ENDGRENT:
+               if (st->fp != NULL) {
+                       fclose(st->fp);
+                       st->fp = NULL;
                }
-               if (*s == ',') {
-                       if (cp) {
-                               *s = '\0';
-                               *m++ = cp;
-                               cp = NULL;
-                       }
-               } else if (*s == '\0' || *s == '\n' || *s == ' ') {
-                       if (cp) {
-                               *s = '\0';
-                               *m++ = cp;
-                       }
+               break;
+       default:
+               break;
+       }
+       return (NS_UNAVAIL);
+}
+
+
+static int
+files_group(void *retval, void *mdata, va_list ap)
+{
+       struct files_state      *st;
+       enum nss_lookup_type     how;
+       const char              *name, *line;
+       struct group            *grp;
+       gid_t                    gid;
+       char                    *buffer;
+       size_t                   bufsize, linesize;
+       off_t                    pos;
+       int                      rv, stayopen, *errnop;
+
+       name = NULL;
+       gid = (gid_t)-1;
+       how = (enum nss_lookup_type)mdata;
+       switch (how) {
+       case nss_lt_name:
+               name = va_arg(ap, const char *);
+               break;
+       case nss_lt_id:
+               gid = va_arg(ap, gid_t);
+               break;
+       case nss_lt_all:
+               break;
+       default:
+               return (NS_NOTFOUND);
+       }
+       grp = va_arg(ap, struct group *);
+       buffer = va_arg(ap, char *);
+       bufsize = va_arg(ap, size_t);
+       errnop = va_arg(ap, int *);
+       *errnop = files_getstate(&st);
+       if (*errnop != 0)
+               return (NS_UNAVAIL);
+       if (st->fp == NULL &&
+           ((st->fp = fopen(_PATH_GROUP, "r")) == NULL)) {
+               *errnop = errno;
+               return (NS_UNAVAIL);
+       }
+       if (how == nss_lt_all)
+               stayopen = 1;
+       else {
+               rewind(st->fp);
+               stayopen = st->stayopen;
+       }
+       rv = NS_NOTFOUND;
+       pos = ftello(st->fp);
+       while ((line = fgetln(st->fp, &linesize)) != NULL) {
+               if (line[linesize-1] == '\n')
+                       linesize--;
+               rv = __gr_match_entry(line, linesize, how, name, gid);
+               if (rv != NS_SUCCESS)
+                       continue;
+               /* We need room at least for the line, a string NUL
+                * terminator, alignment padding, and one (char *)
+                * pointer for the member list terminator.
+                */
+               if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) {
+                       *errnop = ERANGE;
+                       rv = NS_RETURN;
                        break;
-               } else if (cp == NULL) {
-                       cp = s;
                }
+               memcpy(buffer, line, linesize);
+               buffer[linesize] = '\0';
+               rv = __gr_parse_entry(buffer, linesize, grp,
+                   &buffer[linesize + 1], bufsize - linesize - 1, errnop);
+               if (rv & NS_TERMINATE)
+                       break;
+               pos = ftello(st->fp);
        }
-       _gr_group.gr_mem = members;
-       *m = NULL;
+       if (!stayopen && st->fp != NULL) {
+               fclose(st->fp);
+               st->fp = NULL;
+       }
+       if (rv == NS_SUCCESS && retval != NULL)
+               *(struct group **)retval = grp;
+       else if (rv == NS_RETURN && *errnop == ERANGE && st->fp != NULL)
+               fseeko(st->fp, pos, SEEK_SET);
+       return (rv);
+}
+
 
-       return 1;
+#ifdef HESIOD
+/*
+ * dns backend
+ */
+static void
+dns_endstate(void *p)
+{
+
+       free(p);
+}
+
+
+static int
+dns_setgrent(void *retval, void *cb_data, va_list ap)
+{
+       struct dns_state        *st;
+       int                      rv;
+
+       rv = dns_getstate(&st);
+       if (rv != 0)
+               return (NS_UNAVAIL);
+       st->counter = 0;
+       return (NS_UNAVAIL);
 }
 
-static char *_gr_yp_domain;
 
 static int
-_getypgroup(struct group *gr, const char *name, const char *map)
+dns_group(void *retval, void *mdata, va_list ap)
 {
-       char *result, *s;
-       static char resultbuf[YPMAXRECORD + 2];
-       int resultlen;
+       char                     buf[HESIOD_NAME_MAX];
+       struct dns_state        *st;
+       struct group            *grp;
+       const char              *name, *label;
+       void                    *ctx;
+       char                    *buffer, **hes;
+       size_t                   bufsize, adjsize, linesize;
+       gid_t                    gid;
+       enum nss_lookup_type     how;
+       int                      rv, *errnop;
 
-       if(!_gr_yp_domain) {
-               if(yp_get_default_domain(&_gr_yp_domain))
-                 return 0;
+       ctx = NULL;
+       hes = NULL;
+       name = NULL;
+       gid = (gid_t)-1;
+       how = (enum nss_lookup_type)mdata;
+       switch (how) {
+       case nss_lt_name:
+               name = va_arg(ap, const char *);
+               break;
+       case nss_lt_id:
+               gid = va_arg(ap, gid_t);
+               break;
+       case nss_lt_all:
+               break;
+       }
+       grp     = va_arg(ap, struct group *);
+       buffer  = va_arg(ap, char *);
+       bufsize = va_arg(ap, size_t);
+       errnop  = va_arg(ap, int *);
+       *errnop = dns_getstate(&st);
+       if (*errnop != 0)
+               return (NS_UNAVAIL);
+       if (hesiod_init(&ctx) != 0) {
+               *errnop = errno;
+               rv = NS_UNAVAIL;
+               goto fin;
        }
+       do {
+               rv = NS_NOTFOUND;
+               switch (how) {
+               case nss_lt_name:
+                       label = name;
+                       break;
+               case nss_lt_id:
+                       if (snprintf(buf, sizeof(buf), "%lu",
+                           (unsigned long)gid) >= sizeof(buf))
+                               goto fin;
+                       label = buf;
+                       break;
+               case nss_lt_all:
+                       if (st->counter < 0)
+                               goto fin;
+                       if (snprintf(buf, sizeof(buf), "group-%ld",
+                           st->counter++) >= sizeof(buf))
+                               goto fin;
+                       label = buf;
+                       break;
+               }
+               hes = hesiod_resolve(ctx, label,
+                   how == nss_lt_id ? "gid" : "group");
+               if ((how == nss_lt_id && hes == NULL &&
+                   (hes = hesiod_resolve(ctx, buf, "group")) == NULL) ||
+                   hes == NULL) {
+                       if (how == nss_lt_all)
+                               st->counter = -1;
+                       if (errno != ENOENT)
+                               *errnop = errno;
+                       goto fin;
+               }
+               rv = __gr_match_entry(hes[0], strlen(hes[0]), how, name, gid);
+               if (rv != NS_SUCCESS) {
+                       hesiod_free_list(ctx, hes);
+                       hes = NULL;
+                       continue;
+               }
+               /* We need room at least for the line, a string NUL
+                * terminator, alignment padding, and one (char *)
+                * pointer for the member list terminator.
+                */
+               adjsize = bufsize - _ALIGNBYTES - sizeof(char *);
+               linesize = strlcpy(buffer, hes[0], adjsize);
+               if (linesize >= adjsize) {
+                       *errnop = ERANGE;
+                       rv = NS_RETURN;
+                       goto fin;
+               }
+               hesiod_free_list(ctx, hes);
+               hes = NULL;
+               rv = __gr_parse_entry(buffer, linesize, grp,
+                   &buffer[linesize + 1], bufsize - linesize - 1, errnop);
+       } while (how == nss_lt_all && !(rv & NS_TERMINATE));
+fin:
+       if (hes != NULL)
+               hesiod_free_list(ctx, hes);
+       if (ctx != NULL)
+               hesiod_end(ctx);
+       if (rv == NS_SUCCESS && retval != NULL)
+               *(struct group **)retval = grp;
+       return (rv);
+}
+#endif /* HESIOD */
 
-       if(yp_match(_gr_yp_domain, map, name, strlen(name),
-                   &result, &resultlen))
-               return 0;
 
-       s = strchr(result, '\n');
-       if(s) *s = '\0';
+#ifdef YP
+/*
+ * nis backend
+ */
+static void
+nis_endstate(void *p)
+{
+
+       if (p == NULL)
+               return;
+       free(((struct nis_state *)p)->key);
+       free(p);
+}
 
-       if (strlcpy(resultbuf, result, sizeof(resultbuf)) >= sizeof(resultbuf))
-               return(0);
-       free(result);
-       return(_gr_breakout_yp(gr, resultbuf));
 
+static int
+nis_setgrent(void *retval, void *cb_data, va_list ap)
+{
+       struct nis_state        *st;
+       int                      rv;
+
+       rv = nis_getstate(&st);
+       if (rv != 0)
+               return (NS_UNAVAIL);
+       st->done = 0;
+       free(st->key);
+       st->key = NULL;
+       return (NS_UNAVAIL);
 }
 
 
 static int
-_nextypgroup(struct group *gr)
+nis_group(void *retval, void *mdata, va_list ap)
 {
-       static char *key;
-       static size_t keylen;
-       char *lastkey, *result;
-       static char resultbuf[YPMAXRECORD + 2];
-       size_t resultlen;
-       int rv;
+       char             *map;
+       struct nis_state *st;
+       struct group    *grp;
+       const char      *name;
+       char            *buffer, *key, *result;
+       size_t           bufsize;
+       gid_t            gid;
+       enum nss_lookup_type how;
+       int             *errnop, keylen, resultlen, rv;
 
-       if(!_gr_yp_domain) {
-               if(yp_get_default_domain(&_gr_yp_domain))
-                 return 0;
+       name = NULL;
+       gid = (gid_t)-1;
+       how = (enum nss_lookup_type)mdata;
+       switch (how) {
+       case nss_lt_name:
+               name = va_arg(ap, const char *);
+               map = "group.byname";
+               break;
+       case nss_lt_id:
+               gid = va_arg(ap, gid_t);
+               map = "group.bygid";
+               break;
+       case nss_lt_all:
+               map = "group.byname";
+               break;
        }
-
-       if(!_gr_stepping_yp) {
-               if(key) free(key);
-               rv = yp_first(_gr_yp_domain, "group.byname",
-                             &key, &keylen, &result, &resultlen);
-               if(rv) {
-                       return 0;
+       grp     = va_arg(ap, struct group *);
+       buffer  = va_arg(ap, char *);
+       bufsize = va_arg(ap, size_t);
+       errnop  = va_arg(ap, int *);
+       *errnop = nis_getstate(&st);
+       if (*errnop != 0)
+               return (NS_UNAVAIL);
+       if (st->domain[0] == '\0') {
+               if (getdomainname(st->domain, sizeof(st->domain)) != 0) {
+                       *errnop = errno;
+                       return (NS_UNAVAIL);
                }
-               _gr_stepping_yp = 1;
-               goto unpack;
-       } else {
-tryagain:
-               lastkey = key;
-               rv = yp_next(_gr_yp_domain, "group.byname", key, keylen,
-                            &key, &keylen, &result, &resultlen);
-               free(lastkey);
-unpack:
-               if(rv) {
-                       _gr_stepping_yp = 0;
-                       return 0;
+       }
+       result = NULL;
+       do {
+               rv = NS_NOTFOUND;
+               switch (how) {
+               case nss_lt_name:
+                       if (strlcpy(buffer, name, bufsize) >= bufsize)
+                               goto erange;
+                       break;
+               case nss_lt_id:
+                       if (snprintf(buffer, bufsize, "%lu",
+                           (unsigned long)gid) >= bufsize)
+                               goto erange;
+                       break;
+               case nss_lt_all:
+                       if (st->done)
+                               goto fin;
+                       break;
                }
+               result = NULL;
+               if (how == nss_lt_all) {
+                       if (st->key == NULL)
+                               rv = yp_first(st->domain, map, &st->key,
+                                   &st->keylen, &result, &resultlen);
+                       else {
+                               key = st->key;
+                               keylen = st->keylen;
+                               st->key = NULL;
+                               rv = yp_next(st->domain, map, key, keylen,
+                                   &st->key, &st->keylen, &result,
+                                   &resultlen);
+                               free(key);
+                       }
+                       if (rv != 0) {
+                               free(result);
+                               free(st->key);
+                               st->key = NULL;
+                               if (rv == YPERR_NOMORE) {
+                                       st->done = 1;
+                                       rv = NS_NOTFOUND;
+                               } else
+                                       rv = NS_UNAVAIL;
+                               goto fin;
+                       }
+               } else {
+                       rv = yp_match(st->domain, map, buffer, strlen(buffer),
+                           &result, &resultlen);
+                       if (rv == YPERR_KEY) {
+                               rv = NS_NOTFOUND;
+                               continue;
+                       } else if (rv != 0) {
+                               free(result);
+                               rv = NS_UNAVAIL;
+                               continue;
+                       }
+               }
+               /* We need room at least for the line, a string NUL
+                * terminator, alignment padding, and one (char *)
+                * pointer for the member list terminator.
+                */
+               if (resultlen >= bufsize - _ALIGNBYTES - sizeof(char *))
+                       goto erange;
+               memcpy(buffer, result, resultlen);
+               buffer[resultlen] = '\0';
+               free(result);
+               rv = __gr_match_entry(buffer, resultlen, how, name, gid);
+               if (rv == NS_SUCCESS)
+                       rv = __gr_parse_entry(buffer, resultlen, grp,
+                           &buffer[resultlen+1], bufsize - resultlen - 1,
+                           errnop);
+       } while (how == nss_lt_all && !(rv & NS_TERMINATE));
+fin:
+       if (rv == NS_SUCCESS && retval != NULL)
+               *(struct group **)retval = grp;
+       return (rv);
+erange:
+       *errnop = ERANGE;
+       return (NS_RETURN);
+}
+#endif /* YP */
 
-               if(resultlen > sizeof(resultbuf)) {
-                       free(result);
-                       goto tryagain;
+
+
+/*
+ * compat backend
+ */
+static void
+compat_endstate(void *p)
+{
+       struct compat_state *st;
+
+       if (p == NULL)
+               return;
+       st = (struct compat_state *)p;
+       free(st->name);
+       if (st->fp != NULL)
+               fclose(st->fp);
+       free(p);
+}
+
+
+static int
+compat_setgrent(void *retval, void *mdata, va_list ap)
+{
+       static const ns_src compatsrc[] = {
+#ifdef YP
+               { NSSRC_NIS, NS_SUCCESS },
+#endif
+               { NULL, 0 }
+       };
+       ns_dtab dtab[] = {
+#ifdef HESIOD
+               { NSSRC_DNS, dns_setgrent, NULL },
+#endif
+#ifdef YP
+               { NSSRC_NIS, nis_setgrent, NULL },
+#endif
+               { NULL, NULL, NULL }
+       };
+       struct compat_state *st;
+       int              rv, stayopen;
+
+#define set_setent(x, y) do {                                  \
+       int i;                                                  \
+                                                               \
+       for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++)      \
+               x[i].mdata = (void *)y;                         \
+} while (0)
+
+       rv = compat_getstate(&st);
+       if (rv != 0)
+               return (NS_UNAVAIL);
+       switch ((enum constants)mdata) {
+       case SETGRENT:
+               stayopen = va_arg(ap, int);
+               if (st->fp != NULL)
+                       rewind(st->fp);
+               else if (stayopen)
+                       st->fp = fopen(_PATH_GROUP, "r");
+               set_setent(dtab, mdata);
+               _nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgrent",
+                   compatsrc, 0);
+               break;
+       case ENDGRENT:
+               if (st->fp != NULL) {
+                       fclose(st->fp);
+                       st->fp = NULL;
                }
+               set_setent(dtab, mdata);
+               _nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "endgrent",
+                   compatsrc, 0);
+               break;
+       default:
+               break;
+       }
+       st->compat = COMPAT_MODE_OFF;
+       free(st->name);
+       st->name = NULL;
+       return (NS_UNAVAIL);
+#undef set_setent
+}
 
-               strncpy(resultbuf, result, resultlen);
-               resultbuf[resultlen] = '\0';
-               free(result);
-               if((result = strchr(resultbuf, '\n')) != NULL)
-                       *result = '\0';
-               if (_gr_breakout_yp(gr, resultbuf))
-                       return(1);
-               else
-                       goto tryagain;
+
+static int
+compat_group(void *retval, void *mdata, va_list ap)
+{
+       static const ns_src compatsrc[] = {
+#ifdef YP
+               { NSSRC_NIS, NS_SUCCESS },
+#endif
+               { NULL, 0 }
+       };
+       ns_dtab dtab[] = {
+#ifdef YP
+               { NSSRC_NIS, nis_group, NULL },
+#endif
+#ifdef HESIOD
+               { NSSRC_DNS, dns_group, NULL },
+#endif
+               { NULL, NULL, NULL }
+       };
+       struct compat_state     *st;
+       enum nss_lookup_type     how;
+       const char              *name, *line;
+       struct group            *grp;
+       gid_t                    gid;
+       char                    *buffer, *p;
+       void                    *discard;
+       size_t                   bufsize, linesize;
+       off_t                    pos;
+       int                      rv, stayopen, *errnop;
+
+#define set_lookup_type(x, y) do {                             \
+       int i;                                                  \
+                                                               \
+       for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++)      \
+               x[i].mdata = (void *)y;                         \
+} while (0)
+
+       name = NULL;
+       gid = (gid_t)-1;
+       how = (enum nss_lookup_type)mdata;
+       switch (how) {
+       case nss_lt_name:
+               name = va_arg(ap, const char *);
+               break;
+       case nss_lt_id:
+               gid = va_arg(ap, gid_t);
+               break;
+       case nss_lt_all:
+               break;
+       default:
+               return (NS_NOTFOUND);
+       }
+       grp = va_arg(ap, struct group *);
+       buffer = va_arg(ap, char *);
+       bufsize = va_arg(ap, size_t);
+       errnop = va_arg(ap, int *);
+       *errnop = compat_getstate(&st);
+       if (*errnop != 0)
+               return (NS_UNAVAIL);
+       if (st->fp == NULL &&
+           ((st->fp = fopen(_PATH_GROUP, "r")) == NULL)) {
+               *errnop = errno;
+               rv = NS_UNAVAIL;
+               goto fin;
+       }
+       if (how == nss_lt_all)
+               stayopen = 1;
+       else {
+               rewind(st->fp);
+               stayopen = st->stayopen;
+       }
+docompat:
+       switch (st->compat) {
+       case COMPAT_MODE_ALL:
+               set_lookup_type(dtab, how);
+               switch (how) {
+               case nss_lt_all:
+                       rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
+                           "getgrent_r", compatsrc, grp, buffer, bufsize,
+                           errnop);
+                       break;
+               case nss_lt_id:
+                       rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
+                           "getgrgid_r", compatsrc, gid, grp, buffer, bufsize,
+                           errnop);
+                       break;
+               case nss_lt_name:
+                       rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
+                           "getgrnam_r", compatsrc, name, grp, buffer,
+                           bufsize, errnop);
+                       break;
+               }
+               if (rv & NS_TERMINATE)
+                       goto fin;
+               st->compat = COMPAT_MODE_OFF;
+               break;
+       case COMPAT_MODE_NAME:
+               set_lookup_type(dtab, nss_lt_name);
+               rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
+                   "getgrnam_r", compatsrc, st->name, grp, buffer, bufsize,
+                   errnop);
+               switch (rv) {
+               case NS_SUCCESS:
+                       switch (how) {
+                       case nss_lt_name:
+                               if (strcmp(name, grp->gr_name) != 0)
+                                       rv = NS_NOTFOUND;
+                               break;
+                       case nss_lt_id:
+                               if (gid != grp->gr_gid)
+                                       rv = NS_NOTFOUND;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               case NS_RETURN:
+                       goto fin;
+               default:
+                       break;
+               }
+               free(st->name);
+               st->name = NULL;
+               st->compat = COMPAT_MODE_OFF;
+               if (rv == NS_SUCCESS)
+                       goto fin;
+               break;
+       default:
+               break;
+       }
+       rv = NS_NOTFOUND;
+       pos = ftello(st->fp);
+       while ((line = fgetln(st->fp, &linesize)) != NULL) {
+               if (line[linesize-1] == '\n')
+                       linesize--;
+               if (linesize > 2 && line[0] == '+') {
+                       p = memchr(&line[1], ':', linesize);
+                       if (p == NULL || p == &line[1])
+                               st->compat = COMPAT_MODE_ALL;
+                       else {
+                               st->name = malloc(p - line);
+                               if (st->name == NULL) {
+                                       syslog(LOG_ERR,
+                                        "getgrent memory allocation failure");
+                                       *errnop = ENOMEM;
+                                       rv = NS_UNAVAIL;
+                                       break;
+                               }
+                               memcpy(st->name, &line[1], p - line - 1);
+                               st->name[p - line - 1] = '\0';
+                               st->compat = COMPAT_MODE_NAME;
+                       }
+                       goto docompat;
+               }
+               rv = __gr_match_entry(line, linesize, how, name, gid);
+               if (rv != NS_SUCCESS)
+                       continue;
+               /* We need room at least for the line, a string NUL
+                * terminator, alignment padding, and one (char *)
+                * pointer for the member list terminator.
+                */
+               if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) {
+                       *errnop = ERANGE;
+                       rv = NS_RETURN;
+                       break;
+               }
+               memcpy(buffer, line, linesize);
+               buffer[linesize] = '\0';
+               rv = __gr_parse_entry(buffer, linesize, grp,
+                   &buffer[linesize + 1], bufsize - linesize - 1, errnop);
+               if (rv & NS_TERMINATE)
+                       break;
+               pos = ftello(st->fp);
+       }
+fin:
+       if (!stayopen && st->fp != NULL) {
+               fclose(st->fp);
+               st->fp = NULL;
        }
+       if (rv == NS_SUCCESS && retval != NULL)
+               *(struct group **)retval = grp;
+       else if (rv == NS_RETURN && *errnop == ERANGE && st->fp != NULL)
+               fseeko(st->fp, pos, SEEK_SET);
+       return (rv);
+#undef set_lookup_type
 }
 
-#endif /* YP */
+
+/*
+ * common group line matching and parsing
+ */
+int
+__gr_match_entry(const char *line, size_t linesize, enum nss_lookup_type how,
+    const char *name, gid_t gid)
+{
+       size_t           namesize;
+       const char      *p, *eol;
+       char            *q;
+       unsigned long    n;
+       int              i, needed;
+
+       if (linesize == 0 || is_comment_line(line, linesize))
+               return (NS_NOTFOUND);
+       switch (how) {
+       case nss_lt_name:       needed = 1; break;
+       case nss_lt_id:         needed = 2; break;
+       default:                needed = 2; break;
+       }
+       eol = &line[linesize];
+       for (p = line, i = 0; i < needed && p < eol; p++)
+               if (*p == ':')
+                       i++;
+       if (i < needed)
+               return (NS_NOTFOUND);
+       switch (how) {
+       case nss_lt_name:
+               namesize = strlen(name);
+               if (namesize + 1 == (size_t)(p - line) &&
+                   memcmp(line, name, namesize) == 0)
+                       return (NS_SUCCESS);
+               break;
+       case nss_lt_id:
+               n = strtoul(p, &q, 10);
+               if (q < eol && *q == ':' && gid == (gid_t)n)
+                       return (NS_SUCCESS);
+               break;
+       case nss_lt_all:
+               return (NS_SUCCESS);
+       default:
+               break;
+       }
+       return (NS_NOTFOUND);
+}
+
+
+int
+__gr_parse_entry(char *line, size_t linesize, struct group *grp, char *membuf,
+    size_t membufsize, int *errnop)
+{
+       char           *s_gid, *s_mem, *p, **members;
+       unsigned long   n;
+       int             maxmembers;
+
+       memset(grp, 0, sizeof(*grp));
+       members = (char **)_ALIGN(membuf);
+       membufsize -= (char *)members - membuf;
+       maxmembers = membufsize / sizeof(*members);
+       if (maxmembers <= 0 ||
+           (grp->gr_name = strsep(&line, ":")) == NULL ||
+           grp->gr_name[0] == '\0' ||
+           (grp->gr_passwd = strsep(&line, ":")) == NULL ||
+           (s_gid = strsep(&line, ":")) == NULL ||
+           s_gid[0] == '\0')
+               return (NS_NOTFOUND);
+       s_mem = line;
+       n = strtoul(s_gid, &s_gid, 10);
+       if (s_gid[0] != '\0')
+               return (NS_NOTFOUND);
+       grp->gr_gid = (gid_t)n;
+       grp->gr_mem = members;
+       while (maxmembers > 1 && s_mem != NULL) {
+               p = strsep(&s_mem, ",");
+               if (p != NULL && *p != '\0') {
+                       *members++ = p;
+                       maxmembers--;
+               }
+       }
+       *members = NULL;
+       if (s_mem == NULL)
+               return (NS_SUCCESS);
+       else {
+               *errnop = ERANGE;
+               return (NS_RETURN);
+       }
+}
index 54e2ce2..695cee8 100644 (file)
@@ -1,4 +1,4 @@
-/*
+/*-
  * Copyright (c) 1991, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
  * 4. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  * SUCH DAMAGE.
  *
  * @(#)getgrouplist.c  8.2 (Berkeley) 12/8/94
+ * $FreeBSD: src/lib/libc/gen/getgrouplist.c,v 1.16 2007/12/12 10:08:02 bushman Exp $
  * $DragonFly: src/lib/libc/gen/getgrouplist.c,v 1.6 2005/04/27 12:37:43 joerg Exp $
  */
 
 /*
  * get credential
  */
+#include <sys/types.h>
+
 #include <grp.h>
 #include <string.h>
 #include <unistd.h>
 
+extern int __getgroupmembership(const char *, gid_t, gid_t *, int, int *);
+
 int
 getgrouplist(const char *uname, gid_t agroup, gid_t *groups, int *grpcnt)
 {
-       struct group *grp;
-       int i, ngroups;
-       int ret, maxgroups;
-
-       ret = 0;
-       ngroups = 0;
-       maxgroups = *grpcnt;
-       /*
-        * When installing primary group, duplicate it;
-        * the first element of groups is the effective gid
-        * and will be overwritten when a setgid file is executed.
-        */
-       groups[ngroups++] = agroup;
-       if (maxgroups > 1)
-               groups[ngroups++] = agroup;
-       /*
-        * Scan the group file to find additional groups.
-        */
-       setgrent();
-       while ((grp = getgrent()) != NULL) {
-               for (i = 0; i < ngroups; i++) {
-                       if (grp->gr_gid == groups[i])
-                               goto skip;
-               }
-               for (i = 0; grp->gr_mem[i]; i++) {
-                       if (!strcmp(grp->gr_mem[i], uname)) {
-                               if (ngroups >= maxgroups) {
-                                       ret = -1;
-                                       break;
-                               }
-                               groups[ngroups++] = grp->gr_gid;
-                               break;
-                       }
-               }
-skip:
-               ;
-       }
-       endgrent();
-       *grpcnt = ngroups;
-       return (ret);
+       return __getgroupmembership(uname, agroup, groups, *grpcnt, grpcnt);
 }
index 170aee3..d892ea4 100644 (file)
@@ -9,10 +9,6 @@
 .\" 2. Redistributions in binary form must reproduce the above copyright
 .\"    notice, this list of conditions and the following disclaimer in the
 .\"    documentation and/or other materials provided with the distribution.
-.\" 3. All advertising materials mentioning features or use of this software
-.\"    must display the following acknowledgement:
-.\"    This product includes software developed by the University of
-.\"    California, Berkeley and its contributors.
 .\" 4. Neither the name of the University nor the names of its contributors
 .\"    may be used to endorse or promote products derived from this software
 .\"    without specific prior written permission.
 .\" SUCH DAMAGE.
 .\"
 .\"     From: @(#)getpwent.3   8.2 (Berkeley) 12/11/93
-.\" $FreeBSD: src/lib/libc/gen/getpwent.3,v 1.11.2.5 2002/02/01 15:51:16 ru Exp $
+.\" $FreeBSD: src/lib/libc/gen/getpwent.3,v 1.30 2007/01/09 00:27:54 imp Exp $
 .\" $DragonFly: src/lib/libc/gen/getpwent.3,v 1.5 2006/05/26 19:39:36 swildner Exp $
 .\"
-.Dd September 20, 1994
+.Dd April 16, 2003
 .Dt GETPWENT 3
 .Os
 .Sh NAME
 .Nm getpwent ,
+.Nm getpwent_r ,
 .Nm getpwnam ,
+.Nm getpwnam_r ,
 .Nm getpwuid ,
+.Nm getpwuid_r ,
 .Nm setpassent ,
 .Nm setpwent ,
 .Nm endpwent
 .In pwd.h
 .Ft struct passwd *
 .Fn getpwent void
+.Ft int
+.Fn getpwent_r "struct passwd *pwd" "char *buffer" "size_t bufsize" "struct passwd **result"
 .Ft struct passwd *
 .Fn getpwnam "const char *login"
+.Ft int
+.Fn getpwnam_r "const char *name" "struct passwd *pwd" "char *buffer" "size_t bufsize" "struct passwd **result"
 .Ft struct passwd *
 .Fn getpwuid "uid_t uid"
 .Ft int
-.Fn setpassent "int  stayopen"
+.Fn getpwuid_r "uid_t uid" "struct passwd *pwd" "char *buffer" "size_t bufsize" "struct passwd **result"
+.Ft int
+.Fn setpassent "int stayopen"
 .Ft void
 .Fn setpwent void
 .Ft void
@@ -68,7 +73,7 @@ which is described
 in
 .Xr passwd 5 .
 Each entry in the database is defined by the structure
-.Ar passwd
+.Vt passwd
 found in the include
 file
 .In pwd.h :
@@ -101,6 +106,35 @@ function
 sequentially reads the password database and is intended for programs
 that wish to process the complete list of users.
 .Pp
+The functions
+.Fn getpwent_r ,
+.Fn getpwnam_r ,
+and
+.Fn getpwuid_r
+are thread-safe versions of
+.Fn getpwent ,
+.Fn getpwnam ,
+and
+.Fn getpwuid ,
+respectively.
+The caller must provide storage for the results of the search in
+the
+.Fa pwd ,
+.Fa buffer ,
+.Fa bufsize ,
+and
+.Fa result
+arguments.
+When these functions are successful, the
+.Fa pwd
+argument will be filled-in, and a pointer to that argument will be
+stored in
+.Fa result .
+If an entry is not found or an error occurs,
+.Fa result
+will be set to
+.Dv NULL .
+.Pp
 The
 .Fn setpassent
 function
@@ -114,7 +148,7 @@ is non-zero, file descriptors are left open, significantly speeding
 up subsequent accesses for all of the routines.
 (This latter functionality is unnecessary for
 .Fn getpwent
-as it doesn't close its file descriptors by default.)
+as it does not close its file descriptors by default.)
 .Pp
 It is dangerous for long-running programs to keep the file descriptors
 open as the database will become out of date if it is updated while the
@@ -138,32 +172,36 @@ If the process which calls them has an effective uid of 0, the encrypted
 password will be returned, otherwise, the password field of the returned
 structure will point to the string
 .Ql * .
-.Sh YP/NIS INTERACTION
-When the
-.Xr yp 8
-password database is enabled, the
-.Fn getpwnam
-and
-.Fn getpwuid
-functions use the YP maps
-.Dq Li passwd.byname
-and
-.Dq Li passwd.byuid ,
-respectively, if the requested password entry is not found in the
-local database.  The
-.Fn getpwent
-function will step through the YP map
-.Dq Li passwd.byname
-if the entire map is enabled as described in
-.Xr passwd 5 .
 .Sh RETURN VALUES
 The functions
 .Fn getpwent ,
 .Fn getpwnam ,
 and
-.Fn getpwuid ,
+.Fn getpwuid
 return a valid pointer to a passwd structure on success
-and a null pointer if end-of-file is reached or an error occurs.
+or
+.Dv NULL
+if the entry is not found or if an error occurs.
+If an error does occur,
+.Va errno
+will be set.
+Note that programs must explicitly set
+.Va errno
+to zero before calling any of these functions if they need to
+distinguish between a non-existent entry and an error.
+The functions
+.Fn getpwent_r ,
+.Fn getpwnam_r ,
+and
+.Fn getpwuid_r
+return 0 if no error occurred, or an error number to indicate failure.
+It is not an error if a matching entry is not found.
+(Thus, if
+.Fa result
+is
+.Dv NULL
+and the return value is 0, no matching entry exists.)
+.Pp
 The
 .Fn setpassent
 function returns 0 on failure and 1 on success.
@@ -189,13 +227,43 @@ The historic function
 .Xr setpwfile 3 ,
 which allowed the specification of alternate password databases,
 has been deprecated and is no longer available.
+.Sh ERRORS
+These routines may fail for any of the errors specified in
+.Xr open 2 ,
+.Xr dbopen 3 ,
+.Xr socket 2 ,
+and
+.Xr connect 2 ,
+in addition to the following:
+.Bl -tag -width Er
+.It Bq Er ERANGE
+The buffer specified by the
+.Fa buffer
+and
+.Fa bufsize
+arguments was insufficiently sized to store the result.
+The caller should retry with a larger buffer.
+.El
 .Sh SEE ALSO
 .Xr getlogin 2 ,
 .Xr getgrent 3 ,
+.Xr nsswitch.conf 5 ,
 .Xr passwd 5 ,
 .Xr pwd_mkdb 8 ,
 .Xr vipw 8 ,
 .Xr yp 8
+.Sh STANDARDS
+The
+.Fn getpwent ,
+.Fn getpwnam ,
+.Fn getpwnam_r ,
+.Fn getpwuid ,
+.Fn getpwuid_r ,
+.Fn setpwent ,
+and
+.Fn endpwent
+functions conform to
+.St -p1003.1-96 .
 .Sh HISTORY
 The
 .Fn getpwent ,
@@ -210,6 +278,15 @@ The
 .Fn setpassent
 function appeared in
 .Bx 4.3 Reno .
+The
+.Fn getpwent_r ,
+.Fn getpwnam_r ,
+and
+.Fn getpwuid_r
+functions appeared in
+.Fx 5.1
+and
+.Dx 2.1 .
 .Sh BUGS
 The functions
 .Fn getpwent ,
@@ -221,3 +298,21 @@ a pointer to that object.
 Subsequent calls to
 the same function
 will modify the same object.
+.Pp
+The functions
+.Fn getpwent ,
+.Fn getpwent_r ,
+.Fn endpwent ,
+.Fn setpassent ,
+and
+.Fn setpwent
+are fairly useless in a networked environment and should be
+avoided, if possible.
+The
+.Fn getpwent
+and
+.Fn getpwent_r
+functions
+make no attempt to suppress duplicate information if multiple
+sources are specified in
+.Xr nsswitch.conf 5 .
index 507896a..40a9edf 100644 (file)
@@ -1,6 +1,12 @@
-/*
- * Copyright (c) 1988, 1993
- *     The Regents of the University of California.  All rights reserved.
+/*-
+ * Copyright (c) 2003 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by
+ * Jacques A. Vidrine, Safeport Network Services, and Network
+ * Associates Laboratories, the Security Research Division of Network
+ * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
  *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * @(#)getpwent.c      8.2 (Berkeley) 4/27/95
- * $FreeBSD: src/lib/libc/gen/getpwent.c,v 1.53.2.2 2001/03/05 09:52:13 obrien Exp $
+ * $FreeBSD: src/lib/libc/gen/getpwent.c,v 1.90 2006/04/28 12:03:35 ume Exp $
  * $DragonFly: src/lib/libc/gen/getpwent.c,v 1.7 2005/11/19 22:32:53 swildner Exp $
  */
 
 #include "namespace.h"
-#include <stdio.h>
 #include <sys/param.h>
+#ifdef YP
+#include <rpc/rpc.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+#endif
+#include <arpa/inet.h>
+#include <errno.h>
 #include <fcntl.h>
-#include <syslog.h>
+#ifdef HESIOD
+#include <hesiod.h>
+#endif
+#include <netdb.h>
+#include <nsswitch.h>
+#include <pthread.h>
+#include <pthread_np.h>
 #include <pwd.h>
-#include <utmp.h>
-#include <errno.h>
-#include <unistd.h>
 #include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
-#include <limits.h>
-#include <grp.h>
+#include <syslog.h>
+#include <unistd.h>
 #include "un-namespace.h"
-
 #include <db.h>
+#include "libc_private.h"
+#include "pw_scan.h"
+#include "nss_tls.h"
+#ifdef NS_CACHING
+#include "nscache.h"
+#endif
+
+#ifndef CTASSERT
+#define CTASSERT(x)            _CTASSERT(x, __LINE__)
+#define _CTASSERT(x, y)                __CTASSERT(x, y)
+#define __CTASSERT(x, y)       typedef char __assert_ ## y [(x) ? 1 : -1]
+#endif
 
-extern void setnetgrent ( char * );
-extern int getnetgrent ( char **, char **, char ** );
-extern int innetgr ( const char *, const char *, const char *, const char * );
+/* Counter as stored in /etc/pwd.db */
+typedef        int             pwkeynum;
+
+CTASSERT(MAXLOGNAME > sizeof(uid_t));
+CTASSERT(MAXLOGNAME > sizeof(pwkeynum));
+
+enum constants {
+       PWD_STORAGE_INITIAL     = 1 << 10, /* 1 KByte */
+       PWD_STORAGE_MAX         = 1 << 20, /* 1 MByte */
+       SETPWENT                = 1,
+       ENDPWENT                = 2,
+       HESIOD_NAME_MAX         = 256
+};
+
+static const ns_src defaultsrc[] = {
+       { NSSRC_COMPAT, NS_SUCCESS },
+       { NULL, 0 }
+};
+
+int    __pw_match_entry(const char *, size_t, enum nss_lookup_type,
+           const char *, uid_t);
+int    __pw_parse_entry(char *, size_t, struct passwd *, int, int *errnop);
+
+static void     pwd_init(struct passwd *);
+
+union key {
+       const char      *name;
+       uid_t            uid;
+};
+
+static struct passwd *getpw(int (*fn)(union key, struct passwd *, char *,
+                   size_t, struct passwd **), union key);
+static int      wrap_getpwnam_r(union key, struct passwd *, char *,
+                   size_t, struct passwd **);
+static int      wrap_getpwuid_r(union key, struct passwd *, char *, size_t,
+                   struct passwd **);
+static int      wrap_getpwent_r(union key, struct passwd *, char *, size_t,
+                   struct passwd **);
+
+static int      pwdb_match_entry_v3(char *, size_t, enum nss_lookup_type,
+                   const char *, uid_t);
+static int      pwdb_parse_entry_v3(char *, size_t, struct passwd *, int *);
+static int      pwdb_match_entry_v4(char *, size_t, enum nss_lookup_type,
+                   const char *, uid_t);
+static int      pwdb_parse_entry_v4(char *, size_t, struct passwd *, int *);
+
+
+struct {
+       int     (*match)(char *, size_t, enum nss_lookup_type, const char *,
+                   uid_t);
+       int     (*parse)(char *, size_t, struct passwd *, int *);
+} pwdb_versions[] = {
+       { NULL, NULL },                                 /* version 0 */
+       { NULL, NULL },                                 /* version 1 */
+       { NULL, NULL },                                 /* version 2 */
+       { pwdb_match_entry_v3, pwdb_parse_entry_v3 },   /* version 3 */
+       { pwdb_match_entry_v4, pwdb_parse_entry_v4 },   /* version 4 */
+};
+
+
+struct files_state {
+       DB              *db;
+       pwkeynum         keynum;
+       int              stayopen;
+       int              version;
+};
+static void    files_endstate(void *);
+NSS_TLS_HANDLING(files);
+static DB      *pwdbopen(int *);
+static void     files_endstate(void *);
+static int      files_setpwent(void *, void *, va_list);
+static int      files_passwd(void *, void *, va_list);
+
+
+#ifdef HESIOD
+struct dns_state {
+       long    counter;
+};
+static void    dns_endstate(void *);
+NSS_TLS_HANDLING(dns);
+static int      dns_setpwent(void *, void *, va_list);
+static int      dns_passwd(void *, void *, va_list);
+#endif
 
-/*
- * The lookup techniques and data extraction code here must be kept
- * in sync with that in `pwd_mkdb'.
- */
 
-static struct passwd _pw_passwd;       /* password structure */
-static DB *_pw_db;                     /* password database */
-static int _pw_keynum;                 /* key counter */
-static int _pw_stayopen;               /* keep fd's open */
 #ifdef YP
-#include <rpc/rpc.h>
-#include <rpcsvc/yp_prot.h>
-#include <rpcsvc/ypclnt.h>
+struct nis_state {
+       char     domain[MAXHOSTNAMELEN];
+       int      done;
+       char    *key;
+       int      keylen;
+};
+static void     nis_endstate(void *);
+NSS_TLS_HANDLING(nis);
+static int      nis_setpwent(void *, void *, va_list);
+static int      nis_passwd(void *, void *, va_list);
+static int      nis_map(char *, enum nss_lookup_type, char *, size_t, int *);
+static int      nis_adjunct(char *, const char *, char *, size_t);
+#endif
 
-static struct passwd _pw_copy;
-static DBT empty = { NULL, 0 };
-static DB *_ypcache = (DB *)NULL;
-static int _yp_exclusions = 0;
-static int _yp_enabled = -1;
-static int _pw_stepping_yp;            /* set true when stepping thru map */
-static char _ypnam[YPMAXRECORD];
-#define YP_HAVE_MASTER 2
-#define YP_HAVE_ADJUNCT 1
-#define YP_HAVE_NONE 0
-static int _gotmaster;
-static char *_pw_yp_domain;
-static inline int unwind ( char * );
-static void _ypinitdb ( void );
-static int _havemaster (char *);
-static int _getyppass (struct passwd *, const char *, const char * );
-static int _nextyppass (struct passwd *);
-static inline int lookup (const char *);
-static inline void store (const char *);
-static inline int ingr (const char *, const char*);
-static inline int verf (const char *);
-static char * _get_adjunct_pw (const char *);
-#endif
-static int __hashpw(DBT *);
-static int __initdb(void);
 
+struct compat_state {
+       DB              *db;
+       pwkeynum         keynum;
+       int              stayopen;
+       int              version;
+       DB              *exclude;
+       struct passwd    template;
+       char            *name;
+       enum _compat {
+               COMPAT_MODE_OFF = 0,
+               COMPAT_MODE_ALL,
+               COMPAT_MODE_NAME,
+               COMPAT_MODE_NETGROUP
+       }                compat;
+};
+static void     compat_endstate(void *);
+NSS_TLS_HANDLING(compat);
+static int      compat_setpwent(void *, void *, va_list);
+static int      compat_passwd(void *, void *, va_list);
+static void     compat_clear_template(struct passwd *);
+static int      compat_set_template(struct passwd *, struct passwd *);
+static int      compat_use_template(struct passwd *, struct passwd *, char *,
+                   size_t);
+static int      compat_redispatch(struct compat_state *, enum nss_lookup_type,
+                   enum nss_lookup_type, const char *, const char *, uid_t,
+                   struct passwd *, char *, size_t, int *);
+
+#ifdef NS_CACHING
+static int      pwd_id_func(char *, size_t *, va_list ap, void *);
+static int      pwd_marshal_func(char *, size_t *, void *, va_list, void *);
+static int      pwd_unmarshal_func(char *, size_t, void *, va_list, void *);
 
-/*
- * Parse the + entries in the password database and do appropriate
- * NIS lookups. While ugly to look at, this is optimized to do only
- * as many lookups as are absolutely necessary in any given case.
- * Basically, the getpwent() function will feed us + and - lines
- * as they appear in the database. For + lines, we do netgroup/group
- * and user lookups to find all usernames that match the rule and
- * extract them from the NIS passwd maps. For - lines, we save the
- * matching names in a database and a) exlude them, and b) make sure
- * we don't consider them when processing other + lines that appear
- * later.
- */
-static inline int
-unwind(char *grp)
+static int
+pwd_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata)
 {
-       char *user, *host, *domain;
-       static int latch = 0;
-       static struct group *gr = NULL;
-       int rv = 0;
-
-       if (grp[0] == '+') {
-               if (strlen(grp) == 1) {
-                       return(_nextyppass(&_pw_passwd));
+       char    *name;
+       uid_t   uid;
+       size_t  size, desired_size;
+       int     res = NS_UNAVAIL;
+       enum nss_lookup_type lookup_type;
+
+       lookup_type = (enum nss_lookup_type)cache_mdata;
+       switch (lookup_type) {
+       case nss_lt_name:
+               name = va_arg(ap, char *);
+               size = strlen(name);
+               desired_size = sizeof(enum nss_lookup_type) + size + 1;
+               if (desired_size > *buffer_size) {
+                       res = NS_RETURN;
+                       goto fin;
                }
-               if (grp[1] == '@') {
-                       _pw_stepping_yp = 1;
-grpagain:
-                       if (gr != NULL) {
-                               if (*gr->gr_mem != NULL) {
-                                       if (lookup(*gr->gr_mem)) {
-                                               gr->gr_mem++;
-                                               goto grpagain;
-                                       }
-                                       rv = _getyppass(&_pw_passwd,
-                                                       *gr->gr_mem,
-                                                       "passwd.byname");
-                                       gr->gr_mem++;
-                                       return(rv);
-                               } else {
-                                       latch = 0;
-                                       _pw_stepping_yp = 0;
-                                       gr = NULL;
-                                       return(0);
-                               }
-                       }
-                       if (!latch) {
-                               setnetgrent(grp+2);
-                               latch++;
-                       }
-again:
-                       if (getnetgrent(&host, &user, &domain) == 0) {
-                               if ((gr = getgrnam(grp+2)) != NULL)
-                                       goto grpagain;
-                               latch = 0;
-                               _pw_stepping_yp = 0;
-                               return(0);
-                       } else {
-                               if (lookup(user))
-                                       goto again;
-                               if (_getyppass(&_pw_passwd, user,
-                                                       "passwd.byname"))
-                                       return(1);
-                               else
-                                       goto again;
-                       }
-               } else {
-                       if (lookup(grp+1))
-                               return(0);
-                       return(_getyppass(&_pw_passwd, grp+1, "passwd.byname"));
-               }
-       } else {
-               if (grp[1] == '@') {
-                       setnetgrent(grp+2);
-                       rv = 0;
-                       while(getnetgrent(&host, &user, &domain) != 0) {
-                               store(user);
-                               rv++;
-                       }
-                       if (!rv && (gr = getgrnam(grp+2)) != NULL) {
-                               while(*gr->gr_mem) {
-                                       store(*gr->gr_mem);
-                                       gr->gr_mem++;
-                               }
-                       }
-               } else {
-                       store(grp+1);
+
+               memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
+               memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1);
+
+               res = NS_SUCCESS;
+               break;
+       case nss_lt_id:
+               uid = va_arg(ap, uid_t);
+               desired_size = sizeof(enum nss_lookup_type) + sizeof(uid_t);
+               if (desired_size > *buffer_size) {
+                       res = NS_RETURN;
+                       goto fin;
                }
+
+               memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
+               memcpy(buffer + sizeof(enum nss_lookup_type), &uid,
+                   sizeof(uid_t));
+
+               res = NS_SUCCESS;
+               break;
+       default:
+               /* should be unreachable */
+               return (NS_UNAVAIL);
        }
-       return(0);
+
+fin:
+       *buffer_size = desired_size;
+       return (res);
 }
 
-struct passwd *
-getpwent(void)
+static int
+pwd_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap,
+    void *cache_mdata)
 {
-       DBT key;
-       char bf[sizeof(_pw_keynum) + 1];
-       int rv;
+       char *name;
+       uid_t uid;
+       struct passwd *pwd;
+       char *orig_buf;
+       size_t orig_buf_size;
+
+       struct passwd new_pwd;
+       size_t desired_size, size;
+       char *p;
 
-       if (!_pw_db && !__initdb())
-               return((struct passwd *)NULL);
+       switch ((enum nss_lookup_type)cache_mdata) {
+       case nss_lt_name:
+               name = va_arg(ap, char *);
+               break;
+       case nss_lt_id:
+               uid = va_arg(ap, uid_t);
+               break;
+       case nss_lt_all:
+               break;
+       default:
+               /* should be unreachable */
+               return (NS_UNAVAIL);
+       }
 
-#ifdef YP
-       if(_pw_stepping_yp) {
-               _pw_passwd = _pw_copy;
-               if (unwind((char *)&_ypnam))
-                       return(&_pw_passwd);
+       pwd = va_arg(ap, struct passwd *);
+       orig_buf = va_arg(ap, char *);
+       orig_buf_size = va_arg(ap, size_t);
+
+       desired_size = sizeof(struct passwd) + sizeof(char *) +
+           strlen(pwd->pw_name) + 1;
+       if (pwd->pw_passwd != NULL)
+               desired_size += strlen(pwd->pw_passwd) + 1;
+       if (pwd->pw_class != NULL)
+               desired_size += strlen(pwd->pw_class) + 1;
+       if (pwd->pw_gecos != NULL)
+               desired_size += strlen(pwd->pw_gecos) + 1;
+       if (pwd->pw_dir != NULL)
+               desired_size += strlen(pwd->pw_dir) + 1;
+       if (pwd->pw_shell != NULL)
+               desired_size += strlen(pwd->pw_shell) + 1;
+
+       if (*buffer_size < desired_size) {
+               /* this assignment is here for future use */
+               *buffer_size = desired_size;
+               return (NS_RETURN);
        }
-#endif
-tryagain:
 
-       ++_pw_keynum;
-       bf[0] = _PW_KEYBYNUM;
-       bcopy((char *)&_pw_keynum, bf + 1, sizeof(_pw_keynum));
-       key.data = (u_char *)bf;
-       key.size = sizeof(_pw_keynum) + 1;
-       rv = __hashpw(&key);
-       if(!rv) return (struct passwd *)NULL;
-#ifdef YP
-       if(_pw_passwd.pw_name[0] == '+' || _pw_passwd.pw_name[0] == '-') {
-               if (_yp_enabled == -1)
-                       _ypinitdb();
-               bzero((char *)&_ypnam, sizeof(_ypnam));
-               bcopy(_pw_passwd.pw_name, _ypnam,
-                       strlen(_pw_passwd.pw_name));
-               _pw_copy = _pw_passwd;
-               if (unwind((char *)&_ypnam) == 0)
-                       goto tryagain;
-               else
-                       return(&_pw_passwd);
+       memcpy(&new_pwd, pwd, sizeof(struct passwd));
+       memset(buffer, 0, desired_size);
+
+       *buffer_size = desired_size;
+       p = buffer + sizeof(struct passwd) + sizeof(char *);
+       memcpy(buffer + sizeof(struct passwd), &p, sizeof(char *));
+
+       if (new_pwd.pw_name != NULL) {
+               size = strlen(new_pwd.pw_name);
+               memcpy(p, new_pwd.pw_name, size);
+               new_pwd.pw_name = p;
+               p += size + 1;
        }
-#else
-       /* Ignore YP password file entries when YP is disabled. */
-       if(_pw_passwd.pw_name[0] == '+' || _pw_passwd.pw_name[0] == '-') {
-               goto tryagain;
+
+       if (new_pwd.pw_passwd != NULL) {
+               size = strlen(new_pwd.pw_passwd);
+               memcpy(p, new_pwd.pw_passwd, size);
+               new_pwd.pw_passwd = p;
+               p += size + 1;
        }
-#endif
-       return(&_pw_passwd);
-}
 
-struct passwd *
-getpwnam(const char *name)
-{
-       DBT key;
-       int len, rval;
-       char bf[UT_NAMESIZE + 2];
+       if (new_pwd.pw_class != NULL) {
+               size = strlen(new_pwd.pw_class);
+               memcpy(p, new_pwd.pw_class, size);
+               new_pwd.pw_class = p;
+               p += size + 1;
+       }
 
-       if (!_pw_db && !__initdb())
-               return((struct passwd *)NULL);
+       if (new_pwd.pw_gecos != NULL) {
+               size = strlen(new_pwd.pw_gecos);
+               memcpy(p, new_pwd.pw_gecos, size);
+               new_pwd.pw_gecos = p;
+               p += size + 1;
+       }
 
-       bf[0] = _PW_KEYBYNAME;
-       len = strlen(name);
-       if (len > UT_NAMESIZE)
-               return(NULL);
-       bcopy(name, bf + 1, len);
-       key.data = (u_char *)bf;
-       key.size = len + 1;
-       rval = __hashpw(&key);
+       if (new_pwd.pw_dir != NULL) {
+               size = strlen(new_pwd.pw_dir);
+               memcpy(p, new_pwd.pw_dir, size);
+               new_pwd.pw_dir = p;
+               p += size + 1;
+       }
 
-#ifdef YP
-       if (!rval) {
-               if (_yp_enabled == -1)
-                       _ypinitdb();
-               if (_yp_enabled)
-                       rval = _getyppass(&_pw_passwd, name, "passwd.byname");
+       if (new_pwd.pw_shell != NULL) {
+               size = strlen(new_pwd.pw_shell);
+               memcpy(p, new_pwd.pw_shell, size);
+               new_pwd.pw_shell = p;
+               p += size + 1;
        }
-#endif
-       /*
-        * Prevent login attempts when YP is not enabled but YP entries
-        * are in /etc/master.passwd.
-        */
-       if (rval && (_pw_passwd.pw_name[0] == '+'||
-                       _pw_passwd.pw_name[0] == '-')) rval = 0;
 
-       if (!_pw_stayopen)
-               endpwent();
-       return(rval ? &_pw_passwd : (struct passwd *)NULL);
+       memcpy(buffer, &new_pwd, sizeof(struct passwd));
+       return (NS_SUCCESS);
 }
 
-struct passwd *
-getpwuid(uid_t uid)
+static int
+pwd_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
+    void *cache_mdata)
 {
-       DBT key;
-       int keyuid, rval;
-       char bf[sizeof(keyuid) + 1];
+       char *name;
+       uid_t uid;
+       struct passwd *pwd;
+       char *orig_buf;
+       size_t orig_buf_size;
+       int *ret_errno;
 
-       if (!_pw_db && !__initdb())
-               return((struct passwd *)NULL);
+       char *p;
 
-       bf[0] = _PW_KEYBYUID;
-       keyuid = uid;
-       bcopy(&keyuid, bf + 1, sizeof(keyuid));
-       key.data = (u_char *)bf;
-       key.size = sizeof(keyuid) + 1;
-       rval = __hashpw(&key);
+       switch ((enum nss_lookup_type)cache_mdata) {
+       case nss_lt_name:
+               name = va_arg(ap, char *);
+               break;
+       case nss_lt_id:
+               uid = va_arg(ap, uid_t);
+               break;
+       case nss_lt_all:
+               break;
+       default:
+               /* should be unreachable */
+               return (NS_UNAVAIL);
+       }
 
-#ifdef YP
-       if (!rval) {
-               if (_yp_enabled == -1)
-                       _ypinitdb();
-               if (_yp_enabled) {
-                       char ypbuf[16]; /* big enough for 32-bit uids */
-                       snprintf(ypbuf, sizeof ypbuf, "%u", (unsigned)uid);
-                       rval = _getyppass(&_pw_passwd, ypbuf, "passwd.byuid");
-               }
+       pwd = va_arg(ap, struct passwd *);
+       orig_buf = va_arg(ap, char *);
+       orig_buf_size = va_arg(ap, size_t);
+       ret_errno = va_arg(ap, int *);
+
+       if (orig_buf_size <
+           buffer_size - sizeof(struct passwd) - sizeof(char *)) {
+               *ret_errno = ERANGE;
+               return (NS_RETURN);
        }
+
+       memcpy(pwd, buffer, sizeof(struct passwd));
+       memcpy(&p, buffer + sizeof(struct passwd), sizeof(char *));
+       memcpy(orig_buf, buffer + sizeof(struct passwd) + sizeof(char *),
+           buffer_size - sizeof(struct passwd) - sizeof(char *));
+
+       NS_APPLY_OFFSET(pwd->pw_name, orig_buf, p, char *);
+       NS_APPLY_OFFSET(pwd->pw_passwd, orig_buf, p, char *);
+       NS_APPLY_OFFSET(pwd->pw_class, orig_buf, p, char *);
+       NS_APPLY_OFFSET(pwd->pw_gecos, orig_buf, p, char *);
+       NS_APPLY_OFFSET(pwd->pw_dir, orig_buf, p, char *);
+       NS_APPLY_OFFSET(pwd->pw_shell, orig_buf, p, char *);
+
+       if (retval != NULL)
+               *((struct passwd **)retval) = pwd;
+
+       return (NS_SUCCESS);
+}
+
+NSS_MP_CACHE_HANDLING(passwd);
+#endif /* NS_CACHING */
+
+void
+setpwent(void)
+{
+#ifdef NS_CACHING
+       static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+               passwd, (void *)nss_lt_all,
+               NULL, NULL);
 #endif
-       /*
-        * Prevent login attempts when YP is not enabled but YP entries
-        * are in /etc/master.passwd.
-        */
-       if (rval && (_pw_passwd.pw_name[0] == '+'||
-                       _pw_passwd.pw_name[0] == '-')) rval = 0;
 
-       if (!_pw_stayopen)
-               endpwent();
-       return(rval ? &_pw_passwd : (struct passwd *)NULL);
+       static const ns_dtab dtab[] = {
+               { NSSRC_FILES, files_setpwent, (void *)SETPWENT },
+#ifdef HESIOD
+               { NSSRC_DNS, dns_setpwent, (void *)SETPWENT },
+#endif
+#ifdef YP
+               { NSSRC_NIS, nis_setpwent, (void *)SETPWENT },
+#endif
+               { NSSRC_COMPAT, compat_setpwent, (void *)SETPWENT },
+#ifdef NS_CACHING
+               NS_CACHE_CB(&cache_info)
+#endif
+               { NULL, NULL, NULL }
+       };
+       _nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc, 0);
 }
 
+
 int
 setpassent(int stayopen)
 {
-       _pw_keynum = 0;
+#ifdef NS_CACHING
+       static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+               passwd, (void *)nss_lt_all,
+               NULL, NULL);
+#endif
+
+       static const ns_dtab dtab[] = {
+               { NSSRC_FILES, files_setpwent, (void *)SETPWENT },
+#ifdef HESIOD
+               { NSSRC_DNS, dns_setpwent, (void *)SETPWENT },
+#endif
 #ifdef YP
-       _pw_stepping_yp = 0;
-       if (stayopen)
-               setgroupent(1);
+               { NSSRC_NIS, nis_setpwent, (void *)SETPWENT },
+#endif
+               { NSSRC_COMPAT, compat_setpwent, (void *)SETPWENT },
+#ifdef NS_CACHING
+               NS_CACHE_CB(&cache_info)
 #endif
-       _pw_stayopen = stayopen;
-       return(1);
+               { NULL, NULL, NULL }
+       };
+       _nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc, stayopen);
+       return (1);
 }
 
-void
-setpwent(void)
-{
-       setpassent(0);
-}
 
 void
 endpwent(void)
 {
-       _pw_keynum = 0;
-#ifdef YP
-       _pw_stepping_yp = 0;
+#ifdef NS_CACHING
+       static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+               passwd, (void *)nss_lt_all,
+               NULL, NULL);
+#endif
+
+       static const ns_dtab dtab[] = {
+               { NSSRC_FILES, files_setpwent, (void *)ENDPWENT },
+#ifdef HESIOD
+               { NSSRC_DNS, dns_setpwent, (void *)ENDPWENT },
 #endif
-       if (_pw_db) {
-               (_pw_db->close)(_pw_db);
-               _pw_db = (DB *)NULL;
-       }
 #ifdef YP
-       if (_ypcache) {
-               (_ypcache->close)(_ypcache);
-               _ypcache = (DB *)NULL;
-               _yp_exclusions = 0;
-       }
-       /* Fix for PR #12008 */
-       _yp_enabled = -1;
+               { NSSRC_NIS, nis_setpwent, (void *)ENDPWENT },
+#endif
+               { NSSRC_COMPAT, compat_setpwent, (void *)ENDPWENT },
+#ifdef NS_CACHING
+               NS_CACHE_CB(&cache_info)
 #endif
+               { NULL, NULL, NULL }
+       };
+       _nsdispatch(NULL, dtab, NSDB_PASSWD, "endpwent", defaultsrc);
 }
 
-static int
-__initdb(void)
+
+int
+getpwent_r(struct passwd *pwd, char *buffer, size_t bufsize,
+    struct passwd **result)
 {
-       static int warned;
-       char *p;
+#ifdef NS_CACHING
+       static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+               passwd, (void *)nss_lt_all,
+               pwd_marshal_func, pwd_unmarshal_func);
+#endif
 
-       p = (geteuid()) ? _PATH_MP_DB : _PATH_SMP_DB;
-       _pw_db = dbopen(p, O_RDONLY, 0, DB_HASH, NULL);
-       if (_pw_db)
-               return(1);
-       if (!warned++)
-               syslog(LOG_ERR, "%s: %m", p);
-       return(0);
+       static const ns_dtab dtab[] = {
+               { NSSRC_FILES, files_passwd, (void *)nss_lt_all },
+#ifdef HESIOD
+               { NSSRC_DNS, dns_passwd, (void *)nss_lt_all },
+#endif
+#ifdef YP
+               { NSSRC_NIS, nis_passwd, (void *)nss_lt_all },
+#endif
+               { NSSRC_COMPAT, compat_passwd, (void *)nss_lt_all },
+#ifdef NS_CACHING
+               NS_CACHE_CB(&cache_info)
+#endif
+               { NULL, NULL, NULL }
+       };
+       int     rv, ret_errno;
+
+       pwd_init(pwd);
+       ret_errno = 0;
+       *result = NULL;
+       rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwent_r", defaultsrc,
+           pwd, buffer, bufsize, &ret_errno);
+       if (rv == NS_SUCCESS)
+               return (0);
+       else
+               return (ret_errno);
 }
 
-static int
-__hashpw(DBT *key)
+
+int
+getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t bufsize,
+    struct passwd **result)
 {
-       char *p, *t;
-       static u_int max;
-       static char *line;
-       DBT data;
+#ifdef NS_CACHING
+       static const nss_cache_info cache_info =
+               NS_COMMON_CACHE_INFO_INITIALIZER(
+               passwd, (void *)nss_lt_name,
+               pwd_id_func, pwd_marshal_func, pwd_unmarshal_func);
+#endif
+
+       static const ns_dtab dtab[] = {
+               { NSSRC_FILES, files_passwd, (void *)nss_lt_name },
+#ifdef HESIOD
+               { NSSRC_DNS, dns_passwd, (void *)nss_lt_name },
+#endif
+#ifdef YP
+               { NSSRC_NIS, nis_passwd, (void *)nss_lt_name },
+#endif
+               { NSSRC_COMPAT, compat_passwd, (void *)nss_lt_name },
+#ifdef NS_CACHING
+               NS_CACHE_CB(&cache_info)
+#endif
+               { NULL, NULL, NULL }
+       };
+       int     rv, ret_errno;
+
+       pwd_init(pwd);
+       ret_errno = 0;
+       *result = NULL;
+       rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwnam_r", defaultsrc,
+           name, pwd, buffer, bufsize, &ret_errno);
+       if (rv == NS_SUCCESS)
+               return (0);
+       else
+               return (ret_errno);
+}
 
-       if ((_pw_db->get)(_pw_db, key, &data, 0))
-               return(0);
-       p = (char *)data.data;
 
-       /* Increase buffer size for long lines if necessary. */
-       if (data.size > max) {
-               max = data.size + 1024;
-               if (!(line = reallocf(line, max)))
-                       return(0);
-       }
+int
+getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize,
+    struct passwd **result)
+{
+#ifdef NS_CACHING
+       static const nss_cache_info cache_info =
+               NS_COMMON_CACHE_INFO_INITIALIZER(
+               passwd, (void *)nss_lt_id,
+               pwd_id_func, pwd_marshal_func, pwd_unmarshal_func);
+#endif
 
-       /* THIS CODE MUST MATCH THAT IN pwd_mkdb. */
-       t = line;
-#define        EXPAND(e)       e = t; while ( (*t++ = *p++) );
-#define        SCALAR(v)       memmove(&(v), p, sizeof v); p += sizeof v
-       EXPAND(_pw_passwd.pw_name);
-       EXPAND(_pw_passwd.pw_passwd);
-       SCALAR(_pw_passwd.pw_uid);
-       SCALAR(_pw_passwd.pw_gid);
-       SCALAR(_pw_passwd.pw_change);
-       EXPAND(_pw_passwd.pw_class);
-       EXPAND(_pw_passwd.pw_gecos);
-       EXPAND(_pw_passwd.pw_dir);
-       EXPAND(_pw_passwd.pw_shell);
-       SCALAR(_pw_passwd.pw_expire);
-       bcopy(p, (char *)&_pw_passwd.pw_fields, sizeof _pw_passwd.pw_fields);
-       p += sizeof _pw_passwd.pw_fields;
-       return(1);
+       static const ns_dtab dtab[] = {
+               { NSSRC_FILES, files_passwd, (void *)nss_lt_id },
+#ifdef HESIOD
+               { NSSRC_DNS, dns_passwd, (void *)nss_lt_id },
+#endif
+#ifdef YP
+               { NSSRC_NIS, nis_passwd, (void *)nss_lt_id },
+#endif
+               { NSSRC_COMPAT, compat_passwd, (void *)nss_lt_id },
+#ifdef NS_CACHING
+               NS_CACHE_CB(&cache_info)
+#endif
+               { NULL, NULL, NULL }
+       };
+       int     rv, ret_errno;
+
+       pwd_init(pwd);
+       ret_errno = 0;
+       *result = NULL;
+       rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwuid_r", defaultsrc,
+           uid, pwd, buffer, bufsize, &ret_errno);
+       if (rv == NS_SUCCESS)
+               return (0);
+       else
+               return (ret_errno);
 }
 
-#ifdef YP
 
 static void
-_ypinitdb(void)
-{
-       DBT key, data;
-       char buf[] = { _PW_KEYYPENABLED };
-       key.data = buf;
-       key.size = 1;
-       _yp_enabled = 0;
-       if ((_pw_db->get)(_pw_db, &key, &data, 0) == 0) {
-               _yp_enabled = (int)*((char *)data.data) - 2;
-               /* Don't even bother with this if we aren't root. */
-               if (!geteuid()) {
-                       if (!_pw_yp_domain)
-                               if (yp_get_default_domain(&_pw_yp_domain))
-                                       return;
-                       _gotmaster = _havemaster(_pw_yp_domain);
-               } else _gotmaster = YP_HAVE_NONE;
-               /*
-                * Create a DB hash database in memory. Bet you didn't know you
-                * could do a dbopen() with a NULL filename, did you.
-                */
-               if (_ypcache == (DB *)NULL)
-                       _ypcache = dbopen(NULL, O_RDWR, 600, DB_HASH, NULL);
+pwd_init(struct passwd *pwd)
+{
+       static char nul[] = "";
+
+       memset(pwd, 0, sizeof(*pwd));
+       pwd->pw_uid = (uid_t)-1;  /* Considered least likely to lead to */
+       pwd->pw_gid = (gid_t)-1;  /* a security issue.                  */
+       pwd->pw_name = nul;
+       pwd->pw_passwd = nul;
+       pwd->pw_class = nul;
+       pwd->pw_gecos = nul;
+       pwd->pw_dir = nul;
+       pwd->pw_shell = nul;
+}
+
+
+static struct passwd    pwd;
+static char            *pwd_storage;
+static size_t           pwd_storage_size;
+
+
+static struct passwd *
+getpw(int (*fn)(union key, struct passwd *, char *, size_t, struct passwd **),
+    union key key)
+{
+       int              rv;
+       struct passwd   *res;
+
+       if (pwd_storage == NULL) {
+               pwd_storage = malloc(PWD_STORAGE_INITIAL);
+               if (pwd_storage == NULL)
+                       return (NULL);
+               pwd_storage_size = PWD_STORAGE_INITIAL;
        }
+       do {
+               rv = fn(key, &pwd, pwd_storage, pwd_storage_size, &res);
+               if (res == NULL && rv == ERANGE) {
+                       free(pwd_storage);
+                       if ((pwd_storage_size << 1) > PWD_STORAGE_MAX) {
+                               pwd_storage = NULL;
+                               errno = ERANGE;
+                               return (NULL);
+                       }
+                       pwd_storage_size <<= 1;
+                       pwd_storage = malloc(pwd_storage_size);
+                       if (pwd_storage == NULL)
+                               return (NULL);
+               }
+       } while (res == NULL && rv == ERANGE);
+       if (rv != 0)
+               errno = rv;
+       return (res);
 }
 
-/*
- * See if a user is in the blackballed list.
- */
-static inline int
-lookup(const char *name)
+
+static int
+wrap_getpwnam_r(union key key, struct passwd *pwd, char *buffer,
+    size_t bufsize, struct passwd **res)
 {
-       DBT key;
+       return (getpwnam_r(key.name, pwd, buffer, bufsize, res));
+}
 
-       if (!_yp_exclusions)
-               return(0);
 
-       key.data = (char *)name;
-       key.size = strlen(name);
+static int
+wrap_getpwuid_r(union key key, struct passwd *pwd, char *buffer,
+    size_t bufsize, struct passwd **res)
+{
+       return (getpwuid_r(key.uid, pwd, buffer, bufsize, res));
+}
 
-       if ((_ypcache->get)(_ypcache, &key, &empty, 0)) {
-               return(0);
-       }
 
-       return(1);
+static int
+wrap_getpwent_r(union key key __unused, struct passwd *pwd, char *buffer,
+    size_t bufsize, struct passwd **res)
+{
+       return (getpwent_r(pwd, buffer, bufsize, res));
 }
 
-/*
- * Store a blackballed user in an in-core hash database.
- */
-static inline void
-store(const char *key)
+
+struct passwd *
+getpwnam(const char *name)
 {
-       DBT lkey;
-/*
-       if (lookup(key))
-               return;
-*/
+       union key key;
+
+       key.name = name;
+       return (getpw(wrap_getpwnam_r, key));
+}
+
+
+struct passwd *
+getpwuid(uid_t uid)
+{
+       union key key;
 
-       _yp_exclusions = 1;
+       key.uid = uid;
+       return (getpw(wrap_getpwuid_r, key));
+}
 
-       lkey.data = (char *)key;
-       lkey.size = strlen(key);
 
-       (_ypcache->put)(_ypcache, &lkey, &empty, R_NOOVERWRITE);
+struct passwd *
+getpwent(void)
+{
+       union key key;
+
+       key.uid = 0; /* not used */
+       return (getpw(wrap_getpwent_r, key));
 }
 
+
 /*
- * See if a user is a member of a particular group.
+ * files backend
  */
-static inline int
-ingr(const char *grp, const char *name)
+static DB *
+pwdbopen(int *version)
 {
-       struct group *gr;
+       DB      *res;
+       DBT      key, entry;
+       int      rv;
+
+       if (geteuid() != 0 ||
+           (res = dbopen(_PATH_SMP_DB, O_RDONLY, 0, DB_HASH, NULL)) == NULL)
+               res = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL);
+       if (res == NULL)
+               return (NULL);
+       key.data = _PWD_VERSION_KEY;
+       key.size = strlen(_PWD_VERSION_KEY);
+       rv = res->get(res, &key, &entry, 0);
+       if (rv == 0)
+               *version = *(unsigned char *)entry.data;
+       else
+               *version = 3;
+       if (*version < 3 ||
+           *version >= sizeof(pwdb_versions)/sizeof(pwdb_versions[0])) {
+               syslog(LOG_CRIT, "Unsupported password database version %d",
+                   *version);
+               res->close(res);
+               res = NULL;
+       }
+       return (res);
+}
 
-       if ((gr = getgrnam(grp)) == NULL)
-               return(0);
 
-       while(*gr->gr_mem) {
-               if (!strcmp(*gr->gr_mem, name))
-                       return(1);
-               gr->gr_mem++;
-       }
+static void
+files_endstate(void *p)
+{
+       DB      *db;
 
-       return(0);
+       if (p == NULL)
+               return;
+       db = ((struct files_state *)p)->db;
+       if (db != NULL)
+               db->close(db);
+       free(p);
 }
 
-/*
- * Check a user against the +@netgroup/-@netgroup lines listed in
- * the local password database. Also checks +user/-user lines.
- * If no netgroup exists that matches +@netgroup/-@netgroup,
- * try searching regular groups with the same name.
- */
-static inline int
-verf(const char *name)
-{
-       DBT key;
-       char bf[sizeof(_pw_keynum) + 1];
-       int keynum = 0;
-
-again:
-       ++keynum;
-       bf[0] = _PW_KEYYPBYNUM;
-       bcopy((char *)&keynum, bf + 1, sizeof(keynum));
-       key.data = (u_char *)bf;
-       key.size = sizeof(keynum) + 1;
-       if (!__hashpw(&key)) {
-               /* Try again using old format */
-               bf[0] = _PW_KEYBYNUM;
-               bcopy((char *)&keynum, bf + 1, sizeof(keynum));
-               key.data = (u_char *)bf;
-               if (!__hashpw(&key))
-                       return(0);
-       }
-       if (_pw_passwd.pw_name[0] != '+' && (_pw_passwd.pw_name[0] != '-'))
-               goto again;
-       if (_pw_passwd.pw_name[0] == '+') {
-               if (strlen(_pw_passwd.pw_name) == 1) /* Wildcard */
-                       return(1);
-               if (_pw_passwd.pw_name[1] == '@') {
-                       if ((innetgr(_pw_passwd.pw_name+2, NULL, name,
-                                                       _pw_yp_domain) ||
-                           ingr(_pw_passwd.pw_name+2, name)) && !lookup(name))
-                               return(1);
-                       else
-                               goto again;
-               } else {
-                       if (!strcmp(name, _pw_passwd.pw_name+1) &&
-                                                               !lookup(name))
-                               return(1);
-                       else
-                               goto again;
+
+static int
+files_setpwent(void *retval, void *mdata, va_list ap)
+{
+       struct files_state      *st;
+       int                      rv, stayopen;
+
+       rv = files_getstate(&st);
+       if (rv != 0)
+               return (NS_UNAVAIL);
+       switch ((enum constants)mdata) {
+       case SETPWENT:
+               stayopen = va_arg(ap, int);
+               st->keynum = 0;
+               if (stayopen)
+                       st->db = pwdbopen(&st->version);
+               st->stayopen = stayopen;
+               break;
+       case ENDPWENT:
+               if (st->db != NULL) {
+                       st->db->close(st->db);
+                       st->db = NULL;
                }
+               break;
+       default:
+               break;
        }
-       if (_pw_passwd.pw_name[0] == '-') {
-               /* Note that a minus wildcard is a no-op. */
-               if (_pw_passwd.pw_name[1] == '@') {
-                       if (innetgr(_pw_passwd.pw_name+2, NULL, name,
-                                                       _pw_yp_domain) ||
-                           ingr(_pw_passwd.pw_name+2, name)) {
-                               store(name);
-                               return(0);
-                       } else
-                               goto again;
-               } else {
-                       if (!strcmp(name, _pw_passwd.pw_name+1)) {
-                               store(name);
-                               return(0);
-                       } else
-                               goto again;
+       return (NS_UNAVAIL);
+}
+
+
+static int
+files_passwd(void *retval, void *mdata, va_list ap)
+{
+       char                     keybuf[MAXLOGNAME + 1];
+       DBT                      key, entry;
+       struct files_state      *st;
+       enum nss_lookup_type     how;
+       const char              *name;
+       struct passwd           *pwd;
+       char                    *buffer;
+       size_t                   bufsize, namesize;
+       uid_t                    uid;
+       uint32_t                 store;
+       int                      rv, stayopen, *errnop;
+
+       name = NULL;
+       uid = (uid_t)-1;
+       how = (enum nss_lookup_type)mdata;
+       switch (how) {
+       case nss_lt_name:
+               name = va_arg(ap, const char *);
+               keybuf[0] = _PW_KEYBYNAME;
+               break;
+       case nss_lt_id:
+               uid = va_arg(ap, uid_t);
+               keybuf[0] = _PW_KEYBYUID;
+               break;
+       case nss_lt_all:
+               keybuf[0] = _PW_KEYBYNUM;
+               break;
+       default:
+               rv = NS_NOTFOUND;
+               goto fin;
+       }
+       pwd = va_arg(ap, struct passwd *);
+       buffer = va_arg(ap, char *);
+       bufsize = va_arg(ap, size_t);
+       errnop = va_arg(ap, int *);
+       *errnop = files_getstate(&st);
+       if (*errnop != 0)
+               return (NS_UNAVAIL);
+       if (how == nss_lt_all && st->keynum < 0) {
+               rv = NS_NOTFOUND;
+               goto fin;
+       }
+       if (st->db == NULL &&
+           (st->db = pwdbopen(&st->version)) == NULL) {
+               *errnop = errno;
+               rv = NS_UNAVAIL;
+               goto fin;
+       }
+       if (how == nss_lt_all)
+               stayopen = 1;
+       else
+               stayopen = st->stayopen;
+       key.data = keybuf;
+       do {
+               switch (how) {
+               case nss_lt_name:
+                       /* MAXLOGNAME includes NUL byte, but we do not
+                        * include the NUL byte in the key.
+                        */
+                       namesize = strlcpy(&keybuf[1], name, sizeof(keybuf)-1);
+                       if (namesize >= sizeof(keybuf)-1) {
+                               *errnop = EINVAL;
+                               rv = NS_NOTFOUND;
+                               goto fin;
+                       }
+                       key.size = namesize + 1;
+                       break;
+               case nss_lt_id:
+                       if (st->version < _PWD_CURRENT_VERSION) {
+                               memcpy(&keybuf[1], &uid, sizeof(uid));
+                               key.size = sizeof(uid) + 1;
+                       } else {
+                               store = htonl(uid);
+                               memcpy(&keybuf[1], &store, sizeof(store));
+                               key.size = sizeof(store) + 1;
+                       }
+                       break;
+               case nss_lt_all:
+                       st->keynum++;
+                       if (st->version < _PWD_CURRENT_VERSION) {
+                               memcpy(&keybuf[1], &st->keynum,
+                                   sizeof(st->keynum));
+                               key.size = sizeof(st->keynum) + 1;
+                       } else {
+                               store = htonl(st->keynum);
+                               memcpy(&keybuf[1], &store, sizeof(store));
+                               key.size = sizeof(store) + 1;
+                       }
+                       break;
+               }
+               keybuf[0] = _PW_VERSIONED(keybuf[0], st->version);
+               rv = st->db->get(st->db, &key, &entry, 0);
+               if (rv < 0 || rv > 1) { /* should never return > 1 */
+                       *errnop = errno;
+                       rv = NS_UNAVAIL;
+                       goto fin;
+               } else if (rv == 1) {
+                       if (how == nss_lt_all)
+                               st->keynum = -1;
+                       rv = NS_NOTFOUND;
+                       goto fin;
                }
-               
+               rv = pwdb_versions[st->version].match(entry.data, entry.size,
+                   how, name, uid);
+               if (rv != NS_SUCCESS)
+                       continue;
+               if (entry.size > bufsize) {
+                       *errnop = ERANGE;
+                       rv = NS_RETURN;
+                       break;
+               }
+               memcpy(buffer, entry.data, entry.size);
+               rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd,
+                   errnop);
+       } while (how == nss_lt_all && !(rv & NS_TERMINATE));
+fin:
+       if (!stayopen && st->db != NULL) {
+               st->db->close(st->db);
+               st->db = NULL;
+       }
+       if (rv == NS_SUCCESS) {
+               pwd->pw_fields &= ~_PWF_SOURCE;
+               pwd->pw_fields |= _PWF_FILES;
+               if (retval != NULL)
+                       *(struct passwd **)retval = pwd;
        }
-       return(0);
+       return (rv);
 }
 
-static char *
-_get_adjunct_pw(const char *name)
+
+static int
+pwdb_match_entry_v3(char *entry, size_t entrysize, enum nss_lookup_type how,
+    const char *name, uid_t uid)
 {
-       static char adjunctbuf[YPMAXRECORD+2];
-       int rval;
-       char *result;
-       int resultlen;
-       char *map = "passwd.adjunct.byname";
-       char *s;
+       const char      *p, *eom;
+       uid_t            uid2;
+
+       eom = &entry[entrysize];
+       for (p = entry; p < eom; p++)
+               if (*p == '\0')
+                       break;
+       if (*p != '\0')
+               return (NS_NOTFOUND);
+       if (how == nss_lt_all)
+               return (NS_SUCCESS);
+       if (how == nss_lt_name)
+               return (strcmp(name, entry) == 0 ? NS_SUCCESS : NS_NOTFOUND);
+       for (p++; p < eom; p++)
+               if (*p == '\0')
+                       break;
+       if (*p != '\0' || (++p) + sizeof(uid) >= eom)
+               return (NS_NOTFOUND);
+       memcpy(&uid2, p, sizeof(uid2));
+       return (uid == uid2 ? NS_SUCCESS : NS_NOTFOUND);
+}
 
-       if ((rval = yp_match(_pw_yp_domain, map, name, strlen(name),
-                   &result, &resultlen)))
-               return(NULL);
 
-       strncpy(adjunctbuf, result, resultlen);
-       adjunctbuf[resultlen] = '\0';
-       free(result);
-       result = (char *)&adjunctbuf;
+static int
+pwdb_parse_entry_v3(char *buffer, size_t bufsize, struct passwd *pwd,
+    int *errnop)
+{
+       char            *p, *eom;
+       int32_t          pw_change, pw_expire;
+
+       /* THIS CODE MUST MATCH THAT IN pwd_mkdb. */
+       p = buffer;
+       eom = &buffer[bufsize];
+#define STRING(field)  do {                    \
+               (field) = p;                    \
+               while (p < eom && *p != '\0')   \
+                       p++;                    \
+               if (p >= eom)                   \
+                       return (NS_NOTFOUND);   \
+               p++;                            \
+       } while (0)
+#define SCALAR(field)  do {                            \
+               if (p + sizeof(field) > eom)            \
+                       return (NS_NOTFOUND);           \
+               memcpy(&(field), p, sizeof(field));     \
+               p += sizeof(field);                     \
+       } while (0)
+       STRING(pwd->pw_name);
+       STRING(pwd->pw_passwd);
+       SCALAR(pwd->pw_uid);
+       SCALAR(pwd->pw_gid);
+       SCALAR(pw_change);
+       STRING(pwd->pw_class);
+       STRING(pwd->pw_gecos);
+       STRING(pwd->pw_dir);
+       STRING(pwd->pw_shell);
+       SCALAR(pw_expire);
+       SCALAR(pwd->pw_fields);
+#undef STRING
+#undef SCALAR
+       pwd->pw_change = pw_change;
+       pwd->pw_expire = pw_expire;
+       return (NS_SUCCESS);
+}
 
-       /* Don't care about the name. */
-       if ((s = strsep(&result, ":")) == NULL)
-               return (NULL); /* name */
-       if ((s = strsep(&result, ":")) == NULL)
-               return (NULL); /* password */
 
-       return(s);
+static int
+pwdb_match_entry_v4(char *entry, size_t entrysize, enum nss_lookup_type how,
+    const char *name, uid_t uid)
+{
+       const char      *p, *eom;
+       uint32_t         uid2;
+
+       eom = &entry[entrysize];
+       for (p = entry; p < eom; p++)
+               if (*p == '\0')
+                       break;
+       if (*p != '\0')
+               return (NS_NOTFOUND);
+       if (how == nss_lt_all)
+               return (NS_SUCCESS);
+       if (how == nss_lt_name)
+               return (strcmp(name, entry) == 0 ? NS_SUCCESS : NS_NOTFOUND);
+       for (p++; p < eom; p++)
+               if (*p == '\0')
+                       break;
+       if (*p != '\0' || (++p) + sizeof(uid) >= eom)
+               return (NS_NOTFOUND);
+       memcpy(&uid2, p, sizeof(uid2));
+       uid2 = ntohl(uid2);
+       return (uid == (uid_t)uid2 ? NS_SUCCESS : NS_NOTFOUND);
 }
 
+
 static int
-_pw_breakout_yp(struct passwd *pw, char *res, int resultlen, int master)
+pwdb_parse_entry_v4(char *buffer, size_t bufsize, struct passwd *pwd,
+    int *errnop)
 {
-       char *s, *result;
-       static char resbuf[YPMAXRECORD+2];
+       char            *p, *eom;
+       uint32_t         n;
 
-       /*
-        * Be triple, ultra super-duper paranoid: reject entries
-        * that start with a + or -. yp_mkdb and /var/yp/Makefile
-        * are _both_ supposed to strip these out, but you never
-        * know.
-        */
-       if (*res == '+' || *res == '-')
-               return 0;
-
-       /*
-        * The NIS protocol definition limits the size of an NIS
-        * record to YPMAXRECORD bytes. We need to do a copy to
-        * a static buffer here since the memory pointed to by
-        * res will be free()ed when this function returns.
-        */
-       strncpy((char *)&resbuf, res, resultlen);
-       resbuf[resultlen] = '\0';
-       result = (char *)&resbuf;
+       /* THIS CODE MUST MATCH THAT IN pwd_mkdb. */
+       p = buffer;
+       eom = &buffer[bufsize];
+#define STRING(field)  do {                    \
+               (field) = p;                    \
+               while (p < eom && *p != '\0')   \
+                       p++;                    \
+               if (p >= eom)                   \
+                       return (NS_NOTFOUND);   \
+               p++;                            \
+       } while (0)
+#define SCALAR(field)  do {                            \
+               if (p + sizeof(n) > eom)                \
+                       return (NS_NOTFOUND);           \
+               memcpy(&n, p, sizeof(n));               \
+               (field) = ntohl(n);                     \
+               p += sizeof(n);                         \
+       } while (0)
+       STRING(pwd->pw_name);
+       STRING(pwd->pw_passwd);
+       SCALAR(pwd->pw_uid);
+       SCALAR(pwd->pw_gid);
+       SCALAR(pwd->pw_change);
+       STRING(pwd->pw_class);
+       STRING(pwd->pw_gecos);
+       STRING(pwd->pw_dir);
+       STRING(pwd->pw_shell);
+       SCALAR(pwd->pw_expire);
+       SCALAR(pwd->pw_fields);
+#undef STRING
+#undef SCALAR
+       return (NS_SUCCESS);
+}
 
-       /*
-        * XXX Sanity check: make sure all fields are valid (no NULLs).
-        * If we find a badly formatted entry, we punt.
-        */
-       if ((s = strsep(&result, ":")) == NULL) return 0; /* name */
-       /*
-        * We don't care what pw_fields says: we _always_ want the
-        * username returned to us by NIS.
-        */
-       pw->pw_name = s;
-       pw->pw_fields |= _PWF_NAME;
-
-       if ((s = strsep(&result, ":")) == NULL) return 0; /* password */
-       if(!(pw->pw_fields & _PWF_PASSWD)) {
-               /* SunOS passwd.adjunct hack */
-               if (master == YP_HAVE_ADJUNCT && strstr(s, "##") != NULL) {
-                       char *realpw;
-                       realpw = _get_adjunct_pw(pw->pw_name);
-                       if (realpw == NULL)
-                               pw->pw_passwd = s;
-                       else
-                               pw->pw_passwd = realpw;
-               } else {
-                       pw->pw_passwd = s;
-               }
-               pw->pw_fields |= _PWF_PASSWD;
-       }
 
-       if ((s = strsep(&result, ":")) == NULL) return 0; /* uid */
-       if(!(pw->pw_fields & _PWF_UID)) {
-               pw->pw_uid = atoi(s);
-               pw->pw_fields |= _PWF_UID;
-       }
+#ifdef HESIOD
+/*
+ * dns backend
+ */
+static void
+dns_endstate(void *p)
+{
+       free(p);
+}
 
-       if ((s = strsep(&result, ":")) == NULL) return 0; /* gid */
-       if(!(pw->pw_fields & _PWF_GID))  {
-               pw->pw_gid = atoi(s);
-               pw->pw_fields |= _PWF_GID;
-       }
 
-       if (master == YP_HAVE_MASTER) {
-               if ((s = strsep(&result, ":")) == NULL) return 0; /* class */
-               if(!(pw->pw_fields & _PWF_CLASS))  {
-                       pw->pw_class = s;
-                       pw->pw_fields |= _PWF_CLASS;
-               }
+static int
+dns_setpwent(void *retval, void *mdata, va_list ap)
+{
+       struct dns_state        *st;
+       int                      rv;
+
+       rv = dns_getstate(&st);
+       if (rv != 0)
+               return (NS_UNAVAIL);
+       st->counter = 0;
+       return (NS_UNAVAIL);
+}
 
-               if ((s = strsep(&result, ":")) == NULL) return 0; /* change */
-               if(!(pw->pw_fields & _PWF_CHANGE))  {
-                       pw->pw_change = atol(s);
-                       pw->pw_fields |= _PWF_CHANGE;
-               }
 
-               if ((s = strsep(&result, ":")) == NULL) return 0; /* expire */
-               if(!(pw->pw_fields & _PWF_EXPIRE))  {
-                       pw->pw_expire = atol(s);
-                       pw->pw_fields |= _PWF_EXPIRE;
+static int
+dns_passwd(void *retval, void *mdata, va_list ap)
+{
+       char                     buf[HESIOD_NAME_MAX];
+       struct dns_state        *st;
+       struct passwd           *pwd;
+       const char              *name, *label;
+       void                    *ctx;
+       char                    *buffer, **hes;
+       size_t                   bufsize, linesize;
+       uid_t                    uid;
+       enum nss_lookup_type     how;
+       int                      rv, *errnop;
+
+       ctx = NULL;
+       hes = NULL;
+       name = NULL;
+       uid = (uid_t)-1;
+       how = (enum nss_lookup_type)mdata;
+       switch (how) {
+       case nss_lt_name:
+               name = va_arg(ap, const char *);
+               break;
+       case nss_lt_id:
+               uid = va_arg(ap, uid_t);
+               break;
+       case nss_lt_all:
+               break;
+       }
+       pwd     = va_arg(ap, struct passwd *);
+       buffer  = va_arg(ap, char *);
+       bufsize = va_arg(ap, size_t);
+       errnop  = va_arg(ap, int *);
+       *errnop = dns_getstate(&st);
+       if (*errnop != 0)
+               return (NS_UNAVAIL);
+       if (hesiod_init(&ctx) != 0) {
+               *errnop = errno;
+               rv = NS_UNAVAIL;
+               goto fin;
+       }
+       do {
+               rv = NS_NOTFOUND;
+               switch (how) {
+               case nss_lt_name:
+                       label = name;
+                       break;
+               case nss_lt_id:
+                       if (snprintf(buf, sizeof(buf), "%lu",
+                           (unsigned long)uid) >= sizeof(buf))
+                               goto fin;
+                       label = buf;
+                       break;
+               case nss_lt_all:
+                       if (st->counter < 0)
+                               goto fin;
+                       if (snprintf(buf, sizeof(buf), "passwd-%ld",
+                           st->counter++) >= sizeof(buf))
+                               goto fin;
+                       label = buf;
+                       break;
+               }
+               hes = hesiod_resolve(ctx, label,
+                   how == nss_lt_id ? "uid" : "passwd");
+               if (hes == NULL) {
+                       if (how == nss_lt_all)
+                               st->counter = -1;
+                       if (errno != ENOENT)
+                               *errnop = errno;
+                       goto fin;
+               }
+               rv = __pw_match_entry(hes[0], strlen(hes[0]), how, name, uid);
+               if (rv != NS_SUCCESS) {
+                       hesiod_free_list(ctx, hes);
+                       hes = NULL;
+                       continue;
                }
+               linesize = strlcpy(buffer, hes[0], bufsize);
+               if (linesize >= bufsize) {
+                       *errnop = ERANGE;
+                       rv = NS_RETURN;
+                       continue;
+               }
+               hesiod_free_list(ctx, hes);
+               hes = NULL;
+               rv = __pw_parse_entry(buffer, bufsize, pwd, 0, errnop);
+       } while (how == nss_lt_all && !(rv & NS_TERMINATE));
+fin:
+       if (hes != NULL)
+               hesiod_free_list(ctx, hes);
+       if (ctx != NULL)
+               hesiod_end(ctx);
+       if (rv == NS_SUCCESS) {
+               pwd->pw_fields &= ~_PWF_SOURCE;
+               pwd->pw_fields |= _PWF_HESIOD;
+               if (retval != NULL)
+                       *(struct passwd **)retval = pwd;
        }
+       return (rv);
+}
+#endif /* HESIOD */
 
-       if ((s = strsep(&result, ":")) == NULL) return 0; /* gecos */
-       if(!(pw->pw_fields & _PWF_GECOS)) {
-               pw->pw_gecos = s;
-               pw->pw_fields |= _PWF_GECOS;
-       }
 
-       if ((s = strsep(&result, ":")) == NULL) return 0; /* dir */
-       if(!(pw->pw_fields & _PWF_DIR)) {
-               pw->pw_dir = s;
-               pw->pw_fields |= _PWF_DIR;
-       }
+#ifdef YP
+/*
+ * nis backend
+ */
+static void
+nis_endstate(void *p)
+{
+       free(((struct nis_state *)p)->key);
+       free(p);
+}
 
-       if ((s = strsep(&result, ":")) == NULL) return 0; /* shell */
-       if(!(pw->pw_fields & _PWF_SHELL)) {
-               pw->pw_shell = s;
-               pw->pw_fields |= _PWF_SHELL;
+/*
+ * Test for the presence of special FreeBSD-specific master.passwd.by*
+ * maps. We do this using yp_order(). If it fails, then either the server
+ * doesn't have the map, or the YPPROC_ORDER procedure isn't supported by
+ * the server (Sun NIS+ servers in YP compat mode behave this way). If
+ * the master.passwd.by* maps don't exist, then let the lookup routine try
+ * the regular passwd.by* maps instead. If the lookup routine fails, it
+ * can return an error as needed.
+ */
+static int
+nis_map(char *domain, enum nss_lookup_type how, char *buffer, size_t bufsize,
+    int *master)
+{
+       int     rv, order;
+
+       *master = 0;
+       if (geteuid() == 0) {
+               if (snprintf(buffer, bufsize, "master.passwd.by%s",
+                   (how == nss_lt_id) ? "uid" : "name") >= bufsize)
+                       return (NS_UNAVAIL);
+               rv = yp_order(domain, buffer, &order);
+               if (rv == 0) {
+                       *master = 1;
+                       return (NS_SUCCESS);
+               }
        }
 
-       /* Be consistent. */
-       if ((s = strchr(pw->pw_shell, '\n'))) *s = '\0';
+       if (snprintf(buffer, bufsize, "passwd.by%s",
+           (how == nss_lt_id) ? "uid" : "name") >= bufsize)
+               return (NS_UNAVAIL);
 
-       return 1;
+       return (NS_SUCCESS);
 }
 
+
 static int
-_havemaster(char *_yp_domain)
+nis_adjunct(char *domain, const char *name, char *buffer, size_t bufsize)
 {
-       int order;
-       int rval;
+       int      rv;
+       char    *result, *p, *q, *eor;
+       int      resultlen;
+
+       result = NULL;
+       rv = yp_match(domain, "passwd.adjunct.byname", name, strlen(name),
+           &result, &resultlen);
+       if (rv != 0)
+               rv = 1;
+       else {
+               eor = &result[resultlen];
+               p = memchr(result, ':', eor - result);
+               if (p != NULL && ++p < eor &&
+                   (q = memchr(p, ':', eor - p)) != NULL) {
+                       if (q - p >= bufsize)
+                               rv = -1;
+                       else {
+                               memcpy(buffer, p, q - p);
+                               buffer[q - p] ='\0';
+                       }
+               } else
+                       rv = 1;
+       }
+       free(result);
+       return (rv);
+}
 
-       if (!(rval = yp_order(_yp_domain, "master.passwd.byname", &order)))
-               return(YP_HAVE_MASTER);
 
-       /*
-        * NIS+ in YP compat mode doesn't support
-        * YPPROC_ORDER -- no point in continuing.
-        */
-       if (rval == YPERR_YPERR)
-               return(YP_HAVE_NONE);
+static int
+nis_setpwent(void *retval, void *mdata, va_list ap)
+{
+       struct nis_state        *st;
+       int                      rv;
+
+       rv = nis_getstate(&st);
+       if (rv != 0)
+               return (NS_UNAVAIL);
+       st->done = 0;
+       free(st->key);
+       st->key = NULL;
+       return (NS_UNAVAIL);
+}
+
 
-       /* master.passwd doesn't exist -- try passwd.adjunct */
-       if (rval == YPERR_MAP) {
-               rval = yp_order(_yp_domain, "passwd.adjunct.byname", &order);
-               if (!rval)
-                       return(YP_HAVE_ADJUNCT);
+static int
+nis_passwd(void *retval, void *mdata, va_list ap)
+{
+       char             map[YPMAXMAP];
+       struct nis_state *st;
+       struct passwd   *pwd;
+       const char      *name;
+       char            *buffer, *key, *result;
+       size_t           bufsize;
+       uid_t            uid;
+       enum nss_lookup_type how;
+       int             *errnop, keylen, resultlen, rv, master;
+
+       name = NULL;
+       uid = (uid_t)-1;
+       how = (enum nss_lookup_type)mdata;
+       switch (how) {
+       case nss_lt_name:
+               name = va_arg(ap, const char *);
+               break;
+       case nss_lt_id:
+               uid = va_arg(ap, uid_t);
+               break;
+       case nss_lt_all:
+               break;
        }
+       pwd     = va_arg(ap, struct passwd *);
+       buffer  = va_arg(ap, char *);
+       bufsize = va_arg(ap, size_t);
+       errnop  = va_arg(ap, int *);
+       *errnop = nis_getstate(&st);
+       if (*errnop != 0)
+               return (NS_UNAVAIL);
+       if (st->domain[0] == '\0') {
+               if (getdomainname(st->domain, sizeof(st->domain)) != 0) {
+                       *errnop = errno;
+                       return (NS_UNAVAIL);
+               }
+       }
+       rv = nis_map(st->domain, how, map, sizeof(map), &master);
+       if (rv != NS_SUCCESS)
+               return (rv);
+       result = NULL;
+       do {
+               rv = NS_NOTFOUND;
+               switch (how) {
+               case nss_lt_name:
+                       if (strlcpy(buffer, name, bufsize) >= bufsize)
+                               goto erange;
+                       break;
+               case nss_lt_id:
+                       if (snprintf(buffer, bufsize, "%lu",
+                           (unsigned long)uid) >= bufsize)
+                               goto erange;
+                       break;
+               case nss_lt_all:
+                       if (st->done)
+                               goto fin;
+                       break;
+               }
+               result = NULL;
+               if (how == nss_lt_all) {
+                       if (st->key == NULL)
+                               rv = yp_first(st->domain, map, &st->key,
+                                   &st->keylen, &result, &resultlen);
+                       else {
+                               key = st->key;
+                               keylen = st->keylen;
+                               st->key = NULL;
+                               rv = yp_next(st->domain, map, key, keylen,
+                                   &st->key, &st->keylen, &result,
+                                   &resultlen);
+                               free(key);
+                       }
+                       if (rv != 0) {
+                               free(result);
+                               free(st->key);
+                               st->key = NULL;
+                               if (rv == YPERR_NOMORE)
+                                       st->done = 1;
+                               else
+                                       rv = NS_UNAVAIL;
+                               goto fin;
+                       }
+               } else {
+                       rv = yp_match(st->domain, map, buffer, strlen(buffer),
+                           &result, &resultlen);
+                       if (rv == YPERR_KEY) {
+                               rv = NS_NOTFOUND;
+                               continue;
+                       } else if (rv != 0) {
+                               free(result);
+                               rv = NS_UNAVAIL;
+                               continue;
+                       }
+               }
+               if (resultlen >= bufsize)
+                       goto erange;
+               memcpy(buffer, result, resultlen);
+               buffer[resultlen] = '\0';
+               free(result);
+               rv = __pw_match_entry(buffer, resultlen, how, name, uid);
+               if (rv == NS_SUCCESS)
+                       rv = __pw_parse_entry(buffer, resultlen, pwd, master,
+                           errnop);
+       } while (how == nss_lt_all && !(rv & NS_TERMINATE));
+fin:
+       if (rv == NS_SUCCESS) {
+               if (strstr(pwd->pw_passwd, "##") != NULL) {
+                       rv = nis_adjunct(st->domain, pwd->pw_name,
+                           &buffer[resultlen+1], bufsize-resultlen-1);
+                       if (rv < 0)
+                               goto erange;
+                       else if (rv == 0)
+                               pwd->pw_passwd = &buffer[resultlen+1];
+               }
+               pwd->pw_fields &= ~_PWF_SOURCE;
+               pwd->pw_fields |= _PWF_NIS;
+               if (retval != NULL)
+                       *(struct passwd **)retval = pwd;
+               rv = NS_SUCCESS;
+       }
+       return (rv);
+erange:
+       *errnop = ERANGE;
+       return (NS_RETURN);
+}
+#endif /* YP */
+
+
+/*
+ * compat backend
+ */
+static void
+compat_clear_template(struct passwd *template)
+{
 
-       return (YP_HAVE_NONE);
+       free(template->pw_passwd);
+       free(template->pw_gecos);
+       free(template->pw_dir);
+       free(template->pw_shell);
+       memset(template, 0, sizeof(*template));
 }
 
+
 static int
-_getyppass(struct passwd *pw, const char *name, const char *map)
+compat_set_template(struct passwd *src, struct passwd *template)
 {
-       char *result, *s;
-       int resultlen;
-       int rv;
-       char mastermap[YPMAXRECORD];
 
-       if(!_pw_yp_domain) {
-               if(yp_get_default_domain(&_pw_yp_domain))
-                 return 0;
-       }
+       compat_clear_template(template);
+#ifdef PW_OVERRIDE_PASSWD
+       if ((src->pw_fields & _PWF_PASSWD) &&
+           (template->pw_passwd = strdup(src->pw_passwd)) == NULL)
+               goto enomem;
+#endif
+       if (src->pw_fields & _PWF_UID)
+               template->pw_uid = src->pw_uid;
+       if (src->pw_fields & _PWF_GID)
+               template->pw_gid = src->pw_gid;
+       if ((src->pw_fields & _PWF_GECOS) &&
+           (template->pw_gecos = strdup(src->pw_gecos)) == NULL)
+               goto enomem;
+       if ((src->pw_fields & _PWF_DIR) &&
+           (template->pw_dir = strdup(src->pw_dir)) == NULL)
+               goto enomem;
+       if ((src->pw_fields & _PWF_SHELL) &&
+           (template->pw_shell = strdup(src->pw_shell)) == NULL)
+               goto enomem;
+       template->pw_fields = src->pw_fields;
+       return (0);
+enomem:
+       syslog(LOG_ERR, "getpwent memory allocation failure");
+       return (-1);
+}
 
-       if (_gotmaster == YP_HAVE_MASTER)
-               snprintf(mastermap, sizeof(mastermap), "master.%s", map);
-       else
-               snprintf(mastermap, sizeof(mastermap), "%s", map);
-
-       if(yp_match(_pw_yp_domain, (char *)&mastermap, name, strlen(name),
-                   &result, &resultlen)) {
-               if (_gotmaster != YP_HAVE_MASTER)
-                       return 0;
-               snprintf(mastermap, sizeof(mastermap), "%s", map);
-               if (yp_match(_pw_yp_domain, (char *)&mastermap,
-                            name, strlen(name), &result, &resultlen))
-                       return 0;
-               _gotmaster = YP_HAVE_NONE;
-       }
-
-       if (!_pw_stepping_yp) {
-               s = strchr(result, ':');
-               if (s) {
-                       *s = '\0';
-               } else {
-                       /* Must be a malformed entry if no colons. */
-                       free(result);
-                       return(0);
-               }
 
-               if (!verf(result)) {
-                       *s = ':';
-                       free(result);
-                       return(0);
-               }
+static int
+compat_use_template(struct passwd *pwd, struct passwd *template, char *buffer,
+    size_t bufsize)
+{
+       struct passwd hold;
+       char    *copy, *p, *q, *eob;
+       size_t   n;
 
-               *s = ':'; /* Put back the colon we previously replaced with a NUL. */
+       /* We cannot know the layout of the password fields in `buffer',
+        * so we have to copy everything.
+        */
+       if (template->pw_fields == 0) /* nothing to fill-in */
+               return (0);
+       n = 0;
+       n += pwd->pw_name != NULL ? strlen(pwd->pw_name) + 1 : 0;
+       n += pwd->pw_passwd != NULL ? strlen(pwd->pw_passwd) + 1 : 0;
+       n += pwd->pw_class != NULL ? strlen(pwd->pw_class) + 1 : 0;
+       n += pwd->pw_gecos != NULL ? strlen(pwd->pw_gecos) + 1 : 0;
+       n += pwd->pw_dir != NULL ? strlen(pwd->pw_dir) + 1 : 0;
+       n += pwd->pw_shell != NULL ? strlen(pwd->pw_shell) + 1 : 0;
+       copy = malloc(n);
+       if (copy == NULL) {
+               syslog(LOG_ERR, "getpwent memory allocation failure");
+               return (ENOMEM);
        }
+       p = copy;
+       eob = &copy[n];
+#define COPY(field) do {                               \
+       if (pwd->field == NULL)                         \
+               hold.field = NULL;                      \
+       else {                                          \
+               hold.field = p;                         \
+               p += strlcpy(p, pwd->field, eob-p) + 1; \
+       }                                               \
+} while (0)
+       COPY(pw_name);
+       COPY(pw_passwd);
+       COPY(pw_class);
+       COPY(pw_gecos);
+       COPY(pw_dir);
+       COPY(pw_shell);
+#undef COPY
+       p = buffer;
+       eob = &buffer[bufsize];
+#define COPY(field, flag) do {                                          \
+       q = (template->pw_fields & flag) ? template->field : hold.field; \
+       if (q == NULL)                                                   \
+               pwd->field = NULL;                                       \
+       else {                                                           \
+               pwd->field = p;                                          \
+               if ((n = strlcpy(p, q, eob-p)) >= eob-p) {               \
+                       free(copy);                                      \
+                       return (ERANGE);                                 \
+               }                                                        \
+               p += n + 1;                                              \
+       }                                                                \
+} while (0)
+       COPY(pw_name, 0);
+#ifdef PW_OVERRIDE_PASSWD
+       COPY(pw_passwd, _PWF_PASSWD);
+#else
+       COPY(pw_passwd, 0);
+#endif
+       COPY(pw_class, 0);
+       COPY(pw_gecos, _PWF_GECOS);
+       COPY(pw_dir, _PWF_DIR);
+       COPY(pw_shell, _PWF_SHELL);
+#undef COPY
+#define COPY(field, flag) do {                 \
+       if (template->pw_fields & flag)         \
+               pwd->field = template->field;   \
+} while (0)
+       COPY(pw_uid, _PWF_UID);
+       COPY(pw_gid, _PWF_GID);
+#undef COPY
+       free(copy);
+       return (0);
+}
 
-       rv = _pw_breakout_yp(pw, result, resultlen, _gotmaster);
-       free(result);
-       return(rv);
+
+static int
+compat_exclude(const char *name, DB **db)
+{
+       DBT     key, data;
+
+       if (*db == NULL &&
+           (*db = dbopen(NULL, O_RDWR, 600, DB_HASH, 0)) == NULL)
+               return (errno);
+       key.size = strlen(name);
+       key.data = (char *)name;
+       data.size = 0;
+       data.data = NULL;
+
+       if ((*db)->put(*db, &key, &data, 0) == -1)
+               return (errno);
+       return (0);
 }
 
+
 static int
-_nextyppass(struct passwd *pw)
+compat_is_excluded(const char *name, DB *db)
 {
-       static char *key;
-       static int keylen;
-       char *lastkey, *result, *s;
-       int resultlen;
-       int rv;
-       char *map = "passwd.byname";
+       DBT     key, data;
+
+       if (db == NULL)
+               return (0);
+       key.size = strlen(name);
+       key.data = (char *)name;
+       return (db->get(db, &key, &data, 0) == 0);
+}
 
-       if(!_pw_yp_domain) {
-               if(yp_get_default_domain(&_pw_yp_domain))
-                 return 0;
+
+static int
+compat_redispatch(struct compat_state *st, enum nss_lookup_type how,
+    enum nss_lookup_type lookup_how, const char *name, const char *lookup_name,
+    uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, int *errnop)
+{
+       static const ns_src compatsrc[] = {
+#ifdef YP
+               { NSSRC_NIS, NS_SUCCESS },
+#endif
+               { NULL, 0 }
+       };
+       ns_dtab dtab[] = {
+#ifdef YP
+               { NSSRC_NIS, nis_passwd, NULL },
+#endif
+#ifdef HESIOD
+               { NSSRC_DNS, dns_passwd, NULL },
+#endif
+               { NULL, NULL, NULL }
+       };
+       void            *discard;
+       int              rv, e, i;
+
+       for (i = 0; i < sizeof(dtab)/sizeof(dtab[0]) - 1; i++)
+               dtab[i].mdata = (void *)lookup_how;
+more:
+       pwd_init(pwd);
+       switch (lookup_how) {
+       case nss_lt_all:
+               rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT,
+                   "getpwent_r", compatsrc, pwd, buffer, bufsize,
+                   errnop);
+               break;
+       case nss_lt_id:
+               rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT,
+                   "getpwuid_r", compatsrc, uid, pwd, buffer,
+                   bufsize, errnop);
+               break;
+       case nss_lt_name:
+               rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT,
+                   "getpwnam_r", compatsrc, lookup_name, pwd, buffer,
+                   bufsize, errnop);
+               break;
+       default:
+               return (NS_UNAVAIL);
+       }
+       if (rv != NS_SUCCESS)
+               return (rv);
+       if (compat_is_excluded(pwd->pw_name, st->exclude)) {
+               if (how == nss_lt_all)
+                       goto more;
+               return (NS_NOTFOUND);
+       }
+       e = compat_use_template(pwd, &st->template, buffer, bufsize);
+       if (e != 0) {
+               *errnop = e;
+               if (e == ERANGE)
+                       return (NS_RETURN);
+               else
+                       return (NS_UNAVAIL);
        }
+       switch (how) {
+       case nss_lt_name:
+               if (strcmp(name, pwd->pw_name) != 0)
+                       return (NS_NOTFOUND);
+               break;
+       case nss_lt_id:
+               if (uid != pwd->pw_uid)
+                       return (NS_NOTFOUND);
+               break;
+       default:
+               break;
+       }
+       return (NS_SUCCESS);
+}
 
-       if (_gotmaster == YP_HAVE_MASTER)
-               map = "master.passwd.byname";
 
-       if(!_pw_stepping_yp) {
-               if(key) free(key);
-                       rv = yp_first(_pw_yp_domain, map,
-                                     &key, &keylen, &result, &resultlen);
-               if(rv) {
-                       return 0;
-               }
-               _pw_stepping_yp = 1;
-               goto unpack;
-       } else {
-tryagain:
-               lastkey = key;
-                       rv = yp_next(_pw_yp_domain, map, key, keylen,
-                            &key, &keylen, &result, &resultlen);
-               free(lastkey);
-unpack:
-               if(rv) {
-                       _pw_stepping_yp = 0;
-                       return 0;
-               }
+static void
+compat_endstate(void *p)
+{
+       struct compat_state *st;
+
+       if (p == NULL)
+               return;
+       st = (struct compat_state *)p;
+       if (st->db != NULL)
+               st->db->close(st->db);
+       if (st->exclude != NULL)
+               st->exclude->close(st->exclude);
+       compat_clear_template(&st->template);
+       free(p);
+}
 
-               s = strchr(result, ':');
-               if (s) {
-                       *s = '\0';
-               } else {
-                       /* Must be a malformed entry if no colons. */
-                       free(result);
-                       goto tryagain;
-               }
 
-               if (lookup(result)) {
-                       *s = ':';
-                       free(result);
-                       goto tryagain;
+static int
+compat_setpwent(void *retval, void *mdata, va_list ap)
+{
+       static const ns_src compatsrc[] = {
+#ifdef YP
+               { NSSRC_NIS, NS_SUCCESS },
+#endif
+               { NULL, 0 }
+       };
+       ns_dtab dtab[] = {
+#ifdef YP
+               { NSSRC_NIS, nis_setpwent, NULL },
+#endif
+#ifdef HESIOD
+               { NSSRC_DNS, dns_setpwent, NULL },
+#endif
+               { NULL, NULL, NULL }
+       };
+       struct compat_state     *st;
+       int                      rv, stayopen;
+
+#define set_setent(x, y) do {                                  \
+       int i;                                                  \
+                                                               \
+       for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++)      \
+               x[i].mdata = (void *)y;                         \
+} while (0)
+
+       rv = compat_getstate(&st);
+       if (rv != 0)
+               return (NS_UNAVAIL);
+       switch ((enum constants)mdata) {
+       case SETPWENT:
+               stayopen = va_arg(ap, int);
+               st->keynum = 0;
+               if (stayopen)
+                       st->db = pwdbopen(&st->version);
+               st->stayopen = stayopen;
+               set_setent(dtab, mdata);
+               _nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "setpwent",
+                   compatsrc, 0);
+               break;
+       case ENDPWENT:
+               if (st->db != NULL) {
+                       st->db->close(st->db);
+                       st->db = NULL;
                }
+               set_setent(dtab, mdata);
+               _nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "endpwent",
+                   compatsrc, 0);
+               break;
+       default:
+               break;
+       }
+       return (NS_UNAVAIL);
+#undef set_setent
+}
+
 
-               *s = ':'; /* Put back the colon we previously replaced with a NUL. */
-               if (_pw_breakout_yp(pw, result, resultlen, _gotmaster)) {
-                       free(result);
-                       return(1);
+static int
+compat_passwd(void *retval, void *mdata, va_list ap)
+{
+       char                     keybuf[MAXLOGNAME + 1];
+       DBT                      key, entry;
+       struct compat_state     *st;
+       enum nss_lookup_type     how;
+       const char              *name;
+       struct passwd           *pwd;
+       char                    *buffer, *pw_name;
+       char                    *host, *user, *domain;
+       size_t                   bufsize;
+       uid_t                    uid;
+       uint32_t                 store;
+       int                      rv, from_compat, stayopen, *errnop;
+
+       from_compat = 0;
+       name = NULL;
+       uid = (uid_t)-1;
+       how = (enum nss_lookup_type)mdata;
+       switch (how) {
+       case nss_lt_name:
+               name = va_arg(ap, const char *);
+               break;
+       case nss_lt_id:
+               uid = va_arg(ap, uid_t);
+               break;
+       case nss_lt_all:
+               break;
+       default:
+               rv = NS_NOTFOUND;
+               goto fin;
+       }
+       pwd = va_arg(ap, struct passwd *);
+       buffer = va_arg(ap, char *);
+       bufsize = va_arg(ap, size_t);
+       errnop = va_arg(ap, int *);
+       *errnop = compat_getstate(&st);
+       if (*errnop != 0)
+               return (NS_UNAVAIL);
+       if (how == nss_lt_all && st->keynum < 0) {
+               rv = NS_NOTFOUND;
+               goto fin;
+       }
+       if (st->db == NULL &&
+           (st->db = pwdbopen(&st->version)) == NULL) {
+               *errnop = errno;
+               rv = NS_UNAVAIL;
+               goto fin;
+       }
+       if (how == nss_lt_all) {
+               if (st->keynum < 0) {
+                       rv = NS_NOTFOUND;
+                       goto fin;
+               }
+               stayopen = 1;
+       } else {
+               st->keynum = 0;
+               stayopen = st->stayopen;
+       }
+docompat:
+       rv = NS_NOTFOUND;
+       switch (st->compat) {
+       case COMPAT_MODE_ALL:
+               rv = compat_redispatch(st, how, how, name, name, uid, pwd,
+                   buffer, bufsize, errnop);
+               if (rv != NS_SUCCESS)
+                       st->compat = COMPAT_MODE_OFF;
+               break;
+       case COMPAT_MODE_NETGROUP:
+               /* XXX getnetgrent is not thread-safe. */
+               do {
+                       rv = getnetgrent(&host, &user, &domain);
+                       if (rv == 0) {
+                               endnetgrent();
+                               st->compat = COMPAT_MODE_OFF;
+                               rv = NS_NOTFOUND;
+                               continue;
+                       } else if (user == NULL || user[0] == '\0')
+                               continue;
+                       rv = compat_redispatch(st, how, nss_lt_name, name,
+                           user, uid, pwd, buffer, bufsize, errnop);
+               } while (st->compat == COMPAT_MODE_NETGROUP &&
+                   !(rv & NS_TERMINATE));
+               break;
+       case COMPAT_MODE_NAME:
+               rv = compat_redispatch(st, how, nss_lt_name, name, st->name,
+                   uid, pwd, buffer, bufsize, errnop);
+               free(st->name);
+               st->name = NULL;
+               st->compat = COMPAT_MODE_OFF;
+               break;
+       default:
+               break;
+       }
+       if (rv & NS_TERMINATE) {
+               from_compat = 1;
+               goto fin;
+       }
+       key.data = keybuf;
+       rv = NS_NOTFOUND;
+       while (st->keynum >= 0) {
+               st->keynum++;
+               if (st->version < _PWD_CURRENT_VERSION) {
+                       memcpy(&keybuf[1], &st->keynum, sizeof(st->keynum));
+                       key.size = sizeof(st->keynum) + 1;
                } else {
-                       free(result);
-                       goto tryagain;
+                       store = htonl(st->keynum);
+                       memcpy(&keybuf[1], &store, sizeof(store));
+                       key.size = sizeof(store) + 1;
+               }
+               keybuf[0] = _PW_VERSIONED(_PW_KEYBYNUM, st->version);
+               rv = st->db->get(st->db, &key, &entry, 0);
+               if (rv < 0 || rv > 1) { /* should never return > 1 */
+                       *errnop = errno;
+                       rv = NS_UNAVAIL;
+                       goto fin;
+               } else if (rv == 1) {
+                       st->keynum = -1;
+                       rv = NS_NOTFOUND;
+                       goto fin;
                }
+               pw_name = (char *)entry.data;
+               switch (pw_name[0]) {
+               case '+':
+                       switch (pw_name[1]) {
+                       case '\0':
+                               st->compat = COMPAT_MODE_ALL;
+                               break;
+                       case '@':
+                               setnetgrent(&pw_name[2]);
+                               st->compat = COMPAT_MODE_NETGROUP;
+                               break;
+                       default:
+                               st->name = strdup(&pw_name[1]);
+                               if (st->name == NULL) {
+                                       syslog(LOG_ERR,
+                                        "getpwent memory allocation failure");
+                                       *errnop = ENOMEM;
+                                       rv = NS_UNAVAIL;
+                                       break;
+                               }
+                               st->compat = COMPAT_MODE_NAME;
+                       }
+                       if (entry.size > bufsize) {
+                               *errnop = ERANGE;
+                               rv = NS_RETURN;
+                               goto fin;
+                       }
+                       memcpy(buffer, entry.data, entry.size);
+                       rv = pwdb_versions[st->version].parse(buffer,
+                           entry.size, pwd, errnop);
+                       if (rv != NS_SUCCESS)
+                               ;
+                       else if (compat_set_template(pwd, &st->template) < 0) {
+                               *errnop = ENOMEM;
+                               rv = NS_UNAVAIL;
+                               goto fin;
+                       }
+                       goto docompat;
+               case '-':
+                       switch (pw_name[1]) {
+                       case '\0':
+                               /* XXX Maybe syslog warning */
+                               continue;
+                       case '@':
+                               setnetgrent(&pw_name[2]);
+                               while (getnetgrent(&host, &user, &domain) !=
+                                   0) {
+                                       if (user != NULL && user[0] != '\0')
+                                               compat_exclude(user,
+                                                   &st->exclude);
+                               }
+                               endnetgrent();
+                               continue;
+                       default:
+                               compat_exclude(&pw_name[1], &st->exclude);
+                               continue;
+                       }
+                       break;
+               default:
+                       break;
+               }
+               if (compat_is_excluded((char *)entry.data, st->exclude))
+                       continue;
+               rv = pwdb_versions[st->version].match(entry.data, entry.size,
+                   how, name, uid);
+               if (rv == NS_RETURN)
+                       break;
+               else if (rv != NS_SUCCESS)
+                       continue;
+               if (entry.size > bufsize) {
+                       *errnop = ERANGE;
+                       rv = NS_RETURN;
+                       break;
+               }
+               memcpy(buffer, entry.data, entry.size);
+               rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd,
+                   errnop);
+               if (rv & NS_TERMINATE)
+                       break;
+       }
+fin:
+       if (!stayopen && st->db != NULL) {
+               st->db->close(st->db);
+               st->db = NULL;
+       }
+       if (rv == NS_SUCCESS) {
+               if (!from_compat) {
+                       pwd->pw_fields &= ~_PWF_SOURCE;
+                       pwd->pw_fields |= _PWF_FILES;
+               }
+               if (retval != NULL)
+                       *(struct passwd **)retval = pwd;
        }
+       return (rv);
 }
 
-#endif /* YP */
+
+/*
+ * common passwd line matching and parsing
+ */
+int
+__pw_match_entry(const char *entry, size_t entrysize, enum nss_lookup_type how,
+    const char *name, uid_t uid)
+{
+       const char      *p, *eom;
+       char            *q;
+       size_t           len;
+       unsigned long    m;
+
+       eom = entry + entrysize;
+       for (p = entry; p < eom; p++)
+               if (*p == ':')
+                       break;
+       if (*p != ':')
+               return (NS_NOTFOUND);
+       if (how == nss_lt_all)
+               return (NS_SUCCESS);
+       if (how == nss_lt_name) {
+               len = strlen(name);
+               if (len == (p - entry) && memcmp(name, entry, len) == 0)
+                       return (NS_SUCCESS);
+               else
+                       return (NS_NOTFOUND);
+       }
+       for (p++; p < eom; p++)
+               if (*p == ':')
+                       break;
+       if (*p != ':')
+               return (NS_NOTFOUND);
+       m = strtoul(++p, &q, 10);
+       if (q[0] != ':' || (uid_t)m != uid)
+               return (NS_NOTFOUND);
+       else
+               return (NS_SUCCESS);
+}
+
+
+/* XXX buffer must be NUL-terminated.  errnop is not set correctly. */
+int
+__pw_parse_entry(char *buffer, size_t bufsize __unused, struct passwd *pwd,
+    int master, int *errnop __unused)
+{
+
+       if (__pw_scan(buffer, pwd, master ? _PWSCAN_MASTER : 0) == 0)
+               return (NS_NOTFOUND);
+       else
+               return (NS_SUCCESS);
+}
index 375db28..028f9b3 100644 (file)
@@ -1,6 +1,4 @@
 .\"    $NetBSD: getusershell.3,v 1.6 1999/03/22 19:44:42 garbled Exp $
-.\"    $FreeBSD: src/lib/libc/gen/getusershell.3,v 1.5.2.4 2003/03/13 18:05:37 trhodes Exp $
-.\"    $DragonFly: src/lib/libc/gen/getusershell.3,v 1.2 2003/06/17 04:26:42 dillon Exp $
 .\"
 .\" Copyright (c) 1985, 1991, 1993
 .\"    The Regents of the University of California.  All rights reserved.
 .\" 2. Redistributions in binary form must reproduce the above copyright
 .\"    notice, this list of conditions and the following disclaimer in the
 .\"    documentation and/or other materials provided with the distribution.
-.\" 3. All advertising materials mentioning features or use of this software
-.\"    must display the following acknowledgement:
-.\"    This product includes software developed by the University of
-.\"    California, Berkeley and its contributors.
 .\" 4. Neither the name of the University nor the names of its contributors
 .\"    may be used to endorse or promote products derived from this software
 .\"    without specific prior written permission.
 .\" SUCH DAMAGE.
 .\"
 .\"     @(#)getusershell.3     8.1 (Berkeley) 6/4/93
-.\" $FreeBSD: src/lib/libc/gen/getusershell.3,v 1.5.2.4 2003/03/13 18:05:37 trhodes Exp $
+.\" $FreeBSD: src/lib/libc/gen/getusershell.3,v 1.12 2007/01/09 00:27:54 imp Exp $
+.\" $DragonFly: src/lib/libc/gen/getusershell.3,v 1.2 2003/06/17 04:26:42 dillon Exp $
 .\"
-.Dd June 4, 1993
+.Dd January 16, 1999
 .Dt GETUSERSHELL 3
 .Os
 .Sh NAME
 .Nm getusershell ,
 .Nm setusershell ,
 .Nm endusershell
-.Nd get legal user shells
+.Nd get valid user shells
 .Sh LIBRARY
 .Lb libc
 .Sh SYNOPSIS
@@ -88,6 +83,7 @@ The routine
 returns a null pointer (0) on
 .Dv EOF .
 .Sh SEE ALSO
+.Xr nsswitch.conf 5 ,
 .Xr shells 5
 .Sh HISTORY
 The
index 6a6347a..e0a563e 100644 (file)
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
  * 4. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libc/gen/getusershell.c,v 1.3.2.1 2001/03/05 09:17:52 obrien Exp $
- * $DragonFly: src/lib/libc/gen/getusershell.c,v 1.4 2005/11/13 00:07:42 swildner Exp $
- *
  * @(#)getusershell.c  8.1 (Berkeley) 6/4/93
- * $FreeBSD: src/lib/libc/gen/getusershell.c,v 1.3.2.1 2001/03/05 09:17:52 obrien Exp $
+ * $NetBSD: getusershell.c,v 1.17 1999/01/25 01:09:34 lukem Exp $
+ * $FreeBSD: src/lib/libc/gen/getusershell.c,v 1.10 2007/01/09 00:27:54 imp Exp $
+ * $DragonFly: src/lib/libc/gen/getusershell.c,v 1.4 2005/11/13 00:07:42 swildner Exp $
  */
 
+#include "namespace.h"
 #include <sys/param.h>
 #include <sys/file.h>
-#include <sys/stat.h>
 
 #include <ctype.h>
+#include <errno.h>
+#include <nsswitch.h>
+#include <paths.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
+#include <stringlist.h>
 #include <unistd.h>
-#include <paths.h>
+
+#ifdef HESIOD
+#include <hesiod.h>
+#endif
+#ifdef YP
+#include <rpc/rpc.h>
+#include <rpcsvc/ypclnt.h>
+#include <rpcsvc/yp_prot.h>
+#endif
+#include "un-namespace.h"
 
 /*
  * Local shells should NOT be added here.  They should be added in
  * /etc/shells.
  */
 
-static char *okshells[] = { _PATH_BSHELL, _PATH_CSHELL, NULL };
-static char **curshell, **shells, *strings;
-static char **initshells (void);
+static const char *const okshells[] = { _PATH_BSHELL, _PATH_CSHELL, NULL };
+static const char *const *curshell;
+static StringList       *sl;
+
+static const char *const *initshells(void);
 
 /*
- * Get a list of shells from _PATH_SHELLS, if it exists.
+ * Get a list of shells from "shells" nsswitch database
  */
 char *
 getusershell(void)
@@ -66,7 +77,8 @@ getusershell(void)
 
        if (curshell == NULL)
                curshell = initshells();
-       ret = *curshell;
+       /*LINTED*/
+       ret = (char *)*curshell;
        if (ret != NULL)
                curshell++;
        return (ret);
@@ -75,13 +87,10 @@ getusershell(void)
 void
 endusershell(void)
 {
-
-       if (shells != NULL)
-               free(shells);
-       shells = NULL;
-       if (strings != NULL)
-               free(strings);
-       strings = NULL;
+       if (sl) {
+               sl_free(sl, 1);
+               sl = NULL;
+       }
        curshell = NULL;
 }
 
@@ -92,49 +101,159 @@ setusershell(void)
        curshell = initshells();
 }
 
-static char **
-initshells(void)
+
+static int     _local_initshells(void *, void *, va_list);
+
+/*ARGSUSED*/
+static int
+_local_initshells(void *rv, void *cb_data, va_list ap)
 {
-       char **sp, *cp;
-       FILE *fp;
-       struct stat statb;
-
-       if (shells != NULL)
-               free(shells);
-       shells = NULL;
-       if (strings != NULL)
-               free(strings);
-       strings = NULL;
+       char    *sp, *cp;
+       FILE    *fp;
+       char     line[MAXPATHLEN + 2];
+
+       if (sl)
+               sl_free(sl, 1);
+       sl = sl_init();
+
        if ((fp = fopen(_PATH_SHELLS, "r")) == NULL)
-               return (okshells);
-       if (fstat(fileno(fp), &statb) == -1) {
-               fclose(fp);
-               return (okshells);
-       }
-       if ((strings = malloc((u_int)statb.st_size)) == NULL) {
-               fclose(fp);
-               return (okshells);
-       }
-       shells = calloc((unsigned)statb.st_size / 3, sizeof (char *));
-       if (shells == NULL) {
-               fclose(fp);
-               free(strings);
-               strings = NULL;
-               return (okshells);
-       }
-       sp = shells;
-       cp = strings;
+               return NS_UNAVAIL;
+
+       sp = cp = line;
        while (fgets(cp, MAXPATHLEN + 1, fp) != NULL) {
                while (*cp != '#' && *cp != '/' && *cp != '\0')
                        cp++;
                if (*cp == '#' || *cp == '\0')
                        continue;
-               *sp++ = cp;
-               while (!isspace((unsigned char)*cp) && *cp != '#' && *cp != '\0')
+               sp = cp;
+               while (!isspace(*cp) && *cp != '#' && *cp != '\0')
                        cp++;
                *cp++ = '\0';
+               sl_add(sl, strdup(sp));
        }
-       *sp = NULL;
        fclose(fp);
-       return (shells);
+       return NS_SUCCESS;
+}
+
+#ifdef HESIOD
+static int     _dns_initshells(void *, void *, va_list);
+
+/*ARGSUSED*/
+static int
+_dns_initshells(void *rv, void *cb_data, va_list ap)
+{
+       char      shellname[] = "shells-XXXXX";
+       int       hsindex, hpi, r;
+       char    **hp;
+       void     *context;
+
+       if (sl)
+               sl_free(sl, 1);
+       sl = sl_init();
+       r = NS_UNAVAIL;
+       if (hesiod_init(&context) == -1)
+               return (r);
+
+       for (hsindex = 0; ; hsindex++) {
+               snprintf(shellname, sizeof(shellname)-1, "shells-%d", hsindex);
+               hp = hesiod_resolve(context, shellname, "shells");
+               if (hp == NULL) {
+                       if (errno == ENOENT) {
+                               if (hsindex == 0)
+                                       r = NS_NOTFOUND;
+                               else
+                                       r = NS_SUCCESS;
+                       }
+                       break;
+               } else {
+                       for (hpi = 0; hp[hpi]; hpi++)
+                               sl_add(sl, hp[hpi]);
+                       free(hp);
+               }
+       }
+       hesiod_end(context);
+       return (r);
+}
+#endif /* HESIOD */
+
+#ifdef YP
+static int     _nis_initshells(void *, void *, va_list);
+
+/*ARGSUSED*/
+static int
+_nis_initshells(void *rv, void *cb_data, va_list ap)
+{
+       static char *ypdomain;
+       char    *key, *data;
+       char    *lastkey;
+       int      keylen, datalen;
+       int      r;
+
+       if (sl)
+               sl_free(sl, 1);
+       sl = sl_init();
+
+       if (ypdomain == NULL) {
+               switch (yp_get_default_domain(&ypdomain)) {
+               case 0:
+                       break;
+               case YPERR_RESRC:
+                       return NS_TRYAGAIN;
+               default:
+                       return NS_UNAVAIL;
+               }
+       }
+
+       /*
+        * `key' and `data' point to strings dynamically allocated by
+        * the yp_... functions.
+        * `data' is directly put into the stringlist of shells.
+        */
+       key = data = NULL;
+       if (yp_first(ypdomain, "shells", &key, &keylen, &data, &datalen))
+               return NS_UNAVAIL;
+       do {
+               data[datalen] = '\0';           /* clear trailing \n */
+               sl_add(sl, data);
+
+               lastkey = key;
+               r = yp_next(ypdomain, "shells", lastkey, keylen,
+                   &key, &keylen, &data, &datalen);
+               free(lastkey);
+       } while (r == 0);
+
+       if (r == YPERR_NOMORE) {
+               /*
+                * `data' and `key' ought to be NULL - do not try to free them.
+                */
+               return NS_SUCCESS;
+       }
+
+       return NS_UNAVAIL;
+}
+#endif /* YP */
+
+static const char *const *
+initshells(void)
+{
+       static const ns_dtab dtab[] = {
+               NS_FILES_CB(_local_initshells, NULL)
+               NS_DNS_CB(_dns_initshells, NULL)
+               NS_NIS_CB(_nis_initshells, NULL)
+               { 0 }
+       };
+       if (sl)
+               sl_free(sl, 1);
+       sl = sl_init();
+
+       if (_nsdispatch(NULL, dtab, NSDB_SHELLS, "initshells", __nsdefaultsrc)
+           != NS_SUCCESS) {
+               if (sl)
+                       sl_free(sl, 1);
+               sl = NULL;
+               return (okshells);
+       }
+       sl_add(sl, NULL);
+
+       return (const char *const *)(sl->sl_str);
 }
similarity index 65%
rename from usr.sbin/pwd_mkdb/pw_scan.c
rename to lib/libc/gen/pw_scan.c
index 21d591a..f894777 100644 (file)
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
  * 4. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
@@ -31,7 +27,7 @@
  * SUCH DAMAGE.
  *
  * @(#)pw_scan.c       8.3 (Berkeley) 4/2/94
- * $FreeBSD: src/usr.sbin/pwd_mkdb/pw_scan.c,v 1.14.2.1 2002/11/11 08:52:04 maxim Exp $
+ * $FreeBSD: src/lib/libc/gen/pw_scan.c,v 1.26 2007/01/09 00:27:55 imp Exp $
  * $DragonFly: src/usr.sbin/pwd_mkdb/pw_scan.c,v 1.3 2005/12/05 02:40:27 swildner Exp $
  */
 
 
 /*
  * Some software assumes that IDs are short.  We should emit warnings
- * for id's which can not be stored in a short, but we are more liberal
+ * for id's which cannot be stored in a short, but we are more liberal
  * by default, warning for IDs greater than USHRT_MAX.
  *
- * If pw_big_ids_warning is anything other than -1 on entry to pw_scan()
- * it will be set based on the existance of PW_SCAN_BIG_IDS in the
- * environment.
+ * If pw_big_ids_warning is -1 on entry to pw_scan(), it will be set based
+ * on the existence of PW_SCAN_BIG_IDS in the environment.
  */
-int    pw_big_ids_warning = -1;
+static int     pw_big_ids_warning = -1;
 
 int
-pw_scan(char *bp, struct passwd *pw)
+__pw_scan(char *bp, struct passwd *pw, int flags)
 {
        uid_t id;
        int root;
@@ -78,12 +73,13 @@ pw_scan(char *bp, struct passwd *pw)
        if (!(pw->pw_name = strsep(&bp, ":")))          /* login */
                goto fmt;
        root = !strcmp(pw->pw_name, "root");
-       if(pw->pw_name[0] && (pw->pw_name[0] != '+' || pw->pw_name[1] == '\0'))
+       if (pw->pw_name[0] && (pw->pw_name[0] != '+' || pw->pw_name[1] == '\0'))
                pw->pw_fields |= _PWF_NAME;
 
        if (!(pw->pw_passwd = strsep(&bp, ":")))        /* passwd */
                goto fmt;
-       if(pw->pw_passwd[0]) pw->pw_fields |= _PWF_PASSWD;
+       if (pw->pw_passwd[0])
+               pw->pw_fields |= _PWF_PASSWD;
 
        if (!(p = strsep(&bp, ":")))                    /* uid */
                goto fmt;
@@ -91,24 +87,28 @@ pw_scan(char *bp, struct passwd *pw)
                pw->pw_fields |= _PWF_UID;
        else {
                if (pw->pw_name[0] != '+' && pw->pw_name[0] != '-') {
-                       warnx("no uid for user %s", pw->pw_name);
+                       if (flags & _PWSCAN_WARN)
+                               warnx("no uid for user %s", pw->pw_name);
                        return (0);
                }
        }
        id = strtoul(p, &ep, 10);
        if (errno == ERANGE) {
-               warnx("%s > max uid value (%lu)", p, ULONG_MAX);
+               if (flags & _PWSCAN_WARN)
+                       warnx("%s > max uid value (%lu)", p, ULONG_MAX);
                return (0);
        }
        if (*ep != '\0') {
-               warnx("%s uid is incorrect", p);
+               if (flags & _PWSCAN_WARN)
+                       warnx("%s uid is incorrect", p);
                return (0);
        }
        if (root && id) {
-               warnx("root uid should be 0");
+               if (flags & _PWSCAN_WARN)
+                       warnx("root uid should be 0");
                return (0);
        }
-       if (pw_big_ids_warning && id > USHRT_MAX) {
+       if (flags & _PWSCAN_WARN && pw_big_ids_warning && id > USHRT_MAX) {
                warnx("%s > recommended max uid value (%u)", p, USHRT_MAX);
                /*return (0);*/ /* THIS SHOULD NOT BE FATAL! */
        }
@@ -116,67 +116,83 @@ pw_scan(char *bp, struct passwd *pw)
 
        if (!(p = strsep(&bp, ":")))                    /* gid */
                goto fmt;
-       if(p[0])
+       if (p[0])
                pw->pw_fields |= _PWF_GID;
        else {
                if (pw->pw_name[0] != '+' && pw->pw_name[0] != '-') {
-                       warnx("no gid for user %s", pw->pw_name);
+                       if (flags & _PWSCAN_WARN)
+                               warnx("no gid for user %s", pw->pw_name);
                        return (0);
                }
        }
        id = strtoul(p, &ep, 10);
        if (errno == ERANGE) {
-               warnx("%s > max gid value (%lu)", p, ULONG_MAX);
+               if (flags & _PWSCAN_WARN)
+                       warnx("%s > max gid value (%lu)", p, ULONG_MAX);
                return (0);
        }
        if (*ep != '\0') {
-               warnx("%s gid is incorrect", p);
+               if (flags & _PWSCAN_WARN)
+                       warnx("%s gid is incorrect", p);
                return (0);
        }
-       if (pw_big_ids_warning && id > USHRT_MAX) {
+       if (flags & _PWSCAN_WARN && pw_big_ids_warning && id > USHRT_MAX) {
                warnx("%s > recommended max gid value (%u)", p, USHRT_MAX);
                /* return (0); This should not be fatal! */
        }
        pw->pw_gid = id;
 
-       pw->pw_class = strsep(&bp, ":");                /* class */
-       if(pw->pw_class[0]) pw->pw_fields |= _PWF_CLASS;
-
-       if (!(p = strsep(&bp, ":")))                    /* change */
-               goto fmt;
-       if(p[0]) pw->pw_fields |= _PWF_CHANGE;
-       pw->pw_change = atol(p);
-
-       if (!(p = strsep(&bp, ":")))                    /* expire */
-               goto fmt;
-       if(p[0]) pw->pw_fields |= _PWF_EXPIRE;
-       pw->pw_expire = atol(p);
-
+       if (flags & _PWSCAN_MASTER ) {
+               if (!(pw->pw_class = strsep(&bp, ":"))) /* class */
+                       goto fmt;
+               if (pw->pw_class[0])
+                       pw->pw_fields |= _PWF_CLASS;
+
+               if (!(p = strsep(&bp, ":")))            /* change */
+                       goto fmt;
+               if (p[0])
+                       pw->pw_fields |= _PWF_CHANGE;
+               pw->pw_change = atol(p);
+
+               if (!(p = strsep(&bp, ":")))            /* expire */
+                       goto fmt;
+               if (p[0])
+                       pw->pw_fields |= _PWF_EXPIRE;
+               pw->pw_expire = atol(p);
+       }
        if (!(pw->pw_gecos = strsep(&bp, ":")))         /* gecos */
                goto fmt;
-       if(pw->pw_gecos[0]) pw->pw_fields |= _PWF_GECOS;
+       if (pw->pw_gecos[0])
+               pw->pw_fields |= _PWF_GECOS;
 
-       if (!(pw->pw_dir = strsep(&bp, ":")))                   /* directory */
+       if (!(pw->pw_dir = strsep(&bp, ":")))           /* directory */
                goto fmt;
-       if(pw->pw_dir[0]) pw->pw_fields |= _PWF_DIR;
+       if (pw->pw_dir[0])
+               pw->pw_fields |= _PWF_DIR;
 
        if (!(pw->pw_shell = strsep(&bp, ":")))         /* shell */
                goto fmt;
 
        p = pw->pw_shell;
-       if (root && *p)                                 /* empty == /bin/sh */
+       if (root && *p) {                               /* empty == /bin/sh */
                for (setusershell();;) {
                        if (!(sh = getusershell())) {
-                               warnx("warning, unknown root shell");
+                               if (flags & _PWSCAN_WARN)
+                                       warnx("warning, unknown root shell");
                                break;
                        }
                        if (!strcmp(p, sh))
                                break;
                }
-       if(p[0]) pw->pw_fields |= _PWF_SHELL;
+               endusershell();
+       }
+       if (p[0])
+               pw->pw_fields |= _PWF_SHELL;
 
        if ((p = strsep(&bp, ":"))) {                   /* too many */
-fmt:           warnx("corrupted entry");
+fmt:
+               if (flags & _PWSCAN_WARN)
+                       warnx("corrupted entry");
                return (0);
        }
        return (1);
similarity index 78%
copy from usr.sbin/pwd_mkdb/pw_scan.h
copy to lib/libc/gen/pw_scan.h
index 8c2374f..288a919 100644 (file)
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
  * 4. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  * SUCH DAMAGE.
  *
  *     @(#)pw_scan.h   8.1 (Berkeley) 4/1/94
- *
- *     $FreeBSD: src/usr.sbin/pwd_mkdb/pw_scan.h,v 1.2 1999/11/15 16:45:37 sheldonh Exp $
- *     $DragonFly: src/usr.sbin/pwd_mkdb/pw_scan.h,v 1.3 2003/11/03 19:31:41 eirikn Exp $
+ * $FreeBSD: src/lib/libc/gen/pw_scan.h,v 1.7 2007/01/09 00:27:55 imp Exp $
+ * $DragonFly: src/usr.sbin/pwd_mkdb/pw_scan.h,v 1.3 2003/11/03 19:31:41 eirikn Exp $
  */
 
-extern int     pw_big_ids_warning;
+#define _PWSCAN_MASTER 0x01
+#define _PWSCAN_WARN   0x02
 
-extern int     pw_scan(char *, struct passwd *);
+extern int     __pw_scan(char *, struct passwd *, int);
index 86f330f..6c69b7c 100644 (file)
@@ -9,10 +9,6 @@
 .\" 2. Redistributions in binary form must reproduce the above copyright
 .\"    notice, this list of conditions and the following disclaimer in the
 .\"    documentation and/or other materials provided with the distribution.
-.\" 3. All advertising materials mentioning features or use of this software
-.\"    must display the following acknowledgement:
-.\"    This product includes software developed by the University of
-.\"    California, Berkeley and its contributors.
 .\" 4. Neither the name of the University nor the names of its contributors
 .\"    may be used to endorse or promote products derived from this software
 .\"    without specific prior written permission.
 .\" SUCH DAMAGE.
 .\"
 .\"     @(#)pwcache.3  8.1 (Berkeley) 6/9/93
-.\" $FreeBSD: src/lib/libc/gen/pwcache.3,v 1.6.2.4 2001/12/14 18:33:51 ru Exp $
+.\" $FreeBSD: src/lib/libc/gen/pwcache.3,v 1.15 2007/01/09 00:27:55 imp Exp $
 .\" $DragonFly: src/lib/libc/gen/pwcache.3,v 1.2 2003/06/17 04:26:42 dillon Exp $
 .\"
-.Dd June 9, 1993
+.Dd March 22, 2002
 .Dt PWCACHE 3
 .Os
 .Sh NAME
 .Sh LIBRARY
 .Lb libc
 .Sh SYNOPSIS
-.In stdlib.h
-.Ft char *
-.Fn user_from_uid "unsigned long uid" "int nouser"
-.Ft char *
-.Fn group_from_gid "unsigned long gid" "int nogroup"
+.In pwd.h
+.Ft const char *
+.Fn user_from_uid "uid_t uid" "int nouser"
+.In grp.h
+.Ft const char *
+.Fn group_from_gid "gid_t gid" "int nogroup"
 .Sh DESCRIPTION
 The
 .Fn user_from_uid
index 1f35db4..3c61334 100644 (file)
  * SUCH DAMAGE.
  *
  * @(#)pwcache.c       8.1 (Berkeley) 6/4/93
+ * $FreeBSD: src/lib/libc/gen/pwcache.c,v 1.10 2002/03/22 02:35:47 imp Exp $
  * $DragonFly: src/lib/libc/gen/pwcache.c,v 1.5 2005/11/13 00:07:42 swildner Exp $
  */
 
+#include <sys/types.h>
+
 #include <grp.h>
 #include <pwd.h>
-#include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <utmp.h>
@@ -44,7 +46,7 @@
 #define        NCACHE  64                      /* power of 2 */
 #define        MASK    (NCACHE - 1)            /* bits to store with */
 
-char *
+const char *
 user_from_uid(uid_t uid, int nouser)
 {
        static struct ncache {
@@ -78,7 +80,7 @@ user_from_uid(uid_t uid, int nouser)
        return ((nouser && !cp->found) ? NULL : cp->name);
 }
 
-char *
+const char *
 group_from_gid(gid_t gid, int nogroup)
 {
        static struct ncache {
index 508fcd8..0e5646f 100644 (file)
 #define _NAMESPACE_H_
 
 /*
+ * ISO C (C90) section.  Most names in libc aren't in ISO C, so they
+ * should be here.  Most aren't here...
+ */
+#define                nsdispatch                      _nsdispatch
+
+/*
  * Prototypes for syscalls/functions that need to be overridden
  * in libc_r/libpthread.
  */
diff --git a/lib/libc/include/nscache.h b/lib/libc/include/nscache.h
new file mode 100644 (file)
index 0000000..c8bbdb4
--- /dev/null
@@ -0,0 +1,197 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libc/include/nscache.h,v 1.1 2006/04/28 12:03:35 ume Exp $
+ */
+
+#ifndef __NS_CACHE_H__
+#define __NS_CACHE_H__
+
+#include "nscachedcli.h"
+
+typedef int (*nss_cache_id_func_t)(char *, size_t *, va_list, void *);
+typedef int (*nss_cache_marshal_func_t)(char *, size_t *, void *, va_list,
+       void *);
+typedef int (*nss_cache_unmarshal_func_t)(char *, size_t, void *, va_list,
+       void *);
+
+typedef        void (*nss_set_mp_ws_func_t)(cached_mp_write_session);
+typedef        cached_mp_write_session (*nss_get_mp_ws_func_t)(void);
+
+typedef void (*nss_set_mp_rs_func_t)(cached_mp_read_session);
+typedef cached_mp_read_session (*nss_get_mp_rs_func_t)(void);
+
+typedef struct _nss_cache_info {
+       char    *entry_name;
+       void    *mdata;
+
+       /*
+        * These 3 functions should be implemented specifically for each
+        * nsswitch database.
+        */
+       nss_cache_id_func_t id_func;    /* marshals the request parameters */
+       nss_cache_marshal_func_t marshal_func;     /* marshals response */
+       nss_cache_unmarshal_func_t unmarshal_func; /* unmarshals response */
+
+       /*
+        * These 4 functions should be generated with NSS_MP_CACHE_HANDLING
+        * macro.
+        */
+       nss_set_mp_ws_func_t set_mp_ws_func; /* sets current write session */
+       nss_get_mp_ws_func_t get_mp_ws_func; /* gets current write session */
+
+       nss_set_mp_rs_func_t set_mp_rs_func; /* sets current read session */
+       nss_get_mp_rs_func_t get_mp_rs_func; /* gets current read session */
+} nss_cache_info;
+
+/*
+ * NSS_MP_CACHE_HANDLING implements the set_mp_ws, get_mp_ws, set_mp_rs,
+ * get_mp_rs functions, that are used in _nss_cache_info. It uses
+ * NSS_TLS_HANDLING macro to organize thread local storage.
+ */
+#define NSS_MP_CACHE_HANDLING(name)                                    \
+struct name##_mp_state {                                               \
+       cached_mp_write_session mp_write_session;                       \
+       cached_mp_read_session  mp_read_session;                        \
+};                                                                     \
+                                                                       \
+static void                                                            \
+name##_mp_endstate(void *s) {                                          \
+       struct name##_mp_state  *mp_state;                              \
+                                                                       \
+       mp_state = (struct name##_mp_state *)s;                         \
+       if (mp_state->mp_write_session != INVALID_CACHED_MP_WRITE_SESSION)\
+               __abandon_cached_mp_write_session(mp_state->mp_write_session);\
+                                                                       \
+       if (mp_state->mp_read_session != INVALID_CACHED_MP_READ_SESSION)\
+               __close_cached_mp_read_session(mp_state->mp_read_session);\
+}                                                                      \
+NSS_TLS_HANDLING(name##_mp);                                           \
+                                                                       \
+static void                                                            \
+name##_set_mp_ws(cached_mp_write_session ws)                           \
+{                                                                      \
+       struct name##_mp_state  *mp_state;                              \
+       int     res;                                                    \
+                                                                       \
+       res = name##_mp_getstate(&mp_state);                            \
+       if (res != 0)                                                   \
+               return;                                                 \
+                                                                       \
+       mp_state->mp_write_session = ws;                                \
+}                                                                      \
+                                                                       \
+static cached_mp_write_session                                         \
+name##_get_mp_ws(void)                                                 \
+{                                                                      \
+       struct name##_mp_state  *mp_state;                              \
+       int     res;                                                    \
+                                                                       \
+       res = name##_mp_getstate(&mp_state);                            \
+       if (res != 0)                                                   \
+               return (INVALID_CACHED_MP_WRITE_SESSION);               \
+                                                                       \
+       return (mp_state->mp_write_session);                            \
+}                                                                      \
+                                                                       \
+static void                                                            \
+name##_set_mp_rs(cached_mp_read_session rs)                            \
+{                                                                      \
+       struct name##_mp_state  *mp_state;                              \
+       int     res;                                                    \
+                                                                       \
+       res = name##_mp_getstate(&mp_state);                            \
+       if (res != 0)                                                   \
+               return;                                                 \
+                                                                       \
+       mp_state->mp_read_session = rs;                                 \
+}                                                                      \
+                                                                       \
+static cached_mp_read_session                                          \
+name##_get_mp_rs(void)                                                 \
+{                                                                      \
+       struct name##_mp_state  *mp_state;                              \
+       int     res;                                                    \
+                                                                       \
+       res = name##_mp_getstate(&mp_state);                            \
+       if (res != 0)                                                   \
+               return (INVALID_CACHED_MP_READ_SESSION);                \
+                                                                       \
+       return (mp_state->mp_read_session);                             \
+}
+
+/*
+ * These macros should be used to initialize _nss_cache_info structure. For
+ * multipart queries in setXXXent and getXXXent functions mf and uf
+ * (marshal function and unmarshal function) should be both NULL.
+ */
+#define NS_COMMON_CACHE_INFO_INITIALIZER(name, mdata, if, mf, uf)      \
+       {#name, mdata, if, mf, uf, NULL, NULL, NULL, NULL}
+#define NS_MP_CACHE_INFO_INITIALIZER(name, mdata, mf, uf)              \
+       {#name, mdata, NULL, mf, uf, name##_set_mp_ws, name##_get_mp_ws,\
+               name##_set_mp_rs, name##_get_mp_rs }
+
+/*
+ * Analog of other XXX_CB macros. Has the pointer to _nss_cache_info
+ * structure as the only argument.
+ */
+#define NS_CACHE_CB(cinfo) {NSSRC_CACHE, __nss_cache_handler, (void *)(cinfo) },
+
+/* args are: current pointer, current buffer, initial buffer, pointer type */
+#define NS_APPLY_OFFSET(cp, cb, ib, p_type)                            \
+       if ((cp) != NULL)                                               \
+               (cp) = (p_type)((char *)(cb) + (size_t)(cp) - (size_t)(ib))
+/*
+ * Gets new pointer from the marshalled buffer by uisng initial address
+ * and initial buffer address
+ */
+#define NS_GET_NEWP(cp, cb, ib)                                                \
+       ((char *)(cb) + (size_t)(cp) - (size_t)(ib))
+
+typedef struct _nss_cache_data {
+       char    *key;
+       size_t  key_size;
+
+       nss_cache_info const    *info;
+} nss_cache_data;
+
+__BEGIN_DECLS
+/* dummy function, which is needed to make nss_method_lookup happy */
+extern int     __nss_cache_handler(void *, void *, va_list);
+
+#ifdef _NS_PRIVATE
+extern int     __nss_common_cache_read(void *, void *, va_list);
+extern int     __nss_common_cache_write(void *, void *, va_list);
+extern int     __nss_common_cache_write_negative(void *);
+
+extern int     __nss_mp_cache_read(void *, void *, va_list);
+extern int     __nss_mp_cache_write(void *, void *, va_list);
+extern int     __nss_mp_cache_write_submit(void *, void *, va_list);
+extern int     __nss_mp_cache_end(void *, void *, va_list);
+#endif /* _NS_PRIVATE */
+
+__END_DECLS
+
+#endif
diff --git a/lib/libc/include/nscachedcli.h b/lib/libc/include/nscachedcli.h
new file mode 100644 (file)
index 0000000..329f42b
--- /dev/null
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 2004 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libc/include/nscachedcli.h,v 1.1 2006/04/28 12:03:35 ume Exp $
+ */
+
+#ifndef __NS_CACHED_CLI_H__
+#define __NS_CACHED_CLI_H__
+
+/*
+ * This file contains API for working with caching daemon
+ */
+
+enum comm_element_t {
+       CET_UNDEFINED = 0,
+       CET_WRITE_REQUEST = 1,
+       CET_WRITE_RESPONSE = 2,
+       CET_READ_REQUEST = 3,
+       CET_READ_RESPONSE = 4,
+       CET_TRANSFORM_REQUEST = 5,
+       CET_TRANSFORM_RESPONSE = 6,
+       CET_MP_WRITE_SESSION_REQUEST = 7,
+       CET_MP_WRITE_SESSION_RESPONSE = 8,
+       CET_MP_WRITE_SESSION_WRITE_REQUEST = 9,
+       CET_MP_WRITE_SESSION_WRITE_RESPONSE = 10,
+       CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION = 11,
+       CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION = 12,
+       CET_MP_READ_SESSION_REQUEST = 13,
+       CET_MP_READ_SESSION_RESPONSE = 14,
+       CET_MP_READ_SESSION_READ_REQUEST = 15,
+       CET_MP_READ_SESSION_READ_RESPONSE = 16,
+       CET_MP_READ_SESSION_CLOSE_NOTIFICATION = 17
+};
+
+struct cached_connection_params {
+       char    *socket_path;
+       struct  timeval timeout;
+};
+
+struct cached_connection_ {
+       int     sockfd;
+       int     read_queue;
+       int     write_queue;
+
+       int     mp_flag;        /* shows if the connection is used for
+                                * multipart operations */
+};
+
+/* simple abstractions for not to write "struct" every time */
+typedef struct cached_connection_      *cached_connection;
+typedef struct cached_connection_      *cached_mp_write_session;
+typedef struct cached_connection_      *cached_mp_read_session;
+
+#define        INVALID_CACHED_CONNECTION       (NULL)
+#define        INVALID_CACHED_MP_WRITE_SESSION (NULL)
+#define        INVALID_CACHED_MP_READ_SESSION  (NULL)
+
+__BEGIN_DECLS
+
+/* initialization/destruction routines */
+extern cached_connection __open_cached_connection(
+       struct cached_connection_params const *);
+extern void __close_cached_connection(cached_connection);
+
+/* simple read/write operations */
+extern int __cached_write(cached_connection, const char *, const char *,
+       size_t, const char *, size_t);
+extern int __cached_read(cached_connection, const char *, const char *,
+       size_t, char *, size_t *);
+
+/* multipart read/write operations */
+extern cached_mp_write_session __open_cached_mp_write_session(
+       struct cached_connection_params const *, const char *);
+extern int __cached_mp_write(cached_mp_write_session, const char *, size_t);
+extern int __abandon_cached_mp_write_session(cached_mp_write_session);
+extern int __close_cached_mp_write_session(cached_mp_write_session);
+
+extern cached_mp_read_session __open_cached_mp_read_session(
+       struct cached_connection_params const *, const char *);
+extern int __cached_mp_read(cached_mp_read_session, char *, size_t *);
+extern int __close_cached_mp_read_session(cached_mp_read_session);
+
+__END_DECLS
+
+#endif
diff --git a/lib/libc/include/nss_tls.h b/lib/libc/include/nss_tls.h
new file mode 100644 (file)
index 0000000..cd623b0
--- /dev/null
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 2003 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by
+ * Jacques A. Vidrine, Safeport Network Services, and Network
+ * Associates Laboratories, the Security Research Division of Network
+ * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libc/include/nss_tls.h,v 1.3 2004/03/30 15:56:15 nectar Exp $
+ *
+ * Macros which generate thread local storage handling code in NSS modules.
+ */
+#ifndef _NSS_TLS_H_
+#define _NSS_TLS_H_
+
+#define NSS_TLS_HANDLING(name)                                 \
+static pthread_key_t name##_state_key;                         \
+static void     name##_keyinit(void);                          \
+static int      name##_getstate(struct name##_state **);       \
+\
+static void                                                            \
+name##_keyinit(void)                                                   \
+{                                                                      \
+       _pthread_key_create(&name##_state_key, name##_endstate);        \
+}                                                                      \
+\
+static int                                                     \
+name##_getstate(struct name##_state **p)                       \
+{                                                              \
+       static struct name##_state st;                          \
+       static pthread_once_t   keyinit = PTHREAD_ONCE_INIT;    \
+       int                     rv;                             \
+                                                               \
+       if (!__isthreaded || _pthread_main_np() != 0) {         \
+               *p = &st;                                       \
+               return (0);                                     \
+       }                                                       \
+       rv = _pthread_once(&keyinit, name##_keyinit);           \
+       if (rv != 0)                                            \
+               return (rv);