lock.c revision 22347
1/* lock.c: The opielock() library function.
2
3%%% portions-copyright-cmetz
4Portions of this software are Copyright 1996 by Craig Metz, All Rights
5Reserved. The Inner Net License Version 2 applies to these portions of
6the software.
7You should have received a copy of the license with this software. If
8you didn't get a copy, you may request one from <license@inner.net>.
9
10Portions of this software are Copyright 1995 by Randall Atkinson and Dan
11McDonald, All Rights Reserved. All Rights under this copyright are assigned
12to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13License Agreement applies to this software.
14
15        History:
16
17	Modified by cmetz for OPIE 2.3. Do refcounts whether or not we
18            actually lock. Fixed USER_LOCKING=0 case.
19	Modified by cmetz for OPIE 2.22. Added reference count for locks.
20	    Changed lock filename/refcount symbol names to better indicate
21	    that they're not user serviceable.
22	Modified by cmetz for OPIE 2.2. Use FUNCTION declaration et al.
23            Use "principal" instead of "name" to make it clearer.
24            Ifdef around some headers, be more careful about allowed
25            error return values. Check open() return value properly.
26            Avoid NULL.
27        Created at NRL for OPIE 2.2 from opiesubr2.c
28*/
29#include "opie_cfg.h"
30#if HAVE_STRING_H
31#include <string.h>
32#endif /* HAVE_STRING_H */
33#if HAVE_UNISTD_H
34#include <unistd.h>
35#endif /* HAVE_UNISTD_H */
36#include <fcntl.h>
37#if HAVE_STDLIB_H
38#include <stdlib.h>
39#endif /* HAVE_STDLIB_H */
40#include "opie.h"
41
42int __opie_lockrefcount = 0;
43
44#if USER_LOCKING
45char *__opie_lockfilename = (char *)0;
46
47/* atexit() handler for opielock() */
48static VOIDRET opieunlockaeh FUNCTION_NOARGS
49{
50  if (__opie_lockfilename) {
51    __opie_lockrefcount = 0;
52    opieunlock();
53  }
54}
55#endif /* USER_LOCKING */
56
57/*
58   Serialize (we hope) authentication of user to prevent race conditions.
59   Creates a lock file with a name of OPIE_LOCK_PREFIX with the user name
60   appended. This file contains the pid of the lock's owner and a time()
61   stamp. We use the former to check for dead owners and the latter to
62   provide an upper bound on the lock duration. If there are any problems,
63   we assume the lock is bogus.
64
65   The value of this locking and its security implications are still not
66   completely clear and require further study.
67
68   One could conceivably hack this facility to provide locking of user
69   accounts after several authentication failures.
70
71   Return -1 on low-level error, 0 if ok, 1 on locking failure.
72*/
73int opielock FUNCTION((principal), char *principal)
74{
75#if USER_LOCKING
76  int fh, waits = 0, rval = -1, pid, t, i;
77  char buffer[128], buffer2[128], *c, *c2;
78
79  if (__opie_lockfilename) {
80    __opie_lockrefcount++;
81    return 0;
82  }
83
84  if (!(__opie_lockfilename = (char *)malloc(sizeof(OPIE_LOCK_PREFIX) + strlen(principal))))
85    return -1;
86
87  strcpy(__opie_lockfilename, OPIE_LOCK_PREFIX);
88  strcat(__opie_lockfilename, principal);
89
90  fh = 0;
91  while (!fh)
92    if ((fh = open(__opie_lockfilename, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0) {
93      if ((fh = open(__opie_lockfilename, O_RDWR, 0600)) < 0)
94        goto lockret;
95      if ((i = read(fh, buffer, sizeof(buffer))) <= 0)
96        goto lockret;
97
98      buffer[sizeof(buffer) - 1] = 0;
99      buffer[i - 1] = 0;
100
101      if (!(c = strchr(buffer, '\n')))
102        break;
103
104      *(c++) = 0;
105
106      if (!(c2 = strchr(c, '\n')))
107        break;
108
109      *(c2++) = 0;
110
111      if (!(pid = atoi(buffer)))
112        break;
113
114      if (!(t = atoi(c)))
115        break;
116
117      if ((time(0) + OPIE_LOCK_TIMEOUT) < t)
118        break;
119
120      if (kill(pid, 0))
121        break;
122
123      close(fh);
124      fh = 0;
125      sleep(1);
126      if (waits++ > 3) {
127        rval = 1;
128        goto lockret;
129      };
130    };
131
132  sprintf(buffer, "%d\n%d\n", getpid(), time(0));
133  i = strlen(buffer) + 1;
134  if (lseek(fh, 0, SEEK_SET)) {
135    close(fh);
136    unlink(__opie_lockfilename);
137    fh = 0;
138    goto lockret;
139  };
140  if (write(fh, buffer, i) != i) {
141    close(fh);
142    unlink(__opie_lockfilename);
143    fh = 0;
144    goto lockret;
145  };
146  close(fh);
147  if ((fh = open(__opie_lockfilename, O_RDWR, 0600)) < 0) {
148    unlink(__opie_lockfilename);
149    goto lockret;
150  };
151  if (read(fh, buffer2, i) != i) {
152    close(fh);
153    unlink(__opie_lockfilename);
154    fh = 0;
155    goto lockret;
156  };
157  close(fh);
158  if (memcmp(buffer, buffer2, i)) {
159    unlink(__opie_lockfilename);
160    goto lockret;
161  };
162
163  __opie_lockrefcount++;
164  rval = 0;
165  atexit(opieunlockaeh);
166
167lockret:
168  if (fh)
169    close(fh);
170  return rval;
171#else /* USER_LOCKING */
172  __opie_lockrefcount++;
173  return 0;
174#endif /* USER_LOCKING */
175}
176