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