From 5a122e6e86e0ef46251a9c01f7ddc67b6fea9557 Mon Sep 17 00:00:00 2001 From: millert Date: Thu, 13 Sep 2018 12:31:15 +0000 Subject: [PATCH] Add uid_from_user() and gid_from_group(), derived from pax's cache.c. It replaces the existing pwcache.c functions user_from_uid(3) and group_from_gid(3) with the pax equivalents. Adapted from NetBSD (mycroft) changes from our own pax's cache.c. OK guenther@ --- include/grp.h | 5 +- include/pwd.h | 5 +- lib/libc/Symbols.list | 2 + lib/libc/gen/getgrent.c | 3 +- lib/libc/gen/pwcache.3 | 59 ++++- lib/libc/gen/pwcache.c | 466 +++++++++++++++++++++++++++++++++------- lib/libc/hidden/grp.h | 5 +- lib/libc/hidden/pwd.h | 3 +- lib/libc/shlib_version | 2 +- 9 files changed, 456 insertions(+), 94 deletions(-) diff --git a/include/grp.h b/include/grp.h index 2a1a6c7f4994..ebf65189a5a1 100644 --- a/include/grp.h +++ b/include/grp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: grp.h,v 1.12 2014/08/31 04:04:38 guenther Exp $ */ +/* $OpenBSD: grp.h,v 1.13 2018/09/13 12:31:15 millert Exp $ */ /* $NetBSD: grp.h,v 1.7 1995/04/29 05:30:40 cgd Exp $ */ /*- @@ -70,7 +70,8 @@ int getgrnam_r(const char *, struct group *, char *, #endif #if __BSD_VISIBLE int setgroupent(int); -char *group_from_gid(gid_t, int); +int gid_from_group(const char *, gid_t *); +const char *group_from_gid(gid_t, int); #endif __END_DECLS diff --git a/include/pwd.h b/include/pwd.h index 6a5322005947..84107349a999 100644 --- a/include/pwd.h +++ b/include/pwd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pwd.h,v 1.25 2017/03/09 10:13:03 fcambus Exp $ */ +/* $OpenBSD: pwd.h,v 1.26 2018/09/13 12:31:15 millert Exp $ */ /* $NetBSD: pwd.h,v 1.9 1996/05/15 21:36:45 jtc Exp $ */ /*- @@ -106,7 +106,8 @@ void endpwent(void); #endif #if __BSD_VISIBLE int setpassent(int); -char *user_from_uid(uid_t, int); +int uid_from_user(const char *, uid_t *); +const char *user_from_uid(uid_t, int); char *bcrypt_gensalt(u_int8_t); char *bcrypt(const char *, const char *); int bcrypt_newhash(const char *, int, char *, size_t); diff --git a/lib/libc/Symbols.list b/lib/libc/Symbols.list index 55500aea43df..7c1000009794 100644 --- a/lib/libc/Symbols.list +++ b/lib/libc/Symbols.list @@ -663,6 +663,7 @@ getpwuid_shadow getttyent getttynam getusershell +gid_from_group glob globfree group_from_gid @@ -798,6 +799,7 @@ ttyname ttyname_r ttyslot ualarm +uid_from_user uname unvis user_from_uid diff --git a/lib/libc/gen/getgrent.c b/lib/libc/gen/getgrent.c index 348e7dd68833..09cb973a075a 100644 --- a/lib/libc/gen/getgrent.c +++ b/lib/libc/gen/getgrent.c @@ -1,4 +1,4 @@ -/* $OpenBSD: getgrent.c,v 1.46 2015/12/01 15:08:25 deraadt Exp $ */ +/* $OpenBSD: getgrent.c,v 1.47 2018/09/13 12:31:15 millert Exp $ */ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. @@ -144,6 +144,7 @@ getgrnam_r(const char *name, struct group *grp, char *buffer, errno = errnosave; return ret; } +DEF_WEAK(getgrnam_r); static struct group * getgrgid_gs(gid_t gid, struct group *p_gr, struct group_storage *gs) diff --git a/lib/libc/gen/pwcache.3 b/lib/libc/gen/pwcache.3 index a0283ed5be31..80bb7cc22d3a 100644 --- a/lib/libc/gen/pwcache.3 +++ b/lib/libc/gen/pwcache.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: pwcache.3,v 1.13 2016/03/26 14:36:37 schwarze Exp $ +.\" $OpenBSD: pwcache.3,v 1.14 2018/09/13 12:31:15 millert Exp $ .\" .\" Copyright (c) 1989, 1991, 1993 .\" The Regents of the University of California. All rights reserved. @@ -27,19 +27,25 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd $Mdocdate: March 26 2016 $ +.Dd $Mdocdate: September 13 2018 $ .Dt USER_FROM_UID 3 .Os .Sh NAME .Nm user_from_uid , +.Nm uid_from_user , .Nm group_from_gid +.Nm gid_from_group .Nd cache password and group entries .Sh SYNOPSIS -.In grp.h .In pwd.h -.Ft char * +.Ft int +.Fn uid_from_user "const char *name" "uid_t *uid" +.Ft const char * .Fn user_from_uid "uid_t uid" "int nouser" -.Ft char * +.In grp.h +.Ft int +.Fn gid_from_group "const char *name" "gid_t *gid" +.Ft const char * .Fn group_from_gid "gid_t gid" "int nogroup" .Sh DESCRIPTION The @@ -60,6 +66,23 @@ unless the argument is non-zero, in which case a null pointer is returned. .Pp The +.Fn uid_from_user +function returns the user ID associated with the argument +.Fa name . +The user ID is cached so that multiple calls with the same +.Fa name +do not require additional calls to +.Xr getpwnam 3 . +If there is no user ID associated with the +.Fa name , +the +.Fn uid_from_user +function returns -1; +otherwise it stores the user ID at the location pointed to by +.Fa uid +and returns 0. +.Pp +The .Fn group_from_gid function returns the group name associated with the argument .Fa gid . @@ -75,6 +98,23 @@ to a string representation of the unless the argument .Fa nogroup is non-zero, in which case a null pointer is returned. +.Pp +The +.Fn gid_from_group +function returns the group ID associated with the argument +.Fa name . +The group ID is cached so that multiple calls with the same +.Fa name +do not require additional calls to +.Xr getgrnam 3 . +If there is no group ID associated with the +.Fa name , +the +.Fn gid_from_group +function returns -1; +otherwise it stores the group ID at the location pointed to by +.Fa gid +and returns 0. .Sh SEE ALSO .Xr getgrgid 3 , .Xr getpwuid 3 @@ -85,3 +125,12 @@ and .Fn group_from_gid functions first appeared in .Bx 4.4 . +.Pp +The +.Fn uid_from_user +and +.Fn gid_from_group +functions were ported from +.Nx +and first appeared in +.Ox 6.4 . diff --git a/lib/libc/gen/pwcache.c b/lib/libc/gen/pwcache.c index 743cad456643..f02e4db40dee 100644 --- a/lib/libc/gen/pwcache.c +++ b/lib/libc/gen/pwcache.c @@ -1,8 +1,13 @@ -/* $OpenBSD: pwcache.c,v 1.13 2015/11/25 23:16:01 jcs Exp $ */ -/* - * Copyright (c) 1989, 1993 +/* $OpenBSD: pwcache.c,v 1.14 2018/09/13 12:31:15 millert Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -30,102 +35,403 @@ #include +#include #include #include #include +#include #include +#include + +/* + * Constants and data structures used to implement group and password file + * caches. Name lengths have been chosen to be as large as those supported + * by the passwd and group files as well as the standard archive formats. + * CACHE SIZES MUST BE PRIME + */ +#define UNMLEN 32 /* >= user name found in any protocol */ +#define GNMLEN 32 /* >= group name found in any protocol */ +#define UID_SZ 317 /* size of uid to user_name cache */ +#define UNM_SZ 317 /* size of user_name to uid cache */ +#define GID_SZ 251 /* size of gid to group_name cache */ +#define GNM_SZ 251 /* size of group_name to gid cache */ +#define VALID 1 /* entry and name are valid */ +#define INVALID 2 /* entry valid, name NOT valid */ + +/* + * Node structures used in the user, group, uid, and gid caches. + */ + +typedef struct uidc { + int valid; /* is this a valid or a miss entry */ + char name[UNMLEN]; /* uid name */ + uid_t uid; /* cached uid */ +} UIDC; + +typedef struct gidc { + int valid; /* is this a valid or a miss entry */ + char name[GNMLEN]; /* gid name */ + gid_t gid; /* cached gid */ +} GIDC; + +/* + * Routines that control user, group, uid and gid caches. + * Traditional passwd/group cache routines perform quite poorly with + * archives. The chances of hitting a valid lookup with an archive is quite a + * bit worse than with files already resident on the file system. These misses + * create a MAJOR performance cost. To adress this problem, these routines + * cache both hits and misses. + */ + +static UIDC **uidtb; /* uid to name cache */ +static GIDC **gidtb; /* gid to name cache */ +static UIDC **usrtb; /* user name to uid cache */ +static GIDC **grptb; /* group name to gid cache */ + +static u_int +st_hash(const char *name, size_t len, int tabsz) +{ + u_int key = 0; + + assert(name != NULL); + + while (len--) { + key += *name++; + key = (key << 8) | (key >> 24); + } + + return key % tabsz; +} + +/* + * uidtb_start + * creates an an empty uidtb + * Return: + * 0 if ok, -1 otherwise + */ +static int +uidtb_start(void) +{ + static int fail = 0; + + if (uidtb != NULL) + return 0; + if (fail) + return -1; + if ((uidtb = calloc(UID_SZ, sizeof(UIDC *))) == NULL) { + ++fail; + return -1; + } + return 0; +} + +/* + * gidtb_start + * creates an an empty gidtb + * Return: + * 0 if ok, -1 otherwise + */ +static int +gidtb_start(void) +{ + static int fail = 0; + + if (gidtb != NULL) + return 0; + if (fail) + return -1; + if ((gidtb = calloc(GID_SZ, sizeof(GIDC *))) == NULL) { + ++fail; + return -1; + } + return 0; +} + +/* + * usrtb_start + * creates an an empty usrtb + * Return: + * 0 if ok, -1 otherwise + */ +static int +usrtb_start(void) +{ + static int fail = 0; + + if (usrtb != NULL) + return 0; + if (fail) + return -1; + if ((usrtb = calloc(UNM_SZ, sizeof(UIDC *))) == NULL) { + ++fail; + return -1; + } + return 0; +} + +/* + * grptb_start + * creates an an empty grptb + * Return: + * 0 if ok, -1 otherwise + */ +static int +grptb_start(void) +{ + static int fail = 0; -#define NCACHE 16 /* power of 2 */ -#define NLINES 4 /* associativity */ -#define MASK (NCACHE - 1) /* bits to store with */ -#define IDX(x, i) ((x & MASK) + i * NCACHE) + if (grptb != NULL) + return 0; + if (fail) + return -1; + if ((grptb = calloc(GNM_SZ, sizeof(GIDC *))) == NULL) { + ++fail; + return -1; + } + return 0; +} -char * -user_from_uid(uid_t uid, int nouser) +/* + * user_from_uid() + * caches the name (if any) for the uid. If noname clear, we always + * return the stored name (if valid or invalid match). + * We use a simple hash table. + * Return + * Pointer to stored name (or a empty string) + */ +const char * +user_from_uid(uid_t uid, int noname) { - static struct ncache { - uid_t uid; - short noname; - char name[_PW_NAME_LEN + 1]; - } c_uid[NLINES * NCACHE]; + struct passwd pwstore, *pw = NULL; char pwbuf[_PW_BUF_LEN]; - struct passwd pwstore, *pw; - struct ncache *cp; - unsigned int i; - - for (i = 0; i < NLINES; i++) { - cp = &c_uid[IDX(uid, i)]; - if (!*cp->name) { -fillit: - cp->uid = uid; - pw = NULL; - getpwuid_r(uid, &pwstore, pwbuf, sizeof(pwbuf), &pw); - if (pw == NULL) { - snprintf(cp->name, sizeof(cp->name), "%u", uid); - cp->noname = 1; - } else { - strlcpy(cp->name, pw->pw_name, sizeof(cp->name)); - } - } - if (cp->uid == uid) { - if (nouser && cp->noname) - return NULL; - return cp->name; + UIDC **pptr, *ptr = NULL; + + if ((uidtb != NULL) || (uidtb_start() == 0)) { + /* + * see if we have this uid cached + */ + pptr = uidtb + (uid % UID_SZ); + ptr = *pptr; + + if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) { + /* + * have an entry for this uid + */ + if (!noname || (ptr->valid == VALID)) + return ptr->name; + return NULL; } + + if (ptr == NULL) + *pptr = ptr = malloc(sizeof(UIDC)); } - /* move everybody down a slot */ - for (i = 0; i < NLINES - 1; i++) { - struct ncache *next; - cp = &c_uid[IDX(uid, i)]; - next = &c_uid[IDX(uid, i + 1)]; - memcpy(next, cp, sizeof(*cp)); + getpwuid_r(uid, &pwstore, pwbuf, sizeof(pwbuf), &pw); + if (pw == NULL) { + /* + * no match for this uid in the local password file + * a string that is the uid in numeric format + */ + if (ptr == NULL) + return NULL; + ptr->uid = uid; + (void)snprintf(ptr->name, UNMLEN, "%u", uid); + ptr->valid = INVALID; + if (noname) + return NULL; + } else { + /* + * there is an entry for this uid in the password file + */ + if (ptr == NULL) + return pw->pw_name; + ptr->uid = uid; + (void)strlcpy(ptr->name, pw->pw_name, sizeof(ptr->name)); + ptr->valid = VALID; } - cp = &c_uid[IDX(uid, 0)]; - goto fillit; + return ptr->name; } -char * -group_from_gid(gid_t gid, int nogroup) +/* + * group_from_gid() + * caches the name (if any) for the gid. If noname clear, we always + * return the stored name (if valid or invalid match). + * We use a simple hash table. + * Return + * Pointer to stored name (or a empty string) + */ +const char * +group_from_gid(gid_t gid, int noname) { - static struct ncache { - gid_t gid; - short noname; - char name[_PW_NAME_LEN + 1]; - } c_gid[NLINES * NCACHE]; + struct group grstore, *gr = NULL; char grbuf[_GR_BUF_LEN]; - struct group grstore, *gr; - struct ncache *cp; - unsigned int i; - - for (i = 0; i < NLINES; i++) { - cp = &c_gid[IDX(gid, i)]; - if (!*cp->name) { -fillit: - cp->gid = gid; - gr = NULL; - getgrgid_r(gid, &grstore, grbuf, sizeof(grbuf), &gr); - if (gr == NULL) { - snprintf(cp->name, sizeof(cp->name), "%u", gid); - cp->noname = 1; - } else { - strlcpy(cp->name, gr->gr_name, sizeof(cp->name)); - } + GIDC **pptr, *ptr = NULL; + + if ((gidtb != NULL) || (gidtb_start() == 0)) { + /* + * see if we have this gid cached + */ + pptr = gidtb + (gid % GID_SZ); + ptr = *pptr; + + if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) { + /* + * have an entry for this gid + */ + if (!noname || (ptr->valid == VALID)) + return ptr->name; + return NULL; } - if (cp->gid == gid) { - if (nogroup && cp->noname) - return NULL; - return cp->name; + + if (ptr == NULL) + *pptr = ptr = malloc(sizeof(GIDC)); + } + + getgrgid_r(gid, &grstore, grbuf, sizeof(grbuf), &gr); + if (gr == NULL) { + /* + * no match for this gid in the local group file, put in + * a string that is the gid in numeric format + */ + if (ptr == NULL) + return NULL; + ptr->gid = gid; + (void)snprintf(ptr->name, GNMLEN, "%u", gid); + ptr->valid = INVALID; + if (noname) + return NULL; + } else { + /* + * there is an entry for this group in the group file + */ + if (ptr == NULL) + return gr->gr_name; + ptr->gid = gid; + (void)strlcpy(ptr->name, gr->gr_name, sizeof(ptr->name)); + ptr->valid = VALID; + } + return ptr->name; +} + +/* + * uid_from_user() + * caches the uid for a given user name. We use a simple hash table. + * Return + * the uid (if any) for a user name, or a -1 if no match can be found + */ +int +uid_from_user(const char *name, uid_t *uid) +{ + struct passwd pwstore, *pw = NULL; + char pwbuf[_PW_BUF_LEN]; + UIDC **pptr, *ptr = NULL; + size_t namelen; + + /* + * return -1 for mangled names + */ + if (name == NULL || ((namelen = strlen(name)) == 0)) + return -1; + + if ((usrtb != NULL) || (usrtb_start() == 0)) { + /* + * look up in hash table, if found and valid return the uid, + * if found and invalid, return a -1 + */ + pptr = usrtb + st_hash(name, namelen, UNM_SZ); + ptr = *pptr; + + if ((ptr != NULL) && (ptr->valid > 0) && + strcmp(name, ptr->name) == 0) { + if (ptr->valid == INVALID) + return -1; + *uid = ptr->uid; + return 0; + } + + if (ptr == NULL) + *pptr = ptr = malloc(sizeof(UIDC)); + } + + /* + * no match, look it up, if no match store it as an invalid entry, + * or store the matching uid + */ + getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pw); + if (ptr == NULL) { + if (pw == NULL) + return -1; + *uid = pw->pw_uid; + return 0; + } + (void)strlcpy(ptr->name, name, sizeof(ptr->name)); + if (pw == NULL) { + ptr->valid = INVALID; + return -1; + } + ptr->valid = VALID; + *uid = ptr->uid = pw->pw_uid; + return 0; +} + +/* + * gid_from_group() + * caches the gid for a given group name. We use a simple hash table. + * Return + * the gid (if any) for a group name, or a -1 if no match can be found + */ +int +gid_from_group(const char *name, gid_t *gid) +{ + struct group grstore, *gr = NULL; + char grbuf[_GR_BUF_LEN]; + GIDC **pptr, *ptr = NULL; + size_t namelen; + + /* + * return -1 for mangled names + */ + if (name == NULL || ((namelen = strlen(name)) == 0)) + return -1; + + if ((grptb != NULL) || (grptb_start() == 0)) { + /* + * look up in hash table, if found and valid return the uid, + * if found and invalid, return a -1 + */ + pptr = grptb + st_hash(name, namelen, GID_SZ); + ptr = *pptr; + + if ((ptr != NULL) && (ptr->valid > 0) && + strcmp(name, ptr->name) == 0) { + if (ptr->valid == INVALID) + return -1; + *gid = ptr->gid; + return 0; } + + if (ptr == NULL) + *pptr = ptr = malloc(sizeof(GIDC)); + } + + /* + * no match, look it up, if no match store it as an invalid entry, + * or store the matching gid + */ + getgrnam_r(name, &grstore, grbuf, sizeof(grbuf), &gr); + if (ptr == NULL) { + if (gr == NULL) + return -1; + *gid = gr->gr_gid; + return 0; } - /* move everybody down a slot */ - for (i = 0; i < NLINES - 1; i++) { - struct ncache *next; - cp = &c_gid[IDX(gid, i)]; - next = &c_gid[IDX(gid, i + 1)]; - memcpy(next, cp, sizeof(*cp)); + (void)strlcpy(ptr->name, name, sizeof(ptr->name)); + if (gr == NULL) { + ptr->valid = INVALID; + return -1; } - cp = &c_gid[IDX(gid, 0)]; - goto fillit; + ptr->valid = VALID; + *gid = ptr->gid = gr->gr_gid; + return 0; } diff --git a/lib/libc/hidden/grp.h b/lib/libc/hidden/grp.h index 587ea3e6e512..18ec2ec54208 100644 --- a/lib/libc/hidden/grp.h +++ b/lib/libc/hidden/grp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: grp.h,v 1.2 2015/11/24 22:03:33 millert Exp $ */ +/* $OpenBSD: grp.h,v 1.3 2018/09/13 12:31:15 millert Exp $ */ /* * Copyright (c) 2015 Philip Guenther * @@ -29,7 +29,8 @@ PROTO_DEPRECATED(getgrent); PROTO_DEPRECATED(getgrgid); PROTO_NORMAL(getgrgid_r); PROTO_DEPRECATED(getgrnam); -PROTO_DEPRECATED(getgrnam_r); +PROTO_NORMAL(getgrnam_r); +PROTO_DEPRECATED(gid_from_group); PROTO_DEPRECATED(group_from_gid); PROTO_NORMAL(setgrent); PROTO_NORMAL(setgroupent); diff --git a/lib/libc/hidden/pwd.h b/lib/libc/hidden/pwd.h index b4e0dad9ca5e..56370e5adfa0 100644 --- a/lib/libc/hidden/pwd.h +++ b/lib/libc/hidden/pwd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pwd.h,v 1.3 2015/11/24 22:03:33 millert Exp $ */ +/* $OpenBSD: pwd.h,v 1.4 2018/09/13 12:31:15 millert Exp $ */ /* * Copyright (c) 2015 Philip Guenther * @@ -40,6 +40,7 @@ PROTO_NORMAL(getpwuid_shadow); PROTO_NORMAL(pw_dup); PROTO_NORMAL(setpassent); PROTO_DEPRECATED(setpwent); +PROTO_DEPRECATED(uid_from_user); PROTO_DEPRECATED(user_from_uid); #endif /* !_LIBC_PWD_H_ */ diff --git a/lib/libc/shlib_version b/lib/libc/shlib_version index 83b4edef50e5..a919c39c5a44 100644 --- a/lib/libc/shlib_version +++ b/lib/libc/shlib_version @@ -1,4 +1,4 @@ major=92 -minor=4 +minor=5 # note: If changes were made to include/thread_private.h or if system # calls were added/changed then librthread/shlib_version also be updated.