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