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