122347Spst/* lock.c: The opielock() library function.
222347Spst
329964Sache%%% portions-copyright-cmetz-96
492914SmarkmPortions of this software are Copyright 1996-1999 by Craig Metz, All Rights
522347SpstReserved. The Inner Net License Version 2 applies to these portions of
622347Spstthe software.
722347SpstYou should have received a copy of the license with this software. If
822347Spstyou didn't get a copy, you may request one from <license@inner.net>.
922347Spst
1022347SpstPortions of this software are Copyright 1995 by Randall Atkinson and Dan
1122347SpstMcDonald, All Rights Reserved. All Rights under this copyright are assigned
1222347Spstto the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
1322347SpstLicense Agreement applies to this software.
1422347Spst
1522347Spst        History:
1622347Spst
1792914Smarkm	Modified by cmetz for OPIE 2.4. Use snprintf.
1829964Sache	Modified by cmetz for OPIE 2.31. Put locks in a separate dir.
1929964Sache            Bug fixes.
2022347Spst	Modified by cmetz for OPIE 2.3. Do refcounts whether or not we
2122347Spst            actually lock. Fixed USER_LOCKING=0 case.
2222347Spst	Modified by cmetz for OPIE 2.22. Added reference count for locks.
2322347Spst	    Changed lock filename/refcount symbol names to better indicate
2422347Spst	    that they're not user serviceable.
2522347Spst	Modified by cmetz for OPIE 2.2. Use FUNCTION declaration et al.
2622347Spst            Use "principal" instead of "name" to make it clearer.
2722347Spst            Ifdef around some headers, be more careful about allowed
2822347Spst            error return values. Check open() return value properly.
2922347Spst            Avoid NULL.
3022347Spst        Created at NRL for OPIE 2.2 from opiesubr2.c
3159300Skris
3259300Skris$FreeBSD$
3322347Spst*/
3422347Spst#include "opie_cfg.h"
3522347Spst#if HAVE_STRING_H
3622347Spst#include <string.h>
3722347Spst#endif /* HAVE_STRING_H */
3822347Spst#if HAVE_UNISTD_H
3922347Spst#include <unistd.h>
4022347Spst#endif /* HAVE_UNISTD_H */
4129964Sache#include <sys/stat.h>
4229964Sache#include <syslog.h>
4322347Spst#include <fcntl.h>
4422347Spst#if HAVE_STDLIB_H
4522347Spst#include <stdlib.h>
4622347Spst#endif /* HAVE_STDLIB_H */
4729964Sache#include <errno.h>
4822347Spst#include "opie.h"
4922347Spst
5029964Sache#if !HAVE_LSTAT
5129964Sache#define lstat(x, y) stat(x, y)
5229964Sache#endif /* !HAVE_LSTAT */
5329964Sache
5422347Spstint __opie_lockrefcount = 0;
5559300Skrisstatic int do_atexit = 1;
5622347Spst
5759300SkrisVOIDRET opiedisableaeh FUNCTION_NOARGS
5859300Skris{
5959300Skris  do_atexit = 0;
6059300Skris}
6122347Spst#if USER_LOCKING
6222347Spstchar *__opie_lockfilename = (char *)0;
6322347Spst
6422347Spst/* atexit() handler for opielock() */
6559300SkrisVOIDRET opieunlockaeh FUNCTION_NOARGS
6622347Spst{
6722347Spst  if (__opie_lockfilename) {
6822347Spst    __opie_lockrefcount = 0;
6922347Spst    opieunlock();
7022347Spst  }
7122347Spst}
7222347Spst#endif /* USER_LOCKING */
7322347Spst
7422347Spst/*
7522347Spst   Serialize (we hope) authentication of user to prevent race conditions.
7622347Spst   Creates a lock file with a name of OPIE_LOCK_PREFIX with the user name
7722347Spst   appended. This file contains the pid of the lock's owner and a time()
7822347Spst   stamp. We use the former to check for dead owners and the latter to
7922347Spst   provide an upper bound on the lock duration. If there are any problems,
8022347Spst   we assume the lock is bogus.
8122347Spst
8222347Spst   The value of this locking and its security implications are still not
8322347Spst   completely clear and require further study.
8422347Spst
8522347Spst   One could conceivably hack this facility to provide locking of user
8622347Spst   accounts after several authentication failures.
8722347Spst
8822347Spst   Return -1 on low-level error, 0 if ok, 1 on locking failure.
8922347Spst*/
9022347Spstint opielock FUNCTION((principal), char *principal)
9122347Spst{
9222347Spst#if USER_LOCKING
9322347Spst  int fh, waits = 0, rval = -1, pid, t, i;
9422347Spst  char buffer[128], buffer2[128], *c, *c2;
9529964Sache  struct stat statbuf[2];
9622347Spst
9729964Sache  if (getuid() && geteuid()) {
9829964Sache#if DEBUG
9929964Sache    syslog(LOG_DEBUG, "opielock: requires superuser priveleges");
10029964Sache#endif /* DEBUG */
10129964Sache    return -1;
10229964Sache  };
10329964Sache
10422347Spst  if (__opie_lockfilename) {
10522347Spst    __opie_lockrefcount++;
10622347Spst    return 0;
10722347Spst  }
10822347Spst
10929964Sache  if (!(__opie_lockfilename = (char *)malloc(sizeof(OPIE_LOCK_DIR) + 1 + strlen(principal))))
11022347Spst    return -1;
11122347Spst
11229964Sache  strcpy(__opie_lockfilename, OPIE_LOCK_DIR);
11329964Sache
11429964Sache  if (mkdir(__opie_lockfilename, 0700) < 0)
11529964Sache    if (errno != EEXIST)
11629964Sache      return -1;
11729964Sache
11829964Sache  if (lstat(__opie_lockfilename, &statbuf[0]) < 0)
11929964Sache    return -1;
12029964Sache
12129964Sache  if (statbuf[0].st_uid) {
12229964Sache#if DEBUG
12329964Sache    syslog(LOG_DEBUG, "opielock: %s isn't owned by the superuser.", __opie_lockfilename);
12429964Sache#endif /* DEBUG */
12529964Sache    return -1;
12629964Sache  };
12729964Sache
12829964Sache  if (!S_ISDIR(statbuf[0].st_mode)) {
12929964Sache#if DEBUG
13029964Sache    syslog(LOG_DEBUG, "opielock: %s isn't a directory.", __opie_lockfilename);
13129964Sache#endif /* DEBUG */
13229964Sache    return -1;
13329964Sache  };
13429964Sache
13529964Sache  if ((statbuf[0].st_mode & 0777) != 00700) {
13629964Sache#if DEBUG
13729964Sache    syslog(LOG_DEBUG, "opielock: permissions on %s are not correct.", __opie_lockfilename);
13829964Sache#endif /* DEBUG */
13929964Sache    return -1;
14029964Sache  };
14129964Sache
14229964Sache  strcat(__opie_lockfilename, "/");
14322347Spst  strcat(__opie_lockfilename, principal);
14422347Spst
14529964Sache  fh = -1;
14629964Sache  while (fh < 0) {
14729964Sache    if (!lstat(__opie_lockfilename, &statbuf[0]))
14829964Sache      if (!S_ISREG(statbuf[0].st_mode))
14929964Sache        goto lockret;
15029964Sache
15122347Spst    if ((fh = open(__opie_lockfilename, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0) {
15229964Sache      if (lstat(__opie_lockfilename, &statbuf[1]) < 0)
15322347Spst        goto lockret;
15429964Sache      if (statbuf[0].st_ino != statbuf[1].st_ino)
15529964Sache        goto lockret;
15629964Sache      if (statbuf[0].st_mode != statbuf[1].st_mode)
15729964Sache        goto lockret;
15829964Sache      if ((fh = open(__opie_lockfilename, O_RDONLY, 0600)) < 0)
15929964Sache        goto lockret;
16022347Spst      if ((i = read(fh, buffer, sizeof(buffer))) <= 0)
16122347Spst        goto lockret;
16222347Spst
16322347Spst      buffer[sizeof(buffer) - 1] = 0;
16422347Spst      buffer[i - 1] = 0;
16522347Spst
16622347Spst      if (!(c = strchr(buffer, '\n')))
16722347Spst        break;
16822347Spst
16922347Spst      *(c++) = 0;
17022347Spst
17122347Spst      if (!(c2 = strchr(c, '\n')))
17222347Spst        break;
17322347Spst
17422347Spst      *(c2++) = 0;
17522347Spst
17622347Spst      if (!(pid = atoi(buffer)))
17722347Spst        break;
17822347Spst
17922347Spst      if (!(t = atoi(c)))
18022347Spst        break;
18122347Spst
18229964Sache      if ((t + OPIE_LOCK_TIMEOUT) < time(0))
18322347Spst        break;
18422347Spst
18522347Spst      if (kill(pid, 0))
18622347Spst        break;
18722347Spst
18822347Spst      close(fh);
18922347Spst      fh = 0;
19022347Spst      sleep(1);
19122347Spst      if (waits++ > 3) {
19222347Spst        rval = 1;
19322347Spst        goto lockret;
19422347Spst      };
19522347Spst    };
19629964Sache  };
19722347Spst
19829964Sache  if (lstat(__opie_lockfilename, &statbuf[0]) < 0)
19929964Sache    goto lockret;
20029964Sache  if (fstat(fh, &statbuf[1]) < 0)
20129964Sache    goto lockret;
20229964Sache  if (!S_ISREG(statbuf[0].st_mode) || (statbuf[0].st_mode != statbuf[1].st_mode) || (statbuf[0].st_ino != statbuf[1].st_ino))
20329964Sache    goto lockret;
20429964Sache
20592914Smarkm  if (snprintf(buffer, sizeof(buffer), "%d\n%d\n", getpid(), time(0)) >= sizeof(buffer))
20692914Smarkm    goto lockret;
20792914Smarkm
20822347Spst  i = strlen(buffer) + 1;
20922347Spst  if (lseek(fh, 0, SEEK_SET)) {
21022347Spst    close(fh);
21122347Spst    unlink(__opie_lockfilename);
21222347Spst    fh = 0;
21322347Spst    goto lockret;
21422347Spst  };
21522347Spst  if (write(fh, buffer, i) != i) {
21622347Spst    close(fh);
21722347Spst    unlink(__opie_lockfilename);
21822347Spst    fh = 0;
21922347Spst    goto lockret;
22022347Spst  };
22122347Spst  close(fh);
22222347Spst  if ((fh = open(__opie_lockfilename, O_RDWR, 0600)) < 0) {
22322347Spst    unlink(__opie_lockfilename);
22422347Spst    goto lockret;
22522347Spst  };
22622347Spst  if (read(fh, buffer2, i) != i) {
22722347Spst    close(fh);
22822347Spst    unlink(__opie_lockfilename);
22922347Spst    fh = 0;
23022347Spst    goto lockret;
23122347Spst  };
23222347Spst  close(fh);
23322347Spst  if (memcmp(buffer, buffer2, i)) {
23422347Spst    unlink(__opie_lockfilename);
23522347Spst    goto lockret;
23622347Spst  };
23722347Spst
23822347Spst  __opie_lockrefcount++;
23922347Spst  rval = 0;
24059300Skris  if (do_atexit)
24159300Skris    atexit(opieunlockaeh);
24222347Spst
24322347Spstlockret:
24429964Sache  if (fh >= 0)
24522347Spst    close(fh);
24629964Sache  if (!__opie_lockrefcount) {
24729964Sache    free (__opie_lockfilename);
24829964Sache    __opie_lockfilename = NULL;
24929964Sache  };
25022347Spst  return rval;
25122347Spst#else /* USER_LOCKING */
25222347Spst  __opie_lockrefcount++;
25322347Spst  return 0;
25422347Spst#endif /* USER_LOCKING */
25522347Spst}
256