summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornectar <nectar@FreeBSD.org>2003-04-17 14:15:26 +0000
committernectar <nectar@FreeBSD.org>2003-04-17 14:15:26 +0000
commitcd021cdb21c56ef20cd0d905c99c29cf24bd9773 (patch)
tree194a658195371c8a28e5f3ecbd1d1b1e2dfe5588
parent1b1f6bb4f50d42bbbb1291be0c60741c12f8201a (diff)
downloadFreeBSD-src-cd021cdb21c56ef20cd0d905c99c29cf24bd9773.zip
FreeBSD-src-cd021cdb21c56ef20cd0d905c99c29cf24bd9773.tar.gz
= Implement thread-safe versions of the getpwent(3) and getgrent(3)
family of functions using the new nsdispatch(3) core. Remove arbitrary size limits when using the thread-safe versions. = Re-implement the traditional getpwent(3)/getgrent(3) functions on top of the thread-safe versions. = Update the on-disk format of the hashed version of the passwd(5) databases to allow for versioned entries. The legacy version is `3'. (Don't ask.) = Add support for version `4' entries in the passwd(5) database. Entries in this format are identical to version 3 entries except that all integers are stored as 32-bit integers in network byte order (big endian). = pwd_mkdb is updated to generate both version 3 and version 4 entries. Sponsored by: DARPA, Network Associates Laboratories
-rw-r--r--include/grp.h15
-rw-r--r--include/pwd.h32
-rw-r--r--lib/libc/gen/getgrent.397
-rw-r--r--lib/libc/gen/getgrent.c1528
-rw-r--r--lib/libc/gen/getpwent.3104
-rw-r--r--lib/libc/gen/getpwent.c2417
-rw-r--r--usr.sbin/pwd_mkdb/pwd_mkdb.c135
7 files changed, 2763 insertions, 1565 deletions
diff --git a/include/grp.h b/include/grp.h
index db34ff2..70b4a8a24 100644
--- a/include/grp.h
+++ b/include/grp.h
@@ -52,6 +52,11 @@ typedef __gid_t gid_t;
#define _GID_T_DECLARED
#endif
+#ifndef _SIZE_T_DECLARED
+typedef __size_t size_t;
+#define _SIZE_T_DECLARED
+#endif
+
struct group {
char *gr_name; /* group name */
char *gr_passwd; /* group password */
@@ -70,15 +75,17 @@ struct group *getgrnam(const char *);
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
-void setgrfile(const char *);
+int getgrent_r(struct group *, char *, size_t, struct group **);
int setgroupent(int);
#endif
-/*
- * XXX missing getgrgid_r(), getgrnam_r().
- */
__END_DECLS
#endif /* !_GRP_H_ */
diff --git a/include/pwd.h b/include/pwd.h
index 40082c3..cda449a 100644
--- a/include/pwd.h
+++ b/include/pwd.h
@@ -60,6 +60,11 @@ typedef __uid_t uid_t;
#define _UID_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"
@@ -73,11 +78,17 @@ typedef __uid_t uid_t;
#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 */
+#define _PWD_VERSION_KEY "\xFF" "VERSION"
+#define _PWD_CURRENT_VERSION '\x04'
+
+#define _PW_VERSION_MASK '0xF0'
+#define _PW_VERSION(x) ((unsigned char)((x)<<4))
+
+#define _PW_KEYBYNAME '\x01' /* stored by name */
+#define _PW_KEYBYNUM '\x02' /* stored by entry in the "file" */
+#define _PW_KEYBYUID '\x03' /* stored by uid */
+#define _PW_KEYYPENABLED '\x04' /* YP is enabled */
+#define _PW_KEYYPBYNUM '\x05' /* special +@netgroup entries */
#define _PASSWORD_EFMT1 '_' /* extended encryption format */
@@ -110,6 +121,9 @@ 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
@@ -123,12 +137,14 @@ struct passwd *getpwuid(uid_t);
void endpwent(void);
struct passwd *getpwent(void);
void setpwent(void);
-/*
- * XXX missing getpwnam_r() and getpwuid_r().
- */
+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
diff --git a/lib/libc/gen/getgrent.3 b/lib/libc/gen/getgrent.3
index e0a5566..ca85a4a 100644
--- a/lib/libc/gen/getgrent.3
+++ b/lib/libc/gen/getgrent.3
@@ -32,15 +32,17 @@
.\" From: @(#)getgrent.3 8.2 (Berkeley) 4/19/94
.\" $FreeBSD$
.\"
-.Dd September 29, 1994
+.Dd April 16, 2003
.Dt GETGRENT 3
.Os
.Sh NAME
.Nm getgrent ,
+.Nm getgrent_r ,
.Nm getgrnam ,
+.Nm getgrnam_r ,
.Nm getgrgid ,
+.Nm getgrgid_r ,
.Nm setgroupent ,
-.\" .Nm setgrfile ,
.Nm setgrent ,
.Nm endgrent
.Nd group database operations
@@ -50,14 +52,18 @@
.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 void
-.\" .Fn setgrfile "const char *name"
.Ft int
.Fn setgrent void
.Ft void
@@ -99,7 +105,36 @@ 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
@@ -130,8 +165,25 @@ The functions
.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.
+In the latter case,
+.Va errno
+will be set.
+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
@@ -169,6 +221,30 @@ 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 .
+.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 COMPATIBILITY
The historic function
.Fn setgrfile ,
@@ -190,6 +266,7 @@ will modify the same object.
.Pp
The functions
.Fn getgrent ,
+.Fn getgrent_r ,
.Fn endgrent ,
.Fn setgroupent ,
and
@@ -198,7 +275,9 @@ are fairly useless in a networked environment and should be
avoided, if possible.
The
.Fn getgrent
-function
-makes no attempt to suppress duplicate information if multiple
+and
+.Fn getgrent_r
+functions
+make no attempt to suppress duplicate information if multiple
sources are specified in
.Xr nsswitch.conf 5 .
diff --git a/lib/libc/gen/getgrent.c b/lib/libc/gen/getgrent.c
index a073946..86ba544 100644
--- a/lib/libc/gen/getgrent.c
+++ b/lib/libc/gen/getgrent.c
@@ -1,7 +1,12 @@
-/*
- * 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
@@ -11,18 +16,11 @@
* 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)
@@ -30,679 +28,1095 @@
* 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.
+ *
*/
-
-#if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)getgrent.c 8.2 (Berkeley) 3/21/94";
-#endif /* LIBC_SCCS and not lint */
-/* $NetBSD: getgrent.c,v 1.34.2.1 1999/04/27 14:10:58 perry Exp $ */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
-#include <sys/types.h>
-
+#include "namespace.h"
+#include <sys/param.h>
+#ifdef YP
+#include <rpc/rpc.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+#endif
+#include <ctype.h>
#include <errno.h>
+#ifdef HESIOD
+#include <hesiod.h>
+#endif
#include <grp.h>
-#include <limits.h>
#include <nsswitch.h>
+#include <pthread.h>
+#include <pthread_np.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
+#include <unistd.h>
+#include "un-namespace.h"
+#include "libc_private.h"
+#include "nss_tls.h"
-#ifdef HESIOD
-#include <hesiod.h>
-#include <arpa/nameser.h>
-#endif
-#ifdef YP
-#include <rpc/rpc.h>
-#include <rpcsvc/yp_prot.h>
-#include <rpcsvc/ypclnt.h>
-#endif
-
-#if defined(YP) || defined(HESIOD)
-#define _GROUP_COMPAT
-#endif
-static FILE *_gr_fp;
-static struct group _gr_group;
-static int _gr_stayopen;
-static int _gr_filesdone;
+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 void grcleanup(void);
-static int grscan(int, gid_t, const char *);
-static char *getline(void);
-static int copyline(const char*);
-static int matchline(int, gid_t, const char *);
-static int start_gr(void);
+static const ns_src defaultsrc[] = {
+ { NSSRC_FILES, NS_SUCCESS },
+ { NULL, 0 }
+};
+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);
-/* initial size for malloc and increase steps for realloc */
-#define MAXGRP 64
-#define MAXLINELENGTH 256
#ifdef HESIOD
-#if MAXLINELENGTH < NS_MAXLABEL + 1
-#error "MAXLINELENGTH must be at least NS_MAXLABEL + 1"
-#endif
+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 char **members; /* list of group members */
-static int maxgrp; /* current length of **members */
-static char *line; /* buffer for group line */
-static int maxlinelength; /* current length of *line */
-
-/*
- * 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)
#ifdef YP
-static char *__ypcurrent, *__ypdomain;
-static int __ypcurrentlen;
-static int _gr_ypdone;
+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
+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);
+
+
+/* XXX IEEE Std 1003.1, 2003 specifies `void setgrent(void)' */
+int
+setgrent(void)
+{
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_setgrent, (void *)SETGRENT },
#ifdef HESIOD
-static int _gr_hesnum;
+ { NSSRC_DNS, dns_setgrent, (void *)SETGRENT },
#endif
-
-#ifdef _GROUP_COMPAT
-enum _grmode { GRMODE_NONE, GRMODE_FULL, GRMODE_NAME };
-static enum _grmode __grmode;
+#ifdef YP
+ { NSSRC_NIS, nis_setgrent, (void *)SETGRENT },
#endif
+ { NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT },
+ { NULL, NULL, NULL }
+ };
+ (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc, 0);
+ return (1);
+}
-struct group *
-getgrent()
+
+int
+setgroupent(int stayopen)
{
- if ((!_gr_fp && !start_gr()) || !grscan(0, 0, NULL))
- return (NULL);
- return &_gr_group;
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_setgrent, (void *)SETGRENT },
+#ifdef HESIOD
+ { NSSRC_DNS, dns_setgrent, (void *)SETGRENT },
+#endif
+#ifdef YP
+ { NSSRC_NIS, nis_setgrent, (void *)SETGRENT },
+#endif
+ { NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT },
+ { NULL, NULL, NULL }
+ };
+ (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc,
+ stayopen);
+ return (1);
}
-struct group *
-getgrnam(name)
- const char *name;
+
+void
+endgrent(void)
{
- int rval;
-
- if (!start_gr())
- return NULL;
- rval = grscan(1, 0, name);
- if (!_gr_stayopen)
- endgrent();
- return (rval) ? &_gr_group : NULL;
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_setgrent, (void *)ENDGRENT },
+#ifdef HESIOD
+ { NSSRC_DNS, dns_setgrent, (void *)ENDGRENT },
+#endif
+#ifdef YP
+ { NSSRC_NIS, nis_setgrent, (void *)ENDGRENT },
+#endif
+ { NSSRC_COMPAT, compat_setgrent, (void *)ENDGRENT },
+ { NULL, NULL, NULL }
+ };
+ (void)_nsdispatch(NULL, dtab, NSDB_GROUP, "endgrent", defaultsrc);
}
-struct group *
-getgrgid(gid)
- gid_t gid;
+
+int
+getgrent_r(struct group *grp, char *buffer, size_t bufsize,
+ struct group **result)
{
- int rval;
-
- if (!start_gr())
- return NULL;
- rval = grscan(1, gid, NULL);
- if (!_gr_stayopen)
- endgrent();
- return (rval) ? &_gr_group : NULL;
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_group, (void *)nss_lt_all },
+#ifdef HESIOD
+ { NSSRC_DNS, dns_group, (void *)nss_lt_all },
+#endif
+#ifdef YP
+ { NSSRC_NIS, nis_group, (void *)nss_lt_all },
+#endif
+ { NSSRC_COMPAT, compat_group, (void *)nss_lt_all },
+ { NULL, NULL, NULL }
+ };
+ int rv, ret_errno;
+
+ ret_errno = 0;
+ *result = NULL;
+ rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrent_r", defaultsrc,
+ grp, buffer, bufsize, &ret_errno);
+ if (rv == NS_SUCCESS)
+ return (0);
+ else
+ return (ret_errno);
}
-void
-grcleanup()
+
+int
+getgrnam_r(const char *name, struct group *grp, char *buffer, size_t bufsize,
+ struct group **result)
{
- _gr_filesdone = 0;
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_group, (void *)nss_lt_name },
+#ifdef HESIOD
+ { NSSRC_DNS, dns_group, (void *)nss_lt_name },
+#endif
#ifdef YP
- if (__ypcurrent)
- free(__ypcurrent);
- __ypcurrent = NULL;
- _gr_ypdone = 0;
+ { NSSRC_NIS, nis_group, (void *)nss_lt_name },
#endif
+ { NSSRC_COMPAT, compat_group, (void *)nss_lt_name },
+ { NULL, NULL, NULL }
+ };
+ int rv, ret_errno;
+
+ 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);
+}
+
+
+int
+getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize,
+ struct group **result)
+{
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_group, (void *)nss_lt_id },
#ifdef HESIOD
- _gr_hesnum = 0;
+ { NSSRC_DNS, dns_group, (void *)nss_lt_id },
#endif
-#ifdef _GROUP_COMPAT
- __grmode = GRMODE_NONE;
+#ifdef YP
+ { NSSRC_NIS, nis_group, (void *)nss_lt_id },
#endif
+ { NSSRC_COMPAT, compat_group, (void *)nss_lt_id },
+ { 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);
}
-static int
-start_gr()
+
+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)
{
- grcleanup();
- if (maxlinelength == 0) {
- if ((line = (char *)malloc(MAXLINELENGTH)) == NULL)
- return 0;
- maxlinelength = MAXLINELENGTH;
- }
- if (maxgrp == 0) {
- if ((members = (char **) malloc(sizeof(char**) *
- MAXGRP)) == NULL)
- return 0;
- maxgrp = MAXGRP;
+ 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;
}
- if (_gr_fp) {
- rewind(_gr_fp);
- return 1;
- }
- return (_gr_fp = fopen(_PATH_GROUP, "r")) ? 1 : 0;
+ 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;
+ return (NULL);
+ }
+ grp_storage_size <<= 1;
+ grp_storage = malloc(grp_storage_size);
+ if (grp_storage == NULL)
+ return (NULL);
+ }
+ } while (res == NULL && rv == ERANGE);
+ return (res);
}
-int
-setgrent(void)
+
+static int
+wrap_getgrnam_r(union key key, struct group *grp, char *buffer, size_t bufsize,
+ struct group **res)
{
- return setgroupent(0);
+ return (getgrnam_r(key.name, grp, buffer, bufsize, res));
}
-int
-setgroupent(stayopen)
- int stayopen;
+
+static int
+wrap_getgrgid_r(union key key, struct group *grp, char *buffer, size_t bufsize,
+ struct group **res)
{
- if (!start_gr())
- return 0;
- _gr_stayopen = stayopen;
- return 1;
+ return (getgrgid_r(key.gid, grp, buffer, bufsize, res));
}
-void
-endgrent()
+
+static int
+wrap_getgrent_r(union key key __unused, struct group *grp, char *buffer,
+ size_t bufsize, struct group **res)
{
- grcleanup();
- if (_gr_fp) {
- (void)fclose(_gr_fp);
- _gr_fp = NULL;
- }
+ return (getgrent_r(grp, buffer, bufsize, res));
}
-static int _local_grscan(void *, void *, va_list);
+struct group *
+getgrnam(const char *name)
+{
+ union key key;
-/*ARGSUSED*/
-static int
-_local_grscan(rv, cb_data, ap)
- void *rv;
- void *cb_data;
- va_list ap;
+ key.name = name;
+ return (getgr(wrap_getgrnam_r, key));
+}
+
+
+struct group *
+getgrgid(gid_t gid)
{
- int search = va_arg(ap, int);
- gid_t gid = va_arg(ap, gid_t);
- const char *name = va_arg(ap, const char *);
-
- if (_gr_filesdone)
- return NS_NOTFOUND;
- for (;;) {
- if (getline() == NULL) {
- if (!search)
- _gr_filesdone = 1;
- return NS_NOTFOUND;
- }
- if (matchline(search, gid, name))
- return NS_SUCCESS;
- }
- /* NOTREACHED */
+ 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));
}
-#ifdef HESIOD
-static int _dns_grscan(void *, void *, va_list);
-/*ARGSUSED*/
static int
-_dns_grscan(rv, cb_data, ap)
- void *rv;
- void *cb_data;
- va_list ap;
+is_comment_line(const char *s, size_t n)
{
- int search = va_arg(ap, int);
- gid_t gid = va_arg(ap, gid_t);
- const char *name = va_arg(ap, const char *);
-
- char **hp;
- void *context;
- int r;
-
- r = NS_UNAVAIL;
- if (!search && _gr_hesnum == -1)
- return NS_NOTFOUND;
- if (hesiod_init(&context) == -1)
- return (r);
-
- for (;;) {
- if (search) {
- if (name)
- strlcpy(line, name, maxlinelength);
- else
- snprintf(line, maxlinelength, "%u",
- (unsigned int)gid);
- } else {
- snprintf(line, maxlinelength, "group-%u", _gr_hesnum);
- _gr_hesnum++;
- }
+ const char *eom;
- hp = hesiod_resolve(context, line, "group");
- if (hp == NULL) {
- if (errno == ENOENT) {
- if (!search)
- _gr_hesnum = -1;
- r = NS_NOTFOUND;
- }
- break;
- }
+ eom = &s[n];
- /* only check first elem */
- r = copyline(hp[0]);
- hesiod_free_list(context, hp);
- if (r == 0) {
- r = NS_UNAVAIL;
+ for (; s < eom; s++)
+ if (*s == '#' || !isspace((unsigned char)*s))
break;
- }
- if (matchline(search, gid, name)) {
- r = NS_SUCCESS;
- break;
- } else if (search) {
- r = NS_NOTFOUND;
- break;
- }
- }
- hesiod_end(context);
- return (r);
+ return (*s == '#' || s == eom);
}
-#endif
-#ifdef YP
-static int _nis_grscan(void *, void *, va_list);
-/*ARGSUSED*/
-static int
-_nis_grscan(rv, cb_data, ap)
- void *rv;
- void *cb_data;
- va_list ap;
+/*
+ * files backend
+ */
+static void
+files_endstate(void *p)
{
- int search = va_arg(ap, int);
- gid_t gid = va_arg(ap, gid_t);
- const char *name = va_arg(ap, const char *);
- char *key, *data;
- int keylen, datalen;
- int r;
+ if (p == NULL)
+ return;
+ if (((struct files_state *)p)->fp != NULL)
+ fclose(((struct files_state *)p)->fp);
+ free(p);
+}
- if(__ypdomain == NULL) {
- switch (yp_get_default_domain(&__ypdomain)) {
- case 0:
- break;
- case YPERR_RESRC:
- return NS_TRYAGAIN;
- default:
- return NS_UNAVAIL;
+
+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;
}
+ break;
+ default:
+ break;
}
+ return (NS_UNAVAIL);
+}
- if (search) { /* specific group or gid */
- if (name)
- strlcpy(line, name, maxlinelength);
- else
- snprintf(line, maxlinelength, "%u", (unsigned int)gid);
- data = NULL;
- r = yp_match(__ypdomain,
- (name) ? "group.byname" : "group.bygid",
- line, (int)strlen(line), &data, &datalen);
- switch (r) {
- case 0:
+
+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;
+ 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;
+ 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;
- case YPERR_KEY:
- if (data)
- free(data);
- return NS_NOTFOUND;
- default:
- if (data)
- free(data);
- return NS_UNAVAIL;
}
- data[datalen] = '\0'; /* clear trailing \n */
- r = copyline(data);
- free(data);
- if (r == 0)
- return NS_UNAVAIL;
- if (matchline(search, gid, name))
- return NS_SUCCESS;
- else
- return NS_NOTFOUND;
+ 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;
}
-
- /* ! search */
- if (_gr_ypdone)
- return NS_NOTFOUND;
- for (;;) {
- data = NULL;
- if(__ypcurrent) {
- key = NULL;
- r = yp_next(__ypdomain, "group.byname",
- __ypcurrent, __ypcurrentlen,
- &key, &keylen, &data, &datalen);
- free(__ypcurrent);
- switch (r) {
- case 0:
- break;
- case YPERR_NOMORE:
- __ypcurrent = NULL;
- if (key)
- free(key);
- if (data)
- free(data);
- _gr_ypdone = 1;
- return NS_NOTFOUND;
- default:
- if (key)
- free(key);
- if (data)
- free(data);
- return NS_UNAVAIL;
- }
- __ypcurrent = key;
- __ypcurrentlen = keylen;
- } else {
- if (yp_first(__ypdomain, "group.byname",
- &__ypcurrent, &__ypcurrentlen,
- &data, &datalen)) {
- if (data)
- free(data);
- return NS_UNAVAIL;
- }
- }
- data[datalen] = '\0'; /* clear trailing \n */
- r = copyline(data);
- free(data);
- if (r == 0)
- return NS_UNAVAIL;
- if (matchline(search, gid, name))
- return NS_SUCCESS;
+fin:
+ if (!stayopen && st->fp != NULL) {
+ fclose(st->fp);
+ st->fp = NULL;
}
- /* NOTREACHED */
+ if (rv == NS_SUCCESS && retval != NULL)
+ *(struct group **)retval = grp;
+ return (rv);
}
-#endif
-#ifdef _GROUP_COMPAT
+
+#ifdef HESIOD
/*
- * log an error if "files" or "compat" is specified in group_compat database
+ * dns backend
*/
-static int _bad_grscan(void *, void *, va_list);
+static void
+dns_endstate(void *p)
+{
+
+ free(p);
+}
+
-/*ARGSUSED*/
static int
-_bad_grscan(rv, cb_data, ap)
- void *rv;
- void *cb_data;
- va_list ap;
+dns_setgrent(void *retval, void *cb_data, va_list ap)
{
- static int warned;
+ struct dns_state *st;
+ int rv;
+
+ rv = dns_getstate(&st);
+ if (rv != 0)
+ return (NS_UNAVAIL);
+ st->counter = 0;
+ return (NS_UNAVAIL);
+}
+
- if (!warned) {
- syslog(LOG_ERR,
- "nsswitch.conf group_compat database can't use '%s'",
- (char *)cb_data);
+static int
+dns_group(void *retval, void *mdata, va_list ap)
+{
+ 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;
+
+ 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;
}
- warned = 1;
- return NS_UNAVAIL;
+ 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 */
+
+#ifdef YP
/*
- * when a name lookup in compat mode is required, look it up in group_compat
- * nsswitch database. only Hesiod and NIS is supported - it doesn't make
- * sense to lookup compat names from 'files' or 'compat'
+ * nis backend
*/
-
-static int __grscancompat(int, gid_t, const char *);
-
-static int
-__grscancompat(search, gid, name)
- int search;
- gid_t gid;
- const char *name;
+static void
+nis_endstate(void *p)
{
- static const ns_dtab dtab[] = {
- NS_FILES_CB(_bad_grscan, "files")
- NS_DNS_CB(_dns_grscan, NULL)
- NS_NIS_CB(_nis_grscan, NULL)
- NS_COMPAT_CB(_bad_grscan, "compat")
- { 0 }
- };
- static const ns_src defaultnis[] = {
- { NSSRC_NIS, NS_SUCCESS },
- { 0 }
- };
- return (nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "grscancompat",
- defaultnis, search, gid, name));
+ if (p == NULL)
+ return;
+ free(((struct nis_state *)p)->key);
+ free(p);
}
-#endif
-
-static int _compat_grscan(void *, void *, va_list);
-/*ARGSUSED*/
static int
-_compat_grscan(rv, cb_data, ap)
- void *rv;
- void *cb_data;
- va_list ap;
+nis_setgrent(void *retval, void *cb_data, va_list ap)
{
- int search = va_arg(ap, int);
- gid_t gid = va_arg(ap, gid_t);
- const char *name = va_arg(ap, const char *);
+ 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);
+}
-#ifdef _GROUP_COMPAT
- static char *grname = NULL;
-#endif
- for (;;) {
-#ifdef _GROUP_COMPAT
- if(__grmode != GRMODE_NONE) {
- int r;
-
- switch(__grmode) {
- case GRMODE_FULL:
- r = __grscancompat(search, gid, name);
- if (r == NS_SUCCESS)
- return r;
- __grmode = GRMODE_NONE;
- break;
- case GRMODE_NAME:
- if(grname == (char *)NULL) {
- __grmode = GRMODE_NONE;
- break;
- }
- r = __grscancompat(1, 0, grname);
- free(grname);
- grname = (char *)NULL;
- if (r != NS_SUCCESS)
- break;
- if (!search)
- return NS_SUCCESS;
- if (name) {
- if (! strcmp(_gr_group.gr_name, name))
- return NS_SUCCESS;
- } else {
- if (_gr_group.gr_gid == gid)
- return NS_SUCCESS;
- }
- break;
- case GRMODE_NONE:
- abort();
+static int
+nis_group(void *retval, void *mdata, va_list ap)
+{
+ 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;
+
+ 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;
+ }
+ 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);
+ }
+ }
+ 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;
}
- continue;
}
-#endif /* _GROUP_COMPAT */
+ /* 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 (getline() == NULL)
- return NS_NOTFOUND;
-#ifdef _GROUP_COMPAT
- if (line[0] == '+') {
- char *tptr, *bp;
- switch(line[1]) {
- case ':':
- case '\0':
- case '\n':
- __grmode = GRMODE_FULL;
- break;
- default:
- __grmode = GRMODE_NAME;
- bp = line;
- tptr = strsep(&bp, ":\n");
- grname = strdup(tptr + 1);
- break;
- }
- continue;
- }
-#endif /* _GROUP_COMPAT */
- if (matchline(search, gid, name))
- return NS_SUCCESS;
- }
- /* NOTREACHED */
+/*
+ * 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
-grscan(search, gid, name)
- int search;
- gid_t gid;
- const char *name;
+compat_setgrent(void *retval, void *mdata, va_list ap)
{
- int r;
- static const ns_dtab dtab[] = {
- NS_FILES_CB(_local_grscan, NULL)
- NS_DNS_CB(_dns_grscan, NULL)
- NS_NIS_CB(_nis_grscan, NULL)
- NS_COMPAT_CB(_compat_grscan, NULL)
- { 0 }
- };
- static const ns_src compatsrc[] = {
- { NSSRC_COMPAT, NS_SUCCESS },
- { 0 }
- };
-
- r = nsdispatch(NULL, dtab, NSDB_GROUP, "grscan", compatsrc,
- search, gid, name);
- return (r == NS_SUCCESS) ? 1 : 0;
+ struct compat_state *st;
+ int rv, stayopen;
+
+ 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");
+ break;
+ case ENDGRENT:
+ if (st->fp != NULL) {
+ fclose(st->fp);
+ st->fp = NULL;
+ }
+ break;
+ default:
+ break;
+ }
+ st->compat = COMPAT_MODE_OFF;
+ free(st->name);
+ st->name = NULL;
+ return (NS_UNAVAIL);
}
+
static int
-matchline(search, gid, name)
- int search;
- gid_t gid;
- const char *name;
+compat_group(void *retval, void *mdata, va_list ap)
{
- unsigned long id;
- char **m;
- char *cp, *bp, *ep;
-
- if (line[0] == '+')
- return 0; /* sanity check to prevent recursion */
- bp = line;
- _gr_group.gr_name = strsep(&bp, ":\n");
- if (search && name && strcmp(_gr_group.gr_name, name))
- return 0;
- _gr_group.gr_passwd = strsep(&bp, ":\n");
- if (!(cp = strsep(&bp, ":\n")))
- return 0;
- id = strtoul(cp, &ep, 10);
- if (*ep != '\0')
- return 0;
- _gr_group.gr_gid = (gid_t)id;
- if (search && name == NULL && _gr_group.gr_gid != gid)
- return 0;
- cp = NULL;
- if (bp == NULL)
- return 0;
- for (_gr_group.gr_mem = m = members;; bp++) {
- if (m == &members[maxgrp - 1]) {
- members = (char **) reallocf(members, sizeof(char **) *
- (maxgrp + MAXGRP));
- if (members == NULL)
- return 0;
- _gr_group.gr_mem = members;
- m = &members[maxgrp - 1];
- maxgrp += MAXGRP;
+ 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;
+ 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 (*bp == ',') {
- if (cp) {
- *bp = '\0';
- *m++ = cp;
- cp = NULL;
+ 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;
}
- } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
- if (cp) {
- *bp = '\0';
- *m++ = cp;
+ 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;
+ 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;
- } else if (cp == NULL)
- cp = bp;
- }
- *m = NULL;
- return 1;
+ }
+fin:
+ if (!stayopen && st->fp != NULL) {
+ fclose(st->fp);
+ st->fp = NULL;
+ }
+ if (rv == NS_SUCCESS && retval != NULL)
+ *(struct group **)retval = grp;
+ return (rv);
+#undef set_lookup_type
}
-static char *
-getline(void)
+
+/*
+ * 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)
{
- const char *cp;
-
- tryagain:
- if (fgets(line, maxlinelength, _gr_fp) == NULL)
- return NULL;
- if (index(line, '\n') == NULL) {
- do {
- if (feof(_gr_fp))
- return NULL;
- if (MAXLINELENGTHLIMIT > 0 &&
- maxlinelength >= MAXLINELENGTHLIMIT)
- return NULL;
- line = (char *)reallocf(line, maxlinelength +
- MAXLINELENGTH);
- if (line == NULL)
- return NULL;
- if (fgets(line + maxlinelength - 1,
- MAXLINELENGTH + 1, _gr_fp) == NULL)
- return NULL;
- maxlinelength += MAXLINELENGTH;
- } while (index(line + maxlinelength - MAXLINELENGTH - 1,
- '\n') == NULL);
+ 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;
}
-
-
- /*
- * Ignore comments: ^[ \t]*#
- */
- for (cp = line; *cp != '\0'; cp++)
- if (*cp != ' ' && *cp != '\t')
- break;
- if (*cp == '#' || *cp == '\0')
- goto tryagain;
-
- if (cp != line) /* skip white space at beginning of line */
- bcopy(cp, line, strlen(cp));
-
- return line;
+ 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);
}
-static int
-copyline(const char *src)
+
+int
+__gr_parse_entry(char *line, size_t linesize, struct group *grp, char *membuf,
+ size_t membufsize, int *errnop)
{
- size_t sz;
-
- sz = strlen(src);
- if (sz > maxlinelength - 1) {
- sz = ((sz/MAXLINELENGTH)+1) * MAXLINELENGTH;
- if ((line = (char *) reallocf(line, sz)) == NULL)
- return 0;
- maxlinelength = sz;
+ char *s_gid, *s_mem, **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;
+ if (s_mem[0] == '\0') {
+ *members = NULL;
+ return (NS_SUCCESS);
+ }
+ while (maxmembers > 1 && s_mem != NULL) {
+ *members++ = strsep(&s_mem, ",");
+ maxmembers--;
+ }
+ *members = NULL;
+ if (s_mem == NULL)
+ return (NS_SUCCESS);
+ else {
+ *errnop = ERANGE;
+ return (NS_RETURN);
}
- strlcpy(line, src, maxlinelength);
- return 1;
}
-
diff --git a/lib/libc/gen/getpwent.3 b/lib/libc/gen/getpwent.3
index 89662cc..d5eb9a1 100644
--- a/lib/libc/gen/getpwent.3
+++ b/lib/libc/gen/getpwent.3
@@ -32,13 +32,16 @@
.\" From: @(#)getpwent.3 8.2 (Berkeley) 12/11/93
.\" $FreeBSD$
.\"
-.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
@@ -50,11 +53,17 @@
.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 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
@@ -100,6 +109,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
@@ -142,9 +180,26 @@ 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.
+In the latter case,
+.Va errno
+will be set.
+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.
@@ -154,6 +209,23 @@ and
.Fn setpwent
functions
have no return value.
+.Sh ERRORS
+These routines may fail for any of the errors specified in
+.Xr open 2 ,
+.Xr dbopen 2 ,
+.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 FILES
.Bl -tag -width /etc/master.passwd -compact
.It Pa /etc/pwd.db
@@ -187,6 +259,25 @@ 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 .
+.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 COMPATIBILITY
The historic function
.Xr setpwfile 3 ,
@@ -206,6 +297,7 @@ will modify the same object.
.Pp
The functions
.Fn getpwent ,
+.Fn getpwent_r ,
.Fn endpwent ,
.Fn setpassent ,
and
@@ -214,7 +306,9 @@ are fairly useless in a networked environment and should be
avoided, if possible.
The
.Fn getpwent
-function
-makes no attempt to suppress duplicate information if multiple
+and
+.Fn getpwent_r
+functions
+make no attempt to suppress duplicate information if multiple
sources are specified in
.Xr nsswitch.conf 5 .
diff --git a/lib/libc/gen/getpwent.c b/lib/libc/gen/getpwent.c
index 63cec21..9f4c8a4 100644
--- a/lib/libc/gen/getpwent.c
+++ b/lib/libc/gen/getpwent.c
@@ -1,7 +1,12 @@
-/*
- * Copyright (c) 1988, 1993
- * The Regents of the University of California. All rights reserved.
- * Portions Copyright (c) 1994, 1995, 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
@@ -11,18 +16,11 @@
* 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)
@@ -30,1152 +28,1629 @@
* 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.
+ *
*/
-
-#if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)getpwent.c 8.2 (Berkeley) 4/27/95";
-#endif /* LIBC_SCCS and not lint */
-/* $NetBSD: getpwent.c,v 1.40.2.2 1999/04/27 22:09:45 perry Exp $ */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
-#include "un-namespace.h"
+#include "namespace.h"
#include <sys/param.h>
-#include <fcntl.h>
-#include <db.h>
-#include <syslog.h>
-#include <pwd.h>
-#include <utmp.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-#include <nsswitch.h>
-#include <netdb.h>
-#ifdef HESIOD
-#include <hesiod.h>
-#endif
#ifdef YP
-#include <machine/param.h>
-#include <stdio.h>
#include <rpc/rpc.h>
#include <rpcsvc/yp_prot.h>
#include <rpcsvc/ypclnt.h>
#endif
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HESIOD
+#include <hesiod.h>
+#endif
+#include <netdb.h>
+#include <nsswitch.h>
+#include <pthread.h>
+#include <pthread_np.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.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"
-#if defined(YP) || defined(HESIOD)
-#define _PASSWD_COMPAT
+#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
-/*
- * The lookup techniques and data extraction code here must be kept
- * in sync with that in `pwd_mkdb'.
- */
+/* 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_FILES, 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);
+
+union key {
+ const char *name;
+ uid_t uid;
+};
-static struct passwd _pw_passwd = { "", "", 0, 0, 0, "", "", "", "", 0, 0 };
-static DB *_pw_db; /* password database */
-static int _pw_keynum; /* key counter. no more records if -1 */
-static int _pw_stayopen; /* keep fd's open */
+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 */
+};
-static int __hashpw(DBT *);
-static int __initdb(void);
-static const ns_src compatsrc[] = {
- { NSSRC_COMPAT, NS_SUCCESS },
- { 0 }
+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
+
#ifdef YP
-static char *__ypcurrent, *__ypdomain;
-static int __ypcurrentlen;
-static int _pw_ypdone; /* non-zero if no more yp records */
+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
+
+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 *);
+void
+setpwent(void)
+{
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_setpwent, (void *)SETPWENT },
#ifdef HESIOD
-static int _pw_hesnum; /* hes counter. no more records if -1 */
+ { NSSRC_DNS, dns_setpwent, (void *)SETPWENT },
#endif
+#ifdef YP
+ { NSSRC_NIS, nis_setpwent, (void *)SETPWENT },
+#endif
+ { NSSRC_COMPAT, compat_setpwent, (void *)SETPWENT },
+ { NULL, NULL, NULL }
+ };
+ (void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc, 0);
+}
-#ifdef _PASSWD_COMPAT
-enum _pwmode { PWMODE_NONE, PWMODE_FULL, PWMODE_USER, PWMODE_NETGRP };
-static enum _pwmode __pwmode;
-
-enum _ypmap { YPMAP_NONE, YPMAP_ADJUNCT, YPMAP_MASTER };
-
-static struct passwd *__pwproto = (struct passwd *)NULL;
-static int __pwproto_flags;
-static char line[1024];
-static long prbuf[1024 / sizeof(long)];
-static DB *__pwexclude = (DB *)NULL;
-
-static int __pwexclude_add(const char *);
-static int __pwexclude_is(const char *);
-static void __pwproto_set(void);
-static int __ypmaptype(void);
-static int __pwparse(struct passwd *, char *);
-
- /* macros for deciding which YP maps to use. */
-#define PASSWD_BYNAME (__ypmaptype() == YPMAP_MASTER \
- ? "master.passwd.byname" : "passwd.byname")
-#define PASSWD_BYUID (__ypmaptype() == YPMAP_MASTER \
- ? "master.passwd.byuid" : "passwd.byuid")
-/*
- * add a name to the compat mode exclude list
- */
-static int
-__pwexclude_add(name)
- const char *name;
+int
+setpassent(int stayopen)
{
- DBT key;
- DBT data;
-
- /* initialize the exclusion table if needed. */
- if(__pwexclude == (DB *)NULL) {
- __pwexclude = dbopen(NULL, O_RDWR, 600, DB_HASH, NULL);
- if(__pwexclude == (DB *)NULL)
- return 1;
- }
+ 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 },
+ { NULL, NULL, NULL }
+ };
+ (void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc,
+ stayopen);
+ return (1);
+}
- /* set up the key */
- key.size = strlen(name);
- /* LINTED key does not get modified */
- key.data = (char *)name;
- /* data is nothing. */
- data.data = NULL;
- data.size = 0;
-
- /* store it */
- if((__pwexclude->put)(__pwexclude, &key, &data, 0) == -1)
- return 1;
-
- return 0;
+void
+endpwent(void)
+{
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_setpwent, (void *)ENDPWENT },
+#ifdef HESIOD
+ { NSSRC_DNS, dns_setpwent, (void *)ENDPWENT },
+#endif
+#ifdef YP
+ { NSSRC_NIS, nis_setpwent, (void *)ENDPWENT },
+#endif
+ { NSSRC_COMPAT, compat_setpwent, (void *)ENDPWENT },
+ { NULL, NULL, NULL }
+ };
+ (void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "endpwent", defaultsrc);
}
-/*
- * test if a name is on the compat mode exclude list
- */
-static int
-__pwexclude_is(name)
- const char *name;
-{
- DBT key;
- DBT data;
- if(__pwexclude == (DB *)NULL)
- return 0; /* nothing excluded */
+int
+getpwent_r(struct passwd *pwd, char *buffer, size_t bufsize,
+ struct passwd **result)
+{
+ 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 },
+ { NULL, NULL, NULL }
+ };
+ int rv, ret_errno;
+
+ 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);
+}
- /* set up the key */
- key.size = strlen(name);
- /* LINTED key does not get modified */
- key.data = (char *)name;
- if((__pwexclude->get)(__pwexclude, &key, &data, 0) == 0)
- return 1; /* excluded */
-
- return 0;
+int
+getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t bufsize,
+ struct passwd **result)
+{
+ 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 },
+ { NULL, NULL, NULL }
+ };
+ int rv, ret_errno;
+
+ 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);
}
-/*
- * Setup the compat mode prototype template that may be used in
- * __pwparse. Only pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, and
- * pw_shell are used. The other fields are zero'd.
- */
-static void
-__pwproto_set()
+
+int
+getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize,
+ struct passwd **result)
{
- char *ptr;
- struct passwd *pw = &_pw_passwd;
-
- /* make this the new prototype */
- ptr = (char *)(void *)prbuf;
-
- /* first allocate the struct. */
- __pwproto = (struct passwd *)(void *)ptr;
- ptr += sizeof(struct passwd);
- memset(__pwproto, 0, sizeof(*__pwproto));
-
- __pwproto_flags = 0;
-
- /* password */
- if(pw->pw_passwd && (pw->pw_passwd)[0]) {
- ptr = (char *)ALIGN((u_long)ptr);
- memmove(ptr, pw->pw_passwd, strlen(pw->pw_passwd) + 1);
- __pwproto->pw_passwd = ptr;
- ptr += (strlen(pw->pw_passwd) + 1);
- __pwproto_flags |= _PWF_PASSWD;
- }
+ 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 },
+ { NULL, NULL, NULL }
+ };
+ int rv, ret_errno;
+
+ 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);
+}
- /* uid, gid */
- if (pw->pw_fields & _PWF_UID) {
- __pwproto->pw_uid = pw->pw_uid;
- __pwproto_flags |= _PWF_UID;
- }
- if (pw->pw_fields & _PWF_GID) {
- __pwproto->pw_gid = pw->pw_gid;
- __pwproto_flags |= _PWF_GID;
- }
- /* gecos */
- if(pw->pw_gecos && (pw->pw_gecos)[0]) {
- ptr = (char *)ALIGN((u_long)ptr);
- memmove(ptr, pw->pw_gecos, strlen(pw->pw_gecos) + 1);
- __pwproto->pw_gecos = ptr;
- ptr += (strlen(pw->pw_gecos) + 1);
- __pwproto_flags |= _PWF_GECOS;
- }
-
- /* dir */
- if(pw->pw_dir && (pw->pw_dir)[0]) {
- ptr = (char *)ALIGN((u_long)ptr);
- memmove(ptr, pw->pw_dir, strlen(pw->pw_dir) + 1);
- __pwproto->pw_dir = ptr;
- ptr += (strlen(pw->pw_dir) + 1);
- __pwproto_flags |= _PWF_DIR;
- }
+static struct passwd pwd;
+static char *pwd_storage;
+static size_t pwd_storage_size;
+
- /* shell */
- if(pw->pw_shell && (pw->pw_shell)[0]) {
- ptr = (char *)ALIGN((u_long)ptr);
- memmove(ptr, pw->pw_shell, strlen(pw->pw_shell) + 1);
- __pwproto->pw_shell = ptr;
- ptr += (strlen(pw->pw_shell) + 1);
- __pwproto_flags |= _PWF_SHELL;
+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;
+ return (NULL);
+ }
+ pwd_storage_size <<= 1;
+ pwd_storage = malloc(pwd_storage_size);
+ if (pwd_storage == NULL)
+ return (NULL);
+ }
+ } while (res == NULL && rv == ERANGE);
+ return (res);
}
+
static int
-__ypmaptype()
+wrap_getpwnam_r(union key key, struct passwd *pwd, char *buffer,
+ size_t bufsize, struct passwd **res)
{
- static int maptype = -1;
- int order, r;
+ return (getpwnam_r(key.name, pwd, buffer, bufsize, res));
+}
- if (maptype != -1)
- return (maptype);
- maptype = YPMAP_NONE;
- if (geteuid() != 0)
- return (maptype);
+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 (!__ypdomain) {
- if( _yp_check(&__ypdomain) == 0)
- return (maptype);
- }
- r = yp_order(__ypdomain, "master.passwd.byname", &order);
- if (r == 0) {
- maptype = YPMAP_MASTER;
- return (maptype);
- }
+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));
+}
- /*
- * NIS+ in YP compat mode doesn't support
- * YPPROC_ORDER -- no point in continuing.
- */
- if (r == YPERR_YPERR)
- return (maptype);
-
- /* master.passwd doesn't exist -- try passwd.adjunct */
- if (r == YPERR_MAP) {
- r = yp_order(__ypdomain, "passwd.adjunct.byname", &order);
- if (r == 0)
- maptype = YPMAP_ADJUNCT;
- return (maptype);
- }
- return (maptype);
+struct passwd *
+getpwnam(const char *name)
+{
+ union key key;
+
+ key.name = name;
+ return (getpw(wrap_getpwnam_r, key));
+}
+
+
+struct passwd *
+getpwuid(uid_t uid)
+{
+ union key key;
+
+ key.uid = uid;
+ return (getpw(wrap_getpwuid_r, key));
}
+
+struct passwd *
+getpwent(void)
+{
+ union key key;
+
+ key.uid = 0; /* not used */
+ return (getpw(wrap_getpwent_r, key));
+}
+
+
/*
- * parse a passwd file line (from NIS or HESIOD).
- * assumed to be `old-style' if maptype != YPMAP_MASTER.
+ * files backend
*/
-static int
-__pwparse(pw, s)
- struct passwd *pw;
- char *s;
+static DB *
+pwdbopen(int *version)
{
- static char adjunctpw[YPMAXRECORD + 2];
- int flags, maptype;
-
- maptype = __ypmaptype();
- flags = 0;
- if (maptype == YPMAP_MASTER)
- flags |= _PWSCAN_MASTER;
- if (! __pw_scan(s, pw, flags))
- return 1;
-
- /* now let the prototype override, if set. */
- if(__pwproto != (struct passwd *)NULL) {
-#ifdef PW_OVERRIDE_PASSWD
- if(__pwproto_flags & _PWF_PASSWD)
- pw->pw_passwd = __pwproto->pw_passwd;
-#endif
- if(__pwproto_flags & _PWF_UID)
- pw->pw_uid = __pwproto->pw_uid;
- if(__pwproto_flags & _PWF_GID)
- pw->pw_gid = __pwproto->pw_gid;
- if(__pwproto_flags & _PWF_GECOS)
- pw->pw_gecos = __pwproto->pw_gecos;
- if(__pwproto_flags & _PWF_DIR)
- pw->pw_dir = __pwproto->pw_dir;
- if(__pwproto_flags & _PWF_SHELL)
- pw->pw_shell = __pwproto->pw_shell;
+ 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;
}
- if ((maptype == YPMAP_ADJUNCT) &&
- (strstr(pw->pw_passwd, "##") != NULL)) {
- char *data, *bp;
- int datalen;
-
- if (yp_match(__ypdomain, "passwd.adjunct.byname", pw->pw_name,
- (int)strlen(pw->pw_name), &data, &datalen) == 0) {
- if (datalen > sizeof(adjunctpw) - 1)
- datalen = sizeof(adjunctpw) - 1;
- strncpy(adjunctpw, data, (size_t)datalen);
-
- /* skip name to get password */
- if ((bp = strsep(&data, ":")) != NULL &&
- (bp = strsep(&data, ":")) != NULL)
- pw->pw_passwd = bp;
+ return (res);
+}
+
+
+static void
+files_endstate(void *p)
+{
+ DB *db;
+
+ if (p == NULL)
+ return;
+ db = ((struct files_state *)p)->db;
+ if (db != NULL)
+ db->close(db);
+ free(p);
+}
+
+
+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) {
+ (void)st->db->close(st->db);
+ st->db = NULL;
}
+ break;
+ default:
+ break;
}
- return 0;
+ return (NS_UNAVAIL);
}
-#endif /* _PASSWD_COMPAT */
-/*
- * local files implementation of getpw*()
- * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
- */
-static int _local_getpw(void *, void *, va_list);
-/*ARGSUSED*/
static int
-_local_getpw(rv, cb_data, ap)
- void *rv;
- void *cb_data;
- va_list ap;
+files_passwd(void *retval, void *mdata, va_list ap)
{
- DBT key;
- char bf[/*CONSTCOND*/ MAX(MAXLOGNAME, sizeof(_pw_keynum)) + 1];
- uid_t uid;
- int search, len, rval;
- const char *name;
+ 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;
- if (!_pw_db && !__initdb())
- return NS_UNAVAIL;
-
- search = va_arg(ap, int);
- bf[0] = search;
- switch (search) {
- case _PW_KEYBYNUM:
- if (_pw_keynum == -1)
- return NS_NOTFOUND; /* no more local records */
- ++_pw_keynum;
- memmove(bf + 1, &_pw_keynum, sizeof(_pw_keynum));
- key.size = sizeof(_pw_keynum) + 1;
- break;
- case _PW_KEYBYNAME:
+ name = NULL;
+ uid = (uid_t)-1;
+ how = (enum nss_lookup_type)mdata;
+ switch (how) {
+ case nss_lt_name:
name = va_arg(ap, const char *);
- len = strlen(name);
- if (len > sizeof(bf) - 1)
- return NS_NOTFOUND;
- memmove(bf + 1, name, len);
- key.size = len + 1;
+ keybuf[0] = _PW_KEYBYNAME;
break;
- case _PW_KEYBYUID:
+ case nss_lt_id:
uid = va_arg(ap, uid_t);
- memmove(bf + 1, &uid, sizeof(len));
- key.size = sizeof(uid) + 1;
+ keybuf[0] = _PW_KEYBYUID;
+ break;
+ case nss_lt_all:
+ keybuf[0] = _PW_KEYBYNUM;
break;
default:
- abort();
+ rv = NS_NOTFOUND;
+ goto fin;
}
-
- key.data = (u_char *)bf;
- rval = __hashpw(&key);
- if (rval == NS_NOTFOUND && search == _PW_KEYBYNUM)
- _pw_keynum = -1; /* flag `no more local records' */
-
- if (!_pw_stayopen && (search != _PW_KEYBYNUM)) {
- (void)(_pw_db->close)(_pw_db);
- _pw_db = (DB *)NULL;
+ 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 (rval == NS_SUCCESS) {
- _pw_passwd.pw_fields &= ~_PWF_SOURCE;
- _pw_passwd.pw_fields |= _PWF_FILES;
+ if (st->db == NULL &&
+ (st->db = pwdbopen(&st->version)) == NULL) {
+ *errnop = errno;
+ rv = NS_UNAVAIL;
+ goto fin;
}
- return (rval);
+ 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_VERSION(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) {
+ (void)st->db->close(st->db);
+ st->db = NULL;
+ }
+ if (rv == NS_SUCCESS && retval != NULL)
+ *(struct passwd **)retval = pwd;
+ return (rv);
+}
+
+
+static int
+pwdb_match_entry_v3(char *entry, size_t entrysize, enum nss_lookup_type how,
+ const char *name, uid_t uid)
+{
+ 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);
+}
+
+
+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;
+
+ memset(pwd, 0, sizeof(*pwd));
+ /* 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);
+}
+
+
+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
+pwdb_parse_entry_v4(char *buffer, size_t bufsize, struct passwd *pwd,
+ int *errnop)
+{
+ char *p, *eom;
+ uint32_t n;
+
+ memset(pwd, 0, sizeof(*pwd));
+ /* 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);
}
+
#ifdef HESIOD
/*
- * hesiod implementation of getpw*()
- * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
+ * dns backend
*/
-static int _dns_getpw(void *, void *, va_list);
+static void
+dns_endstate(void *p)
+{
+ free(p);
+}
+
-/*ARGSUSED*/
static int
-_dns_getpw(rv, cb_data, ap)
- void *rv;
- void *cb_data;
- va_list ap;
+dns_setpwent(void *retval, void *mdata, va_list ap)
{
- const char *name;
- uid_t uid;
- int search;
-
- const char *map;
- char **hp;
- void *context;
- int r;
-
- search = va_arg(ap, int);
- nextdnsbynum:
- switch (search) {
- case _PW_KEYBYNUM:
- if (_pw_hesnum == -1)
- return NS_NOTFOUND; /* no more hesiod records */
- snprintf(line, sizeof(line) - 1, "passwd-%u", _pw_hesnum);
- _pw_hesnum++;
- map = "passwd";
- break;
- case _PW_KEYBYNAME:
+ struct dns_state *st;
+ int rv;
+
+ rv = dns_getstate(&st);
+ if (rv != 0)
+ return (NS_UNAVAIL);
+ st->counter = 0;
+ return (NS_UNAVAIL);
+}
+
+
+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 *);
- strncpy(line, name, sizeof(line));
- map = "passwd";
break;
- case _PW_KEYBYUID:
+ case nss_lt_id:
uid = va_arg(ap, uid_t);
- snprintf(line, sizeof(line), "%u", (unsigned int)uid);
- map = "uid"; /* XXX this is `passwd' on ultrix */
break;
- default:
- abort();
+ case nss_lt_all:
+ break;
}
- line[sizeof(line) - 1] = '\0';
-
- r = NS_UNAVAIL;
- if (hesiod_init(&context) == -1)
- return (r);
-
- hp = hesiod_resolve(context, line, map);
- if (hp == NULL) {
- if (errno == ENOENT) {
- /* flag `no more hesiod records' */
- if (search == _PW_KEYBYNUM)
- _pw_hesnum = -1;
- r = NS_NOTFOUND;
- }
- goto cleanup_dns_getpw;
+ 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;
}
-
- strncpy(line, hp[0], sizeof(line)); /* only check first elem */
- line[sizeof(line) - 1] = '\0';
- hesiod_free_list(context, hp);
- if (__pwparse(&_pw_passwd, line)) {
- if (search == _PW_KEYBYNUM)
- goto nextdnsbynum; /* skip dogdy entries */
- r = NS_UNAVAIL;
- } else {
- _pw_passwd.pw_fields &= ~_PWF_SOURCE;
- _pw_passwd.pw_fields |= _PWF_HESIOD;
- r = NS_SUCCESS;
+ 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;
}
- cleanup_dns_getpw:
- hesiod_end(context);
- return (r);
+ return (rv);
}
-#endif
+#endif /* HESIOD */
+
#ifdef YP
/*
- * nis implementation of getpw*()
- * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
+ * nis backend
*/
-static int _nis_getpw(void *, void *, va_list);
+static void
+nis_endstate(void *p)
+{
+ free(((struct nis_state *)p)->key);
+ free(p);
+}
-/*ARGSUSED*/
static int
-_nis_getpw(rv, cb_data, ap)
- void *rv;
- void *cb_data;
- va_list ap;
+nis_map(char *domain, enum nss_lookup_type how, char *buffer, size_t bufsize,
+ int *master)
{
- const char *name;
- uid_t uid;
- int search;
- char *key, *data;
- char *map;
- int keylen, datalen, r, rval;
-
- if(__ypdomain == NULL) {
- if(_yp_check(&__ypdomain) == 0)
- return NS_UNAVAIL;
+ int rv, order;
+
+ *master = 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);
}
+ if (snprintf(buffer, bufsize, "passwd.by%s",
+ (how == nss_lt_id) ? "uid" : "name") >= bufsize)
+ return (NS_UNAVAIL);
+ rv = yp_order(domain, buffer, &order);
+ if (rv == 0)
+ return (NS_SUCCESS);
+ return (NS_UNAVAIL);
+}
- map = PASSWD_BYNAME;
- search = va_arg(ap, int);
- switch (search) {
- case _PW_KEYBYNUM:
- break;
- case _PW_KEYBYNAME:
+
+static int
+nis_adjunct(char *domain, const char *name, char *buffer, size_t bufsize)
+{
+ 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);
+}
+
+
+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);
+}
+
+
+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 *);
- strncpy(line, name, sizeof(line));
break;
- case _PW_KEYBYUID:
+ case nss_lt_id:
uid = va_arg(ap, uid_t);
- snprintf(line, sizeof(line), "%u", (unsigned int)uid);
- map = PASSWD_BYUID;
break;
- default:
- abort();
+ case nss_lt_all:
+ break;
}
- line[sizeof(line) - 1] = '\0';
- rval = NS_UNAVAIL;
- if (search != _PW_KEYBYNUM) {
- data = NULL;
- r = yp_match(__ypdomain, map, line, (int)strlen(line),
- &data, &datalen);
- if (r == YPERR_KEY)
- rval = NS_NOTFOUND;
- if (r != 0) {
- if (data)
- free(data);
- return (rval);
+ 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);
}
- data[datalen] = '\0'; /* clear trailing \n */
- strncpy(line, data, sizeof(line));
- line[sizeof(line) - 1] = '\0';
- free(data);
- if (__pwparse(&_pw_passwd, line))
- return NS_UNAVAIL;
- _pw_passwd.pw_fields &= ~_PWF_SOURCE;
- _pw_passwd.pw_fields |= _PWF_NIS;
- return NS_SUCCESS;
}
-
- if (_pw_ypdone)
- return NS_NOTFOUND;
- for (;;) {
- data = key = NULL;
- if (__ypcurrent) {
- r = yp_next(__ypdomain, map,
- __ypcurrent, __ypcurrentlen,
- &key, &keylen, &data, &datalen);
- free(__ypcurrent);
- switch (r) {
- case 0:
- __ypcurrent = key;
- __ypcurrentlen = keylen;
- break;
- case YPERR_NOMORE:
- __ypcurrent = NULL;
- /* flag `no more yp records' */
- _pw_ypdone = 1;
- rval = NS_NOTFOUND;
- }
- } else {
- r = yp_first(__ypdomain, map, &__ypcurrent,
- &__ypcurrentlen, &data, &datalen);
+ 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;
}
- if (r != 0) {
- if (key)
+ 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 (data)
- free(data);
- return (rval);
+ }
+ 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;
+ }
}
- data[datalen] = '\0'; /* clear trailing \n */
- strncpy(line, data, sizeof(line));
- line[sizeof(line) - 1] = '\0';
- free(data);
- if (! __pwparse(&_pw_passwd, line)) {
- _pw_passwd.pw_fields &= ~_PWF_SOURCE;
- _pw_passwd.pw_fields |= _PWF_NIS;
- return NS_SUCCESS;
+ 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, 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;
}
- /* NOTREACHED */
-} /* _nis_getpw */
-#endif
+ return (rv);
+erange:
+ *errnop = ERANGE;
+ return (NS_RETURN);
+}
+#endif /* YP */
+
-#ifdef _PASSWD_COMPAT
/*
- * See if the compat token is in the database. Only works if pwd_mkdb knows
- * about the token.
+ * compat backend
*/
-static int __has_compatpw(void);
-
-static int
-__has_compatpw()
+static void
+compat_clear_template(struct passwd *template)
{
- DBT key, data;
- DBT pkey, pdata;
- char bf[MAXLOGNAME];
- u_char cyp[] = { _PW_KEYYPENABLED };
-
- /*LINTED*/
- key.data = cyp;
- key.size = 1;
-
- /* Pre-token database support. */
- bf[0] = _PW_KEYBYNAME;
- bf[1] = '+';
- pkey.data = (u_char *)bf;
- pkey.size = 2;
-
- if ((_pw_db->get)(_pw_db, &key, &data, 0)
- && (_pw_db->get)(_pw_db, &pkey, &pdata, 0))
- return 0; /* No compat token */
- return 1;
+
+ free(template->pw_passwd);
+ free(template->pw_gecos);
+ free(template->pw_dir);
+ free(template->pw_shell);
+ memset(template, 0, sizeof(*template));
}
-/*
- * log an error if "files" or "compat" is specified in passwd_compat database
- */
-static int _bad_getpw(void *, void *, va_list);
-/*ARGSUSED*/
static int
-_bad_getpw(rv, cb_data, ap)
- void *rv;
- void *cb_data;
- va_list ap;
+compat_set_template(struct passwd *src, struct passwd *template)
{
- static int warned;
- if (!warned) {
- syslog(LOG_ERR,
- "nsswitch.conf passwd_compat database can't use '%s'",
- (char *)cb_data);
- }
- warned = 1;
- return NS_UNAVAIL;
+
+ 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);
}
-/*
- * when a name lookup in compat mode is required (e.g., '+name', or a name in
- * '+@netgroup'), look it up in the 'passwd_compat' nsswitch database.
- * only Hesiod and NIS is supported - it doesn't make sense to lookup
- * compat names from 'files' or 'compat'.
- */
-static int __getpwcompat(int, uid_t, const char *);
static int
-__getpwcompat(type, uid, name)
- int type;
- uid_t uid;
- const char *name;
+compat_use_template(struct passwd *pwd, struct passwd *template, char *buffer,
+ size_t bufsize)
{
- static const ns_dtab dtab[] = {
- NS_FILES_CB(_bad_getpw, "files")
- NS_DNS_CB(_dns_getpw, NULL)
- NS_NIS_CB(_nis_getpw, NULL)
- NS_COMPAT_CB(_bad_getpw, "compat")
- { 0 }
- };
- static const ns_src defaultnis[] = {
- { NSSRC_NIS, NS_SUCCESS },
- { 0 }
- };
+ struct passwd hold;
+ char *copy, *p, *q, *eob;
+ size_t n;
- switch (type) {
- case _PW_KEYBYNUM:
- return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "getpwcompat",
- defaultnis, type);
- case _PW_KEYBYNAME:
- return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "getpwcompat",
- defaultnis, type, name);
- case _PW_KEYBYUID:
- return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "getpwcompat",
- defaultnis, type, uid);
- default:
- abort();
- /*NOTREACHED*/
+ /* 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);
}
-#endif /* _PASSWD_COMPAT */
-/*
- * compat implementation of getpwent()
- * varargs (ignored):
- * type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
- */
-static int _compat_getpwent(void *, void *, va_list);
-/*ARGSUSED*/
static int
-_compat_getpwent(rv, cb_data, ap)
- void *rv;
- void *cb_data;
- va_list ap;
+compat_exclude(const char *name, DB **db)
{
- DBT key;
- int rval;
- char bf[sizeof(_pw_keynum) + 1];
-#ifdef _PASSWD_COMPAT
- static char *name = NULL;
- char *user, *host, *dom;
- int has_compatpw;
-#endif
+ DBT key, data;
- if (!_pw_db && !__initdb())
- return NS_UNAVAIL;
+ 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;
-#ifdef _PASSWD_COMPAT
- has_compatpw = __has_compatpw();
+ if ((*db)->put(*db, &key, &data, 0) == -1)
+ return (errno);
+ return (0);
+}
-again:
- if (has_compatpw && (__pwmode != PWMODE_NONE)) {
- int r;
- switch (__pwmode) {
- case PWMODE_FULL:
- r = __getpwcompat(_PW_KEYBYNUM, 0, NULL);
- if (r == NS_SUCCESS)
- return r;
- __pwmode = PWMODE_NONE;
- break;
+static int
+compat_is_excluded(const char *name, DB *db)
+{
+ DBT key, data;
- case PWMODE_NETGRP:
- r = getnetgrent(&host, &user, &dom);
- if (r == 0) { /* end of group */
- endnetgrent();
- __pwmode = PWMODE_NONE;
- break;
- }
- if (!user || !*user)
- break;
- r = __getpwcompat(_PW_KEYBYNAME, 0, user);
- if (r == NS_SUCCESS)
- return r;
- break;
+ if (db == NULL)
+ return (0);
+ key.size = strlen(name);
+ key.data = (char *)name;
+ return (db->get(db, &key, &data, 0) == 0);
+}
- case PWMODE_USER:
- if (name == NULL) {
- __pwmode = PWMODE_NONE;
- break;
- }
- r = __getpwcompat(_PW_KEYBYNAME, 0, name);
- free(name);
- name = NULL;
- if (r == NS_SUCCESS)
- return r;
- break;
- case PWMODE_NONE:
- abort();
- }
- goto again;
- }
+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
-
- if (_pw_keynum == -1)
- return NS_NOTFOUND; /* no more local records */
- ++_pw_keynum;
- bf[0] = _PW_KEYBYNUM;
- memmove(bf + 1, &_pw_keynum, sizeof(_pw_keynum));
- key.data = (u_char *)bf;
- key.size = sizeof(_pw_keynum) + 1;
- rval = __hashpw(&key);
- if (rval == NS_NOTFOUND)
- _pw_keynum = -1; /* flag `no more local records' */
- else if (rval == NS_SUCCESS) {
-#ifdef _PASSWD_COMPAT
- /* if we don't have YP at all, don't bother. */
- if (has_compatpw) {
- if(_pw_passwd.pw_name[0] == '+') {
- /* set the mode */
- switch(_pw_passwd.pw_name[1]) {
- case '\0':
- __pwmode = PWMODE_FULL;
- break;
- case '@':
- __pwmode = PWMODE_NETGRP;
- setnetgrent(_pw_passwd.pw_name + 2);
- break;
- default:
- __pwmode = PWMODE_USER;
- name = strdup(_pw_passwd.pw_name + 1);
- break;
- }
-
- /* save the prototype */
- __pwproto_set();
- goto again;
- } else if(_pw_passwd.pw_name[0] == '-') {
- /* an attempted exclusion */
- switch(_pw_passwd.pw_name[1]) {
- case '\0':
- break;
- case '@':
- setnetgrent(_pw_passwd.pw_name + 2);
- while(getnetgrent(&host, &user, &dom)) {
- if(user && *user)
- __pwexclude_add(user);
- }
- endnetgrent();
- break;
- default:
- __pwexclude_add(_pw_passwd.pw_name + 1);
- break;
- }
- goto again;
- }
- }
+ { 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:
+ 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 (rval);
+ return (NS_SUCCESS);
+}
+
+
+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);
}
-/*
- * compat implementation of getpwnam() and getpwuid()
- * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
- */
-static int _compat_getpw(void *, void *, va_list);
static int
-_compat_getpw(rv, cb_data, ap)
- void *rv;
- void *cb_data;
- va_list ap;
+compat_setpwent(void *retval, void *mdata, va_list ap)
{
-#ifdef _PASSWD_COMPAT
- DBT key;
- int search, rval, r, s, keynum;
- uid_t uid;
- char bf[sizeof(keynum) + 1];
- char *name, *host, *user, *dom;
-#endif
+ struct compat_state *st;
+ int rv, stayopen;
+
+ 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;
+ break;
+ case ENDPWENT:
+ if (st->db != NULL) {
+ (void)st->db->close(st->db);
+ st->db = NULL;
+ }
+ break;
+ default:
+ break;
+ }
+ return (NS_UNAVAIL);
+}
- if (!_pw_db && !__initdb())
- return NS_UNAVAIL;
- /*
- * If there isn't a compat token in the database, use files.
- */
-#ifdef _PASSWD_COMPAT
- if (! __has_compatpw())
-#endif
- return (_local_getpw(rv, cb_data, ap));
+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, stayopen, *errnop;
-#ifdef _PASSWD_COMPAT
- search = va_arg(ap, int);
- uid = 0;
name = NULL;
- rval = NS_NOTFOUND;
- switch (search) {
- case _PW_KEYBYNAME:
- name = va_arg(ap, char *);
+ uid = (uid_t)-1;
+ how = (enum nss_lookup_type)mdata;
+ switch (how) {
+ case nss_lt_name:
+ name = va_arg(ap, const char *);
break;
- case _PW_KEYBYUID:
+ case nss_lt_id:
uid = va_arg(ap, uid_t);
break;
+ case nss_lt_all:
+ break;
default:
- abort();
+ rv = NS_NOTFOUND;
+ goto fin;
}
-
- for (s = -1, keynum = 1 ; ; keynum++) {
- bf[0] = _PW_KEYBYNUM;
- memmove(bf + 1, &keynum, sizeof(keynum));
- key.data = (u_char *)bf;
- key.size = sizeof(keynum) + 1;
- if(__hashpw(&key) != NS_SUCCESS)
- break;
- switch(_pw_passwd.pw_name[0]) {
+ 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)
+ 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 {
+ store = htonl(st->keynum);
+ memcpy(&keybuf[1], &store, sizeof(store));
+ key.size = sizeof(store) + 1;
+ }
+ keybuf[0] = _PW_KEYBYNUM | _PW_VERSION(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 '+':
- /* save the prototype */
- __pwproto_set();
-
- switch(_pw_passwd.pw_name[1]) {
+ switch (pw_name[1]) {
case '\0':
- r = __getpwcompat(search, uid, name);
- if (r != NS_SUCCESS)
- continue;
+ st->compat = COMPAT_MODE_ALL;
break;
case '@':
-pwnam_netgrp:
-#if 0 /* XXX: is this a hangover from pre-nsswitch? */
- if(__ypcurrent) {
- free(__ypcurrent);
- __ypcurrent = NULL;
- }
-#endif
- if (s == -1) /* first time */
- setnetgrent(_pw_passwd.pw_name + 2);
- s = getnetgrent(&host, &user, &dom);
- if (s == 0) { /* end of group */
- endnetgrent();
- s = -1;
- continue;
- }
- if (!user || !*user)
- goto pwnam_netgrp;
-
- r = __getpwcompat(_PW_KEYBYNAME, 0, user);
-
- if (r == NS_UNAVAIL)
- return r;
- if (r == NS_NOTFOUND) {
- /*
- * just because this user is bad
- * it doesn't mean they all are.
- */
- goto pwnam_netgrp;
- }
+ setnetgrent(&pw_name[2]);
+ st->compat = COMPAT_MODE_NETGROUP;
break;
default:
- user = _pw_passwd.pw_name + 1;
- r = __getpwcompat(_PW_KEYBYNAME, 0, user);
-
- if (r == NS_UNAVAIL)
- return r;
- if (r == NS_NOTFOUND)
- continue;
- break;
+ 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(__pwexclude_is(_pw_passwd.pw_name)) {
- if(s == 1) /* inside netgroup */
- goto pwnam_netgrp;
- continue;
+ if (entry.size > bufsize) {
+ *errnop = ERANGE;
+ rv = NS_RETURN;
+ goto fin;
}
- break;
+ 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 '-':
- /* attempted exclusion */
- switch(_pw_passwd.pw_name[1]) {
+ switch (pw_name[1]) {
case '\0':
- break;
+ /* XXX Maybe syslog warning */
+ continue;
case '@':
- setnetgrent(_pw_passwd.pw_name + 2);
- while(getnetgrent(&host, &user, &dom)) {
- if(user && *user)
- __pwexclude_add(user);
+ setnetgrent(&pw_name[2]);
+ while (getnetgrent(&host, &user, &domain) !=
+ NULL) {
+ if (user != NULL && user[0] != '\0')
+ compat_exclude(user,
+ &st->exclude);
}
endnetgrent();
- break;
+ continue;
default:
- __pwexclude_add(_pw_passwd.pw_name + 1);
- break;
+ compat_exclude(&pw_name[1], &st->exclude);
+ continue;
}
break;
default:
- _pw_passwd.pw_fields &= ~_PWF_SOURCE;
- _pw_passwd.pw_fields |= _PWF_FILES;
+ break;
}
- if ((search == _PW_KEYBYNAME &&
- strcmp(_pw_passwd.pw_name, name) == 0)
- || (search == _PW_KEYBYUID && _pw_passwd.pw_uid == uid)) {
- rval = NS_SUCCESS;
+ 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;
}
- if(s == 1) /* inside netgroup */
- goto pwnam_netgrp;
- continue;
- }
- __pwproto = (struct passwd *)NULL;
-
- if (!_pw_stayopen) {
- (void)(_pw_db->close)(_pw_db);
- _pw_db = (DB *)NULL;
+ memcpy(buffer, entry.data, entry.size);
+ rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd,
+ errnop);
+ if (rv & NS_TERMINATE)
+ break;
}
- if(__pwexclude != (DB *)NULL) {
- (void)(__pwexclude->close)(__pwexclude);
- __pwexclude = (DB *)NULL;
+fin:
+ if (!stayopen && st->db != NULL) {
+ (void)st->db->close(st->db);
+ st->db = NULL;
}
- return rval;
-#endif /* _PASSWD_COMPAT */
+ if (rv == NS_SUCCESS && retval != NULL)
+ *(struct passwd **)retval = pwd;
+ return (rv);
}
-struct passwd *
-getpwent()
-{
- int r;
- static const ns_dtab dtab[] = {
- NS_FILES_CB(_local_getpw, NULL)
- NS_DNS_CB(_dns_getpw, NULL)
- NS_NIS_CB(_nis_getpw, NULL)
- NS_COMPAT_CB(_compat_getpwent, NULL)
- { 0 }
- };
-
- r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwent", compatsrc,
- _PW_KEYBYNUM);
- if (r != NS_SUCCESS)
- return (struct passwd *)NULL;
- return &_pw_passwd;
-}
-
-struct passwd *
-getpwnam(name)
- const char *name;
-{
- int r;
- static const ns_dtab dtab[] = {
- NS_FILES_CB(_local_getpw, NULL)
- NS_DNS_CB(_dns_getpw, NULL)
- NS_NIS_CB(_nis_getpw, NULL)
- NS_COMPAT_CB(_compat_getpw, NULL)
- { 0 }
- };
-
- if (name == NULL || name[0] == '\0')
- return (struct passwd *)NULL;
-
- r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwnam", compatsrc,
- _PW_KEYBYNAME, name);
- return (r == NS_SUCCESS ? &_pw_passwd : (struct passwd *)NULL);
-}
-
-struct passwd *
-getpwuid(uid)
- uid_t uid;
-{
- int r;
- static const ns_dtab dtab[] = {
- NS_FILES_CB(_local_getpw, NULL)
- NS_DNS_CB(_dns_getpw, NULL)
- NS_NIS_CB(_nis_getpw, NULL)
- NS_COMPAT_CB(_compat_getpw, NULL)
- { 0 }
- };
-
- r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwuid", compatsrc,
- _PW_KEYBYUID, uid);
- return (r == NS_SUCCESS ? &_pw_passwd : (struct passwd *)NULL);
-}
+/*
+ * common passwd line matching and parsing
+ */
int
-setpassent(stayopen)
- int stayopen;
-{
- _pw_keynum = 0;
- _pw_stayopen = stayopen;
-#ifdef YP
- __pwmode = PWMODE_NONE;
- if(__ypcurrent)
- free(__ypcurrent);
- __ypcurrent = NULL;
- _pw_ypdone = 0;
-#endif
-#ifdef HESIOD
- _pw_hesnum = 0;
-#endif
-#ifdef _PASSWD_COMPAT
- if(__pwexclude != (DB *)NULL) {
- (void)(__pwexclude->close)(__pwexclude);
- __pwexclude = (DB *)NULL;
- }
- __pwproto = (struct passwd *)NULL;
-#endif
- return 1;
-}
-
-void
-setpwent()
-{
- (void) setpassent(0);
-}
-
-void
-endpwent()
+__pw_match_entry(const char *entry, size_t entrysize, enum nss_lookup_type how,
+ const char *name, uid_t uid)
{
- _pw_keynum = 0;
- if (_pw_db) {
- (void)(_pw_db->close)(_pw_db);
- _pw_db = (DB *)NULL;
- }
-#ifdef _PASSWD_COMPAT
- __pwmode = PWMODE_NONE;
-#endif
-#ifdef YP
- if(__ypcurrent)
- free(__ypcurrent);
- __ypcurrent = NULL;
- _pw_ypdone = 0;
-#endif
-#ifdef HESIOD
- _pw_hesnum = 0;
-#endif
-#ifdef _PASSWD_COMPAT
- if(__pwexclude != (DB *)NULL) {
- (void)(__pwexclude->close)(__pwexclude);
- __pwexclude = (DB *)NULL;
+ 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);
}
- __pwproto = (struct passwd *)NULL;
-#endif
+ 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);
}
-static int
-__initdb()
-{
- static int warned;
- char *p;
-
-#ifdef _PASSWD_COMPAT
- __pwmode = PWMODE_NONE;
-#endif
- if (geteuid() == 0) {
- _pw_db = dbopen((p = _PATH_SMP_DB), O_RDONLY, 0, DB_HASH, NULL);
- if (_pw_db)
- return(1);
- }
- _pw_db = dbopen((p = _PATH_MP_DB), O_RDONLY, 0, DB_HASH, NULL);
- if (_pw_db)
- return 1;
- if (!warned)
- syslog(LOG_ERR, "%s: %m", p);
- warned = 1;
- return 0;
-}
-static int
-__hashpw(key)
- DBT *key;
+/* 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)
{
- char *p, *t;
- static u_int max;
- static char *buf;
- int32_t pw_change, pw_expire;
- DBT data;
-
- switch ((_pw_db->get)(_pw_db, key, &data, 0)) {
- case 0:
- break; /* found */
- case 1:
- return NS_NOTFOUND;
- case -1:
- return NS_UNAVAIL; /* error in db routines */
- default:
- abort();
- }
-
- p = (char *)data.data;
- if (data.size > max && !(buf = realloc(buf, (max += 1024))))
- return NS_UNAVAIL;
-
- /* THIS CODE MUST MATCH THAT IN pwd_mkdb. */
- t = buf;
-#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_change);
- EXPAND(_pw_passwd.pw_class);
- EXPAND(_pw_passwd.pw_gecos);
- EXPAND(_pw_passwd.pw_dir);
- EXPAND(_pw_passwd.pw_shell);
- SCALAR(pw_expire);
- SCALAR(_pw_passwd.pw_fields);
- _pw_passwd.pw_change = pw_change;
- _pw_passwd.pw_expire = pw_expire;
- return NS_SUCCESS;
+ memset(pwd, 0, sizeof(*pwd));
+ if (__pw_scan(buffer, pwd, master ? _PWSCAN_MASTER : 0) == 0)
+ return (NS_NOTFOUND);
+ else
+ return (NS_SUCCESS);
}
diff --git a/usr.sbin/pwd_mkdb/pwd_mkdb.c b/usr.sbin/pwd_mkdb/pwd_mkdb.c
index 0ef01c1..47f9ef6 100644
--- a/usr.sbin/pwd_mkdb/pwd_mkdb.c
+++ b/usr.sbin/pwd_mkdb/pwd_mkdb.c
@@ -47,6 +47,7 @@ static const char rcsid[] =
#include <sys/param.h>
#include <sys/stat.h>
+#include <arpa/inet.h>
#include <db.h>
#include <err.h>
@@ -66,6 +67,8 @@ static const char rcsid[] =
#define SECURE 2
#define PERM_INSECURE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
#define PERM_SECURE (S_IRUSR|S_IWUSR)
+#define LEGACY_VERSION _PW_VERSION(3)
+#define CURRENT_VERSION _PW_VERSION(4)
HASHINFO openinfo = {
4096, /* bsize */
@@ -94,6 +97,8 @@ static void usage(void);
int
main(int argc, char *argv[])
{
+ static char verskey[] = _PWD_VERSION_KEY;
+ char version = _PWD_CURRENT_VERSION;
DB *dp, *sdp, *pw_db;
DBT data, sdata, key;
FILE *fp, *oldfp;
@@ -101,6 +106,7 @@ main(int argc, char *argv[])
int ch, cnt, ypcnt, makeold, tfd, yp_enabled = 0;
unsigned int len;
int32_t pw_change, pw_expire;
+ uint32_t store;
const char *t;
char *p;
char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024];
@@ -223,7 +229,7 @@ main(int argc, char *argv[])
pw_db = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL);
if (!pw_db)
error(_MP_DB);
- buf[0] = _PW_KEYBYNAME;
+ buf[0] = _PW_KEYBYNAME | CURRENT_VERSION;
len = strlen(username);
/* Only check that username fits in buffer */
@@ -239,7 +245,7 @@ main(int argc, char *argv[])
while (*p++)
;
- buf[0] = _PW_KEYBYUID;
+ buf[0] = _PW_KEYBYUID | CURRENT_VERSION;
memmove(buf + 1, p, sizeof(int));
key.data = (u_char *)buf;
key.size = sizeof(int) + 1;
@@ -304,6 +310,19 @@ main(int argc, char *argv[])
* original file prepended by the _PW_KEYBYNUM character. (The special
* characters are prepended to ensure that the keys do not collide.)
*/
+ /* In order to transition this file into a machine-independent
+ * form, we have to change the format of entries. However, since
+ * older binaries will still expect the old MD format entries, we
+ * create * those as usual and use versioned tags for the new entries.
+ */
+ key.data = verskey;
+ key.size = sizeof(verskey)-1;
+ data.data = &version;
+ data.size = 1;
+ if ((dp->put)(dp, &key, &data, 0) == -1)
+ error("put");
+ if ((dp->put)(sdp, &key, &data, 0) == -1)
+ error("put");
ypcnt = 1;
data.data = (u_char *)buf;
sdata.data = (u_char *)sbuf;
@@ -315,6 +334,9 @@ main(int argc, char *argv[])
if (is_comment)
--cnt;
#define COMPACT(e) t = e; while ((*p++ = *t++));
+#define SCALAR(e) store = htonl((uint32_t)(e)); \
+ memmove(p, &store, sizeof(store)); \
+ p += sizeof(store);
if (!is_comment &&
(!username || (strcmp(username, pwd.pw_name) == 0))) {
pw_change = pwd.pw_change;
@@ -323,6 +345,97 @@ main(int argc, char *argv[])
p = buf;
COMPACT(pwd.pw_name);
COMPACT("*");
+ SCALAR(pwd.pw_uid);
+ SCALAR(pwd.pw_gid);
+ SCALAR(pwd.pw_change);
+ COMPACT(pwd.pw_class);
+ COMPACT(pwd.pw_gecos);
+ COMPACT(pwd.pw_dir);
+ COMPACT(pwd.pw_shell);
+ SCALAR(pwd.pw_expire);
+ SCALAR(pwd.pw_fields);
+ data.size = p - buf;
+
+ /* Create secure data. */
+ p = sbuf;
+ COMPACT(pwd.pw_name);
+ COMPACT(pwd.pw_passwd);
+ SCALAR(pwd.pw_uid);
+ SCALAR(pwd.pw_gid);
+ SCALAR(pwd.pw_change);
+ COMPACT(pwd.pw_class);
+ COMPACT(pwd.pw_gecos);
+ COMPACT(pwd.pw_dir);
+ COMPACT(pwd.pw_shell);
+ SCALAR(pwd.pw_expire);
+ SCALAR(pwd.pw_fields);
+ sdata.size = p - sbuf;
+
+ /* Store insecure by name. */
+ tbuf[0] = _PW_KEYBYNAME | CURRENT_VERSION;
+ len = strlen(pwd.pw_name);
+ memmove(tbuf + 1, pwd.pw_name, len);
+ key.size = len + 1;
+ if ((dp->put)(dp, &key, &data, method) == -1)
+ error("put");
+
+ /* Store insecure by number. */
+ tbuf[0] = _PW_KEYBYNUM | CURRENT_VERSION;
+ store = htonl(cnt);
+ memmove(tbuf + 1, &store, sizeof(store));
+ key.size = sizeof(store) + 1;
+ if ((dp->put)(dp, &key, &data, method) == -1)
+ error("put");
+
+ /* Store insecure by uid. */
+ tbuf[0] = _PW_KEYBYUID | CURRENT_VERSION;
+ store = htonl(pwd.pw_uid);
+ memmove(tbuf + 1, &store, sizeof(store));
+ key.size = sizeof(store) + 1;
+ if ((dp->put)(dp, &key, &data, methoduid) == -1)
+ error("put");
+
+ /* Store secure by name. */
+ tbuf[0] = _PW_KEYBYNAME | CURRENT_VERSION;
+ len = strlen(pwd.pw_name);
+ memmove(tbuf + 1, pwd.pw_name, len);
+ key.size = len + 1;
+ if ((sdp->put)(sdp, &key, &sdata, method) == -1)
+ error("put");
+
+ /* Store secure by number. */
+ tbuf[0] = _PW_KEYBYNUM | CURRENT_VERSION;
+ store = htonl(cnt);
+ memmove(tbuf + 1, &store, sizeof(store));
+ key.size = sizeof(store) + 1;
+ if ((sdp->put)(sdp, &key, &sdata, method) == -1)
+ error("put");
+
+ /* Store secure by uid. */
+ tbuf[0] = _PW_KEYBYUID | CURRENT_VERSION;
+ store = htonl(pwd.pw_uid);
+ memmove(tbuf + 1, &store, sizeof(store));
+ key.size = sizeof(store) + 1;
+ if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1)
+ error("put");
+
+ /* Store insecure and secure special plus and special minus */
+ if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') {
+ tbuf[0] = _PW_KEYYPBYNUM | CURRENT_VERSION;
+ store = htonl(ypcnt);
+ memmove(tbuf + 1, &store, sizeof(store));
+ ypcnt++;
+ key.size = sizeof(store) + 1;
+ if ((dp->put)(dp, &key, &data, method) == -1)
+ error("put");
+ if ((sdp->put)(sdp, &key, &sdata, method) == -1)
+ error("put");
+ }
+
+ /* Create insecure data. (legacy version) */
+ p = buf;
+ COMPACT(pwd.pw_name);
+ COMPACT("*");
memmove(p, &pwd.pw_uid, sizeof(pwd.pw_uid));
p += sizeof(int);
memmove(p, &pwd.pw_gid, sizeof(pwd.pw_gid));
@@ -339,7 +452,7 @@ main(int argc, char *argv[])
p += sizeof pwd.pw_fields;
data.size = p - buf;
- /* Create secure data. */
+ /* Create secure data. (legacy version) */
p = sbuf;
COMPACT(pwd.pw_name);
COMPACT(pwd.pw_passwd);
@@ -360,7 +473,7 @@ main(int argc, char *argv[])
sdata.size = p - sbuf;
/* Store insecure by name. */
- tbuf[0] = _PW_KEYBYNAME;
+ tbuf[0] = _PW_KEYBYNAME | LEGACY_VERSION;
len = strlen(pwd.pw_name);
memmove(tbuf + 1, pwd.pw_name, len);
key.size = len + 1;
@@ -368,21 +481,21 @@ main(int argc, char *argv[])
error("put");
/* Store insecure by number. */
- tbuf[0] = _PW_KEYBYNUM;
+ tbuf[0] = _PW_KEYBYNUM | LEGACY_VERSION;
memmove(tbuf + 1, &cnt, sizeof(cnt));
key.size = sizeof(cnt) + 1;
if ((dp->put)(dp, &key, &data, method) == -1)
error("put");
/* Store insecure by uid. */
- tbuf[0] = _PW_KEYBYUID;
+ tbuf[0] = _PW_KEYBYUID | LEGACY_VERSION;
memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
key.size = sizeof(pwd.pw_uid) + 1;
if ((dp->put)(dp, &key, &data, methoduid) == -1)
error("put");
/* Store secure by name. */
- tbuf[0] = _PW_KEYBYNAME;
+ tbuf[0] = _PW_KEYBYNAME | LEGACY_VERSION;
len = strlen(pwd.pw_name);
memmove(tbuf + 1, pwd.pw_name, len);
key.size = len + 1;
@@ -390,14 +503,14 @@ main(int argc, char *argv[])
error("put");
/* Store secure by number. */
- tbuf[0] = _PW_KEYBYNUM;
+ tbuf[0] = _PW_KEYBYNUM | LEGACY_VERSION;
memmove(tbuf + 1, &cnt, sizeof(cnt));
key.size = sizeof(cnt) + 1;
if ((sdp->put)(sdp, &key, &sdata, method) == -1)
error("put");
/* Store secure by uid. */
- tbuf[0] = _PW_KEYBYUID;
+ tbuf[0] = _PW_KEYBYUID | LEGACY_VERSION;
memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
key.size = sizeof(pwd.pw_uid) + 1;
if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1)
@@ -405,7 +518,7 @@ main(int argc, char *argv[])
/* Store insecure and secure special plus and special minus */
if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') {
- tbuf[0] = _PW_KEYYPBYNUM;
+ tbuf[0] = _PW_KEYYPBYNUM | LEGACY_VERSION;
memmove(tbuf + 1, &ypcnt, sizeof(cnt));
ypcnt++;
key.size = sizeof(cnt) + 1;
@@ -437,7 +550,7 @@ main(int argc, char *argv[])
if (yp_enabled) {
buf[0] = yp_enabled + 2;
data.size = 1;
- tbuf[0] = _PW_KEYYPENABLED;
+ tbuf[0] = _PW_KEYYPENABLED | LEGACY_VERSION;
key.size = 1;
if ((dp->put)(dp, &key, &data, method) == -1)
error("put");
OpenPOWER on IntegriCloud