138494Sobrien/* 2310490Scy * Copyright (c) 1997-2014 Erez Zadok 338494Sobrien * Copyright (c) 1989 Jan-Simon Pendry 438494Sobrien * Copyright (c) 1989 Imperial College of Science, Technology & Medicine 538494Sobrien * Copyright (c) 1989 The Regents of the University of California. 638494Sobrien * All rights reserved. 738494Sobrien * 838494Sobrien * This code is derived from software contributed to Berkeley by 938494Sobrien * Jan-Simon Pendry at Imperial College, London. 1038494Sobrien * 1138494Sobrien * Redistribution and use in source and binary forms, with or without 1238494Sobrien * modification, are permitted provided that the following conditions 1338494Sobrien * are met: 1438494Sobrien * 1. Redistributions of source code must retain the above copyright 1538494Sobrien * notice, this list of conditions and the following disclaimer. 1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1738494Sobrien * notice, this list of conditions and the following disclaimer in the 1838494Sobrien * documentation and/or other materials provided with the distribution. 19310490Scy * 3. Neither the name of the University nor the names of its contributors 2038494Sobrien * may be used to endorse or promote products derived from this software 2138494Sobrien * without specific prior written permission. 2238494Sobrien * 2338494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2438494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2538494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2638494Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2738494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2838494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2938494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3038494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3138494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3238494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3338494Sobrien * SUCH DAMAGE. 3438494Sobrien * 3538494Sobrien * 36174313Sobrien * File: am-utils/hlfsd/homedir.c 3738494Sobrien * 3838494Sobrien * HLFSD was written at Columbia University Computer Science Department, by 3938494Sobrien * Erez Zadok <ezk@cs.columbia.edu> and Alexander Dupuy <dupuy@cs.columbia.edu> 4038494Sobrien * It is being distributed under the same terms and conditions as amd does. 4138494Sobrien */ 4238494Sobrien 4338494Sobrien#ifdef HAVE_CONFIG_H 4438494Sobrien# include <config.h> 4538494Sobrien#endif /* HAVE_CONFIG_H */ 4638494Sobrien#include <am_defs.h> 4738494Sobrien#include <hlfsd.h> 4838494Sobrien 4938494Sobrien 5038494Sobrien/* 5138494Sobrien * STATIC VARIABLES AND FUNCTIONS: 5238494Sobrien */ 5338494Sobrienstatic FILE *passwd_fp = NULL; 5438494Sobrienstatic char pw_name[16], pw_dir[128]; 5538494Sobrienstatic int cur_pwtab_num = 0, max_pwtab_num = 0; 5638494Sobrienstatic int hlfsd_diskspace(char *); 5738494Sobrienstatic int hlfsd_stat(char *, struct stat *); 5838494Sobrienstatic int passwd_line = 0; 5938494Sobrienstatic int plt_reset(void); 6038494Sobrienstatic struct passwd passwd_ent; 6138494Sobrienstatic uid2home_t *lastchild; 6238494Sobrienstatic uid2home_t *pwtab; 6338494Sobrienstatic void delay(uid2home_t *, int); 64174313Sobrienstatic void table_add(u_int, const char *, const char *); 65119682Smbrstatic char mboxfile[MAXPATHLEN]; 66119682Smbrstatic char *root_home; /* root's home directory */ 6738494Sobrien 6838494Sobrien/* GLOBAL FUNCTIONS */ 6938494Sobrienchar *homeof(char *username); 7038494Sobrienint uidof(char *username); 7138494Sobrien 7238494Sobrien/* GLOBALS VARIABLES */ 7338494Sobrienusername2uid_t *untab; /* user name table */ 7438494Sobrien 7538494Sobrien/* 7638494Sobrien * Return the home directory pathname for the user with uid "userid". 7738494Sobrien */ 7838494Sobrienchar * 79119682Smbrhomedir(int userid, int groupid) 8038494Sobrien{ 8138494Sobrien static char linkval[MAXPATHLEN + 1]; 8238494Sobrien static struct timeval tp; 8338494Sobrien uid2home_t *found; 8438494Sobrien char *homename; 8538494Sobrien struct stat homestat; 86119682Smbr int old_groupid, old_userid; 8738494Sobrien 8838494Sobrien if ((found = plt_search(userid)) == (uid2home_t *) NULL) { 8938494Sobrien return alt_spooldir; /* use alt spool for unknown uid */ 9038494Sobrien } 9138494Sobrien homename = found->home; 9238494Sobrien 9338494Sobrien if (homename[0] != '/' || homename[1] == '\0') { 9438494Sobrien found->last_status = 1; 9538494Sobrien return alt_spooldir; /* use alt spool for / or rel. home */ 9638494Sobrien } 97119682Smbr if ((int) userid == 0) /* force all uid 0 to use root's home */ 98174313Sobrien xsnprintf(linkval, sizeof(linkval), "%s/%s", root_home, home_subdir); 99119682Smbr else 100174313Sobrien xsnprintf(linkval, sizeof(linkval), "%s/%s", homename, home_subdir); 10138494Sobrien 10238494Sobrien if (noverify) { 10338494Sobrien found->last_status = 0; 10438494Sobrien return linkval; 10538494Sobrien } 10638494Sobrien 10738494Sobrien /* 10838494Sobrien * To optimize hlfsd, we don't actually check the validity of the 109119682Smbr * symlink if it has been checked in the last N seconds. It is 11038494Sobrien * very likely that the link, machine, and filesystem are still 111119682Smbr * valid, as long as N is small. But if N is large, that may not be 11238494Sobrien * true. That's why the default N is 5 minutes, but we allow the 11338494Sobrien * user to override this value via a command line option. Note that 11438494Sobrien * we do not update the last_access_time each time it is accessed, 11538494Sobrien * but only once every N seconds. 11638494Sobrien */ 11738494Sobrien if (gettimeofday(&tp, (struct timezone *) NULL) < 0) { 11838494Sobrien tp.tv_sec = 0; 11938494Sobrien } else { 12038494Sobrien if ((tp.tv_sec - found->last_access_time) < cache_interval) { 12138494Sobrien if (found->last_status == 0) { 12238494Sobrien return linkval; 12338494Sobrien } else { 12438494Sobrien return alt_spooldir; 12538494Sobrien } 12638494Sobrien } else { 12738494Sobrien found->last_access_time = tp.tv_sec; 12838494Sobrien } 12938494Sobrien } 13038494Sobrien 13138494Sobrien /* 132310490Scy * Only run this forking code if ask for -D fork (default). 133310490Scy * Disable forking using -D nofork. 13438494Sobrien */ 135310490Scy if (amuDebug(D_FORK)) { 13638494Sobrien /* fork child to process request if none in progress */ 137174313Sobrien if (found->child && kill(found->child, 0)) 13838494Sobrien found->child = 0; 13938494Sobrien 14038494Sobrien if (found->child) 14138494Sobrien delay(found, 5); /* wait a bit if in progress */ 142174313Sobrien if (found->child) { /* better safe than sorry - maybe */ 143174313Sobrien found->last_status = 1; 14438494Sobrien return alt_spooldir; 14538494Sobrien } 14638494Sobrien if ((found->child = fork()) < 0) { 14738494Sobrien found->last_status = 1; 14838494Sobrien return alt_spooldir; 14938494Sobrien } 15038494Sobrien if (found->child) { /* PARENT */ 15138494Sobrien if (lastchild) 152174313Sobrien dlog("cache spill uid = %ld, pid = %ld, home = %s", 15351300Sobrien (long) lastchild->uid, (long) lastchild->child, 15438494Sobrien lastchild->home); 15538494Sobrien lastchild = found; 15638494Sobrien return (char *) NULL; /* return NULL to parent, so it can continue */ 15738494Sobrien } 158174313Sobrien } 15938494Sobrien 16038494Sobrien /* 161174313Sobrien * CHILD: (or parent if -D fork) 16238494Sobrien * 16338494Sobrien * Check and create dir if needed. 16438494Sobrien * Check disk space and/or quotas too. 16538494Sobrien * 16638494Sobrien * We don't need to set the _last_status field of found after the fork 16738494Sobrien * in the child, b/c that information would be later determined in 16838494Sobrien * nfsproc_readlink_2() and the correct exit status would be returned 16938494Sobrien * to the parent upon SIGCHLD in interlock(). 17038494Sobrien * 17138494Sobrien */ 17242633Sobrien am_set_mypid(); /* for logging routines */ 173119682Smbr if ((old_groupid = setgid(groupid)) < 0) { 174119682Smbr plog(XLOG_WARNING, "could not setgid to %d: %m", groupid); 175119682Smbr return linkval; 176119682Smbr } 177119682Smbr if ((old_userid = seteuid(userid)) < 0) { 17838494Sobrien plog(XLOG_WARNING, "could not seteuid to %d: %m", userid); 179119682Smbr setgid(old_groupid); 18038494Sobrien return linkval; 18138494Sobrien } 18238494Sobrien if (hlfsd_stat(linkval, &homestat) < 0) { 18338494Sobrien if (errno == ENOENT) { /* make the spool dir if possible */ 18438494Sobrien /* don't use recursive mkdirs here */ 18538494Sobrien if (mkdir(linkval, PERS_SPOOLMODE) < 0) { 186119682Smbr seteuid(old_userid); 187119682Smbr setgid(old_groupid); 18838494Sobrien plog(XLOG_WARNING, "can't make directory %s: %m", linkval); 18938494Sobrien return alt_spooldir; 19038494Sobrien } 19138494Sobrien /* fall through to testing the disk space / quota */ 19238494Sobrien } else { /* the home dir itself must not exist then */ 193119682Smbr seteuid(old_userid); 194119682Smbr setgid(old_groupid); 19538494Sobrien plog(XLOG_WARNING, "bad link to %s: %m", linkval); 19638494Sobrien return alt_spooldir; 19738494Sobrien } 19838494Sobrien } 19938494Sobrien 20038494Sobrien /* 20138494Sobrien * If gets here, then either the spool dir in the home dir exists, 20238494Sobrien * or it was just created. In either case, we now need to 20338494Sobrien * test if we can create a small file and write at least one 20438494Sobrien * byte into it. This will test that we have both enough inodes 20538494Sobrien * and disk blocks to spare, or they fall within the user's quotas too. 20638494Sobrien * We are still seteuid to the user at this point. 20738494Sobrien */ 20838494Sobrien if (hlfsd_diskspace(linkval) < 0) { 209119682Smbr seteuid(old_userid); 210119682Smbr setgid(old_groupid); 21138494Sobrien plog(XLOG_WARNING, "no more space in %s: %m", linkval); 21238494Sobrien return alt_spooldir; 21338494Sobrien } else { 214119682Smbr seteuid(old_userid); 215119682Smbr setgid(old_groupid); 21638494Sobrien return linkval; 21738494Sobrien } 21838494Sobrien} 21938494Sobrien 22038494Sobrien 22138494Sobrienstatic int 22238494Sobrienhlfsd_diskspace(char *path) 22338494Sobrien{ 22438494Sobrien char buf[MAXPATHLEN]; 22538494Sobrien int fd, len; 22638494Sobrien 227174313Sobrien xsnprintf(buf, sizeof(buf), "%s/._hlfstmp_%lu", path, (long) getpid()); 22838494Sobrien if ((fd = open(buf, O_RDWR | O_CREAT, 0600)) < 0) { 22938494Sobrien plog(XLOG_ERROR, "cannot open %s: %m", buf); 23038494Sobrien return -1; 23138494Sobrien } 23238494Sobrien len = strlen(buf); 23338494Sobrien if (write(fd, buf, len) < len) { 23438494Sobrien plog(XLOG_ERROR, "cannot write \"%s\" (%d bytes) to %s : %m", buf, len, buf); 23538494Sobrien close(fd); 23638494Sobrien unlink(buf); /* cleanup just in case */ 23738494Sobrien return -1; 23838494Sobrien } 23938494Sobrien if (unlink(buf) < 0) { 24038494Sobrien plog(XLOG_ERROR, "cannot unlink %s : %m", buf); 24138494Sobrien } 24238494Sobrien close(fd); 24338494Sobrien return 0; 24438494Sobrien} 24538494Sobrien 24638494Sobrien 24738494Sobrienstatic int 24838494Sobrienhlfsd_stat(char *path, struct stat *statp) 24938494Sobrien{ 25038494Sobrien if (stat(path, statp) < 0) 25138494Sobrien return -1; 25238494Sobrien else if (!S_ISDIR(statp->st_mode)) { 25338494Sobrien errno = ENOTDIR; 25438494Sobrien return -1; 25538494Sobrien } 25638494Sobrien return 0; 25738494Sobrien} 25838494Sobrien 25938494Sobrien 26038494Sobrienstatic void 26138494Sobriendelay(uid2home_t *found, int secs) 26238494Sobrien{ 26338494Sobrien struct timeval tv; 26438494Sobrien 265310490Scy if (found) 266310490Scy dlog("delaying on child %ld for %d seconds", (long) found->child, secs); 26738494Sobrien 26838494Sobrien tv.tv_usec = 0; 26938494Sobrien 27038494Sobrien do { 27138494Sobrien tv.tv_sec = secs; 272310490Scy if (select(0, NULL, NULL, NULL, &tv) == 0) 27338494Sobrien break; 27438494Sobrien } while (--secs && found->child); 27538494Sobrien} 27638494Sobrien 27738494Sobrien 27838494Sobrien/* 27938494Sobrien * This function is called when a child has terminated after 28038494Sobrien * servicing an nfs request. We need to check the exit status and 28138494Sobrien * update the last_status field of the requesting user. 28238494Sobrien */ 28338494SobrienRETSIGTYPE 28438494Sobrieninterlock(int signum) 28538494Sobrien{ 28638494Sobrien int child; 28738494Sobrien uid2home_t *lostchild; 28838494Sobrien int status; 28938494Sobrien 29038494Sobrien#ifdef HAVE_WAITPID 291174313Sobrien while ((child = waitpid((pid_t) -1, &status, WNOHANG)) > 0) { 29238494Sobrien#else /* not HAVE_WAITPID */ 293310490Scy while ((child = wait3(&status, WNOHANG, (struct rusage *) NULL)) > 0) { 29438494Sobrien#endif /* not HAVE_WAITPID */ 29538494Sobrien 29638494Sobrien /* high chances this was the last child forked */ 29738494Sobrien if (lastchild && lastchild->child == child) { 29838494Sobrien lastchild->child = 0; 29938494Sobrien 30038494Sobrien if (WIFEXITED(status)) 30138494Sobrien lastchild->last_status = WEXITSTATUS(status); 30238494Sobrien lastchild = (uid2home_t *) NULL; 30338494Sobrien } else { 30438494Sobrien /* and if not, we have to search for it... */ 30538494Sobrien for (lostchild = pwtab; lostchild < &pwtab[cur_pwtab_num]; lostchild++) { 30638494Sobrien if (lostchild->child == child) { 30738494Sobrien if (WIFEXITED(status)) 30838494Sobrien lostchild->last_status = WEXITSTATUS(status); 309174313Sobrien lostchild->child = 0; 31038494Sobrien break; 31138494Sobrien } 31238494Sobrien } 31338494Sobrien } 31438494Sobrien } 31538494Sobrien} 31638494Sobrien 31738494Sobrien 31838494Sobrien/* 31938494Sobrien * PASSWORD AND USERNAME LOOKUP TABLES FUNCTIONS 32038494Sobrien */ 32138494Sobrien 32238494Sobrien/* 32338494Sobrien * get index of UserName table entry which matches username. 32438494Sobrien * must not return uid_t because we want to return a negative number. 32538494Sobrien */ 32638494Sobrienint 32738494Sobrienuntab_index(char *username) 32838494Sobrien{ 32938494Sobrien int max, min, mid, cmp; 33038494Sobrien 33138494Sobrien max = cur_pwtab_num - 1; 33238494Sobrien min = 0; 33338494Sobrien 33438494Sobrien do { 33538494Sobrien mid = (max + min) / 2; 33638494Sobrien cmp = strcmp(untab[mid].username, username); 33738494Sobrien if (cmp == 0) /* record found! */ 33838494Sobrien return mid; 33938494Sobrien if (cmp > 0) 34038494Sobrien max = mid; 34138494Sobrien else 34238494Sobrien min = mid; 34338494Sobrien } while (max > min + 1); 34438494Sobrien 34538494Sobrien if (STREQ(untab[max].username, username)) 34638494Sobrien return max; 34738494Sobrien if (STREQ(untab[min].username, username)) 34838494Sobrien return min; 34938494Sobrien 35038494Sobrien /* if gets here then record was not found */ 35138494Sobrien return -1; 35238494Sobrien} 35338494Sobrien 35438494Sobrien 35538494Sobrien/* 35638494Sobrien * Don't make this return a uid_t, because we need to return negative 35738494Sobrien * numbers as well (error codes.) 35838494Sobrien */ 35938494Sobrienint 36038494Sobrienuidof(char *username) 36138494Sobrien{ 36238494Sobrien int idx; 36338494Sobrien 36438494Sobrien if ((idx = untab_index(username)) < 0) /* not found */ 36538494Sobrien return INVALIDID; /* an invalid user id */ 36638494Sobrien return untab[idx].uid; 36738494Sobrien} 36838494Sobrien 36938494Sobrien 37038494Sobrien/* 37138494Sobrien * Don't make this return a uid_t, because we need to return negative 37238494Sobrien * numbers as well (error codes.) 37338494Sobrien */ 37438494Sobrienchar * 37538494Sobrienhomeof(char *username) 37638494Sobrien{ 37738494Sobrien int idx; 37838494Sobrien 37938494Sobrien if ((idx = untab_index(username)) < 0) /* not found */ 38038494Sobrien return (char *) NULL; /* an invalid user id */ 38138494Sobrien return untab[idx].home; 38238494Sobrien} 38338494Sobrien 38438494Sobrien 38538494Sobrienchar * 38638494Sobrienmailbox(int uid, char *username) 38738494Sobrien{ 38838494Sobrien char *home; 38938494Sobrien 39038494Sobrien if (uid < 0) 39138494Sobrien return (char *) NULL; /* not found */ 39238494Sobrien 39338494Sobrien if ((home = homeof(username)) == (char *) NULL) 39438494Sobrien return (char *) NULL; 39538494Sobrien if (STREQ(home, "/")) 396174313Sobrien xsnprintf(mboxfile, sizeof(mboxfile), 397174313Sobrien "/%s/%s", home_subdir, username); 39838494Sobrien else 399174313Sobrien xsnprintf(mboxfile, sizeof(mboxfile), 400174313Sobrien "%s/%s/%s", home, home_subdir, username); 40138494Sobrien return mboxfile; 40238494Sobrien} 40338494Sobrien 40438494Sobrien 40538494Sobrienstatic int 40638494Sobrienplt_compare_fxn(const voidp x, const voidp y) 407174313Sobrien 40838494Sobrien{ 40938494Sobrien uid2home_t *i = (uid2home_t *) x; 41038494Sobrien uid2home_t *j = (uid2home_t *) y; 41138494Sobrien 41238494Sobrien return i->uid - j->uid; 41338494Sobrien} 41438494Sobrien 41538494Sobrien 41638494Sobrienstatic int 41738494Sobrienunt_compare_fxn(const voidp x, const voidp y) 41838494Sobrien{ 41938494Sobrien username2uid_t *i = (username2uid_t *) x; 42038494Sobrien username2uid_t *j = (username2uid_t *) y; 42138494Sobrien 42238494Sobrien return strcmp(i->username, j->username); 42338494Sobrien} 42438494Sobrien 42538494Sobrien 42638494Sobrien/* perform initialization of user passwd database */ 42738494Sobrienstatic void 42838494Sobrienhlfsd_setpwent(void) 42938494Sobrien{ 43038494Sobrien if (!passwdfile) { 43138494Sobrien setpwent(); 43238494Sobrien return; 43338494Sobrien } 43438494Sobrien 43538494Sobrien passwd_fp = fopen(passwdfile, "r"); 43638494Sobrien if (!passwd_fp) { 43738494Sobrien plog(XLOG_ERROR, "unable to read passwd file %s: %m", passwdfile); 43838494Sobrien return; 43938494Sobrien } 44038494Sobrien plog(XLOG_INFO, "reading password entries from file %s", passwdfile); 44138494Sobrien 44238494Sobrien passwd_line = 0; 44338494Sobrien memset((char *) &passwd_ent, 0, sizeof(struct passwd)); 44438494Sobrien passwd_ent.pw_name = (char *) &pw_name; 44538494Sobrien passwd_ent.pw_dir = (char *) &pw_dir; 44638494Sobrien} 44738494Sobrien 44838494Sobrien 44938494Sobrien/* perform de-initialization of user passwd database */ 45038494Sobrienstatic void 45138494Sobrienhlfsd_endpwent(void) 45238494Sobrien{ 45338494Sobrien if (!passwdfile) { 45438494Sobrien /* 45538494Sobrien * Don't actually run this because we will be making more passwd calls 45638494Sobrien * afterwards. On Solaris 2.5.1, making getpwent() calls after calling 45738494Sobrien * endpwent() results in a memory leak! (and no, even Purify didn't 45838494Sobrien * detect it...) 45938494Sobrien * 46038494Sobrien endpwent(); 46138494Sobrien */ 46238494Sobrien return; 46338494Sobrien } 46438494Sobrien 46538494Sobrien if (passwd_fp) { 46638494Sobrien fclose(passwd_fp); 46738494Sobrien } 46838494Sobrien} 46938494Sobrien 47038494Sobrien 47138494Sobrien/* perform record reading/parsing of individual passwd database records */ 47238494Sobrienstatic struct passwd * 47338494Sobrienhlfsd_getpwent(void) 47438494Sobrien{ 47538494Sobrien char buf[256], *cp; 47638494Sobrien 47738494Sobrien /* check if to perform standard unix function */ 47838494Sobrien if (!passwdfile) { 47938494Sobrien return getpwent(); 48038494Sobrien } 48138494Sobrien 48238494Sobrien /* return here to read another entry */ 48338494Sobrienreadent: 48438494Sobrien 48538494Sobrien /* return NULL if reached end of file */ 48638494Sobrien if (feof(passwd_fp)) 48738494Sobrien return NULL; 48838494Sobrien 48938494Sobrien pw_name[0] = pw_dir[0] = '\0'; 49038494Sobrien 49138494Sobrien /* read records */ 49238494Sobrien buf[0] = '\0'; 493277879Spfg if (fgets(buf, 256, passwd_fp) == NULL) 494277879Spfg return NULL; 49538494Sobrien passwd_line++; 496277879Spfg if (buf[0] == '\0') 49738494Sobrien goto readent; 49838494Sobrien 49938494Sobrien /* read user name */ 50038494Sobrien cp = strtok(buf, ":"); 50138494Sobrien if (!cp || cp[0] == '\0') { 50238494Sobrien plog(XLOG_ERROR, "no user name on line %d of %s", passwd_line, passwdfile); 50338494Sobrien goto readent; 50438494Sobrien } 505174313Sobrien /* pw_name will show up in passwd_ent.pw_name */ 506174313Sobrien xstrlcpy(pw_name, cp, sizeof(pw_name)); 50738494Sobrien 50838494Sobrien /* skip passwd */ 50938494Sobrien strtok(NULL, ":"); 51038494Sobrien 51138494Sobrien /* read uid */ 51238494Sobrien cp = strtok(NULL, ":"); 51338494Sobrien if (!cp || cp[0] == '\0') { 51438494Sobrien plog(XLOG_ERROR, "no uid on line %d of %s", passwd_line, passwdfile); 51538494Sobrien goto readent; 51638494Sobrien } 51738494Sobrien passwd_ent.pw_uid = atoi(cp); 51838494Sobrien 51938494Sobrien /* skip gid and gcos */ 52038494Sobrien strtok(NULL, ":"); 52138494Sobrien strtok(NULL, ":"); 52238494Sobrien 52338494Sobrien /* read home dir */ 52438494Sobrien cp = strtok(NULL, ":"); 52538494Sobrien if (!cp || cp[0] == '\0') { 52638494Sobrien plog(XLOG_ERROR, "no home dir on line %d of %s", passwd_line, passwdfile); 52738494Sobrien goto readent; 52838494Sobrien } 529174313Sobrien /* pw_dir will show up in passwd_ent.pw_dir */ 530174313Sobrien xstrlcpy(pw_dir, cp, sizeof(pw_dir)); 53138494Sobrien 53238494Sobrien /* the rest of the fields are unimportant and not being considered */ 53338494Sobrien 53451300Sobrien plog(XLOG_USER, "hlfsd_getpwent: name=%s, uid=%ld, dir=%s", 53551300Sobrien passwd_ent.pw_name, (long) passwd_ent.pw_uid, passwd_ent.pw_dir); 53638494Sobrien 53738494Sobrien return &passwd_ent; 53838494Sobrien} 53938494Sobrien 54038494Sobrien 54138494Sobrien/* 54238494Sobrien * read and hash the passwd file or NIS map 54338494Sobrien */ 54438494Sobrienvoid 54538494Sobrienplt_init(void) 54638494Sobrien{ 54738494Sobrien struct passwd *pent_p; 54838494Sobrien 54938494Sobrien if (plt_reset() < 0) /* could not reset table. skip. */ 55038494Sobrien return; 55138494Sobrien 55238494Sobrien plog(XLOG_INFO, "reading password map"); 55338494Sobrien 55438494Sobrien hlfsd_setpwent(); /* prepare to read passwd entries */ 55538494Sobrien while ((pent_p = hlfsd_getpwent()) != (struct passwd *) NULL) { 55638494Sobrien table_add(pent_p->pw_uid, pent_p->pw_dir, pent_p->pw_name); 557119682Smbr if (STREQ("root", pent_p->pw_name)) { 558119682Smbr int len; 559119682Smbr if (root_home) 560119682Smbr XFREE(root_home); 561310490Scy root_home = xstrdup(pent_p->pw_dir); 562119682Smbr len = strlen(root_home); 563119682Smbr /* remove any trailing '/' chars from root's home (even if just one) */ 564119682Smbr while (len > 0 && root_home[len - 1] == '/') { 565119682Smbr len--; 566119682Smbr root_home[len] = '\0'; 567119682Smbr } 568119682Smbr } 56938494Sobrien } 57038494Sobrien hlfsd_endpwent(); 57138494Sobrien 57238494Sobrien qsort((char *) pwtab, cur_pwtab_num, sizeof(uid2home_t), 57338494Sobrien plt_compare_fxn); 57438494Sobrien qsort((char *) untab, cur_pwtab_num, sizeof(username2uid_t), 57538494Sobrien unt_compare_fxn); 57638494Sobrien 577119682Smbr if (!root_home) 578310490Scy root_home = xstrdup(""); 579119682Smbr 58038494Sobrien plog(XLOG_INFO, "password map read and sorted"); 58138494Sobrien} 58238494Sobrien 58338494Sobrien 58438494Sobrien/* 58538494Sobrien * This is essentially so that we don't reset known good lookup tables when a 58638494Sobrien * YP server goes down. 58738494Sobrien */ 58838494Sobrienstatic int 58938494Sobrienplt_reset(void) 59038494Sobrien{ 59138494Sobrien int i; 59238494Sobrien 59338494Sobrien hlfsd_setpwent(); 59438494Sobrien if (hlfsd_getpwent() == (struct passwd *) NULL) { 59538494Sobrien hlfsd_endpwent(); 59638494Sobrien return -1; /* did not reset table */ 59738494Sobrien } 59838494Sobrien hlfsd_endpwent(); 59938494Sobrien 60038494Sobrien lastchild = (uid2home_t *) NULL; 60138494Sobrien 60238494Sobrien if (max_pwtab_num > 0) /* was used already. cleanup old table */ 60338494Sobrien for (i = 0; i < cur_pwtab_num; ++i) { 60438494Sobrien if (pwtab[i].home) { 60538494Sobrien XFREE(pwtab[i].home); 60638494Sobrien pwtab[i].home = (char *) NULL; 60738494Sobrien } 60838494Sobrien pwtab[i].uid = INVALIDID; /* not a valid uid (yet...) */ 60938494Sobrien pwtab[i].child = (pid_t) 0; 61038494Sobrien pwtab[i].uname = (char *) NULL; /* only a ptr to untab[i].username */ 61138494Sobrien if (untab[i].username) { 61238494Sobrien XFREE(untab[i].username); 61338494Sobrien untab[i].username = (char *) NULL; 61438494Sobrien } 61538494Sobrien untab[i].uid = INVALIDID; /* invalid uid */ 61638494Sobrien untab[i].home = (char *) NULL; /* only a ptr to pwtab[i].home */ 61738494Sobrien } 61838494Sobrien cur_pwtab_num = 0; /* zero current size */ 61938494Sobrien 620119682Smbr if (root_home) 621119682Smbr XFREE(root_home); 622119682Smbr 62338494Sobrien return 0; /* resetting ok */ 62438494Sobrien} 62538494Sobrien 62638494Sobrien 62738494Sobrien/* 62838494Sobrien * u: uid number 62938494Sobrien * h: home directory 63038494Sobrien * n: user ID name 63138494Sobrien */ 63238494Sobrienstatic void 633174313Sobrientable_add(u_int u, const char *h, const char *n) 63438494Sobrien{ 63538494Sobrien int i; 63638494Sobrien 63738494Sobrien if (max_pwtab_num <= 0) { /* was never initialized */ 63838494Sobrien max_pwtab_num = 1; 63938494Sobrien pwtab = (uid2home_t *) xmalloc(max_pwtab_num * 64038494Sobrien sizeof(uid2home_t)); 64138494Sobrien memset((char *) &pwtab[0], 0, max_pwtab_num * sizeof(uid2home_t)); 64238494Sobrien untab = (username2uid_t *) xmalloc(max_pwtab_num * 64338494Sobrien sizeof(username2uid_t)); 64438494Sobrien memset((char *) &untab[0], 0, max_pwtab_num * sizeof(username2uid_t)); 64538494Sobrien } 64638494Sobrien 64738494Sobrien /* check if need more space. */ 64838494Sobrien if (cur_pwtab_num + 1 > max_pwtab_num) { 64938494Sobrien /* need more space in table */ 65038494Sobrien max_pwtab_num *= 2; 65138494Sobrien plog(XLOG_INFO, "reallocating table spaces to %d entries", max_pwtab_num); 65238494Sobrien pwtab = (uid2home_t *) xrealloc(pwtab, 65338494Sobrien sizeof(uid2home_t) * max_pwtab_num); 65438494Sobrien untab = (username2uid_t *) xrealloc(untab, 65538494Sobrien sizeof(username2uid_t) * 65638494Sobrien max_pwtab_num); 65738494Sobrien /* zero out newly added entries */ 65838494Sobrien for (i=cur_pwtab_num; i<max_pwtab_num; ++i) { 65938494Sobrien memset((char *) &pwtab[i], 0, sizeof(uid2home_t)); 66038494Sobrien memset((char *) &untab[i], 0, sizeof(username2uid_t)); 66138494Sobrien } 66238494Sobrien } 66338494Sobrien 66438494Sobrien /* do NOT add duplicate entries (this is an O(N^2) algorithm... */ 66538494Sobrien for (i=0; i<cur_pwtab_num; ++i) 66638494Sobrien if (u == pwtab[i].uid && u != 0 ) { 66738494Sobrien dlog("ignoring duplicate home %s for uid %d (already %s)", 66838494Sobrien h, u, pwtab[i].home); 66938494Sobrien return; 67038494Sobrien } 67138494Sobrien 67238494Sobrien /* add new password entry */ 673310490Scy pwtab[cur_pwtab_num].home = xstrdup(h); 67438494Sobrien pwtab[cur_pwtab_num].child = 0; 67538494Sobrien pwtab[cur_pwtab_num].last_access_time = 0; 67638494Sobrien pwtab[cur_pwtab_num].last_status = 0; /* assume best: used homedir */ 67738494Sobrien pwtab[cur_pwtab_num].uid = u; 67838494Sobrien 67938494Sobrien /* add new userhome entry */ 680310490Scy untab[cur_pwtab_num].username = xstrdup(n); 68138494Sobrien 68238494Sobrien /* just a second pointer */ 68338494Sobrien pwtab[cur_pwtab_num].uname = untab[cur_pwtab_num].username; 68438494Sobrien untab[cur_pwtab_num].uid = u; 68538494Sobrien untab[cur_pwtab_num].home = pwtab[cur_pwtab_num].home; /* a ptr */ 68638494Sobrien 68738494Sobrien /* increment counter */ 68838494Sobrien ++cur_pwtab_num; 68938494Sobrien} 69038494Sobrien 69138494Sobrien 69238494Sobrien/* 69338494Sobrien * return entry in lookup table 69438494Sobrien */ 69538494Sobrienuid2home_t * 696174313Sobrienplt_search(u_int u) 69738494Sobrien{ 69838494Sobrien int max, min, mid; 69938494Sobrien 70038494Sobrien /* 70138494Sobrien * empty table should not happen, 70238494Sobrien * but I have a bug with signals to trace... 70338494Sobrien */ 70438494Sobrien if (pwtab == (uid2home_t *) NULL) 70538494Sobrien return (uid2home_t *) NULL; 70638494Sobrien 70738494Sobrien max = cur_pwtab_num - 1; 70838494Sobrien min = 0; 70938494Sobrien 71038494Sobrien do { 71138494Sobrien mid = (max + min) / 2; 71238494Sobrien if (pwtab[mid].uid == u) /* record found! */ 71338494Sobrien return &pwtab[mid]; 71438494Sobrien if (pwtab[mid].uid > u) 71538494Sobrien max = mid; 71638494Sobrien else 71738494Sobrien min = mid; 71838494Sobrien } while (max > min + 1); 71938494Sobrien 72038494Sobrien if (pwtab[max].uid == u) 72138494Sobrien return &pwtab[max]; 72238494Sobrien if (pwtab[min].uid == u) 72338494Sobrien return &pwtab[min]; 72438494Sobrien 72538494Sobrien /* if gets here then record was not found */ 72638494Sobrien return (uid2home_t *) NULL; 72738494Sobrien} 72838494Sobrien 72938494Sobrien 73038494Sobrien#if defined(DEBUG) || defined(DEBUG_PRINT) 73138494Sobrienvoid 73238494Sobrienplt_print(int signum) 73338494Sobrien{ 73438494Sobrien FILE *dumpfile; 73538494Sobrien int dumpfd; 73638494Sobrien char dumptmp[] = "/usr/tmp/hlfsd.dump.XXXXXX"; 73738494Sobrien int i; 73838494Sobrien 73938494Sobrien#ifdef HAVE_MKSTEMP 74038494Sobrien dumpfd = mkstemp(dumptmp); 74138494Sobrien#else /* not HAVE_MKSTEMP */ 74238494Sobrien mktemp(dumptmp); 74338494Sobrien if (!dumptmp) { 744119682Smbr plog(XLOG_ERROR, "cannot create temporary dump file"); 74538494Sobrien return; 74638494Sobrien } 74738494Sobrien dumpfd = open(dumptmp, O_RDONLY); 74838494Sobrien#endif /* not HAVE_MKSTEMP */ 74938494Sobrien if (dumpfd < 0) { 75038494Sobrien plog(XLOG_ERROR, "cannot open temporary dump file"); 75138494Sobrien return; 75238494Sobrien } 75338494Sobrien if ((dumpfile = fdopen(dumpfd, "a")) != NULL) { 75438494Sobrien plog(XLOG_INFO, "dumping internal state to file %s", dumptmp); 75538494Sobrien fprintf(dumpfile, "\n\nNew plt_dump():\n"); 75638494Sobrien for (i = 0; i < cur_pwtab_num; ++i) 75738494Sobrien fprintf(dumpfile, 75838494Sobrien "%4d %5lu %10lu %1d %4lu \"%s\" uname=\"%s\"\n", 75938494Sobrien i, 76038494Sobrien (long) pwtab[i].child, 76138494Sobrien pwtab[i].last_access_time, 76238494Sobrien pwtab[i].last_status, 76338494Sobrien (long) pwtab[i].uid, 76438494Sobrien pwtab[i].home, 76538494Sobrien pwtab[i].uname); 76638494Sobrien fprintf(dumpfile, "\nUserName table by plt_print():\n"); 76738494Sobrien for (i = 0; i < cur_pwtab_num; ++i) 76838494Sobrien fprintf(dumpfile, "%4d : \"%s\" %4lu \"%s\"\n", i, 76938494Sobrien untab[i].username, (long) untab[i].uid, untab[i].home); 77038494Sobrien close(dumpfd); 77138494Sobrien fclose(dumpfile); 77238494Sobrien } 77338494Sobrien} 77438494Sobrien 77538494Sobrien 77638494Sobrienvoid 77738494Sobrienplt_dump(uid2home_t *lastc, pid_t this) 77838494Sobrien{ 77938494Sobrien FILE *dumpfile; 78038494Sobrien int i; 78138494Sobrien 78238494Sobrien if ((dumpfile = fopen("/var/tmp/hlfsdump", "a")) != NULL) { 78338494Sobrien fprintf(dumpfile, "\n\nNEW PLT_DUMP -- "); 78438494Sobrien fprintf(dumpfile, "lastchild->child=%d ", 78538494Sobrien (int) (lastc ? lastc->child : -999)); 78638494Sobrien fprintf(dumpfile, ", child from wait3=%lu:\n", (long) this); 78738494Sobrien for (i = 0; i < cur_pwtab_num; ++i) 78838494Sobrien fprintf(dumpfile, "%4d %5lu: %4lu \"%s\" uname=\"%s\"\n", i, 78938494Sobrien (long) pwtab[i].child, (long) pwtab[i].uid, 79038494Sobrien pwtab[i].home, pwtab[i].uname); 79138494Sobrien fprintf(dumpfile, "\nUserName table by plt_dump():\n"); 79238494Sobrien for (i = 0; i < cur_pwtab_num; ++i) 79338494Sobrien fprintf(dumpfile, "%4d : \"%s\" %4lu \"%s\"\n", i, 79438494Sobrien untab[i].username, (long) untab[i].uid, untab[i].home); 79538494Sobrien fprintf(dumpfile, "ezk: ent=%d, uid=%lu, home=\"%s\"\n", 79638494Sobrien untab_index("ezk"), 79738494Sobrien (long) untab[untab_index("ezk")].uid, 79838494Sobrien pwtab[untab[untab_index("ezk")].uid].home); 79938494Sobrien fprintf(dumpfile, "rezk: ent=%d, uid=%lu, home=\"%s\"\n", 80038494Sobrien untab_index("rezk"), 80138494Sobrien (long) untab[untab_index("rezk")].uid, 80238494Sobrien pwtab[untab[untab_index("rezk")].uid].home); 80338494Sobrien fclose(dumpfile); 80438494Sobrien } 80538494Sobrien} 80638494Sobrien#endif /* defined(DEBUG) || defined(DEBUG_PRINT) */ 807