lock.c revision 59300
1155192Srwatson/* lock.c: The opielock() library function.
2188311Srwatson
3155192Srwatson%%% portions-copyright-cmetz-96
4155192SrwatsonPortions of this software are Copyright 1996-1998 by Craig Metz, All Rights
5155192SrwatsonReserved. The Inner Net License Version 2 applies to these portions of
6155192Srwatsonthe software.
7155192SrwatsonYou should have received a copy of the license with this software. If
8155192Srwatsonyou didn't get a copy, you may request one from <license@inner.net>.
9155192Srwatson
10155192SrwatsonPortions of this software are Copyright 1995 by Randall Atkinson and Dan
11155192SrwatsonMcDonald, All Rights Reserved. All Rights under this copyright are assigned
12155192Srwatsonto the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13180701SrwatsonLicense Agreement applies to this software.
14155192Srwatson
15155192Srwatson        History:
16155192Srwatson
17155192Srwatson	Modified by cmetz for OPIE 2.31. Put locks in a separate dir.
18155192Srwatson            Bug fixes.
19155192Srwatson	Modified by cmetz for OPIE 2.3. Do refcounts whether or not we
20155192Srwatson            actually lock. Fixed USER_LOCKING=0 case.
21155192Srwatson	Modified by cmetz for OPIE 2.22. Added reference count for locks.
22155192Srwatson	    Changed lock filename/refcount symbol names to better indicate
23155192Srwatson	    that they're not user serviceable.
24155192Srwatson	Modified by cmetz for OPIE 2.2. Use FUNCTION declaration et al.
25155192Srwatson            Use "principal" instead of "name" to make it clearer.
26155192Srwatson            Ifdef around some headers, be more careful about allowed
27155192Srwatson            error return values. Check open() return value properly.
28155192Srwatson            Avoid NULL.
29155192Srwatson        Created at NRL for OPIE 2.2 from opiesubr2.c
30178186Srwatson
31178186Srwatson$FreeBSD: head/contrib/opie/libopie/lock.c 59300 2000-04-17 00:01:23Z kris $
32178186Srwatson*/
33155192Srwatson#include "opie_cfg.h"
34155192Srwatson#if HAVE_STRING_H
35155192Srwatson#include <string.h>
36155192Srwatson#endif /* HAVE_STRING_H */
37155192Srwatson#if HAVE_UNISTD_H
38155192Srwatson#include <unistd.h>
39155192Srwatson#endif /* HAVE_UNISTD_H */
40160136Swsalamon#include <sys/stat.h>
41155192Srwatson#include <syslog.h>
42155192Srwatson#include <fcntl.h>
43155192Srwatson#if HAVE_STDLIB_H
44155192Srwatson#include <stdlib.h>
45155192Srwatson#endif /* HAVE_STDLIB_H */
46155192Srwatson#include <errno.h>
47155192Srwatson#include "opie.h"
48155192Srwatson
49155192Srwatson#if !HAVE_LSTAT
50155192Srwatson#define lstat(x, y) stat(x, y)
51155192Srwatson#endif /* !HAVE_LSTAT */
52155192Srwatson
53155192Srwatsonint __opie_lockrefcount = 0;
54155192Srwatsonstatic int do_atexit = 1;
55155192Srwatson
56155192SrwatsonVOIDRET opiedisableaeh FUNCTION_NOARGS
57155192Srwatson{
58155192Srwatson  do_atexit = 0;
59155192Srwatson}
60155192Srwatson#if USER_LOCKING
61155192Srwatsonchar *__opie_lockfilename = (char *)0;
62155192Srwatson
63155192Srwatson/* atexit() handler for opielock() */
64155192SrwatsonVOIDRET opieunlockaeh FUNCTION_NOARGS
65155192Srwatson{
66155192Srwatson  if (__opie_lockfilename) {
67155192Srwatson    __opie_lockrefcount = 0;
68155192Srwatson    opieunlock();
69155192Srwatson  }
70155192Srwatson}
71155192Srwatson#endif /* USER_LOCKING */
72155192Srwatson
73156889Srwatson/*
74156889Srwatson   Serialize (we hope) authentication of user to prevent race conditions.
75156889Srwatson   Creates a lock file with a name of OPIE_LOCK_PREFIX with the user name
76180706Srwatson   appended. This file contains the pid of the lock's owner and a time()
77156889Srwatson   stamp. We use the former to check for dead owners and the latter to
78156889Srwatson   provide an upper bound on the lock duration. If there are any problems,
79155192Srwatson   we assume the lock is bogus.
80156889Srwatson
81155192Srwatson   The value of this locking and its security implications are still not
82156889Srwatson   completely clear and require further study.
83155192Srwatson
84162466Srwatson   One could conceivably hack this facility to provide locking of user
85155192Srwatson   accounts after several authentication failures.
86155192Srwatson
87155192Srwatson   Return -1 on low-level error, 0 if ok, 1 on locking failure.
88155192Srwatson*/
89155192Srwatsonint opielock FUNCTION((principal), char *principal)
90155192Srwatson{
91155192Srwatson#if USER_LOCKING
92155192Srwatson  int fh, waits = 0, rval = -1, pid, t, i;
93155192Srwatson  char buffer[128], buffer2[128], *c, *c2;
94156889Srwatson  struct stat statbuf[2];
95155192Srwatson
96155192Srwatson  if (getuid() && geteuid()) {
97155192Srwatson#if DEBUG
98155192Srwatson    syslog(LOG_DEBUG, "opielock: requires superuser priveleges");
99155192Srwatson#endif /* DEBUG */
100155192Srwatson    return -1;
101155192Srwatson  };
102155192Srwatson
103155192Srwatson  if (__opie_lockfilename) {
104155192Srwatson    __opie_lockrefcount++;
105155192Srwatson    return 0;
106155192Srwatson  }
107155192Srwatson
108155192Srwatson  if (!(__opie_lockfilename = (char *)malloc(sizeof(OPIE_LOCK_DIR) + 1 + strlen(principal))))
109155192Srwatson    return -1;
110155192Srwatson
111155192Srwatson  strcpy(__opie_lockfilename, OPIE_LOCK_DIR);
112155192Srwatson
113155192Srwatson  if (mkdir(__opie_lockfilename, 0700) < 0)
114155192Srwatson    if (errno != EEXIST)
115155192Srwatson      return -1;
116184856Scsjp
117184856Scsjp  if (lstat(__opie_lockfilename, &statbuf[0]) < 0)
118184856Scsjp    return -1;
119156889Srwatson
120184856Scsjp  if (statbuf[0].st_uid) {
121184856Scsjp#if DEBUG
122184856Scsjp    syslog(LOG_DEBUG, "opielock: %s isn't owned by the superuser.", __opie_lockfilename);
123184856Scsjp#endif /* DEBUG */
124184856Scsjp    return -1;
125184856Scsjp  };
126184856Scsjp
127184856Scsjp  if (!S_ISDIR(statbuf[0].st_mode)) {
128184856Scsjp#if DEBUG
129184856Scsjp    syslog(LOG_DEBUG, "opielock: %s isn't a directory.", __opie_lockfilename);
130184856Scsjp#endif /* DEBUG */
131184856Scsjp    return -1;
132184856Scsjp  };
133184856Scsjp
134184856Scsjp  if ((statbuf[0].st_mode & 0777) != 00700) {
135184856Scsjp#if DEBUG
136162466Srwatson    syslog(LOG_DEBUG, "opielock: permissions on %s are not correct.", __opie_lockfilename);
137170196Srwatson#endif /* DEBUG */
138162466Srwatson    return -1;
139162466Srwatson  };
140184856Scsjp
141184856Scsjp  strcat(__opie_lockfilename, "/");
142184856Scsjp  strcat(__opie_lockfilename, principal);
143184856Scsjp
144162466Srwatson  fh = -1;
145155192Srwatson  while (fh < 0) {
146162466Srwatson    if (!lstat(__opie_lockfilename, &statbuf[0]))
147162466Srwatson      if (!S_ISREG(statbuf[0].st_mode))
148155192Srwatson        goto lockret;
149162466Srwatson
150162466Srwatson    if ((fh = open(__opie_lockfilename, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0) {
151162466Srwatson      if (lstat(__opie_lockfilename, &statbuf[1]) < 0)
152162466Srwatson        goto lockret;
153162466Srwatson      if (statbuf[0].st_ino != statbuf[1].st_ino)
154155192Srwatson        goto lockret;
155155192Srwatson      if (statbuf[0].st_mode != statbuf[1].st_mode)
156155192Srwatson        goto lockret;
157155192Srwatson      if ((fh = open(__opie_lockfilename, O_RDONLY, 0600)) < 0)
158156889Srwatson        goto lockret;
159156889Srwatson      if ((i = read(fh, buffer, sizeof(buffer))) <= 0)
160155192Srwatson        goto lockret;
161155192Srwatson
162155192Srwatson      buffer[sizeof(buffer) - 1] = 0;
163155192Srwatson      buffer[i - 1] = 0;
164155192Srwatson
165155192Srwatson      if (!(c = strchr(buffer, '\n')))
166156889Srwatson        break;
167155192Srwatson
168155192Srwatson      *(c++) = 0;
169155192Srwatson
170155192Srwatson      if (!(c2 = strchr(c, '\n')))
171156889Srwatson        break;
172155192Srwatson
173155192Srwatson      *(c2++) = 0;
174156889Srwatson
175155192Srwatson      if (!(pid = atoi(buffer)))
176155192Srwatson        break;
177155192Srwatson
178155192Srwatson      if (!(t = atoi(c)))
179155192Srwatson        break;
180170196Srwatson
181180704Srwatson      if ((t + OPIE_LOCK_TIMEOUT) < time(0))
182155192Srwatson        break;
183155192Srwatson
184155192Srwatson      if (kill(pid, 0))
185155192Srwatson        break;
186195925Srwatson
187195925Srwatson      close(fh);
188195925Srwatson      fh = 0;
189195925Srwatson      sleep(1);
190195925Srwatson      if (waits++ > 3) {
191195925Srwatson        rval = 1;
192195925Srwatson        goto lockret;
193195925Srwatson      };
194195925Srwatson    };
195195925Srwatson  };
196195925Srwatson
197195925Srwatson  if (lstat(__opie_lockfilename, &statbuf[0]) < 0)
198195925Srwatson    goto lockret;
199195925Srwatson  if (fstat(fh, &statbuf[1]) < 0)
200180708Srwatson    goto lockret;
201155192Srwatson  if (!S_ISREG(statbuf[0].st_mode) || (statbuf[0].st_mode != statbuf[1].st_mode) || (statbuf[0].st_ino != statbuf[1].st_ino))
202155192Srwatson    goto lockret;
203155192Srwatson
204155192Srwatson  sprintf(buffer, "%d\n%d\n", getpid(), time(0));
205155192Srwatson  i = strlen(buffer) + 1;
206155192Srwatson  if (lseek(fh, 0, SEEK_SET)) {
207180708Srwatson    close(fh);
208155192Srwatson    unlink(__opie_lockfilename);
209155192Srwatson    fh = 0;
210155192Srwatson    goto lockret;
211155192Srwatson  };
212155192Srwatson  if (write(fh, buffer, i) != i) {
213155192Srwatson    close(fh);
214180708Srwatson    unlink(__opie_lockfilename);
215195925Srwatson    fh = 0;
216195925Srwatson    goto lockret;
217195925Srwatson  };
218195925Srwatson  close(fh);
219180709Srwatson  if ((fh = open(__opie_lockfilename, O_RDWR, 0600)) < 0) {
220155192Srwatson    unlink(__opie_lockfilename);
221155192Srwatson    goto lockret;
222155192Srwatson  };
223155192Srwatson  if (read(fh, buffer2, i) != i) {
224155192Srwatson    close(fh);
225180708Srwatson    unlink(__opie_lockfilename);
226246911Spjd    fh = 0;
227180709Srwatson    goto lockret;
228155192Srwatson  };
229155192Srwatson  close(fh);
230155192Srwatson  if (memcmp(buffer, buffer2, i)) {
231155192Srwatson    unlink(__opie_lockfilename);
232155192Srwatson    goto lockret;
233180708Srwatson  };
234180709Srwatson
235155192Srwatson  __opie_lockrefcount++;
236155192Srwatson  rval = 0;
237155192Srwatson  if (do_atexit)
238155192Srwatson    atexit(opieunlockaeh);
239155192Srwatson
240180711Srwatsonlockret:
241155192Srwatson  if (fh >= 0)
242155192Srwatson    close(fh);
243155192Srwatson  if (!__opie_lockrefcount) {
244155192Srwatson    free (__opie_lockfilename);
245155192Srwatson    __opie_lockfilename = NULL;
246155192Srwatson  };
247155192Srwatson  return rval;
248155192Srwatson#else /* USER_LOCKING */
249155192Srwatson  __opie_lockrefcount++;
250156889Srwatson  return 0;
251156889Srwatson#endif /* USER_LOCKING */
252155192Srwatson}
253155192Srwatson