0
|
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 } |