/*-1* SPDX-License-Identifier: BSD-3-Clause2*3* Copyright (c) 1992 Keith Muller.4* Copyright (c) 1992, 19935* The Regents of the University of California. All rights reserved.6*7* This code is derived from software contributed to Berkeley by8* Keith Muller of the University of California, San Diego.9*10* Redistribution and use in source and binary forms, with or without11* modification, are permitted provided that the following conditions12* are met:13* 1. Redistributions of source code must retain the above copyright14* notice, this list of conditions and the following disclaimer.15* 2. Redistributions in binary form must reproduce the above copyright16* notice, this list of conditions and the following disclaimer in the17* documentation and/or other materials provided with the distribution.18* 3. Neither the name of the University nor the names of its contributors19* may be used to endorse or promote products derived from this software20* without specific prior written permission.21*22* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND23* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE24* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE25* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE26* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL27* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS28* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)29* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT30* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY31* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF32* SUCH DAMAGE.33*/3435#include <sys/types.h>36#include <sys/stat.h>37#include <string.h>38#include <stdio.h>39#include <pwd.h>40#include <grp.h>41#include <stdlib.h>42#include "pax.h"43#include "cache.h"44#include "extern.h"4546/*47* routines that control user, group, uid and gid caches (for the archive48* member print routine).49* IMPORTANT:50* these routines cache BOTH hits and misses, a major performance improvement51*/5253static int pwopn = 0; /* is password file open */54static int gropn = 0; /* is group file open */55static UIDC **uidtb = NULL; /* uid to name cache */56static GIDC **gidtb = NULL; /* gid to name cache */57static UIDC **usrtb = NULL; /* user name to uid cache */58static GIDC **grptb = NULL; /* group name to gid cache */5960/*61* uidtb_start62* creates an empty uidtb63* Return:64* 0 if ok, -1 otherwise65*/6667int68uidtb_start(void)69{70static int fail = 0;7172if (uidtb != NULL)73return(0);74if (fail)75return(-1);76if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) {77++fail;78paxwarn(1, "Unable to allocate memory for user id cache table");79return(-1);80}81return(0);82}8384/*85* gidtb_start86* creates an empty gidtb87* Return:88* 0 if ok, -1 otherwise89*/9091int92gidtb_start(void)93{94static int fail = 0;9596if (gidtb != NULL)97return(0);98if (fail)99return(-1);100if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) {101++fail;102paxwarn(1, "Unable to allocate memory for group id cache table");103return(-1);104}105return(0);106}107108/*109* usrtb_start110* creates an empty usrtb111* Return:112* 0 if ok, -1 otherwise113*/114115int116usrtb_start(void)117{118static int fail = 0;119120if (usrtb != NULL)121return(0);122if (fail)123return(-1);124if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {125++fail;126paxwarn(1, "Unable to allocate memory for user name cache table");127return(-1);128}129return(0);130}131132/*133* grptb_start134* creates an empty grptb135* Return:136* 0 if ok, -1 otherwise137*/138139int140grptb_start(void)141{142static int fail = 0;143144if (grptb != NULL)145return(0);146if (fail)147return(-1);148if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {149++fail;150paxwarn(1,"Unable to allocate memory for group name cache table");151return(-1);152}153return(0);154}155156/*157* name_uid()158* caches the name (if any) for the uid. If frc set, we always return the159* the stored name (if valid or invalid match). We use a simple hash table.160* Return161* Pointer to stored name (or an empty string).162*/163164const char *165name_uid(uid_t uid, int frc)166{167struct passwd *pw;168UIDC *ptr;169170if ((uidtb == NULL) && (uidtb_start() < 0))171return("");172173/*174* see if we have this uid cached175*/176ptr = uidtb[uid % UID_SZ];177if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {178/*179* have an entry for this uid180*/181if (frc || (ptr->valid == VALID))182return(ptr->name);183return("");184}185186/*187* No entry for this uid, we will add it188*/189if (!pwopn) {190setpassent(1);191++pwopn;192}193if (ptr == NULL)194ptr = uidtb[uid % UID_SZ] = (UIDC *)malloc(sizeof(UIDC));195196if ((pw = getpwuid(uid)) == NULL) {197/*198* no match for this uid in the local password file199* a string that is the uid in numeric format200*/201if (ptr == NULL)202return("");203ptr->uid = uid;204ptr->valid = INVALID;205(void)snprintf(ptr->name, sizeof(ptr->name), "%lu",206(unsigned long)uid);207if (frc == 0)208return("");209} else {210/*211* there is an entry for this uid in the password file212*/213if (ptr == NULL)214return(pw->pw_name);215ptr->uid = uid;216(void)strncpy(ptr->name, pw->pw_name, UNMLEN - 1);217ptr->name[UNMLEN-1] = '\0';218ptr->valid = VALID;219}220return(ptr->name);221}222223/*224* name_gid()225* caches the name (if any) for the gid. If frc set, we always return the226* the stored name (if valid or invalid match). We use a simple hash table.227* Return228* Pointer to stored name (or an empty string).229*/230231const char *232name_gid(gid_t gid, int frc)233{234struct group *gr;235GIDC *ptr;236237if ((gidtb == NULL) && (gidtb_start() < 0))238return("");239240/*241* see if we have this gid cached242*/243ptr = gidtb[gid % GID_SZ];244if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {245/*246* have an entry for this gid247*/248if (frc || (ptr->valid == VALID))249return(ptr->name);250return("");251}252253/*254* No entry for this gid, we will add it255*/256if (!gropn) {257setgroupent(1);258++gropn;259}260if (ptr == NULL)261ptr = gidtb[gid % GID_SZ] = (GIDC *)malloc(sizeof(GIDC));262263if ((gr = getgrgid(gid)) == NULL) {264/*265* no match for this gid in the local group file, put in266* a string that is the gid in numeric format267*/268if (ptr == NULL)269return("");270ptr->gid = gid;271ptr->valid = INVALID;272(void)snprintf(ptr->name, sizeof(ptr->name), "%lu",273(unsigned long)gid);274if (frc == 0)275return("");276} else {277/*278* there is an entry for this group in the group file279*/280if (ptr == NULL)281return(gr->gr_name);282ptr->gid = gid;283(void)strncpy(ptr->name, gr->gr_name, GNMLEN - 1);284ptr->name[GNMLEN-1] = '\0';285ptr->valid = VALID;286}287return(ptr->name);288}289290/*291* uid_name()292* caches the uid for a given user name. We use a simple hash table.293* Return294* the uid (if any) for a user name, or a -1 if no match can be found295*/296297int298uid_name(char *name, uid_t *uid)299{300struct passwd *pw;301UIDC *ptr;302int namelen;303304/*305* return -1 for mangled names306*/307if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))308return(-1);309if ((usrtb == NULL) && (usrtb_start() < 0))310return(-1);311312/*313* look up in hash table, if found and valid return the uid,314* if found and invalid, return a -1315*/316ptr = usrtb[st_hash(name, namelen, UNM_SZ)];317if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {318if (ptr->valid == INVALID)319return(-1);320*uid = ptr->uid;321return(0);322}323324if (!pwopn) {325setpassent(1);326++pwopn;327}328329if (ptr == NULL)330ptr = usrtb[st_hash(name, namelen, UNM_SZ)] =331(UIDC *)malloc(sizeof(UIDC));332333/*334* no match, look it up, if no match store it as an invalid entry,335* or store the matching uid336*/337if (ptr == NULL) {338if ((pw = getpwnam(name)) == NULL)339return(-1);340*uid = pw->pw_uid;341return(0);342}343(void)strncpy(ptr->name, name, UNMLEN - 1);344ptr->name[UNMLEN-1] = '\0';345if ((pw = getpwnam(name)) == NULL) {346ptr->valid = INVALID;347return(-1);348}349ptr->valid = VALID;350*uid = ptr->uid = pw->pw_uid;351return(0);352}353354/*355* gid_name()356* caches the gid for a given group name. We use a simple hash table.357* Return358* the gid (if any) for a group name, or a -1 if no match can be found359*/360361int362gid_name(char *name, gid_t *gid)363{364struct group *gr;365GIDC *ptr;366int namelen;367368/*369* return -1 for mangled names370*/371if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))372return(-1);373if ((grptb == NULL) && (grptb_start() < 0))374return(-1);375376/*377* look up in hash table, if found and valid return the uid,378* if found and invalid, return a -1379*/380ptr = grptb[st_hash(name, namelen, GID_SZ)];381if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {382if (ptr->valid == INVALID)383return(-1);384*gid = ptr->gid;385return(0);386}387388if (!gropn) {389setgroupent(1);390++gropn;391}392if (ptr == NULL)393ptr = grptb[st_hash(name, namelen, GID_SZ)] =394(GIDC *)malloc(sizeof(GIDC));395396/*397* no match, look it up, if no match store it as an invalid entry,398* or store the matching gid399*/400if (ptr == NULL) {401if ((gr = getgrnam(name)) == NULL)402return(-1);403*gid = gr->gr_gid;404return(0);405}406407(void)strncpy(ptr->name, name, GNMLEN - 1);408ptr->name[GNMLEN-1] = '\0';409if ((gr = getgrnam(name)) == NULL) {410ptr->valid = INVALID;411return(-1);412}413ptr->valid = VALID;414*gid = ptr->gid = gr->gr_gid;415return(0);416}417418419