Mercurial > notdcc
comparison dcclib/lock_open.c @ 0:c7f6b056b673
First import of vendor version
author | Peter Gervai <grin@grin.hu> |
---|---|
date | Tue, 10 Mar 2009 13:49:58 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c7f6b056b673 |
---|---|
1 /* Distributed Checksum Clearinghouse | |
2 * | |
3 * open and lock database and whitelist files | |
4 * | |
5 * Copyright (c) 2008 by Rhyolite Software, LLC | |
6 * | |
7 * This agreement is not applicable to any entity which sells anti-spam | |
8 * solutions to others or provides an anti-spam solution as part of a | |
9 * security solution sold to other entities, or to a private network | |
10 * which employs the DCC or uses data provided by operation of the DCC | |
11 * but does not provide corresponding data to other users. | |
12 * | |
13 * Permission to use, copy, modify, and distribute this software without | |
14 * changes for any purpose with or without fee is hereby granted, provided | |
15 * that the above copyright notice and this permission notice appear in all | |
16 * copies and any distributed versions or copies are either unchanged | |
17 * or not called anything similar to "DCC" or "Distributed Checksum | |
18 * Clearinghouse". | |
19 * | |
20 * Parties not eligible to receive a license under this agreement can | |
21 * obtain a commercial license to use DCC by contacting Rhyolite Software | |
22 * at sales@rhyolite.com. | |
23 * | |
24 * A commercial license would be for Distributed Checksum and Reputation | |
25 * Clearinghouse software. That software includes additional features. This | |
26 * free license for Distributed ChecksumClearinghouse Software does not in any | |
27 * way grant permision to use Distributed Checksum and Reputation Clearinghouse | |
28 * software | |
29 * | |
30 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL | |
31 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES | |
32 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC | |
33 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES | |
34 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | |
35 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, | |
36 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS | |
37 * SOFTWARE. | |
38 * | |
39 * Rhyolite Software DCC 1.3.103-1.60 $Revision$ | |
40 */ | |
41 | |
42 #include "dcc_defs.h" | |
43 #include <signal.h> | |
44 | |
45 | |
46 #ifndef DCC_WIN32 | |
47 uid_t dcc_real_uid, dcc_effective_uid; | |
48 gid_t dcc_real_gid, dcc_effective_gid; | |
49 #endif | |
50 | |
51 /* We must be SUID to read and write the system's common | |
52 * connection parameter memory mapped file. | |
53 * If the real UID is 0, then forget any SUID stuff and run as root. | |
54 * Otherwise remember the powerful UID and the real UID, and | |
55 * release the privilege of using the powerful UID */ | |
56 void | |
57 dcc_init_priv(void) | |
58 { | |
59 #ifndef DCC_WIN32 | |
60 dcc_real_uid = getuid(); | |
61 if (dcc_real_uid == 0) { | |
62 dcc_effective_uid = 0; | |
63 } else { | |
64 dcc_effective_uid = geteuid(); | |
65 } | |
66 | |
67 if (0 > seteuid(dcc_real_uid)) | |
68 dcc_error_msg("seteuid(%d): %s", | |
69 (int)dcc_real_uid, ERROR_STR()); | |
70 | |
71 dcc_real_gid = getgid(); | |
72 if (dcc_real_gid == 0) { | |
73 dcc_effective_gid = 0; | |
74 } else { | |
75 dcc_effective_gid = getegid(); | |
76 } | |
77 | |
78 if (0 > setegid(dcc_real_gid)) | |
79 dcc_error_msg("setegid(%d): %s", | |
80 (int)dcc_real_gid, ERROR_STR()); | |
81 #endif | |
82 } | |
83 | |
84 | |
85 | |
86 #ifndef DCC_WIN32 | |
87 void | |
88 dcc_get_priv(void) | |
89 { | |
90 if (dcc_real_uid != dcc_effective_uid | |
91 && 0 > seteuid(dcc_effective_uid)) | |
92 dcc_error_msg("seteuid(%d): %s", | |
93 (int)dcc_effective_uid, ERROR_STR()); | |
94 if (dcc_real_gid != dcc_effective_gid | |
95 && 0 > setegid(dcc_effective_gid)) | |
96 dcc_error_msg("setegid(%d): %s", | |
97 (int)dcc_effective_gid, ERROR_STR()); | |
98 } | |
99 | |
100 | |
101 | |
102 /* get set-UID privilege if the file is in the DCC home directory */ | |
103 u_char /* 0=bad idea, 1=have privilege */ | |
104 dcc_get_priv_home(const char *nm) | |
105 { | |
106 DCC_PATH abs_nm; | |
107 | |
108 if (dcc_real_uid == dcc_effective_uid | |
109 && dcc_real_gid == dcc_effective_gid) | |
110 return 0; | |
111 | |
112 if (!fnm2abs(abs_nm, nm, 0) | |
113 || strncmp(abs_nm, DCC_HOMEDIR, LITZ(DCC_HOMEDIR))) | |
114 return 0; | |
115 | |
116 dcc_get_priv(); | |
117 return 1; | |
118 } | |
119 #endif | |
120 | |
121 | |
122 | |
123 void | |
124 dcc_rel_priv(void) | |
125 { | |
126 #ifndef DCC_WIN32 | |
127 int serrno; | |
128 | |
129 if (dcc_real_uid != dcc_effective_uid | |
130 || dcc_real_gid != dcc_effective_gid) { | |
131 serrno = errno; | |
132 if (0 > seteuid(dcc_real_uid)) | |
133 dcc_error_msg("seteuid(%d): %s", | |
134 (int)dcc_real_uid, ERROR_STR()); | |
135 if (0 > setegid(dcc_real_gid)) | |
136 dcc_error_msg("setegid(%d): %s", | |
137 (int)dcc_real_gid, ERROR_STR()); | |
138 errno = serrno; | |
139 } | |
140 #endif | |
141 } | |
142 | |
143 | |
144 | |
145 /* see if a file is private */ | |
146 u_char | |
147 dcc_ck_private(DCC_EMSG emsg, struct stat *sb, const char *nm, int fd) | |
148 { | |
149 struct stat sb0; | |
150 DCC_PATH path; | |
151 int i; | |
152 | |
153 if (!sb) | |
154 sb = &sb0; | |
155 | |
156 if (fd == -1) { | |
157 i = stat(nm, sb); | |
158 } else { | |
159 i = fstat(fd, sb); | |
160 } | |
161 if (i < 0) { | |
162 dcc_pemsg(EX_IOERR, emsg, "stat(%s): %s", | |
163 fnm2abs_err(path, nm), ERROR_STR()); | |
164 return 0; | |
165 } | |
166 #ifdef HAVE_PRIVATE_FILES | |
167 /* even on systems like Windows without private files, | |
168 * some callers want the results of the stat() */ | |
169 if ((sb->st_mode & (S_IRGRP|S_IWGRP|S_IWOTH|S_IXOTH)) != 0) { | |
170 dcc_pemsg(EX_NOPERM, emsg, | |
171 "%s is not private", fnm2abs_err(path, nm)); | |
172 return 0; | |
173 } | |
174 #endif | |
175 return 1; | |
176 } | |
177 | |
178 | |
179 | |
180 #ifndef DCC_WIN32 | |
181 static void | |
182 set_fl(struct flock *fl, int type, int byte_num) | |
183 { | |
184 memset(fl, 0, sizeof(*fl)); | |
185 fl->l_type = type; | |
186 fl->l_whence = SEEK_SET; | |
187 if (byte_num != DCC_LOCK_ALL_FILE) { | |
188 fl->l_start = byte_num; | |
189 fl->l_len = 1; | |
190 } | |
191 } | |
192 #endif /* DCC_WIN32 */ | |
193 | |
194 | |
195 | |
196 /* open a file with a lock, | |
197 * The lock can optionally be a pretense. */ | |
198 int /* -1=failed & emsg set, >=0 if done */ | |
199 dcc_lock_open(DCC_EMSG emsg, | |
200 const char *nm, | |
201 int open_flags, /* eg. O_CREAT */ | |
202 u_char lock_mode, /* DCC_LOCK_OPEN_* */ | |
203 int lock_num, /* which byte of the file to lock */ | |
204 u_char *busyp) /* 1=file is busy */ | |
205 { | |
206 #ifdef DCC_WIN32 | |
207 /* Win95 and Win98 do not include UNIX style file locking, | |
208 * including non-blocking locks and upgrading locks from shared to exclusive. | |
209 * In principle they could be constructed from the WIN32 synchronization | |
210 * primitives and a few bytes of shared memory. Win98 does not even | |
211 * have a blocking file lock function. | |
212 */ | |
213 HANDLE h; | |
214 int fd; | |
215 DCC_PATH path; | |
216 | |
217 h = CreateFile(nm, | |
218 (open_flags == O_RDONLY | |
219 ? GENERIC_READ | |
220 : (GENERIC_READ | GENERIC_WRITE)), | |
221 FILE_SHARE_READ | FILE_SHARE_WRITE, 0, | |
222 ((open_flags & O_EXCL) | |
223 ? CREATE_NEW | |
224 : (open_flags & O_CREAT) | |
225 ? CREATE_ALWAYS | |
226 : OPEN_EXISTING), | |
227 FILE_ATTRIBUTE_NORMAL, 0); | |
228 if (h == INVALID_HANDLE_VALUE) { | |
229 dcc_pemsg(EX_IOERR, emsg, "CreateFile(%s): %s", | |
230 fnm2abs_err(path, nm), ERROR_STR()); | |
231 if (busyp) | |
232 *busyp = 0; | |
233 return -1; | |
234 } | |
235 fd = _open_osfhandle((long)h, 0); | |
236 if (fd < 0) { | |
237 dcc_pemsg(EX_IOERR, emsg, "_open_osfhandle(%s): %s", | |
238 nm, ERROR_STR()); | |
239 CloseHandle(h); | |
240 if (busyp) | |
241 *busyp = 0; | |
242 return -1; | |
243 } | |
244 if (!(lock_mode & DCC_LOCK_OPEN_NOLOCK)) { | |
245 if (!win32_lock(h, | |
246 ((lock_mode & DCC_LOCK_OPEN_SHARE) | |
247 ? 0 : LOCKFILE_EXCLUSIVE_LOCK) | |
248 | (lock_mode & DCC_LOCK_OPEN_NOWAIT | |
249 ? 0 : LOCKFILE_FAIL_IMMEDIATELY))) { | |
250 dcc_pemsg(EX_IOERR, emsg, "open LockFileEx(%s): %s", | |
251 fnm2abs_err(path, nm), ERROR_STR()); | |
252 if (busyp) | |
253 *busyp = (GetLastError() == ERROR_LOCK_VIOLATION | |
254 || GetLastError()==ERROR_LOCK_FAILED); | |
255 close(fd); | |
256 return -1; | |
257 } | |
258 } | |
259 | |
260 #else /* !DCC_WIN32 */ | |
261 static u_char checked_stdio = 0; | |
262 int fd; | |
263 struct flock fl; | |
264 DCC_PATH path; | |
265 | |
266 /* ensure 0, 1, and 2 are open so none of our real files get | |
267 * those file descriptors and are used as stdin, stdout, or stderr */ | |
268 if (!checked_stdio) { | |
269 clean_stdio(); | |
270 checked_stdio = 1; | |
271 } | |
272 | |
273 fd = open(nm, open_flags, 0666); | |
274 if (fd < 0 && dcc_get_priv_home(nm)) { | |
275 fd = open(nm, open_flags, 0666); | |
276 dcc_rel_priv(); | |
277 } | |
278 if (fd < 0) { | |
279 if (errno == EACCES) | |
280 dcc_pemsg(EX_NOINPUT, emsg, | |
281 "lock_open(%s): %s;" | |
282 " file not writeable for locking", | |
283 fnm2abs_err(path, nm), ERROR_STR()); | |
284 else | |
285 dcc_pemsg(EX_NOINPUT, emsg, | |
286 "lock_open(%s): %s", | |
287 fnm2abs_err(path, nm), ERROR_STR()); | |
288 if (busyp) | |
289 *busyp = DCC_BLOCK_ERROR(); | |
290 return -1; | |
291 } | |
292 | |
293 if (0 > fcntl(fd, F_SETFD, FD_CLOEXEC)) { | |
294 if (busyp) | |
295 *busyp = 0; | |
296 dcc_pemsg(EX_IOERR, emsg, | |
297 "fcntl(F_SETFD FD_CLOEXEC %s %d): %s", | |
298 fnm2abs_err(path, nm), lock_num, ERROR_STR()); | |
299 close(fd); | |
300 return -1; | |
301 } | |
302 | |
303 /* We may already have a lock on the file under a different file | |
304 * descriptor. If not, try to get a lock */ | |
305 if (lock_mode != DCC_LOCK_OPEN_NOLOCK) { | |
306 set_fl(&fl, | |
307 (lock_mode & DCC_LOCK_OPEN_SHARE) ? F_RDLCK : F_WRLCK, | |
308 lock_num); | |
309 if (0 > fcntl(fd, (lock_mode & DCC_LOCK_OPEN_NOWAIT | |
310 ? F_SETLK : F_SETLKW), | |
311 &fl)) { | |
312 dcc_pemsg(EX_NOINPUT, emsg, | |
313 "open fcntl(%s F_WRLCK %s %d): %s", | |
314 (lock_mode & DCC_LOCK_OPEN_NOWAIT | |
315 ? "F_SETLK" : "F_SETLKW"), | |
316 fnm2abs_err(path, nm), lock_num, | |
317 ERROR_STR()); | |
318 if (busyp) | |
319 *busyp = (DCC_BLOCK_ERROR() | |
320 || errno == EACCES); /* for SunOS */ | |
321 close(fd); | |
322 return -1; | |
323 } | |
324 } | |
325 #endif /* !DCC_WIN32 or UNIX */ | |
326 | |
327 if (busyp) | |
328 *busyp = 0; | |
329 return fd; | |
330 } | |
331 | |
332 | |
333 | |
334 u_char /* 1=done 0=failed */ | |
335 dcc_unlock_fd(DCC_EMSG emsg UATTRIB, int fd, | |
336 int lock_num, /* which byte of the file to unlock */ | |
337 const char *str, const char *nm) | |
338 { | |
339 #ifdef DCC_WIN32 | |
340 DCC_PATH path; | |
341 | |
342 if (!win32_unlock((HANDLE)_get_osfhandle(fd))) { | |
343 dcc_pemsg(EX_NOINPUT, emsg, | |
344 "UnlockFileEx(%s%s): %s", | |
345 str, fnm2abs_err(path, nm), ERROR_STR()); | |
346 return 0; | |
347 } | |
348 return 1; | |
349 #else | |
350 struct flock fl; | |
351 DCC_PATH path; | |
352 | |
353 set_fl(&fl, F_UNLCK, lock_num); | |
354 if (0 > fcntl(fd, F_SETLK, &fl)) { | |
355 #ifdef DCC_DEBUG_CLNT_LOCK | |
356 dcc_logbad(EX_SOFTWARE, | |
357 "fcntl(F_SETLK F_UNLCK %s%s %d): %s", | |
358 str, fnm2abs_err(path, nm), lock_num, ERROR_STR()); | |
359 #else | |
360 dcc_pemsg(EX_NOINPUT, emsg, | |
361 "fcntl(F_SETLK F_UNLCK %s%s %d): %s", | |
362 str, fnm2abs_err(path, nm), lock_num, ERROR_STR()); | |
363 #endif | |
364 return 0; | |
365 } | |
366 return 1; | |
367 #endif /* DCC_WIN32 */ | |
368 } | |
369 | |
370 | |
371 | |
372 #ifndef DCC_WIN32 | |
373 /* cause EINTR if we cannot get the lock */ | |
374 static void | |
375 deadman(int s UATTRIB) | |
376 { | |
377 #if 0 | |
378 dcc_logbad(EX_SOFTWARE, "exclusive lock deadman timer fired"); | |
379 #endif | |
380 } | |
381 #endif | |
382 | |
383 | |
384 | |
385 u_char /* 1=done 0=failed */ | |
386 dcc_exlock_fd(DCC_EMSG emsg, int fd, | |
387 int lock_num, /* which byte of the file to lock */ | |
388 int max_delay, | |
389 const char *str, const char *nm) | |
390 { | |
391 #ifdef DCC_WIN32 | |
392 DCC_PATH path; | |
393 | |
394 if (!win32_lock((HANDLE)_get_osfhandle(fd), LOCKFILE_EXCLUSIVE_LOCK)) { | |
395 dcc_pemsg(EX_IOERR, emsg, "LockFileEx(%s %s): %s", | |
396 str, fnm2abs_err(path, nm), ERROR_STR()); | |
397 return 0; | |
398 } | |
399 return 1; | |
400 | |
401 #else /* !DCC_WIN32 */ | |
402 struct flock fl; | |
403 DCC_PATH path; | |
404 time_t start; | |
405 u_char result; | |
406 | |
407 start = time(0); | |
408 if (max_delay) { | |
409 #ifdef HAVE_SIGINTERRUPT | |
410 siginterrupt(SIGALRM, 1); | |
411 #endif | |
412 signal(SIGALRM, deadman); | |
413 alarm(max_delay+1); | |
414 } | |
415 | |
416 for (;;) { | |
417 | |
418 set_fl(&fl, F_WRLCK, lock_num); | |
419 if (0 <= fcntl(fd, F_SETLKW, &fl)) { | |
420 result = 1; | |
421 break; | |
422 } | |
423 | |
424 /* EINTR means something is hogging the lock */ | |
425 if (DCC_SELECT_NERROR()) { | |
426 time_t delta = time(0) - start; | |
427 if (delta < max_delay) { | |
428 if (delta < 0) { /* handle time jump */ | |
429 start = time(0); | |
430 delta = 0; | |
431 } | |
432 alarm(max_delay-delta+1); | |
433 continue; | |
434 } | |
435 dcc_pemsg(EX_NOINPUT, emsg, | |
436 "fcntl(F_SETLKW F_WRLCK %s%s %d)" | |
437 " failed after %d seconds", | |
438 str, fnm2abs_err(path, nm), lock_num, | |
439 (int)delta); | |
440 result = 0; | |
441 break; | |
442 } | |
443 #ifdef DCC_DEBUG_CLNT_LOCK | |
444 if (errno == EDEADLK) { | |
445 struct flock fl2; | |
446 pid_t our_pid; | |
447 | |
448 our_pid = getpid(); | |
449 fl2 = fl; | |
450 if (0 > fcntl(fd, F_GETLK, &fl2)) | |
451 dcc_pemsg(EX_NOINPUT, emsg, | |
452 "fcntl(F_GETLK): %s", ERROR_STR()); | |
453 dcc_logbad(EX_SOFTWARE, | |
454 "%d %d fcntl(F_SETLKW F_WRLCK %s%s %d): %s", | |
455 (int)our_pid, (int)fl2.l_pid, | |
456 str, fnm2abs_err(path, nm), lock_num, | |
457 strerror(EDEADLK)); | |
458 } | |
459 if (errno == EBADF) { | |
460 dcc_logbad(EX_SOFTWARE, | |
461 "fcntl(F_SETLKW F_WRLCK %s%s %d): %s", | |
462 str, fnm2abs_err(path, nm), lock_num, | |
463 ERROR_STR()); | |
464 } | |
465 #endif | |
466 dcc_pemsg(EX_NOINPUT, emsg, | |
467 "fcntl(F_SETLKW F_WRLCK %s%s %d): %s", | |
468 str, fnm2abs_err(path, nm), lock_num, | |
469 ERROR_STR()); | |
470 result = 0; | |
471 break; | |
472 } | |
473 | |
474 if (max_delay) { | |
475 signal(SIGALRM, SIG_DFL); | |
476 alarm(0); | |
477 } | |
478 return result; | |
479 #endif /* DCC_WIN32 */ | |
480 } | |
481 | |
482 | |
483 | |
484 /* Several systems do not update the mtimes of files modified with mmap(). | |
485 * Some like BSD/OS delay changing the mtime until the file accessed with | |
486 * read(). Others including filesystems on some versions of Linux | |
487 * apparently never change the mtime. */ | |
488 u_char | |
489 dcc_set_mtime(DCC_EMSG emsg, const char *nm, int fd UATTRIB, | |
490 const struct timeval *mtime) | |
491 { | |
492 #undef DIDIT | |
493 #if defined(HAVE_FUTIMES) && !defined(linux) && !defined(DIDIT) | |
494 /* some Linux systems have a broken futimes implementation that does | |
495 * not work for programs started as root but that release privileges */ | |
496 struct timeval tbuf[2], *tbufp; | |
497 DCC_PATH path; | |
498 int result; | |
499 | |
500 if (mtime) { | |
501 tbuf[0] = *mtime; | |
502 tbuf[1] = *mtime; | |
503 tbufp = tbuf; | |
504 } else { | |
505 tbufp = 0; | |
506 } | |
507 result = futimes(fd, tbufp); | |
508 if (result < 0 | |
509 && (errno == EACCES || errno == EPERM) | |
510 && dcc_real_uid != dcc_effective_uid) { | |
511 dcc_get_priv(); | |
512 result = futimes(fd, tbufp); | |
513 dcc_rel_priv(); | |
514 } | |
515 if (result < 0) | |
516 dcc_pemsg(EX_IOERR, emsg,"futimes(%s): %s", | |
517 fnm2abs_err(path, nm), ERROR_STR()); | |
518 #define DIDIT | |
519 #endif /* HAVE_FUTIMES */ | |
520 #if defined(HAVE_UTIME_H) && !defined(DIDIT) | |
521 struct utimbuf tbuf, *tbufp; | |
522 DCC_PATH path; | |
523 int result; | |
524 | |
525 if (mtime) { | |
526 tbuf.actime = tbuf.modtime = mtime->tv_sec; | |
527 tbufp = &tbuf; | |
528 } else { | |
529 tbufp = 0; | |
530 } | |
531 result = utime(nm, tbufp); | |
532 if (result < 0 | |
533 && dcc_real_uid != dcc_effective_uid) { | |
534 dcc_get_priv(); | |
535 result = utime(nm, tbufp); | |
536 dcc_rel_priv(); | |
537 } | |
538 if (result < 0) | |
539 dcc_pemsg(EX_IOERR, emsg,"utime(%s): %s", | |
540 fnm2abs_err(path, nm), ERROR_STR()); | |
541 #define DIDIT | |
542 #endif /* HAVE_UTIME_H */ | |
543 | |
544 #ifdef DIDIT | |
545 return result >= 0; | |
546 #undef DIDIT | |
547 #else | |
548 return 1; /* WIN32 */ | |
549 #endif | |
550 } |