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