Index: head/lib/libc/gen/Symbol.map =================================================================== --- head/lib/libc/gen/Symbol.map (revision 336745) +++ head/lib/libc/gen/Symbol.map (revision 336746) @@ -1,551 +1,552 @@ /* * $FreeBSD$ */ FBSD_1.0 { __xuname; pthread_atfork; pthread_attr_destroy; pthread_attr_getdetachstate; pthread_attr_getguardsize; pthread_attr_getinheritsched; pthread_attr_getschedparam; pthread_attr_getschedpolicy; pthread_attr_getscope; pthread_attr_getstackaddr; pthread_attr_getstacksize; pthread_attr_init; pthread_attr_setdetachstate; pthread_attr_setguardsize; pthread_attr_setinheritsched; pthread_attr_setschedparam; pthread_attr_setschedpolicy; pthread_attr_setscope; pthread_attr_setstackaddr; pthread_attr_setstacksize; pthread_cancel; pthread_cleanup_pop; pthread_cleanup_push; pthread_cond_broadcast; pthread_cond_destroy; pthread_cond_init; pthread_cond_signal; pthread_cond_timedwait; pthread_cond_wait; pthread_detach; pthread_equal; pthread_exit; pthread_getspecific; pthread_join; pthread_key_create; pthread_key_delete; pthread_kill; pthread_main_np; pthread_mutex_destroy; pthread_mutex_init; pthread_mutex_lock; pthread_mutex_trylock; pthread_mutex_unlock; pthread_mutexattr_destroy; pthread_mutexattr_init; pthread_mutexattr_settype; pthread_once; pthread_rwlock_destroy; pthread_rwlock_init; pthread_rwlock_rdlock; pthread_rwlock_tryrdlock; pthread_rwlock_trywrlock; pthread_rwlock_unlock; pthread_rwlock_wrlock; pthread_self; pthread_setcancelstate; pthread_setcanceltype; pthread_setspecific; pthread_sigmask; pthread_testcancel; alarm; arc4random; arc4random_addrandom; arc4random_stir; __assert; check_utility_compat; clock; closedir; confstr; ctermid; ctermid_r; daemon; getdiskbyname; dladdr; dlclose; dlerror; dlfunc; dllockinit; dlopen; dlsym; dlvsym; dlinfo; dl_iterate_phdr; drand48; erand48; err_set_file; err_set_exit; err; verr; errc; verrc; errx; verrx; warn; vwarn; warnc; vwarnc; warnx; vwarnx; sys_errlist; sys_nerr; errno; exect; execl; execle; execlp; execv; execvp; execvP; fmtcheck; fmtmsg; fnmatch; __fpclassifyf; __fpclassifyd; __fpclassifyl; frexp; setfstab; getfstab; getfsent; getfsspec; getfsfile; setfsent; endfsent; ftok; getbootfile; getbsize; cgetset; cgetcap; cgetent; cgetmatch; cgetfirst; cgetclose; cgetnext; cgetstr; cgetustr; cgetnum; getcwd; getdomainname; setgrent; setgroupent; endgrent; getgrent_r; getgrnam_r; getgrgid_r; getgrnam; getgrgid; getgrent; /* * Why are __gr_parse_entry() and __gr_match_entry() not static in * gen/getgrent.c? */ getgrouplist; gethostname; getloadavg; getlogin; getlogin_r; setnetgrent; getnetgrent; endnetgrent; innetgr; getosreldate; getpagesize; getpeereid; _getprogname; getprogname; setpwent; setpassent; endpwent; getpwent_r; getpwnam_r; getpwuid_r; getpwnam; getpwuid; getpwent; getttynam; getttyent; setttyent; endttyent; isdialuptty; isnettty; getusershell; endusershell; setusershell; getvfsbyname; __isnan; isnan; __isnanf; isnanf; __isinf; isinf; __isinff; __isinfl; isatty; initgroups; jrand48; lcong48; ldexp; lockf; lrand48; modf; mrand48; nice; nlist; nrand48; opendir; pause; posix_madvise; popen; pclose; psignal; raise; readpassphrase; getpass; rewinddir; seed48; seekdir; user_from_uid; group_from_gid; setdomainname; sethostname; longjmperror; getmode; setmode; setproctitle; setprogname; siginterrupt; sys_signame; sys_siglist; sys_nsig; signal; sigaddset; sigdelset; sigemptyset; sigfillset; sigismember; sleep; srand48; fstatvfs; statvfs; sl_init; sl_add; sl_free; sl_find; fflagstostr; strtofflags; sysconf; sysctl; sysctlbyname; sysctlnametomib; syslog; vsyslog; openlog; closelog; setlogmask; ttyname_r; ttyname; timezone; times; time; telldir; tcgetattr; tcsetattr; tcsetpgrp; tcgetpgrp; cfgetospeed; cfgetispeed; cfsetospeed; cfsetispeed; cfsetspeed; cfmakeraw; tcsendbreak; _init_tls; __tls_get_addr; tcdrain; tcflush; tcflow; ualarm; ulimit; uname; strunvis; strunvisx; usleep; utime; valloc; vis; strvis; strvisx; wait; wait3; waitpid; wordexp; wordfree; }; FBSD_1.1 { arc4random_buf; arc4random_uniform; fdevname; fdevname_r; fdopendir; feature_present; posix_spawn; posix_spawn_file_actions_addclose; posix_spawn_file_actions_adddup2; posix_spawn_file_actions_addopen; posix_spawn_file_actions_destroy; posix_spawn_file_actions_init; posix_spawnattr_destroy; posix_spawnattr_getflags; posix_spawnattr_getpgroup; posix_spawnattr_getschedparam; posix_spawnattr_getschedpolicy; posix_spawnattr_getsigdefault; posix_spawnattr_getsigmask; posix_spawnattr_init; posix_spawnattr_setflags; posix_spawnattr_setpgroup; posix_spawnattr_setschedparam; posix_spawnattr_setschedpolicy; posix_spawnattr_setsigdefault; posix_spawnattr_setsigmask; posix_spawnp; semctl; tcgetsid; tcsetsid; __pthread_cleanup_pop_imp; __pthread_cleanup_push_imp; }; FBSD_1.2 { cfmakesane; endutxent; getpagesizes; getutxent; getutxid; getutxline; getutxuser; pututxline; sem_close; sem_destroy; sem_getvalue; sem_init; sem_open; sem_post; sem_timedwait; sem_trywait; sem_unlink; sem_wait; setutxdb; setutxent; }; FBSD_1.3 { clock_getcpuclockid; dirfd; dup3; fdclosedir; fdlopen; __FreeBSD_libc_enter_restricted_mode; getcontextx; gid_from_group; nvis; pwcache_userdb; pwcache_groupdb; snvis; strenvisx; strnunvis; strnunvisx; strnvis; strnvisx; strsenvisx; strsnvis; strsnvisx; strsvis; strsvisx; svis; uid_from_user; unvis; waitid; }; FBSD_1.4 { getnetgrent_r; pthread_mutex_consistent; pthread_mutexattr_getrobust; pthread_mutexattr_setrobust; stravis; }; FBSD_1.5 { alphasort; basename; daemonfd; devname; devname_r; dirname; elf_aux_info; fts_children; fts_close; fts_get_clientptr; fts_get_stream; fts_open; fts_read; fts_set; fts_set_clientptr; ftw; getentropy; getmntinfo; glob; globfree; nftw; readdir; readdir_r; scandir; scandir_b; sem_clockwait_np; setproctitle_fast; }; FBSDprivate_1.0 { /* needed by thread libraries */ __thr_jtable; _pthread_atfork; _pthread_attr_destroy; _pthread_attr_getdetachstate; _pthread_attr_getguardsize; _pthread_attr_getinheritsched; _pthread_attr_getschedparam; _pthread_attr_getschedpolicy; _pthread_attr_getscope; _pthread_attr_getstackaddr; _pthread_attr_getstacksize; _pthread_attr_init; _pthread_attr_setdetachstate; _pthread_attr_setguardsize; _pthread_attr_setinheritsched; _pthread_attr_setschedparam; _pthread_attr_setschedpolicy; _pthread_attr_setscope; _pthread_attr_setstackaddr; _pthread_attr_setstacksize; _pthread_cancel; _pthread_cancel_enter; _pthread_cancel_leave; _pthread_cleanup_pop; _pthread_cleanup_push; _pthread_cond_broadcast; _pthread_cond_destroy; _pthread_cond_init; _pthread_cond_signal; _pthread_cond_timedwait; _pthread_cond_wait; _pthread_detach; _pthread_equal; _pthread_exit; _pthread_getspecific; _pthread_join; _pthread_key_create; _pthread_key_delete; _pthread_kill; _pthread_main_np; _pthread_mutex_destroy; _pthread_mutex_init_calloc_cb; _pthread_mutex_init; _pthread_mutex_lock; _pthread_mutex_trylock; _pthread_mutex_unlock; _pthread_mutexattr_destroy; _pthread_mutexattr_init; _pthread_mutexattr_settype; _pthread_once; _pthread_rwlock_destroy; _pthread_rwlock_init; _pthread_rwlock_rdlock; _pthread_rwlock_tryrdlock; _pthread_rwlock_trywrlock; _pthread_rwlock_unlock; _pthread_rwlock_wrlock; _pthread_self; _pthread_setcancelstate; _pthread_setcanceltype; _pthread_setspecific; _pthread_sigmask; _pthread_testcancel; _spinlock; _spinunlock; _rtld_addr_phdr; _rtld_atfork_pre; _rtld_atfork_post; _rtld_error; /* for private use */ _rtld_get_stack_prot; _rtld_is_dlopened; _rtld_thread_init; /* for private use */ __elf_phdr_match_addr; _err; _warn; __fmtcheck; /* __pw_match_entry; */ /* __pw_parse_entry; */ __fdnlist; /* used by libkvm */ /* __aout_fdnlist; */ /* __elf_is_okay__; */ /* __elf_fdnlist; */ __opendir2; __pause; _pause; + __pw_initpwd; /* Used by (at least) libutil */ __pw_scan; /* Used by (at least) libutil */ __raise; _raise; __sleep; _sleep; _rtld_allocate_tls; _rtld_free_tls; #if defined(i386) ___libc_tls_get_addr; /* x86 only */ #endif __libc_tls_get_addr; __tcdrain; _tcdrain; __usleep; _usleep; __wait; _wait; __waitpid; _waitpid; _libc_sem_init_compat; _libc_sem_destroy_compat; _libc_sem_open_compat; _libc_sem_close_compat; _libc_sem_unlink_compat; _libc_sem_wait_compat; _libc_sem_trywait_compat; _libc_sem_timedwait_compat; _libc_sem_post_compat; _libc_sem_getvalue_compat; __libc_tcdrain; __elf_aux_vector; __pthread_map_stacks_exec; __fillcontextx; __fillcontextx2; __getcontextx_size; }; Index: head/lib/libc/gen/getpwent.c =================================================================== --- head/lib/libc/gen/getpwent.c (revision 336745) +++ head/lib/libc/gen/getpwent.c (revision 336746) @@ -1,2012 +1,1993 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2003 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by * Jacques A. Vidrine, Safeport Network Services, and Network * Associates Laboratories, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 * ("CBOSS"), as part of the DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #ifdef YP #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" #ifdef NS_CACHING #include "nscache.h" #endif #ifndef CTASSERT #define CTASSERT(x) _CTASSERT(x, __LINE__) #define _CTASSERT(x, y) __CTASSERT(x, y) #define __CTASSERT(x, y) typedef char __assert_ ## y [(x) ? 1 : -1] #endif /* Counter as stored in /etc/pwd.db */ typedef int pwkeynum; CTASSERT(MAXLOGNAME > sizeof(uid_t)); CTASSERT(MAXLOGNAME > sizeof(pwkeynum)); enum constants { PWD_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ PWD_STORAGE_MAX = 1 << 20, /* 1 MByte */ SETPWENT = 1, ENDPWENT = 2, HESIOD_NAME_MAX = 256 }; static const ns_src defaultsrc[] = { { NSSRC_COMPAT, NS_SUCCESS }, { NULL, 0 } }; int __pw_match_entry(const char *, size_t, enum nss_lookup_type, const char *, uid_t); int __pw_parse_entry(char *, size_t, struct passwd *, int, int *errnop); -static void pwd_init(struct passwd *); - union key { const char *name; uid_t uid; }; static struct passwd *getpw(int (*fn)(union key, struct passwd *, char *, size_t, struct passwd **), union key); static int wrap_getpwnam_r(union key, struct passwd *, char *, size_t, struct passwd **); static int wrap_getpwuid_r(union key, struct passwd *, char *, size_t, struct passwd **); static int wrap_getpwent_r(union key, struct passwd *, char *, size_t, struct passwd **); static int pwdb_match_entry_v3(char *, size_t, enum nss_lookup_type, const char *, uid_t); static int pwdb_parse_entry_v3(char *, size_t, struct passwd *, int *); static int pwdb_match_entry_v4(char *, size_t, enum nss_lookup_type, const char *, uid_t); static int pwdb_parse_entry_v4(char *, size_t, struct passwd *, int *); struct { int (*match)(char *, size_t, enum nss_lookup_type, const char *, uid_t); int (*parse)(char *, size_t, struct passwd *, int *); } pwdb_versions[] = { { NULL, NULL }, /* version 0 */ { NULL, NULL }, /* version 1 */ { NULL, NULL }, /* version 2 */ { pwdb_match_entry_v3, pwdb_parse_entry_v3 }, /* version 3 */ { pwdb_match_entry_v4, pwdb_parse_entry_v4 }, /* version 4 */ }; struct files_state { DB *db; pwkeynum keynum; int stayopen; int version; }; static void files_endstate(void *); NSS_TLS_HANDLING(files); static DB *pwdbopen(int *); static void files_endstate(void *); static int files_setpwent(void *, void *, va_list); static int files_passwd(void *, void *, va_list); #ifdef HESIOD struct dns_state { long counter; }; static void dns_endstate(void *); NSS_TLS_HANDLING(dns); static int dns_setpwent(void *, void *, va_list); static int dns_passwd(void *, void *, va_list); #endif #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 struct compat_state { DB *db; pwkeynum keynum; int stayopen; int version; DB *exclude; struct passwd template; char *name; enum _compat { COMPAT_MODE_OFF = 0, COMPAT_MODE_ALL, COMPAT_MODE_NAME, COMPAT_MODE_NETGROUP } compat; }; static void compat_endstate(void *); NSS_TLS_HANDLING(compat); static int compat_setpwent(void *, void *, va_list); static int compat_passwd(void *, void *, va_list); static void compat_clear_template(struct passwd *); static int compat_set_template(struct passwd *, struct passwd *); static int compat_use_template(struct passwd *, struct passwd *, char *, size_t); static int compat_redispatch(struct compat_state *, enum nss_lookup_type, enum nss_lookup_type, const char *, const char *, uid_t, struct passwd *, char *, size_t, int *); #ifdef NS_CACHING static int pwd_id_func(char *, size_t *, va_list ap, void *); static int pwd_marshal_func(char *, size_t *, void *, va_list, void *); static int pwd_unmarshal_func(char *, size_t, void *, va_list, void *); static int pwd_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) { char *name; uid_t uid; size_t size, desired_size; int res = NS_UNAVAIL; enum nss_lookup_type lookup_type; lookup_type = (enum nss_lookup_type)cache_mdata; switch (lookup_type) { case nss_lt_name: name = va_arg(ap, char *); size = strlen(name); desired_size = sizeof(enum nss_lookup_type) + size + 1; if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); res = NS_SUCCESS; break; case nss_lt_id: uid = va_arg(ap, uid_t); desired_size = sizeof(enum nss_lookup_type) + sizeof(uid_t); if (desired_size > *buffer_size) { res = NS_RETURN; goto fin; } memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); memcpy(buffer + sizeof(enum nss_lookup_type), &uid, sizeof(uid_t)); res = NS_SUCCESS; break; default: /* should be unreachable */ return (NS_UNAVAIL); } fin: *buffer_size = desired_size; return (res); } static int pwd_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; uid_t uid; struct passwd *pwd; char *orig_buf; size_t orig_buf_size; struct passwd new_pwd; size_t desired_size, size; char *p; switch ((enum nss_lookup_type)cache_mdata) { case nss_lt_name: name = va_arg(ap, char *); break; case nss_lt_id: uid = va_arg(ap, uid_t); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } pwd = va_arg(ap, struct passwd *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); desired_size = sizeof(struct passwd) + sizeof(char *) + strlen(pwd->pw_name) + 1; if (pwd->pw_passwd != NULL) desired_size += strlen(pwd->pw_passwd) + 1; if (pwd->pw_class != NULL) desired_size += strlen(pwd->pw_class) + 1; if (pwd->pw_gecos != NULL) desired_size += strlen(pwd->pw_gecos) + 1; if (pwd->pw_dir != NULL) desired_size += strlen(pwd->pw_dir) + 1; if (pwd->pw_shell != NULL) desired_size += strlen(pwd->pw_shell) + 1; if (*buffer_size < desired_size) { /* this assignment is here for future use */ *buffer_size = desired_size; return (NS_RETURN); } memcpy(&new_pwd, pwd, sizeof(struct passwd)); memset(buffer, 0, desired_size); *buffer_size = desired_size; p = buffer + sizeof(struct passwd) + sizeof(char *); memcpy(buffer + sizeof(struct passwd), &p, sizeof(char *)); if (new_pwd.pw_name != NULL) { size = strlen(new_pwd.pw_name); memcpy(p, new_pwd.pw_name, size); new_pwd.pw_name = p; p += size + 1; } if (new_pwd.pw_passwd != NULL) { size = strlen(new_pwd.pw_passwd); memcpy(p, new_pwd.pw_passwd, size); new_pwd.pw_passwd = p; p += size + 1; } if (new_pwd.pw_class != NULL) { size = strlen(new_pwd.pw_class); memcpy(p, new_pwd.pw_class, size); new_pwd.pw_class = p; p += size + 1; } if (new_pwd.pw_gecos != NULL) { size = strlen(new_pwd.pw_gecos); memcpy(p, new_pwd.pw_gecos, size); new_pwd.pw_gecos = p; p += size + 1; } if (new_pwd.pw_dir != NULL) { size = strlen(new_pwd.pw_dir); memcpy(p, new_pwd.pw_dir, size); new_pwd.pw_dir = p; p += size + 1; } if (new_pwd.pw_shell != NULL) { size = strlen(new_pwd.pw_shell); memcpy(p, new_pwd.pw_shell, size); new_pwd.pw_shell = p; p += size + 1; } memcpy(buffer, &new_pwd, sizeof(struct passwd)); return (NS_SUCCESS); } static int pwd_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, void *cache_mdata) { char *name; uid_t uid; struct passwd *pwd; char *orig_buf; size_t orig_buf_size; int *ret_errno; char *p; switch ((enum nss_lookup_type)cache_mdata) { case nss_lt_name: name = va_arg(ap, char *); break; case nss_lt_id: uid = va_arg(ap, uid_t); break; case nss_lt_all: break; default: /* should be unreachable */ return (NS_UNAVAIL); } pwd = va_arg(ap, struct passwd *); orig_buf = va_arg(ap, char *); orig_buf_size = va_arg(ap, size_t); ret_errno = va_arg(ap, int *); if (orig_buf_size < buffer_size - sizeof(struct passwd) - sizeof(char *)) { *ret_errno = ERANGE; return (NS_RETURN); } memcpy(pwd, buffer, sizeof(struct passwd)); memcpy(&p, buffer + sizeof(struct passwd), sizeof(char *)); memcpy(orig_buf, buffer + sizeof(struct passwd) + sizeof(char *), buffer_size - sizeof(struct passwd) - sizeof(char *)); NS_APPLY_OFFSET(pwd->pw_name, orig_buf, p, char *); NS_APPLY_OFFSET(pwd->pw_passwd, orig_buf, p, char *); NS_APPLY_OFFSET(pwd->pw_class, orig_buf, p, char *); NS_APPLY_OFFSET(pwd->pw_gecos, orig_buf, p, char *); NS_APPLY_OFFSET(pwd->pw_dir, orig_buf, p, char *); NS_APPLY_OFFSET(pwd->pw_shell, orig_buf, p, char *); if (retval != NULL) *((struct passwd **)retval) = pwd; return (NS_SUCCESS); } NSS_MP_CACHE_HANDLING(passwd); #endif /* NS_CACHING */ void setpwent(void) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( passwd, (void *)nss_lt_all, NULL, NULL); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_setpwent, (void *)SETPWENT }, #ifdef HESIOD { NSSRC_DNS, dns_setpwent, (void *)SETPWENT }, #endif #ifdef YP { NSSRC_NIS, nis_setpwent, (void *)SETPWENT }, #endif { NSSRC_COMPAT, compat_setpwent, (void *)SETPWENT }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; (void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc, 0); } int setpassent(int stayopen) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( passwd, (void *)nss_lt_all, NULL, NULL); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_setpwent, (void *)SETPWENT }, #ifdef HESIOD { NSSRC_DNS, dns_setpwent, (void *)SETPWENT }, #endif #ifdef YP { NSSRC_NIS, nis_setpwent, (void *)SETPWENT }, #endif { NSSRC_COMPAT, compat_setpwent, (void *)SETPWENT }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; (void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc, stayopen); return (1); } void endpwent(void) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( passwd, (void *)nss_lt_all, NULL, NULL); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_setpwent, (void *)ENDPWENT }, #ifdef HESIOD { NSSRC_DNS, dns_setpwent, (void *)ENDPWENT }, #endif #ifdef YP { NSSRC_NIS, nis_setpwent, (void *)ENDPWENT }, #endif { NSSRC_COMPAT, compat_setpwent, (void *)ENDPWENT }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; (void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "endpwent", defaultsrc); } int getpwent_r(struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( passwd, (void *)nss_lt_all, pwd_marshal_func, pwd_unmarshal_func); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_passwd, (void *)nss_lt_all }, #ifdef HESIOD { NSSRC_DNS, dns_passwd, (void *)nss_lt_all }, #endif #ifdef YP { NSSRC_NIS, nis_passwd, (void *)nss_lt_all }, #endif { NSSRC_COMPAT, compat_passwd, (void *)nss_lt_all }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; int rv, ret_errno; - pwd_init(pwd); + __pw_initpwd(pwd); ret_errno = 0; *result = NULL; rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwent_r", defaultsrc, pwd, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } int getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( passwd, (void *)nss_lt_name, pwd_id_func, pwd_marshal_func, pwd_unmarshal_func); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_passwd, (void *)nss_lt_name }, #ifdef HESIOD { NSSRC_DNS, dns_passwd, (void *)nss_lt_name }, #endif #ifdef YP { NSSRC_NIS, nis_passwd, (void *)nss_lt_name }, #endif { NSSRC_COMPAT, compat_passwd, (void *)nss_lt_name }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; int rv, ret_errno; - pwd_init(pwd); + __pw_initpwd(pwd); ret_errno = 0; *result = NULL; rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwnam_r", defaultsrc, name, pwd, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } int getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { #ifdef NS_CACHING static const nss_cache_info cache_info = NS_COMMON_CACHE_INFO_INITIALIZER( passwd, (void *)nss_lt_id, pwd_id_func, pwd_marshal_func, pwd_unmarshal_func); #endif static const ns_dtab dtab[] = { { NSSRC_FILES, files_passwd, (void *)nss_lt_id }, #ifdef HESIOD { NSSRC_DNS, dns_passwd, (void *)nss_lt_id }, #endif #ifdef YP { NSSRC_NIS, nis_passwd, (void *)nss_lt_id }, #endif { NSSRC_COMPAT, compat_passwd, (void *)nss_lt_id }, #ifdef NS_CACHING NS_CACHE_CB(&cache_info) #endif { NULL, NULL, NULL } }; int rv, ret_errno; - pwd_init(pwd); + __pw_initpwd(pwd); ret_errno = 0; *result = NULL; rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwuid_r", defaultsrc, uid, pwd, buffer, bufsize, &ret_errno); if (rv == NS_SUCCESS) return (0); else return (ret_errno); } -static void -pwd_init(struct passwd *pwd) -{ - static char nul[] = ""; - - memset(pwd, 0, sizeof(*pwd)); - pwd->pw_uid = (uid_t)-1; /* Considered least likely to lead to */ - pwd->pw_gid = (gid_t)-1; /* a security issue. */ - pwd->pw_name = nul; - pwd->pw_passwd = nul; - pwd->pw_class = nul; - pwd->pw_gecos = nul; - pwd->pw_dir = nul; - pwd->pw_shell = nul; -} - - static struct passwd pwd; static char *pwd_storage; static size_t pwd_storage_size; static struct passwd * getpw(int (*fn)(union key, struct passwd *, char *, size_t, struct passwd **), union key key) { int rv; struct passwd *res; if (pwd_storage == NULL) { pwd_storage = malloc(PWD_STORAGE_INITIAL); if (pwd_storage == NULL) return (NULL); pwd_storage_size = PWD_STORAGE_INITIAL; } do { rv = fn(key, &pwd, pwd_storage, pwd_storage_size, &res); if (res == NULL && rv == ERANGE) { free(pwd_storage); if ((pwd_storage_size << 1) > PWD_STORAGE_MAX) { pwd_storage = NULL; errno = ERANGE; return (NULL); } pwd_storage_size <<= 1; pwd_storage = malloc(pwd_storage_size); if (pwd_storage == NULL) return (NULL); } } while (res == NULL && rv == ERANGE); if (rv != 0) errno = rv; return (res); } 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 wrap_getpwuid_r(union key key, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **res) { return (getpwuid_r(key.uid, pwd, buffer, bufsize, res)); } 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)); } struct passwd * getpwnam(const char *name) { union key key; key.name = name; return (getpw(wrap_getpwnam_r, key)); } struct passwd * getpwuid(uid_t uid) { union key key; key.uid = uid; return (getpw(wrap_getpwuid_r, key)); } struct passwd * getpwent(void) { union key key; key.uid = 0; /* not used */ return (getpw(wrap_getpwent_r, key)); } /* * files backend */ static DB * pwdbopen(int *version) { DB *res; DBT key, entry; int rv; if (geteuid() != 0 || (res = dbopen(_PATH_SMP_DB, O_RDONLY, 0, DB_HASH, NULL)) == NULL) res = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL); if (res == NULL) return (NULL); key.data = _PWD_VERSION_KEY; key.size = strlen(_PWD_VERSION_KEY); rv = res->get(res, &key, &entry, 0); if (rv == 0) *version = *(unsigned char *)entry.data; else *version = 3; if (*version < 3 || *version >= nitems(pwdb_versions)) { syslog(LOG_CRIT, "Unsupported password database version %d", *version); res->close(res); res = NULL; } return (res); } static void files_endstate(void *p) { DB *db; if (p == NULL) return; db = ((struct files_state *)p)->db; if (db != NULL) db->close(db); free(p); } static int files_setpwent(void *retval, void *mdata, va_list ap) { struct files_state *st; int rv, stayopen; rv = files_getstate(&st); if (rv != 0) return (NS_UNAVAIL); switch ((enum constants)mdata) { case SETPWENT: stayopen = va_arg(ap, int); st->keynum = 0; if (stayopen) st->db = pwdbopen(&st->version); st->stayopen = stayopen; break; case ENDPWENT: if (st->db != NULL) { (void)st->db->close(st->db); st->db = NULL; } break; default: break; } return (NS_UNAVAIL); } static int files_passwd(void *retval, void *mdata, va_list ap) { char keybuf[MAXLOGNAME + 1]; DBT key, entry; struct files_state *st; enum nss_lookup_type how; const char *name; struct passwd *pwd; char *buffer; size_t bufsize, namesize; uid_t uid; uint32_t store; int rv, stayopen = 0, *errnop; name = NULL; uid = (uid_t)-1; how = (enum nss_lookup_type)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, const char *); keybuf[0] = _PW_KEYBYNAME; break; case nss_lt_id: uid = va_arg(ap, uid_t); keybuf[0] = _PW_KEYBYUID; break; case nss_lt_all: keybuf[0] = _PW_KEYBYNUM; break; default: rv = NS_NOTFOUND; goto fin; } pwd = va_arg(ap, struct passwd *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = files_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (how == nss_lt_all && st->keynum < 0) { rv = NS_NOTFOUND; goto fin; } if (st->db == NULL && (st->db = pwdbopen(&st->version)) == NULL) { *errnop = errno; rv = NS_UNAVAIL; goto fin; } if (how == nss_lt_all) stayopen = 1; else stayopen = st->stayopen; key.data = keybuf; do { switch (how) { case nss_lt_name: /* MAXLOGNAME includes NUL byte, but we do not * include the NUL byte in the key. */ namesize = strlcpy(&keybuf[1], name, sizeof(keybuf)-1); if (namesize >= sizeof(keybuf)-1) { *errnop = EINVAL; rv = NS_NOTFOUND; goto fin; } key.size = namesize + 1; break; case nss_lt_id: if (st->version < _PWD_CURRENT_VERSION) { memcpy(&keybuf[1], &uid, sizeof(uid)); key.size = sizeof(uid) + 1; } else { store = htonl(uid); memcpy(&keybuf[1], &store, sizeof(store)); key.size = sizeof(store) + 1; } break; case nss_lt_all: st->keynum++; if (st->version < _PWD_CURRENT_VERSION) { memcpy(&keybuf[1], &st->keynum, sizeof(st->keynum)); key.size = sizeof(st->keynum) + 1; } else { store = htonl(st->keynum); memcpy(&keybuf[1], &store, sizeof(store)); key.size = sizeof(store) + 1; } break; } keybuf[0] = _PW_VERSIONED(keybuf[0], st->version); rv = st->db->get(st->db, &key, &entry, 0); if (rv < 0 || rv > 1) { /* should never return > 1 */ *errnop = errno; rv = NS_UNAVAIL; goto fin; } else if (rv == 1) { if (how == nss_lt_all) st->keynum = -1; rv = NS_NOTFOUND; goto fin; } rv = pwdb_versions[st->version].match(entry.data, entry.size, how, name, uid); if (rv != NS_SUCCESS) continue; if (entry.size > bufsize) { *errnop = ERANGE; rv = NS_RETURN; break; } memcpy(buffer, entry.data, entry.size); rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd, errnop); } while (how == nss_lt_all && !(rv & NS_TERMINATE)); fin: if (st->db != NULL && !stayopen) { (void)st->db->close(st->db); st->db = NULL; } if (rv == NS_SUCCESS) { pwd->pw_fields &= ~_PWF_SOURCE; pwd->pw_fields |= _PWF_FILES; if (retval != NULL) *(struct passwd **)retval = pwd; } return (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; /* 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; /* 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 /* * dns backend */ static void dns_endstate(void *p) { free(p); } static int dns_setpwent(void *retval, void *mdata, va_list ap) { struct dns_state *st; int rv; rv = dns_getstate(&st); if (rv != 0) return (NS_UNAVAIL); st->counter = 0; return (NS_UNAVAIL); } static int dns_passwd(void *retval, void *mdata, va_list ap) { char buf[HESIOD_NAME_MAX]; struct dns_state *st; struct passwd *pwd; const char *name, *label; void *ctx; char *buffer, **hes; size_t bufsize, linesize; uid_t uid; enum nss_lookup_type how; int rv, *errnop; ctx = NULL; hes = NULL; name = NULL; uid = (uid_t)-1; how = (enum nss_lookup_type)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, const char *); break; case nss_lt_id: uid = va_arg(ap, uid_t); break; case nss_lt_all: break; } pwd = va_arg(ap, struct passwd *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = dns_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (hesiod_init(&ctx) != 0) { *errnop = errno; rv = NS_UNAVAIL; goto fin; } do { rv = NS_NOTFOUND; switch (how) { case nss_lt_name: label = name; break; case nss_lt_id: if (snprintf(buf, sizeof(buf), "%lu", (unsigned long)uid) >= sizeof(buf)) goto fin; label = buf; break; case nss_lt_all: if (st->counter < 0) goto fin; if (snprintf(buf, sizeof(buf), "passwd-%ld", st->counter++) >= sizeof(buf)) goto fin; label = buf; break; } hes = hesiod_resolve(ctx, label, how == nss_lt_id ? "uid" : "passwd"); if (hes == NULL) { if (how == nss_lt_all) st->counter = -1; if (errno != ENOENT) *errnop = errno; goto fin; } rv = __pw_match_entry(hes[0], strlen(hes[0]), how, name, uid); if (rv != NS_SUCCESS) { hesiod_free_list(ctx, hes); hes = NULL; continue; } linesize = strlcpy(buffer, hes[0], bufsize); if (linesize >= bufsize) { *errnop = ERANGE; rv = NS_RETURN; continue; } hesiod_free_list(ctx, hes); hes = NULL; rv = __pw_parse_entry(buffer, bufsize, pwd, 0, errnop); } while (how == nss_lt_all && !(rv & NS_TERMINATE)); fin: if (hes != NULL) hesiod_free_list(ctx, hes); if (ctx != NULL) hesiod_end(ctx); if (rv == NS_SUCCESS) { pwd->pw_fields &= ~_PWF_SOURCE; pwd->pw_fields |= _PWF_HESIOD; if (retval != NULL) *(struct passwd **)retval = pwd; } return (rv); } #endif /* HESIOD */ #ifdef YP /* * nis backend */ static void nis_endstate(void *p) { free(((struct nis_state *)p)->key); free(p); } /* * Test for the presence of special FreeBSD-specific master.passwd.by* * maps. We do this using yp_order(). If it fails, then either the server * doesn't have the map, or the YPPROC_ORDER procedure isn't supported by * the server (Sun NIS+ servers in YP compat mode behave this way). If * the master.passwd.by* maps don't exist, then let the lookup routine try * the regular passwd.by* maps instead. If the lookup routine fails, it * can return an error as needed. */ static int nis_map(char *domain, enum nss_lookup_type how, char *buffer, size_t bufsize, int *master) { int rv, order; *master = 0; if (geteuid() == 0) { if (snprintf(buffer, bufsize, "master.passwd.by%s", (how == nss_lt_id) ? "uid" : "name") >= bufsize) return (NS_UNAVAIL); rv = yp_order(domain, buffer, &order); if (rv == 0) { *master = 1; return (NS_SUCCESS); } } if (snprintf(buffer, bufsize, "passwd.by%s", (how == nss_lt_id) ? "uid" : "name") >= bufsize) return (NS_UNAVAIL); return (NS_SUCCESS); } 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 *); break; case nss_lt_id: uid = va_arg(ap, uid_t); break; case nss_lt_all: break; } pwd = va_arg(ap, struct passwd *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = nis_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (st->domain[0] == '\0') { if (getdomainname(st->domain, sizeof(st->domain)) != 0) { *errnop = errno; return (NS_UNAVAIL); } } rv = nis_map(st->domain, how, map, sizeof(map), &master); if (rv != NS_SUCCESS) return (rv); result = NULL; do { rv = NS_NOTFOUND; switch (how) { case nss_lt_name: if (strlcpy(buffer, name, bufsize) >= bufsize) goto erange; break; case nss_lt_id: if (snprintf(buffer, bufsize, "%lu", (unsigned long)uid) >= bufsize) goto erange; break; case nss_lt_all: if (st->done) goto fin; break; } result = NULL; if (how == nss_lt_all) { if (st->key == NULL) rv = yp_first(st->domain, map, &st->key, &st->keylen, &result, &resultlen); else { key = st->key; keylen = st->keylen; st->key = NULL; rv = yp_next(st->domain, map, key, keylen, &st->key, &st->keylen, &result, &resultlen); free(key); } if (rv != 0) { free(result); free(st->key); st->key = NULL; if (rv == YPERR_NOMORE) st->done = 1; else rv = NS_UNAVAIL; goto fin; } } else { rv = yp_match(st->domain, map, buffer, strlen(buffer), &result, &resultlen); if (rv == YPERR_KEY) { rv = NS_NOTFOUND; continue; } else if (rv != 0) { free(result); rv = NS_UNAVAIL; continue; } } if (resultlen >= bufsize) { free(result); goto erange; } memcpy(buffer, result, resultlen); buffer[resultlen] = '\0'; free(result); rv = __pw_match_entry(buffer, resultlen, how, name, uid); if (rv == NS_SUCCESS) rv = __pw_parse_entry(buffer, resultlen, pwd, master, errnop); } while (how == nss_lt_all && !(rv & NS_TERMINATE)); fin: if (rv == NS_SUCCESS) { if (strstr(pwd->pw_passwd, "##") != NULL) { rv = nis_adjunct(st->domain, pwd->pw_name, &buffer[resultlen+1], bufsize-resultlen-1); if (rv < 0) goto erange; else if (rv == 0) pwd->pw_passwd = &buffer[resultlen+1]; } pwd->pw_fields &= ~_PWF_SOURCE; pwd->pw_fields |= _PWF_NIS; if (retval != NULL) *(struct passwd **)retval = pwd; rv = NS_SUCCESS; } return (rv); erange: *errnop = ERANGE; return (NS_RETURN); } #endif /* YP */ /* * compat backend */ static void compat_clear_template(struct passwd *template) { free(template->pw_passwd); free(template->pw_gecos); free(template->pw_dir); free(template->pw_shell); memset(template, 0, sizeof(*template)); } static int compat_set_template(struct passwd *src, struct passwd *template) { 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); } 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); } static int compat_exclude(const char *name, DB **db) { DBT key, data; if (*db == NULL && (*db = dbopen(NULL, O_RDWR, 600, DB_HASH, 0)) == NULL) return (errno); key.size = strlen(name); key.data = (char *)name; data.size = 0; data.data = NULL; if ((*db)->put(*db, &key, &data, 0) == -1) return (errno); return (0); } static int compat_is_excluded(const char *name, DB *db) { DBT key, data; if (db == NULL) return (0); key.size = strlen(name); key.data = (char *)name; return (db->get(db, &key, &data, 0) == 0); } static int compat_redispatch(struct compat_state *st, enum nss_lookup_type how, enum nss_lookup_type lookup_how, const char *name, const char *lookup_name, uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, int *errnop) { static const ns_src compatsrc[] = { #ifdef YP { NSSRC_NIS, NS_SUCCESS }, #endif { NULL, 0 } }; ns_dtab dtab[] = { #ifdef YP { NSSRC_NIS, nis_passwd, NULL }, #endif #ifdef HESIOD { NSSRC_DNS, dns_passwd, NULL }, #endif { NULL, NULL, NULL } }; void *discard; int e, i, rv; for (i = 0; i < (int)(nitems(dtab) - 1); i++) dtab[i].mdata = (void *)lookup_how; more: - pwd_init(pwd); + __pw_initpwd(pwd); switch (lookup_how) { case nss_lt_all: rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, "getpwent_r", compatsrc, pwd, buffer, bufsize, errnop); break; case nss_lt_id: rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, "getpwuid_r", compatsrc, uid, pwd, buffer, bufsize, errnop); break; case nss_lt_name: rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT, "getpwnam_r", compatsrc, lookup_name, pwd, buffer, bufsize, errnop); break; default: return (NS_UNAVAIL); } if (rv != NS_SUCCESS) return (rv); if (compat_is_excluded(pwd->pw_name, st->exclude)) { if (how == nss_lt_all) goto more; return (NS_NOTFOUND); } e = compat_use_template(pwd, &st->template, buffer, bufsize); if (e != 0) { *errnop = e; if (e == ERANGE) return (NS_RETURN); else return (NS_UNAVAIL); } switch (how) { case nss_lt_name: if (strcmp(name, pwd->pw_name) != 0) return (NS_NOTFOUND); break; case nss_lt_id: if (uid != pwd->pw_uid) return (NS_NOTFOUND); break; default: break; } return (NS_SUCCESS); } static void compat_endstate(void *p) { struct compat_state *st; if (p == NULL) return; st = (struct compat_state *)p; if (st->db != NULL) st->db->close(st->db); if (st->exclude != NULL) st->exclude->close(st->exclude); compat_clear_template(&st->template); free(p); } static int compat_setpwent(void *retval, void *mdata, va_list ap) { static const ns_src compatsrc[] = { #ifdef YP { NSSRC_NIS, NS_SUCCESS }, #endif { NULL, 0 } }; ns_dtab dtab[] = { #ifdef YP { NSSRC_NIS, nis_setpwent, NULL }, #endif #ifdef HESIOD { NSSRC_DNS, dns_setpwent, NULL }, #endif { NULL, NULL, NULL } }; struct compat_state *st; int rv, stayopen; #define set_setent(x, y) do { \ int i; \ for (i = 0; i < (int)(nitems(x) - 1); i++) \ x[i].mdata = (void *)y; \ } while (0) rv = compat_getstate(&st); if (rv != 0) return (NS_UNAVAIL); switch ((enum constants)mdata) { case SETPWENT: stayopen = va_arg(ap, int); st->keynum = 0; if (stayopen) st->db = pwdbopen(&st->version); st->stayopen = stayopen; set_setent(dtab, mdata); (void)_nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "setpwent", compatsrc, 0); break; case ENDPWENT: if (st->db != NULL) { (void)st->db->close(st->db); st->db = NULL; } set_setent(dtab, mdata); (void)_nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "endpwent", compatsrc, 0); break; default: break; } return (NS_UNAVAIL); #undef set_setent } static int compat_passwd(void *retval, void *mdata, va_list ap) { char keybuf[MAXLOGNAME + 1]; DBT key, entry; struct compat_state *st; enum nss_lookup_type how; const char *name; struct passwd *pwd; char *buffer, *pw_name; char *host, *user, *domain; size_t bufsize; uid_t uid; uint32_t store; int rv, from_compat, stayopen, *errnop; from_compat = 0; name = NULL; uid = (uid_t)-1; how = (enum nss_lookup_type)mdata; switch (how) { case nss_lt_name: name = va_arg(ap, const char *); break; case nss_lt_id: uid = va_arg(ap, uid_t); break; case nss_lt_all: break; default: rv = NS_NOTFOUND; goto fin; } pwd = va_arg(ap, struct passwd *); buffer = va_arg(ap, char *); bufsize = va_arg(ap, size_t); errnop = va_arg(ap, int *); *errnop = compat_getstate(&st); if (*errnop != 0) return (NS_UNAVAIL); if (how == nss_lt_all && st->keynum < 0) { rv = NS_NOTFOUND; goto fin; } if (st->db == NULL && (st->db = pwdbopen(&st->version)) == NULL) { *errnop = errno; rv = NS_UNAVAIL; goto fin; } if (how == nss_lt_all) { if (st->keynum < 0) { rv = NS_NOTFOUND; goto fin; } stayopen = 1; } else { st->keynum = 0; stayopen = st->stayopen; } docompat: rv = NS_NOTFOUND; switch (st->compat) { case COMPAT_MODE_ALL: rv = compat_redispatch(st, how, how, name, name, uid, pwd, buffer, bufsize, errnop); if (rv != NS_SUCCESS) st->compat = COMPAT_MODE_OFF; break; case COMPAT_MODE_NETGROUP: /* XXX getnetgrent is not thread-safe. */ do { rv = getnetgrent(&host, &user, &domain); if (rv == 0) { endnetgrent(); st->compat = COMPAT_MODE_OFF; rv = NS_NOTFOUND; continue; } else if (user == NULL || user[0] == '\0') continue; rv = compat_redispatch(st, how, nss_lt_name, name, user, uid, pwd, buffer, bufsize, errnop); } while (st->compat == COMPAT_MODE_NETGROUP && !(rv & NS_TERMINATE)); break; case COMPAT_MODE_NAME: rv = compat_redispatch(st, how, nss_lt_name, name, st->name, uid, pwd, buffer, bufsize, errnop); free(st->name); st->name = NULL; st->compat = COMPAT_MODE_OFF; break; default: break; } if (rv & NS_TERMINATE) { from_compat = 1; goto fin; } key.data = keybuf; rv = NS_NOTFOUND; while (st->keynum >= 0) { st->keynum++; if (st->version < _PWD_CURRENT_VERSION) { memcpy(&keybuf[1], &st->keynum, sizeof(st->keynum)); key.size = sizeof(st->keynum) + 1; } else { store = htonl(st->keynum); memcpy(&keybuf[1], &store, sizeof(store)); key.size = sizeof(store) + 1; } keybuf[0] = _PW_VERSIONED(_PW_KEYBYNUM, st->version); rv = st->db->get(st->db, &key, &entry, 0); if (rv < 0 || rv > 1) { /* should never return > 1 */ *errnop = errno; rv = NS_UNAVAIL; goto fin; } else if (rv == 1) { st->keynum = -1; rv = NS_NOTFOUND; goto fin; } pw_name = (char *)entry.data; switch (pw_name[0]) { case '+': switch (pw_name[1]) { case '\0': st->compat = COMPAT_MODE_ALL; break; case '@': setnetgrent(&pw_name[2]); st->compat = COMPAT_MODE_NETGROUP; break; default: st->name = strdup(&pw_name[1]); if (st->name == NULL) { syslog(LOG_ERR, "getpwent memory allocation failure"); *errnop = ENOMEM; rv = NS_UNAVAIL; break; } st->compat = COMPAT_MODE_NAME; } if (entry.size > bufsize) { *errnop = ERANGE; rv = NS_RETURN; goto fin; } memcpy(buffer, entry.data, entry.size); rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd, errnop); if (rv != NS_SUCCESS) ; else if (compat_set_template(pwd, &st->template) < 0) { *errnop = ENOMEM; rv = NS_UNAVAIL; goto fin; } goto docompat; case '-': switch (pw_name[1]) { case '\0': /* XXX Maybe syslog warning */ continue; case '@': setnetgrent(&pw_name[2]); while (getnetgrent(&host, &user, &domain) != 0) { if (user != NULL && user[0] != '\0') compat_exclude(user, &st->exclude); } endnetgrent(); continue; default: compat_exclude(&pw_name[1], &st->exclude); continue; } break; default: break; } if (compat_is_excluded((char *)entry.data, st->exclude)) continue; rv = pwdb_versions[st->version].match(entry.data, entry.size, how, name, uid); if (rv == NS_RETURN) break; else if (rv != NS_SUCCESS) continue; if (entry.size > bufsize) { *errnop = ERANGE; rv = NS_RETURN; break; } memcpy(buffer, entry.data, entry.size); rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd, errnop); if (rv & NS_TERMINATE) break; } fin: if (st->db != NULL && !stayopen) { (void)st->db->close(st->db); st->db = NULL; } if (rv == NS_SUCCESS) { if (!from_compat) { pwd->pw_fields &= ~_PWF_SOURCE; pwd->pw_fields |= _PWF_FILES; } if (retval != NULL) *(struct passwd **)retval = pwd; } return (rv); } /* * common passwd line matching and parsing */ int __pw_match_entry(const char *entry, size_t entrysize, enum nss_lookup_type how, const char *name, uid_t uid) { const char *p, *eom; char *q; size_t len; unsigned long m; eom = entry + entrysize; for (p = entry; p < eom; p++) if (*p == ':') break; if (*p != ':') return (NS_NOTFOUND); if (how == nss_lt_all) return (NS_SUCCESS); if (how == nss_lt_name) { len = strlen(name); if (len == (p - entry) && memcmp(name, entry, len) == 0) return (NS_SUCCESS); else return (NS_NOTFOUND); } for (p++; p < eom; p++) if (*p == ':') break; if (*p != ':') return (NS_NOTFOUND); m = strtoul(++p, &q, 10); if (q[0] != ':' || (uid_t)m != uid) return (NS_NOTFOUND); else return (NS_SUCCESS); } /* XXX buffer must be NUL-terminated. errnop is not set correctly. */ int __pw_parse_entry(char *buffer, size_t bufsize __unused, struct passwd *pwd, int master, int *errnop __unused) { if (__pw_scan(buffer, pwd, master ? _PWSCAN_MASTER : 0) == 0) return (NS_NOTFOUND); else return (NS_SUCCESS); } Index: head/lib/libc/gen/pw_scan.c =================================================================== --- head/lib/libc/gen/pw_scan.c (revision 336745) +++ head/lib/libc/gen/pw_scan.c (revision 336746) @@ -1,210 +1,226 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1990, 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. 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. */ #include __SCCSID("@(#)pw_scan.c 8.3 (Berkeley) 4/2/94"); __FBSDID("$FreeBSD$"); /* * This module is used to "verify" password entries by chpass(1) and * pwd_mkdb(8). */ #include #include #include #include #include #include #include #include "pw_scan.h" /* * Some software assumes that IDs are short. We should emit warnings * for id's which cannot be stored in a short, but we are more liberal * by default, warning for IDs greater than USHRT_MAX. * * If pw_big_ids_warning is -1 on entry to pw_scan(), it will be set based * on the existence of PW_SCAN_BIG_IDS in the environment. * * It is believed all baseline system software that can not handle the * normal ID sizes is now gone so pw_big_ids_warning is disabled for now. * But the code has been left in place in case end-users want to re-enable * it and/or for the next time the ID sizes get bigger but pieces of the * system lag behind. */ static int pw_big_ids_warning = 0; +void +__pw_initpwd(struct passwd *pwd) +{ + static char nul[] = ""; + + memset(pwd, 0, sizeof(*pwd)); + pwd->pw_uid = (uid_t)-1; /* Considered least likely to lead to */ + pwd->pw_gid = (gid_t)-1; /* a security issue. */ + pwd->pw_name = nul; + pwd->pw_passwd = nul; + pwd->pw_class = nul; + pwd->pw_gecos = nul; + pwd->pw_dir = nul; + pwd->pw_shell = nul; +} + int __pw_scan(char *bp, struct passwd *pw, int flags) { uid_t id; int root; char *ep, *p, *sh; unsigned long temp; if (pw_big_ids_warning == -1) pw_big_ids_warning = getenv("PW_SCAN_BIG_IDS") == NULL ? 1 : 0; pw->pw_fields = 0; if (!(pw->pw_name = strsep(&bp, ":"))) /* login */ goto fmt; root = !strcmp(pw->pw_name, "root"); if (pw->pw_name[0] && (pw->pw_name[0] != '+' || pw->pw_name[1] == '\0')) pw->pw_fields |= _PWF_NAME; if (!(pw->pw_passwd = strsep(&bp, ":"))) /* passwd */ goto fmt; if (pw->pw_passwd[0]) pw->pw_fields |= _PWF_PASSWD; if (!(p = strsep(&bp, ":"))) /* uid */ goto fmt; if (p[0]) pw->pw_fields |= _PWF_UID; else { if (pw->pw_name[0] != '+' && pw->pw_name[0] != '-') { if (flags & _PWSCAN_WARN) warnx("no uid for user %s", pw->pw_name); return (0); } } errno = 0; temp = strtoul(p, &ep, 10); if ((temp == ULONG_MAX && errno == ERANGE) || temp > UID_MAX) { if (flags & _PWSCAN_WARN) warnx("%s > max uid value (%u)", p, UID_MAX); return (0); } id = temp; if (*ep != '\0') { if (flags & _PWSCAN_WARN) warnx("%s uid is incorrect", p); return (0); } if (root && id) { if (flags & _PWSCAN_WARN) warnx("root uid should be 0"); return (0); } if (flags & _PWSCAN_WARN && pw_big_ids_warning && id > USHRT_MAX) { warnx("%s > recommended max uid value (%u)", p, USHRT_MAX); /*return (0);*/ /* THIS SHOULD NOT BE FATAL! */ } pw->pw_uid = id; if (!(p = strsep(&bp, ":"))) /* gid */ goto fmt; if (p[0]) pw->pw_fields |= _PWF_GID; else { if (pw->pw_name[0] != '+' && pw->pw_name[0] != '-') { if (flags & _PWSCAN_WARN) warnx("no gid for user %s", pw->pw_name); return (0); } } errno = 0; temp = strtoul(p, &ep, 10); if ((temp == ULONG_MAX && errno == ERANGE) || temp > GID_MAX) { if (flags & _PWSCAN_WARN) warnx("%s > max gid value (%u)", p, GID_MAX); return (0); } id = temp; if (*ep != '\0') { if (flags & _PWSCAN_WARN) warnx("%s gid is incorrect", p); return (0); } if (flags & _PWSCAN_WARN && pw_big_ids_warning && id > USHRT_MAX) { warnx("%s > recommended max gid value (%u)", p, USHRT_MAX); /* return (0); This should not be fatal! */ } pw->pw_gid = id; if (flags & _PWSCAN_MASTER ) { if (!(pw->pw_class = strsep(&bp, ":"))) /* class */ goto fmt; if (pw->pw_class[0]) pw->pw_fields |= _PWF_CLASS; if (!(p = strsep(&bp, ":"))) /* change */ goto fmt; if (p[0]) pw->pw_fields |= _PWF_CHANGE; pw->pw_change = atol(p); if (!(p = strsep(&bp, ":"))) /* expire */ goto fmt; if (p[0]) pw->pw_fields |= _PWF_EXPIRE; pw->pw_expire = atol(p); } if (!(pw->pw_gecos = strsep(&bp, ":"))) /* gecos */ goto fmt; if (pw->pw_gecos[0]) pw->pw_fields |= _PWF_GECOS; if (!(pw->pw_dir = strsep(&bp, ":"))) /* directory */ goto fmt; if (pw->pw_dir[0]) pw->pw_fields |= _PWF_DIR; if (!(pw->pw_shell = strsep(&bp, ":"))) /* shell */ goto fmt; p = pw->pw_shell; if (root && *p) { /* empty == /bin/sh */ for (setusershell();;) { if (!(sh = getusershell())) { if (flags & _PWSCAN_WARN) warnx("warning, unknown root shell"); break; } if (!strcmp(p, sh)) break; } endusershell(); } if (p[0]) pw->pw_fields |= _PWF_SHELL; if ((p = strsep(&bp, ":"))) { /* too many */ fmt: if (flags & _PWSCAN_WARN) warnx("corrupted entry"); return (0); } return (1); } Index: head/lib/libc/gen/pw_scan.h =================================================================== --- head/lib/libc/gen/pw_scan.h (revision 336745) +++ head/lib/libc/gen/pw_scan.h (revision 336746) @@ -1,38 +1,39 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 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. 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. * * @(#)pw_scan.h 8.1 (Berkeley) 4/1/94 * $FreeBSD$ */ #define _PWSCAN_MASTER 0x01 #define _PWSCAN_WARN 0x02 +extern void __pw_initpwd(struct passwd *); extern int __pw_scan(char *, struct passwd *, int); Index: head/lib/libutil/libutil.h =================================================================== --- head/lib/libutil/libutil.h (revision 336745) +++ head/lib/libutil/libutil.h (revision 336746) @@ -1,255 +1,256 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1996 Peter Wemm . * All rights reserved. * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. * * Portions of this software were developed for the FreeBSD Project by * ThinkSec AS and NAI Labs, 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, is 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. The name of the author may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _LIBUTIL_H_ #define _LIBUTIL_H_ #include #include #include #ifndef _GID_T_DECLARED typedef __gid_t gid_t; #define _GID_T_DECLARED #endif #ifndef _MODE_T_DECLARED typedef __mode_t mode_t; #define _MODE_T_DECLARED #endif #ifndef _PID_T_DECLARED typedef __pid_t pid_t; #define _PID_T_DECLARED #endif #ifndef _SIZE_T_DECLARED typedef __size_t size_t; #define _SIZE_T_DECLARED #endif #ifndef _UID_T_DECLARED typedef __uid_t uid_t; #define _UID_T_DECLARED #endif #define PROPERTY_MAX_NAME 64 #define PROPERTY_MAX_VALUE 512 /* For properties.c. */ typedef struct _property { struct _property *next; char *name; char *value; } *properties; /* Avoid pulling in all the include files for no need. */ struct in_addr; struct pidfh; struct sockaddr; struct termios; struct winsize; __BEGIN_DECLS char *auth_getval(const char *_name); void clean_environment(const char * const *_white, const char * const *_more_white); int expand_number(const char *_buf, uint64_t *_num); int extattr_namespace_to_string(int _attrnamespace, char **_string); int extattr_string_to_namespace(const char *_string, int *_attrnamespace); int flopen(const char *_path, int _flags, ...); int flopenat(int _dirfd, const char *_path, int _flags, ...); int forkpty(int *_amaster, char *_name, struct termios *_termp, struct winsize *_winp); void hexdump(const void *_ptr, int _length, const char *_hdr, int _flags); int humanize_number(char *_buf, size_t _len, int64_t _number, const char *_suffix, int _scale, int _flags); struct kinfo_file * kinfo_getfile(pid_t _pid, int *_cntp); struct kinfo_vmentry * kinfo_getvmmap(pid_t _pid, int *_cntp); struct kinfo_vmobject * kinfo_getvmobject(int *_cntp); struct kinfo_proc * kinfo_getallproc(int *_cntp); struct kinfo_proc * kinfo_getproc(pid_t _pid); int kld_isloaded(const char *_name); int kld_load(const char *_name); int login_tty(int _fd); int openpty(int *_amaster, int *_aslave, char *_name, struct termios *_termp, struct winsize *_winp); int pidfile_close(struct pidfh *_pfh); int pidfile_fileno(const struct pidfh *_pfh); struct pidfh * pidfile_open(const char *_path, mode_t _mode, pid_t *_pidptr); int pidfile_remove(struct pidfh *_pfh); int pidfile_write(struct pidfh *_pfh); void properties_free(properties _list); char *property_find(properties _list, const char *_name); properties properties_read(int _fd); int realhostname(char *_host, size_t _hsize, const struct in_addr *_ip); int realhostname_sa(char *_host, size_t _hsize, struct sockaddr *_addr, int _addrlen); int _secure_path(const char *_path, uid_t _uid, gid_t _gid); void trimdomain(char *_fullhost, int _hostsize); const char * uu_lockerr(int _uu_lockresult); int uu_lock(const char *_ttyname); int uu_unlock(const char *_ttyname); int uu_lock_txfr(const char *_ttyname, pid_t _pid); /* * Conditionally prototype the following functions if the include * files upon which they depend have been included. */ #ifdef _STDIO_H_ char *fparseln(FILE *_fp, size_t *_len, size_t *_lineno, const char _delim[3], int _flags); #endif #ifdef _PWD_H_ int pw_copy(int _ffd, int _tfd, const struct passwd *_pw, struct passwd *_old_pw); struct passwd *pw_dup(const struct passwd *_pw); int pw_edit(int _notsetuid); int pw_equal(const struct passwd *_pw1, const struct passwd *_pw2); void pw_fini(void); int pw_init(const char *_dir, const char *_master); +void pw_initpwd(struct passwd *_pw); char *pw_make(const struct passwd *_pw); char *pw_make_v7(const struct passwd *_pw); int pw_mkdb(const char *_user); int pw_lock(void); struct passwd * pw_scan(const char *_line, int _flags); const char * pw_tempname(void); int pw_tmp(int _mfd); #endif #ifdef _GRP_H_ int gr_copy(int __ffd, int _tfd, const struct group *_gr, struct group *_old_gr); struct group * gr_dup(const struct group *_gr); struct group * gr_add(const struct group *_gr, const char *_newmember); int gr_equal(const struct group *_gr1, const struct group *_gr2); void gr_fini(void); int gr_init(const char *_dir, const char *_master); int gr_lock(void); char *gr_make(const struct group *_gr); int gr_mkdb(void); struct group * gr_scan(const char *_line); int gr_tmp(int _mdf); #endif #ifdef _UFS_UFS_QUOTA_H_ struct fstab; struct quotafile; int quota_check_path(const struct quotafile *_qf, const char *_path); void quota_close(struct quotafile *_qf); int quota_convert(struct quotafile *_qf, int _wordsize); const char * quota_fsname(const struct quotafile *_qf); int quota_maxid(struct quotafile *_qf); int quota_off(struct quotafile *_qf); int quota_on(struct quotafile *_qf); struct quotafile * quota_open(struct fstab *_fs, int _quotatype, int _openflags); const char * quota_qfname(const struct quotafile *_qf); int quota_read(struct quotafile *_qf, struct dqblk *_dqb, int _id); int quota_write_limits(struct quotafile *_qf, struct dqblk *_dqb, int _id); int quota_write_usage(struct quotafile *_qf, struct dqblk *_dqb, int _id); #endif __END_DECLS /* fparseln(3) */ #define FPARSELN_UNESCESC 0x01 #define FPARSELN_UNESCCONT 0x02 #define FPARSELN_UNESCCOMM 0x04 #define FPARSELN_UNESCREST 0x08 #define FPARSELN_UNESCALL 0x0f /* Flags for hexdump(3). */ #define HD_COLUMN_MASK 0xff #define HD_DELIM_MASK 0xff00 #define HD_OMIT_COUNT (1 << 16) #define HD_OMIT_HEX (1 << 17) #define HD_OMIT_CHARS (1 << 18) /* Values for humanize_number(3)'s flags parameter. */ #define HN_DECIMAL 0x01 #define HN_NOSPACE 0x02 #define HN_B 0x04 #define HN_DIVISOR_1000 0x08 #define HN_IEC_PREFIXES 0x10 /* Values for humanize_number(3)'s scale parameter. */ #define HN_GETSCALE 0x10 #define HN_AUTOSCALE 0x20 /* Return values from realhostname(). */ #define HOSTNAME_FOUND 0 #define HOSTNAME_INCORRECTNAME 1 #define HOSTNAME_INVALIDADDR 2 #define HOSTNAME_INVALIDNAME 3 /* Flags for pw_scan(). */ #define PWSCAN_MASTER 0x01 #define PWSCAN_WARN 0x02 /* Return values from uu_lock(). */ #define UU_LOCK_INUSE 1 #define UU_LOCK_OK 0 #define UU_LOCK_OPEN_ERR (-1) #define UU_LOCK_READ_ERR (-2) #define UU_LOCK_CREAT_ERR (-3) #define UU_LOCK_WRITE_ERR (-4) #define UU_LOCK_LINK_ERR (-5) #define UU_LOCK_TRY_ERR (-6) #define UU_LOCK_OWNER_ERR (-7) #endif /* !_LIBUTIL_H_ */ Index: head/lib/libutil/pw_util.3 =================================================================== --- head/lib/libutil/pw_util.3 (revision 336745) +++ head/lib/libutil/pw_util.3 (revision 336746) @@ -1,287 +1,301 @@ .\" Copyright (c) 2012 Baptiste Daroussin .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" -.Dd July 02, 2015 +.Dd July 26, 2018 .Dt PW_UTIL 3 .Os .Sh NAME .Nm pw_copy , .Nm pw_dup , .Nm pw_edit , .Nm pw_equal , .Nm pw_fini , .Nm pw_init , .Nm pw_make , .Nm pw_make_v7 , .Nm pw_mkdb , .Nm pw_lock , .Nm pw_scan , .Nm pw_tempname , .Nm pw_tmp .Nd "functions for passwd file handling" .Sh LIBRARY .Lb libutil .Sh SYNOPSIS .In pwd.h .In libutil.h .Ft int .Fn pw_copy "int ffd" "int tfd" "const struct passwd *pw" "struct passwd *oldpw" .Ft "struct passwd *" .Fn pw_dup "const struct passwd *pw" .Ft int .Fn pw_edit "int nosetuid" .Ft int .Fn pw_equal "const struct passwd *pw1" "const struct passwd *pw2" .Ft void .Fn pw_fini "void" .Ft int .Fn pw_init "const char *dir" const char *master" +.Ft void +.Fn pw_initpwd "struct passwd *pw" .Ft "char *" .Fn pw_make "const struct passwd *pw" .Ft "char *" .Fn pw_make_v7 "const struct passwd *pw" .Ft int .Fn pw_mkdb "const char *user" .Ft int .Fn pw_lock "void" .Ft "struct passwd *" .Fn pw_scan "const char *line" "int flags" .Ft "const char *" .Fn pw_tempname "void" .Ft int .Fn pw_tmp "int mfd" .Sh DESCRIPTION The .Fn pw_copy function reads a password file from .Vt ffd and writes it back out to .Vt tfd possibly with modifications: .Bl -dash .It If .Fa pw is .Dv NULL and .Fa oldpw is not .Dv NULL , then the record represented by .Fa oldpw will not be copied (corresponding to user deletion). .It If .Fa pw and .Fa oldpw are not .Dv NULL then the record corresponding to .Fa pw will be replaced by the record corresponding to .Fa oldpw . .It If .Vt pw is set and .Vt oldpw is .Dv NULL then the record corresponding to .Vt pw will be appended (corresponding to user addition). .El .Pp The .Fn pw_copy function returns -1 in case of failure otherwise 0. .Pp The .Fn pw_dup function duplicates the .Vt struct passwd pointed to by .Fa pw and returns a pointer to the copy, or .Dv NULL in case of failure. The new .Vt struct passwd is allocated with .Xr malloc 3 , and it is the caller's responsibility to free it with .Xr free 3 . .Pp The .Fn pw_edit function invokes the command specified by the .Ev EDITOR environment variable (or .Pa /usr/bin/vi if .Ev EDITOR is not defined) on a temporary copy of the master password file created by .Fn pw_tmp . If the file was modified, .Fn pw_edit installs it and regenerates the password database. The .Fn pw_edit function returns -1 in case of failure, 0 if the file was not modified, and a non-zero positive number if the file was modified and successfully installed. .Pp The .Fn pw_equal function compares two .Vt struct passwd and returns 0 if they are equal. .Pp The .Fn pw_fini function destroy the temporary file created by .Fn pw_tmp if any, kills any running instance of .Ev EDITOR executed by .Fn pw_edit if any, and closes the lock created by .Fn pw_lock if any. .Pp The .Fn pw_init -initialize the static variable representing the path a password file. +initializes the static variable representing the path to a password file. .Fa dir is the directory where the password file is located. If set to .Dv NULL , it will default to .Pa /etc . .Fa master is the name of the password file. If set to .Dv NULL? it will default to .Pa master.passwd +.Pp +The +.Fn pw_initpwd +function initializes the +.Vt passwd +struct to canonical values. +The entire structure is zeroed, then +.Va pw_uid +and +.Va pw_gid +are set to -1, and all string pointers are set to point at +an internally-defined zero-length string. .Pp The .Fn pw_make function creates a properly formatted .Bx .Xr passwd 5 line from a .Vt struct passwd , and returns a pointer to the resulting string. The string is allocated with .Xr malloc 3 , and it is the caller's responsibility to free it with .Xr free 3 . .Pp The .Fn pw_make_v7 function creates a properly formatted .Ux V7 .Xr passwd 5 line from a .Vt struct passwd , and returns a pointer to the resulting string. The string is allocated with .Xr malloc 3 , and it is the caller's responsibility to free it with .Xr free 3 . .Pp The .Fn pw_mkdb function regenerates the password database by running .Xr pwd_mkdb 8 . If .Fa user only the record corresponding to that user will be updated. The .Fn pw_mkdb function returns 0 in case of success and -1 in case of failure. .Pp The .Fn pw_lock function locks the master password file. It returns a file descriptor to the master password file on success and -1 on failure. .Pp The .Fn pw_scan function is a wrapper around the internal libc function .Fn __pw_scan . It scans the master password file for a line corresponding to the .Fa line provided and return a .Vt struct passwd if it matched an existing record. In case of failure, it returns .Dv NULL . Otherwise, it returns a pointer to a .Vt struct passwd containing the matching record. The .Vt struct passwd is allocated with .Xr malloc 3 , and it is the caller's responsibility to free it with .Xr free 3 . .Pp The .Fn pw_tempname function returns the temporary name of the masterfile created via .Fn pw_tmp . .Pp The .Fn pw_tmp creates and opens a presumably safe temporary password file. If .Fa mfd is a file descriptor to an open password file, it will be read and written back to the temporary password file. Otherwise if should be set -1. The .Fn pw_tmp returns an open file descriptor to the temporary password file or -1 in case of failure. .Sh AUTHORS Portions of this software were developed for the .Fx Project by ThinkSec AS and Network Associates Laboratories, the Security Research Division of Network Associates, Inc.\& under DARPA/SPAWAR contract N66001-01-C-8035 .Pq Dq CBOSS , as part of the DARPA CHATS research program. .Pp This manual page was written by .An Baptiste Daroussin Aq Mt bapt@FreeBSD.org . Index: head/lib/libutil/pw_util.c =================================================================== --- head/lib/libutil/pw_util.c (revision 336745) +++ head/lib/libutil/pw_util.c (revision 336746) @@ -1,672 +1,681 @@ /*-- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. * * Portions of this software were developed for the FreeBSD Project by * ThinkSec AS and NAI Labs, 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. 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. */ #include __FBSDID("$FreeBSD$"); __SCCSID("@(#)pw_util.c 8.3 (Berkeley) 4/2/94"); /* * This file is used by all the "password" programs; vipw(8), chpass(1), * and passwd(1). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libutil.h" static pid_t editpid = -1; static int lockfd = -1; static char masterpasswd[PATH_MAX]; static char passwd_dir[PATH_MAX]; static char tempname[PATH_MAX]; static int initialized; #if 0 void pw_cont(int sig) { if (editpid != -1) kill(editpid, sig); } #endif /* * Initialize statics and set limits, signals & umask to try to avoid * interruptions, crashes etc. that might expose passord data. */ int pw_init(const char *dir, const char *master) { #if 0 struct rlimit rlim; #endif if (dir == NULL) { strcpy(passwd_dir, _PATH_ETC); } else { if (strlen(dir) >= sizeof(passwd_dir)) { errno = ENAMETOOLONG; return (-1); } strcpy(passwd_dir, dir); } if (master == NULL) { if (dir == NULL) { strcpy(masterpasswd, _PATH_MASTERPASSWD); } else if (snprintf(masterpasswd, sizeof(masterpasswd), "%s/%s", passwd_dir, _MASTERPASSWD) > (int)sizeof(masterpasswd)) { errno = ENAMETOOLONG; return (-1); } } else { if (strlen(master) >= sizeof(masterpasswd)) { errno = ENAMETOOLONG; return (-1); } strcpy(masterpasswd, master); } /* * The code that follows is extremely disruptive to the calling * process, and is therefore disabled until someone can conceive * of a realistic scenario where it would fend off a compromise. * Race conditions concerning the temporary files can be guarded * against in other ways than masking signals (by checking stat(2) * results after creation). */ #if 0 /* Unlimited resource limits. */ rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; (void)setrlimit(RLIMIT_CPU, &rlim); (void)setrlimit(RLIMIT_FSIZE, &rlim); (void)setrlimit(RLIMIT_STACK, &rlim); (void)setrlimit(RLIMIT_DATA, &rlim); (void)setrlimit(RLIMIT_RSS, &rlim); /* Don't drop core (not really necessary, but GP's). */ rlim.rlim_cur = rlim.rlim_max = 0; (void)setrlimit(RLIMIT_CORE, &rlim); /* Turn off signals. */ (void)signal(SIGALRM, SIG_IGN); (void)signal(SIGHUP, SIG_IGN); (void)signal(SIGINT, SIG_IGN); (void)signal(SIGPIPE, SIG_IGN); (void)signal(SIGQUIT, SIG_IGN); (void)signal(SIGTERM, SIG_IGN); (void)signal(SIGCONT, pw_cont); /* Create with exact permissions. */ (void)umask(0); #endif initialized = 1; return (0); } /* * Lock the master password file. */ int pw_lock(void) { if (*masterpasswd == '\0') return (-1); /* * If the master password file doesn't exist, the system is hosed. * Might as well try to build one. Set the close-on-exec bit so * that users can't get at the encrypted passwords while editing. * Open should allow flock'ing the file; see 4.4BSD. XXX */ for (;;) { struct stat st; lockfd = flopen(masterpasswd, O_RDONLY|O_NONBLOCK|O_CLOEXEC, 0); if (lockfd == -1) { if (errno == EWOULDBLOCK) { errx(1, "the password db file is busy"); } else { err(1, "could not lock the passwd file: "); } } /* * If the password file was replaced while we were trying to * get the lock, our hardlink count will be 0 and we have to * close and retry. */ if (fstat(lockfd, &st) == -1) err(1, "fstat() failed: "); if (st.st_nlink != 0) break; close(lockfd); lockfd = -1; } return (lockfd); } /* * Create and open a presumably safe temp file for editing the password * data, and copy the master password file into it. */ int pw_tmp(int mfd) { char buf[8192]; ssize_t nr; const char *p; int tfd; if (*masterpasswd == '\0') return (-1); if ((p = strrchr(masterpasswd, '/'))) ++p; else p = masterpasswd; if (snprintf(tempname, sizeof(tempname), "%.*spw.XXXXXX", (int)(p - masterpasswd), masterpasswd) >= (int)sizeof(tempname)) { errno = ENAMETOOLONG; return (-1); } if ((tfd = mkostemp(tempname, 0)) == -1) return (-1); if (mfd != -1) { while ((nr = read(mfd, buf, sizeof(buf))) > 0) if (write(tfd, buf, (size_t)nr) != nr) break; if (nr != 0) { unlink(tempname); *tempname = '\0'; close(tfd); return (-1); } } return (tfd); } /* * Regenerate the password database. */ int pw_mkdb(const char *user) { int pstat; pid_t pid; (void)fflush(stderr); switch ((pid = fork())) { case -1: return (-1); case 0: /* child */ if (user == NULL) execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", "-d", passwd_dir, tempname, (char *)NULL); else execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", "-d", passwd_dir, "-u", user, tempname, (char *)NULL); _exit(1); /* NOTREACHED */ default: /* parent */ break; } if (waitpid(pid, &pstat, 0) == -1) return (-1); if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) return (0); errno = 0; return (-1); } /* * Edit the temp file. Return -1 on error, >0 if the file was modified, 0 * if it was not. */ int pw_edit(int notsetuid) { struct sigaction sa, sa_int, sa_quit; sigset_t oldsigset, nsigset; struct stat st1, st2; const char *editor; int pstat; if ((editor = getenv("EDITOR")) == NULL) editor = _PATH_VI; if (stat(tempname, &st1) == -1) return (-1); sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGINT, &sa, &sa_int); sigaction(SIGQUIT, &sa, &sa_quit); sigemptyset(&nsigset); sigaddset(&nsigset, SIGCHLD); sigprocmask(SIG_BLOCK, &nsigset, &oldsigset); switch ((editpid = fork())) { case -1: return (-1); case 0: sigaction(SIGINT, &sa_int, NULL); sigaction(SIGQUIT, &sa_quit, NULL); sigprocmask(SIG_SETMASK, &oldsigset, NULL); if (notsetuid) { (void)setgid(getgid()); (void)setuid(getuid()); } errno = 0; execlp(editor, editor, tempname, (char *)NULL); _exit(errno); default: /* parent */ break; } for (;;) { if (waitpid(editpid, &pstat, WUNTRACED) == -1) { if (errno == EINTR) continue; unlink(tempname); editpid = -1; break; } else if (WIFSTOPPED(pstat)) { raise(WSTOPSIG(pstat)); } else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) { editpid = -1; break; } else { unlink(tempname); editpid = -1; break; } } sigaction(SIGINT, &sa_int, NULL); sigaction(SIGQUIT, &sa_quit, NULL); sigprocmask(SIG_SETMASK, &oldsigset, NULL); if (stat(tempname, &st2) == -1) return (-1); return (st1.st_mtim.tv_sec != st2.st_mtim.tv_sec || st1.st_mtim.tv_nsec != st2.st_mtim.tv_nsec); } /* * Clean up. Preserve errno for the caller's convenience. */ void pw_fini(void) { int serrno, status; if (!initialized) return; initialized = 0; serrno = errno; if (editpid != -1) { kill(editpid, SIGTERM); kill(editpid, SIGCONT); waitpid(editpid, &status, 0); editpid = -1; } if (*tempname != '\0') { unlink(tempname); *tempname = '\0'; } if (lockfd != -1) close(lockfd); errno = serrno; } /* * Compares two struct pwds. */ int pw_equal(const struct passwd *pw1, const struct passwd *pw2) { return (strcmp(pw1->pw_name, pw2->pw_name) == 0 && pw1->pw_uid == pw2->pw_uid && pw1->pw_gid == pw2->pw_gid && strcmp(pw1->pw_class, pw2->pw_class) == 0 && pw1->pw_change == pw2->pw_change && pw1->pw_expire == pw2->pw_expire && strcmp(pw1->pw_gecos, pw2->pw_gecos) == 0 && strcmp(pw1->pw_dir, pw2->pw_dir) == 0 && strcmp(pw1->pw_shell, pw2->pw_shell) == 0); } /* * Make a passwd line out of a struct passwd. */ char * pw_make(const struct passwd *pw) { char *line; asprintf(&line, "%s:%s:%ju:%ju:%s:%ju:%ju:%s:%s:%s", pw->pw_name, pw->pw_passwd, (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid, pw->pw_class, (uintmax_t)pw->pw_change, (uintmax_t)pw->pw_expire, pw->pw_gecos, pw->pw_dir, pw->pw_shell); return (line); } /* * Make a passwd line (in v7 format) out of a struct passwd */ char * pw_make_v7(const struct passwd *pw) { char *line; asprintf(&line, "%s:*:%ju:%ju:%s:%s:%s", pw->pw_name, (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid, pw->pw_gecos, pw->pw_dir, pw->pw_shell); return (line); } /* * Copy password file from one descriptor to another, replacing, deleting * or adding a single record on the way. */ int pw_copy(int ffd, int tfd, const struct passwd *pw, struct passwd *old_pw) { char *buf, *end, *line, *p, *q, *r, *tmp; struct passwd *fpw; const struct passwd *spw; size_t len, size; int eof, readlen; char t; if (old_pw == NULL && pw == NULL) return (-1); spw = old_pw; /* deleting a user */ if (pw == NULL) { line = NULL; } else { if ((line = pw_make(pw)) == NULL) return (-1); } /* adding a user */ if (spw == NULL) spw = pw; /* initialize the buffer */ if ((buf = malloc(size = 1024)) == NULL) goto err; eof = 0; len = 0; p = q = end = buf; for (;;) { /* find the end of the current line */ for (p = q; q < end && *q != '\0'; ++q) if (*q == '\n') break; /* if we don't have a complete line, fill up the buffer */ if (q >= end) { if (eof) break; while ((size_t)(q - p) >= size) { if ((tmp = reallocarray(buf, 2, size)) == NULL) { warnx("passwd line too long"); goto err; } p = tmp + (p - buf); q = tmp + (q - buf); end = tmp + (end - buf); buf = tmp; size = size * 2; } if (p < end) { q = memmove(buf, p, end - p); end -= p - buf; } else { p = q = end = buf; } readlen = read(ffd, end, size - (end - buf)); if (readlen == -1) goto err; else len = (size_t)readlen; if (len == 0 && p == buf) break; end += len; len = end - buf; if (len < size) { eof = 1; if (len > 0 && buf[len - 1] != '\n') ++len, *end++ = '\n'; } continue; } /* is it a blank line or a comment? */ for (r = p; r < q && isspace(*r); ++r) /* nothing */ ; if (r == q || *r == '#') { /* yep */ if (write(tfd, p, q - p + 1) != q - p + 1) goto err; ++q; continue; } /* is it the one we're looking for? */ t = *q; *q = '\0'; fpw = pw_scan(r, PWSCAN_MASTER); /* * fpw is either the struct passwd for the current line, * or NULL if the line is malformed. */ *q = t; if (fpw == NULL || strcmp(fpw->pw_name, spw->pw_name) != 0) { /* nope */ if (fpw != NULL) free(fpw); if (write(tfd, p, q - p + 1) != q - p + 1) goto err; ++q; continue; } if (old_pw && !pw_equal(fpw, old_pw)) { warnx("entry inconsistent"); free(fpw); errno = EINVAL; /* hack */ goto err; } free(fpw); /* it is, replace or remove it */ if (line != NULL) { len = strlen(line); if (write(tfd, line, len) != (int)len) goto err; } else { /* when removed, avoid the \n */ q++; } /* we're done, just copy the rest over */ for (;;) { if (write(tfd, q, end - q) != end - q) goto err; q = buf; readlen = read(ffd, buf, size); if (readlen == 0) break; else len = (size_t)readlen; if (readlen == -1) goto err; end = buf + len; } goto done; } /* if we got here, we didn't find the old entry */ if (line == NULL) { errno = ENOENT; goto err; } len = strlen(line); if ((size_t)write(tfd, line, len) != len || write(tfd, "\n", 1) != 1) goto err; done: free(line); free(buf); return (0); err: free(line); free(buf); return (-1); } /* * Return the current value of tempname. */ const char * pw_tempname(void) { return (tempname); } /* * Duplicate a struct passwd. */ struct passwd * pw_dup(const struct passwd *pw) { char *dst; struct passwd *npw; ssize_t len; len = sizeof(*npw); if (pw->pw_name != NULL) len += strlen(pw->pw_name) + 1; if (pw->pw_passwd != NULL) len += strlen(pw->pw_passwd) + 1; if (pw->pw_class != NULL) len += strlen(pw->pw_class) + 1; if (pw->pw_gecos != NULL) len += strlen(pw->pw_gecos) + 1; if (pw->pw_dir != NULL) len += strlen(pw->pw_dir) + 1; if (pw->pw_shell != NULL) len += strlen(pw->pw_shell) + 1; if ((npw = malloc((size_t)len)) == NULL) return (NULL); memcpy(npw, pw, sizeof(*npw)); dst = (char *)npw + sizeof(*npw); if (pw->pw_name != NULL) { npw->pw_name = dst; dst = stpcpy(npw->pw_name, pw->pw_name) + 1; } if (pw->pw_passwd != NULL) { npw->pw_passwd = dst; dst = stpcpy(npw->pw_passwd, pw->pw_passwd) + 1; } if (pw->pw_class != NULL) { npw->pw_class = dst; dst = stpcpy(npw->pw_class, pw->pw_class) + 1; } if (pw->pw_gecos != NULL) { npw->pw_gecos = dst; dst = stpcpy(npw->pw_gecos, pw->pw_gecos) + 1; } if (pw->pw_dir != NULL) { npw->pw_dir = dst; dst = stpcpy(npw->pw_dir, pw->pw_dir) + 1; } if (pw->pw_shell != NULL) { npw->pw_shell = dst; dst = stpcpy(npw->pw_shell, pw->pw_shell) + 1; } return (npw); } #include "pw_scan.h" /* - * Wrapper around an internal libc function + * Wrapper around some internal libc functions. */ + +void +pw_initpwd(struct passwd *pw) +{ + + __pw_initpwd(pw); +} + struct passwd * pw_scan(const char *line, int flags) { struct passwd pw, *ret; char *bp; if ((bp = strdup(line)) == NULL) return (NULL); + __pw_initpwd(&pw); if (!__pw_scan(bp, &pw, flags)) { free(bp); return (NULL); } ret = pw_dup(&pw); free(bp); return (ret); }