Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 12 Jul 1999 15:06:50 -0600
From:      Wes Peters <wes@softweyr.com>
To:        Eivind Eklund <eivind@FreeBSD.ORG>
Cc:        cjclark@home.com, FreeBSD Security <freebsd-security@FreeBSD.ORG>
Subject:   Re: Secure Deletion
Message-ID:  <378A58EA.ACF1412F@softweyr.com>
References:  <199906250212.WAA07810@cc942873-a.ewndsr1.nj.home.com> <3773F67A.CC9B6215@softweyr.com> <19990629131529.A61249@bitbox.follo.net>

next in thread | previous in thread | raw e-mail | index | archive | help
Eivind Eklund wrote:
> 
> On Fri, Jun 25, 1999 at 03:36:58PM -0600, Wes Peters wrote:
> > This won't do it, if you're really interested in obliterating the file
> > contents.  What you want to do is overwrite the file blocks with
>                                                   ^^^^
> disk
> 
> > alternating patterns of 10101010 then 01010101 at least 100 times.
> > Due to the way modern recording formats work, and the memory of the
> > cells that actually store the bits on the disk, anything less won't
> > really erase the disk.
> 
> More or less correct.  There are a lot of details to this, and just
> writing 0x55/0xaa as normal data values won't make them hit the disk
> that way.
> 
> Since what I have to write about this topic would just end up being a
> paraphrase of what Peter Gutmann has to say, I suggest you read the
> paper he presented at Usenix 1996:
>         http://www.cs.auckland.ac.nz/~pgut001/pubs/secure_del.html
> 
> Eivind.

I've read Peter's paper, which I found to be very informative.  I've
incorporated his table of successive values into my program, along with
a couple of bug fixes.  (I wasn't unmapping the file before closing and
unlinking it, and I was mapping it MAP_PRIVATE so it wasn't really over-
writing the file anyhow.  Doh!)  Here's the source for the new, improved
version if anyone wants to test it themselves.

Unless anyone has strenuous objections, I'll make this into a port and
commit it (as soon as I learn how to make a port).

/*
 * Obliterate - a simple program to obliterate file contents.
 *
 * Copyright (c) 1999 Softweyr LLC, South Jordan, Utah USA.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY Softweyr LLC ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL Softweyr LLC OR ANY CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * Author: Wes Peters
 * Date: Mon Jul 12 15:04:51 MDT 1999
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>

#include <stdio.h>
#include <fcntl.h>

#define MIN(a,b) ((a < b) ? a : b)

/*
 * The bit patterns used to overwrite the disk blocks.  These patterns are
 * designed to provide a complete overwrite on all common disk recording
 * formats.
 *
 * Pattern devised by Peter Gutmann, Department of Computer Science,
 * University of Auckland <pgut001@cs.auckland.ac.nz>.  See Peter's paper
 * "Secure Deletion of Data from Magnetic and Solid-State Memory" at
 * http://www.cs.auckland.ac.nz/~pgut001/pubs/secure_del.html for more
 * information about how and why this pattern works.
 */

unsigned char *patterns[] =
{
    "R",		/* random */
    "R",		/* random */
    "R",		/* random */
    "R",		/* random */
    "\x55",		/* 1,7 RLL         MFM */
    "\xaa",		/* 1,7 RLL         MFM */
    "\x92\x49\x24",	/*         2,7 RLL MFM */
    "\x49\x24\x92",	/*         2,7 RLL MFM */
    "\x24\x92\x49",	/*         2,7 RLL MFM */
    "\x00",		/* 1,7 RLL 2,7 RLL     */
    "\x11",		/* 1,7 RLL             */
    "\x22",		/* 1,7 RLL             */
    "\x33",		/* 1,7 RLL 2,7 RLL     */
    "\x44",		/* 1,7 RLL             */
    "\x55",		/* 1,7 RLL         MFM */
    "\x66",		/* 1,7 RLL 2,7 RLL     */
    "\x77",		/* 1,7 RLL             */
    "\x88",		/* 1,7 RLL             */
    "\x99",		/* 1,7 RLL 2,7 RLL     */
    "\xaa",		/* 1,7 RLL         MFM */
    "\xbb",		/* 1,7 RLL             */
    "\xcc",		/* 1,7 RLL 2,7 RLL     */
    "\xdd",		/* 1,7 RLL             */
    "\xee",		/* 1,7 RLL             */
    "\xff",		/* 1,7 RLL 2,7 RLL     */
    "\x92\x49\x24",	/*         2,7 RLL MFM */
    "\x49\x24\x92",	/*         2,7 RLL MFM */
    "\x24\x92\x49",	/*         2,7 RLL MFM */
    "\x6d\xb6\xdb",	/*         2,7 RLL     */
    "\xb6\xdb\x6d",	/*         2,7 RLL     */
    "\xdb\x6d\xb6",	/*         2,7 RLL     */
    "R",		/* random */
    "R",		/* random */
    "R",		/* random */
    "R",		/* random */
};

static int npatterns = sizeof (patterns) / sizeof (patterns[0]);

/*
 * A file handle to access the system pool of randomness.
 */

int randfd = 0;

/*
 * Fill a block of memory with a specified pattern, repeating the pattern as
 * necessary to completely fill the block.  This routine is smart about
 * "special" patterns such as an apparent NULL pattern (this means zero-fill
 * the block) and "R" which means fill the block with random values.
 */

int
fill(void *data, off_t size, char *pattern)
{
    /*
     * If the first byte is zero, it means zero-fill the block.
     */
    if (*pattern == '\0')
    {
	(void) memset(data, 0, size);
    }
    /*
     * If the first byte is 'R' it means random fill the block.  We use the
     * system pool of randomness here because it is an easy to use source of
     * pretty much random data.  Reading blocks of any appreciable size from
     * the pool probably degrades it into a cascade of MD5 signatures, but
     * should be sufficient for our needs here.
     */
    else if (*pattern == 'R')
    {
	read(randfd, data, size);
    }
    else
    {
	/*
	 * Be smart about using library functions where available.
	 */
	int l = strlen(pattern);
	if (l == 1)
	{
	    memset(data, *pattern, size);
	}
	else
	{
	    char *p, *e = data + size;
	    for (p = data; p < e; p += l)
	    {
		memcpy(p, pattern, MIN(l, e - p));
	    }
	}
    }
}


/*
 * Overwrite the named file.  Return 0 on success, -1 otherwise.
 */

int
obliterate(char *fname)
{
    struct stat S;
    int fd;
    void *data;
    int iteration;

    if (stat(fname, &S) == -1)
    {
	perror(fname);
	return -1;
    }

    if (!S_ISREG(S.st_mode))
    {
	fprintf(stderr, "%s: not a regular file\n", fname);
	return -1;
    }

    fd = open(fname, O_RDWR);
    if (fd == -1)
    {
	perror(fname);
	return -1;
    }
    
    data = mmap(NULL, S.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (data == MAP_FAILED)
    {
	perror(fname);
	close(fd);
	return -1;
    }

    /*
     * Overwrite the file with successive patterns as specified above.
     */
    for (iteration = 0; iteration < npatterns; iteration++)
    {
	fill(data, S.st_size, patterns[iteration]);
	if (fsync(fd) == -1)
	{
	    perror(fname);
	    close(fd);
	    return -1;
	}
    }

    /*
     * Get rid of the file (from the filesystem point of view).
     */
    if (munmap(data, S.st_size) == -1)
    {
	perror(fname);
	close(fd);
	return -1;
    }

    close(fd);

    if (unlink(fname) == -1)
    {
	perror(fname);
	return -1;
    }

    return 0;
}


/*
 * Obliterate each file named on the command line.
 */

int
main(int argc, char *argv[])
{
    int result = 0;

    /*
     * Open the system entropy pool for random data.
     */
    if ((randfd = open("/dev/urandom", O_RDONLY)) == -1)
    {
	perror("Opening system pool of randomness (/dev/urandom)");
	return -1;
    }

    while (--argc)
    {
	result |= obliterate(*++argv);
    }

    close(randfd);
    return result;
}
/* EOF */

-- 
            "Where am I, and what am I doing in this handbasket?"

Wes Peters                                                         Softweyr LLC
http://softweyr.com/                                           wes@softweyr.com


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-security" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?378A58EA.ACF1412F>