Index: head/include/grp.h =================================================================== --- head/include/grp.h (revision 113595) +++ head/include/grp.h (revision 113596) @@ -1,84 +1,91 @@ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the 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 * 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 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)grp.h 8.2 (Berkeley) 1/21/94 * $FreeBSD$ */ #ifndef _GRP_H_ #define _GRP_H_ #include #include #define _PATH_GROUP "/etc/group" #ifndef _GID_T_DECLARED 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 */ gid_t gr_gid; /* group id */ char **gr_mem; /* group members */ }; __BEGIN_DECLS #if __BSD_VISIBLE || __POSIX_VISIBLE >= 200112 || __XSI_VISIBLE void endgrent(void); struct group *getgrent(void); #endif struct group *getgrgid(gid_t); struct group *getgrnam(const char *); #if __BSD_VISIBLE const char *group_from_gid(gid_t, int); #endif #if __BSD_VISIBLE || __POSIX_VISIBLE >= 200112 || __XSI_VISIBLE +/* XXX IEEE Std 1003.1, 2003 specifies `void setgrent(void)' */ int setgrent(void); +int getgrgid_r(gid_t, struct group *, char *, size_t, + struct group **); +int getgrnam_r(const char *, struct group *, char *, size_t, + struct group **); #endif #if __BSD_VISIBLE -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_ */ Index: head/include/pwd.h =================================================================== --- head/include/pwd.h (revision 113595) +++ head/include/pwd.h (revision 113596) @@ -1,137 +1,153 @@ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the 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 * 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 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)pwd.h 8.2 (Berkeley) 1/21/94 * $FreeBSD$ */ #ifndef _PWD_H_ #define _PWD_H_ #include #include #ifndef _GID_T_DECLARED typedef __gid_t gid_t; #define _GID_T_DECLARED #endif #ifndef _TIME_T_DECLARED typedef __time_t time_t; #define _TIME_T_DECLARED #endif #ifndef _UID_T_DECLARED 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" #define _PATH_MASTERPASSWD "/etc/master.passwd" #define _MASTERPASSWD "master.passwd" #define _PATH_MP_DB "/etc/pwd.db" #define _MP_DB "pwd.db" #define _PATH_SMP_DB "/etc/spwd.db" #define _SMP_DB "spwd.db" #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 */ #define _PASSWORD_LEN 128 /* max length, not counting NULL */ struct passwd { char *pw_name; /* user name */ char *pw_passwd; /* encrypted password */ uid_t pw_uid; /* user uid */ gid_t pw_gid; /* user gid */ time_t pw_change; /* password change time */ char *pw_class; /* user access class */ char *pw_gecos; /* Honeywell login info */ char *pw_dir; /* home directory */ char *pw_shell; /* default shell */ time_t pw_expire; /* account expiration */ int pw_fields; /* internal: fields filled in */ }; /* Mapping from fields to bits for pw_fields. */ #define _PWF(x) (1 << x) #define _PWF_NAME _PWF(0) #define _PWF_PASSWD _PWF(1) #define _PWF_UID _PWF(2) #define _PWF_GID _PWF(3) #define _PWF_CHANGE _PWF(4) #define _PWF_CLASS _PWF(5) #define _PWF_GECOS _PWF(6) #define _PWF_DIR _PWF(7) #define _PWF_SHELL _PWF(8) #define _PWF_EXPIRE _PWF(9) +/* XXX These flags are bogus. With nsswitch, there are many + * possible sources and they cannot be represented in a small integer. + */ #define _PWF_SOURCE 0x3000 #define _PWF_FILES 0x1000 #define _PWF_NIS 0x2000 #define _PWF_HESIOD 0x3000 __BEGIN_DECLS struct passwd *getpwnam(const char *); struct passwd *getpwuid(uid_t); #if __POSIX_VISIBLE >= 200112 || __XSI_VISIBLE >= 500 void endpwent(void); struct passwd *getpwent(void); void setpwent(void); -/* - * 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 __END_DECLS #endif /* !_PWD_H_ */ Index: head/lib/libc/gen/getgrent.3 =================================================================== --- head/lib/libc/gen/getgrent.3 (revision 113595) +++ head/lib/libc/gen/getgrent.3 (revision 113596) @@ -1,204 +1,283 @@ .\" Copyright (c) 1989, 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by the 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 .\" 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 .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" 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 .Sh LIBRARY .Lb libc .Sh SYNOPSIS .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 .Fn endgrent void .Sh DESCRIPTION These functions operate on the group database file .Pa /etc/group which is described in .Xr group 5 . Each line of the database is defined by the structure .Vt group found in the include file .Aq Pa grp.h : .Bd -literal -offset indent struct group { char *gr_name; /* group name */ char *gr_passwd; /* group password */ gid_t gr_gid; /* group id */ char **gr_mem; /* group members */ }; .Ed .Pp The functions .Fn getgrnam and .Fn getgrgid search the group database for the given group name pointed to by .Fa name or the group id pointed to by .Fa gid , respectively, returning the first one encountered. Identical group names or group gids may result in undefined behavior. .Pp The .Fn getgrent function sequentially reads the group database and is intended for programs that wish to step through the complete list of groups. .Pp -All three routines will open the group file for reading, if necessary. +The functions +.Fn getgrent_r , +.Fn getgrnam_r , +and +.Fn getgrgid_r +are thread-safe versions of +.Fn getgrent , +.Fn getgrnam , +and +.Fn getgrgid , +respectively. +The caller must provide storage for the results of the search in +the +.Fa grp , +.Fa buffer , +.Fa bufsize , +and +.Fa result +arguments. +When these functions are successful, the +.Fa grp +argument will be filled-in, and a pointer to that argument will be +stored in +.Fa result . +If an entry is not found or an error occurs, +.Fa result +will be set to +.Dv NULL . .Pp +These functions will open the group file for reading, if necessary. +.Pp The .Fn setgroupent function opens the file, or rewinds it if it is already open. If .Fa stayopen is non-zero, file descriptors are left open, significantly speeding functions subsequent calls. This functionality is unnecessary for .Fn getgrent as it doesn't close its file descriptors by default. It should also be noted that it is dangerous for long-running programs to use this functionality as the group file may be updated. .Pp The .Fn setgrent function is identical to .Fn setgroupent with an argument of zero. .Pp The .Fn endgrent function closes any open files. .Sh RETURN VALUES The functions .Fn getgrent , .Fn getgrnam , and .Fn getgrgid , -return a pointer to the group entry if successful; if end-of-file -is reached or an error occurs a null pointer is returned. +return a pointer to a group structure on success or +.Dv NULL +if the entry is not found or if an error occurs. +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 .Fn setgrent return the value 1 if successful, otherwise the value 0 is returned. The functions .Fn endgrent and .Fn setgrfile have no return value. .Sh FILES .Bl -tag -width /etc/group -compact .It Pa /etc/group group database file .El .Sh SEE ALSO .Xr getpwent 3 , .Xr group 5 , .Xr nsswitch.conf 5 , .Xr yp 8 .Sh HISTORY The functions .Fn endgrent , .Fn getgrent , .Fn getgrnam , .Fn getgrgid , and .Fn setgrent appeared in .At v7 . The functions .Fn setgrfile 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 , which allowed the specification of alternate password databases, has been deprecated and is no longer available. .Sh BUGS The functions .Fn getgrent , .Fn getgrnam , .Fn getgrgid , .Fn setgroupent and .Fn setgrent leave their results in an internal static object and return a pointer to that object. Subsequent calls to the same function will modify the same object. .Pp The functions .Fn getgrent , +.Fn getgrent_r , .Fn endgrent , .Fn setgroupent , and .Fn setgrent are fairly useless in a networked environment and should be avoided, if possible. The .Fn getgrent -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 . Index: head/lib/libc/gen/getgrent.c =================================================================== --- head/lib/libc/gen/getgrent.c (revision 113595) +++ head/lib/libc/gen/getgrent.c (revision 113596) @@ -1,708 +1,1122 @@ -/* - * 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 * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the 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) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * */ - -#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 __FBSDID("$FreeBSD$"); -#include - +#include "namespace.h" +#include +#ifdef YP +#include +#include +#include +#endif +#include #include +#ifdef HESIOD +#include +#endif #include -#include #include +#include +#include #include #include #include #include +#include +#include "un-namespace.h" +#include "libc_private.h" +#include "nss_tls.h" -#ifdef HESIOD -#include -#include -#endif -#ifdef YP -#include -#include -#include -#endif -#if defined(YP) || defined(HESIOD) -#define _GROUP_COMPAT -#endif +enum constants { + GRP_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ + GRP_STORAGE_MAX = 1 << 20, /* 1 MByte */ + SETGRENT = 1, + ENDGRENT = 2, + HESIOD_NAME_MAX = 256, +}; -static FILE *_gr_fp; -static struct group _gr_group; -static int _gr_stayopen; -static int _gr_filesdone; +static const ns_src defaultsrc[] = { + { NSSRC_FILES, NS_SUCCESS }, + { NULL, 0 } +}; -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); +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" +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 + + +#ifdef YP +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 -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 */ +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); -/* - * 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) +/* 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 + { NSSRC_DNS, dns_setgrent, (void *)SETGRENT }, +#endif #ifdef YP -static char *__ypcurrent, *__ypdomain; -static int __ypcurrentlen; -static int _gr_ypdone; + { 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); +} + +int +setgroupent(int stayopen) +{ + 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, + stayopen); + return (1); +} -struct group * -getgrent() + +void +endgrent(void) { - if ((!_gr_fp && !start_gr()) || !grscan(0, 0, NULL)) - return (NULL); - return &_gr_group; + 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 * -getgrnam(name) - const char *name; + +int +getgrent_r(struct group *grp, char *buffer, size_t bufsize, + struct group **result) { - int rval; + 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; - if (!start_gr()) - return NULL; - rval = grscan(1, 0, name); - if (!_gr_stayopen) - endgrent(); - return (rval) ? &_gr_group : NULL; + 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); } -struct group * -getgrgid(gid) - gid_t gid; + +int +getgrnam_r(const char *name, struct group *grp, char *buffer, size_t bufsize, + struct group **result) { - int rval; + 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 + { NSSRC_NIS, nis_group, (void *)nss_lt_name }, +#endif + { NSSRC_COMPAT, compat_group, (void *)nss_lt_name }, + { NULL, NULL, NULL } + }; + int rv, ret_errno; - if (!start_gr()) - return NULL; - rval = grscan(1, gid, NULL); - if (!_gr_stayopen) - endgrent(); - return (rval) ? &_gr_group : NULL; + 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); } -void -grcleanup() + +int +getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize, + struct group **result) { - _gr_filesdone = 0; -#ifdef YP - if (__ypcurrent) - free(__ypcurrent); - __ypcurrent = NULL; - _gr_ypdone = 0; -#endif + 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; + 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 (maxgrp == 0) { - if ((members = (char **) malloc(sizeof(char**) * - MAXGRP)) == NULL) - return 0; - maxgrp = MAXGRP; - } - 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 *); + union key key; - 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 */ + key.gid = gid; + return (getgr(wrap_getgrgid_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; +struct group * +getgrent(void) { - int search = va_arg(ap, int); - gid_t gid = va_arg(ap, gid_t); - const char *name = va_arg(ap, const char *); + union key key; - char **hp; - void *context; - int r; + key.gid = 0; /* not used */ + return (getgr(wrap_getgrent_r, key)); +} - 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++; - } +static int +is_comment_line(const char *s, size_t n) +{ + 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) +{ -/*ARGSUSED*/ + free(p); +} + + 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; - if (!warned) { - syslog(LOG_ERR, - "nsswitch.conf group_compat database can't use '%s'", - (char *)cb_data); + rv = dns_getstate(&st); + if (rv != 0) + return (NS_UNAVAIL); + st->counter = 0; + return (NS_UNAVAIL); +} + + +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 void +nis_endstate(void *p) +{ -static int __grscancompat(int, gid_t, const char *); + if (p == NULL) + return; + free(((struct nis_state *)p)->key); + free(p); +} + static int -__grscancompat(search, gid, name) - int search; - gid_t gid; - const char *name; +nis_setgrent(void *retval, void *cb_data, va_list ap) { - 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 } - }; + struct nis_state *st; + int rv; - return (nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "grscancompat", - defaultnis, search, gid, name)); + rv = nis_getstate(&st); + if (rv != 0) + return (NS_UNAVAIL); + st->done = 0; + free(st->key); + st->key = NULL; + return (NS_UNAVAIL); } -#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_group(void *retval, void *mdata, 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 *); + 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; + } + } + /* 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 */ -#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(); - } - continue; - } -#endif /* _GROUP_COMPAT */ +/* + * compat backend + */ +static void +compat_endstate(void *p) +{ + struct compat_state *st; - if (getline() == NULL) - return NS_NOTFOUND; + if (p == NULL) + return; + st = (struct compat_state *)p; + free(st->name); + if (st->fp != NULL) + fclose(st->fp); + free(p); +} -#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; +static int +compat_setgrent(void *retval, void *mdata, va_list ap) +{ + 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; } -#endif /* _GROUP_COMPAT */ - if (matchline(search, gid, name)) - return NS_SUCCESS; + break; + default: + break; } - /* NOTREACHED */ + st->compat = COMPAT_MODE_OFF; + free(st->name); + st->name = NULL; + return (NS_UNAVAIL); } + static int -grscan(search, gid, name) - int search; - gid_t gid; - const char *name; +compat_group(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 } +#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; - r = nsdispatch(NULL, dtab, NSDB_GROUP, "grscan", compatsrc, - search, gid, name); - return (r == NS_SUCCESS) ? 1 : 0; -} +#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) -static int -matchline(search, gid, name) - int search; - gid_t gid; - const char *name; -{ - 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; + 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; - } else if (cp == NULL) - cp = bp; - } - *m = NULL; - return 1; + } + 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; + } +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; + size_t namesize; + const char *p, *eol; + char *q; + unsigned long n; + int i, needed; - 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); + 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; + char *s_gid, *s_mem, **members; + unsigned long n; + int maxmembers; - sz = strlen(src); - if (sz > maxlinelength - 1) { - sz = ((sz/MAXLINELENGTH)+1) * MAXLINELENGTH; - if ((line = (char *) reallocf(line, sz)) == NULL) - return 0; - maxlinelength = sz; + 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); } - strlcpy(line, src, maxlinelength); - return 1; + 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); + } } - Index: head/lib/libc/gen/getpwent.3 =================================================================== --- head/lib/libc/gen/getpwent.3 (revision 113595) +++ head/lib/libc/gen/getpwent.3 (revision 113596) @@ -1,220 +1,314 @@ .\" Copyright (c) 1988, 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by the 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 .\" 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 .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" 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 .Nd password database operations .Sh LIBRARY .Lb libc .Sh SYNOPSIS .In sys/types.h .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 .Ft void .Fn endpwent void .Sh DESCRIPTION These functions operate on the password database file which is described in .Xr passwd 5 . Each entry in the database is defined by the structure .Vt passwd found in the include file .Aq Pa pwd.h : .Bd -literal -offset indent struct passwd { char *pw_name; /* user name */ char *pw_passwd; /* encrypted password */ uid_t pw_uid; /* user uid */ gid_t pw_gid; /* user gid */ time_t pw_change; /* password change time */ char *pw_class; /* user access class */ char *pw_gecos; /* Honeywell login info */ char *pw_dir; /* home directory */ char *pw_shell; /* default shell */ time_t pw_expire; /* account expiration */ int pw_fields; /* internal: fields filled in */ }; .Ed .Pp The functions .Fn getpwnam and .Fn getpwuid search the password database for the given login name or user uid, respectively, always returning the first one encountered. .Pp The .Fn getpwent 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 accomplishes two purposes. First, it causes .Fn getpwent to ``rewind'' to the beginning of the database. Additionally, if .Fa stayopen is non-zero, file descriptors are left open, significantly speeding up subsequent accesses for all of the routines. (This latter functionality is unnecessary for .Fn getpwent as it doesn't close its file descriptors by default.) .Pp It is dangerous for long-running programs to keep the file descriptors open as the database will become out of date if it is updated while the program is running. .Pp The .Fn setpwent function is identical to .Fn setpassent with an argument of zero. .Pp The .Fn endpwent function closes any open files. .Pp These routines have been written to ``shadow'' the password file, e.g.\& allow only certain programs to have access to the encrypted password. If the process which calls them has an effective uid of 0, the encrypted password will be returned, otherwise, the password field of the returned structure will point to the string .Ql * . .Sh RETURN VALUES The functions .Fn getpwent , .Fn getpwnam , and -.Fn getpwuid , +.Fn getpwuid return a valid pointer to a passwd structure on success -and a null pointer if end-of-file is reached or an error occurs. +or +.Dv NULL +if the entry is not found or if an error occurs. +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. The .Fn endpwent 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 The insecure password database file .It Pa /etc/spwd.db The secure password database file .It Pa /etc/master.passwd The current password file .It Pa /etc/passwd A Version 7 format password file .El .Sh SEE ALSO .Xr getlogin 2 , .Xr getgrent 3 , .Xr nsswitch.conf 5 , .Xr passwd 5 , .Xr pwd_mkdb 8 , .Xr vipw 8 , .Xr yp 8 .Sh HISTORY The .Fn getpwent , .Fn getpwnam , .Fn getpwuid , .Fn setpwent , and .Fn endpwent functions appeared in .At v7 . 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 , which allowed the specification of alternate password databases, has been deprecated and is no longer available. .Sh BUGS The functions .Fn getpwent , .Fn getpwnam , and .Fn getpwuid , leave their results in an internal static object and return a pointer to that object. Subsequent calls to the same function will modify the same object. .Pp The functions .Fn getpwent , +.Fn getpwent_r , .Fn endpwent , .Fn setpassent , and .Fn setpwent are fairly useless in a networked environment and should be avoided, if possible. The .Fn getpwent -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 . Index: head/lib/libc/gen/getpwent.c =================================================================== --- head/lib/libc/gen/getpwent.c (revision 113595) +++ head/lib/libc/gen/getpwent.c (revision 113596) @@ -1,1181 +1,1656 @@ -/* - * 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 * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the 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) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * */ - -#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 __FBSDID("$FreeBSD$"); -#include "un-namespace.h" +#include "namespace.h" #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HESIOD -#include -#endif #ifdef YP -#include -#include #include #include #include #endif +#include +#include +#include +#ifdef HESIOD +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "un-namespace.h" +#include #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; -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 */ +CTASSERT(MAXLOGNAME > sizeof(uid_t)); +CTASSERT(MAXLOGNAME > sizeof(pwkeynum)); -static int __hashpw(DBT *); -static int __initdb(void); +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 compatsrc[] = { - { NSSRC_COMPAT, NS_SUCCESS }, - { 0 } +static const ns_src defaultsrc[] = { + { NSSRC_FILES, NS_SUCCESS }, + { NULL, 0 } }; -#ifdef YP -static char *__ypcurrent, *__ypdomain; -static int __ypcurrentlen; -static int _pw_ypdone; /* non-zero if no more yp records */ -#endif +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 *getpw(int (*fn)(union key, struct passwd *, char *, + size_t, struct passwd **), union key); +static int wrap_getpwnam_r(union key, struct passwd *, char *, + size_t, struct passwd **); +static int wrap_getpwuid_r(union key, struct passwd *, char *, size_t, + struct passwd **); +static int wrap_getpwent_r(union key, struct passwd *, char *, size_t, + struct passwd **); + +static int pwdb_match_entry_v3(char *, size_t, enum nss_lookup_type, + const char *, uid_t); +static int pwdb_parse_entry_v3(char *, size_t, struct passwd *, int *); +static int pwdb_match_entry_v4(char *, size_t, enum nss_lookup_type, + const char *, uid_t); +static int pwdb_parse_entry_v4(char *, size_t, struct passwd *, int *); + + +struct { + int (*match)(char *, size_t, enum nss_lookup_type, const char *, + uid_t); + int (*parse)(char *, size_t, struct passwd *, int *); +} pwdb_versions[] = { + { NULL, NULL }, /* version 0 */ + { NULL, NULL }, /* version 1 */ + { NULL, NULL }, /* version 2 */ + { pwdb_match_entry_v3, pwdb_parse_entry_v3 }, /* version 3 */ + { pwdb_match_entry_v4, pwdb_parse_entry_v4 }, /* version 4 */ +}; + + +struct files_state { + DB *db; + pwkeynum keynum; + int stayopen; + int version; +}; +static void files_endstate(void *); +NSS_TLS_HANDLING(files); +static DB *pwdbopen(int *); +static void files_endstate(void *); +static int files_setpwent(void *, void *, va_list); +static int files_passwd(void *, void *, va_list); + + #ifdef HESIOD -static int _pw_hesnum; /* hes counter. no more records if -1 */ +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 _PASSWD_COMPAT -enum _pwmode { PWMODE_NONE, PWMODE_FULL, PWMODE_USER, PWMODE_NETGRP }; -static enum _pwmode __pwmode; -enum _ypmap { YPMAP_NONE, YPMAP_ADJUNCT, YPMAP_MASTER }; +#ifdef YP +struct nis_state { + char domain[MAXHOSTNAMELEN]; + int done; + char *key; + int keylen; +}; +static void nis_endstate(void *); +NSS_TLS_HANDLING(nis); +static int nis_setpwent(void *, void *, va_list); +static int nis_passwd(void *, void *, va_list); +static int nis_map(char *, enum nss_lookup_type, char *, size_t, int *); +static int nis_adjunct(char *, const char *, char *, size_t); +#endif -static struct passwd *__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; +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) { - DBT key; - DBT data; + 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, 0); +} - /* 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; - } - /* set up the key */ - key.size = strlen(name); - /* LINTED key does not get modified */ - key.data = (char *)name; +int +setpassent(int stayopen) +{ + 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); +} - /* 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; + +int +getpwent_r(struct passwd *pwd, char *buffer, size_t bufsize, + struct passwd **result) { - DBT key; - DBT data; + 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; - if(__pwexclude == (DB *)NULL) - return 0; /* nothing excluded */ + 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; + 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; - /* make this the new prototype */ - ptr = (char *)(void *)prbuf; + 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); +} - /* first allocate the struct. */ - __pwproto = (struct passwd *)(void *)ptr; - ptr += sizeof(struct passwd); - memset(__pwproto, 0, sizeof(*__pwproto)); - __pwproto_flags = 0; +static struct passwd pwd; +static char *pwd_storage; +static size_t pwd_storage_size; - /* 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; - } - /* 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; - } +static struct passwd * +getpw(int (*fn)(union key, struct passwd *, char *, size_t, struct passwd **), + union key key) +{ + int rv; + struct passwd *res; - /* 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; + if (pwd_storage == NULL) { + pwd_storage = malloc(PWD_STORAGE_INITIAL); + if (pwd_storage == NULL) + return (NULL); + pwd_storage_size = PWD_STORAGE_INITIAL; } - - /* 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; - } + 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); +} - /* 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 int +wrap_getpwnam_r(union key key, struct passwd *pwd, char *buffer, + size_t bufsize, struct passwd **res) +{ + return (getpwnam_r(key.name, pwd, buffer, bufsize, res)); } + static int -__ypmaptype() +wrap_getpwuid_r(union key key, struct passwd *pwd, char *buffer, + size_t bufsize, struct passwd **res) { - static int maptype = -1; - int order, r; + return (getpwuid_r(key.uid, pwd, buffer, bufsize, res)); +} - if (maptype != -1) - return (maptype); - maptype = YPMAP_NONE; - if (geteuid() != 0) - 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)); +} - 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); - } +struct passwd * +getpwnam(const char *name) +{ + union key key; - /* - * NIS+ in YP compat mode doesn't support - * YPPROC_ORDER -- no point in continuing. - */ - if (r == YPERR_YPERR) - return (maptype); + key.name = name; + return (getpw(wrap_getpwnam_r, key)); +} - /* 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 * +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; + DB *res; + DBT key, entry; + int rv; - 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; + 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; + return (res); +} - 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; +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; + struct dns_state *st; + int rv; - const char *map; - char **hp; - void *context; - int r; + rv = dns_getstate(&st); + if (rv != 0) + return (NS_UNAVAIL); + st->counter = 0; + return (NS_UNAVAIL); +} - 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: + +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; + pwd = va_arg(ap, struct passwd *); + buffer = va_arg(ap, char *); + bufsize = va_arg(ap, size_t); + errnop = va_arg(ap, int *); + *errnop = dns_getstate(&st); + if (*errnop != 0) + return (NS_UNAVAIL); + if (hesiod_init(&ctx) != 0) { + *errnop = errno; + rv = NS_UNAVAIL; + goto fin; + } + do { + rv = NS_NOTFOUND; + switch (how) { + case nss_lt_name: + label = name; + break; + case nss_lt_id: + if (snprintf(buf, sizeof(buf), "%lu", + (unsigned long)uid) >= sizeof(buf)) + goto fin; + label = buf; + break; + case nss_lt_all: + if (st->counter < 0) + goto fin; + if (snprintf(buf, sizeof(buf), "passwd-%ld", + st->counter++) >= sizeof(buf)) + goto fin; + label = buf; + break; } - goto cleanup_dns_getpw; + 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; } - - 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; - } - 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; + int rv, order; - if(__ypdomain == NULL) { - if(_yp_check(&__ypdomain) == 0) - return NS_UNAVAIL; + *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; + rv = nis_map(st->domain, how, map, sizeof(map), &master); + if (rv != NS_SUCCESS) + return (rv); + result = NULL; + do { + rv = NS_NOTFOUND; + switch (how) { + case nss_lt_name: + if (strlcpy(buffer, name, bufsize) >= bufsize) + goto erange; + break; + case nss_lt_id: + if (snprintf(buffer, bufsize, "%lu", + (unsigned long)uid) >= bufsize) + goto erange; + break; + case nss_lt_all: + if (st->done) + goto fin; + break; + } + result = NULL; + if (how == nss_lt_all) { + if (st->key == NULL) + rv = yp_first(st->domain, map, &st->key, + &st->keylen, &result, &resultlen); + else { + key = st->key; + keylen = st->keylen; + st->key = NULL; + rv = yp_next(st->domain, map, key, keylen, + &st->key, &st->keylen, &result, + &resultlen); + free(key); } + if (rv != 0) { + free(result); + free(st->key); + st->key = NULL; + if (rv == YPERR_NOMORE) + st->done = 1; + else + rv = NS_UNAVAIL; + goto fin; + } } else { - r = yp_first(__ypdomain, map, &__ypcurrent, - &__ypcurrentlen, &data, &datalen); + rv = yp_match(st->domain, map, buffer, strlen(buffer), + &result, &resultlen); + if (rv == YPERR_KEY) { + rv = NS_NOTFOUND; + continue; + } else if (rv != 0) { + free(result); + rv = NS_UNAVAIL; + continue; + } } - if (r != 0) { - if (key) - free(key); - if (data) - free(data); - return (rval); + 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]; } - 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; - } + 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 void +compat_clear_template(struct passwd *template) +{ + free(template->pw_passwd); + free(template->pw_gecos); + free(template->pw_dir); + free(template->pw_shell); + memset(template, 0, sizeof(*template)); +} + + static int -__has_compatpw() +compat_set_template(struct passwd *src, struct passwd *template) { - DBT key, data; - DBT pkey, pdata; - char bf[MAXLOGNAME]; - u_char cyp[] = { _PW_KEYYPENABLED }; - /*LINTED*/ - key.data = cyp; - key.size = 1; + 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); +} - /* 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; +static int +compat_use_template(struct passwd *pwd, struct passwd *template, char *buffer, + size_t bufsize) +{ + struct passwd hold; + char *copy, *p, *q, *eob; + size_t n; + + /* 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 = ©[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); } -/* - * 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_exclude(const char *name, DB **db) { - 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; + DBT key, data; + + if (*db == NULL && + (*db = dbopen(NULL, O_RDWR, 600, DB_HASH, 0)) == NULL) + return (errno); + key.size = strlen(name); + key.data = (char *)name; + data.size = 0; + data.data = NULL; + + if ((*db)->put(*db, &key, &data, 0) == -1) + return (errno); + return (0); } -/* - * 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_is_excluded(const char *name, DB *db) { - 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 } - }; + DBT key, data; - 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*/ - } + if (db == NULL) + return (0); + key.size = strlen(name); + key.data = (char *)name; + return (db->get(db, &key, &data, 0) == 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_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) { - DBT key; - int rval; - char bf[sizeof(_pw_keynum) + 1]; -#ifdef _PASSWD_COMPAT - static char *name = NULL; - char *user, *host, *dom; - int has_compatpw; + static const ns_src compatsrc[] = { +#ifdef YP + { NSSRC_NIS, NS_SUCCESS }, #endif + { NULL, 0 } + }; + ns_dtab dtab[] = { +#ifdef YP + { NSSRC_NIS, nis_passwd, NULL }, +#endif +#ifdef HESIOD + { NSSRC_DNS, dns_passwd, NULL }, +#endif + { NULL, NULL, NULL } + }; + void *discard; + int rv, e, i; - if (!_pw_db && !__initdb()) - return NS_UNAVAIL; + 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 (NS_SUCCESS); +} -#ifdef _PASSWD_COMPAT - has_compatpw = __has_compatpw(); -again: - if (has_compatpw && (__pwmode != PWMODE_NONE)) { - int r; +static void +compat_endstate(void *p) +{ + struct compat_state *st; - switch (__pwmode) { - case PWMODE_FULL: - r = __getpwcompat(_PW_KEYBYNUM, 0, NULL); - if (r == NS_SUCCESS) - return r; - __pwmode = PWMODE_NONE; - break; + 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); +} - 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; - 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; +static int +compat_setpwent(void *retval, void *mdata, va_list ap) +{ + struct compat_state *st; + int rv, stayopen; - case PWMODE_NONE: - abort(); + 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; } - goto again; + break; + default: + break; } -#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; - } - } -#endif - } - return (rval); + return (NS_UNAVAIL); } -/* - * 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_passwd(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 + 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; - 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)); - -#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; + memcpy(buffer, entry.data, entry.size); + rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd, + errnop); + if (rv & NS_TERMINATE) + break; } - __pwproto = (struct passwd *)NULL; - - if (!_pw_stayopen) { - (void)(_pw_db->close)(_pw_db); - _pw_db = (DB *)NULL; +fin: + if (!stayopen && st->db != NULL) { + (void)st->db->close(st->db); + st->db = NULL; } - if(__pwexclude != (DB *)NULL) { - (void)(__pwexclude->close)(__pwexclude); - __pwexclude = (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_match_entry(const char *entry, size_t entrysize, enum nss_lookup_type how, + const char *name, uid_t uid) { - _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; -} + const char *p, *eom; + char *q; + size_t len; + unsigned long m; -void -setpwent() -{ - (void) setpassent(0); -} - -void -endpwent() -{ - _pw_keynum = 0; - if (_pw_db) { - (void)(_pw_db->close)(_pw_db); - _pw_db = (DB *)NULL; + 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); } -#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; - } - __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); } Index: head/usr.sbin/pwd_mkdb/pwd_mkdb.c =================================================================== --- head/usr.sbin/pwd_mkdb/pwd_mkdb.c (revision 113595) +++ head/usr.sbin/pwd_mkdb/pwd_mkdb.c (revision 113596) @@ -1,622 +1,735 @@ /*- * Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the 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 * 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 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1991, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)pwd_mkdb.c 8.5 (Berkeley) 4/20/94"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include "pw_scan.h" #define INSECURE 1 #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 */ 32, /* ffactor */ 256, /* nelem */ 2048 * 1024, /* cachesize */ NULL, /* hash() */ 0 /* lorder */ }; static enum state { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean; static struct passwd pwd; /* password structure */ static char *pname; /* password file name */ static char prefix[MAXPATHLEN]; static int is_comment; /* flag for comments */ static char line[LINE_MAX]; void cleanup(void); void error(const char *); void cp(char *, char *, mode_t mode); void mv(char *, char *); int scan(FILE *, struct passwd *); 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; sigset_t set; 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]; char sbuf[MAX(MAXPATHLEN, LINE_MAX * 2)]; char buf2[MAXPATHLEN]; char sbuf2[MAXPATHLEN]; char *username; u_int method, methoduid; int Cflag; int nblock = 0; Cflag = 0; strcpy(prefix, _PATH_PWD); makeold = 0; username = NULL; while ((ch = getopt(argc, argv, "Cd:ps:u:vN")) != -1) switch(ch) { case 'C': /* verify only */ Cflag = 1; break; case 'd': strncpy(prefix, optarg, sizeof prefix - 1); break; case 'p': /* create V7 "file.orig" */ makeold = 1; break; case 's': /* change default cachesize */ openinfo.cachesize = atoi(optarg) * 1024 * 1024; break; case 'u': /* only update this record */ username = optarg; break; case 'v': /* backward compatible */ break; case 'N': /* do not wait for lock */ nblock = LOCK_NB; break; default: usage(); } argc -= optind; argv += optind; if (argc != 1 || (username && (*username == '+' || *username == '-'))) usage(); /* * This could be changed to allow the user to interrupt. * Probably not worth the effort. */ sigemptyset(&set); sigaddset(&set, SIGTSTP); sigaddset(&set, SIGHUP); sigaddset(&set, SIGINT); sigaddset(&set, SIGQUIT); sigaddset(&set, SIGTERM); (void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL); /* We don't care what the user wants. */ (void)umask(0); pname = *argv; /* * Open and lock the original password file. We have to check * the hardlink count after we get the lock to handle any potential * unlink/rename race. * * This lock is necessary when someone runs pwd_mkdb manually, directly * on master.passwd, to handle the case where a user might try to * change his password while pwd_mkdb is running. */ for (;;) { struct stat st; if (!(fp = fopen(pname, "r"))) error(pname); if (flock(fileno(fp), LOCK_EX|nblock) < 0) error("flock"); if (fstat(fileno(fp), &st) < 0) error(pname); if (st.st_nlink != 0) break; fclose(fp); fp = NULL; } /* check only if password database is valid */ if (Cflag) { for (cnt = 1; scan(fp, &pwd); ++cnt); exit(0); } /* Open the temporary insecure password database. */ (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB); (void)snprintf(sbuf, sizeof(sbuf), "%s/%s.tmp", prefix, _SMP_DB); if (username) { (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB); (void)snprintf(sbuf2, sizeof(sbuf2), "%s/%s", prefix, _SMP_DB); clean = FILE_INSECURE; cp(buf2, buf, PERM_INSECURE); dp = dbopen(buf, O_RDWR|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo); if (dp == NULL) error(buf); clean = FILE_SECURE; cp(sbuf2, sbuf, PERM_SECURE); sdp = dbopen(sbuf, O_RDWR|O_EXCL, PERM_SECURE, DB_HASH, &openinfo); if (sdp == NULL) error(sbuf); /* * Do some trouble to check if we should store this users * uid. Don't use getpwnam/getpwuid as that interferes * with NIS. */ 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 */ memmove(buf + 1, username, MIN(len, sizeof(buf) - 1)); key.data = (u_char *)buf; key.size = len + 1; if ((pw_db->get)(pw_db, &key, &data, 0) == 0) { p = (char *)data.data; /* jump over pw_name and pw_passwd, to get to pw_uid */ while (*p++) ; 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; if ((pw_db->get)(pw_db, &key, &data, 0) == 0) { /* First field of data.data holds pw_pwname */ if (!strcmp(data.data, username)) methoduid = 0; else methoduid = R_NOOVERWRITE; } else { methoduid = R_NOOVERWRITE; } } else { methoduid = R_NOOVERWRITE; } if ((pw_db->close)(pw_db)) error("close pw_db"); method = 0; } else { dp = dbopen(buf, O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo); if (dp == NULL) error(buf); clean = FILE_INSECURE; sdp = dbopen(sbuf, O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo); if (sdp == NULL) error(sbuf); clean = FILE_SECURE; method = R_NOOVERWRITE; methoduid = R_NOOVERWRITE; } /* * Open file for old password file. Minor trickiness -- don't want to * chance the file already existing, since someone (stupidly) might * still be using this for permission checking. So, open it first and * fdopen the resulting fd. The resulting file should be readable by * everyone. */ if (makeold) { (void)snprintf(buf, sizeof(buf), "%s.orig", pname); if ((tfd = open(buf, O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0) error(buf); if ((oldfp = fdopen(tfd, "w")) == NULL) error(buf); clean = FILE_ORIG; } /* * The databases actually contain three copies of the original data. * Each password file entry is converted into a rough approximation * of a ``struct passwd'', with the strings placed inline. This * object is then stored as the data for three separate keys. The * first key * is the pw_name field prepended by the _PW_KEYBYNAME * character. The second key is the pw_uid field prepended by the * _PW_KEYBYUID character. The third key is the line number in the * 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; key.data = (u_char *)tbuf; for (cnt = 1; scan(fp, &pwd); ++cnt) { if (!is_comment && (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-')) yp_enabled = 1; 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; pw_expire = pwd.pw_expire; /* Create insecure data. */ 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)); p += sizeof(int); memmove(p, &pw_change, sizeof(pw_change)); p += sizeof(pw_change); COMPACT(pwd.pw_class); COMPACT(pwd.pw_gecos); COMPACT(pwd.pw_dir); COMPACT(pwd.pw_shell); memmove(p, &pw_expire, sizeof(pw_expire)); p += sizeof(pw_expire); memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields); 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); memmove(p, &pwd.pw_uid, sizeof(pwd.pw_uid)); p += sizeof(int); memmove(p, &pwd.pw_gid, sizeof(pwd.pw_gid)); p += sizeof(int); memmove(p, &pw_change, sizeof(pw_change)); p += sizeof(pw_change); COMPACT(pwd.pw_class); COMPACT(pwd.pw_gecos); COMPACT(pwd.pw_dir); COMPACT(pwd.pw_shell); memmove(p, &pw_expire, sizeof(pw_expire)); p += sizeof(pw_expire); memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields); p += sizeof pwd.pw_fields; 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; if ((dp->put)(dp, &key, &data, method) == -1) 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; if ((sdp->put)(sdp, &key, &sdata, method) == -1) 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) error("put"); /* 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; if ((dp->put)(dp, &key, &data, method) == -1) error("put"); if ((sdp->put)(sdp, &key, &sdata, method) == -1) error("put"); } } /* Create original format password file entry */ if (is_comment && makeold){ /* copy comments */ if (fprintf(oldfp, "%s\n", line) < 0) error("write old"); } else if (makeold) { char uidstr[20]; char gidstr[20]; snprintf(uidstr, sizeof(uidstr), "%u", pwd.pw_uid); snprintf(gidstr, sizeof(gidstr), "%u", pwd.pw_gid); if (fprintf(oldfp, "%s:*:%s:%s:%s:%s:%s\n", pwd.pw_name, pwd.pw_fields & _PWF_UID ? uidstr : "", pwd.pw_fields & _PWF_GID ? gidstr : "", pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell) < 0) error("write old"); } } /* If YP enabled, set flag. */ 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"); if ((sdp->put)(sdp, &key, &data, method) == -1) error("put"); } if ((dp->close)(dp) == -1) error("close"); if ((sdp->close)(sdp) == -1) error("close"); if (makeold) { (void)fflush(oldfp); if (fclose(oldfp) == EOF) error("close old"); } /* Set master.passwd permissions, in case caller forgot. */ (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR); /* Install as the real password files. */ (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB); (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB); mv(buf, buf2); (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB); (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _SMP_DB); mv(buf, buf2); if (makeold) { (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _PASSWD); (void)snprintf(buf, sizeof(buf), "%s.orig", pname); mv(buf, buf2); } /* * Move the master password LAST -- chpass(1), passwd(1) and vipw(8) * all use flock(2) on it to block other incarnations of themselves. * The rename means that everything is unlocked, as the original file * can no longer be accessed. */ (void)snprintf(buf, sizeof(buf), "%s/%s", prefix, _MASTERPASSWD); mv(pname, buf); /* * Close locked password file after rename() */ if (fclose(fp) == EOF) error("close fp"); exit(0); } int scan(fp, pw) FILE *fp; struct passwd *pw; { static int lcnt; char *p; if (!fgets(line, sizeof(line), fp)) return (0); ++lcnt; /* * ``... if I swallow anything evil, put your fingers down my * throat...'' * -- The Who */ if (!(p = strchr(line, '\n'))) { /* * XXX: This may also happen if the last line in a * file does not have a trailing newline. */ warnx("line #%d too long", lcnt); goto fmt; } *p = '\0'; /* * Ignore comments: ^[ \t]*# */ for (p = line; *p != '\0'; p++) if (*p != ' ' && *p != '\t') break; if (*p == '#' || *p == '\0') { is_comment = 1; return(1); } else is_comment = 0; if (!__pw_scan(line, pw, _PWSCAN_WARN|_PWSCAN_MASTER)) { warnx("at line #%d", lcnt); fmt: errno = EFTYPE; /* XXX */ error(pname); } return (1); } void cp(from, to, mode) char *from, *to; mode_t mode; { static char buf[MAXBSIZE]; int from_fd, rcount, to_fd, wcount; if ((from_fd = open(from, O_RDONLY, 0)) < 0) error(from); if ((to_fd = open(to, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) error(to); while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { wcount = write(to_fd, buf, rcount); if (rcount != wcount || wcount == -1) { int sverrno = errno; (void)snprintf(buf, sizeof(buf), "%s to %s", from, to); errno = sverrno; error(buf); } } if (rcount < 0) { int sverrno = errno; (void)snprintf(buf, sizeof(buf), "%s to %s", from, to); errno = sverrno; error(buf); } } void mv(from, to) char *from, *to; { char buf[MAXPATHLEN]; if (rename(from, to)) { int sverrno = errno; (void)snprintf(buf, sizeof(buf), "%s to %s", from, to); errno = sverrno; error(buf); } } void error(name) const char *name; { warn("%s", name); cleanup(); exit(1); } void cleanup() { char buf[MAXPATHLEN]; switch(clean) { case FILE_ORIG: (void)snprintf(buf, sizeof(buf), "%s.orig", pname); (void)unlink(buf); /* FALLTHROUGH */ case FILE_SECURE: (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB); (void)unlink(buf); /* FALLTHROUGH */ case FILE_INSECURE: (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB); (void)unlink(buf); } } static void usage() { (void)fprintf(stderr, "usage: pwd_mkdb [-C] [-N] [-p] [-d ] [-s ] [-u ] file\n"); exit(1); }