#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <db.h>
#include <syslog.h>
#include <pwd.h>
#include <errno.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <netgroup.h>
#ifdef YP
#include <stdio.h>
#include <rpc/rpc.h>
#include <rpcsvc/yp.h>
#include <rpcsvc/ypclnt.h>
#include "ypinternal.h"
#include "ypexclude.h"
#endif
#include "thread_private.h"
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
struct pw_storage {
struct passwd pw;
uid_t uid;
char name[_PW_NAME_LEN + 1];
char pwbuf[_PW_BUF_LEN];
};
_THREAD_PRIVATE_KEY(pw);
static DB *_pw_db;
static struct pw_storage *_pw_storage = MAP_FAILED;
static int _pw_keynum;
static int _pw_stayopen;
static int _pw_flags;
static int __hashpw(DBT *, char *buf, size_t buflen, struct passwd *, int *);
static int __initdb(int);
static struct passwd *_pwhashbyname(const char *name, char *buf,
size_t buflen, struct passwd *pw, int *);
static struct passwd *_pwhashbyuid(uid_t uid, char *buf,
size_t buflen, struct passwd *pw, int *);
#ifdef YP
static char *__ypdomain;
enum _ypmode { YPMODE_NONE, YPMODE_FULL, YPMODE_USER, YPMODE_NETGRP };
static enum _ypmode __ypmode;
static char *__ypcurrent;
static int __ypcurrentlen;
static int __yp_pw_flags;
static int __getpwent_has_yppw = -1;
static struct _ypexclude *__ypexhead;
static int __has_yppw(void);
static int __has_ypmaster(void);
static int __ypparse(struct passwd *pw, char *s, int);
#define LOOKUP_BYNAME 0
#define LOOKUP_BYUID 1
static struct passwd *__yppwlookup(int, char *, uid_t, struct passwd *,
char *, size_t, int *);
#define PASSWD_BYNAME \
(__has_ypmaster() ? "master.passwd.byname" : "passwd.byname")
#define PASSWD_BYUID \
(__has_ypmaster() ? "master.passwd.byuid" : "passwd.byuid")
static struct passwd *__ypproto;
static void __ypproto_set(struct passwd *, struct pw_storage *, int, int *);
static void
__ypproto_set(struct passwd *pw, struct pw_storage *buf, int flags,
int *yp_pw_flagsp)
{
char *ptr = buf->pwbuf;
__ypproto = &buf->pw;
if (pw->pw_name && (pw->pw_name)[0]) {
bcopy(pw->pw_name, ptr, strlen(pw->pw_name) + 1);
__ypproto->pw_name = ptr;
ptr += (strlen(pw->pw_name) + 1);
} else
__ypproto->pw_name = NULL;
if (pw->pw_passwd && (pw->pw_passwd)[0]) {
bcopy(pw->pw_passwd, ptr, strlen(pw->pw_passwd) + 1);
__ypproto->pw_passwd = ptr;
ptr += (strlen(pw->pw_passwd) + 1);
} else
__ypproto->pw_passwd = NULL;
__ypproto->pw_uid = pw->pw_uid;
__ypproto->pw_gid = pw->pw_gid;
__ypproto->pw_change = pw->pw_change;
__ypproto->pw_class = "";
if (pw->pw_gecos && (pw->pw_gecos)[0]) {
bcopy(pw->pw_gecos, ptr, strlen(pw->pw_gecos) + 1);
__ypproto->pw_gecos = ptr;
ptr += (strlen(pw->pw_gecos) + 1);
} else
__ypproto->pw_gecos = NULL;
if (pw->pw_dir && (pw->pw_dir)[0]) {
bcopy(pw->pw_dir, ptr, strlen(pw->pw_dir) + 1);
__ypproto->pw_dir = ptr;
ptr += (strlen(pw->pw_dir) + 1);
} else
__ypproto->pw_dir = NULL;
if (pw->pw_shell && (pw->pw_shell)[0]) {
bcopy(pw->pw_shell, ptr, strlen(pw->pw_shell) + 1);
__ypproto->pw_shell = ptr;
ptr += (strlen(pw->pw_shell) + 1);
} else
__ypproto->pw_shell = NULL;
__ypproto->pw_expire = pw->pw_expire;
*yp_pw_flagsp = flags;
}
static int
__ypparse(struct passwd *pw, char *s, int yp_pw_flags)
{
char *bp, *cp, *endp;
u_long ul;
int count = 0;
bp = s;
while (*bp != '\0') {
if (*bp++ == ':')
count++;
}
bp = s;
pw->pw_name = strsep(&bp, ":\n");
pw->pw_passwd = strsep(&bp, ":\n");
if (!(cp = strsep(&bp, ":\n")))
return (1);
ul = strtoul(cp, &endp, 10);
if (endp == cp || *endp != '\0' || ul >= UID_MAX)
return (1);
pw->pw_uid = (uid_t)ul;
if (!(cp = strsep(&bp, ":\n")))
return (1);
ul = strtoul(cp, &endp, 10);
if (endp == cp || *endp != '\0' || ul >= GID_MAX)
return (1);
pw->pw_gid = (gid_t)ul;
if (count == 9) {
long l;
pw->pw_class = strsep(&bp, ":\n");
if (!(cp = strsep(&bp, ":\n")))
return (1);
l = strtol(cp, &endp, 10);
if (endp == cp || *endp != '\0' || l >= INT_MAX || l <= INT_MIN)
return (1);
pw->pw_change = (time_t)l;
if (!(cp = strsep(&bp, ":\n")))
return (1);
l = strtol(cp, &endp, 10);
if (endp == cp || *endp != '\0' || l >= INT_MAX || l <= INT_MIN)
return (1);
pw->pw_expire = (time_t)l;
} else {
pw->pw_class = "";
pw->pw_change = 0;
pw->pw_expire = 0;
}
pw->pw_gecos = strsep(&bp, ":\n");
pw->pw_dir = strsep(&bp, ":\n");
pw->pw_shell = strsep(&bp, ":\n");
if (__ypproto) {
if (!(yp_pw_flags & _PASSWORD_NOUID))
pw->pw_uid = __ypproto->pw_uid;
if (!(yp_pw_flags & _PASSWORD_NOGID))
pw->pw_gid = __ypproto->pw_gid;
if (__ypproto->pw_gecos)
pw->pw_gecos = __ypproto->pw_gecos;
if (__ypproto->pw_dir)
pw->pw_dir = __ypproto->pw_dir;
if (__ypproto->pw_shell)
pw->pw_shell = __ypproto->pw_shell;
}
return (0);
}
#endif
static struct passwd *
__get_pw_buf(char **bufp, size_t *buflenp, uid_t uid, const char *name)
{
bool remap = true;
if (_pw_storage != MAP_FAILED) {
if (name != NULL) {
if (strcmp(_pw_storage->name, name) == 0) {
#ifdef PWDEBUG
struct syslog_data sdata = SYSLOG_DATA_INIT;
syslog_r(LOG_CRIT | LOG_CONS, &sdata,
"repeated passwd lookup of user \"%s\"",
name);
#endif
remap = false;
}
} else if (uid != (uid_t)-1) {
if (_pw_storage->uid == uid) {
#ifdef PWDEBUG
struct syslog_data sdata = SYSLOG_DATA_INIT;
syslog_r(LOG_CRIT | LOG_CONS, &sdata,
"repeated passwd lookup of uid %u",
uid);
#endif
remap = false;
}
}
if (remap)
munmap(_pw_storage, sizeof(*_pw_storage));
}
if (remap) {
_pw_storage = mmap(NULL, sizeof(*_pw_storage),
PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
if (_pw_storage == MAP_FAILED)
return NULL;
if (name != NULL)
strlcpy(_pw_storage->name, name, sizeof(_pw_storage->name));
_pw_storage->uid = uid;
}
*bufp = _pw_storage->pwbuf;
*buflenp = sizeof(_pw_storage->pwbuf);
return &_pw_storage->pw;
}
struct passwd *
getpwent(void)
{
#ifdef YP
static char *name = NULL;
char *map;
#endif
char bf[1 + sizeof(_pw_keynum)];
struct passwd *pw, *ret = NULL;
char *pwbuf;
size_t buflen;
DBT key;
_THREAD_PRIVATE_MUTEX_LOCK(pw);
if (!_pw_db && !__initdb(0))
goto done;
if ((pw = __get_pw_buf(&pwbuf, &buflen, -1, NULL)) == NULL)
goto done;
#ifdef YP
map = PASSWD_BYNAME;
if (__getpwent_has_yppw == -1)
__getpwent_has_yppw = __has_yppw();
again:
if (__getpwent_has_yppw && (__ypmode != YPMODE_NONE)) {
const char *user, *host, *dom;
int keylen, datalen, r, s;
char *key, *data = NULL;
if (!__ypdomain)
yp_get_default_domain(&__ypdomain);
switch (__ypmode) {
case YPMODE_FULL:
if (__ypcurrent) {
r = yp_next(__ypdomain, map,
__ypcurrent, __ypcurrentlen,
&key, &keylen, &data, &datalen);
free(__ypcurrent);
__ypcurrent = NULL;
if (r != 0) {
__ypmode = YPMODE_NONE;
free(data);
goto again;
}
__ypcurrent = key;
__ypcurrentlen = keylen;
} else {
r = yp_first(__ypdomain, map,
&__ypcurrent, &__ypcurrentlen,
&data, &datalen);
if (r != 0 ||
__ypcurrentlen > buflen) {
__ypmode = YPMODE_NONE;
free(data);
goto again;
}
}
bcopy(data, pwbuf, datalen);
free(data);
break;
case YPMODE_NETGRP:
s = getnetgrent(&host, &user, &dom);
if (s == 0) {
endnetgrent();
__ypmode = YPMODE_NONE;
goto again;
}
if (user && *user) {
r = yp_match(__ypdomain, map,
user, strlen(user), &data, &datalen);
} else
goto again;
if (r != 0 ||
__ypcurrentlen > buflen) {
free(data);
goto again;
}
bcopy(data, pwbuf, datalen);
free(data);
break;
case YPMODE_USER:
if (name) {
r = yp_match(__ypdomain, map,
name, strlen(name), &data, &datalen);
__ypmode = YPMODE_NONE;
free(name);
name = NULL;
if (r != 0 ||
__ypcurrentlen > buflen) {
free(data);
goto again;
}
bcopy(data, pwbuf, datalen);
free(data);
} else {
__ypmode = YPMODE_NONE;
goto again;
}
break;
case YPMODE_NONE:
break;
}
pwbuf[datalen] = '\0';
if (__ypparse(pw, pwbuf, __yp_pw_flags))
goto again;
ret = pw;
goto done;
}
#endif
++_pw_keynum;
bf[0] = _PW_KEYBYNUM;
bcopy((char *)&_pw_keynum, &bf[1], sizeof(_pw_keynum));
key.data = (u_char *)bf;
key.size = 1 + sizeof(_pw_keynum);
if (__hashpw(&key, pwbuf, buflen, pw, &_pw_flags)) {
#ifdef YP
static struct pw_storage __yppbuf;
const char *user, *host, *dom;
if (__getpwent_has_yppw) {
if (pw->pw_name[0] == '+') {
switch (pw->pw_name[1]) {
case '\0':
__ypmode = YPMODE_FULL;
break;
case '@':
__ypmode = YPMODE_NETGRP;
setnetgrent(pw->pw_name + 2);
break;
default:
__ypmode = YPMODE_USER;
name = strdup(pw->pw_name + 1);
break;
}
__ypproto_set(pw, &__yppbuf, _pw_flags,
&__yp_pw_flags);
goto again;
} else if (pw->pw_name[0] == '-') {
switch (pw->pw_name[1]) {
case '\0':
break;
case '@':
setnetgrent(pw->pw_name + 2);
while (getnetgrent(&host, &user, &dom)) {
if (user && *user)
__ypexclude_add(&__ypexhead,
user);
}
endnetgrent();
break;
default:
__ypexclude_add(&__ypexhead,
pw->pw_name + 1);
break;
}
goto again;
}
}
#endif
ret = pw;
goto done;
}
done:
_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
return (ret);
}
#ifdef YP
static int
__has_yppw(void)
{
DBT key, data, pkey, pdata;
char bf[2];
key.data = (u_char *)_PW_YPTOKEN;
key.size = strlen(_PW_YPTOKEN);
bf[0] = _PW_KEYBYNAME;
bf[1] = '+';
pkey.data = (u_char *)bf;
pkey.size = sizeof(bf);
if ((_pw_db->get)(_pw_db, &key, &data, 0) &&
(_pw_db->get)(_pw_db, &pkey, &pdata, 0))
return (0);
return (1);
}
static int
__has_ypmaster(void)
{
int keylen, resultlen;
char *key, *result;
static int checked = -1;
static uid_t saved_uid, saved_euid;
uid_t uid = getuid(), euid = geteuid();
if (checked != -1 && saved_uid == uid && saved_euid == euid)
return (checked);
if (euid != 0) {
saved_uid = uid;
saved_euid = euid;
checked = 0;
return (checked);
}
if (!__ypdomain)
yp_get_default_domain(&__ypdomain);
if (yp_first(__ypdomain, "master.passwd.byname",
&key, &keylen, &result, &resultlen)) {
saved_uid = uid;
saved_euid = euid;
checked = 0;
return (checked);
}
free(result);
free(key);
saved_uid = uid;
saved_euid = euid;
checked = 1;
return (checked);
}
static struct passwd *
__yppwlookup(int lookup, char *name, uid_t uid, struct passwd *pw,
char *buf, size_t buflen, int *flagsp)
{
char bf[1 + _PW_NAME_LEN], *ypcurrent = NULL, *map = NULL;
int yp_pw_flags = 0, ypcurrentlen, r, s = -1, pw_keynum;
static struct pw_storage __yppbuf;
struct _ypexclude *ypexhead = NULL;
const char *host, *user, *dom;
DBT key;
for (pw_keynum = 1; pw_keynum; pw_keynum++) {
bf[0] = _PW_KEYBYNUM;
bcopy((char *)&pw_keynum, &bf[1], sizeof(pw_keynum));
key.data = (u_char *)bf;
key.size = 1 + sizeof(pw_keynum);
if (__hashpw(&key, buf, buflen, pw, flagsp) == 0)
break;
switch (pw->pw_name[0]) {
case '+':
if (!__ypdomain)
yp_get_default_domain(&__ypdomain);
__ypproto_set(pw, &__yppbuf, *flagsp, &yp_pw_flags);
if (!map) {
if (lookup == LOOKUP_BYNAME) {
if ((name = strdup(name)) == NULL) {
pw = NULL;
goto done;
}
map = PASSWD_BYNAME;
} else {
if (asprintf(&name, "%u", uid) == -1) {
pw = NULL;
goto done;
}
map = PASSWD_BYUID;
}
}
switch (pw->pw_name[1]) {
case '\0':
free(ypcurrent);
ypcurrent = NULL;
r = yp_match(__ypdomain, map,
name, strlen(name),
&ypcurrent, &ypcurrentlen);
if (r != 0 || ypcurrentlen > buflen) {
free(ypcurrent);
ypcurrent = NULL;
continue;
}
break;
case '@':
pwnam_netgrp:
free(ypcurrent);
ypcurrent = NULL;
if (s == -1)
setnetgrent(pw->pw_name + 2);
s = getnetgrent(&host, &user, &dom);
if (s == 0) {
endnetgrent();
s = -1;
continue;
} else {
if (user && *user) {
r = yp_match(__ypdomain, map,
user, strlen(user),
&ypcurrent, &ypcurrentlen);
} else
goto pwnam_netgrp;
if (r != 0 || ypcurrentlen > buflen) {
free(ypcurrent);
ypcurrent = NULL;
goto pwnam_netgrp;
}
}
break;
default:
free(ypcurrent);
ypcurrent = NULL;
user = pw->pw_name + 1;
r = yp_match(__ypdomain, map,
user, strlen(user),
&ypcurrent, &ypcurrentlen);
if (r != 0 || ypcurrentlen > buflen) {
free(ypcurrent);
ypcurrent = NULL;
continue;
}
break;
}
bcopy(ypcurrent, buf, ypcurrentlen);
buf[ypcurrentlen] = '\0';
if (__ypparse(pw, buf, yp_pw_flags) ||
__ypexclude_is(&ypexhead, pw->pw_name)) {
if (s == 1)
goto pwnam_netgrp;
continue;
}
break;
case '-':
switch (pw->pw_name[1]) {
case '\0':
break;
case '@':
setnetgrent(pw->pw_name + 2);
while (getnetgrent(&host, &user, &dom)) {
if (user && *user)
__ypexclude_add(&ypexhead, user);
}
endnetgrent();
break;
default:
__ypexclude_add(&ypexhead, pw->pw_name + 1);
break;
}
break;
}
if ((lookup == LOOKUP_BYUID && pw->pw_uid == uid) ||
(lookup == LOOKUP_BYNAME && strcmp(pw->pw_name, name) == 0))
goto done;
if (s == 1)
goto pwnam_netgrp;
continue;
}
pw = NULL;
done:
__ypexclude_free(&ypexhead);
__ypproto = NULL;
free(ypcurrent);
ypcurrent = NULL;
if (map)
free(name);
return (pw);
}
#endif
static struct passwd *
_pwhashbyname(const char *name, char *buf, size_t buflen, struct passwd *pw,
int *flagsp)
{
char bf[1 + _PW_NAME_LEN];
size_t len;
DBT key;
int r;
len = strlen(name);
if (len > _PW_NAME_LEN)
return (NULL);
bf[0] = _PW_KEYBYNAME;
bcopy(name, &bf[1], MINIMUM(len, _PW_NAME_LEN));
key.data = (u_char *)bf;
key.size = 1 + MINIMUM(len, _PW_NAME_LEN);
r = __hashpw(&key, buf, buflen, pw, flagsp);
if (r)
return (pw);
return (NULL);
}
static struct passwd *
_pwhashbyuid(uid_t uid, char *buf, size_t buflen, struct passwd *pw,
int *flagsp)
{
char bf[1 + sizeof(int)];
DBT key;
int r;
bf[0] = _PW_KEYBYUID;
bcopy(&uid, &bf[1], sizeof(uid));
key.data = (u_char *)bf;
key.size = 1 + sizeof(uid);
r = __hashpw(&key, buf, buflen, pw, flagsp);
if (r)
return (pw);
return (NULL);
}
static int
getpwnam_internal(const char *name, struct passwd *pw, char *buf, size_t buflen,
struct passwd **pwretp, bool shadow, bool reentrant)
{
struct passwd *pwret = NULL;
int flags = 0, *flagsp = &flags;
int my_errno = 0;
int saved_errno, tmp_errno;
_THREAD_PRIVATE_MUTEX_LOCK(pw);
saved_errno = errno;
errno = 0;
if (!_pw_db && !__initdb(shadow))
goto fail;
if (!reentrant) {
if ((pw = __get_pw_buf(&buf, &buflen, -1, name)) == NULL)
goto fail;
flagsp = &_pw_flags;
}
#ifdef YP
if (__has_yppw())
pwret = __yppwlookup(LOOKUP_BYNAME, (char *)name, 0, pw,
buf, buflen, flagsp);
#endif
if (!pwret)
pwret = _pwhashbyname(name, buf, buflen, pw, flagsp);
if (!_pw_stayopen) {
tmp_errno = errno;
(void)(_pw_db->close)(_pw_db);
_pw_db = NULL;
errno = tmp_errno;
}
fail:
if (pwretp)
*pwretp = pwret;
if (pwret == NULL)
my_errno = errno;
errno = saved_errno;
_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
return (my_errno);
}
int
getpwnam_r(const char *name, struct passwd *pw, char *buf, size_t buflen,
struct passwd **pwretp)
{
return getpwnam_internal(name, pw, buf, buflen, pwretp, false, true);
}
DEF_WEAK(getpwnam_r);
struct passwd *
getpwnam(const char *name)
{
struct passwd *pw = NULL;
int my_errno;
my_errno = getpwnam_internal(name, NULL, NULL, 0, &pw, false, false);
if (my_errno) {
pw = NULL;
errno = my_errno;
}
return (pw);
}
struct passwd *
getpwnam_shadow(const char *name)
{
struct passwd *pw = NULL;
int my_errno;
my_errno = getpwnam_internal(name, NULL, NULL, 0, &pw, true, false);
if (my_errno) {
pw = NULL;
errno = my_errno;
}
return (pw);
}
DEF_WEAK(getpwnam_shadow);
static int
getpwuid_internal(uid_t uid, struct passwd *pw, char *buf, size_t buflen,
struct passwd **pwretp, bool shadow, bool reentrant)
{
struct passwd *pwret = NULL;
int flags = 0, *flagsp = &flags;
int my_errno = 0;
int saved_errno, tmp_errno;
_THREAD_PRIVATE_MUTEX_LOCK(pw);
saved_errno = errno;
errno = 0;
if (!_pw_db && !__initdb(shadow))
goto fail;
if (!reentrant) {
if ((pw = __get_pw_buf(&buf, &buflen, uid, NULL)) == NULL)
goto fail;
flagsp = &_pw_flags;
}
#ifdef YP
if (__has_yppw())
pwret = __yppwlookup(LOOKUP_BYUID, NULL, uid, pw,
buf, buflen, flagsp);
#endif
if (!pwret)
pwret = _pwhashbyuid(uid, buf, buflen, pw, flagsp);
if (!_pw_stayopen) {
tmp_errno = errno;
(void)(_pw_db->close)(_pw_db);
_pw_db = NULL;
errno = tmp_errno;
}
fail:
if (pwretp)
*pwretp = pwret;
if (pwret == NULL)
my_errno = errno;
errno = saved_errno;
_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
return (my_errno);
}
int
getpwuid_r(uid_t uid, struct passwd *pw, char *buf, size_t buflen,
struct passwd **pwretp)
{
return getpwuid_internal(uid, pw, buf, buflen, pwretp, false, true);
}
DEF_WEAK(getpwuid_r);
struct passwd *
getpwuid(uid_t uid)
{
struct passwd *pw = NULL;
int my_errno;
my_errno = getpwuid_internal(uid, NULL, NULL, 0, &pw, false, false);
if (my_errno) {
pw = NULL;
errno = my_errno;
}
return (pw);
}
struct passwd *
getpwuid_shadow(uid_t uid)
{
struct passwd *pw = NULL;
int my_errno;
my_errno = getpwuid_internal(uid, NULL, NULL, 0, &pw, true, false);
if (my_errno) {
pw = NULL;
errno = my_errno;
}
return (pw);
}
DEF_WEAK(getpwuid_shadow);
int
setpassent(int stayopen)
{
_THREAD_PRIVATE_MUTEX_LOCK(pw);
_pw_keynum = 0;
_pw_stayopen = stayopen;
#ifdef YP
__ypmode = YPMODE_NONE;
free(__ypcurrent);
__ypcurrent = NULL;
__ypexclude_free(&__ypexhead);
__ypproto = NULL;
#endif
_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
return (1);
}
DEF_WEAK(setpassent);
void
setpwent(void)
{
(void) setpassent(0);
}
void
endpwent(void)
{
int saved_errno;
_THREAD_PRIVATE_MUTEX_LOCK(pw);
saved_errno = errno;
_pw_keynum = 0;
if (_pw_db) {
(void)(_pw_db->close)(_pw_db);
_pw_db = NULL;
}
#ifdef YP
__ypmode = YPMODE_NONE;
free(__ypcurrent);
__ypcurrent = NULL;
__ypexclude_free(&__ypexhead);
__ypproto = NULL;
#endif
errno = saved_errno;
_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
}
static int
__initdb(int shadow)
{
static int warned;
int saved_errno = errno, fd;
#ifdef YP
__ypmode = YPMODE_NONE;
__getpwent_has_yppw = -1;
#endif
if (shadow) {
fd = __pledge_open(_PATH_SMP_DB, O_RDONLY | O_CLOEXEC);
if (fd != -1)
_pw_db = __hash_open(NULL, fd, O_RDONLY, 0, NULL, 0);
if (_pw_db == NULL)
close(fd);
}
if (!_pw_db) {
fd = __pledge_open(_PATH_MP_DB, O_RDONLY | O_CLOEXEC);
if (fd != -1)
_pw_db = __hash_open(NULL, fd, O_RDONLY, 0, NULL, 0);
if (_pw_db == NULL)
close(fd);
}
if (_pw_db) {
errno = saved_errno;
return (1);
}
if (!warned) {
saved_errno = errno;
errno = saved_errno;
warned = 1;
}
return (0);
}
static int
__hashpw(DBT *key, char *buf, size_t buflen, struct passwd *pw,
int *flagsp)
{
char *p, *t;
DBT data;
if ((_pw_db->get)(_pw_db, key, &data, 0))
return (0);
p = (char *)data.data;
if (data.size > buflen) {
errno = ERANGE;
return (0);
}
t = buf;
#define EXPAND(e) e = t; while ((*t++ = *p++));
EXPAND(pw->pw_name);
EXPAND(pw->pw_passwd);
bcopy(p, (char *)&pw->pw_uid, sizeof(int));
p += sizeof(int);
bcopy(p, (char *)&pw->pw_gid, sizeof(int));
p += sizeof(int);
bcopy(p, (char *)&pw->pw_change, sizeof(time_t));
p += sizeof(time_t);
EXPAND(pw->pw_class);
EXPAND(pw->pw_gecos);
EXPAND(pw->pw_dir);
EXPAND(pw->pw_shell);
bcopy(p, (char *)&pw->pw_expire, sizeof(time_t));
p += sizeof(time_t);
if (data.size > (p - (char *)data.data)) {
bcopy(p, (char *)flagsp, sizeof(int));
p += sizeof(int);
} else
*flagsp = _PASSWORD_NOUID|_PASSWORD_NOGID;
return (1);
}