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 }