Postfix3.3.1
mypwd.c
[詳解]
1 /*++
2 /* NAME
3 /* mypwd 3
4 /* SUMMARY
5 /* caching getpwnam_r()/getpwuid_r()
6 /* SYNOPSIS
7 /* #include <mypwd.h>
8 /*
9 /* int mypwuid_err(uid, pwd)
10 /* uid_t uid;
11 /* struct mypasswd **pwd;
12 /*
13 /* int mypwnam_err(name, pwd)
14 /* const char *name;
15 /* struct mypasswd **pwd;
16 /*
17 /* void mypwfree(pwd)
18 /* struct mypasswd *pwd;
19 /* BACKWARDS COMPATIBILITY
20 /* struct mypasswd *mypwuid(uid)
21 /* uid_t uid;
22 /*
23 /* struct mypasswd *mypwnam(name)
24 /* const char *name;
25 /* DESCRIPTION
26 /* This module maintains a reference-counted cache of password
27 /* database lookup results. The idea is to avoid making repeated
28 /* getpw*() calls for the same information.
29 /*
30 /* mypwnam_err() and mypwuid_err() are wrappers that cache a
31 /* private copy of results from the getpwnam_r() and getpwuid_r()
32 /* library routines (on legacy systems: from getpwnam() and
33 /* getpwuid(). Note: cache updates are not protected by mutex.
34 /*
35 /* Results are shared between calls with the same \fIname\fR
36 /* or \fIuid\fR argument, so changing results is verboten.
37 /*
38 /* mypwnam() and mypwuid() are binary-compatibility wrappers
39 /* for legacy applications.
40 /*
41 /* mypwfree() cleans up the result of mypwnam*() and mypwuid*().
42 /* BUGS
43 /* This module is security sensitive and complex at the same
44 /* time, which is bad.
45 /* DIAGNOSTICS
46 /* mypwnam_err() and mypwuid_err() return a non-zero system
47 /* error code when the lookup could not be performed. They
48 /* return zero, plus a null struct mypasswd pointer, when the
49 /* requested information was not found.
50 /*
51 /* Fatal error: out of memory.
52 /* LICENSE
53 /* .ad
54 /* .fi
55 /* The Secure Mailer license must be distributed with this software.
56 /* AUTHOR(S)
57 /* Wietse Venema
58 /* IBM T.J. Watson Research
59 /* P.O. Box 704
60 /* Yorktown Heights, NY 10598, USA
61 /*--*/
62 
63 /* System library. */
64 
65 #include <sys_defs.h>
66 #include <unistd.h>
67 #include <string.h>
68 #ifdef USE_PATHS_H
69 #include <paths.h>
70 #endif
71 #include <errno.h>
72 
73 /* Utility library. */
74 
75 #include <mymalloc.h>
76 #include <htable.h>
77 #include <binhash.h>
78 #include <msg.h>
79 
80 /* Global library. */
81 
82 #include "mypwd.h"
83 
84  /*
85  * Workaround: Solaris >= 2.5.1 provides two getpwnam_r() and getpwuid_r()
86  * implementations. The default variant is compatible with historical
87  * Solaris implementations. The non-default variant is POSIX-compliant.
88  *
89  * To get the POSIX-compliant variant, we include the file <pwd.h> after
90  * defining _POSIX_PTHREAD_SEMANTICS. We do this after all other includes,
91  * so that we won't unexpectedly affect any other APIs.
92  *
93  * This happens to work because nothing above this includes <pwd.h>, and
94  * because of the specific way that Solaris redefines getpwnam_r() and
95  * getpwuid_r() for POSIX compliance. We know the latter from peeking under
96  * the hood. What we do is only marginally better than directly invoking
97  * __posix_getpwnam_r() and __posix_getpwuid_r().
98  */
99 #ifdef GETPW_R_NEEDS_POSIX_PTHREAD_SEMANTICS
100 #define _POSIX_PTHREAD_SEMANTICS
101 #endif
102 #include <pwd.h>
103 
104  /*
105  * The private cache. One for lookups by name, one for lookups by uid, and
106  * one for the last looked up result. There is a minor problem: multiple
107  * cache entries may have the same uid value, but the cache that is indexed
108  * by uid can store only one entry per uid value.
109  */
110 static HTABLE *mypwcache_name = 0;
111 static BINHASH *mypwcache_uid = 0;
112 static struct mypasswd *last_pwd;
113 
114  /*
115  * XXX Solaris promises that we can determine the getpw*_r() buffer size by
116  * calling sysconf(_SC_GETPW_R_SIZE_MAX). Many systems promise that they
117  * will return an ERANGE error when the buffer is too small. However, not
118  * all systems make such promises. Therefore, we settle for the dumbest
119  * option: a large buffer. This is acceptable because the buffer is used
120  * only for short-term storage.
121  */
122 #ifdef HAVE_POSIX_GETPW_R
123 #define GETPW_R_BUFSIZ 1024
124 #endif
125 #define MYPWD_ERROR_DELAY (30)
126 
127 /* mypwenter - enter password info into cache */
128 
129 static struct mypasswd *mypwenter(const struct passwd * pwd)
130 {
131  struct mypasswd *mypwd;
132 
133  /*
134  * Initialize on the fly.
135  */
136  if (mypwcache_name == 0) {
137  mypwcache_name = htable_create(0);
138  mypwcache_uid = binhash_create(0);
139  }
140  mypwd = (struct mypasswd *) mymalloc(sizeof(*mypwd));
141  mypwd->refcount = 0;
142  mypwd->pw_name = mystrdup(pwd->pw_name);
143  mypwd->pw_passwd = mystrdup(pwd->pw_passwd);
144  mypwd->pw_uid = pwd->pw_uid;
145  mypwd->pw_gid = pwd->pw_gid;
146  mypwd->pw_gecos = mystrdup(pwd->pw_gecos);
147  mypwd->pw_dir = mystrdup(pwd->pw_dir);
148  mypwd->pw_shell = mystrdup(*pwd->pw_shell ? pwd->pw_shell : _PATH_BSHELL);
149 
150  /*
151  * Avoid mypwcache_uid memory leak when multiple names have the same UID.
152  * This makes the lookup result dependent on program history. But, it was
153  * already history-dependent before we added this extra check.
154  */
155  htable_enter(mypwcache_name, mypwd->pw_name, (void *) mypwd);
156  if (binhash_locate(mypwcache_uid, (void *) &mypwd->pw_uid,
157  sizeof(mypwd->pw_uid)) == 0)
158  binhash_enter(mypwcache_uid, (void *) &mypwd->pw_uid,
159  sizeof(mypwd->pw_uid), (void *) mypwd);
160  return (mypwd);
161 }
162 
163 /* mypwuid - caching getpwuid() */
164 
165 struct mypasswd *mypwuid(uid_t uid)
166 {
167  struct mypasswd *mypwd;
168 
169  while ((errno = mypwuid_err(uid, &mypwd)) != 0) {
170  msg_warn("getpwuid_r: %m");
171  sleep(MYPWD_ERROR_DELAY);
172  }
173  return (mypwd);
174 }
175 
176 /* mypwuid_err - caching getpwuid_r(), minus thread safety */
177 
178 int mypwuid_err(uid_t uid, struct mypasswd ** result)
179 {
180  struct passwd *pwd;
181  struct mypasswd *mypwd;
182 
183  /*
184  * See if this is the same user as last time.
185  */
186  if (last_pwd != 0) {
187  if (last_pwd->pw_uid != uid) {
188  mypwfree(last_pwd);
189  last_pwd = 0;
190  } else {
191  *result = mypwd = last_pwd;
192  mypwd->refcount++;
193  return (0);
194  }
195  }
196 
197  /*
198  * Find the info in the cache or in the password database.
199  */
200  if ((mypwd = (struct mypasswd *)
201  binhash_find(mypwcache_uid, (void *) &uid, sizeof(uid))) == 0) {
202 #ifdef HAVE_POSIX_GETPW_R
203  char pwstore[GETPW_R_BUFSIZ];
204  struct passwd pwbuf;
205  int err;
206 
207  err = getpwuid_r(uid, &pwbuf, pwstore, sizeof(pwstore), &pwd);
208  if (err != 0)
209  return (err);
210  if (pwd == 0) {
211  *result = 0;
212  return (0);
213  }
214 #else
215  if ((pwd = getpwuid(uid)) == 0) {
216  *result = 0;
217  return (0);
218  }
219 #endif
220  mypwd = mypwenter(pwd);
221  }
222  *result = last_pwd = mypwd;
223  mypwd->refcount += 2;
224  return (0);
225 }
226 
227 /* mypwnam - caching getpwnam() */
228 
229 struct mypasswd *mypwnam(const char *name)
230 {
231  struct mypasswd *mypwd;
232 
233  while ((errno = mypwnam_err(name, &mypwd)) != 0) {
234  msg_warn("getpwnam_r: %m");
235  sleep(MYPWD_ERROR_DELAY);
236  }
237  return (mypwd);
238 }
239 
240 /* mypwnam_err - caching getpwnam_r(), minus thread safety */
241 
242 int mypwnam_err(const char *name, struct mypasswd ** result)
243 {
244  struct passwd *pwd;
245  struct mypasswd *mypwd;
246 
247  /*
248  * See if this is the same user as last time.
249  */
250  if (last_pwd != 0) {
251  if (strcmp(last_pwd->pw_name, name) != 0) {
252  mypwfree(last_pwd);
253  last_pwd = 0;
254  } else {
255  *result = mypwd = last_pwd;
256  mypwd->refcount++;
257  return (0);
258  }
259  }
260 
261  /*
262  * Find the info in the cache or in the password database.
263  */
264  if ((mypwd = (struct mypasswd *) htable_find(mypwcache_name, name)) == 0) {
265 #ifdef HAVE_POSIX_GETPW_R
266  char pwstore[GETPW_R_BUFSIZ];
267  struct passwd pwbuf;
268  int err;
269 
270  err = getpwnam_r(name, &pwbuf, pwstore, sizeof(pwstore), &pwd);
271  if (err != 0)
272  return (err);
273  if (pwd == 0) {
274  *result = 0;
275  return (0);
276  }
277 #else
278  if ((pwd = getpwnam(name)) == 0) {
279  *result = 0;
280  return (0);
281  }
282 #endif
283  mypwd = mypwenter(pwd);
284  }
285  *result = last_pwd = mypwd;
286  mypwd->refcount += 2;
287  return (0);
288 }
289 
290 /* mypwfree - destroy password info */
291 
292 void mypwfree(struct mypasswd * mypwd)
293 {
294  if (mypwd->refcount < 1)
295  msg_panic("mypwfree: refcount %d", mypwd->refcount);
296 
297  /*
298  * See mypwenter() for the reason behind the binhash_locate() test.
299  */
300  if (--mypwd->refcount == 0) {
301  htable_delete(mypwcache_name, mypwd->pw_name, (void (*) (void *)) 0);
302  if (binhash_locate(mypwcache_uid, (void *) &mypwd->pw_uid,
303  sizeof(mypwd->pw_uid)))
304  binhash_delete(mypwcache_uid, (void *) &mypwd->pw_uid,
305  sizeof(mypwd->pw_uid), (void (*) (void *)) 0);
306  myfree(mypwd->pw_name);
307  myfree(mypwd->pw_passwd);
308  myfree(mypwd->pw_gecos);
309  myfree(mypwd->pw_dir);
310  myfree(mypwd->pw_shell);
311  myfree((void *) mypwd);
312  }
313 }
314 
315 #ifdef TEST
316 
317  /*
318  * Test program. Look up a couple users and/or uid values and see if the
319  * results will be properly free()d.
320  */
321 #include <stdlib.h>
322 #include <ctype.h>
323 #include <vstream.h>
324 #include <msg_vstream.h>
325 
326 int main(int argc, char **argv)
327 {
328  struct mypasswd **mypwd;
329  int i;
330 
331  msg_vstream_init(argv[0], VSTREAM_ERR);
332  if (argc == 1)
333  msg_fatal("usage: %s name or uid ...", argv[0]);
334 
335  mypwd = (struct mypasswd **) mymalloc((argc + 2) * sizeof(*mypwd));
336 
337  /*
338  * Do a sequence of lookups.
339  */
340  for (i = 1; i < argc; i++) {
341  if (ISDIGIT(argv[i][0]))
342  mypwd[i] = mypwuid(atoi(argv[i]));
343  else
344  mypwd[i] = mypwnam(argv[i]);
345  if (mypwd[i] == 0)
346  msg_fatal("%s: not found", argv[i]);
347  msg_info("lookup %s %s/%d refcount=%d name_cache=%d uid_cache=%d",
348  argv[i], mypwd[i]->pw_name, mypwd[i]->pw_uid,
349  mypwd[i]->refcount, mypwcache_name->used, mypwcache_uid->used);
350  }
351  mypwd[argc] = last_pwd;
352 
353  /*
354  * The following should free all entries.
355  */
356  for (i = 1; i < argc + 1; i++) {
357  msg_info("free %s/%d refcount=%d name_cache=%d uid_cache=%d",
358  mypwd[i]->pw_name, mypwd[i]->pw_uid, mypwd[i]->refcount,
359  mypwcache_name->used, mypwcache_uid->used);
360  mypwfree(mypwd[i]);
361  }
362  msg_info("name_cache=%d uid_cache=%d",
363  mypwcache_name->used, mypwcache_uid->used);
364 
365  myfree((void *) mypwd);
366  return (0);
367 }
368 
369 #endif
int mypwnam_err(const char *name, struct mypasswd **result)
Definition: mypwd.c:242
void myfree(void *ptr)
Definition: mymalloc.c:207
char * pw_dir
Definition: mypwd.h:24
char * mystrdup(const char *str)
Definition: mymalloc.c:225
char * pw_shell
Definition: mypwd.h:25
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
int mypwuid_err(uid_t uid, struct mypasswd **result)
Definition: mypwd.c:178
Definition: mypwd.h:17
ssize_t used
Definition: htable.h:27
int main(int argc, char **argv)
Definition: anvil.c:1010
struct mypasswd * mypwuid(uid_t uid)
Definition: mypwd.c:165
BINHASH * binhash_create(ssize_t size)
Definition: binhash.c:175
#define MYPWD_ERROR_DELAY
Definition: mypwd.c:125
BINHASH_INFO * binhash_enter(BINHASH *table, const void *key, ssize_t key_len, void *value)
Definition: binhash.c:207
char * pw_name
Definition: mypwd.h:19
BINHASH_INFO * binhash_locate(BINHASH *table, const void *key, ssize_t key_len)
Definition: binhash.c:238
Definition: htable.h:25
HTABLE * htable_create(ssize_t size)
Definition: htable.c:179
ssize_t used
Definition: binhash.h:28
char * pw_passwd
Definition: mypwd.h:20
struct mypasswd * mypwnam(const char *name)
Definition: mypwd.c:229
#define ISDIGIT(c)
Definition: sys_defs.h:1748
gid_t pw_gid
Definition: mypwd.h:22
char * pw_gecos
Definition: mypwd.h:23
uid_t pw_uid
Definition: mypwd.h:21
void msg_warn(const char *fmt,...)
Definition: msg.c:215
void * htable_find(HTABLE *table, const char *key)
Definition: htable.c:227
int refcount
Definition: mypwd.h:18
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
void mypwfree(struct mypasswd *mypwd)
Definition: mypwd.c:292
void msg_vstream_init(const char *name, VSTREAM *vp)
Definition: msg_vstream.c:77
void htable_delete(HTABLE *table, const char *key, void(*free_fn)(void *))
Definition: htable.c:257
#define VSTREAM_ERR
Definition: vstream.h:68
void * binhash_find(BINHASH *table, const void *key, ssize_t key_len)
Definition: binhash.c:223
void binhash_delete(BINHASH *table, const void *key, ssize_t key_len, void(*free_fn)(void *))
Definition: binhash.c:251
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150
HTABLE_INFO * htable_enter(HTABLE *table, const char *key, void *value)
Definition: htable.c:212
void msg_info(const char *fmt,...)
Definition: msg.c:199