[27156] in Source-Commits
/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;
+}