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