xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/mypwd.c (revision 4fee23f98c45552038ad6b5bd05124a41302fb01)
1 /*	$NetBSD: mypwd.c,v 1.1.1.1 2009/06/23 10:08:47 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	mypwd 3
6 /* SUMMARY
7 /*	caching getpwnam()/getpwuid()
8 /* SYNOPSIS
9 /*	#include <mypwd.h>
10 /*
11 /*	struct mypasswd *mypwuid(uid)
12 /*	uid_t	uid;
13 /*
14 /*	struct mypasswd *mypwnam(name)
15 /*	const char *name;
16 /*
17 /*	void	mypwfree(pwd)
18 /*	struct mypasswd *pwd;
19 /* DESCRIPTION
20 /*	This module maintains a reference-counted cache of password
21 /*	database lookup results. The idea is to avoid surprises by
22 /*	getpwnam() or getpwuid() overwriting a previous result, while
23 /*	at the same time avoiding duplicate copies of password
24 /*	information in memory, and to avoid making repeated getpwxxx()
25 /*	calls for the same information.
26 /*
27 /*	mypwnam() and mypwuid() are wrappers that cache a private copy
28 /*	of results from the getpwnam() and getpwuid() library routines.
29 /*	Results are shared between calls with the same \fIname\fR
30 /*	or \fIuid\fR argument, so changing results is verboten.
31 /*
32 /*	mypwfree() cleans up the result of mypwnam() and mypwuid().
33 /* BUGS
34 /*	This module is security sensitive and complex at the same
35 /*	time, which is bad.
36 /* LICENSE
37 /* .ad
38 /* .fi
39 /*	The Secure Mailer license must be distributed with this software.
40 /* AUTHOR(S)
41 /*	Wietse Venema
42 /*	IBM T.J. Watson Research
43 /*	P.O. Box 704
44 /*	Yorktown Heights, NY 10598, USA
45 /*--*/
46 
47 /* System library. */
48 
49 #include <sys_defs.h>
50 #include <pwd.h>
51 #include <string.h>
52 #ifdef USE_PATHS_H
53 #include <paths.h>
54 #endif
55 
56 /* Utility library. */
57 
58 #include <mymalloc.h>
59 #include <htable.h>
60 #include <binhash.h>
61 #include <msg.h>
62 
63 /* Global library. */
64 
65 #include "mypwd.h"
66 
67  /*
68   * The private cache. One for lookups by name, one for lookups by uid, and
69   * one for the last looked up result.
70   */
71 static HTABLE *mypwcache_name = 0;
72 static BINHASH *mypwcache_uid = 0;
73 static struct mypasswd *last_pwd;
74 
75 /* mypwenter - enter password info into cache */
76 
77 static struct mypasswd *mypwenter(struct passwd * pwd)
78 {
79     struct mypasswd *mypwd;
80 
81     /*
82      * Initialize on the fly.
83      */
84     if (mypwcache_name == 0) {
85 	mypwcache_name = htable_create(0);
86 	mypwcache_uid = binhash_create(0);
87     }
88     mypwd = (struct mypasswd *) mymalloc(sizeof(*mypwd));
89     mypwd->refcount = 0;
90     mypwd->pw_name = mystrdup(pwd->pw_name);
91     mypwd->pw_passwd = mystrdup(pwd->pw_passwd);
92     mypwd->pw_uid = pwd->pw_uid;
93     mypwd->pw_gid = pwd->pw_gid;
94     mypwd->pw_gecos = mystrdup(pwd->pw_gecos);
95     mypwd->pw_dir = mystrdup(pwd->pw_dir);
96     mypwd->pw_shell = mystrdup(*pwd->pw_shell ? pwd->pw_shell : _PATH_BSHELL);
97     htable_enter(mypwcache_name, mypwd->pw_name, (char *) mypwd);
98     binhash_enter(mypwcache_uid, (char *) &mypwd->pw_uid,
99 		  sizeof(mypwd->pw_uid), (char *) mypwd);
100     return (mypwd);
101 }
102 
103 /* mypwuid - caching getpwuid() */
104 
105 struct mypasswd *mypwuid(uid_t uid)
106 {
107     struct passwd *pwd;
108     struct mypasswd *mypwd;
109 
110     /*
111      * See if this is the same user as last time.
112      */
113     if (last_pwd != 0) {
114 	if (last_pwd->pw_uid != uid) {
115 	    mypwfree(last_pwd);
116 	    last_pwd = 0;
117 	} else {
118 	    mypwd = last_pwd;
119 	    mypwd->refcount++;
120 	    return (mypwd);
121 	}
122     }
123 
124     /*
125      * Find the info in the cache or in the password database. Make a copy,
126      * so that repeated getpwnam() calls will not clobber our result.
127      */
128     if ((mypwd = (struct mypasswd *)
129 	 binhash_find(mypwcache_uid, (char *) &uid, sizeof(uid))) == 0) {
130 	if ((pwd = getpwuid(uid)) == 0)
131 	    return (0);
132 	mypwd = mypwenter(pwd);
133     }
134     last_pwd = mypwd;
135     mypwd->refcount += 2;
136     return (mypwd);
137 }
138 
139 /* mypwnam - caching getpwnam() */
140 
141 struct mypasswd *mypwnam(const char *name)
142 {
143     struct passwd *pwd;
144     struct mypasswd *mypwd;
145 
146     /*
147      * See if this is the same user as last time.
148      */
149     if (last_pwd != 0) {
150 	if (strcmp(last_pwd->pw_name, name) != 0) {
151 	    mypwfree(last_pwd);
152 	    last_pwd = 0;
153 	} else {
154 	    mypwd = last_pwd;
155 	    mypwd->refcount++;
156 	    return (mypwd);
157 	}
158     }
159 
160     /*
161      * Find the info in the cache or in the password database. Make a copy,
162      * so that repeated getpwnam() calls will not clobber our result.
163      */
164     if ((mypwd = (struct mypasswd *) htable_find(mypwcache_name, name)) == 0) {
165 	if ((pwd = getpwnam(name)) == 0)
166 	    return (0);
167 	mypwd = mypwenter(pwd);
168     }
169     last_pwd = mypwd;
170     mypwd->refcount += 2;
171     return (mypwd);
172 }
173 
174 /* mypwfree - destroy password info */
175 
176 void    mypwfree(struct mypasswd * mypwd)
177 {
178     if (mypwd->refcount < 1)
179 	msg_panic("mypwfree: refcount %d", mypwd->refcount);
180 
181     if (--mypwd->refcount == 0) {
182 	htable_delete(mypwcache_name, mypwd->pw_name, (void (*) (char *)) 0);
183 	binhash_delete(mypwcache_uid, (char *) &mypwd->pw_uid,
184 		       sizeof(mypwd->pw_uid), (void (*) (char *)) 0);
185 	myfree(mypwd->pw_name);
186 	myfree(mypwd->pw_passwd);
187 	myfree(mypwd->pw_gecos);
188 	myfree(mypwd->pw_dir);
189 	myfree(mypwd->pw_shell);
190 	myfree((char *) mypwd);
191     }
192 }
193 
194 #ifdef TEST
195 
196  /*
197   * Test program. Look up a couple users and/or uid values and see if the
198   * results will be properly free()d.
199   */
200 #include <stdlib.h>
201 #include <ctype.h>
202 #include <vstream.h>
203 #include <msg_vstream.h>
204 
205 int     main(int argc, char **argv)
206 {
207     struct mypasswd **mypwd;
208     int     i;
209 
210     msg_vstream_init(argv[0], VSTREAM_ERR);
211     if (argc == 1)
212 	msg_fatal("usage: %s name or uid ...", argv[0]);
213 
214     mypwd = (struct mypasswd **) mymalloc((argc + 1) * sizeof(*mypwd));
215 
216     for (i = 1; i < argc; i++) {
217 	if (ISDIGIT(argv[i][0]))
218 	    mypwd[i] = mypwuid(atoi(argv[i]));
219 	else
220 	    mypwd[i] = mypwnam(argv[i]);
221 	if (mypwd[i] == 0)
222 	    msg_fatal("%s: not found", argv[i]);
223 	msg_info("+ %s link=%d used=%d used=%d", argv[i], mypwd[i]->refcount,
224 		 mypwcache_name->used, mypwcache_uid->used);
225     }
226     for (i = 1; i < argc; i++) {
227 	msg_info("- %s link=%d used=%d used=%d", argv[i], mypwd[i]->refcount,
228 		 mypwcache_name->used, mypwcache_uid->used);
229 	mypwfree(mypwd[i]);
230     }
231 
232     myfree((char *) mypwd);
233     return (0);
234 }
235 
236 #endif
237