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