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