Add uid_from_user() and gid_from_group(), derived from pax's cache.c.
authormillert <millert@openbsd.org>
Thu, 13 Sep 2018 12:31:15 +0000 (12:31 +0000)
committermillert <millert@openbsd.org>
Thu, 13 Sep 2018 12:31:15 +0000 (12:31 +0000)
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
include/pwd.h
lib/libc/Symbols.list
lib/libc/gen/getgrent.c
lib/libc/gen/pwcache.3
lib/libc/gen/pwcache.c
lib/libc/hidden/grp.h
lib/libc/hidden/pwd.h
lib/libc/shlib_version

index 2a1a6c7..ebf6518 100644 (file)
@@ -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
 
index 6a53220..8410734 100644 (file)
@@ -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);
index 55500ae..7c10000 100644 (file)
@@ -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
index 348e7dd..09cb973 100644 (file)
@@ -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)
index a0283ed..80bb7cc 100644 (file)
@@ -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.
 .\" 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 .
index 743cad4..f02e4db 100644 (file)
@@ -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:
 
 #include <sys/types.h>
 
+#include <assert.h>
 #include <grp.h>
 #include <pwd.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
+
+/*
+ * 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;
 }
index 587ea3e..18ec2ec 100644 (file)
@@ -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 <guenther@openbsd.org>
  *
@@ -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);
index b4e0dad..56370e5 100644 (file)
@@ -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 <guenther@openbsd.org>
  *
@@ -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_ */
index 83b4ede..a919c39 100644 (file)
@@ -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.