Testsuite: create test db on-the-fliy for LMDB testcase
[exim.git] / src / src / exim_lock.c
index 9b5b2620960e36059e351c06087cf1c3d8304b28..068216816054e832085d9cb035daa8ae50f12ba8 100644 (file)
@@ -1,5 +1,3 @@
-/* $Cambridge: exim/src/src/exim_lock.c,v 1.2 2005/06/22 15:44:38 ph10 Exp $ */
-
 /* A program to lock a file exactly as Exim would, for investigation of
 interlocking problems.
 
@@ -11,6 +9,8 @@ Options:  -fcntl    use fcntl() lock
 Default is -fcntl -lockfile.
 
 Argument: the name of the lock file
+
+Copyright (c) The Exim Maintainers 2016
 */
 
 #include "os.h"
@@ -38,7 +38,7 @@ in sys/file.h. */
 #endif
 
 
-typedef int BOOL;
+typedef unsigned BOOL;
 #define FALSE 0
 #define TRUE  1
 
@@ -72,6 +72,10 @@ the other stuff in os.c, so force the other macros to omit it. */
   #define FIND_RUNNING_INTERFACES
 #endif
 
+#ifndef OS_GET_DNS_RESOLVER_RES
+  #define OS_GET_DNS_RESOLVER_RES
+#endif
+
 #include "../src/os.c"
 
 
@@ -173,7 +177,7 @@ int  fd = -1;
 int  hd = -1;
 int  md = -1;
 int  yield = 0;
-int  now = time(NULL);
+time_t now = time(NULL);
 BOOL use_lockfile = FALSE;
 BOOL use_fcntl = FALSE;
 BOOL use_flock = FALSE;
@@ -183,7 +187,8 @@ BOOL quiet = FALSE;
 BOOL restore_times = FALSE;
 char *filename;
 char *lockname = NULL, *hitchname = NULL;
-char *primary_hostname, *command;
+char *primary_hostname;
+const char *command;
 struct utsname s;
 char buffer[256];
 char tempname[256];
@@ -213,7 +218,7 @@ for (i = 1; i < argc; i++)
   else usage();
   }
 
-if (quiet) verbose = 0;
+if (quiet) verbose = FALSE;
 
 /* Can't use flock() if the OS doesn't provide it */
 
@@ -297,8 +302,10 @@ if (use_lockfile)
   lockname = malloc(len + 8);
   sprintf(lockname, "%s.lock", filename);
   hitchname = malloc(len + 32 + (int)strlen(primary_hostname));
+
+  /* Presumably, this must match appendfile.c */
   sprintf(hitchname, "%s.%s.%08x.%08x", lockname, primary_hostname,
-    now, (int)getpid());
+    (unsigned int)now, (unsigned int)getpid());
 
   if (verbose)
     printf("exim_lock: lockname =  %s\n           hitchname = %s\n", lockname,
@@ -310,13 +317,14 @@ if (use_lockfile)
 for (j = 0; j < lock_retries; j++)
   {
   int sleep_before_retry = TRUE;
-  struct stat statbuf, ostatbuf;
+  struct stat statbuf, ostatbuf, lstatbuf, statbuf2;
+  int mbx_tmp_oflags;
 
   /* Try to build a lock file if so configured */
 
   if (use_lockfile)
     {
-    int rc;
+    int rc, rc2;
     if (verbose) printf("exim_lock: creating lock file\n");
     hd = open(hitchname, O_WRONLY | O_CREAT | O_EXCL, 0440);
     if (hd < 0)
@@ -328,11 +336,12 @@ for (j = 0; j < lock_retries; j++)
 
     /* Apply hitching post algorithm. */
 
-    if ((rc = link(hitchname, lockname)) != 0) fstat(hd, &statbuf);
-    close(hd);
+    if ((rc = link(hitchname, lockname)) != 0)
+     rc2 = fstat(hd, &statbuf);
+    (void)close(hd);
     unlink(hitchname);
 
-    if (rc != 0 && statbuf.st_nlink != 2)
+    if (rc != 0 && (rc2 != 0 || statbuf.st_nlink != 2))
       {
       printf("exim_lock: failed to link hitching post to lock file\n");
       hd = -1;
@@ -348,8 +357,7 @@ for (j = 0; j < lock_retries; j++)
 
   /* Open the file for writing. */
 
-  fd = open(filename, O_RDWR + O_APPEND);
-  if (fd < 0)
+  if ((fd = open(filename, O_RDWR + O_APPEND)) < 0)
     {
     printf("exim_lock: failed to open %s for writing: %s\n", filename,
       strerror(errno));
@@ -368,7 +376,6 @@ for (j = 0; j < lock_retries; j++)
   a timeout changes it to blocking. */
 
   if (!use_mbx && (use_fcntl || use_flock))
-    {
     if (apply_lock(fd, F_WRLCK, use_fcntl, lock_fcntl_timeout, use_flock,
         lock_flock_timeout) >= 0)
       {
@@ -379,8 +386,8 @@ for (j = 0; j < lock_retries; j++)
         }
       break;
       }
-    else goto RETRY;   /* Message already output */
-    }
+    else
+      goto RETRY;   /* Message already output */
 
   /* Lock using MBX rules. This is complicated and is documented with the
   source of the c-client library that goes with Pine and IMAP. What has to
@@ -431,7 +438,11 @@ for (j = 0; j < lock_retries; j++)
         }
       }
 
-    md = open(tempname, O_RDWR | O_CREAT, 0600);
+    mbx_tmp_oflags = O_RDWR | O_CREAT;
+#ifdef O_NOFOLLOW
+    mbx_tmp_oflags |= O_NOFOLLOW;
+#endif
+    md = open(tempname, mbx_tmp_oflags, 0600);
     if (md < 0)
       {
       printf("exim_lock: failed to create mbx lock file %s: %s\n",
@@ -439,6 +450,30 @@ for (j = 0; j < lock_retries; j++)
       goto CLEAN_UP;
       }
 
+    /* security fixes from 2010-05 */
+    if (lstat(tempname, &lstatbuf) < 0)
+      {
+      printf("exim_lock: failed to lstat(%s) after opening it: %s\n",
+          tempname, strerror(errno));
+      goto CLEAN_UP;
+      }
+    if (fstat(md, &statbuf2) < 0)
+      {
+      printf("exim_lock: failed to fstat() open fd of \"%s\": %s\n",
+          tempname, strerror(errno));
+      goto CLEAN_UP;
+      }
+    if ((statbuf2.st_nlink > 1) ||
+        (lstatbuf.st_nlink > 1) ||
+        (!S_ISREG(lstatbuf.st_mode)) ||
+        (lstatbuf.st_dev != statbuf2.st_dev) ||
+        (lstatbuf.st_ino != statbuf2.st_ino))
+      {
+      printf("exim_lock: race condition exploited against us when "
+          "locking \"%s\"\n", tempname);
+      goto CLEAN_UP;
+      }
+
     (void)chmod(tempname, 0600);
 
     if (apply_lock(md, F_WRLCK, use_fcntl, lock_fcntl_timeout, use_flock,
@@ -549,14 +584,41 @@ else
 if (restore_times)
   {
   struct stat strestore;
+#ifdef EXIM_HAVE_OPENAT
+  int fd = open(filename, O_RDWR); /* use fd for both get & restore */
+  struct timespec tt[2];
+
+  if (fd < 0)
+    {
+    printf("open '%s': %s\n", filename, strerror(errno));
+    yield = 1;
+    goto CLEAN_UP;
+    }
+  if (fstat(fd, &strestore) != 0)
+    {
+    printf("fstat '%s': %s\n", filename, strerror(errno));
+    yield = 1;
+    close(fd);
+    goto CLEAN_UP;
+    }
+  i = system(command);
+  tt[0] = strestore.st_atim;
+  tt[1] = strestore.st_mtim;
+  (void) futimens(fd, tt);
+  (void) close(fd);
+#else
   struct utimbuf ut;
+
   stat(filename, &strestore);
-  (void)system(command);
+  i = system(command);
   ut.actime = strestore.st_atime;
   ut.modtime = strestore.st_mtime;
   utime(filename, &ut);
+#endif
   }
-else (void)system(command);
+else i = system(command);
+
+if(i && !quiet) printf("warning: nonzero status %d\n", i);
 
 /* Remove the locks and exit. Unlink the /tmp file if we can get an exclusive
 lock on the mailbox. This should be a non-blocking lock call, as there is no