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