12dfcbf1pst/* lock.c: The opielock() library function.
22dfcbf1pst
3620a154ache%%% portions-copyright-cmetz-96
4b38a001markmPortions of this software are Copyright 1996-1999 by Craig Metz, All Rights
52dfcbf1pstReserved. The Inner Net License Version 2 applies to these portions of
62dfcbf1pstthe software.
72dfcbf1pstYou should have received a copy of the license with this software. If
82dfcbf1pstyou didn't get a copy, you may request one from <license@inner.net>.
92dfcbf1pst
102dfcbf1pstPortions of this software are Copyright 1995 by Randall Atkinson and Dan
112dfcbf1pstMcDonald, All Rights Reserved. All Rights under this copyright are assigned
122dfcbf1pstto the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
132dfcbf1pstLicense Agreement applies to this software.
142dfcbf1pst
152dfcbf1pst        History:
162dfcbf1pst
17b38a001markm	Modified by cmetz for OPIE 2.4. Use snprintf.
18620a154ache	Modified by cmetz for OPIE 2.31. Put locks in a separate dir.
19620a154ache            Bug fixes.
202dfcbf1pst	Modified by cmetz for OPIE 2.3. Do refcounts whether or not we
212dfcbf1pst            actually lock. Fixed USER_LOCKING=0 case.
222dfcbf1pst	Modified by cmetz for OPIE 2.22. Added reference count for locks.
232dfcbf1pst	    Changed lock filename/refcount symbol names to better indicate
242dfcbf1pst	    that they're not user serviceable.
252dfcbf1pst	Modified by cmetz for OPIE 2.2. Use FUNCTION declaration et al.
262dfcbf1pst            Use "principal" instead of "name" to make it clearer.
272dfcbf1pst            Ifdef around some headers, be more careful about allowed
282dfcbf1pst            error return values. Check open() return value properly.
292dfcbf1pst            Avoid NULL.
302dfcbf1pst        Created at NRL for OPIE 2.2 from opiesubr2.c
317db2ebckris
327db2ebckris$FreeBSD$
332dfcbf1pst*/
342dfcbf1pst#include "opie_cfg.h"
352dfcbf1pst#if HAVE_STRING_H
362dfcbf1pst#include <string.h>
372dfcbf1pst#endif /* HAVE_STRING_H */
382dfcbf1pst#if HAVE_UNISTD_H
392dfcbf1pst#include <unistd.h>
402dfcbf1pst#endif /* HAVE_UNISTD_H */
41620a154ache#include <sys/stat.h>
42620a154ache#include <syslog.h>
432dfcbf1pst#include <fcntl.h>
442dfcbf1pst#if HAVE_STDLIB_H
452dfcbf1pst#include <stdlib.h>
462dfcbf1pst#endif /* HAVE_STDLIB_H */
47620a154ache#include <errno.h>
482dfcbf1pst#include "opie.h"
492dfcbf1pst
50620a154ache#if !HAVE_LSTAT
51620a154ache#define lstat(x, y) stat(x, y)
52620a154ache#endif /* !HAVE_LSTAT */
53620a154ache
542dfcbf1pstint __opie_lockrefcount = 0;
557db2ebckrisstatic int do_atexit = 1;
562dfcbf1pst
577db2ebckrisVOIDRET opiedisableaeh FUNCTION_NOARGS
587db2ebckris{
597db2ebckris  do_atexit = 0;
607db2ebckris}
612dfcbf1pst#if USER_LOCKING
622dfcbf1pstchar *__opie_lockfilename = (char *)0;
632dfcbf1pst
642dfcbf1pst/* atexit() handler for opielock() */
657db2ebckrisVOIDRET opieunlockaeh FUNCTION_NOARGS
662dfcbf1pst{
672dfcbf1pst  if (__opie_lockfilename) {
682dfcbf1pst    __opie_lockrefcount = 0;
692dfcbf1pst    opieunlock();
702dfcbf1pst  }
712dfcbf1pst}
722dfcbf1pst#endif /* USER_LOCKING */
732dfcbf1pst
742dfcbf1pst/*
752dfcbf1pst   Serialize (we hope) authentication of user to prevent race conditions.
762dfcbf1pst   Creates a lock file with a name of OPIE_LOCK_PREFIX with the user name
772dfcbf1pst   appended. This file contains the pid of the lock's owner and a time()
782dfcbf1pst   stamp. We use the former to check for dead owners and the latter to
792dfcbf1pst   provide an upper bound on the lock duration. If there are any problems,
802dfcbf1pst   we assume the lock is bogus.
812dfcbf1pst
822dfcbf1pst   The value of this locking and its security implications are still not
832dfcbf1pst   completely clear and require further study.
842dfcbf1pst
852dfcbf1pst   One could conceivably hack this facility to provide locking of user
862dfcbf1pst   accounts after several authentication failures.
872dfcbf1pst
882dfcbf1pst   Return -1 on low-level error, 0 if ok, 1 on locking failure.
892dfcbf1pst*/
902dfcbf1pstint opielock FUNCTION((principal), char *principal)
912dfcbf1pst{
922dfcbf1pst#if USER_LOCKING
932dfcbf1pst  int fh, waits = 0, rval = -1, pid, t, i;
942dfcbf1pst  char buffer[128], buffer2[128], *c, *c2;
95620a154ache  struct stat statbuf[2];
96620a154ache
97620a154ache  if (getuid() && geteuid()) {
98620a154ache#if DEBUG
99620a154ache    syslog(LOG_DEBUG, "opielock: requires superuser priveleges");
100620a154ache#endif /* DEBUG */
101620a154ache    return -1;
102620a154ache  };
1032dfcbf1pst
1042dfcbf1pst  if (__opie_lockfilename) {
1052dfcbf1pst    __opie_lockrefcount++;
1062dfcbf1pst    return 0;
1072dfcbf1pst  }
1082dfcbf1pst
109620a154ache  if (!(__opie_lockfilename = (char *)malloc(sizeof(OPIE_LOCK_DIR) + 1 + strlen(principal))))
110620a154ache    return -1;
111620a154ache
112620a154ache  strcpy(__opie_lockfilename, OPIE_LOCK_DIR);
113620a154ache
114620a154ache  if (mkdir(__opie_lockfilename, 0700) < 0)
115620a154ache    if (errno != EEXIST)
116620a154ache      return -1;
117620a154ache
118620a154ache  if (lstat(__opie_lockfilename, &statbuf[0]) < 0)
119620a154ache    return -1;
120620a154ache
121620a154ache  if (statbuf[0].st_uid) {
122620a154ache#if DEBUG
123620a154ache    syslog(LOG_DEBUG, "opielock: %s isn't owned by the superuser.", __opie_lockfilename);
124620a154ache#endif /* DEBUG */
125620a154ache    return -1;
126620a154ache  };
127620a154ache
128620a154ache  if (!S_ISDIR(statbuf[0].st_mode)) {
129620a154ache#if DEBUG
130620a154ache    syslog(LOG_DEBUG, "opielock: %s isn't a directory.", __opie_lockfilename);
131620a154ache#endif /* DEBUG */
132620a154ache    return -1;
133620a154ache  };
134620a154ache
135620a154ache  if ((statbuf[0].st_mode & 0777) != 00700) {
136620a154ache#if DEBUG
137620a154ache    syslog(LOG_DEBUG, "opielock: permissions on %s are not correct.", __opie_lockfilename);
138620a154ache#endif /* DEBUG */
1392dfcbf1pst    return -1;
140620a154ache  };
1412dfcbf1pst
142620a154ache  strcat(__opie_lockfilename, "/");
1432dfcbf1pst  strcat(__opie_lockfilename, principal);
1442dfcbf1pst
145620a154ache  fh = -1;
146620a154ache  while (fh < 0) {
147620a154ache    if (!lstat(__opie_lockfilename, &statbuf[0]))
148620a154ache      if (!S_ISREG(statbuf[0].st_mode))
149620a154ache        goto lockret;
150620a154ache
1512dfcbf1pst    if ((fh = open(__opie_lockfilename, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0) {
152620a154ache      if (lstat(__opie_lockfilename, &statbuf[1]) < 0)
153620a154ache        goto lockret;
154620a154ache      if (statbuf[0].st_ino != statbuf[1].st_ino)
155620a154ache        goto lockret;
156620a154ache      if (statbuf[0].st_mode != statbuf[1].st_mode)
157620a154ache        goto lockret;
158620a154ache      if ((fh = open(__opie_lockfilename, O_RDONLY, 0600)) < 0)
1592dfcbf1pst        goto lockret;
1602dfcbf1pst      if ((i = read(fh, buffer, sizeof(buffer))) <= 0)
1612dfcbf1pst        goto lockret;
1622dfcbf1pst
1632dfcbf1pst      buffer[sizeof(buffer) - 1] = 0;
1642dfcbf1pst      buffer[i - 1] = 0;
1652dfcbf1pst
1662dfcbf1pst      if (!(c = strchr(buffer, '\n')))
1672dfcbf1pst        break;
1682dfcbf1pst
1692dfcbf1pst      *(c++) = 0;
1702dfcbf1pst
1712dfcbf1pst      if (!(c2 = strchr(c, '\n')))
1722dfcbf1pst        break;
1732dfcbf1pst
1742dfcbf1pst      *(c2++) = 0;
1752dfcbf1pst
1762dfcbf1pst      if (!(pid = atoi(buffer)))
1772dfcbf1pst        break;
1782dfcbf1pst
1792dfcbf1pst      if (!(t = atoi(c)))
1802dfcbf1pst        break;
1812dfcbf1pst
182620a154ache      if ((t + OPIE_LOCK_TIMEOUT) < time(0))
1832dfcbf1pst        break;
1842dfcbf1pst
1852dfcbf1pst      if (kill(pid, 0))
1862dfcbf1pst        break;
1872dfcbf1pst
1882dfcbf1pst      close(fh);
1892dfcbf1pst      fh = 0;
1902dfcbf1pst      sleep(1);
1912dfcbf1pst      if (waits++ > 3) {
1922dfcbf1pst        rval = 1;
1932dfcbf1pst        goto lockret;
1942dfcbf1pst      };
1952dfcbf1pst    };
196620a154ache  };
197620a154ache
198620a154ache  if (lstat(__opie_lockfilename, &statbuf[0]) < 0)
199620a154ache    goto lockret;
200620a154ache  if (fstat(fh, &statbuf[1]) < 0)
201620a154ache    goto lockret;
202620a154ache  if (!S_ISREG(statbuf[0].st_mode) || (statbuf[0].st_mode != statbuf[1].st_mode) || (statbuf[0].st_ino != statbuf[1].st_ino))
203620a154ache    goto lockret;
2042dfcbf1pst
205b38a001markm  if (snprintf(buffer, sizeof(buffer), "%d\n%d\n", getpid(), time(0)) >= sizeof(buffer))
206b38a001markm    goto lockret;
207b38a001markm
2082dfcbf1pst  i = strlen(buffer) + 1;
2092dfcbf1pst  if (lseek(fh, 0, SEEK_SET)) {
2102dfcbf1pst    close(fh);
2112dfcbf1pst    unlink(__opie_lockfilename);
2122dfcbf1pst    fh = 0;
2132dfcbf1pst    goto lockret;
2142dfcbf1pst  };
2152dfcbf1pst  if (write(fh, buffer, i) != i) {
2162dfcbf1pst    close(fh);
2172dfcbf1pst    unlink(__opie_lockfilename);
2182dfcbf1pst    fh = 0;
2192dfcbf1pst    goto lockret;
2202dfcbf1pst  };
2212dfcbf1pst  close(fh);
2222dfcbf1pst  if ((fh = open(__opie_lockfilename, O_RDWR, 0600)) < 0) {
2232dfcbf1pst    unlink(__opie_lockfilename);
2242dfcbf1pst    goto lockret;
2252dfcbf1pst  };
2262dfcbf1pst  if (read(fh, buffer2, i) != i) {
2272dfcbf1pst    close(fh);
2282dfcbf1pst    unlink(__opie_lockfilename);
2292dfcbf1pst    fh = 0;
2302dfcbf1pst    goto lockret;
2312dfcbf1pst  };
2322dfcbf1pst  close(fh);
2332dfcbf1pst  if (memcmp(buffer, buffer2, i)) {
2342dfcbf1pst    unlink(__opie_lockfilename);
2352dfcbf1pst    goto lockret;
2362dfcbf1pst  };
2372dfcbf1pst
2382dfcbf1pst  __opie_lockrefcount++;
2392dfcbf1pst  rval = 0;
2407db2ebckris  if (do_atexit)
2417db2ebckris    atexit(opieunlockaeh);
2422dfcbf1pst
2432dfcbf1pstlockret:
244620a154ache  if (fh >= 0)
2452dfcbf1pst    close(fh);
246620a154ache  if (!__opie_lockrefcount) {
247620a154ache    free (__opie_lockfilename);
248620a154ache    __opie_lockfilename = NULL;
249620a154ache  };
2502dfcbf1pst  return rval;
2512dfcbf1pst#else /* USER_LOCKING */
2522dfcbf1pst  __opie_lockrefcount++;
2532dfcbf1pst  return 0;
2542dfcbf1pst#endif /* USER_LOCKING */
2552dfcbf1pst}
256