[27156] in Source-Commits

home help back first fref pref prev next nref lref last post

/svn/athena r25984 - in trunk/athena/bin/saferm: . debian debian/source man src

daemon@ATHENA.MIT.EDU (Victor Vasiliev)
Sat May 11 18:19:51 2013

Date: Sat, 11 May 2013 18:19:43 -0400
From: Victor Vasiliev <vasilvv@MIT.EDU>
Message-Id: <201305112219.r4BMJhdf008661@drugstore.mit.edu>
To: source-commits@MIT.EDU
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Author: vasilvv
Date: 2013-05-11 18:19:43 -0400 (Sat, 11 May 2013)
New Revision: 25984

Added:
   trunk/athena/bin/saferm/Makefile.am
   trunk/athena/bin/saferm/configure.ac
   trunk/athena/bin/saferm/debian/source/
   trunk/athena/bin/saferm/debian/source/format
   trunk/athena/bin/saferm/man/
   trunk/athena/bin/saferm/man/Makefile.am
   trunk/athena/bin/saferm/man/saferm.1
   trunk/athena/bin/saferm/src/
   trunk/athena/bin/saferm/src/Makefile.am
   trunk/athena/bin/saferm/src/saferm.c
Removed:
   trunk/athena/bin/saferm/Makefile.in
   trunk/athena/bin/saferm/configure.in
   trunk/athena/bin/saferm/saferm.1
   trunk/athena/bin/saferm/saferm.c
Modified:
   trunk/athena/bin/saferm/debian/changelog
   trunk/athena/bin/saferm/debian/control
   trunk/athena/bin/saferm/debian/rules
Log:
In saferm:
  * Use automake
  * Bump upstream version
  * Switch to debhelper7
  * Switch to source format 3.0


Added: trunk/athena/bin/saferm/Makefile.am
===================================================================
--- trunk/athena/bin/saferm/Makefile.am	                        (rev 0)
+++ trunk/athena/bin/saferm/Makefile.am	2013-05-11 22:19:43 UTC (rev 25984)
@@ -0,0 +1,2 @@
+ACLOCAL_AMFLAGS = --install -I m4
+SUBDIRS = src man

Added: trunk/athena/bin/saferm/configure.ac
===================================================================
--- trunk/athena/bin/saferm/configure.ac	                        (rev 0)
+++ trunk/athena/bin/saferm/configure.ac	2013-05-11 22:19:43 UTC (rev 25984)
@@ -0,0 +1,31 @@
+#                                               -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ([2.65])
+AC_INIT([saferm], [10.1.0], [debathena@mit.edu])
+AM_INIT_AUTOMAKE([foreign -Wall])
+AM_MAINTAINER_MODE([enable])
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+AC_CONFIG_SRCDIR([src/saferm.c])
+AC_CONFIG_HEADERS([config.h])
+
+# Checks for programs.
+AC_PROG_CC
+
+# Checks for libraries.
+
+# Checks for header files.
+AC_CHECK_HEADERS([fcntl.h stdlib.h string.h unistd.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_OFF_T
+AC_TYPE_SSIZE_T
+AC_SYS_LARGEFILE
+
+# Checks for library functions.
+AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK
+AC_FUNC_MALLOC
+AC_CHECK_FUNCS([fchdir memset rmdir strchr strerror strrchr])
+
+AC_CONFIG_FILES([Makefile src/Makefile man/Makefile])
+AC_OUTPUT

Modified: trunk/athena/bin/saferm/debian/changelog
===================================================================
--- trunk/athena/bin/saferm/debian/changelog	2013-05-11 22:15:52 UTC (rev 25983)
+++ trunk/athena/bin/saferm/debian/changelog	2013-05-11 22:19:43 UTC (rev 25984)
@@ -1,4 +1,4 @@
-debathena-saferm (10.0.1-0debathena2) UNRELEASED; urgency=low
+debathena-saferm (10.1.0-0debathena1) UNRELEASED; urgency=low
 
   [ Geoffrey Thomas ]
   * Change description to clarify that this package does not
@@ -11,6 +11,12 @@
   * Bump Standards-Version to 3.9.3
   * Bump compat level to 7
 
+  [ Victor Vasiliev ]
+  * Use automake
+  * Bump upstream version
+  * Switch to debhelper7
+  * Switch to source format 3.0
+
  -- Jonathan Reed <jdreed@mit.edu>  Wed, 03 Apr 2013 13:59:55 -0400
 
 debathena-saferm (10.0.1-0debathena1) unstable; urgency=low

Modified: trunk/athena/bin/saferm/debian/control
===================================================================
--- trunk/athena/bin/saferm/debian/control	2013-05-11 22:15:52 UTC (rev 25983)
+++ trunk/athena/bin/saferm/debian/control	2013-05-11 22:19:43 UTC (rev 25984)
@@ -2,7 +2,7 @@
 Section: debathena/utils
 Priority: extra
 Maintainer: Debathena Project <debathena@mit.edu>
-Build-Depends: cdbs, debhelper, dh-buildinfo, autoconf
+Build-Depends: debhelper (>= 7), dh-autoreconf, autotools-dev
 Standards-Version: 3.9.3
 
 Package: debathena-saferm

Modified: trunk/athena/bin/saferm/debian/rules
===================================================================
--- trunk/athena/bin/saferm/debian/rules	2013-05-11 22:15:52 UTC (rev 25983)
+++ trunk/athena/bin/saferm/debian/rules	2013-05-11 22:19:43 UTC (rev 25984)
@@ -1,10 +1,5 @@
 #!/usr/bin/make -f
 
-DEB_AUTO_UPDATE_AUTOCONF = 2.50
-include /usr/share/cdbs/1/rules/debhelper.mk
-include /usr/share/cdbs/1/class/autotools.mk
+%:
+	dh $@ --with-autoreconf
 
-DEB_CONFIGURE_EXTRA_FLAGS += lbindir=/bin
-
-clean::
-	rm -f configure

Added: trunk/athena/bin/saferm/debian/source/format
===================================================================
--- trunk/athena/bin/saferm/debian/source/format	                        (rev 0)
+++ trunk/athena/bin/saferm/debian/source/format	2013-05-11 22:19:43 UTC (rev 25984)
@@ -0,0 +1 @@
+3.0 (quilt)

Added: trunk/athena/bin/saferm/man/Makefile.am
===================================================================
--- trunk/athena/bin/saferm/man/Makefile.am	                        (rev 0)
+++ trunk/athena/bin/saferm/man/Makefile.am	2013-05-11 22:19:43 UTC (rev 25984)
@@ -0,0 +1 @@
+dist_man1_MANS = saferm.1

Copied: trunk/athena/bin/saferm/man/saferm.1 (from rev 25973, trunk/athena/bin/saferm/saferm.1)
===================================================================
--- trunk/athena/bin/saferm/man/saferm.1	                        (rev 0)
+++ trunk/athena/bin/saferm/man/saferm.1	2013-05-11 22:19:43 UTC (rev 25984)
@@ -0,0 +1,52 @@
+.\" $Id: saferm.1,v 1.2 1999-12-28 15:57:24 ghudson Exp $
+.\"
+.\" Copyright 1998 by the Massachusetts Institute of Technology.
+.\"
+.\" Permission to use, copy, modify, and distribute this
+.\" software and its documentation for any purpose and without
+.\" fee is hereby granted, provided that the above copyright
+.\" notice appear in all copies and that both that copyright
+.\" notice and this permission notice appear in supporting
+.\" documentation, and that the name of M.I.T. not be used in
+.\" advertising or publicity pertaining to distribution of the
+.\" software without specific, written prior permission.
+.\" M.I.T. makes no representations about the suitability of
+.\" this software for any purpose.  It is provided "as is"
+.\" without express or implied warranty.
+.\"
+.TH SAFERM 1 "30 Mar 1998"
+.SH NAME
+saferm \- remove files without race conditions
+.SH SYNOPSIS
+.B saferm
+[
+.B \-q
+] [
+.B \-d
+|
+.B \-z
+]
+.B filename
+[
+.B filename ...
+]
+.fi
+.SH DESCRIPTION
+.I saferm
+is a file removal program which can be used running as root to safely
+remove and optionally zero files or directories from world-writable
+directories, such as /tmp.  It will refuse to change directories
+through symbolic links, and it will not zero files that have more than
+one hard link.  Files are zeroed only when the
+.B \-z
+option is specified.  If a directory is being removed, the
+.B \-d
+flag must be specified.  The
+.B \-q
+flag suppresses the error message for the case when a directory
+cannot be removed because it is not empty.
+
+.SH AUTHOR
+Craig Fields, MIT Information Systems
+.br
+Copyright (c) 1998 Massachusetts Institute of Technology

Added: trunk/athena/bin/saferm/src/Makefile.am
===================================================================
--- trunk/athena/bin/saferm/src/Makefile.am	                        (rev 0)
+++ trunk/athena/bin/saferm/src/Makefile.am	2013-05-11 22:19:43 UTC (rev 25984)
@@ -0,0 +1,2 @@
+bin_PROGRAMS = saferm
+saferm_SOURCES = saferm.c

Copied: trunk/athena/bin/saferm/src/saferm.c (from rev 25973, trunk/athena/bin/saferm/saferm.c)
===================================================================
--- trunk/athena/bin/saferm/src/saferm.c	                        (rev 0)
+++ trunk/athena/bin/saferm/src/saferm.c	2013-05-11 22:19:43 UTC (rev 25984)
@@ -0,0 +1,427 @@
+/* Copyright 1998 by the Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ */
+
+/*
+ * saferm
+ *
+ * Running as root, remove and optionally zero files in /tmp that
+ * may be owned by anybody, avoiding any possible race conditions
+ * resulting in security problems.
+ */
+
+static const char rcsid[] = "$Id: saferm.c,v 1.6 1999-09-21 20:09:11 danw Exp $";
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+char *program_name;
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+static int safe_open(char *filename, struct stat *file_stat);
+static int zero(int file, struct stat *file_stat);
+static int safe_to_unlink(char *name);
+static char *safe_cd(char *path);
+static int safe_rm(char *path, int zero_file);
+static int safe_rmdir(char *path, int quietflag);
+
+int main(int argc, char **argv)
+{
+  int c, zeroflag = 0, dirflag = 0, quietflag = 0, errflag = 0;
+  int dir, status = 0;
+
+  /* Set up our program name. */
+  if (argv[0] != NULL)
+    {
+      program_name = strrchr(argv[0], '/');
+      if (program_name != NULL)
+        program_name++;
+      else
+        program_name = argv[0];
+    }
+  else
+    program_name = "saferm";
+
+  while ((c = getopt(argc, argv, "dqz")) != EOF)
+    {
+      switch(c)
+	{
+	case 'd':
+	  dirflag = 1;
+	  break;
+	case 'q':
+	  quietflag = 1;
+	  break;
+	case 'z':
+	  zeroflag = 1;
+	  break;
+	case '?':
+	  errflag = 1;
+	  break;
+	}
+    }
+
+  if (errflag || optind == argc || (dirflag && zeroflag))
+    {
+      fprintf(stderr, "usage: %s [-q] [-d|-z] filename [filename ...]\n",
+	      program_name);
+      exit(1);
+    }
+
+  /* Remember our current directory in case we are being passed
+   * relative paths.
+   */
+  dir = open(".", O_RDONLY);
+  if (dir == -1)
+    {
+      fprintf(stderr, "%s: error opening current directory: %s\n",
+	      program_name, strerror(errno));
+      exit(1);
+    }
+
+  while (optind < argc)
+    {
+      /* If we're removing a relative path, be sure to cd to where
+       * we started.
+       */
+      if (argv[optind][0] != '/')
+	{
+	  if (fchdir(dir) == -1)
+	    {
+	      fprintf(stderr, "%s: error cding to initial directory: %s\n",
+		      program_name, strerror(errno));
+	      exit(1);
+	    }
+	}
+
+      if (dirflag && safe_rmdir(argv[optind], quietflag) == -1)
+	status = 1;
+      else if (!dirflag && safe_rm(argv[optind], zeroflag) == -1)
+	status = 1;
+
+      optind++;
+    }
+
+  exit(status);
+}
+
+/* Safely open a file with intent to zero. */
+static int safe_open(char *filename, struct stat *file_stat)
+{
+  int file;
+  struct stat name_stat;
+
+  /* Try to open the file we think we want to zero. */
+  file = open(filename, O_WRONLY);
+  if (file == -1)
+    {
+      fprintf(stderr, "%s: open(%s) for writing failed: %s\n",
+	      program_name, filename, strerror(errno));
+      return -1;
+    }
+
+  /* Stat the file we actually opened. */
+  if (fstat(file, file_stat) == -1)
+    {
+      fprintf(stderr, "%s: fstat(%s) failed: %s\n",
+	      program_name, filename, strerror(errno));
+      close(file);
+      return -1;
+    }
+
+  /* Make sure it's a regular file. */
+  if (!S_ISREG(file_stat->st_mode))
+    {
+      fprintf(stderr, "%s: %s is %s\n",
+	      program_name, filename,
+	      (S_ISDIR(file_stat->st_mode) 
+	       ? "a directory"
+	       : "not a regular file"));
+      close(file);
+      return -1;
+    }
+
+  /* Stat the file we think we want to zero. */
+  if (lstat(filename, &name_stat) == -1)
+    {
+      fprintf(stderr, "%s: lstat(%s) failed: %s\n",
+	      program_name, filename, strerror(errno));
+      close(file);
+      return -1;
+    }
+
+  /* We must be sure that we are not zeroing a hard link or symbolic
+   * link to something like the password file. To do this, we must
+   * verify that the link count on the file we zero is equal to 1, and
+   * that we did not follow a symbolic link to reach that file.
+   *
+   * Simply lstat()ing the file before or after we open it is subject to
+   * lose in a race condition, as there is no way to know that the file we
+   * opened is the same file as the one we lstat()ed. Simply fstat()ing
+   * the file we actually open can't tell us whether we followed a symbolic
+   * link to get there, and also can't give us a reliable link count also
+   * due to race conditions.
+   *
+   * But if we both lstat() and fstat() and compare the inode numbers from
+   * the two cases, we can verify that the file we have open, at the time
+   * of our lstat(), existed in a unique location identified by the pathname
+   * we used to open it.
+   *
+   * So first, verify that our two stats got the same file.
+   */
+  if (file_stat->st_dev != name_stat.st_dev
+      || file_stat->st_ino != name_stat.st_ino)
+    {
+      fprintf(stderr, "%s: %s and file opened are not identical\n",
+	      program_name, filename);
+      close(file);
+      return -1;
+    }
+
+  /* Now verify that the link count is 1. Note that file_stat may not be
+   * used here, otherwise we could be beaten by a race condition:
+   *
+   *   1. The attacker creates /tmp/passwd as a symlink to /etc/passwd.
+   *   2. We open /tmp/passwd, which follows the symlink, and so we have
+   *      fstat()ed /etc/passwd. The link count as of this stat is 1.
+   *   3. The attacker removes the /tmp/passwd symlink and replaces it
+   *      with a hard link to /etc/passwd.
+   *   4. We lstat() /tmp/passwd. The link count now is 2.
+   */
+  if (name_stat.st_nlink != 1)
+    {
+      fprintf(stderr, "%s: multiple links for file %s, not zeroing\n",
+	      program_name, filename);
+      close(file);
+      return -1;
+    }
+
+  /* Now we know it is safe to muck with the file we opened. */
+  return file;
+}
+
+/* Zero the file represented by the passed file descriptor. Assumes
+ * that the file pointer is already at the beginning of the file, and
+ * file_stat points to a stat of the file.
+ */
+static int zero(int file, struct stat *file_stat)
+{
+  char zeroes[BUFSIZ];
+  off_t written = 0;
+  ssize_t status;
+
+  memset(zeroes, 0, BUFSIZ);
+
+  while (written < file_stat->st_size)
+    {
+      status = write(file, zeroes, MIN(BUFSIZ, file_stat->st_size - written));
+      if (status == -1)
+	{
+	  fprintf(stderr, "%s: write() failed: %s\n",
+		  program_name, strerror(errno));
+	  return -1;
+	}
+      written += status;
+    }
+
+  fsync(file);
+  return 0;
+}
+
+/* Return non-zero if it is safe to unlink NAME. */
+static int safe_to_unlink(char *name)
+{
+  struct stat st;
+  
+  if (lstat(name, &st) < 0)
+    {
+      fprintf(stderr, "%s: %s: %s\n", program_name, name, strerror(errno));
+      return 0;
+    }
+  
+  if (S_ISDIR(st.st_mode))
+    {
+      fprintf(stderr, "%s: %s is a directory\n", program_name, name);
+      return 0;
+    }
+
+  return 1;
+}
+
+/* path is the full pathname to a file. We cd into the directory
+ * containing the file, following no symbolic links. A pointer to just
+ * the filename is returned on success (a substring of path), and NULL
+ * is returned on failure.
+ */
+static char *safe_cd(char *path)
+{
+  char *start_ptr, *end_ptr, *path_buf;
+  struct stat above, below;
+
+  if (path == NULL || *path == 0)
+    return NULL;
+
+  /* Make a copy of the path to be removed, since we're going to modify it. */
+  path_buf = malloc(strlen(path) + 1);
+  if (path_buf == NULL)
+    {
+      fprintf(stderr, "%s: out of memory\n", program_name);
+      return NULL;
+    }
+  strcpy(path_buf, path);
+
+  /* Step through each path component and cd into it, verifying
+   * that we wind up where we expect to.
+   *
+   * strchr()ing from start_ptr + 1 handles the absolute path case
+   * as well as beginning to address the double-/ case.
+   */
+  start_ptr = path_buf;
+  while ((end_ptr = strchr(start_ptr + 1, '/')))
+    {
+      /* Include any remaining extra /'s at the end of this component. */
+      while (*(end_ptr + 1) == '/')
+	end_ptr++;
+
+      *end_ptr = 0;
+
+      if (lstat(start_ptr, &above) == -1)
+	{
+	  fprintf(stderr, "%s: lstat of \"%s\" failed: %s\n", program_name,
+		  path_buf, strerror(errno));
+	  free(path_buf);
+	  return NULL;
+	}
+
+      if (!S_ISDIR(above.st_mode))
+	{
+	  fprintf(stderr, "%s: final component of \"%s\" is not a directory\n",
+		  program_name, path_buf);
+	  free(path_buf);
+	  return NULL;
+	}
+
+      if (chdir(start_ptr) == -1)
+	{
+	  fprintf(stderr, "%s: chdir to \"%s\" failed: %s\n", program_name,
+		  path_buf, strerror(errno));
+	  free(path_buf);
+	  return NULL;
+	}
+
+      if (stat(".", &below) == -1)
+	{
+	  fprintf(stderr, "%s: stat of \"%s\" failed: %s\n", program_name,
+		  path_buf, strerror(errno));
+	  free(path_buf);
+	  return NULL;
+	}
+
+      if (above.st_dev != below.st_dev || above.st_ino != below.st_ino)
+	{
+	  fprintf(stderr, "%s: final component of \"%s\" changed during "
+		  "traversal\n", program_name, path_buf);
+	  free(path_buf);
+	  return NULL;
+	}
+
+      /* Advance to next component. */
+      start_ptr = end_ptr + 1;
+
+      /* Restore the / we nulled for diagnostics. */
+      *end_ptr = '/';
+    }
+
+  free(path_buf);
+  return (start_ptr - path_buf) + path;
+}
+
+/* Safely remove and optionally zero a file. -1 is returned for any
+ * level of failure, such as zeroing failed but unlinking succeeded.
+ */
+static int safe_rm(char *path, int zero_file)
+{
+  int file, status = 0;
+  struct stat file_stat;
+  char *name;
+
+  name = safe_cd(path);
+  if (name == NULL)
+    return -1;
+
+  /* Do the unlink before the actual zeroing so that the appearance
+   * is atomic.
+   */
+
+  if (zero_file)
+    {
+      file = safe_open(name, &file_stat);
+      if (file == -1)
+	return -1;
+    }
+
+  if (safe_to_unlink(name) && unlink(name) == -1)
+    {
+      fprintf(stderr, "%s: error unlinking %s: %s\n", program_name,
+	      path, strerror(errno));
+      status = -1;
+    }
+
+  if (zero_file && file != -1)
+    {
+      if (zero(file, &file_stat) == -1)
+	status = -1;
+      close(file);
+    }
+
+  return status;
+}
+
+/* Safely remove a directory. -1 is returned for any level of failure. */
+static int safe_rmdir(char *path, int quietflag)
+{
+  char *name;
+
+  name = safe_cd(path);
+  if (name == NULL)
+    return -1;
+
+  if (rmdir(name) == -1)
+    {
+      /* System V systems return EEXIST when a directory isn't empty
+       * but hack the rmdir command to display a better error message.
+       * Well, we can do that too.
+       */
+      if (errno == EEXIST)
+	errno = ENOTEMPTY;
+
+      if (!quietflag || errno != ENOTEMPTY)
+	{
+	  fprintf(stderr, "%s: error removing directory %s: %s\n",
+		  program_name, path, strerror(errno));
+	}
+      return -1;
+    }
+
+  return 0;
+}


home help back first fref pref prev next nref lref last post