1 /* idcache.c -- map user and group IDs, cached for speed
2    Copyright (C) 1985, 1988-1990, 2004, 2007, 2010, 2014-2015, 2017 Free
3    Software Foundation, Inc.
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3, or (at your option)
8    any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public
16    License along with this program; if not, write to the Free
17    Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18    Boston, MA 02110-1301 USA.  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #include <stdio.h>
25 #include <sys/types.h>
26 #include <xalloc.h>
27 
28 #include <system.h>
29 
30 #if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
31 #include <string.h>
32 #else
33 #include <strings.h>
34 #endif
35 
36 #include <unistd.h>
37 
38 struct userid
39 {
40   union
41     {
42       uid_t u;
43       gid_t g;
44     } id;
45   char *name;
46   struct userid *next;
47 };
48 
49 static struct userid *user_alist;
50 
51 /* The members of this list have names not in the local passwd file.  */
52 static struct userid *nouser_alist;
53 
54 /* Translate UID to a login name or a stringified number,
55    with cache.  */
56 
57 char *
getuser(uid_t uid)58 getuser (uid_t uid)
59 {
60   register struct userid *tail;
61   struct passwd *pwent;
62   char usernum_string[20];
63 
64   for (tail = user_alist; tail; tail = tail->next)
65     if (tail->id.u == uid)
66       return tail->name;
67 
68   pwent = getpwuid (uid);
69   tail = (struct userid *) xmalloc (sizeof (struct userid));
70   tail->id.u = uid;
71   if (pwent == 0)
72     {
73       sprintf (usernum_string, "%u", (unsigned) uid);
74       tail->name = xstrdup (usernum_string);
75     }
76   else
77     tail->name = xstrdup (pwent->pw_name);
78 
79   /* Add to the head of the list, so most recently used is first.  */
80   tail->next = user_alist;
81   user_alist = tail;
82   return tail->name;
83 }
84 
85 /* Translate USER to a UID, with cache.
86    Return NULL if there is no such user.
87    (We also cache which user names have no passwd entry,
88    so we don't keep looking them up.)  */
89 
90 uid_t *
getuidbyname(char * user)91 getuidbyname (char *user)
92 {
93   register struct userid *tail;
94   struct passwd *pwent;
95 
96   for (tail = user_alist; tail; tail = tail->next)
97     /* Avoid a function call for the most common case.  */
98     if (*tail->name == *user && !strcmp (tail->name, user))
99       return &tail->id.u;
100 
101   for (tail = nouser_alist; tail; tail = tail->next)
102     /* Avoid a function call for the most common case.  */
103     if (*tail->name == *user && !strcmp (tail->name, user))
104       return 0;
105 
106   pwent = getpwnam (user);
107 
108   tail = (struct userid *) xmalloc (sizeof (struct userid));
109   tail->name = xstrdup (user);
110 
111   /* Add to the head of the list, so most recently used is first.  */
112   if (pwent)
113     {
114       tail->id.u = pwent->pw_uid;
115       tail->next = user_alist;
116       user_alist = tail;
117       return &tail->id.u;
118     }
119 
120   tail->next = nouser_alist;
121   nouser_alist = tail;
122   return 0;
123 }
124 
125 /* Use the same struct as for userids.  */
126 static struct userid *group_alist;
127 static struct userid *nogroup_alist;
128 
129 /* Translate GID to a group name or a stringified number,
130    with cache.  */
131 
132 char *
getgroup(gid_t gid)133 getgroup (gid_t gid)
134 {
135   register struct userid *tail;
136   struct group *grent;
137   char groupnum_string[20];
138 
139   for (tail = group_alist; tail; tail = tail->next)
140     if (tail->id.g == gid)
141       return tail->name;
142 
143   grent = getgrgid (gid);
144   tail = (struct userid *) xmalloc (sizeof (struct userid));
145   tail->id.g = gid;
146   if (grent == 0)
147     {
148       sprintf (groupnum_string, "%u", (unsigned int) gid);
149       tail->name = xstrdup (groupnum_string);
150     }
151   else
152     tail->name = xstrdup (grent->gr_name);
153 
154   /* Add to the head of the list, so most recently used is first.  */
155   tail->next = group_alist;
156   group_alist = tail;
157   return tail->name;
158 }
159 
160 /* Translate GROUP to a UID, with cache.
161    Return NULL if there is no such group.
162    (We also cache which group names have no group entry,
163    so we don't keep looking them up.)  */
164 
165 gid_t *
getgidbyname(char * group)166 getgidbyname (char *group)
167 {
168   register struct userid *tail;
169   struct group *grent;
170 
171   for (tail = group_alist; tail; tail = tail->next)
172     /* Avoid a function call for the most common case.  */
173     if (*tail->name == *group && !strcmp (tail->name, group))
174       return &tail->id.g;
175 
176   for (tail = nogroup_alist; tail; tail = tail->next)
177     /* Avoid a function call for the most common case.  */
178     if (*tail->name == *group && !strcmp (tail->name, group))
179       return 0;
180 
181   grent = getgrnam (group);
182 
183   tail = (struct userid *) xmalloc (sizeof (struct userid));
184   tail->name = xstrdup (group);
185 
186   /* Add to the head of the list, so most recently used is first.  */
187   if (grent)
188     {
189       tail->id.g = grent->gr_gid;
190       tail->next = group_alist;
191       group_alist = tail;
192       return &tail->id.g;
193     }
194 
195   tail->next = nogroup_alist;
196   nogroup_alist = tail;
197   return 0;
198 }
199