lock.c revision 59119
1/* lock.c: The opielock() library function.
2
3%%% portions-copyright-cmetz-96
4Portions of this software are Copyright 1996-1998 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.31. Put locks in a separate dir.
18            Bug fixes.
19	Modified by cmetz for OPIE 2.3. Do refcounts whether or not we
20            actually lock. Fixed USER_LOCKING=0 case.
21	Modified by cmetz for OPIE 2.22. Added reference count for locks.
22	    Changed lock filename/refcount symbol names to better indicate
23	    that they're not user serviceable.
24	Modified by cmetz for OPIE 2.2. Use FUNCTION declaration et al.
25            Use "principal" instead of "name" to make it clearer.
26            Ifdef around some headers, be more careful about allowed
27            error return values. Check open() return value properly.
28            Avoid NULL.
29        Created at NRL for OPIE 2.2 from opiesubr2.c
30*/
31#include "opie_cfg.h"
32#if HAVE_STRING_H
33#include <string.h>
34#endif /* HAVE_STRING_H */
35#if HAVE_UNISTD_H
36#include <unistd.h>
37#endif /* HAVE_UNISTD_H */
38#include <sys/stat.h>
39#include <syslog.h>
40#include <fcntl.h>
41#if HAVE_STDLIB_H
42#include <stdlib.h>
43#endif /* HAVE_STDLIB_H */
44#include <errno.h>
45#include "opie.h"
46
47#if !HAVE_LSTAT
48#define lstat(x, y) stat(x, y)
49#endif /* !HAVE_LSTAT */
50
51int __opie_lockrefcount = 0;
52
53#if USER_LOCKING
54char *__opie_lockfilename = (char *)0;
55
56/* atexit() handler for opielock() */
57static VOIDRET opieunlockaeh FUNCTION_NOARGS
58{
59  if (__opie_lockfilename) {
60    __opie_lockrefcount = 0;
61    opieunlock();
62  }
63}
64#endif /* USER_LOCKING */
65
66/*
67   Serialize (we hope) authentication of user to prevent race conditions.
68   Creates a lock file with a name of OPIE_LOCK_PREFIX with the user name
69   appended. This file contains the pid of the lock's owner and a time()
70   stamp. We use the former to check for dead owners and the latter to
71   provide an upper bound on the lock duration. If there are any problems,
72   we assume the lock is bogus.
73
74   The value of this locking and its security implications are still not
75   completely clear and require further study.
76
77   One could conceivably hack this facility to provide locking of user
78   accounts after several authentication failures.
79
80   Return -1 on low-level error, 0 if ok, 1 on locking failure.
81*/
82int opielock FUNCTION((principal), char *principal)
83{
84#if USER_LOCKING
85  int fh, waits = 0, rval = -1, pid, t, i;
86  char buffer[128], buffer2[128], *c, *c2;
87  struct stat statbuf[2];
88
89  if (getuid() && geteuid()) {
90#if DEBUG
91    syslog(LOG_DEBUG, "opielock: requires superuser priveleges");
92#endif /* DEBUG */
93    return -1;
94  };
95
96  if (__opie_lockfilename) {
97    __opie_lockrefcount++;
98    return 0;
99  }
100
101  if (!(__opie_lockfilename = (char *)malloc(sizeof(OPIE_LOCK_DIR) + 1 + strlen(principal))))
102    return -1;
103
104  strcpy(__opie_lockfilename, OPIE_LOCK_DIR);
105
106  if (mkdir(__opie_lockfilename, 0700) < 0)
107    if (errno != EEXIST)
108      return -1;
109
110  if (lstat(__opie_lockfilename, &statbuf[0]) < 0)
111    return -1;
112
113  if (statbuf[0].st_uid) {
114#if DEBUG
115    syslog(LOG_DEBUG, "opielock: %s isn't owned by the superuser.", __opie_lockfilename);
116#endif /* DEBUG */
117    return -1;
118  };
119
120  if (!S_ISDIR(statbuf[0].st_mode)) {
121#if DEBUG
122    syslog(LOG_DEBUG, "opielock: %s isn't a directory.", __opie_lockfilename);
123#endif /* DEBUG */
124    return -1;
125  };
126
127  if ((statbuf[0].st_mode & 0777) != 00700) {
128#if DEBUG
129    syslog(LOG_DEBUG, "opielock: permissions on %s are not correct.", __opie_lockfilename);
130#endif /* DEBUG */
131    return -1;
132  };
133
134  strcat(__opie_lockfilename, "/");
135  strcat(__opie_lockfilename, principal);
136
137  fh = -1;
138  while (fh < 0) {
139    if (!lstat(__opie_lockfilename, &statbuf[0]))
140      if (!S_ISREG(statbuf[0].st_mode))
141        goto lockret;
142
143    if ((fh = open(__opie_lockfilename, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0) {
144      if (lstat(__opie_lockfilename, &statbuf[1]) < 0)
145        goto lockret;
146      if (statbuf[0].st_ino != statbuf[1].st_ino)
147        goto lockret;
148      if (statbuf[0].st_mode != statbuf[1].st_mode)
149        goto lockret;
150      if ((fh = open(__opie_lockfilename, O_RDONLY, 0600)) < 0)
151        goto lockret;
152      if ((i = read(fh, buffer, sizeof(buffer))) <= 0)
153        goto lockret;
154
155      buffer[sizeof(buffer) - 1] = 0;
156      buffer[i - 1] = 0;
157
158      if (!(c = strchr(buffer, '\n')))
159        break;
160
161      *(c++) = 0;
162
163      if (!(c2 = strchr(c, '\n')))
164        break;
165
166      *(c2++) = 0;
167
168      if (!(pid = atoi(buffer)))
169        break;
170
171      if (!(t = atoi(c)))
172        break;
173
174      if ((t + OPIE_LOCK_TIMEOUT) < time(0))
175        break;
176
177      if (kill(pid, 0))
178        break;
179
180      close(fh);
181      fh = 0;
182      sleep(1);
183      if (waits++ > 3) {
184        rval = 1;
185        goto lockret;
186      };
187    };
188  };
189
190  if (lstat(__opie_lockfilename, &statbuf[0]) < 0)
191    goto lockret;
192  if (fstat(fh, &statbuf[1]) < 0)
193    goto lockret;
194  if (!S_ISREG(statbuf[0].st_mode) || (statbuf[0].st_mode != statbuf[1].st_mode) || (statbuf[0].st_ino != statbuf[1].st_ino))
195    goto lockret;
196
197  sprintf(buffer, "%d\n%d\n", getpid(), time(0));
198  i = strlen(buffer) + 1;
199  if (lseek(fh, 0, SEEK_SET)) {
200    close(fh);
201    unlink(__opie_lockfilename);
202    fh = 0;
203    goto lockret;
204  };
205  if (write(fh, buffer, i) != i) {
206    close(fh);
207    unlink(__opie_lockfilename);
208    fh = 0;
209    goto lockret;
210  };
211  close(fh);
212  if ((fh = open(__opie_lockfilename, O_RDWR, 0600)) < 0) {
213    unlink(__opie_lockfilename);
214    goto lockret;
215  };
216  if (read(fh, buffer2, i) != i) {
217    close(fh);
218    unlink(__opie_lockfilename);
219    fh = 0;
220    goto lockret;
221  };
222  close(fh);
223  if (memcmp(buffer, buffer2, i)) {
224    unlink(__opie_lockfilename);
225    goto lockret;
226  };
227
228  __opie_lockrefcount++;
229  rval = 0;
230  atexit(opieunlockaeh);
231
232lockret:
233  if (fh >= 0)
234    close(fh);
235  if (!__opie_lockrefcount) {
236    free (__opie_lockfilename);
237    __opie_lockfilename = NULL;
238  };
239  return rval;
240#else /* USER_LOCKING */
241  __opie_lockrefcount++;
242  return 0;
243#endif /* USER_LOCKING */
244}
245