diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/lock_open.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,550 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * open and lock database and whitelist files
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Rhyolite Software DCC 1.3.103-1.60 $Revision$
+ */
+
+#include "dcc_defs.h"
+#include <signal.h>
+
+
+#ifndef DCC_WIN32
+uid_t dcc_real_uid, dcc_effective_uid;
+gid_t dcc_real_gid, dcc_effective_gid;
+#endif
+
+/* We must be SUID to read and write the system's common
+ *	connection parameter memory mapped file.
+ *	If the real UID is 0, then forget any SUID stuff and run as root.
+ *	Otherwise remember the powerful UID and the real UID, and
+ *	release the privilege of using the powerful UID */
+void
+dcc_init_priv(void)
+{
+#ifndef DCC_WIN32
+	dcc_real_uid = getuid();
+	if (dcc_real_uid == 0) {
+		dcc_effective_uid = 0;
+	} else {
+		dcc_effective_uid = geteuid();
+	}
+
+	if (0 > seteuid(dcc_real_uid))
+		dcc_error_msg("seteuid(%d): %s",
+			      (int)dcc_real_uid, ERROR_STR());
+
+	dcc_real_gid = getgid();
+	if (dcc_real_gid == 0) {
+		dcc_effective_gid = 0;
+	} else {
+		dcc_effective_gid = getegid();
+	}
+
+	if (0 > setegid(dcc_real_gid))
+		dcc_error_msg("setegid(%d): %s",
+			      (int)dcc_real_gid, ERROR_STR());
+#endif
+}
+
+
+
+#ifndef DCC_WIN32
+void
+dcc_get_priv(void)
+{
+	if (dcc_real_uid != dcc_effective_uid
+	    && 0 > seteuid(dcc_effective_uid))
+		dcc_error_msg("seteuid(%d): %s",
+			      (int)dcc_effective_uid, ERROR_STR());
+	if (dcc_real_gid != dcc_effective_gid
+	    && 0 > setegid(dcc_effective_gid))
+		dcc_error_msg("setegid(%d): %s",
+			      (int)dcc_effective_gid, ERROR_STR());
+}
+
+
+
+/* get set-UID privilege if the file is in the DCC home directory */
+u_char					/* 0=bad idea, 1=have privilege */
+dcc_get_priv_home(const char *nm)
+{
+	DCC_PATH abs_nm;
+
+	if (dcc_real_uid == dcc_effective_uid
+	    && dcc_real_gid == dcc_effective_gid)
+		return 0;
+
+	if (!fnm2abs(abs_nm, nm, 0)
+	    || strncmp(abs_nm, DCC_HOMEDIR, LITZ(DCC_HOMEDIR)))
+		return 0;
+
+	dcc_get_priv();
+	return 1;
+}
+#endif
+
+
+
+void
+dcc_rel_priv(void)
+{
+#ifndef DCC_WIN32
+	int serrno;
+
+	if (dcc_real_uid != dcc_effective_uid
+	    || dcc_real_gid != dcc_effective_gid) {
+		serrno = errno;
+		if (0 > seteuid(dcc_real_uid))
+			dcc_error_msg("seteuid(%d): %s",
+				      (int)dcc_real_uid, ERROR_STR());
+		if (0 > setegid(dcc_real_gid))
+			dcc_error_msg("setegid(%d): %s",
+				      (int)dcc_real_gid, ERROR_STR());
+		errno = serrno;
+	}
+#endif
+}
+
+
+
+/* see if a file is private */
+u_char
+dcc_ck_private(DCC_EMSG emsg, struct stat *sb, const char *nm, int fd)
+{
+	struct stat sb0;
+	DCC_PATH path;
+	int i;
+
+	if (!sb)
+		sb = &sb0;
+
+	if (fd == -1) {
+		i = stat(nm, sb);
+	} else {
+		i = fstat(fd, sb);
+	}
+	if (i < 0) {
+		dcc_pemsg(EX_IOERR, emsg, "stat(%s): %s",
+			  fnm2abs_err(path, nm), ERROR_STR());
+		return 0;
+	}
+#ifdef HAVE_PRIVATE_FILES
+	/* even on systems like Windows without private files,
+	 * some callers want the results of the stat() */
+	if ((sb->st_mode & (S_IRGRP|S_IWGRP|S_IWOTH|S_IXOTH)) != 0) {
+		dcc_pemsg(EX_NOPERM, emsg,
+			  "%s is not private", fnm2abs_err(path, nm));
+		return 0;
+	}
+#endif
+	return 1;
+}
+
+
+
+#ifndef DCC_WIN32
+static void
+set_fl(struct flock *fl, int type, int byte_num)
+{
+	memset(fl, 0, sizeof(*fl));
+	fl->l_type = type;
+	fl->l_whence = SEEK_SET;
+	if (byte_num != DCC_LOCK_ALL_FILE) {
+		fl->l_start = byte_num;
+		fl->l_len = 1;
+	}
+}
+#endif /* DCC_WIN32 */
+
+
+
+/* open a file with a lock,
+ *	The lock can optionally be a pretense. */
+int					/* -1=failed & emsg set, >=0 if done */
+dcc_lock_open(DCC_EMSG emsg,
+	      const char *nm,
+	      int open_flags,		/* eg. O_CREAT */
+	      u_char lock_mode,		/* DCC_LOCK_OPEN_* */
+	      int lock_num,		/* which byte of the file to lock */
+	      u_char *busyp)		/* 1=file is busy */
+{
+#ifdef DCC_WIN32
+/* Win95 and Win98 do not include UNIX style file locking,
+ * including non-blocking locks and upgrading locks from shared to exclusive.
+ * In principle they could be constructed from the WIN32 synchronization
+ * primitives and a few bytes of shared memory.  Win98 does not even
+ * have a blocking file lock function.
+ */
+	HANDLE h;
+	int fd;
+	DCC_PATH path;
+
+	h = CreateFile(nm,
+		       (open_flags == O_RDONLY
+			? GENERIC_READ
+			: (GENERIC_READ | GENERIC_WRITE)),
+		       FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
+		       ((open_flags & O_EXCL)
+			? CREATE_NEW
+			: (open_flags & O_CREAT)
+			? CREATE_ALWAYS
+			: OPEN_EXISTING),
+		       FILE_ATTRIBUTE_NORMAL, 0);
+	if (h == INVALID_HANDLE_VALUE) {
+		dcc_pemsg(EX_IOERR, emsg, "CreateFile(%s): %s",
+			  fnm2abs_err(path, nm), ERROR_STR());
+		if (busyp)
+			*busyp = 0;
+		return -1;
+	}
+	fd = _open_osfhandle((long)h, 0);
+	if (fd < 0) {
+		dcc_pemsg(EX_IOERR, emsg, "_open_osfhandle(%s): %s",
+			  nm, ERROR_STR());
+		CloseHandle(h);
+		if (busyp)
+			*busyp = 0;
+		return -1;
+	}
+	if (!(lock_mode & DCC_LOCK_OPEN_NOLOCK)) {
+		if (!win32_lock(h,
+				((lock_mode & DCC_LOCK_OPEN_SHARE)
+				 ? 0 : LOCKFILE_EXCLUSIVE_LOCK)
+				| (lock_mode & DCC_LOCK_OPEN_NOWAIT
+				   ? 0 : LOCKFILE_FAIL_IMMEDIATELY))) {
+			dcc_pemsg(EX_IOERR, emsg, "open LockFileEx(%s): %s",
+				  fnm2abs_err(path, nm), ERROR_STR());
+			if (busyp)
+				*busyp = (GetLastError() == ERROR_LOCK_VIOLATION
+					  || GetLastError()==ERROR_LOCK_FAILED);
+			close(fd);
+			return -1;
+		}
+	}
+
+#else /* !DCC_WIN32 */
+	static u_char checked_stdio = 0;
+	int fd;
+	struct flock fl;
+	DCC_PATH path;
+
+	/* ensure 0, 1, and 2 are open so none of our real files get
+	 * those file descriptors and are used as stdin, stdout, or stderr */
+	if (!checked_stdio) {
+		clean_stdio();
+		checked_stdio = 1;
+	}
+
+	fd = open(nm, open_flags, 0666);
+	if (fd < 0 && dcc_get_priv_home(nm)) {
+		fd = open(nm, open_flags, 0666);
+		dcc_rel_priv();
+	}
+	if (fd < 0) {
+		if (errno == EACCES)
+			dcc_pemsg(EX_NOINPUT, emsg,
+				  "lock_open(%s): %s;"
+				  " file not writeable for locking",
+				  fnm2abs_err(path, nm), ERROR_STR());
+		else
+			dcc_pemsg(EX_NOINPUT, emsg,
+				  "lock_open(%s): %s",
+				  fnm2abs_err(path, nm), ERROR_STR());
+		if (busyp)
+			*busyp = DCC_BLOCK_ERROR();
+		return -1;
+	}
+
+	if (0 > fcntl(fd, F_SETFD, FD_CLOEXEC)) {
+		if (busyp)
+			*busyp = 0;
+		dcc_pemsg(EX_IOERR, emsg,
+			  "fcntl(F_SETFD FD_CLOEXEC %s %d): %s",
+			  fnm2abs_err(path, nm), lock_num, ERROR_STR());
+		close(fd);
+		return -1;
+	}
+
+	/* We may already have a lock on the file under a different file
+	 * descriptor.  If not, try to get a lock */
+	if (lock_mode != DCC_LOCK_OPEN_NOLOCK) {
+		set_fl(&fl,
+		       (lock_mode & DCC_LOCK_OPEN_SHARE) ? F_RDLCK : F_WRLCK,
+		       lock_num);
+		if (0 > fcntl(fd, (lock_mode & DCC_LOCK_OPEN_NOWAIT
+				   ? F_SETLK : F_SETLKW),
+			      &fl)) {
+			dcc_pemsg(EX_NOINPUT, emsg,
+				  "open fcntl(%s F_WRLCK %s %d): %s",
+				  (lock_mode & DCC_LOCK_OPEN_NOWAIT
+				   ? "F_SETLK" : "F_SETLKW"),
+				  fnm2abs_err(path, nm), lock_num,
+				  ERROR_STR());
+			if (busyp)
+				*busyp = (DCC_BLOCK_ERROR()
+					 || errno == EACCES);	/* for SunOS */
+			close(fd);
+			return -1;
+		}
+	}
+#endif /* !DCC_WIN32 or UNIX */
+
+	if (busyp)
+		*busyp = 0;
+	return fd;
+}
+
+
+
+u_char					/* 1=done 0=failed */
+dcc_unlock_fd(DCC_EMSG emsg UATTRIB, int fd,
+	      int lock_num,		/* which byte of the file to unlock */
+	      const char *str, const char *nm)
+{
+#ifdef DCC_WIN32
+	DCC_PATH path;
+
+	if (!win32_unlock((HANDLE)_get_osfhandle(fd))) {
+		dcc_pemsg(EX_NOINPUT, emsg,
+			  "UnlockFileEx(%s%s): %s",
+			  str, fnm2abs_err(path, nm), ERROR_STR());
+		return 0;
+	}
+	return 1;
+#else
+	struct flock fl;
+	DCC_PATH path;
+
+	set_fl(&fl, F_UNLCK, lock_num);
+	if (0 > fcntl(fd, F_SETLK, &fl)) {
+#ifdef DCC_DEBUG_CLNT_LOCK
+		dcc_logbad(EX_SOFTWARE,
+			   "fcntl(F_SETLK F_UNLCK %s%s %d): %s",
+			   str, fnm2abs_err(path, nm), lock_num, ERROR_STR());
+#else
+		dcc_pemsg(EX_NOINPUT, emsg,
+			  "fcntl(F_SETLK F_UNLCK %s%s %d): %s",
+			  str, fnm2abs_err(path, nm), lock_num, ERROR_STR());
+#endif
+		return 0;
+	}
+	return 1;
+#endif /* DCC_WIN32 */
+}
+
+
+
+#ifndef DCC_WIN32
+/* cause EINTR if we cannot get the lock */
+static void
+deadman(int s UATTRIB)
+{
+#if 0
+	dcc_logbad(EX_SOFTWARE, "exclusive lock deadman timer fired");
+#endif
+}
+#endif
+
+
+
+u_char					/* 1=done 0=failed */
+dcc_exlock_fd(DCC_EMSG emsg, int fd,
+	      int lock_num,		/* which byte of the file to lock */
+	      int max_delay,
+	      const char *str, const char *nm)
+{
+#ifdef DCC_WIN32
+	DCC_PATH path;
+
+	if (!win32_lock((HANDLE)_get_osfhandle(fd), LOCKFILE_EXCLUSIVE_LOCK)) {
+		dcc_pemsg(EX_IOERR, emsg, "LockFileEx(%s %s): %s",
+			  str, fnm2abs_err(path, nm), ERROR_STR());
+		return 0;
+	}
+	return 1;
+
+#else /* !DCC_WIN32 */
+	struct flock fl;
+	DCC_PATH path;
+	time_t start;
+	u_char result;
+
+	start = time(0);
+	if (max_delay) {
+#ifdef HAVE_SIGINTERRUPT
+		siginterrupt(SIGALRM, 1);
+#endif
+		signal(SIGALRM, deadman);
+		alarm(max_delay+1);
+	}
+
+	for (;;) {
+
+		set_fl(&fl, F_WRLCK, lock_num);
+		if (0 <= fcntl(fd, F_SETLKW, &fl)) {
+			result = 1;
+			break;
+		}
+
+		/* EINTR means something is hogging the lock */
+		if (DCC_SELECT_NERROR()) {
+			time_t delta = time(0) - start;
+			if (delta < max_delay) {
+				if (delta < 0) {    /* handle time jump */
+					start = time(0);
+					delta = 0;
+				}
+				alarm(max_delay-delta+1);
+				continue;
+			}
+			dcc_pemsg(EX_NOINPUT, emsg,
+				  "fcntl(F_SETLKW F_WRLCK %s%s %d)"
+				  " failed after %d seconds",
+				  str, fnm2abs_err(path, nm), lock_num,
+				  (int)delta);
+			result = 0;
+			break;
+		}
+#ifdef DCC_DEBUG_CLNT_LOCK
+		if (errno == EDEADLK) {
+			struct flock fl2;
+			pid_t our_pid;
+
+			our_pid = getpid();
+			fl2 = fl;
+			if (0 > fcntl(fd, F_GETLK, &fl2))
+				dcc_pemsg(EX_NOINPUT, emsg,
+					  "fcntl(F_GETLK): %s", ERROR_STR());
+			dcc_logbad(EX_SOFTWARE,
+				   "%d %d fcntl(F_SETLKW F_WRLCK %s%s %d): %s",
+				   (int)our_pid, (int)fl2.l_pid,
+				   str, fnm2abs_err(path, nm), lock_num,
+				   strerror(EDEADLK));
+		}
+		if (errno == EBADF) {
+			dcc_logbad(EX_SOFTWARE,
+				   "fcntl(F_SETLKW F_WRLCK %s%s %d): %s",
+				   str, fnm2abs_err(path, nm), lock_num,
+				   ERROR_STR());
+		}
+#endif
+		dcc_pemsg(EX_NOINPUT, emsg,
+			  "fcntl(F_SETLKW F_WRLCK %s%s %d): %s",
+			  str, fnm2abs_err(path, nm), lock_num,
+			  ERROR_STR());
+		result = 0;
+		break;
+	}
+
+	if (max_delay) {
+		signal(SIGALRM, SIG_DFL);
+		alarm(0);
+	}
+	return result;
+#endif /* DCC_WIN32 */
+}
+
+
+
+/* Several systems do not update the mtimes of files modified with mmap().
+ * Some like BSD/OS delay changing the mtime until the file accessed with
+ * read().  Others including filesystems on some versions of Linux
+ * apparently never change the mtime. */
+u_char
+dcc_set_mtime(DCC_EMSG emsg, const char *nm, int fd UATTRIB,
+	      const struct timeval *mtime)
+{
+#undef DIDIT
+#if defined(HAVE_FUTIMES) && !defined(linux) && !defined(DIDIT)
+	/* some Linux systems have a broken futimes implementation that does
+	 * not work for programs started as root but that release privileges */
+	struct timeval tbuf[2], *tbufp;
+	DCC_PATH path;
+	int result;
+
+	if (mtime) {
+		tbuf[0] = *mtime;
+		tbuf[1] = *mtime;
+		tbufp = tbuf;
+	} else {
+		tbufp = 0;
+	}
+	result = futimes(fd, tbufp);
+	if (result < 0
+	    && (errno == EACCES || errno == EPERM)
+	    && dcc_real_uid != dcc_effective_uid) {
+		dcc_get_priv();
+		result = futimes(fd, tbufp);
+		dcc_rel_priv();
+	}
+	if (result < 0)
+		dcc_pemsg(EX_IOERR, emsg,"futimes(%s): %s",
+			  fnm2abs_err(path, nm), ERROR_STR());
+#define DIDIT
+#endif /* HAVE_FUTIMES */
+#if defined(HAVE_UTIME_H) && !defined(DIDIT)
+	struct utimbuf tbuf, *tbufp;
+	DCC_PATH path;
+	int result;
+
+	if (mtime) {
+		tbuf.actime = tbuf.modtime = mtime->tv_sec;
+		tbufp = &tbuf;
+	} else {
+		tbufp = 0;
+	}
+	result = utime(nm, tbufp);
+	if (result < 0
+	    && dcc_real_uid != dcc_effective_uid) {
+		dcc_get_priv();
+		result = utime(nm, tbufp);
+		dcc_rel_priv();
+	}
+	if (result < 0)
+		dcc_pemsg(EX_IOERR, emsg,"utime(%s): %s",
+			  fnm2abs_err(path, nm), ERROR_STR());
+#define DIDIT
+#endif /* HAVE_UTIME_H */
+
+#ifdef DIDIT
+	return result >= 0;
+#undef DIDIT
+#else
+	return 1;			/* WIN32 */
+#endif
+}