1 /* 2 * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. 3 * 4 * All rights reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, and/or sell copies of the Software, and to permit persons 11 * to whom the Software is furnished to do so, provided that the above 12 * copyright notice(s) and this permission notice appear in all copies of 13 * the Software and that both the above copyright notice(s) and this 14 * permission notice appear in supporting documentation. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 19 * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 20 * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL 21 * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING 22 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 23 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 24 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 25 * 26 * Except as contained in this notice, the name of a copyright holder 27 * shall not be used in advertising or otherwise to promote the sale, use 28 * or other dealings in this Software without prior written authorization 29 * of the copyright holder. 30 */ 31 32 /* 33 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 34 * Use is subject to license terms. 35 */ 36 37 #pragma ident "%Z%%M% %I% %E% SMI" 38 39 /* 40 * If file-system access is to be excluded, this module has no function, 41 * so all of its code should be excluded. 42 */ 43 #ifndef WITHOUT_FILE_SYSTEM 44 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <errno.h> 49 50 #include <unistd.h> 51 #include <sys/types.h> 52 #include <sys/stat.h> 53 #include <pwd.h> 54 55 #include "pathutil.h" 56 #include "homedir.h" 57 #include "errmsg.h" 58 59 /* 60 * Use the reentrant POSIX threads versions of the password lookup functions? 61 */ 62 #if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L 63 #define THREAD_COMPATIBLE 1 64 /* 65 * Under Solaris we can use thr_main() to determine whether 66 * threads are actually running, and thus when it is necessary 67 * to avoid non-reentrant features. 68 */ 69 #if defined __sun && defined __SVR4 70 #include <thread.h> /* Solaris thr_main() */ 71 #endif 72 #endif 73 74 /* 75 * Provide a password buffer size fallback in case the max size reported 76 * by sysconf() is said to be indeterminate. 77 */ 78 #define DEF_GETPW_R_SIZE_MAX 1024 79 80 /* 81 * The resources needed to lookup and record a home directory are 82 * maintained in objects of the following type. 83 */ 84 struct HomeDir { 85 ErrMsg *err; /* The error message report buffer */ 86 char *buffer; /* A buffer for reading password entries and */ 87 /* directory paths. */ 88 int buflen; /* The allocated size of buffer[] */ 89 #ifdef THREAD_COMPATIBLE 90 struct passwd pwd; /* The password entry of a user */ 91 #endif 92 }; 93 94 static const char *hd_getpwd(HomeDir *home); 95 96 /*....................................................................... 97 * Create a new HomeDir object. 98 * 99 * Output: 100 * return HomeDir * The new object, or NULL on error. 101 */ 102 HomeDir *_new_HomeDir(void) 103 { 104 HomeDir *home; /* The object to be returned */ 105 size_t pathlen; /* The estimated maximum size of a pathname */ 106 /* 107 * Allocate the container. 108 */ 109 home = (HomeDir *) malloc(sizeof(HomeDir)); 110 if(!home) { 111 errno = ENOMEM; 112 return NULL; 113 }; 114 /* 115 * Before attempting any operation that might fail, initialize the 116 * container at least up to the point at which it can safely be passed 117 * to _del_HomeDir(). 118 */ 119 home->err = NULL; 120 home->buffer = NULL; 121 home->buflen = 0; 122 /* 123 * Allocate a place to record error messages. 124 */ 125 home->err = _new_ErrMsg(); 126 if(!home->err) 127 return _del_HomeDir(home); 128 /* 129 * Allocate the buffer that is used by the reentrant POSIX password-entry 130 * lookup functions. 131 */ 132 #ifdef THREAD_COMPATIBLE 133 /* 134 * Get the length of the buffer needed by the reentrant version 135 * of getpwnam(). 136 */ 137 #ifndef _SC_GETPW_R_SIZE_MAX 138 home->buflen = DEF_GETPW_R_SIZE_MAX; 139 #else 140 errno = 0; 141 home->buflen = sysconf(_SC_GETPW_R_SIZE_MAX); 142 /* 143 * If the limit isn't available, substitute a suitably large fallback value. 144 */ 145 if(home->buflen < 0 || errno) 146 home->buflen = DEF_GETPW_R_SIZE_MAX; 147 #endif 148 #endif 149 /* 150 * If the existing buffer length requirement is too restrictive to record 151 * a pathname, increase its length. 152 */ 153 pathlen = _pu_pathname_dim(); 154 if(pathlen > home->buflen) 155 home->buflen = pathlen; 156 /* 157 * Allocate a work buffer. 158 */ 159 home->buffer = (char *) malloc(home->buflen); 160 if(!home->buffer) { 161 errno = ENOMEM; 162 return _del_HomeDir(home); 163 }; 164 return home; 165 } 166 167 /*....................................................................... 168 * Delete a HomeDir object. 169 * 170 * Input: 171 * home HomeDir * The object to be deleted. 172 * Output: 173 * return HomeDir * The deleted object (always NULL). 174 */ 175 HomeDir *_del_HomeDir(HomeDir *home) 176 { 177 if(home) { 178 home->err = _del_ErrMsg(home->err); 179 if(home->buffer) 180 free(home->buffer); 181 free(home); 182 }; 183 return NULL; 184 } 185 186 /*....................................................................... 187 * Lookup the home directory of a given user in the password file. 188 * 189 * Input: 190 * home HomeDir * The resources needed to lookup the home directory. 191 * user const char * The name of the user to lookup, or "" to lookup 192 * the home directory of the person running the 193 * program. 194 * Output: 195 * return const char * The home directory. If the library was compiled 196 * with threads, this string is part of the HomeDir 197 * object and will change on subsequent calls. If 198 * the library wasn't compiled to be reentrant, 199 * then the string is a pointer into a static string 200 * in the C library and will change not only on 201 * subsequent calls to this function, but also if 202 * any calls are made to the C library password 203 * file lookup functions. Thus to be safe, you should 204 * make a copy of this string before calling any 205 * other function that might do a password file 206 * lookup. 207 * 208 * On error, NULL is returned and a description 209 * of the error can be acquired by calling 210 * _hd_last_home_dir_error(). 211 */ 212 const char *_hd_lookup_home_dir(HomeDir *home, const char *user) 213 { 214 const char *home_dir; /* A pointer to the home directory of the user */ 215 /* 216 * If no username has been specified, arrange to lookup the current 217 * user. 218 */ 219 int login_user = !user || *user=='\0'; 220 /* 221 * Check the arguments. 222 */ 223 if(!home) { 224 errno = EINVAL; 225 return NULL; 226 }; 227 /* 228 * Handle the ksh "~+". This expands to the absolute path of the 229 * current working directory. 230 */ 231 if(!login_user && strcmp(user, "+") == 0) { 232 home_dir = hd_getpwd(home); 233 if(!home_dir) { 234 _err_record_msg(home->err, "Can't determine current directory", 235 END_ERR_MSG); 236 return NULL; 237 } 238 return home_dir; 239 }; 240 /* 241 * When looking up the home directory of the current user, see if the 242 * HOME environment variable is set, and if so, return its value. 243 */ 244 if(login_user) { 245 home_dir = getenv("HOME"); 246 if(home_dir) 247 return home_dir; 248 }; 249 /* 250 * Look up the password entry of the user. 251 * First the POSIX threads version - this is painful! 252 */ 253 #ifdef THREAD_COMPATIBLE 254 { 255 struct passwd *ret; /* The returned pointer to pwd */ 256 int status; /* The return value of getpwnam_r() */ 257 /* 258 * Look up the password entry of the specified user. 259 */ 260 if(login_user) 261 status = getpwuid_r(geteuid(), &home->pwd, home->buffer, home->buflen, 262 &ret); 263 else 264 status = getpwnam_r(user, &home->pwd, home->buffer, home->buflen, &ret); 265 if(status || !ret) { 266 _err_record_msg(home->err, "User '", user, "' doesn't exist.", 267 END_ERR_MSG); 268 return NULL; 269 }; 270 /* 271 * Get a pointer to the string that holds the home directory. 272 */ 273 home_dir = home->pwd.pw_dir; 274 }; 275 /* 276 * Now the classic unix version. 277 */ 278 #else 279 { 280 struct passwd *pwd = login_user ? getpwuid(geteuid()) : getpwnam(user); 281 if(!pwd) { 282 _err_record_msg(home->err, "User '", user, "' doesn't exist.", 283 END_ERR_MSG); 284 return NULL; 285 }; 286 /* 287 * Get a pointer to the home directory. 288 */ 289 home_dir = pwd->pw_dir; 290 }; 291 #endif 292 return home_dir; 293 } 294 295 /*....................................................................... 296 * Return a description of the last error that caused _hd_lookup_home_dir() 297 * to return NULL. 298 * 299 * Input: 300 * home HomeDir * The resources needed to record the home directory. 301 * Output: 302 * return char * The description of the last error. 303 */ 304 const char *_hd_last_home_dir_error(HomeDir *home) 305 { 306 return home ? _err_get_msg(home->err) : "NULL HomeDir argument"; 307 } 308 309 /*....................................................................... 310 * The _hd_scan_user_home_dirs() function calls a user-provided function 311 * for each username known by the system, passing the function both 312 * the name and the home directory of the user. 313 * 314 * Input: 315 * home HomeDir * The resource object for reading home 316 * directories. 317 * prefix const char * Only information for usernames that 318 * start with this prefix will be 319 * returned. Note that the empty 320 & string "", matches all usernames. 321 * data void * Anonymous data to be passed to the 322 * callback function. 323 * callback_fn HOME_DIR_FN(*) The function to call for each user. 324 * Output: 325 * return int 0 - Successful completion. 326 * 1 - An error occurred. A description 327 * of the error can be obtained by 328 * calling _hd_last_home_dir_error(). 329 */ 330 int _hd_scan_user_home_dirs(HomeDir *home, const char *prefix, 331 void *data, HOME_DIR_FN(*callback_fn)) 332 { 333 int waserr = 0; /* True after errors */ 334 int prefix_len; /* The length of prefix[] */ 335 /* 336 * Check the arguments. 337 */ 338 if(!home || !prefix || !callback_fn) { 339 if(home) { 340 _err_record_msg(home->err, 341 "_hd_scan_user_home_dirs: Missing callback function", 342 END_ERR_MSG); 343 }; 344 return 1; 345 }; 346 /* 347 * Get the length of the username prefix. 348 */ 349 prefix_len = strlen(prefix); 350 /* 351 * There are no reentrant versions of getpwent() etc for scanning 352 * the password file, so disable username completion when the 353 * library is compiled to be reentrant. 354 */ 355 #if defined(PREFER_REENTRANT) && defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199506L 356 #if defined __sun && defined __SVR4 357 if(0) 358 #else 359 if(1) 360 #endif 361 { 362 struct passwd pwd_buffer; /* A returned password entry */ 363 struct passwd *pwd; /* A pointer to pwd_buffer */ 364 char buffer[512]; /* The buffer in which the string members of */ 365 /* pwd_buffer are stored. */ 366 /* 367 * See if the prefix that is being completed is a complete username. 368 */ 369 if(!waserr && getpwnam_r(prefix, &pwd_buffer, buffer, sizeof(buffer), 370 &pwd) == 0 && pwd != NULL) { 371 waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir, 372 _err_get_msg(home->err), ERR_MSG_LEN); 373 }; 374 /* 375 * See if the username of the current user minimally matches the prefix. 376 */ 377 if(!waserr && getpwuid_r(getuid(), &pwd_buffer, buffer, sizeof(buffer), 378 &pwd) == 0 && pwd != NULL && 379 strncmp(prefix, pwd->pw_name, prefix_len)==0) { 380 waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir, 381 _err_get_msg(home->err), ERR_MSG_LEN); 382 }; 383 /* 384 * Reentrancy not required? 385 */ 386 } else 387 #endif 388 { 389 struct passwd pwd_buffer; /* A returned password entry */ 390 struct passwd *pwd; /* The pointer to the latest password entry */ 391 /* 392 * Open the password file. 393 */ 394 setpwent(); 395 /* 396 * Read the contents of the password file, looking for usernames 397 * that start with the specified prefix, and adding them to the 398 * list of matches. 399 */ 400 #if defined __sun && defined __SVR4 401 while((pwd = getpwent_r(&pwd_buffer, home->buffer, home->buflen)) != NULL && !waserr) { 402 #else 403 while((pwd = getpwent()) != NULL && !waserr) { 404 #endif 405 if(strncmp(prefix, pwd->pw_name, prefix_len) == 0) { 406 waserr = callback_fn(data, pwd->pw_name, pwd->pw_dir, 407 _err_get_msg(home->err), ERR_MSG_LEN); 408 }; 409 }; 410 /* 411 * Close the password file. 412 */ 413 endpwent(); 414 }; 415 /* 416 * Under ksh ~+ stands for the absolute pathname of the current working 417 * directory. 418 */ 419 if(!waserr && strncmp(prefix, "+", prefix_len) == 0) { 420 const char *pwd = hd_getpwd(home); 421 if(pwd) { 422 waserr = callback_fn(data, "+", pwd, _err_get_msg(home->err),ERR_MSG_LEN); 423 } else { 424 waserr = 1; 425 _err_record_msg(home->err, "Can't determine current directory.", 426 END_ERR_MSG); 427 }; 428 }; 429 return waserr; 430 } 431 432 /*....................................................................... 433 * Return the value of getenv("PWD") if this points to the current 434 * directory, or the return value of getcwd() otherwise. The reason for 435 * prefering PWD over getcwd() is that the former preserves the history 436 * of symbolic links that have been traversed to reach the current 437 * directory. This function is designed to provide the equivalent 438 * expansion of the ksh ~+ directive, which normally returns its value 439 * of PWD. 440 * 441 * Input: 442 * home HomeDir * The resource object for reading home directories. 443 * Output: 444 * return const char * A pointer to either home->buffer, where the 445 * pathname is recorded, the string returned by 446 * getenv("PWD"), or NULL on error. 447 */ 448 static const char *hd_getpwd(HomeDir *home) 449 { 450 /* 451 * Get the absolute path of the current working directory. 452 */ 453 char *cwd = getcwd(home->buffer, home->buflen); 454 /* 455 * Some shells set PWD with the path of the current working directory. 456 * This will differ from cwd in that it won't have had symbolic links 457 * expanded. 458 */ 459 const char *pwd = getenv("PWD"); 460 /* 461 * If PWD was set, and it points to the same directory as cwd, return 462 * its value. Note that it won't be the same if the current shell or 463 * the current program has changed directories, after inheriting PWD 464 * from a parent shell. 465 */ 466 struct stat cwdstat, pwdstat; 467 if(pwd && cwd && stat(cwd, &cwdstat)==0 && stat(pwd, &pwdstat)==0 && 468 cwdstat.st_dev == pwdstat.st_dev && cwdstat.st_ino == pwdstat.st_ino) 469 return pwd; 470 /* 471 * Also return pwd if getcwd() failed, since it represents the best 472 * information that we have access to. 473 */ 474 if(!cwd) 475 return pwd; 476 /* 477 * In the absence of a valid PWD, return cwd. 478 */ 479 return cwd; 480 } 481 482 #endif /* ifndef WITHOUT_FILE_SYSTEM */ 483