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