secure-delete-3.1.orig/0040755000175000017500000000000010006237022015254 5ustar robertlerobertlesecure-delete-3.1.orig/CHANGES0100644000175000017500000001235207750316441016266 0ustar robertlerobertlev3.1 (November 2003) PUBLIC RELEASE (sdel-mod) I added a kernel module in v3.0 which wipes all files which are unlinked() in the kernel. However it didnt work, so it was not official. However, deathcubek@md2600.net sent in a patch, so now it is working :-) After "make install" do "insmod sdel-mod" (srm) Fixed a bug which prevented to delete directories v3.0 (April 2003) PUBLIC RELEASE (all) Added >2GB file support (all) rewrote all tools to use a central library, this was long time necessary! (all) warning now displayed if ulimit value resetting support was not compiled in. (sswap) added -j(ump) option to skip over a defined number of bytes of the block device. (srm) skipping file wipe if it is hardlinked! (fileutils-rm) Update the rm.diff for the newest fileutil (4.1) (all) updated the link for our homepage + email to thc.org v2.5 (May 2001) PUBLIC RELEASE (all) updated the link for our homepage to www.thehackerschoice.com v2.4 (October 2000) PRIVATE RELEASE (all) added the option -z to write zeros as last wipe instead of random data (sfill) added the options -i and -I to not or exclusivly wipe inode space and expanded the number of inodes to wipe (the_cleaner.sh) added a new shellscript called the_cleaner.sh which wipes temporary directories, all user's history and netscape history/cache files, free disk and inode space, swap space and the memory. Easy shellscript to place in the shutdown phase of runlevel 2 and 3! (all) beautified the help output; updated documentation (all) added srm/sfill/sswap/smem as linux elf binaries v2.3 (August 2000) PUBLIC RELEASE (sfill) now also wipes all inodes in the directory specified (srm) fixed a bug in srm when deleting dirs without -r option (thanks to typo/teso) v2.2 (October 1999) PUBLIC RELEASE (WWW) ugh, the redirector service base.org is down! WWW site: http://www.infowar.co.uk/thc (all) code cleanup to prevent warnings (thanks Rebecca!) (some weird systems need O_FSYNC and strings.h) (srm) the blocksize used for wiping is now enlarged if the filesystem is detected to use bigger blocks. (srm/sfill/sswap) changed the behaviour, now the last wipe is always a random one, so it's harder to find out that a secure deletion was done. (Thanks to Dan Farmer for this idea) (srm) the random buffer to write was not renewed after one write to the file, this makes it pretty fast but also not that secure. No all random writes are unique. The other tools had that from the beginning on. duh v2.1 (April 1999) PRIVATE RELEASE (all) added the -f(ast) option. (smem) added a new tool: smem, which wipes free memory (srm/sfill/smem) added rlimit resets (sfill/sswap) quadroupled the buffer size (srm/sfill/sswap) fix for openbsd which doesn't know O_SYNC open(2). (srm) added the -d(ot) option. Ignores "." and ".." on the commandline (srm) fixed directory and file link races. Thanks to Solar Designer bringing this to my attention. Note that there is a small racecondition left which can be exploited from an attacker to chmod 0600 a file to it's owner if the owner is running srm and the attacker has write access to the target directory. The other wipe programs have this problem too - and ( - wiping anything in your tree for which you have write permission ... - ) worse. v2.0 (all) added man pages for srm, sfill and sswap! (all) increased speed by ~300% by writing big buffers. (all) using /dev/urandom for random passes now if available (this is more secure, however costs speed) (all) added the O_SYNC bit to all open calls. Should not add security, but who knows how weired systems behave. (all) the -s option(s) is now ignored. The standard mode is now the secure mode. Use -l to lessen the security. (srm) added the option -r for directory recursion (srm) doing a chmod() now before opening the file (srm) fixed handling of symlink and special files (rm.diff) includes all the above except for the new -s/-l change (all) cosmetics + documenation updates v1.8 (rm.diff) updated the diff and did some more security and speed. (all) a bit cosmetics v1.7 (srm) using fsync() now, which gives you 10-25% speed ! (all) using perror() for better error reporting (srm/sfill) included file truncating before unlinking (security!) v1.6 [www distrib.] http://r3wt.base.org - check out the THC area [email] my new email: vh@reptile.rug.ac.be (sswap) added a new tool: a secure swap cleaner. remember to unmount your swap first. *only tested on linux* ! (srm/sfill) added trapping signals and exiting clean on them (srm/sfill) errors are sent do stderr now instead to stdout (srm/sfill) general code cleanup (sfill) fixed race condition to prevent symlink attacks (sfill) warning if executed by user!=root, because not all free space can be overwritten (idea by fyodor@dhp.com) (rm.diff) replaces the rm.c replacement with a diff (+ bugfix) (sdel.exe/sfill.exe) removed the msdos versions. They 1st already included the features above and 2nd are no longer supported (at least by me - get a good unix) v1.4 (sfill) some flushing on stdout done (sfill) more error checking (srm/sfill) cosmetics v1.3 initial public release. secure-delete-3.1.orig/FILES0100644000175000017500000000165507655464140016070 0ustar robertlerobertleFILES you are reading me CHANGES what's new/changed ? README link to secure_deletion.doc Makefile unix makefile for srm.c, sfill.c and sswap.c rm-fileutil-3.x.diff Linux rm.c diff patch (for fileutil 3.x) rm-fileutil-4.1.diff Linux rm.c diff patch (for fileutil 4.1) secure_deletion.doc Documentation (READ!) sfill.c secure free diskspace overwriting source smem.c secure memory cleaner souce srm.c secure file deletion source sswap.c secure swap space cleaner source sdel-lib.c secure delete library sdel-lib.h secure delete library header sdel.h secure delete header for the tools config.h system configuration option header file the_cleaner.sh a shellscript which wipes reasonable files,space,swap,mem sfill.1 man page for sfill smem.1 man page for smem srm.1 man page for srm sswap.1 man page for sswap usenix6-gutmann.doc Gutmann's article about secure data deletion secure-delete-3.1.orig/Makefile0100644000175000017500000000365007750025772016740 0ustar robertlerobertleCC=gcc OPT=-O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE #OPT=-Wall -D_DEBUG_ -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE INSTALL_DIR=/usr/local/bin MAN_DIR=/usr/local/man DOC_DIR=/usr/share/doc/secure_delete OPT_MOD=-D__KERNEL__ -DMODULE -fomit-frame-pointer -fno-strict-aliasing -pipe -mpreferred-stack-boundary=2 #LD_MOD=-r all: sdel-lib.o srm sfill sswap smem sdel-mod.o @echo @echo "A Puritan is someone who is deathly afraid that someone, somewhere, is" @echo "having fun." @echo @echo "I hope YOU have fun!" @echo sdel-mod.o: sdel-mod.c $(CC) $(OPT) $(OPT_MOD) $(LD_MOD) -I/lib/modules/`uname -r`/build/include -c sdel-mod.c sdel-lib.o: sdel-lib.c $(CC) ${OPT} -c sdel-lib.c srm: srm.c $(CC) ${OPT} -o srm srm.c sdel-lib.o -strip srm sfill: sfill.c $(CC) ${OPT} -o sfill sfill.c sdel-lib.o -strip sfill sswap: sswap.c $(CC) ${OPT} -o sswap sswap.c sdel-lib.o -strip sswap smem: smem.c $(CC) ${OPT} -o smem smem.c sdel-lib.o -strip smem clean: rm -f sfill srm sswap smem sdel sdel-lib.o sdel-mod.o core *~ install: all mkdir -p -m 755 ${INSTALL_DIR} 2> /dev/null rm -f sdel && ln -s srm sdel cp -f sdel srm sfill sswap smem the_cleaner.sh ${INSTALL_DIR} chmod 711 ${INSTALL_DIR}/srm ${INSTALL_DIR}/sfill ${INSTALL_DIR}/sswap ${INSTALL_DIR}/smem ${INSTALL_DIR}/the_cleaner.sh mkdir -p -m 755 ${MAN_DIR}/man1 2> /dev/null cp -f srm.1 sfill.1 sswap.1 smem.1 ${MAN_DIR}/man1 chmod 644 ${MAN_DIR}/man1/srm.1 ${MAN_DIR}/man1/sfill.1 ${MAN_DIR}/man1/sswap.1 ${MAN_DIR}/man1/smem.1 mkdir -p -m 755 ${DOC_DIR} 2> /dev/null cp -f CHANGES FILES README secure_delete.doc usenix6-gutmann.doc ${DOC_DIR} -test -e sdel-mod.o && cp -f sdel-mod.o /lib/modules/`uname -r`/kernel/drivers/char # @-test '!' -e sdel-mod.o -a `uname -s` = 'Linux' && echo "type \"make sdel-mod install\" to compile and install the Linux loadable kernel module for secure delete" @echo @echo "If men could get pregnant, abortion would be a sacrament." @echo secure-delete-3.1.orig/README0100644000175000017500000004635407750316551016166 0ustar robertlerobertle S E C U R E D E L E T E (c) 1997-2003 by van Hauser / THC http://www.thc.org " Does the average person really need this kind of security? I say yes. [...] He may be living in a country that does not respect the rights of privacy of it's citizens. He may be doing something that he feels shouldn't be illegal, but is. Whatever his reasons, his data and communications are personal, private, and nobody's business but his own. " Bruce Schneier in the Preface of his book "Applied Cryptography" " A Puritan is someone who is deathly afraid that someone, somewhere, is having fun. " Unknown 1. INTRODUCTION 2. HOW THESE PROGRAMS WORK 3. COMMANDLINE OPTIONS 4. LIMITATIONS 5. COMPARISON 6. LAST WORDS 1. INTRODUCTION Years ago, people using the old msdos, simply deleted a file by doing "del filename" and thought the the erase was forever. Then Norton's undelete was released and everyone could get back the files most of the time. After that people who wanted to keep people of their deleted files erased them by overwriting the file with random or 0x00 bytes and felt secure. In 1996 Peter Gutmann published a paper called "Secure Deletion of Data from Magnetic and Solid-State Memory" at the 6th Usenix Security Symposium, where he pointed out that the data could even be recovered if you overwrote them triple times and more - using cheap equipment for about 1000-2500$. The three utilities presented here try to cover this new area of secure deletion and prevent file recovery. This release includes the full paper of Peter Gutmann from the 6th Usenix Security Proceeding (usenix6-gutmann.doc). The four utilities do the following : srm does secure deletion of files. sfill does a secure overwriting of the unused diskspace on the harddisk. sswap does a secure overwriting and cleaning of the swap filesystem. (note that sswap was only tested on linux so far. you must unmount your swap first!) smem does a secure overwriting of unused memory (RAM) For Linux, there's a diff (rm.diff) patch for rm.c which puts the overwriting feature into it. (You need the fileutil sources) 2. HOW THESE PROGRAMS WORK The deletion process is as follows: 1. The overwriting procedure (in the secure mode) does a 38 times overwriting. After each pass, the disk cache is flushed. 2. truncating the file, so that an attacker don't know which diskblocks belonged to the file. 3. renaming of the file, so that an attacker can't draw any conclusion from the filename on the contents of the deleted file. 4. finally deleting the file (unlink). Note that with v2.0 all secure_delete utilities work in secure mode (38 special passes). To lower the security and make it faster, you may add -l (onf 0xff pass, one random pass) or -ll (one random pass) to the parameters. The secure overwrite mode works that way: 1x overwrite with 0xff 5x random passes 27x overwriting with special values to make the recovery from MFM and RLL encoded harddisks hard/impossible - see Gutmann's paper on that which is also included. 5x random passes Some statistics : in 1 second you can approx. overwrite 1 to 2 MB of data, depending on your harddrive performance. In totally insecure mode, a 100 MB file/free-disk-space takes approx. 15 seconds, and in the totally secure mode approx. 60 minutes. 3. COMMANDLINE OPTIONS Here are the commandline options: srm [-d] [-f] [-l] [-l] [-v] [-z] file [file] [another file] [etc.] sfill [-i] [-I] [-f] [-l] [-l] [-v] [-z] target-directory sswap [-f] [-l] [-l] [-v] [-z] /dev/of_swap_filesystem smem [-f] [-l] [-l] [-v] The -s options are depricated now, and will be ignored. -d don't delete the dot special files "." and ".." on the commandline (only srm) -i wipe only free inode space, not free disk space on the filesystem (only sfill) -I wipe only free disk space, not free inode space on the filesystem (only sfill) -f fast writes without O_SYNC and sync() between writes. Much faster but less secure. -l lessens the security. Only one random plus one pass with 0xff are written. -l a seconds time as parameter switches into the insecurest mode, it overwrites the file only once with 0xff. -v turn verbose mode on. -z last wipe mode writes zeros instead of random data file file to delete. Wildcards are of course allowed. For unix: you need write permissions. For msdos: It may be hidden, system, readonly etc. we don't care. target-directory target is a directory in the filesystem to write to. swap_filesystem your swap filesystem. Unmount it first!! only tested on linux Options may be applied like "-lfv", "-l -f -v" or a mix. Note: If you use a gnu-compactible linux, you can use the patch rm.diff included in the package to put the features from srm into your normal rm. Just enter your fileutils-3.16 directory, type "patch < rm.diff" and then "make". You need at least one -s switch to activate (1 overwrite). Note that -sss is needed for full security. NOTE: For the linux kernel module, you just have to do "insmod sdel-mod" to load the module. After that, all files, which are deleted by any program are then wiped once before the space is marked as free. 4. LIMITATIONS This section discusses limitations of the programs presented and general problems and threats of secure data deletion - and how to handle them. As you can see from the sourcecode, these are very small and generic programs. That means that they aren't perfect and doesn't cover any aspect of secure data deletion. Please read this section carefully to learn against which problems it does NOT help. - Random Number Generation Since v2.0, secure_deletion uses the /dev/urandom as a random source if available. This should fix this problem. However, for completeness, read on: The numbers which WERE generated by the programs were far away being "real" random. Standard random number generators are used and they are easy to predict. This is a major risk if you are using them for online crypting purpose, for the purpose of overwriting it is nearly enough. However, I added two extra random overwrites to be sure. A solution for paranoid people : change the random number generator in the programs to something you trust. Here's an extract from an answer Peter Gutmann wrote me as I asked him about that problem : > 1st to be compactible with all platforms i didn t use a crypt-random > library, I use a simple (256*rand()/(RAND_MAX+1.0)) seeded 1st time with > the pid. For every byte to overwrite the file I call that function. > I know that this random stuff is not "secure" for online crypting, > but is it enough for deletion? > I added 2 more random overwrite modes in the source to minimize that risk. > What do you think? A strong RNG isn't that essential, as long as you're not writing a constant pattern. I use RC4 in SFS because it's faster than most RNG's provided in compiler libraries. [/dev/urandom is used if available. This is a very good RNG] - Disk Caching Note that this is only important for FILE DELETION (srm) for free diskspace wiping the data which will be overwritten is large enough. Imagine that you overwrite a file for 50 times, you feel secure, but only one - the last overwrite - is really made. This can be the case if you use smart software caches, hardware cache-controllers - or the cachebuffers which are present on all (E)IDE and SCSI harddisks. This programs uses fsync() - it depends on your unix and hardware if this is enough. If you use an hardware cache-controller you must remove it. For the cachebuffers of the harddisks you must overwrite a file which is greater in size than the diskbuffer. You can either add data to the file until it reachs that size or you define the BLOCKSIZE definition to a size big enough Here again is en extract of an answer Peter Gutmann wrote in regard of this threat : > 2nd is the chaching problem. For msdos i flush smartdrive after every > write of a pattern, on unix I use sync. That makes it easy to compile > on all platforms, yes, but it won't flush the harddisks internal caches. > how big must be the file to force the internal cache to write to disk? > at the moment I do only full 16 kb writes to a) overwrite the whole block > and also to reach the limit of the internal cache. I don't think there's an easy answer to this. Most cheap commercial drives have tiny caches (typically 96KB or 128KB with 16KB (EIDE) or 32KB (SCSI) used by the firmware), but larger SCSI drives designed for servers and/or controllers on servers can have considerable caches. I'd say 16KB or 32KB would be reasonably safe. [secure_deletion uses 32kb, and since 2.2 enlarges this if the blocksize of the filesystem is larger] - Temporary files and disks Windows 3.x, Win95 and WinNT support virtuell memory which means that if more memory is needed, some space of the harddisk will be used. Unix does the same, using the swap space partition (and additionally swap files can be created). Some other programs do the same, especially databases. Other programs you use, f.e. a word processor etc. writes recovery and/or backup files. Those must be secure deleted too - which is a major problem if the programs delete them after the program exit. Solution, regardless of the operating system of your choice : - All disk partitions must be set write protected in some way before you want to do something which shouldn't be saved anywhere. MsDos tools are available, on unix they can be mounted readonly, Win95/WinNT : don't know if thats possible. - Install a ramdisk from which you start all applications (for Windows set the working directory on the ramdisk) and ensure that all temporary stuff, etc. points to that disk. If you can't afford ram, repartition your disks so that you get an 20+ MB diskspace either ensure a complete wiping of that partition after every session (use "sfill -v") or use a crypted filesystem (see next step). For unix you should set a ramdrive for the swap partition, and use an additional ramdisk or crypted partition for /tmp and ensure that /usr/tmp and /var/tmp point to it. If you can't afford buying ram so you don't need swap, you can use the secure swap cleaner, included in this package. With v1.8 of secure_deletion, you can also use sswap to clean your swap space after you unmounted it. - If you really need the data you produced or analysed, then create a crypted filesystem on a disk partition. For unix you can use CFS (newest version v1.4.x), for MsDos and Windows 3.x there are SFS v1.7 and SecureDrive v1.4a available. I don't know any for Win95 and WinNT - but these have special problems anyway so see the next topic : - Windows 95 and WinNT As you can see, the programs were NOT programmed for any Windows environment and this has got the following reasons : - I don't know enough about these systems to make the programs secure, also I know that they've got an internal function to flush their caches without any problems. - Special problems like in the NTFS, the WinNT Filesystem, which holds too much information on the files, so that real secure data deletion is tricky. - Windows machines swap very often - and where and how to control that - I don't know, in my opinion it would be too difficult to make it a secure system (against data recovery). So why writing a secure deletion programs when fragments of the files are everywhere on the harddisk? - Networks Before we'll discuss further matters of that topic let me put it short and straight : YOU CAN'T ENSURE SECURE DATA DELETION WHEN WORKING OVER A NETWORK YOU DO NOT *COMPLETLY* CONTROL ! Because : - The network servers and maybe even your local computer caches the data to be written. See "Disk Caching" above why this is a problem - and this one can't be solved. - How long are your files present on the server? Long enough that they are written on a backup? - A hacker or law enforcement/spies could have trojanized the server in a way that your files won't be overwritten and removed but those files are written to a special place waiting to be retrieved by them. Nearly all known network operating systems and also some firewalls can be penetrated from remote, no kidding. So don't think that you are not vulnerable. Another possiblity is that the memory of the server is surveilled and all reading/writing processes own by you or anyone copied. - Even if everything is ensured there might be still problems on high-end systems, which use f.e. Raid5 or similar redunant harddisk systems which prevent data loss by keeping copies and checksums, and you must find a way to trash those information too. The solution is easy: don't put any private and important stuff you don't want anybody to see on the network - crypt it before transfering it on a server. - Paranoia Finally two points which are for very paranoid guys. Imagine a temporary file was written by a program you used for your important files and they were deleted. You run "sfill" f.e. to clean all unused diskspace to trash all information. But another file, f.e. a config file, was written when exiting the program and parts of the temporary file are now owned and overwritten by the config file. sfill or any similar program won't trash that filearea because it's used by a file. And the data on this area can be recovered with cheap hardware. Solution : see above, "temporary files and disks" If you really care about your files that they can't be recovered you should also ensure that the "others" can't get the data by other means, f.e. by either hacking your computer or analyzing the electromagnetic/sound/wave emissions from the monitor, printer, fax and cables. Solution : pull out your network/modem cable when working and try to shield your computer (search the inet for more info on that) - But there is help Watch out. Soon there will be a new release from THC which shows how to make a Linux machine anonymous. When followed, nothing will be recoverable for someone having your harddisks. -> This was released looong ago now :-) Go the the THC website, enter the articles/papers section and look for "anonymizing unix systems". 5. COMPARISON Program secure_delete (srm) wipe wipe Version 2.1 0.2 0.56-2a Programmer van Hauser / THC Berke Durak Tom Vier Email vh@reptile.rug.ac.be bedrettin@chez.com thomassr@erols.com Standard Passes 38 35 35 More passes via cmd no no yes Fewer passes via cmd yes yes yes Good RNG yes yes yes Blocksize (larger is better) 32k 1k 0 Truncates file yes no yes Rename file/directory yes no no recursive mode yes yes yes secure recursive (link races) yes no no verbose mode yes yes yes additional wipe tools yes no no Time: 1 File, 1MB 25s 40s 26s Time: 10 Files, 10kb 12s 6s 4s (Parameters for tests) -f -fTe (needed for the fastest mode) [otherwise it needs x12 time] Why is secure_delete that fast with big files but slower with many small ones? It's fast, because it uses a big buffer for writing. It's slow because the additional security features (rename, truncating, more passes, better RNG [against wipe-0.2] and by far the biggest blocksize). It's also the only one which comes with a free diskspace wiper and a special cleaner for swap space and memory. I think the choice is easy ,-) I hope the other programmers will make their programs better too, the more good & secure & fast programs, the better. Competition helps us all. 6. LAST WORDS I hope these little utilities help those who really need them. For any bugs, ideas or ongoing discussion feel free to email me at vh@reptile.rug.ac.be using the public pgp key below. http://www.thehackerschoice.com Have fun ... van Hauser / [THC] - The Hacker's Choice Type Bits/KeyID Date User ID pub 2048/CDD6A571 1998/04/27 van Hauser / THC -----BEGIN PGP PUBLIC KEY BLOCK----- Version: 2.6.3i mQENAzVE0A4AAAEIAOzKPhKBDFDyeTvMKQ1xx6781tEdIYgrkrsUEL6VoJ8H8CIU SeXDuCVu3JlMKITD6nPMFJ/DT0iKHgnHUZGdCQEk/b1YHUYOcig1DPGsg3WeTX7L XL1M4DwqDvPz5QUQ+U+VHuNOUzgxfcjhHsjJj2qorVZ/T5x4k3U960CMJ11eOVNC meD/+c6a2FfLZJG0sJ/kIZ9HUkY/dvXDInOJaalQc1mYjkvfcPsSzas4ddiXiDyc QcKX+HAXIdmT7bjq5+JS6yspnBvIZC55tB7ci2axTjwpkdzJBZIkCoBlWsDXNwyq s70Lo3H9dcaNt4ubz5OMVIvJHFMCEtIGS83WpXEABRG0J3ZhbiBIYXVzZXIgLyBU SEMgPHZoQHJlcHRpbGUucnVnLmFjLmJlPokAlQMFEDVE0D7Kb9wCOxiMfQEBvpAD /3UCDgJs1CNg/zpLhRuUBlYsZ1kimb9cbB/ufL1I4lYM5WMyw+YfGN0p02oY4pVn CQN6ca5OsqeXHWfn7LxBT3lXEPCckd+vb9LPPCzuDPS/zYnOkUXgUQdPo69B04dl C9C1YXcZjplYso2q3NYnuc0lu7WVD0qT52snNUDkd19ciQEVAwUQNUTQDhLSBkvN 1qVxAQGRTwgA05OmurXHVByFcvDaBRMhX6pKbTiVKh8HdJa8IdvuqHOcYFZ2L+xZ PAQy2WCqeakvss9Xn9I28/PQZ+6TmqWUmG0qgxe5MwkaXWxszKwRsQ8hH+bcppsZ 2/Q3BxSfPege4PPwFWsajnymsnmhdVvvrt69grzJDm+iMK0WR33+RvtgjUj+i22X lpt5hLHufDatQzukMu4R84M1tbGnUCNF0wICrU4U503yCA4DT/1eMoDXI0BQXmM/ Ygk9bO2Icy+lw1WPodrWmg4TJhdIgxuYlNLIu6TyqDYxjA/c525cBbdqwoE+YvUI o7CN/bJN0bKg1Y/BMTHEK3mpRLLWxVMRYw== =MdzX -----END PGP PUBLIC KEY BLOCK----- secure-delete-3.1.orig/TODO0100644000175000017500000000041007750316615015756 0ustar robertlerobertlepercentage output sfill - no mktemp() just take some random data and then use twofish to generate a random stream sfill inode wipe needs to traverse the full filesystem, and perform it in every directory add wipe mode and verbose mode to sdel-mod via cmd line secure-delete-3.1.orig/config.h0100644000175000017500000000051007522311302016666 0ustar robertlerobertle#define BLOCKSIZE 32769 /* must be mod 3 = 0, should be >= 16k */ #define RANDOM_DEVICE "/dev/urandom" /* must not exist */ #define DIR_SEPERATOR '/' /* '/' on unix, '\' on dos/win */ #define FLUSH sync() /* system call to flush the disk */ #define MAXINODEWIPE 4194304 /* 22 bits */ secure-delete-3.1.orig/rm-fileutil-3.x.diff0100644000175000017500000001542507522311302020754 0ustar robertlerobertle--- src/rm.c.orig Sat Nov 23 23:11:12 1996 +++ src/rm.c Sun Jan 24 13:37:20 1999 @@ -1,3 +1,10 @@ +/* patch for rm from fileutils-3.16 to support secure data deletion + * to be activated by -s. -sss gives full erasure security (38 overwrites) + * This patch is from the secure_delete-2.0.tar.gz package available + * at http://www.thehackerschoice.com - get it, and read the documentation! + * (c)1999 by van Hauser / [THC] - The Hacker's Choice, vh@reptile.rug.ac.be + */ + /* `rm' file deletion utility for GNU. Copyright (C) 88, 90, 91, 94, 95, 1996 Free Software Foundation, Inc. @@ -21,6 +28,11 @@ #include #include #include +/* SRM BEGIN */ +#include +#include +#include +/* SRM END */ #include "system.h" #include "error.h" @@ -50,6 +62,14 @@ int yesno (); void strip_trailing_slashes (); +/* SRM BEGIN */ +#define BLOCKSIZE 32769 +#define DIR_SEPERATOR '/' +#define FLUSH sync() +#define RANDOM_DEVICE "/dev/urandom" +/* SRM END */ + +static int secure_delete (); static int clear_directory __P ((struct stat *statp)); static int duplicate_entry __P ((struct pathstack *stack, ino_t inum)); static int remove_dir __P ((struct stat *statp)); @@ -68,6 +88,12 @@ /* Path of file now being processed; extended as necessary. */ static char *pathname; +/* SRM BEGIN */ +static int secure; /* secure overwrite mode */ +static char fillbuf[BLOCKSIZE]; +static FILE *devrandom; +/* SRM END */ + /* Number of bytes currently allocated for `pathname'; made larger when necessary, but never smaller. */ static int pnsize; @@ -104,6 +130,9 @@ {"force", no_argument, NULL, 'f'}, {"interactive", no_argument, NULL, 'i'}, {"recursive", no_argument, &recursive, 1}, +/* SRM BEGIN */ + {"secure_delete", no_argument, NULL, 's'}, +/* SRM END */ {"verbose", no_argument, &verbose, 1}, {"help", no_argument, &show_help, 1}, {"version", no_argument, &show_version, 1}, @@ -125,8 +154,8 @@ = unlink_dirs = 0; pnsize = 256; pathname = xmalloc (pnsize); - - while ((c = getopt_long (argc, argv, "dfirvR", long_opts, (int *) 0)) != EOF) + + while ((c = getopt_long (argc, argv, "dfirsvR", long_opts, (int *) 0)) != EOF) { switch (c) { @@ -147,6 +176,11 @@ case 'R': recursive = 1; break; +/* SRM BEGIN */ + case 's': + secure++; + break; +/* SRM END */ case 'v': verbose = 1; break; @@ -198,6 +232,116 @@ exit (err > 0); } +/* SRM BEGIN */ + +void fill_buf(char pattern[3]) { + int loop; + int where; + for (loop = 0; loop < (BLOCKSIZE / 3); loop++) { + where = loop * 3; + fillbuf[where] = pattern[0]; + fillbuf[where+1] = pattern[1]; + fillbuf[where+2] = pattern[2]; + } +} +void random_buf() { + int loop; + if (devrandom != NULL) + for (loop = 0; loop < BLOCKSIZE; loop++) + fillbuf[loop] = (unsigned char) (256.0*rand()/(RAND_MAX+1.0)); + else + fread(&fillbuf, BLOCKSIZE, 1, devrandom); +} + +/* overwriting the file several times */ + + static int + secure_delete (delfile) + char *delfile; + { + unsigned char write_modes[27][3] = { + {"\x55\x55\x55"}, {"\xaa\xaa\xaa"}, {"\x92\x49\x24"}, {"\x49\x24\x92"}, + {"\x24\x92\x49"}, {"\x00\x00\x00"}, {"\x11\x11\x11"}, {"\x22\x22\x22"}, + {"\x33\x33\x33"}, {"\x44\x44\x44"}, {"\x55\x55\x55"}, {"\x66\x66\x66"}, + {"\x77\x77\x77"}, {"\x88\x88\x88"}, {"\x99\x99\x99"}, {"\xaa\xaa\xaa"}, + {"\xbb\xbb\xbb"}, {"\xcc\xcc\xcc"}, {"\xdd\xdd\xdd"}, {"\xee\xee\xee"}, + {"\xff\xff\xff"}, {"\x92\x49\x24"}, {"\x49\x24\x92"}, {"\x24\x92\x49"}, + {"\x6d\xb6\xdb"}, {"\xb6\xdb\x6d"}, {"\xdb\x6d\xb6"} + }; + unsigned char std_array[3] = "\xff\xff\xff"; + FILE *f; + int file; + unsigned long writes; + unsigned long counter; + unsigned long filesize; + struct stat filestat; + int turn; + int result; + unsigned char ch; + char newname[512]; + +/* open the file, get the filesize, calculate the numbers of needed writings */ + if (lstat(delfile, &filestat)) + return 1; + if (! S_ISREG(filestat.st_mode)) + return 1; + if ((f = fopen(delfile, "r+b")) == NULL) + return 1; + filesize = filestat.st_size; + writes = (1 + (filesize / BLOCKSIZE)); + file=fileno(f); + (void) setvbuf(stdout, NULL, _IONBF, 0); + devrandom = fopen(RANDOM_DEVICE, "r"); + + if (verbose) + printf(" "); + + + if (secure > 1) { + fill_buf(std_array); + for (counter=1; counter<=writes; counter++) + fwrite(&fillbuf, 1, BLOCKSIZE, f); + if (verbose) + printf("*"); + fflush(f); + fsync(file); + } + +/* do the overwriting stuff */ + for (turn=0; turn<=36; turn++) { + rewind(f); + if ((secure < 3) && (turn > 0)) break; + if ((turn>=5) && (turn<=31)) { + fill_buf(write_modes[turn-5]); + for (counter=1; counter<=writes; counter++) + fwrite(&fillbuf, 1, BLOCKSIZE, f); + } else { + for (counter=1; counter<=writes; counter++) { + random_buf(); + fwrite(&fillbuf, 1, BLOCKSIZE, f); + } + } + fflush(f); + if (fsync(file) < 0) + FLUSH; + if (verbose) + printf("*"); + } + (void) fclose(f); + (void) fclose(devrandom); +/* Hard Flush -> Force cached data to be written to disk */ + FLUSH; +/* open + truncating the file, so an attacker doesn't know the diskblocks */ + if ((file = open(delfile, O_WRONLY | O_TRUNC)) >= 0) + close(file); + if (verbose) + printf(" wiped\n"); + return 0; +} + +/* SRM END */ + /* Remove file or directory `pathname' after checking appropriate things. Return 0 if `pathname' is removed, 1 if not. */ @@ -264,8 +408,16 @@ return 1; } - if (verbose) - printf ("%s\n", pathname); + if (verbose) { + printf ("%s", pathname); + if (secure == 0) + printf ("\n"); + } + +/* SRM BEGIN */ + if (secure) + secure_delete(pathname); +/* SRM END */ if (unlink (pathname) && (errno != ENOENT || !ignore_missing_files)) { @@ -536,11 +688,13 @@ -f, --force ignore nonexistent files, never prompt\n\ -i, --interactive prompt before any removal\n\ -r, -R, --recursive remove the contents of directories recursively\n\ + -s, --secure_delete secure overwrite (-sss for full security)\n\ -v, --verbose explain what is being done\n\ --help display this help and exit\n\ --version output version information and exit\n\ ")); puts (_("\nReport bugs to fileutils-bugs@gnu.ai.mit.edu")); + puts (_("\nWith secure_delete patch by van Hauser ")); } exit (status); } secure-delete-3.1.orig/sdel-lib.c0100644000175000017500000002542307655463767017157 0ustar robertlerobertle/* Secure Delete Library - by van Hauser / [THC], vh@thc.org * * Secure Delete Library provides the following public functions: * * void sdel_init(int secure_random) * Initializiation function for sdel_overwrite. It needs to be called * once at program start, not for each file to be overwritten. * Options: * secure_random - if != 0 defines that the secure random number * generator RANDOM_DEVICE should be used * * void sdel_finnish() * Clean-up function, if sdel_init() was called in a program. It needs * only to be called at the end of the program. * * int sdel_overwrite(int mode, int fd, long start, unsigned long bufsize, * unsigned long length, int zero) * This is the heart of sdel-lib. It overwrites the target file * descriptor securely to make life hard even for the NSA. * Read the next paragraph for the techniques. * Options: * mode = 0 - once overwrite with random data * 1 - once overwrite with 0xff, then once with random data * 2 - overwrite 38 times with special values * fd - filedescriptor of the target to overwrite * start - where to start overwriting. 0 is from the beginning * this is needed for wiping swap spaces etc. * bufsize - size of the buffer to use for overwriting, depends * on the filesystem * length - amount of data to write (file size), 0 means until * an error occurs * zero - last wipe is zero bytes, not random * returns 0 on success, -1 on errors * * int sdel_unlink(char *filename, int directory, int truncate, int slow) * First truncates the file (if it is not a directory), then renames it * and finally rmdir/unlinks the target. * Options: * filename - filename/directory to unlink/rmdir * directory - if != 0, it is a directory * truncate - if != 0, it truncates the file * slow - is either O_SYNC (see open(2)) or 0 * returns 0 on success, -1 on errors * * Compiles clean on OpenBSD, Linux, Solaris, AIX and I guess all others. * */ /* * For security reasons full 32kb blocks are written so that the whole block * on which the file(s) live are overwritten. (change #define #BLOCKSIZE) * Standard mode is a real security wipe for 38 times, flushing * the caches after every write. The wipe technique was proposed by Peter * Gutmann at Usenix '96 and includes 10 random overwrites plus 28 special * defined characters. Take a look at the paper of him, it's really worth * your time. * * Read the manual for limitations. */ #include "sdel-lib.h" // // STARTING FUNCTIONS // void __sdel_fill_buf(char pattern[3], unsigned long bufsize, char *buf) { int loop; int where; for (loop = 0; loop < (bufsize / 3); loop++) { where = loop * 3; *buf++ = pattern[0]; *buf++ = pattern[1]; *buf++ = pattern[2]; } } void __sdel_random_buf(unsigned long bufsize, char *buf) { int loop; if (devrandom == NULL) for (loop = 0; loop < bufsize; loop++) *buf++ = (unsigned char) (256.0*rand()/(RAND_MAX+1.0)); else fread(buf, bufsize, 1, devrandom); } void __sdel_random_filename(char *filename) { int i; for (i = strlen(filename) - 1; (filename[i] != DIR_SEPERATOR) && (i >= 0); i--) if (filename[i] != '.') /* keep dots in the filename */ filename[i] = 97+(int) ((int) ((256.0 * rand()) / (RAND_MAX + 1.0)) % 26); } void sdel_init(int secure_random) { (void) setvbuf(stdout, NULL, _IONBF, 0); (void) setvbuf(stderr, NULL, _IONBF, 0); if (BLOCKSIZE<16384) fprintf(stderr, "Programming Warning: in-compiled blocksize is <16k !\n"); if (BLOCKSIZE % 3 > 0) fprintf(stderr, "Programming Error: in-compiled blocksize is not a multiple of 3!\n"); srand( (getpid()+getuid()+getgid()) ^ time(0) ); devrandom = NULL; #ifdef RANDOM_DEVICE if (secure_random) { if ((devrandom = fopen(RANDOM_DEVICE, "r")) != NULL) if (verbose) printf("Using %s for random input.\n", RANDOM_DEVICE); } #endif __internal_sdel_init = 1; } void sdel_finnish() { if (devrandom != NULL) { fclose(devrandom); devrandom = NULL; } if (! __internal_sdel_init) { fprintf(stderr, "Programming Error: sdel-lib was not initialized before calling sdel_finnish().\n"); return; } __internal_sdel_init = 0; } /* * secure_overwrite function parameters: * mode = 0 : once overwrite with random data * 1 : once overwrite with 0xff, then once with random data * 2 : overwrite 38 times with special values * fd : filedescriptor of the target to overwrite * start : where to start overwriting. 0 is from the beginning * bufsize : size of the buffer to use for overwriting, depends on the filesystem * length : amount of data to write (file size), 0 means until an error occurs * * returns 0 on success, -1 on errors */ int sdel_overwrite(int mode, int fd, long start, unsigned long bufsize, unsigned long length, int zero) { unsigned long writes; unsigned long counter; int turn; int last = 0; char buf[65535]; FILE *f; if (! __internal_sdel_init) fprintf(stderr, "Programming Error: sdel-lib was not initialized before sdel_overwrite().\n"); if ((f = fdopen(fd, "r+b")) == NULL) return -1; /* calculate the number of writes */ if (length > 0) writes = (1 + (length / bufsize)); else writes = 0; /* do the first overwrite */ if (start == 0) rewind(f); else if (fseek(f, start, SEEK_SET) != 0) return -1; if (mode != 0 || zero) { if (mode == 0) __sdel_fill_buf(std_array_00, bufsize, buf); else __sdel_fill_buf(std_array_ff, bufsize, buf); if (writes > 0) for (counter=1; counter<=writes; counter++) fwrite(&buf, 1, bufsize, f); // dont care for errors else do {} while(fwrite(&buf, 1, bufsize, f) == bufsize); if (verbose) printf("*"); fflush(f); if (fsync(fd) < 0) FLUSH; if (mode == 0) return 0; } /* do the rest of the overwriting stuff */ for (turn = 0; turn <= 36; turn++) { if (start == 0) rewind(f); else if (fseek(f, start, SEEK_SET) != 0) return -1; if ((mode < 2) && (turn > 0)) break; if ((turn >= 5) && (turn <= 31)) { __sdel_fill_buf(write_modes[turn-5], bufsize, buf); if (writes > 0) for (counter = 1; counter <= writes; counter++) fwrite(&buf, 1, bufsize, f); // dont care for errors else do {} while(fwrite(&buf, 1, bufsize, f) == bufsize); } else { if (zero && ((mode == 2 && turn == 36) || mode == 1)) { last = 1; __sdel_fill_buf(std_array_00, bufsize, buf); } if (writes > 0) { for (counter = 1; counter <= writes; counter++) { if (! last) __sdel_random_buf(bufsize, buf); fwrite(&buf, 1, bufsize, f); // dont care for errors } } else { do { if (! last) __sdel_random_buf(bufsize, buf); } while (fwrite(&buf, 1, bufsize, f) == bufsize); // dont care for errors } } fflush(f); if (fsync(fd) < 0) FLUSH; if (verbose) printf("*"); } (void) fclose(f); /* Hard Flush -> Force cached data to be written to disk */ FLUSH; return 0; } /* * secure_unlink function parameters: * filename : the file or directory to remove * directory : defines if the filename poses a directory * truncate : truncate file * slow : do things slowly, to prevent caching * * returns 0 on success, -1 on errors. */ int sdel_unlink(char *filename, int directory, int truncate, int slow) { int fd; int turn = 0; int result; char newname[strlen(filename) + 1]; struct stat filestat; /* open + truncating the file, so an attacker doesn't know the diskblocks */ if (! directory && truncate) if ((fd = open(filename, O_WRONLY | O_TRUNC | slow)) >= 0) close(fd); /* Generate random unique name, renaming and deleting of the file */ strcpy(newname, filename); // not a buffer overflow as it has got the exact length do { __sdel_random_filename(newname); if ((result = lstat(newname, &filestat)) >= 0) turn++; } while ((result >= 0) && (turn <= 100)); if (turn <= 100) { result = rename(filename, newname); if (result != 0) { fprintf(stderr, "Warning: Couldn't rename %s - ", filename); perror(""); strcpy(newname, filename); } } else { fprintf(stderr,"Warning: Couldn't find a free filename for %s!\n",filename); strcpy(newname, filename); } if (directory) { result = rmdir(newname); if (result) { printf("Warning: Unable to remove directory %s - ", filename); perror(""); (void) rename(newname, filename); } else if (verbose) printf("Removed directory %s ...", filename); } else { result = unlink(newname); if (result) { printf("Warning: Unable to unlink file %s - ", filename); perror(""); (void) rename(newname, filename); } else if (verbose) printf(" Removed file %s ...", filename); } if (result != 0) return -1; return 0; } void sdel_wipe_inodes(char *loc, char **array) { char *template = malloc(strlen(loc) + 16); int i = 0; int fail = 0; int fd; if (verbose) printf("Wiping inodes ..."); array = malloc(MAXINODEWIPE * sizeof(template)); strcpy(template, loc); if (loc[strlen(loc) - 1] != '/') strcat(template, "/"); strcat(template, "xxxxxxxx.xxx"); while(i < MAXINODEWIPE && fail < 5) { __sdel_random_filename(template); if (open(template, O_CREAT | O_EXCL | O_WRONLY, 0600) < 0) fail++; else { array[i] = malloc(strlen(template)); strcpy(array[i], template); i++; } } FLUSH; if (fail < 5) { fprintf(stderr, "Warning: could not wipe all inodes!\n"); } array[i] = NULL; fd = 0; while(fd < i) { unlink(array[fd]); free(array[fd]); fd++; } free(array); array = NULL; FLUSH; if (verbose) printf(" Done ... "); } secure-delete-3.1.orig/sdel-lib.h0100644000175000017500000000212307634200776017136 0ustar robertlerobertle#ifndef _SDEL_LIB_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" // // STARTING CONSTANTS AND VARIABLES // unsigned char write_modes[27][3] = { {"\x55\x55\x55"}, {"\xaa\xaa\xaa"}, {"\x92\x49\x24"}, {"\x49\x24\x92"}, {"\x24\x92\x49"}, {"\x00\x00\x00"}, {"\x11\x11\x11"}, {"\x22\x22\x22"}, {"\x33\x33\x33"}, {"\x44\x44\x44"}, {"\x55\x55\x55"}, {"\x66\x66\x66"}, {"\x77\x77\x77"}, {"\x88\x88\x88"}, {"\x99\x99\x99"}, {"\xaa\xaa\xaa"}, {"\xbb\xbb\xbb"}, {"\xcc\xcc\xcc"}, {"\xdd\xdd\xdd"}, {"\xee\xee\xee"}, {"\xff\xff\xff"}, {"\x92\x49\x24"}, {"\x49\x24\x92"}, {"\x24\x92\x49"}, {"\x6d\xb6\xdb"}, {"\xb6\xdb\x6d"}, {"\xdb\x6d\xb6"} }; unsigned char std_array_ff[3] = "\xff\xff\xff"; unsigned char std_array_00[3] = "\x00\x00\x00"; FILE *devrandom = NULL; int verbose = 0; int __internal_sdel_init = 0; #define _SDEL_LIB_H_ #endif secure-delete-3.1.orig/sdel-mod.c0100644000175000017500000002502307750047764017153 0ustar robertlerobertle/* * sdel-mod.c (c)2003 by van Hauser / THC and Frank Heimann * */ //#define _DEBUG_ #include #include #if defined(MODVERSIONS) #include #endif #include #include #include #include #include #include #include #include #define LOG(x,y) printk( KERN_DEBUG "sdel-mod: " x,y ) #if !defined SEEK_SET #define SEEK_SET 0 #endif extern void *sys_call_table[]; int (*unlink_orig)(const char *filename); int (*lstat_orig)( const char *file_name, struct stat *buf ); int (*fstat_orig)(int filedes, struct stat *buf); int (*rename_orig)(const char *oldpath, const char *newpath); int (*open_orig)(const char *filename, int flags); int (*close_orig)(int fd); ssize_t (*read_orig)(int fd, void *buf, size_t count); ssize_t (*write_orig)(int fd, const void *buf, size_t count); int (*sync_orig)(void); int (*fsync_orig)(int fd); off_t (*lseek_orig)(int fildes, off_t offset, int whence); int (*setrlimit_orig)(int resource, const struct rlimit *rlim); int (*brk_orig)(void *end_data_segment); char* (*getcwd_orig)(char *buf, size_t size); void cleanup_module(void); unsigned char write_modes[27][3] = { {"\x55\x55\x55"}, {"\xaa\xaa\xaa"}, {"\x92\x49\x24"}, {"\x49\x24\x92"}, {"\x24\x92\x49"}, {"\x00\x00\x00"}, {"\x11\x11\x11"}, {"\x22\x22\x22"}, {"\x33\x33\x33"}, {"\x44\x44\x44"}, {"\x55\x55\x55"}, {"\x66\x66\x66"}, {"\x77\x77\x77"}, {"\x88\x88\x88"}, {"\x99\x99\x99"}, {"\xaa\xaa\xaa"}, {"\xbb\xbb\xbb"}, {"\xcc\xcc\xcc"}, {"\xdd\xdd\xdd"}, {"\xee\xee\xee"}, {"\xff\xff\xff"}, {"\x92\x49\x24"}, {"\x49\x24\x92"}, {"\x24\x92\x49"}, {"\x6d\xb6\xdb"}, {"\xb6\xdb\x6d"}, {"\xdb\x6d\xb6"} }; unsigned char std_array[3] = "\xff\xff\xff"; #define DIR_SEPERATOR '/' #define FLUSH sync_orig() #define BLOCKSIZE 32769 #ifndef O_SYNC #ifdef O_FSYNC #define O_SYNC O_FSYNC #else #define O_SYNC 0 #endif #endif #define RAND_MAX 2147483647 unsigned long bufsize = BLOCKSIZE; char buf[BLOCKSIZE]; int slow = O_SYNC; void __sdel_random_filename(char *filename) { int i; unsigned char rand; for (i = strlen(filename) - 1; (filename[i] != '/') && (i >= 0); i--) if (filename[i] != '.') { /* keep dots in the filename */ get_random_bytes(&rand, 1); filename[i] = 97 + (int) ((int) rand % 26); } } /* * secure_unlink function parameters: * filename : the file or directory to UNLINK * * returns 0 on success, -1 on errors. */ static int sdel_unlink(const char *filename) { int turn = 0; int result; char newname[strlen(filename) + 1]; // just in kernelspace char *ul_newname=0; // for memory in userspace, syscalls need all userspace unsigned long mmm=0; // for storing old memory pointer struct stat filestat; /* Generate random unique name, renaming and deleting of the file */ strcpy(newname, filename); // not a buffer overflow as it has got the exact length do { __sdel_random_filename(newname); if ((result = lstat_orig(newname, &filestat)) >= 0) turn++; } while ((result >= 0) && (turn <= 100)); if (turn <= 100) { mmm = current->mm->brk; if( brk_orig((void*) mmm + strlen(filename) + 1 ) < 0) { LOG( "Can't allocate userspace mem %s","\n" ); return (-ENOMEM); } ul_newname = (void*)(mmm + 2); // set variable to new allocates userspace mem copy_to_user(ul_newname,newname,strlen(newname)); result = rename_orig(filename, ul_newname); if (result != 0) { LOG("Warning: Couldn't rename %s\n", filename); strcpy(newname, filename); } } else { LOG("Warning: Couldn't find a free filename for %s\n",filename); strcpy(newname, filename); } result = unlink_orig(ul_newname); if (result) { LOG("Warning: Unable to unlink file %s\n", filename); (void) rename_orig(newname, filename); } #if defined _DEBUG_ else LOG("Renamed and unlinked file %s\n", filename); #endif if (result != 0) return -1; if( brk_orig((void*) mmm) < 0 ) return (-ENOMEM); return 0; } static void fill_buf(char pattern[3]) { int loop; int where; for (loop = 0; loop < (bufsize / 3); loop++) { where = loop * 3; buf[where] = pattern[0]; buf[where+1] = pattern[1]; buf[where+2] = pattern[2]; } } static void random_buf(char* ul_buf) { get_random_bytes(buf, bufsize); copy_to_user(ul_buf, buf, bufsize); } static int smash_it(const char *ul_filename, const char*kl_filename, struct stat kl_filestat, int mode) { unsigned long writes; unsigned long counter; unsigned long filesize; struct stat kl_controlstat; struct stat *tmp; int turn; int i; int kl_file; char *kl_newname=0; unsigned long mmm; char *ul_buf = NULL; #if defined _DEBUG_ LOG("smashing with mode %d\n", mode); #endif /* if the blocksize on the filesystem is bigger than the on compiled with, enlarge! */ if (kl_filestat.st_blksize > bufsize) { if (kl_filestat.st_blksize > ( BLOCKSIZE - 3 )) bufsize = BLOCKSIZE; else bufsize = (((kl_filestat.st_blksize / 3) + 1) * 3); } /* open the file for writing in sync. mode */ if ((kl_file = open_orig(ul_filename, O_RDWR)) < 0) { LOG("open failed %d\n", kl_file); return kl_file; } // LOG("open %s\n", "ok"); mmm = current->mm->brk; if(brk_orig((void*) mmm + sizeof(struct stat)) < 0) { LOG("brk %s\n", "failed"); return (-ENOMEM); } tmp = (void*)(mmm + 2); // LOG( "brk %s\n","ok" ); // do we need to check for races? hmmm if ((i = fstat_orig(kl_file, tmp)) < 0) { LOG("fstat failed %d\n", i); if (brk_orig((void*) mmm) < 0) return (-ENOMEM); return i; } // LOG( "brk %s\n","ok" ); copy_from_user(&kl_controlstat, tmp, sizeof(struct stat)); if (brk_orig((void*) mmm) < 0) return (-ENOMEM); if ((kl_filestat.st_dev != kl_controlstat.st_dev) || (kl_filestat.st_ino != kl_controlstat.st_ino) || (! S_ISREG(kl_controlstat.st_mode))) { LOG( "RACE - CONDITION %s\n"," " ); return (-EIO); } /* calculate the number of writes */ filesize = kl_filestat.st_size; writes = (1 + (filesize / bufsize)); #if defined _DEBUG_ LOG("start overwriting in mode %d\n", mode); #endif // if (mode != 0) { fill_buf(std_array); mmm = current->mm->brk; if (brk_orig((void*) mmm + bufsize) < 0) { LOG("brk %s\n", "failed"); return (-ENOMEM); } ul_buf = (void*)(mmm + 2); // LOG("brk %s\n", "ok"); copy_to_user(ul_buf, buf, bufsize); for (counter=1; counter<=writes; counter++) { if ((i = write_orig(kl_file, ul_buf, bufsize)) < 0) LOG("write failure: %d\n", i); } if (fsync_orig(kl_file) < 0) FLUSH; // } /* do the overwriting stuff */ if (mode > 0) { for (turn=0; turn<=36; turn++) { if (lseek_orig(kl_file, SEEK_SET, 0) < 0) LOG( "lseek %s\n", "failed"); if ((mode < 2) && (turn > 0)) break; if ((turn>=5) && (turn<=31)) { #if defined _DEBUG_ LOG("pattern o/w%s\n", " "); #endif fill_buf(write_modes[turn-5]); copy_to_user(ul_buf, buf, bufsize); for (counter=1; counter<=writes; counter++) write_orig(kl_file, ul_buf, bufsize); } else { #if defined _DEBUG_ LOG("random o/w%s\n", " "); #endif for (counter=1; counter<=writes; counter++) { random_buf(ul_buf); write_orig(kl_file,ul_buf,bufsize); } } if (fsync_orig(kl_file) < 0) FLUSH; } } if (brk_orig((void*) mmm) < 0) return (-ENOMEM); /* Hard Flush -> Force cached data to be written to disk */ FLUSH; /* open + truncating the file, so an attacker doesn't know the diskblocks */ if ((kl_file = open_orig(ul_filename, O_WRONLY | O_TRUNC | slow)) >= 0) close_orig(kl_file); if (brk_orig((void*) mmm) < 0) return (-ENOMEM); kfree(kl_newname); return 0; } int wipefile(const char *ul_filename) { struct stat kl_filestat; struct stat *ul_fs; int ret; int size; char *kl_filename; unsigned long mmm; lock_kernel(); MOD_INC_USE_COUNT; kl_filename = getname(ul_filename); ret = PTR_ERR(kl_filename); if (IS_ERR(kl_filename)) { LOG("getname %s\n", "failed"); putname(kl_filename); return ret; } // LOG("getname %s\n", "ok"); size = strlen(kl_filename); if (size <= PATH_MAX) { mmm = current->mm->brk; ret = brk_orig((void*) mmm + sizeof(struct stat)); ul_fs = (void*)(mmm + 2); if ((ret = (*lstat_orig)(ul_filename, ul_fs)) < 0) { // LOG("lstat returned %d\n", ret); // LOG("on: %s\n", kl_filename); if (brk_orig((void*) mmm) < 0) return (-ENOMEM); } else { copy_from_user(&kl_filestat, ul_fs, sizeof(struct stat)); if (brk_orig((void*) mmm ) < 0) return (-ENOMEM); if (S_ISREG(kl_filestat.st_mode) && ret >= 0 && kl_filestat.st_nlink == 1 && kl_filestat.st_size > 0){ #if defined _DEBUG_ LOG("wiping file %s\n", kl_filename); #endif ret = smash_it(ul_filename, kl_filename, kl_filestat, 0); // ret = smash_it(ul_filename, kl_filename, kl_filestat, 2); } else { // LOG("ret = %d ",ret); // LOG(" kl_filestat.st_nlink = %d ",kl_filestat.st_nlink); // LOG("kl_filestat.st_size =%ld\n",kl_filestat.st_size); } #if defined _DEBUG_ LOG("unlinking %s\n", kl_filename); #endif ret = sdel_unlink(ul_filename); } } else { LOG("Filename too long %s\n", kl_filename); ret = (-ENAMETOOLONG); } putname(kl_filename); MOD_DEC_USE_COUNT; unlock_kernel(); return ret; } int init_module(void) { MODULE_LICENSE("GPL"); printk(KERN_INFO "Loading sdel-mod - (c) 2003 by van Hauser / THC and Frank Heimann \n"); lstat_orig = sys_call_table[ SYS_lstat ]; fstat_orig = sys_call_table[ SYS_fstat ]; rename_orig = sys_call_table[ SYS_rename ]; open_orig = sys_call_table[ SYS_open ]; close_orig = sys_call_table[ SYS_close ]; read_orig = sys_call_table[ SYS_read ]; write_orig = sys_call_table[ SYS_write ]; sync_orig = sys_call_table[ SYS_sync ]; fsync_orig = sys_call_table[ SYS_fsync ]; lseek_orig = sys_call_table[ SYS_lseek ]; setrlimit_orig = sys_call_table[ SYS_setrlimit ]; brk_orig = sys_call_table[ SYS_brk ]; getcwd_orig = sys_call_table[ SYS_getcwd ]; #if defined _DEBUG_ LOG( "syscalls %s\n","ok" ); #endif unlink_orig = sys_call_table[ SYS_unlink ]; sys_call_table[ SYS_unlink ] = wipefile; #if defined _DEBUG_ LOG( "unlinkpointer %s\n","ok" ); #endif return 0; } void cleanup_module(void) { printk(KERN_INFO "Removing sdel-mod - (c) 2003 by van Hauser / THC and Frank Heimann \n"); sys_call_table[ SYS_unlink ] = unlink_orig; } secure-delete-3.1.orig/sdel.h0100644000175000017500000000156107750025634016374 0ustar robertlerobertle#ifndef _SDEL_H #include "config.h" #define VERSION "v3.1" #define AUTHOR "van Hauser / THC" #define EMAIL "vh@thc.org" #define WEB "http://www.thc.org" #ifndef O_SYNC #ifdef O_FSYNC #define O_SYNC O_FSYNC #else #define O_SYNC 0 #endif #endif #ifndef O_LARGEFILE #define O_LARGEFILE 0 #endif char *prg; extern int verbose; extern void sdel_init(int secure_random); extern void sdel_finnish(); extern int sdel_overwrite(int mode, int fd, long start, unsigned long bufsize, unsigned long length, int zero); extern int sdel_unlink(char *filename, int directory, int truncate, int slow); extern void sdel_wipe_inodes(char *loc, char **array); extern void __sdel_fill_buf(char pattern[3], unsigned long bufsize, char *buf); extern void __sdel_random_buf(unsigned long bufsize, char *buf); extern void __sdel_random_filename(char *filename); #define _SDEL_H #endif secure-delete-3.1.orig/secure_delete.doc0100644000175000017500000004576307655463624020620 0ustar robertlerobertle S E C U R E D E L E T E (c) 1997-2003 by van Hauser / THC http://www.thc.org " Does the average person really need this kind of security? I say yes. [...] He may be living in a country that does not respect the rights of privacy of it's citizens. He may be doing something that he feels shouldn't be illegal, but is. Whatever his reasons, his data and communications are personal, private, and nobody's business but his own. " Bruce Schneier in the Preface of his book "Applied Cryptography" " A Puritan is someone who is deathly afraid that someone, somewhere, is having fun. " Unknown 1. INTRODUCTION 2. HOW THESE PROGRAMS WORK 3. COMMANDLINE OPTIONS 4. LIMITATIONS 5. COMPARISON 6. LAST WORDS 1. INTRODUCTION Years ago, people using the old msdos, simply deleted a file by doing "del filename" and thought the the erase was forever. Then Norton's undelete was released and everyone could get back the files most of the time. After that people who wanted to keep people of their deleted files erased them by overwriting the file with random or 0x00 bytes and felt secure. In 1996 Peter Gutmann published a paper called "Secure Deletion of Data from Magnetic and Solid-State Memory" at the 6th Usenix Security Symposium, where he pointed out that the data could even be recovered if you overwrote them triple times and more - using cheap equipment for about 1000-2500$. The three utilities presented here try to cover this new area of secure deletion and prevent file recovery. This release includes the full paper of Peter Gutmann from the 6th Usenix Security Proceeding (usenix6-gutmann.doc). The four utilities do the following : srm does secure deletion of files. sfill does a secure overwriting of the unused diskspace on the harddisk. sswap does a secure overwriting and cleaning of the swap filesystem. (note that sswap was only tested on linux so far. you must unmount your swap first!) smem does a secure overwriting of unused memory (RAM) For Linux, there's a diff (rm.diff) patch for rm.c which puts the overwriting feature into it. (You need the fileutil sources) 2. HOW THESE PROGRAMS WORK The deletion process is as follows: 1. The overwriting procedure (in the secure mode) does a 38 times overwriting. After each pass, the disk cache is flushed. 2. truncating the file, so that an attacker don't know which diskblocks belonged to the file. 3. renaming of the file, so that an attacker can't draw any conclusion from the filename on the contents of the deleted file. 4. finally deleting the file (unlink). Note that with v2.0 all secure_delete utilities work in secure mode (38 special passes). To lower the security and make it faster, you may add -l (onf 0xff pass, one random pass) or -ll (one random pass) to the parameters. The secure overwrite mode works that way: 1x overwrite with 0xff 5x random passes 27x overwriting with special values to make the recovery from MFM and RLL encoded harddisks hard/impossible - see Gutmann's paper on that which is also included. 5x random passes Some statistics : in 1 second you can approx. overwrite 1 to 2 MB of data, depending on your harddrive performance. In totally insecure mode, a 100 MB file/free-disk-space takes approx. 15 seconds, and in the totally secure mode approx. 60 minutes. 3. COMMANDLINE OPTIONS Here are the commandline options: srm [-d] [-f] [-l] [-l] [-v] [-z] file [file] [another file] [etc.] sfill [-i] [-I] [-f] [-l] [-l] [-v] [-z] target-directory sswap [-f] [-l] [-l] [-v] [-z] /dev/of_swap_filesystem smem [-f] [-l] [-l] [-v] The -s options are depricated now, and will be ignored. -d don't delete the dot special files "." and ".." on the commandline (only srm) -i wipe only free inode space, not free disk space on the filesystem (only sfill) -I wipe only free disk space, not free inode space on the filesystem (only sfill) -f fast writes without O_SYNC and sync() between writes. Much faster but less secure. -l lessens the security. Only one random plus one pass with 0xff are written. -l a seconds time as parameter switches into the insecurest mode, it overwrites the file only once with 0xff. -v turn verbose mode on. -z last wipe mode writes zeros instead of random data file file to delete. Wildcards are of course allowed. For unix: you need write permissions. For msdos: It may be hidden, system, readonly etc. we don't care. target-directory target is a directory in the filesystem to write to. swap_filesystem your swap filesystem. Unmount it first!! only tested on linux Options may be applied like "-lfv", "-l -f -v" or a mix. Note: If you use a gnu-compactible linux, you can use the patch rm.diff included in the package to put the features from srm into your normal rm. Just enter your fileutils-3.16 directory, type "patch < rm.diff" and then "make". You need at least one -s switch to activate (1 overwrite). Note that -sss is needed for full security. 4. LIMITATIONS This section discusses limitations of the programs presented and general problems and threats of secure data deletion - and how to handle them. As you can see from the sourcecode, these are very small and generic programs. That means that they aren't perfect and doesn't cover any aspect of secure data deletion. Please read this section carefully to learn against which problems it does NOT help. - Random Number Generation Since v2.0, secure_deletion uses the /dev/urandom as a random source if available. This should fix this problem. However, for completeness, read on: The numbers which WERE generated by the programs were far away being "real" random. Standard random number generators are used and they are easy to predict. This is a major risk if you are using them for online crypting purpose, for the purpose of overwriting it is nearly enough. However, I added two extra random overwrites to be sure. A solution for paranoid people : change the random number generator in the programs to something you trust. Here's an extract from an answer Peter Gutmann wrote me as I asked him about that problem : > 1st to be compactible with all platforms i didn t use a crypt-random > library, I use a simple (256*rand()/(RAND_MAX+1.0)) seeded 1st time with > the pid. For every byte to overwrite the file I call that function. > I know that this random stuff is not "secure" for online crypting, > but is it enough for deletion? > I added 2 more random overwrite modes in the source to minimize that risk. > What do you think? A strong RNG isn't that essential, as long as you're not writing a constant pattern. I use RC4 in SFS because it's faster than most RNG's provided in compiler libraries. [/dev/urandom is used if available. This is a very good RNG] - Disk Caching Note that this is only important for FILE DELETION (srm) for free diskspace wiping the data which will be overwritten is large enough. Imagine that you overwrite a file for 50 times, you feel secure, but only one - the last overwrite - is really made. This can be the case if you use smart software caches, hardware cache-controllers - or the cachebuffers which are present on all (E)IDE and SCSI harddisks. This programs uses fsync() - it depends on your unix and hardware if this is enough. If you use an hardware cache-controller you must remove it. For the cachebuffers of the harddisks you must overwrite a file which is greater in size than the diskbuffer. You can either add data to the file until it reachs that size or you define the BLOCKSIZE definition to a size big enough Here again is en extract of an answer Peter Gutmann wrote in regard of this threat : > 2nd is the chaching problem. For msdos i flush smartdrive after every > write of a pattern, on unix I use sync. That makes it easy to compile > on all platforms, yes, but it won't flush the harddisks internal caches. > how big must be the file to force the internal cache to write to disk? > at the moment I do only full 16 kb writes to a) overwrite the whole block > and also to reach the limit of the internal cache. I don't think there's an easy answer to this. Most cheap commercial drives have tiny caches (typically 96KB or 128KB with 16KB (EIDE) or 32KB (SCSI) used by the firmware), but larger SCSI drives designed for servers and/or controllers on servers can have considerable caches. I'd say 16KB or 32KB would be reasonably safe. [secure_deletion uses 32kb, and since 2.2 enlarges this if the blocksize of the filesystem is larger] - Temporary files and disks Windows 3.x, Win95 and WinNT support virtuell memory which means that if more memory is needed, some space of the harddisk will be used. Unix does the same, using the swap space partition (and additionally swap files can be created). Some other programs do the same, especially databases. Other programs you use, f.e. a word processor etc. writes recovery and/or backup files. Those must be secure deleted too - which is a major problem if the programs delete them after the program exit. Solution, regardless of the operating system of your choice : - All disk partitions must be set write protected in some way before you want to do something which shouldn't be saved anywhere. MsDos tools are available, on unix they can be mounted readonly, Win95/WinNT : don't know if thats possible. - Install a ramdisk from which you start all applications (for Windows set the working directory on the ramdisk) and ensure that all temporary stuff, etc. points to that disk. If you can't afford ram, repartition your disks so that you get an 20+ MB diskspace either ensure a complete wiping of that partition after every session (use "sfill -v") or use a crypted filesystem (see next step). For unix you should set a ramdrive for the swap partition, and use an additional ramdisk or crypted partition for /tmp and ensure that /usr/tmp and /var/tmp point to it. If you can't afford buying ram so you don't need swap, you can use the secure swap cleaner, included in this package. With v1.8 of secure_deletion, you can also use sswap to clean your swap space after you unmounted it. - If you really need the data you produced or analysed, then create a crypted filesystem on a disk partition. For unix you can use CFS (newest version v1.4.x), for MsDos and Windows 3.x there are SFS v1.7 and SecureDrive v1.4a available. I don't know any for Win95 and WinNT - but these have special problems anyway so see the next topic : - Windows 95 and WinNT As you can see, the programs were NOT programmed for any Windows environment and this has got the following reasons : - I don't know enough about these systems to make the programs secure, also I know that they've got an internal function to flush their caches without any problems. - Special problems like in the NTFS, the WinNT Filesystem, which holds too much information on the files, so that real secure data deletion is tricky. - Windows machines swap very often - and where and how to control that - I don't know, in my opinion it would be too difficult to make it a secure system (against data recovery). So why writing a secure deletion programs when fragments of the files are everywhere on the harddisk? - Networks Before we'll discuss further matters of that topic let me put it short and straight : YOU CAN'T ENSURE SECURE DATA DELETION WHEN WORKING OVER A NETWORK YOU DO NOT *COMPLETLY* CONTROL ! Because : - The network servers and maybe even your local computer caches the data to be written. See "Disk Caching" above why this is a problem - and this one can't be solved. - How long are your files present on the server? Long enough that they are written on a backup? - A hacker or law enforcement/spies could have trojanized the server in a way that your files won't be overwritten and removed but those files are written to a special place waiting to be retrieved by them. Nearly all known network operating systems and also some firewalls can be penetrated from remote, no kidding. So don't think that you are not vulnerable. Another possiblity is that the memory of the server is surveilled and all reading/writing processes own by you or anyone copied. - Even if everything is ensured there might be still problems on high-end systems, which use f.e. Raid5 or similar redunant harddisk systems which prevent data loss by keeping copies and checksums, and you must find a way to trash those information too. The solution is easy: don't put any private and important stuff you don't want anybody to see on the network - crypt it before transfering it on a server. - Paranoia Finally two points which are for very paranoid guys. Imagine a temporary file was written by a program you used for your important files and they were deleted. You run "sfill" f.e. to clean all unused diskspace to trash all information. But another file, f.e. a config file, was written when exiting the program and parts of the temporary file are now owned and overwritten by the config file. sfill or any similar program won't trash that filearea because it's used by a file. And the data on this area can be recovered with cheap hardware. Solution : see above, "temporary files and disks" If you really care about your files that they can't be recovered you should also ensure that the "others" can't get the data by other means, f.e. by either hacking your computer or analyzing the electromagnetic/sound/wave emissions from the monitor, printer, fax and cables. Solution : pull out your network/modem cable when working and try to shield your computer (search the inet for more info on that) - But there is help Watch out. Soon there will be a new release from THC which shows how to make a Linux machine anonymous. When followed, nothing will be recoverable for someone having your harddisks. -> This was released looong ago now :-) Go the the THC website, enter the articles/papers section and look for "anonymizing unix systems". 5. COMPARISON Program secure_delete (srm) wipe wipe Version 2.1 0.2 0.56-2a Programmer van Hauser / THC Berke Durak Tom Vier Email vh@thc.org bedrettin@chez.com thomassr@erols.com Standard Passes 38 35 35 More passes via cmd no no yes Fewer passes via cmd yes yes yes Good RNG yes yes yes Blocksize (larger is better) 32k 1k 0 Truncates file yes no yes Rename file/directory yes no no recursive mode yes yes yes secure recursive (link races) yes no no verbose mode yes yes yes additional wipe tools yes no no Time: 1 File, 1MB 25s 40s 26s Time: 10 Files, 10kb 12s 6s 4s (Parameters for tests) -f -fTe (needed for the fastest mode) [otherwise it needs x12 time] Why is secure_delete that fast with big files but slower with many small ones? It's fast, because it uses a big buffer for writing. It's slow because the additional security features (rename, truncating, more passes, better RNG [against wipe-0.2] and by far the biggest blocksize). It's also the only one which comes with a free diskspace wiper and a special cleaner for swap space and memory. I think the choice is easy ,-) I hope the other programmers will make their programs better too, the more good & secure & fast programs, the better. Competition helps us all. 6. LAST WORDS I hope these little utilities help those who really need them. For any bugs, ideas or ongoing discussion feel free to email me at vh@thc.org using the public pgp key below. http://www.thc.org Have fun ... van Hauser / [THC] - The Hacker's Choice Type Bits/KeyID Date User ID pub 2048/CDD6A571 1998/04/27 van Hauser / THC -----BEGIN PGP PUBLIC KEY BLOCK----- Version: 2.6.3i mQENAzVE0A4AAAEIAOzKPhKBDFDyeTvMKQ1xx6781tEdIYgrkrsUEL6VoJ8H8CIU SeXDuCVu3JlMKITD6nPMFJ/DT0iKHgnHUZGdCQEk/b1YHUYOcig1DPGsg3WeTX7L XL1M4DwqDvPz5QUQ+U+VHuNOUzgxfcjhHsjJj2qorVZ/T5x4k3U960CMJ11eOVNC meD/+c6a2FfLZJG0sJ/kIZ9HUkY/dvXDInOJaalQc1mYjkvfcPsSzas4ddiXiDyc QcKX+HAXIdmT7bjq5+JS6yspnBvIZC55tB7ci2axTjwpkdzJBZIkCoBlWsDXNwyq s70Lo3H9dcaNt4ubz5OMVIvJHFMCEtIGS83WpXEABRG0J3ZhbiBIYXVzZXIgLyBU SEMgPHZoQHJlcHRpbGUucnVnLmFjLmJlPokAlQMFEDVE0D7Kb9wCOxiMfQEBvpAD /3UCDgJs1CNg/zpLhRuUBlYsZ1kimb9cbB/ufL1I4lYM5WMyw+YfGN0p02oY4pVn CQN6ca5OsqeXHWfn7LxBT3lXEPCckd+vb9LPPCzuDPS/zYnOkUXgUQdPo69B04dl C9C1YXcZjplYso2q3NYnuc0lu7WVD0qT52snNUDkd19ciQEVAwUQNUTQDhLSBkvN 1qVxAQGRTwgA05OmurXHVByFcvDaBRMhX6pKbTiVKh8HdJa8IdvuqHOcYFZ2L+xZ PAQy2WCqeakvss9Xn9I28/PQZ+6TmqWUmG0qgxe5MwkaXWxszKwRsQ8hH+bcppsZ 2/Q3BxSfPege4PPwFWsajnymsnmhdVvvrt69grzJDm+iMK0WR33+RvtgjUj+i22X lpt5hLHufDatQzukMu4R84M1tbGnUCNF0wICrU4U503yCA4DT/1eMoDXI0BQXmM/ Ygk9bO2Icy+lw1WPodrWmg4TJhdIgxuYlNLIu6TyqDYxjA/c525cBbdqwoE+YvUI o7CN/bJN0bKg1Y/BMTHEK3mpRLLWxVMRYw== =MdzX -----END PGP PUBLIC KEY BLOCK----- secure-delete-3.1.orig/sfill.10100644000175000017500000000643407655464311016476 0ustar robertlerobertle.\" This definition swiped from the gcc(1) man page .de Sp .if n .sp .if t .sp 0.4 .. .TH SFILL 1 .SH NAME sfill \- secure free disk and inode space wiper (secure_deletion toolkit) .SH SYNOPSIS .B sfill [-f] [-i] [-I] [-l] [-l] [-v] [-z] directory/mountpoint .SH DESCRIPTION .I sfill is designed to delete data which lies on available diskspace on mediums in a secure manner which can not be recovered by thiefs, law enforcement or other threats. The wipe algorythm is based on the paper "Secure Deletion of Data from Magnetic and Solid-State Memory" presented at the 6th Usenix Security Symposium by Peter Gutmann, one of the leading civilian cryptographers. .PP The .I secure data deletion process of sfill goes like this: .PP .TP .B * 1 pass with 0xff .TP .B * 5 random passes. /dev/urandom is used for a secure RNG if available. .TP .B * 27 passes with special values defined by Peter Gutmann. .TP .B * 5 random passes. /dev/urandom is used for a secure RNG if available. .PP afterwards as many temporary files as possible are generated to wipe the free inode space. After no more temporary files can be created, they are removed and sfill is finnished. .PP .SH COMMANDLINE OPTIONS .PP .TP .B \-f fast (and insecure mode): no /dev/urandom, no synchronize mode. .TP .B \-i wipe only free inode space, not free disk space .TP .B \-I wipe only free disk space, not free inode space .TP .B \-l lessens the security. Only two passes are written: one mode with 0xff and a final mode with random values. .TP .B \-l -l for a second time lessons the security even more: only one random pass is written. .TP .B \-v verbose mode .TP .B \-z wipes the last write with zeros instead of random data .PP .PP .B directory/mountpoint this is the location of the file created in your filesystem. It should lie on the partition you want to write. .PP .SH LIMITATIONS .TP .B FILESYSTEM INTELLIGENCE Most filesystems (ext2, ffs, etc.) have several features included to enhance performance, which will result in that sfill might not receive all available free space. Sad but true. Nothing can be done about that ... .TP .B NFS Beware of NFS. You can't ensure you really completely wiped your data from the remote disks. (especially because of caching) .TP .B Raid Raid Systems use stripped disks and have got large caches. It's hard to wipe them. .TP .B swap Some of your data might have a copy in your swapspace. .I sswap is available for this task. .PP .SH BUGS No bugs. There was never a bug in the secure_deletion package (in contrast to my other tools, whew, good luck ;-) Send me any that you find. Patches are nice too :) .SH AUTHOR .Sp van Hauser / THC .I .SH DISTRIBUTION The newest version of the .I secure_deletion package can be obtained from .I http://www.thc.org .Sp .I sfill and the .I secure_deletion package is (C) 1997-2003 by van Hauser / THC (vh@thc.org) .Sp This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; Version 2. .Sp This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. .SH SEE ALSO .I srm (1), .I sswap (1), .I smem (1) secure-delete-3.1.orig/sfill.c0100644000175000017500000001734407655464363016571 0ustar robertlerobertle/* Secure FILL - by van Hauser / [THC], vh@thc.org * * Secure FILL overwrites all available free diskspace by creating a file, * wiping all free diskspace it gets and finally deleting the file. * * Standard mode is a real security wipe for 38 times, flushing * the caches after every write. The wipe technique was proposed by Peter * Gutmann at Usenix '96 and includes 10 random overwrites plus 28 special * defined characters. Take a look at the paper of him, it's really worth * your time. * The option -l overwrites two times the data. (0xff + random) * The option -ll overwrites the data once. (random) * New with v2.3: wipes all inodes in the defined directory * * Read the manual for limitations. * Compiles clean on OpenBSD, Linux, Solaris and AIX * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sdel.h" #ifdef BLOCKSIZE #undef BLOCKSIZE #endif #define BLOCKSIZE 65535 #define MAXINODEWIPE 4194304 /* 22 bits */ char **array = NULL; int slow = O_SYNC; int debug = 1; int zero = 0; int inode_only = 0; int fd = -1; char *filename = NULL; FILE *f; void help() { printf("sfill %s (c) 1997-2003 by %s <%s>\n\n", VERSION, AUTHOR, EMAIL); printf("Syntax: %s [-fiIlvz] directory\n\n", prg); printf("Options:\n"); printf("\t-f fast (and insecure mode): no /dev/urandom, no synchronize mode.\n"); printf("\t-i wipe only inodes in the directory specified\n"); printf("\t-I just wipe space, not inodes\n"); printf("\t-l lessens the security (use twice for total insecure mode).\n"); printf("\t-v is verbose mode.\n"); printf("\t-z last wipe writes zeros, not random data.\n"); printf("\nsfill does a secure overwrite of the free space on the partition the specified\ndirectory resides and all free inodes of the directory specified.\n"); printf("Default is secure mode (38 writes).\n"); printf("You can find updates at %s\n", WEB); exit(1); } void cleanup(int signo) { fprintf(stderr,"\nTerminated by signal. Clean exit.\n"); if (fd > 0) { fsync(fd); (void) close(fd); sync(); if (filename != NULL && unlink(filename) != 0) fprintf(stderr, "Error: Could not remove temporary file %s!\n", filename); } if (array != NULL) { int i = 0; while(array[i] != NULL) { unlink(array[i]); i++; } } exit(1); } int main (int argc, char *argv[]) { int result; int secure = 2; /* standard is secure mode */ int loop; int turn; int counter; struct stat filestat; struct rlimit rlim; char type[15] = "random"; if (getuid() != 0) fprintf(stderr,"Warning: you are not root. You might not be able to wipe the whole filesystem.\n"); prg = argv[0]; if (argc == 1 || strncmp(argv[1], "-h", 2) == 0 || strncmp(argv[1], "--h", 3) == 0) help(); while (1) { result = getopt(argc, argv, "fFiIlLsSvVzZ"); if (result < 0) break; switch (result) { case 'f' : case 'F' : slow = 0; break; case 'i' : inode_only = 1; break; case 'I' : inode_only = -1; break; case 'l' : case 'L' : if (secure) secure--; break; case 's' : case 'S' : secure++; break; case 'v' : case 'V' : verbose++; break; case 'Z' : case 'z' : zero++; break; default : help(); } } loop = optind; if (loop >= argc) help(); if (zero) strcpy(type, "zero"); do { char newname[strlen(argv[loop]) + 16]; strcpy(newname, argv[loop]); // can not overflow if (opendir(newname) == NULL) { /* no need for ensuring close */ fprintf(stderr, "Error: %s is not a directory\n", newname); } else { /* Generate random unique name for tempfile */ srand(getpid()+getuid()); if (newname[strlen(newname)-1] != DIR_SEPERATOR) strcat(newname, "/"); turn = 0; result = 0; strcat(newname, "oooooooo.ooo"); result = lstat(newname, &filestat); while ((result >= 0) && (turn <= 250)) { for (counter = strlen(newname)-1; (newname[counter] != DIR_SEPERATOR); counter--) if (newname[counter] != '.') newname[counter] = 97+(int) (27.0 * rand() / (RAND_MAX + 1.0)); if ((result = lstat(newname, &filestat)) >= 0) turn++; }; if (result >= 0) fprintf(stderr,"Error: couldn't find a free filename in %s\n",argv[loop]); else { signal(SIGINT, cleanup); signal(SIGTERM, cleanup); signal(SIGHUP, cleanup); sdel_init(slow); if (verbose && inode_only < 1) { switch (secure) { case 0 : printf("Wipe mode is insecure (one pass [%s])\n",type); break; case 1 : printf("Wipe mode is insecure (two passes [0xff/%s])\n",type); break; default: printf("Wipe mode is secure (38 special passes)\n"); } printf("Wiping now ...\n"); } #ifdef RLIM_INFINITY #ifdef RLIMIT_FSIZE rlim.rlim_cur = RLIM_INFINITY; rlim.rlim_max = RLIM_INFINITY; if (setrlimit(RLIMIT_FSIZE, &rlim) != 0) fprintf(stderr, "Warning: Could not reset ulimit for filesize.\n"); #else fprintf(stderr, "Warning: not compiled with support for resetting ulimit for filesize.\n"); #endif #else fprintf(stderr, "Warning: not compiled with support for resetting ulimit for filesize.\n"); #endif result = 9; if (inode_only < 1) { /* create the file */ if (verbose) printf("Creating %s ... ", newname); if ((fd = open(newname, O_RDWR | O_EXCL | O_CREAT | O_LARGEFILE | slow, 0600 )) < 0) result = 1; else { filename = newname; result = sdel_overwrite(secure, fd, 0, BLOCKSIZE, 0, zero); /* Hard Flush -> Force cached data to be written to disk - the defines above! */ FLUSH; if ((fd = open(newname, O_WRONLY | O_TRUNC)) >= 0) close(fd); } } if ((result == 0 && inode_only == 0) || inode_only > 0) { if (result == 0 && inode_only == 0) printf(" "); sdel_wipe_inodes(argv[loop], array); result = 0; } switch (result) { case 0 : if (verbose) printf(" Finished\n"); break; case 1 : fprintf(stderr, "Error: No write permission for %s. ", argv[loop]); perror(""); break; case 9: break; default: fprintf(stderr, "Unknown error\n"); } if (unlink(newname) != 0) fprintf(stderr, "Error: Could not remove temporary file %s!\n", newname); filename = NULL; } } loop++; } while (loop < argc); sdel_finnish(); exit(0); } secure-delete-3.1.orig/smem.10100644000175000017500000000457107655464320016326 0ustar robertlerobertle.\" This definition swiped from the gcc(1) man page .de Sp .if n .sp .if t .sp 0.4 .. .TH SMEM 1 .SH NAME smem \- secure memory wiper (secure_deletion toolkit) .SH SYNOPSIS .B smem [-f] [-l] [-l] [-v] .SH DESCRIPTION .I smem is designed to delete data which may lie still in your memory (RAM) in a secure manner which can not be recovered by thiefs, law enforcement or other threats. Note that with the new SDRAMs, data will not wither away but will be kept static - it is easy to extract the necessary information! The wipe algorythm is based on the paper "Secure Deletion of Data from Magnetic and Solid-State Memory" presented at the 6th Usenix Security Symposium by Peter Gutmann, one of the leading civilian cryptographers. .PP The .I secure data deletion process of smem goes like this: .PP .TP .B * 1 pass with 0x00 .TP .B * 5 random passes. /dev/urandom is used for a secure RNG if available. .TP .B * 27 passes with special values defined by Peter Gutmann. .TP .B * 5 random passes. /dev/urandom is used for a secure RNG if available. .PP .SH COMMANDLINE OPTIONS .PP .TP .B \-f fast (and insecure mode): no /dev/urandom. .TP .B \-l lessens the security. Only two passes are written: the first with 0x00 and a final random one. .TP .B \-l -l for a second time lessons the security even more: only one pass with 0x00 is written. .TP .B \-v verbose mode .PP .SH BEWARE .TP .B SLOW Wiping the memory is very slow. You might use smem with the -ll option. (tip) .TP .B BETA! .I smem is still beta. .PP .SH BUGS No bugs. There was never a bug in the secure_deletion package (in contrast to my other tools, whew, good luck ;-) Send me any that you find. Patches are nice too :) .SH AUTHOR .Sp van Hauser / THC .I .SH DISTRIBUTION The newest version of the .I secure_deletion package can be obtained from .I http://www.thc.org .Sp .I smem and the .I secure_deletion package is (C) 1997-2003 by van Hauser / THC (vh@thc.org) .Sp This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; Version 2. .Sp This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. .SH SEE ALSO .I srm (1), .I sfill (1), .I sswap (1) secure-delete-3.1.orig/smem.c0100644000175000017500000001440507655464367016420 0ustar robertlerobertle/* Secure MEMORY cleaner - by van Hauser / [THC], vh@thc.org * * Note that this program is beta. It was tested with linux, solaris and * openbsd but I can't tell for other platforms. * * Secure MEMORY overwrites all data in your memory it gets. * The any -l option this does a real security wipe for 38 times, flushing * the caches after every write. The wipe technique was proposed by Peter * Gutmann at Usenix '96 and includes 10 random overwrites plus 29 special * defined characters. Take a look at the paper of him, it's really worth * your time. * If run with one -l option, it wipes the memory twice, first with null * bytes, then with random values. * If run with two -l options, it wipes the memory only once with null bytes. * * Note that it is *very* slow. You might run it with "-llf" * * Read the manual for limitations. * */ #include #include #include #include #include #include #include #include #include "sdel.h" #ifdef BLOCKSIZE #undef BLOCKSIZE #endif #define BLOCKSIZE 65536 char buf[BLOCKSIZE+2]; int slow = 1; extern FILE *devrandom; void help() { printf("smem %s (c) 1997-2003 by %s <%s>\n\n", VERSION, AUTHOR, EMAIL); printf("Syntax: %s [-flv]\n\n", prg); printf("Options:\n"); printf("\t-f fast (and insecure mode): no /dev/urandom.\n"); printf("\t-l lessens the security (use twice for total insecure mode).\n"); printf("\t-v is verbose mode.\n"); printf("\nsmem does a secure overwrite of the memory (RAM), because memory contents can\n"); printf("be recovered even after a shutdown! Default is secure mode (38 writes).\n"); printf("You can find updates at %s\n", WEB); exit(1); } int smash_it(int mode) { unsigned char write_modes[27][3] = { {"\x55\x55\x55"}, {"\xaa\xaa\xaa"}, {"\x92\x49\x24"}, {"\x49\x24\x92"}, {"\x24\x92\x49"}, {"\x00\x00\x00"}, {"\x11\x11\x11"}, {"\x22\x22\x22"}, {"\x33\x33\x33"}, {"\x44\x44\x44"}, {"\x55\x55\x55"}, {"\x66\x66\x66"}, {"\x77\x77\x77"}, {"\x88\x88\x88"}, {"\x99\x99\x99"}, {"\xaa\xaa\xaa"}, {"\xbb\xbb\xbb"}, {"\xcc\xcc\xcc"}, {"\xdd\xdd\xdd"}, {"\xee\xee\xee"}, {"\xff\xff\xff"}, {"\x92\x49\x24"}, {"\x49\x24\x92"}, {"\x24\x92\x49"}, {"\x6d\xb6\xdb"}, {"\xb6\xdb\x6d"}, {"\xdb\x6d\xb6"} }; int turn; unsigned int counter = 0; unsigned char buffers[27][BLOCKSIZE+2]; char *ptr; struct rlimit rlim; if (verbose) { switch (mode) { case 0 : printf("Wipe mode is insecure (one pass with 0x00)\n"); break; case 1 : printf("Wipe mode is insecure (two passes [0x00/random])\n"); break; default: printf("Wipe mode is secure (38 special passes)\n"); } } if (slow && mode) if ((devrandom = fopen(RANDOM_DEVICE, "r")) != NULL) if (verbose) printf("Using %s for random input.\n", RANDOM_DEVICE); /* We set a new ulimit, so we can grab all memory ... */ #ifdef RLIM_INFINITY rlim.rlim_cur = RLIM_INFINITY; rlim.rlim_max = RLIM_INFINITY; #ifdef RLIMIT_DATA if (setrlimit(RLIMIT_DATA, &rlim) != 0) fprintf(stderr, "Warning: Could not reset ulimit for data.\n"); #endif #ifdef RLIMIT_STACK if (setrlimit(RLIMIT_STACK, &rlim) != 0) fprintf(stderr, "Warning: Could not reset ulimit for stack.\n"); #endif #ifdef RLIMIT_RSS if (setrlimit(RLIMIT_RSS, &rlim) != 0) fprintf(stderr, "Warning: Could not reset ulimit for rss.\n"); #endif #ifdef RLIMIT_MEMLOCK if (setrlimit(RLIMIT_MEMLOCK, &rlim) != 0) fprintf(stderr, "Warning: Could not reset ulimit for mem locked.\n"); #endif #ifndef RLIMIT_DATA #ifndef RLIMIT_STACK #ifndef RLIMIT_RSS #ifndef RLIMIT_MEMLOCK fprintf(stderr, "Warning: Not compiled with support for resetting ulimits for memory\n"); #endif #endif #endif #endif #else fprintf(stderr, "Warning: Not compiled with support for resetting ulimits for memory\n"); #endif if (mode > 1) { for (turn=0; turn<27; turn++) { __sdel_fill_buf(write_modes[turn], BLOCKSIZE + 2, buf); memcpy(buffers[turn], buf, BLOCKSIZE); } } alarm(600); /* needed to prevent mem caching */ while ( (ptr = calloc(4096, 16)) != NULL) { if (mode > 0) { for (turn=0; turn<=36; turn++) { if ((mode == 1) && (turn > 0)) break; if ((turn>=5) && (turn<=31)) { memcpy(ptr, buffers[turn-5], BLOCKSIZE); } else { __sdel_random_buf(BLOCKSIZE + 2, buf); memcpy(ptr, buf, BLOCKSIZE); } } } if (verbose && (counter > 8)) { /* every 512kb */ printf("*"); counter = 0; } else counter++; } if (devrandom) fclose(devrandom); if (verbose) printf(" done\n"); return 0; } void cleanup() { fprintf(stderr,"Terminated by signal. Clean exit.\n"); if (devrandom) fclose(devrandom); fflush(stdout); fflush(stderr); exit(1); } int main (int argc, char *argv[]) { int secure = 2; int result; prg = argv[0]; if (argc == 2) if ( (strncmp(argv[1],"-h", 2) == 0) || (strcmp(argv[1],"-?") == 0) ) help(); while (1) { result = getopt(argc, argv, "FfLlSsVvZz"); if (result<0) break; switch (result) { case 'F' : case 'f' : slow = 0; break; case 'L' : case 'l' : if (secure) secure--; break; case 'S' : case 's' : secure++; break; case 'V' : case 'v' : verbose++; break; case 'Z': case 'z': break; default : help(); } } if (optind < argc) help(); printf("Starting Wiping the memory, press Control-C to abort earlier. Help: \"%s -h\"\n", prg); (void) setvbuf(stdout, NULL, _IONBF, 0); signal(SIGINT, cleanup); signal(SIGTERM, cleanup); signal(SIGHUP, cleanup); signal(SIGALRM, cleanup); smash_it(secure); /* thats all */ exit(0); } secure-delete-3.1.orig/srm.10100644000175000017500000000644507655464327016177 0ustar robertlerobertle.\" This definition swiped from the gcc(1) man page .de Sp .if n .sp .if t .sp 0.4 .. .TH SRM 1 .SH NAME srm \- secure remove (secure_deletion toolkit) .SH SYNOPSIS .B srm [-d] [-f] [-l] [-l] [-r] [-v] [-z] files .SH DESCRIPTION .I srm is designed to delete data on mediums in a secure manner which can not be recovered by thiefs, law enforcement or other threats. The wipe algorythm is based on the paper "Secure Deletion of Data from Magnetic and Solid-State Memory" presented at the 6th Usenix Security Symposium by Peter Gutmann, one of the leading civilian cryptographers. .PP The .I secure data deletion process of srm goes like this: .PP .TP .B * 1 pass with 0xff .TP .B * 5 random passes. /dev/urandom is used for a secure RNG if available. .TP .B * 27 passes with special values defined by Peter Gutmann. .TP .B * 5 random passes. /dev/urandom is used for a secure RNG if available. .TP .B * Rename the file to a random value .TP .B * Truncate the file .PP .PP As an additional measure of security, the file is opened in O_SYNC mode and after each pass an fsync() call is done. .I srm writes 32k blocks for the purpose of speed, filling buffers of disk caches to force them to flush and overwriting old data which belonged to the file. .PP .SH COMMANDLINE OPTIONS .PP .TP .B \-d ignore the two special dot files . and .. on the commandline. (so you can execute it like "srm -d .* *") .TP .B \-f fast (and insecure mode): no /dev/urandom, no synchronize mode. .TP .B \-l lessens the security. Only two passes are written: one mode with 0xff and a final mode random values. .TP .B \-l -l for a second time lessons the security even more: only one random pass is written. .TP .B \-r recursive mode, deletes all subdirectories. .TP .B \-v verbose mode .TP .B \-z wipes the last write with zeros instead of random data .PP .SH LIMITATIONS .TP .B NFS Beware of NFS. You can't ensure you really completely wiped your data from the remote disks. .TP .B Raid Raid Systems use stripped disks and have got large caches. It's hard to wipe them. .TP .B swap, /tmp, etc. Some of your data might have a temporary (deleted) copy somewhere on the disk. You should use .I sfill which comes with the .I secure_deletion package to ensure to wipe also the free diskspace. However, If already a small file aquired a block with your precious data, no tool known to me can help you here. For a secure deletion of the swap space .I sswap is available. .PP .SH BUGS No bugs. There was never a bug in the secure_deletion package (in contrast to my other tools, whew, good luck ;-) Send me any that you find. Patches are nice too :) .SH AUTHOR .Sp van Hauser / THC .I .SH DISTRIBUTION The newest version of the .I secure_deletion package can be obtained from .I http://www.thc.org .Sp .I srm and the .I secure_deletion package is (C) 1997-2003 by van Hauser / THC (vh@thc.org) .Sp This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; Version 2. .Sp This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. .SH SEE ALSO .I sfill (1), .I sswap (1), .I smem (1) secure-delete-3.1.orig/srm.c0100644000175000017500000002374207750315777016260 0ustar robertlerobertle/* Secure RM - by van Hauser / [THC], vh@thc.org * * Secure ReMove first overwrites then renames and finally deletes the target * file(s) specified via parameters. * For security reasons full 32kb blocks are written so that the whole block * on which the file(s) live are overwritten. (change #define #BLOCKSIZE) * The option -l overwrites two times the data. * The option -ll overwrites the data once. * Standard mode is a real security wipe for 38 times, flushing * the caches after every write. The wipe technique was proposed by Peter * Gutmann at Usenix '96 and includes 10 random overwrites plus 28 special * defined characters. Take a look at the paper of him, it's really worth * your time. * * Advice : set "alias rm 'srm -v'" * * Read the manual for limitations. * Compiles clean on OpenBSD, Linux, Solaris, AIX and I guess all others. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sdel.h" int slow = O_SYNC; int recursive = 0; int zero = 0; unsigned long bufsize = BLOCKSIZE; int fd; void help () { printf("srm %s (c) 1997-2003 by %s <%s>\n\n", VERSION, AUTHOR, EMAIL); printf("Syntax: %s [-dflrvz] file1 file2 etc.\n\n", prg); printf("Options:\n"); printf("\t-d ignore the two dot special files \".\" and \"..\".\n"); printf("\t-f fast (and insecure mode): no /dev/urandom, no synchronize mode.\n"); printf("\t-l lessens the security (use twice for total insecure mode).\n"); printf("\t-r recursive mode, deletes all subdirectories.\n"); printf("\t-v is verbose mode.\n"); printf("\t-z last wipe writes zeros instead of random data.\n"); printf("\nsrm does a secure overwrite/rename/delete of the target file(s).\n"); printf("Default is secure mode (38 writes).\n"); printf("You can find updates at %s\n", WEB); exit(1); } int smash_it(char *filename, int mode) { struct stat filestat; struct stat controlstat; int i_am_a_directory = 0; /* get the file stats */ if (lstat(filename, &filestat)) return 1; if (S_ISREG(filestat.st_mode) && filestat.st_nlink > 1) { fprintf(stderr, "Error: File %s - file is hardlinked %d time(s), skipping!\n", filename, filestat.st_nlink - 1); return -1; } /* if the blocksize on the filesystem is bigger than the on compiled with, enlarge! */ if (filestat.st_blksize > bufsize) { if (filestat.st_blksize > 65532) { bufsize = 65535; } else { bufsize = (((filestat.st_blksize / 3) + 1) * 3); } } /* handle the recursive mode */ if (recursive) if (S_ISDIR(filestat.st_mode)) { DIR *dir; struct dirent *dir_entry; struct stat cwd_stat; char current_dir[4097]; int res; int chdir_success = 1; if (verbose) printf("DIRECTORY (going recursive now)\n"); getcwd(current_dir, 4096); current_dir[4096] = '\0'; /* a won race will chmod a file to 0700 if the user is owner/root I'll think about a secure solution to this, however, I think there isn't one - anyone with an idea? */ if (chdir(filename)) { (void) chmod(filename, 0700); /* ignore permission errors */ if (chdir(filename)) { fprintf(stderr,"Can't chdir() to %s, hence I can't wipe it.\n", filename); chdir_success = 0; } } if (chdir_success) { lstat(".", &controlstat); lstat("..", &cwd_stat); if ( (filestat.st_dev != controlstat.st_dev) || (filestat.st_ino != controlstat.st_ino) ) { fprintf(stderr, "Race found! (directory %s became a link)\n", filename); } else { if ((dir = opendir (".")) != NULL) { (void) chmod(".", 0700); /* ignore permission errors */ dir = opendir ("."); } if (dir != NULL) { while ((dir_entry = readdir(dir)) != NULL) if (strcmp(dir_entry->d_name, ".") && strcmp(dir_entry->d_name, "..")) { if (verbose) printf("Wiping %s ", dir_entry->d_name); if ( (res = smash_it(dir_entry->d_name, mode)) > 0) { if (res == 3) fprintf(stderr,"File %s was raced, hence I won't wipe it.\n", dir_entry->d_name); else { fprintf(stderr,"Couldn't delete %s. ", dir_entry->d_name); perror(""); } } else if (verbose) printf(" Done\n"); } closedir(dir); } } if(chdir(current_dir) != 0) { fprintf(stderr, "Error: Can't chdir to %s (aborting) - ", current_dir); perror(""); exit(1); } /* lstat(current_dir, &controlstat); if ( (cwd_stat.st_dev != controlstat.st_dev) || (cwd_stat.st_ino != controlstat.st_ino) ) { fprintf(stderr, "Race found! (directory %s was exchanged or its your working directory)\n", current_dir); exit(1); } */ i_am_a_directory = 1; } } /* end of recursive function */ if (S_ISREG(filestat.st_mode)) { /* open the file for writing in sync. mode */ if ((fd = open(filename, O_RDWR | O_LARGEFILE | slow)) < 0) { /* here again this has a race problem ... hmmm */ /* make it writable for us if possible */ (void) chmod(filename, 0600); /* ignore errors */ if ((fd = open(filename, O_RDWR | O_LARGEFILE | slow)) < 0) return 1; } fstat(fd, &controlstat); if ((filestat.st_dev != controlstat.st_dev) || (filestat.st_ino != controlstat.st_ino) || (! S_ISREG(controlstat.st_mode))) { close(fd); return 3; } if (sdel_overwrite(mode, fd, 0, bufsize, filestat.st_size > 0 ? filestat.st_size : 1, zero) == 0) return sdel_unlink(filename, 0, 1, slow); } /* end IS_REG() */ else { if (S_ISDIR(filestat.st_mode)) { if (i_am_a_directory == 0) { fprintf(stderr,"Warning: %s is a directory. I will not remove it, because the -r option is missing!\n", filename); return 0; } else return sdel_unlink(filename, 1, 0, slow); } else if (! S_ISDIR(filestat.st_mode)) { fprintf(stderr,"Warning: %s is not a regular file, rename/unlink only!", filename); if (! verbose) printf("\n"); return sdel_unlink(filename, 0, 0, slow); } } return 99; // not reached } void cleanup(int signo) { fprintf(stderr,"Terminated by signal. Clean exit.\n"); if (fd >= 0) close(fd); FLUSH; exit(1); } int main (int argc, char *argv[]) { int errors = 0; int dot = 0; int result; int secure = 2; /* Standard is now SECURE mode (38 overwrites) [since v2.0] */ int loop; struct rlimit rlim; prg = argv[0]; if (argc < 2 || strncmp(argv[1], "-h", 2) == 0|| strncmp(argv[1], "--h", 3) == 0) help(); while (1) { result = getopt(argc, argv, "DdFfLlRrSsVvZz"); if (result < 0) break; switch (result) { case 'd' : case 'D' : dot = 1; break; case 'F' : case 'f' : slow = 0; break; case 'L' : case 'l' : if (secure) secure--; break; case 'R' : case 'r' : recursive++; break; case 'S' : case 's' : secure++; break; case 'V' : case 'v' : verbose++; break; case 'Z' : case 'z' : zero++; break; default : help(); } } loop = optind; if (loop == argc) help(); signal(SIGINT, cleanup); signal(SIGTERM, cleanup); signal(SIGHUP, cleanup); sdel_init(slow); if (verbose) { char type[15] = "random"; if (zero) strcpy(type, "zero"); switch (secure) { case 0 : printf("Wipe mode is insecure (one pass [%s])\n",type); break; case 1 : printf("Wipe mode is insecure (two passes [0xff/%s])\n",type); break; default: printf("Wipe mode is secure (38 special passes)\n"); } } #ifdef RLIM_INFINITY #ifdef RLIMIT_FSIZE rlim.rlim_cur = RLIM_INFINITY; rlim.rlim_max = RLIM_INFINITY; if (setrlimit(RLIMIT_FSIZE, &rlim) != 0) fprintf(stderr, "Warning: Could not reset ulimit for filesize.\n"); #else fprintf(stderr, "Warning: Not compiled with support for resetting ulimit filesize.\n"); #endif #endif while (loop < argc) { char rmfile[strlen(argv[loop]) + 1]; strcpy(rmfile, argv[loop]); loop++; if (strcmp("/", rmfile) == 0) { fprintf(stderr,"Warning: Do you really want to remove the ROOT directory??\n"); fprintf(stderr,"I'm giving you 5 seconds to abort ... press Control-C\n"); sleep(6); fprintf(stderr,"Doing my evil work now, don't whimp later, you had been informed!\n"); } if (dot) if ((strcmp(".", rmfile) == 0) || (strcmp("..", rmfile) == 0)) continue; if (verbose) printf("Wiping %s ", rmfile); result = (int) smash_it(rmfile, secure); switch (result) { case 0 : if (verbose) printf(" Done\n"); break; case 1 : fprintf(stderr, "Error: File %s - ", rmfile); perror(""); break; case -1: break; case 3 : fprintf(stderr, "File %s was raced, hence I won't wipe it!\n", rmfile); break; default: fprintf(stderr, "Unknown error\n"); } if (result) errors++; } sdel_finnish(); if (errors) return 1; else return 0; } secure-delete-3.1.orig/sswap.10100644000175000017500000000472007655464337016526 0ustar robertlerobertle.\" This definition swiped from the gcc(1) man page .de Sp .if n .sp .if t .sp 0.4 .. .TH SSWAP 1 .SH NAME sswap \- secure swap wiper (secure_deletion toolkit) .SH SYNOPSIS .B sswap [-f] [-l] [-l] [-v] [-z] swapdevice .SH DESCRIPTION .I sswap is designed to delete data which may lie still on your swapspace in a secure manner which can not be recovered by thiefs, law enforcement or other threats. The wipe algorythm is based on the paper "Secure Deletion of Data from Magnetic and Solid-State Memory" presented at the 6th Usenix Security Symposium by Peter Gutmann, one of the leading civilian cryptographers. .PP The .I secure data deletion process of sswap goes like this: .PP .TP .B * 1 pass with 0xff .TP .B * 5 random passes. /dev/urandom is used for a secure RNG if available. .TP .B * 27 passes with special values defined by Peter Gutmann. .TP .B * 5 random passes. /dev/urandom is used for a secure RNG if available. .PP .SH COMMANDLINE OPTIONS .PP .TP .B \-f fast (and insecure mode): no /dev/urandom, no synchronize mode. .TP .B \-l lessens the security. Only two passes are written: one mode with 0xff and a final mode with random values. .TP .B \-l -l for a second time lessons the security even more: only one pass with random values is written. .TP .B \-v verbose mode .TP .B \-z wipes the last write with zeros instead of random data .PP .SH BEWARE .TP .B swapoff unmount your swapspace before using this tool! Otherwise your system might crash! .TP .B BETA! .I sswap is still beta. It was only tested on Linux but on this system it performed it's work all of the time. .PP .SH BUGS No bugs. There was never a bug in the secure_deletion package (in contrast to my other tools, whew, good luck ;-) Send me any that you find. Patches are nice too :) .SH AUTHOR .Sp van Hauser / THC .I .SH DISTRIBUTION The newest version of the .I secure_deletion package can be obtained from .I http://www.thc.org .Sp .I sswap and the .I secure_deletion package is (C) 1997-2003 by van Hauser / THC (vh@thc.org) .Sp This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; Version 2. .Sp This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. .SH SEE ALSO .I srm (1), .I sfill (1), .I smem (1) secure-delete-3.1.orig/sswap.c0100644000175000017500000001146607655464400016604 0ustar robertlerobertle/* Secure SWAP cleaner - by van Hauser / [THC], vh@thc.org * * Note that this program is beta. It was tested with linux, but I can't * tell for other platforms. Read the statement at #define SWAP_PAGESIZE * on how to use this program on other unix machines. * * of course: you have to turn of the swapspace before using this program ! * * Secure SWAP overwrites all data on your swap device. * Standard mode is a real security wipe for 38 times, flushing * the caches after every write. The wipe technique was proposed by Peter * Gutmann at Usenix '96 and includes 10 random overwrites plus 28 special * defined characters. Take a look at the paper of him, it's really worth * your time. * The option -l overwrites two times the data. (0xff + random) * The option -ll overwrites the data once. (random) * * Read the manual for limitations. * Compiles clean on OpenBSD, Linux, Solaris and AIX * */ #include #include #include #include #include #include #include #include #include #include "sdel.h" /* SWAP_PAGESIZE is an important variable. You have to set this * to your header length of your swapdevice. For Linux this is 4096, * I don't know for the other OSs. To be sure, set this to 0 and * recreate your swapspace afterwards (for linux: mkswap /dev/swapdevice) */ #define SWAP_PAGESIZE 4096 #ifdef BLOCKSIZE #undef BLOCKSIZE #endif #define BLOCKSIZE 65535 int fd; int slow = O_SYNC; int zero = 0; void help() { printf("sswap %s (c) 1997-2003 by %s <%s>\n\n", VERSION, AUTHOR, EMAIL); printf("Syntax: %s [-flvz] [-j start] /dev/of_swap_device\n\n", prg); printf("Options:\n"); printf("\t-f fast (and insecure mode): no /dev/urandom, no synchronize mode.\n"); printf("\t-j jump over the first number of bytes when wiping. (default: %d)\n", SWAP_PAGESIZE); printf("\t-l lessens the security (use twice for total insecure mode).\n"); printf("\t-v is verbose mode.\n"); printf("\t-z last wipe writes zeros instead of random data.\n"); printf("\nsswap does a secure overwrite of the swap space.\n"); printf("Default is secure mode (38 writes).\n"); printf("Updates can be found at %s\n", WEB); printf("\nNOTE: You must disable the swapspace before using this program!\007\n"); exit(1); } void cleanup() { fprintf(stderr,"\nTerminated by signal. Clean exit.\n"); close(fd); sync(); exit(1); } int main (int argc, char *argv[]) { int secure = 2; int result; int mode; unsigned long start = SWAP_PAGESIZE; struct stat stats; char *filename; prg = argv[0]; if (argc == 1 || strncmp(argv[1], "-h", 2) == 0 || strncmp(argv[1], "--h", 3) == 0) help(); while (1) { result = getopt(argc, argv, "FfJ:j:LlSsVvZz"); if (result<0) break; switch (result) { case 'F' : case 'f' : slow = 0; break; case 'J' : case 'j' : start = atol(optarg); if (start < 0 || start > 65535) { fprintf(stderr, "Error: The -j option must be set between 0 and 65535!\n"); exit(-1); } break; case 'L' : case 'l' : if (secure) secure--; break; case 'S' : case 's' : secure++; break; case 'V' : case 'v' : verbose++; break; case 'Z' : case 'z' : zero++; break; default : help(); } } if ((optind+1) != argc) help(); signal(SIGINT, cleanup); signal(SIGTERM, cleanup); signal(SIGHUP, cleanup); filename = argv[optind]; mode = secure; if ((fd = open (filename, O_RDWR | O_LARGEFILE | slow)) < 0) { fprintf(stderr, "Error: Can't open %s for writing.", filename); perror(""); exit(1); } fstat(fd, &stats); if (!S_ISBLK(stats.st_mode)) { fprintf(stderr, "Error: Target is not a block device - %s\n", filename); exit(1); } if (verbose) { char type[15] = "random"; if (zero) strcpy(type, "zero"); switch (mode) { case 0 : printf("Wipe mode is insecure (one pass [%s])\n",type); break; case 1 : printf("Wipe mode is insecure (two passes [0xff/%s])\n",type); break; default: printf("Wipe mode is secure (38 special passes)\n"); } printf("Writing to device %s: ", filename); } sdel_init(slow); if (sdel_overwrite(mode, fd, start, BLOCKSIZE, 0, zero) == 0) if (verbose) printf(" done\n"); sdel_finnish(); /* thats all */ exit(0); } secure-delete-3.1.orig/the_cleaner.sh0100644000175000017500000000744407522311302020072 0ustar robertlerobertle#!/bin/sh # # THE CLEANER SCRIPT # Part of the secure_data_deletion toolkit by van Hauser / THC # Run at your own risk. Tested on Linux only. # # Run this to wipe your system as most as possible with automatic stuff. # You should run this in the STOP runlevels 2 and 3. # # ------------------------------------------------------------------------ # # Please configure the following variables: # # # WIPE_MODE: 1-3 # 1: highly secure mode (38 wipes) # 2: insecure mode (2 wipes) # 3: highly insecure mode (1 wipe) WIPE_MODE=3 # # # WIPE_FAST: yes/no # yes: dont use a secure random number generator, less secure # no: use a secure random number generator, secure WIPE_FAST=yes # # # WIPE_VERBOSE: yes/no # yes: write verbose messages # no: only write if error/warnings occur WIPE_VERBOSE=yes # # # WIPE_DIRECTORIES: directories you want to wipe completely all files and # subdirectories from. Usually /tmp, /usr/tmp and /var/tmp. WIPE_DIRECTORIES="/tmp /usr/tmp /var/tmp" # # WIPE_USER_FILES: files you want to wipe from user directories. Usually # .*history*, .netscape/cache/*, .netscape/history*, # .netscape/cookies, tmp/*, *~ and core WIPE_USER_FILES=".*history* .netscape/cache/ .netscape/history* \ .netscape/cookies .lynx_cookies tmp/* *~ .gqview_thmb/ dead.letter core" # ------------------------------------------------------------------------ # # Preparation Phase # test "$WIPE_MODE" -gt 0 -a "$WIPE_MODE" -lt 4 || { echo "WIPE_MODE must be a value between 1 and 3." exit 1 } test "$WIPE_FAST" = yes -o "$WIPE_FAST" = "no" || { echo "WIPE_FAST must be either yes or no." exit 1 } test "$WIPE_VERBOSE" = yes -o "$WIPE_VERBOSE" = "no" || { echo "WIPE_VERBOSE must be either yes or no." exit 1 } MODE="" test "$WIPE_MODE" -eq 2 && MODE="-l" test "$WIPE_MODE" -eq 3 && MODE="-ll" FAST="" test "$WIPE_FAST" = yes && FAST="-f" VERBOSE="" test "$WIPE_VERBOSE" = yes && VERBOSE="-v" # ------------------------------------------------------------------------ # # Starting the wiping process # # Wipe directories test -z "$VERBOSE" || echo "STARTING THE CLEANER." test -z "$VERBOSE" || echo "CLEANER: Wiping directory contents" for i in $WIPE_DIRECTORIES; do test -z "$i" -o "$i" = "." -o "$i" = ".." -o "$i" = "/" || { test -z "$VERBOSE" || echo " $i" cd "$i" && srm $MODE $FAST -d -r -- .* * } done # Wipe files test -z "$VERBOSE" || echo "CLEANER: Wiping user files" awk -F: '{ print $1 " " $6 }' /etc/passwd | while read user homedir; do test "$homedir" = "" -o "$homedir" = "." -o "$homedir" = ".." || { cd "$homedir" && { test -z "$VERBOSE" || echo " $user" for j in $WIPE_USER_FILES; do test -z "$j" || { test -L "$j" || { test -e "$j" && srm $MODE $FAST -d -r -- $j # test -e "$j" && "i would wipe: $j" } } done } } done # Wipe free space and inodes test -z "$VERBOSE" || echo "CLEANER: Wiping free space and inodes on filesystems" for i in `mount|grep -E '^/dev/.* type ext'|awk '{print$3}'`; do test -z "$VERBOSE" || echo " $i" test -z "$i" || sfill $MODE $FAST "$i" done # Now the swap space: test -z "$VERBOSE" || echo "CLEANER: Wiping swap space" #ACTIVE=`swapoff -s|grep ^/dev/|awk '{print$1}'` swapoff -a for i in `(grep -w swap /etc/fstab|awk '{print$1}';echo "$ACTIVE";)|sort -u`; do test -z "$VERBOSE" || echo " $i" test -z "$i" || sswap $MODE $FAST "$i" done # Finally the memory: test -z "$VERBOSE" || echo "CLEANER: Wiping the memory" smem $MODE $FAST # swapon -a # FINNISHED! test -z "$VERBOSE" || echo "THE CLEANER FINNISHED." secure-delete-3.1.orig/usenix6-gutmann.doc0100644000175000017500000017260607522311302021027 0ustar robertlerobertle The following paper was originally published in the Proceedings of the Sixth USENIX Security Symposium San Jose, California, July 1996. For more information about USENIX Association contact: 1. Phone: (510) 528-8649 2. FAX: (510) 548-5738 3. Email: office@usenix.org 4. WWW URL: http://www.usenix.org _________________________________________________________________ Secure Deletion of Data from Magnetic and Solid-State Memory Peter Gutmann Department of Computer Science University of Auckland pgut001@cs.auckland.ac.nz Abstract With the use of increasingly sophisticated encryption systems, an attacker wishing to gain access to sensitive data is forced to look elsewhere for information. One avenue of attack is the recovery of supposedly erased data from magnetic media or random-access memory. This paper covers some of the methods available to recover erased data and presents schemes to make this recovery significantly more difficult. 1. Introduction Much research has gone into the design of highly secure encryption systems intended to protect sensitive information. However work on methods of securing (or at least safely deleting) the original plaintext form of the encrypted data against sophisticated new analysis techniques seems difficult to find. In the 1980's some work was done on the recovery of erased data from magnetic media [1] [2] [3], but to date the main source of information is government standards covering the destruction of data. There are two main problems with these official guidelines for sanitizing media. The first is that they are often somewhat old and may predate newer techniques for both recording data on the media and for recovering the recorded data. For example most of the current guidelines on sanitizing magnetic media predate the early-90's jump in recording densities, the adoption of sophisticated channel coding techniques such as PRML, the use of magnetic force microscopy for the analysis of magnetic media, and recent studies of certain properties of magnetic media recording such as the behaviour of erase bands. The second problem with official data destruction standards is that the information in them may be partially inaccurate in an attempt to fool opposing intelligence agencies (which is probably why a great many guidelines on sanitizing media are classified). By deliberately under-stating the requirements for media sanitization in publicly-available guides, intelligence agencies can preserve their information-gathering capabilities while at the same time protecting their own data using classified techniques. This paper represents an attempt to analyse the problems inherent in trying to erase data from magnetic disk media and random-access memory without access to specialised equipment, and suggests methods for ensuring that the recovery of data from these media can be made as difficult as possible for an attacker. 2. Methods of Recovery for Data stored on Magnetic Media Magnetic force microscopy (MFM) is a recent technique for imaging magnetization patterns with high resolution and minimal sample preparation. The technique is derived from scanning probe microscopy (SPM) and uses a sharp magnetic tip attached to a flexible cantilever placed close to the surface to be analysed, where it interacts with the stray field emanating from the sample. An image of the field at the surface is formed by moving the tip across the surface and measuring the force (or force gradient) as a function of position. The strength of the interaction is measured by monitoring the position of the cantilever using an optical interferometer or tunnelling sensor. Magnetic force scanning tunneling microscopy (STM) is a more recent variant of this technique which uses a probe tip typically made by plating pure nickel onto a prepatterned surface, peeling the resulting thin film from the substrate it was plated onto and plating it with a thin layer of gold to minimise corrosion, and mounting it in a probe where it is placed at some small bias potential (typically a few tenths of a nanoamp at a few volts DC) so that electrons from the surface under test can tunnel across the gap to the probe tip (or vice versa). The probe is scanned across the surface to be analysed as a feedback system continuously adjusts the vertical position to maintain a constant current. The image is then generated in the same way as for MFM [4] [5]. Other techniques which have been used in the past to analyse magnetic media are the use of ferrofluid in combination with optical microscopes (which, with gigabit/square inch recording density is no longer feasible as the magnetic features are smaller than the wavelength of visible light) and a number of exotic techniques which require significant sample preparation and expensive equipment. In comparison, MFM can be performed through the protective overcoat applied to magnetic media, requires little or no sample preparation, and can produce results in a very short time. Even for a relatively inexperienced user the time to start getting images of the data on a drive platter is about 5 minutes. To start getting useful images of a particular track requires more than a passing knowledge of disk formats, but these are well-documented, and once the correct location on the platter is found a single image would take approximately 2-10 minutes depending on the skill of the operator and the resolution required. With one of the more expensive MFM's it is possible to automate a collection sequence and theoretically possible to collect an image of the entire disk by changing the MFM controller software. There are, from manufacturers sales figures, several thousand SPM's in use in the field today, some of which have special features for analysing disk drive platters, such as the vacuum chucks for standard disk drive platters along with specialised modes of operation for magnetic media analysis. These SPM's can be used with sophisticated programmable controllers and analysis software to allow automation of the data recovery process. If commercially-available SPM's are considered too expensive, it is possible to build a reasonably capable SPM for about US$1400, using a PC as a controller [6]. Faced with techniques such as MFM, truly deleting data from magnetic media is very difficult. The problem lies in the fact that when data is written to the medium, the write head sets the polarity of most, but not all, of the magnetic domains. This is partially due to the inability of the writing device to write in exactly the same location each time, and partially due to the variations in media sensitivity and field strength over time and among devices. In conventional terms, when a one is written to disk the media records a one, and when a zero is written the media records a zero. However the actual effect is closer to obtaining a 0.95 when a zero is overwritten with a one, and a 1.05 when a one is overwritten with a one. Normal disk circuitry is set up so that both these values are read as ones, but using specialised circuitry it is possible to work out what previous "layers" contained. The recovery of at least one or two layers of overwritten data isn't too hard to perform by reading the signal from the analog head electronics with a high-quality digital sampling oscilloscope, downloading the sampled waveform to a PC, and analysing it in software to recover the previously recorded signal. What the software does is generate an "ideal" read signal and subtract it from what was actually read, leaving as the difference the remnant of the previous signal. Since the analog circuitry in a commercial hard drive is nowhere near the quality of the circuitry in the oscilloscope used to sample the signal, the ability exists to recover a lot of extra information which isn't exploited by the hard drive electronics (although with newer channel coding techniques such as PRML (explained further on) which require extensive amounts of signal processing, the use of simple tools such as an oscilloscope to directly recover the data is no longer possible). Using MFM, we can go even further than this. During normal readback, a conventional head averages the signal over the track, and any remnant magnetization at the track edges simply contributes a small percentage of noise to the total signal. The sampling region is too broad to distinctly detect the remnant magnetization at the track edges, so that the overwritten data which is still present beside the new data cannot be recovered without the use of specialised techniques such as MFM or STM (in fact one of the "official" uses of MFM or STM is to evaluate the effectiveness of disk drive servo-positioning mechanisms) [7]. Most drives are capable of microstepping the heads for internal diagnostic and error recovery purposes (typical error recovery strategies consist of rereading tracks with slightly changed data threshold and window offsets and varying the head positioning by a few percent to either side of the track), but writing to the media while the head is off-track in order to erase the remnant signal carries too much risk of making neighbouring tracks unreadable to be useful (for this reason the microstepping capability is made very difficult to access by external means). These specialised techniques also allow data to be recovered from magnetic media long after the read/write head of the drive is incapable of reading anything useful. For example one experiment in AC erasure involved driving the write head with a 40 MHz square wave with an initial current of 12 mA which was dropped in 2 mA steps to a final level of 2 mA in successive passes, an order of magnitude more than the usual write current which ranges from high microamps to low milliamps. Any remnant bit patterns left by this erasing process were far too faint to be detected by the read head, but could still be observed using MFM [8]. Even with a DC erasure process, traces of the previously recorded signal may persist until the applied DC field is several times the media coercivity [9]. Deviations in the position of the drive head from the original track may leave significant portions of the previous data along the track edge relatively untouched. Newly written data, present as wide alternating light and dark bands in MFM and STM images, are often superimposed over previously recorded data which persists at the track edges. Regions where the old and new data coincide create continuous magnetization between the two. However, if the new transition is out of phase with the previous one, a few microns of erase band with no definite magnetization are created at the juncture of the old and new tracks. The write field in the erase band is above the coercivity of the media and would change the magnetization in these areas, but its magnitude is not high enough to create new well- defined transitions. One experiment involved writing a fixed pattern of all 1's with a bit interval of 2.5 ‘m, moving the write head off-track by approximately half a track width, and then writing the pattern again with a frequency slightly higher than that of the previously recorded track for a bit interval of 2.45 ‘m to create all possible phase differences between the transitions in the old and new tracks. Using a 4.2 ‘m wide head produced an erase band of approximately 1 ‘m in width when the old and new tracks were 180ø out of phase, dropping to almost nothing when the two tracks were in-phase. Writing data at a higher frequency with the original tracks bit interval at 0.5 ‘m and the new tracks bit interval at 0.49 ‘m allows a single MFM image to contain all possible phase differences, showing a dramatic increase in the width of the erase band as the two tracks move from in-phase to 180ø out of phase [10]. In addition, the new track width can exhibit modulation which depends on the phase relationship between the old and new patterns, allowing the previous data to be recovered even if the old data patterns themselves are no longer distinct. The overwrite performance also depends on the position of the write head relative to the originally written track. If the head is directly aligned with the track, overwrite performance is relatively good; as the head moves offtrack, the performance drops markedly as the remnant components of the original data are read back along with the newly-written signal. This effect is less noticeable as the write frequency increases due to the greater attenuation of the field with distance [11]. When all the above factors are combined it turns out that each track contains an image of everything ever written to it, but that the contribution from each "layer" gets progressively smaller the further back it was made. Intelligence organisations have a lot of expertise in recovering these palimpsestuous images. 3. Erasure of Data stored on Magnetic Media The general concept behind an overwriting scheme is to flip each magnetic domain on the disk back and forth as much as possible (this is the basic idea behind degaussing) without writing the same pattern twice in a row. If the data was encoded directly, we could simply choose the desired overwrite pattern of ones and zeroes and write it repeatedly. However, disks generally use some form of run-length limited (RLL) encoding, so that the adjacent ones won't be written. This encoding is used to ensure that transitions aren't placed too closely together, or too far apart, which would mean the drive would lose track of where it was in the data. To erase magnetic media, we need to overwrite it many times with alternating patterns in order to expose it to a magnetic field oscillating fast enough that it does the desired flipping of the magnetic domains in a reasonable amount of time. Unfortunately, there is a complication in that we need to saturate the disk surface to the greatest depth possible, and very high frequency signals only "scratch the surface" of the magnetic medium. Disk drive manufacturers, in trying to achieve ever-higher densities, use the highest possible frequencies, whereas we really require the lowest frequency a disk drive can produce. Even this is still rather high. The best we can do is to use the lowest frequency possible for overwrites, to penetrate as deeply as possible into the recording medium. The write frequency also determines how effectively previous data can be overwritten due to the dependence of the field needed to cause magnetic switching on the length of time the field is applied. Tests on a number of typical disk drive heads have shown a difference of up to 20 dB in overwrite performance when data recorded at 40 kFCI (flux changes per inch), typical of recent disk drives, is overwritten with a signal varying from 0 to 100 kFCI. The best average performance for the various heads appears to be with an overwrite signal of around 10 kFCI, with the worst performance being at 100 kFCI [12]. The track write width is also affected by the write frequency - as the frequency increases, the write width decreases for both MR and TFI heads. In [13] there was a decrease in write width of around 20% as the write frequency was increased from 1 to 40 kFCI, with the decrease being most marked at the high end of the frequency range. However, the decrease in write width is balanced by a corresponding increase in the two side- erase bands so that the sum of the two remains nearly constant with frequency and equal to the DC erase width for the head. The media coercivity also affects the width of the write and erase bands, with their width dropping as the coercivity increases (this is one of the explanations for the ever-increasing coercivity of newer, higher-density drives). To try to write the lowest possible frequency we must determine what decoded data to write to produce a low-frequency encoded signal. In order to understand the theory behind the choice of data patterns to write, it is necessary to take a brief look at the recording methods used in disk drives. The main limit on recording density is that as the bit density is increased, the peaks in the analog signal recorded on the media are read at a rate which may cause them to appear to overlap, creating intersymbol interference which leads to data errors. Traditional peak detector read channels try to reduce the possibility of intersymbol interference by coding data in such a way that the analog signal peaks are separated as far as possible. The read circuitry can then accurately detect the peaks (actually the head itself only detects transitions in magnetisation, so the simplest recording code uses a transition to encode a 1 and the absence of a transition to encode a 0. The transition causes a positive/negative peak in the head output voltage (thus the name "peak detector read channel"). To recover the data, we differentiate the output and look for the zero crossings). Since a long string of 0's will make clocking difficult, we need to set a limit on the maximum consecutive number of 0's. The separation of peaks is implemented as some form of run-length-limited, or RLL, coding. The RLL encoding used in most current drives is described by pairs of run-length limits (d, k), where d is the minimum number of 0 symbols which must occur between each 1 symbol in the encoded data, and k is the maximum. The parameters (d, k) are chosen to place adjacent 1's far enough apart to avoid problems with intersymbol interference, but not so far apart that we lose synchronisation. The grandfather of all RLL codes was FM, which wrote one user data bit followed by one clock bit, so that a 1 bit was encoded as two transitions (1 wavelength) while a 0 bit was encoded as one transition (½ wavelength). A different approach was taken in modified FM (MFM), which suppresses the clock bit except between adjacent 0's (the ambiguity in the use of the term MFM is unfortunate. From here on it will be used to refer to modified FM rather than magnetic force microscopy). Taking three example sequences 0000, 1111, and 1010, these will be encoded as 0(1)0(1)0(1)0, 1(0)1(0)1(0)1, and 1(0)0(0)1(0)0 (where the ()s are the clock bits inserted by the encoding process). The maximum time between 1 bits is now three 0 bits (so that the peaks are no more than four encoded time periods apart), and there is always at least one 0 bit (so that the peaks in the analog signal are at least two encoded time periods apart), resulting in a (1,3) RLL code. (1,3) RLL/MFM is the oldest code still in general use today, but is only really used in floppy drives which need to remain backwards-compatible. These constraints help avoid intersymbol interference, but the need to separate the peaks reduces the recording density and therefore the amount of data which can be stored on a disk. To increase the recording density, MFM was gradually replaced by (2,7) RLL (the original "RLL" format), and that in turn by (1,7) RLL, each of which placed less constraints on the recorded signal. Using our knowledge of how the data is encoded, we can now choose which decoded data patterns to write in order to obtain the desired encoded signal. The three encoding methods described above cover the vast majority of magnetic disk drives. However, each of these has several possible variants. With MFM, only one is used with any frequency, but the newest (1,7) RLL code has at least half a dozen variants in use. For MFM with at most four bit times between transitions, the lowest write frequency possible is attained by writing the repeating decoded data patterns 1010 and 0101. These have a 1 bit every other "data" bit, and the intervening "clock" bits are all 0. We would also like patterns with every other clock bit set to 1 and all others set to 0, but these are not possible in the MFM encoding (such "violations" are used to generate special marks on the disk to identify sector boundaries). The best we can do here is three bit times between transitions, which is generated by repeating the decoded patterns 100100, 010010 and 001001. We should use several passes with these patterns, as MFM drives are the oldest, lowest-density drives around (this is especially true for the very-low-density floppy drives). As such, they are the easiest to recover data from with modern equipment and we need to take the most care with them. From MFM we jump to the next simplest case, which is (1,7) RLL. Although there can be as many as 8 bit times between transitions, the lowest sustained frequency we can have in practice is 6 bit times between transitions. This is a desirable property from the point of view of the clock-recovery circuitry, and all (1,7) RLL codes seem to have this property. We now need to find a way to write the desired pattern without knowing the particular (1,7) RLL code used. We can do this by looking at the way the drives error-correction system works. The error- correction is applied to the decoded data, even though errors generally occur in the encoded data. In order to make this work well, the data encoding should have limited error amplification, so that an erroneous encoded bit should affect only a small, finite number of decoded bits. Decoded bits therefore depend only on nearby encoded bits, so that a repeating pattern of encoded bits will correspond to a repeating pattern of decoded bits. The repeating pattern of encoded bits is 6 bits long. Since the rate of the code is 2/3, this corresponds to a repeating pattern of 4 decoded bits. There are only 16 possibilities for this pattern, making it feasible to write all of them during the erase process. So to achieve good overwriting of (1,7) RLL disks, we write the patterns 0000, 0001, 0010, 0011, 0100, 0101, 0110, 0111, 1000, 1001, 1010, 1011, 1100, 1101, 1110, and 1111. These patterns also conveniently cover two of the ones needed for MFM overwrites, although we should add a few more iterations of the MFM-specific patterns for the reasons given above. Finally, we have (2,7) RLL drives. These are similar to MFM in that an eight-bit-time signal can be written in some phases, but not all. A six-bit-time signal will fill in the remaining cracks. Using a ½ encoding rate, an eight-bit-time signal corresponds to a repeating pattern of 4 data bits. The most common (2,7) RLL code is shown below: The most common (2,7) RLL Code Decoded Data (2,7) RLL Encoded Data 00 1000 01 0100 100 001000 101 100100 111 000100 1100 00001000 1101 00100100 The second most common (2,7) RLL code is the same but with the "decoded data" complemented, which doesn't alter these patterns. Writing the required encoded data can be achieved for every other phase using patterns of 0x33, 0x66, 0xCC and 0x99, which are already written for (1,7) RLL drives. Six-bit-time patterns can be written using 3-bit repeating patterns. The all-zero and all-one patterns overlap with the (1,7) RLL patterns, leaving six others: 001001001001001001001001 2 4 9 2 4 9 in binary or 0x24 0x92 0x49, 0x92 0x49 0x24 and 0x49 0x24 0x92 in hex, and 011011011011011011011011 6 D B 6 D B in binary or 0x6D 0xB6 0xDB, 0xB6 0xDB 0x6D and 0xDB 0x6D 0xB6 in hex. The first three are the same as the MFM patterns, so we need only three extra patterns to cover (2,7) RLL drives. Although (1,7) is more popular in recent (post-1990) drives, some older hard drives do still use (2,7) RLL, and with the ever-increasing reliability of newer drives it is likely that they will remain in use for some time to come, often being passed down from one machine to another. The above three patterns also cover any problems with endianness issues, which weren't a concern in the previous two cases, but would be in this case (actually, thanks to the strong influence of IBM mainframe drives, everything seems to be uniformly big-endian within bytes, with the most significant bit being written to the disk first). The latest high-density drives use methods like Partial-Response Maximum-Likelihood (PRML) encoding, which may be roughly equated to the trellis encoding done by V.32 modems in that it is effective but computationally expensive. PRML codes are still RLL codes, but with somewhat different constraints. A typical code might have (0,4,4) constraints in which the 0 means that 1's in a data stream can occur right next to 0's (so that peaks in the analog readback signal are not separated), the first 4 means that there can be no more than four 0's between 1's in a data stream, and the second 4 specifies the maximum number of 0's between 1's in certain symbol subsequences. PRML codes avoid intersymbol influence errors by using digital filtering techniques to shape the read signal to exhibit desired frequency and timing characteristics (this is the "partial response" part of PRML) followed by maximum- likelihood digital data detection to determine the most likely sequence of data bits that was written to the disk (this is the "maximum likelihood" part of PRML). PRML channels achieve the same low bit error rate as standard peak-detection methods, but with much higher recording densities, while using the same heads and media. Several manufacturers are currently engaged in moving their peak-detection-based product lines across to PRML, giving a 30-40% density increase over standard RLL channels [14]. Since PRML codes don't try to separate peaks in the same way that non-PRML RLL codes do, all we can do is to write a variety of random patterns because the processing inside the drive is too complex to second- guess. Fortunately, these drives push the limits of the magnetic media much more than older drives ever did by encoding data with much smaller magnetic domains, closer to the physical capacity of the magnetic media (the current state of the art in PRML drives has a track density of around 6700 TPI (tracks per inch) and a data recording density of 170 kFCI, nearly double that of the nearest (1,7) RLL equivalent. A convenient side-effect of these very high recording densities is that a written transition may experience the write field cycles for successive transitions, especially at the track edges where the field distribution is much broader [15]. Since this is also where remnant data is most likely to be found, this can only help in reducing the recoverability of the data). If these drives require sophisticated signal processing just to read the most recently written data, reading overwritten layers is also correspondingly more difficult. A good scrubbing with random data will do about as well as can be expected. We now have a set of 22 overwrite patterns which should erase everything, regardless of the raw encoding. The basic disk eraser can be improved slightly by adding random passes before and after the erase process, and by performing the deterministic passes in random order to make it more difficult to guess which of the known data passes were made at which point. To deal with all this in the overwrite process, we use the sequence of 35 consecutive writes shown below: Overwrite Data Pass No. Data Written Encoding Scheme Targeted 1 Random 2 Random 3 Random 4 Random 5 01010101 01010101 01010101 0x55 (1,7) RLL MFM 6 10101010 10101010 10101010 0xAA (1,7) RLL MFM 7 10010010 01001001 00100100 0x92 0x49 0x24 (2,7) RLL MFM 8 01001001 00100100 10010010 0x49 0x24 0x92 (2,7) RLL MFM 9 00100100 10010010 01001001 0x24 0x92 0x49 (2,7) RLL MFM 10 00000000 00000000 00000000 0x00 (1,7) RLL (2,7) RLL 11 00010001 00010001 00010001 0x11 (1,7) RLL 12 00100010 00100010 00100010 0x22 (1,7) RLL 13 00110011 00110011 00110011 0x33 (1,7) RLL (2,7) RLL 14 01000100 01000100 01000100 0x44 (1,7) RLL 15 01010101 01010101 01010101 0x55 (1,7) RLL MFM 16 01100110 01100110 01100110 0x66 (1,7) RLL (2,7) RLL 17 01110111 01110111 01110111 0x77 (1,7) RLL 18 10001000 10001000 10001000 0x88 (1,7) RLL 19 10011001 10011001 10011001 0x99 (1,7) RLL (2,7) RLL 20 10101010 10101010 10101010 0xAA (1,7) RLL MFM 21 10111011 10111011 10111011 0xBB (1,7) RLL 22 11001100 11001100 11001100 0xCC (1,7) RLL (2,7) RLL 23 11011101 11011101 11011101 0xDD (1,7) RLL 24 11101110 11101110 11101110 0xEE (1,7) RLL 25 11111111 11111111 11111111 0xFF (1,7) RLL (2,7) RLL 26 10010010 01001001 00100100 0x92 0x49 0x24 (2,7) RLL MFM 27 01001001 00100100 10010010 0x49 0x24 0x92 (2,7) RLL MFM 28 00100100 10010010 01001001 0x24 0x92 0x49 (2,7) RLL MFM 29 01101101 10110110 11011011 0x6D 0xB6 0xDB (2,7) RLL 30 10110110 11011011 01101101 0xB6 0xDB 0x6D (2,7) RLL 31 11011011 01101101 10110110 0xDB 0x6D 0xB6 (2,7) RLL 32 Random 33 Random 34 Random 35 Random The MFM-specific patterns are repeated twice because MFM drives have the lowest density and are thus particularly easy to examine. The deterministic patterns between the random writes are permuted before the write is performed, to make it more difficult for an opponent to use knowledge of the erasure data written to attempt to recover overwritten data (in fact we need to use a cryptographically strong random number generator to perform the permutations to avoid the problem of an opponent who can read the last overwrite pass being able to predict the previous passes and "echo cancel" passes by subtracting the known overwrite data). If the device being written to supports caching or buffering of data, this should be disabled to ensure that physical disk writes are performed for each pass instead of everything but the last pass being lost in the buffering. For example physical disk access can be forced during SCSI-2 Group 1 write commands by setting the Force Unit Access bit in the SCSI command block (although at least one popular drive has a bug which causes all writes to be ignored when this bit is set - remember to test your overwrite scheme before you deploy it). Another consideration which needs to be taken into account when trying to erase data through software is that drives conforming to some of the higher-level protocols such as the various SCSI standards are relatively free to interpret commands sent to them in whichever way they choose (as long as they still conform to the SCSI specification). Thus some drives, if sent a FORMAT UNIT command may return immediately without performing any action, may simply perform a read test on the entire disk (the most common option), or may actually write data to the disk (the SCSI- 2 standard includes an initialization pattern (IP) option for the FORMAT UNIT command, however this is not necessarily supported by existing drives). If the data is very sensitive and is stored on floppy disk, it can best be destroyed by removing the media from the disk liner and burning it, or by burning the entire disk, liner and all (most floppy disks burn remarkably well - albeit with quantities of oily smoke - and leave very little residue). 4. Other Methods of Erasing Magnetic Media The previous section has concentrated on erasure methods which require no specialised equipment to perform the erasure. Alternative means of erasing media which do require specialised equipment are degaussing (a process in which the recording media is returned to its initial state) and physical destruction. Degaussing is a reasonably effective means of purging data from magnetic disk media, and will even work through most drive cases (research has shown that the aluminium housings of most disk drives attenuate the degaussing field by only about 2 dB [16]). The switching of a single-domain magnetic particle from one magnetization direction to another requires the overcoming of an energy barrier, with an external magnetic field helping to lower this barrier. The switching depends not only on the magnitude of the external field, but also on the length of time for which it is applied. For typical disk drive media, the short-term field needed to flip enough of the magnetic domains to be useful in recording a signal is about 1/3 higher than the coercivity of the media (the exact figure varies with different media types) [17]. However, to effectively erase a medium to the extent that recovery of data from it becomes uneconomical requires a magnetic force of about five times the coercivity of the medium [18], although even small external magnetic fields are sufficient to upset the normal operation of a hard disk (typically a few gauss at DC, dropping to a few milligauss at 1 MHz). Coercivity (measured in Oersteds, Oe) is a property of magnetic material and is defined as the amount of magnetic field necessary to reduce the magnetic induction in the material to zero - the higher the coercivity, the harder it is to erase data from a medium. Typical figures for various types of magnetic media are given below: Typical Media Coercivity Figures Medium Coercivity 5.25" 360K floppy disk 300 Oe 5.25" 1.2M floppy disk 675 Oe 3.5" 720K floppy disk 300 Oe 3.5" 1.44M floppy disk 700 Oe 3.5" 2.88M floppy disk 750 Oe 3.5" 21M floptical disk 750 Oe Older (1980's) hard disks 900-1400 Oe Newer (1990's) hard disks 1400-2200 Oe 1/2" magnetic tape 300 Oe 1/4" QIC tape 550 Oe 8 mm metallic particle tape 1500 Oe DAT metallic particle tape 1500 Oe US Government guidelines class tapes of 350 Oe coercivity or less as low-energy or Class I tapes and tapes of 350-750 Oe coercivity as high-energy or Class II tapes. Degaussers are available for both types of tapes. Tapes of over 750 Oe coercivity are referred to as Class III, with no known degaussers capable of fully erasing them being known [19], since even the most powerful commercial AC degausser cannot generate the recommended 7,500 Oe needed for full erasure of a typical DAT tape currently used for data backups. Degaussing of disk media is somewhat more difficult - even older hard disks generally have a coercivity equivalent to Class III tapes, making them fairly difficult to erase at the outset. Since manufacturers rate their degaussers in peak gauss and measure the field at a certain orientation which may not be correct for the type of medium being erased, and since degaussers tend to be rated by whether they erase sufficiently for clean rerecording rather than whether they make the information impossible to recover, it may be necessary to resort to physical destruction of the media to completely sanitise it (in fact since degaussing destroys the sync bytes, ID fields, error correction information, and other paraphernalia needed to identify sectors on the media, thus rendering the drive unusable, it makes the degaussing process mostly equivalent to physical destruction). In addition, like physical destruction, it requires highly specialised equipment which is expensive and difficult to obtain (one example of an adequate degausser was the 2.5 MW Navy research magnet used by a former Pentagon site manager to degauss a 14" hard drive for 1½ minutes. It bent the platters on the drive and probably succeeded in erasing it beyond the capabilities of any data recovery attempts [20]). 5. Further Problems with Magnetic Media A major issue which cannot be easily addressed using any standard software-based overwrite technique is the problem of defective sector handling. When the drive is manufactured, the surface is scanned for defects which are added to a defect list or flaw map. If further defects, called grown defects, occur during the life of the drive, they are added to the defect list by the drive or by drive management software. There are several techniques which are used to mask the defects in the defect list. The first, alternate tracks, moves data from tracks with defects to known good tracks. This scheme is the simplest, but carries a high access cost, as each read from a track with defects requires seeking to the alternate track and a rotational latency delay while waiting for the data location to appear under the head, performing the read or write, and, if the transfer is to continue onto a neighbouring track, seeking back to the original position. Alternate tracks may be interspersed among data tracks to minimise the seek time to access them. A second technique, alternate sectors, allocates alternate sectors at the end of the track to minimise seeks caused by defective sectors. This eliminates the seek delay, but still carries some overhead due to rotational latency. In addition it reduces the usable storage capacity by 1-3%. A third technique, inline sector sparing, again allocates a spare sector at the end of each track, but resequences the sector ID's to skip the defective sector and include the spare sector at the end of the track, in effect pushing the sectors past the defective one towards the end of the track. The associated cost is the lowest of the three, being one sector time to skip the defective sector [21]. The handling of mapped-out sectors and tracks is an issue which can't be easily resolved without the cooperation of hard drive manufacturers. Although some SCSI and IDE hard drives may allow access to defect lists and even to mapped-out areas, this must be done in a highly manufacturer- and drive-specific manner. For example the SCSI-2 READ DEFECT DATA command can be used to obtain a list of all defective areas on the drive. Since SCSI logical block numbers may be mapped to arbitrary locations on the disk, the defect list is recorded in terms of heads, tracks, and sectors. As all SCSI device addressing is performed in terms of logical block numbers, mapped-out sectors or tracks cannot be addressed. The only reasonably portable possibility is to clear various automatic correction flags in the read-write error recovery mode page to force the SCSI device to report read/write errors to the user instead of transparently remapping the defective areas. The user can then use the READ LONG and WRITE LONG commands (which allow access to sectors and extra data even in the presence of read/write errors), to perform any necessary operations on the defective areas, and then use the REASSIGN BLOCKS command to reassign the defective sections. However this operation requires an in-depth knowledge of the operation of the SCSI device and extensive changes to disk drivers, and more or less defeats the purpose of having an intelligent peripheral. The ANSI X3T-10 and X3T-13 subcommittees are currently looking at creating new standards for a Universal Security Reformat command for IDE and SCSI hard disks which will address these issues. This will involve a multiple-pass overwrite process which covers mapped-out disk areas with deliberate off-track writing. Many drives available today can be modified for secure erasure through a firmware upgrade, and once the new firmware is in place the erase procedure is handled by the drive itself, making unnecessary any interaction with the host system beyond the sending of the command which begins the erase process. Long-term ageing can also have a marked effect on the erasability of magnetic media. For example, some types of magnetic tape become increasingly difficult to erase after being stored at an elevated temperature or having contained the same magnetization pattern for a considerable period of time [22]. The same applies for magnetic disk media, with decreases in erasability of several dB being recorded [23]. The erasability of the data depends on the amount of time it has been stored on the media, not on the age of the media itself (so that, for example, a five-year-old freshly-written disk is no less erasable than a new freshly-written disk). The dependence of media coercivity on temperature can affect overwrite capability if the data was initially recorded at a temperature where the coercivity was low (so that the recorded pattern penetrated deep into the media), but must be overwritten at a temperature where the coercivity is relatively high. This is important in hard disk drives, where the temperature varies depending on how long the unit has been used and, in the case of drives with power-saving features enabled, how recently and frequently it has been used. However the overwrite performance depends not only on temperature-dependent changes in the media, but also on temperature-dependent changes in the read/write head. Thankfully the combination of the most common media used in current drives with various common types of read/write heads produce a change in overwrite performance of only a few hundredths of a decibel per degree over the temperature range -40øC to + 40øC, as changes in the head compensate for changes in the media [24]. Another issue which needs to be taken into account is the ability of most newer storage devices to recover from having a remarkable amount of damage inflicted on them through the use of various error-correction schemes. As increasing storage densities began to lead to multiple-bit errors, manufacturers started using sophisticated error-correction codes (ECC's) capable of correcting multiple error bursts. A typical drive might have 512 bytes of data, 4 bytes of CRC, and 11 bytes of ECC per sector. This ECC would be capable of correcting single burst errors of up to 22 bits or double burst errors of up to 11 bits, and can detect a single burst error of up to 51 bits or three burst errors of up to 11 bits in length [25]. Another drive manufacturer quotes the ability to correct up to 120 bits, or up to 32 bits on the fly, using 198-bit Reed-Solomon ECC [26]. Therefore even if some data is reliably erased, it may be possible to recover it using the built-in error-correction capabilities of the drive. Conversely, any erasure scheme which manages to destroy the ECC information (for example through the use of the SCSI-2 WRITE LONG command which can be used to write to areas of a disk sector outside the normal data areas) stands a greater chance of making the data unrecoverable. 6. Sidestepping the Problem The easiest way to solve the problem of erasing sensitive information from magnetic media is to ensure that it never gets to the media in the first place. Although not practical for general data, it is often worthwhile to take steps to keep particularly important information such as encryption keys from ever being written to disk. This would typically happen when the memory containing the keys is paged out to disk by the operating system, where they can then be recovered at a later date, either manually or using software which is aware of the in-memory data format and can locate it automatically in the swap file (for example there exists software which will search the Windows swap file for keys from certain DOS encryption programs). An even worse situation occurs when the data is paged over a network, allowing anyone with a packet sniffer or similar tool on the same subnet to observe the information (for example there exists software which will monitor and even alter NFS traffic on the fly which could be modified to look for known in-memory data patterns moving to and from a networked swap disk [27]). To solve these problems the memory pages containing the information can be locked to prevent them from being paged to disk or transmitted over a network. This approach is taken by at least one encryption library, which allocates all keying information inside protected memory blocks visible to the user only as opaque handles, and then optionally locks the memory (provided the underlying OS allows it) to prevent it from being paged [28]. The exact details of locking pages in memory depend on the operating system being used. Many Unix systems now support the mlock()/munlock() calls or have some alternative mechanism hidden among the mmap()-related functions which can be used to lock pages in memory. Unfortunately these operations require superuser priviledges because of their potential impact on system performance if large ranges of memory are locked. Other systems such as Microsoft Windows NT allow user processes to lock memory with the VirtualLock()/VirtualUnlock() calls, but limit the total number of regions which can be locked. Most paging algorithms are relatively insensitive to having sections of memory locked, and can even relocate the locked pages (since the logical to physical mapping is invisible to the user), or can move the pages to a "safe" location when the memory is first locked. The main effect of locking pages in memory is to increase the minimum working set size which, taken in moderation, has little noticeable effect on performance. The overall effects depend on the operating system and/or hardware implementations of virtual memory. Most Unix systems have a global page replacement policy in which a page fault may be satisfied by any page frame. A smaller number of operating systems use a local page replacement policy in which pages are allocated from a fixed (or occasionally dynamically variable) number of page frames allocated on a per- process basis. This makes them much more sensitive to the effects of locking pages, since every locked page decreases the (finite) number of pages available to the process. On the other hand it makes the system as a whole less sensitive to the effects of one process locking a large number of pages. The main effective difference between the two is that under a local replacement policy a process can only lock a small fixed number of pages without affecting other processes, whereas under a global replacement policy the number of pages a process can lock is determined on a system-wide basis and may be affected by other processes. In practice neither of these allocation strategies seem to cause any real problems. Although any practical measurements are very difficult to perform since they vary wildly depending on the amount of physical memory present, paging strategy, operating system, and system load, in practice locking a dozen 1K regions of memory (which might be typical of a system on which a number of users are running programs such as mail encryption software) produced no noticeable performance degradation observable by system- monitoring tools. On machines such as network servers handling large numbers of secure connections (for example an HTTP server using SSL), the effects of locking large numbers of pages may be more noticeable. 7. Methods of Recovery for Data stored in Random-Access Memory Contrary to conventional wisdom, "volatile" semiconductor memory does not entirely lose its contents when power is removed. Both static (SRAM) and dynamic (DRAM) memory retains some information on the data stored in it while power was still applied. SRAM is particularly susceptible to this problem, as storing the same data in it over a long period of time has the effect of altering the preferred power-up state to the state which was stored when power was removed. Older SRAM chips could often "remember" the previously held state for several days. In fact, it is possible to manufacture SRAM's which always have a certain state on power-up, but which can be overwritten later on - a kind of "writeable ROM". DRAM can also "remember" the last stored state, but in a slightly different way. It isn't so much that the charge (in the sense of a voltage appearing across a capacitance) is retained by the RAM cells, but that the thin oxide which forms the storage capacitor dielectric is highly stressed by the applied field, or is not stressed by the field, so that the properties of the oxide change slightly depending on the state of the data. One thing that can cause a threshold shift in the RAM cells is ionic contamination of the cell(s) of interest, although such contamination is rarer now than it used to be because of robotic handling of the materials and because the purity of the chemicals used is greatly improved. However, even a perfect oxide is subject to having its properties changed by an applied field. When it comes to contaminants, sodium is the most common offender - it is found virtually everywhere, and is a fairly small (and therefore mobile) atom with a positive charge. In the presence of an electric field, it migrates towards the negative pole with a velocity which depends on temperature, the concentration of the sodium, the oxide quality, and the other impurities in the oxide such as dopants from the processing. If the electric field is zero and given enough time, this stress tends to dissipate eventually. The stress on the cell is a cumulative effect, much like charging an RC circuit. If the data is applied for only a few milliseconds then there is very little "learning" of the cell, but if it is applied for hours then the cell will acquire a strong (relatively speaking) change in its threshold. The effects of the stress on the RAM cells can be measured using the built-in self test capabilities of the cells, which provide the ability to impress a weak voltage on a storage cell in order to measure its margin. Cells will show different margins depending on how much oxide stress has been present. Many DRAM's have undocumented test modes which allow some normal I/O pin to become the power supply for the RAM core when the special mode is active. These test modes are typically activated by running the RAM in a nonstandard configuration, so that a certain set of states which would not occur in a normally-functioning system has to be traversed to activate the mode. Manufacturers won't admit to such capabilities in their products because they don't want their customers using them and potentially rejecting devices which comply with their spec sheets, but have little margin beyond that. A simple but somewhat destructive method to speed up the annihilation of stored bits in semiconductor memory is to heat it. Both DRAM's and SRAM's will lose their contents a lot more quickly at Tjunction = 140øC than they will at room temperature. Several hours at this temperature with no power applied will clear their contents sufficiently to make recovery difficult. Conversely, to extend the life of stored bits with the power removed, the temperature should be dropped below -60øC. Such cooling should lead to weeks, instead of hours or days, of data retention. 8. Erasure of Data stored in Random-Access Memory Simply repeatedly overwriting the data held in DRAM with new data isn't nearly as effective as it is for magnetic media. The new data will begin stressing or relaxing the oxide as soon as it is written, and the oxide will immediately begin to take a "set" which will either reinforce the previous "set" or will weaken it. The greater the amount of time that new data has existed in the cell, the more the old stress is "diluted", and the less reliable the information extraction will be. Generally, the rates of change due to stress and relaxation are in the same order of magnitude. Thus, a few microseconds of storing the opposite data to the currently stored value will have little effect on the oxide. Ideally, the oxide should be exposed to as much stress at the highest feasible temperature and for as long as possible to get the greatest "erasure" of the data. Unfortunately if carried too far this has a rather detrimental effect on the life expectancy of the RAM. Therefore the goal to aim for when sanitising memory is to store the data for as long as possible rather than trying to change it as often as possible. Conversely, storing the data for as short a time as possible will reduce the chances of it being "remembered" by the cell. Based on tests on DRAM cells, a storage time of one second causes such a small change in threshold that it probably isn't detectable. On the other hand, one minute is probably detectable, and 10 minutes is certainly detectable. The most practical solution to the problem of DRAM data retention is therefore to constantly flip the bits in memory to ensure that a memory cell never holds a charge long enough for it to be "remembered". While not practical for general use, it is possible to do this for small amounts of very sensitive data such as encryption keys. This is particularly advisable where keys are stored in the same memory location for long periods of time and control access to large amounts of information, such as keys used for transparent encryption of files on disk drives. The bit-flipping also has the convenient side-effect of keeping the page containing the encryption keys at the top of the queue maintained by the system's paging mechanism, greatly reducing the chances of it being paged to disk at some point. 9. Conclusion Data overwritten once or twice may be recovered by subtracting what is expected to be read from a storage location from what is actually read. Data which is overwritten an arbitrarily large number of times can still be recovered provided that the new data isn't written to the same location as the original data (for magnetic media), or that the recovery attempt is carried out fairly soon after the new data was written (for RAM). For this reason it is effectively impossible to sanitise storage locations by simple overwriting them, no matter how many overwrite passes are made or what data patterns are written. However by using the relatively simple methods presented in this paper the task of an attacker can be made significantly more difficult, if not prohibitively expensive. Acknowledgments The author would like to thank Nigel Bree, Peter Fenwick, Andy Hospodor, Kevin Martinez, Colin Plumb, and Charles Preston for their advice and input during the preparation of this paper. References [1] "Emergency Destruction of Information Storing Media", M.Slusarczuk et al, Institute for Defense Analyses, December 1987. [2] "A Guide to Understanding Data Remanence in Automated Information Systems", National Computer Security Centre, September 1991. [3] "Detection of Digital Information from Erased Magnetic Disks", Venugopal Veeravalli, Masters thesis, Carnegie-Mellon University, 1987. [4] "Magnetic force microscopy: General principles and application to longitudinal recording media", D.Rugar, H.Mamin, P.Guenther, S.Lambert, J.Stern, I.McFadyen, and T.Yogi, Journal of Applied Physics, Vol.68, No.3 (August 1990), p.1169. [5] "Tunneling-stabilized Magnetic Force Microscopy of Bit Tracks on a Hard Disk", Paul Rice and John Moreland, IEEE Trans.on Magnetics, Vol.27, No.3 (May 1991), p.3452. [6] "NanoTools: The Homebrew STM Page", Jim Rice, NanoTools: The Homebrew STM Page. [7] "Magnetic Force Scanning Tunnelling Microscope Imaging of Overwritten Data", Romel Gomez, Amr Adly, Isaak Mayergoyz, Edward Burke, IEEE Trans.on Magnetics, Vol.28, No.5 (September 1992), p.3141. [8] "Comparison of Magnetic Fields of Thin-Film Heads and Their Corresponding Patterns Using Magnetic Force Microscopy", Paul Rice, Bill Hallett, and John Moreland, IEEE Trans.on Magnetics, Vol.30, No.6 (November 1994), p.4248. [9] "Computation of Magnetic Fields in Hysteretic Media", Amr Adly, Isaak Mayergoyz, Edward Burke, IEEE Trans.on Magnetics, Vol.29, No.6 (November 1993), p.2380. [10] "Magnetic Force Microscopy Study of Edge Overwrite Characteristics in Thin Film Media", Jian- Gang Zhu, Yansheng Luo, and Juren Ding, IEEE Trans.on Magnetics, Vol.30, No.6 (November 1994), p.4242. [11] "Microscopic Investigations of Overwritten Data", Romel Gomez, Edward Burke, Amr Adly, Isaak Mayergoyz, J.Gorczyca, Journal of Applied Physics, Vol.73, No.10 (May 1993), p.6001. [12] "Relationship between Overwrite and Transition Shift in Perpendicular Magnetic Recording", Hiroaki Muraoka, Satoshi Ohki, and Yoshihisa Nakamura, IEEE Trans.on Magnetics, Vol.30, No.6 (November 1994), p.4272. [13] "Effects of Current and Frequency on Write, Read, and Erase Widths for Thin-Film Inductive and Magnetoresistive Heads", Tsann Lin, Jodie Christner, Terry Mitchell, Jing-Sheng Gau, and Peter George, IEEE Trans.on Magnetics, Vol.25, No.1 (January 1989), p.710. [14] "PRML Read Channels: Bringing Higher Densities and Performance to New-Generation Hard Drives", Quantum Corporation, 1995. [15] "Density and Phase Dependence of Edge Erase Band in MR/Thin Film Head Recording", Yansheng Luo, Terence Lam, Jian-Gang Zhu, IEEE Trans.on Magnetics, Vol.31, No.6 (November 1995), p.3105. [16] "A Guide to Understanding Data Remanence in Automated Information Systems", National Computer Security Centre, September 1991. [17] "Time-dependant Magnetic Phenomena and Particle-size Effects in Recording Media", IEEE Trans.on Magnetics, Vol.26, No.1 (January 1990), p.193. [18] "The Data Dilemna", Charles Preston, Security Management Journal, February 1995. [19] "Magnetic Tape Degausser", NSA/CSS Specification L14-4-A, 31 October 1985. [20] "How many times erased does DoD want?", David Hayes, posting to comp.periphs.scsi newsgroup, 24 July 1991, message-ID 1991Jul24.050701.16005@sulaco.lone star.org. [21] "The Changing Nature of Disk Controllers", Andrew Hospodor and Albert Hoagland, Proceedings of the IEEE, Vol.81, No.4 (April 1993), p.586. [22] "Annealing Study of the Erasability of High Energy Tapes", L.Lekawat, G.Spratt, and M.Kryder, IEEE Trans.on Magnetics, Vol.29, No.6 (November 1993), p.3628. [23] "The Effect of Aging on Erasure in Particulate Disk Media", K.Mountfield and M.Kryder, IEEE Trans.on Magnetics, Vol.25, No 5 (September 1989), p.3638. [24] "Overwrite Temperature Dependence for Magnetic Recording", Takayuki Takeda, Katsumichi Tagami, and Takaaki Watanabe, Journal of Applied Physics, Vol.63, No.8 (April 1988), p.3438. [25] Conner 3.5" hard drive data sheets, 1994, 1995. [26] "Technology and Time-to-Market: The Two Go Hand-in-Hand", Quantum Corporation, 1995. [27] "Basic Flaws in Internet Security and Commerce", Paul Gauthier, posting to comp.security.unix newsgroup, 9 October 1995, message-ID gauthier.813274073@espresso.cs.ber keley.edu. [28] "cryptlib Free Encryption Library", Peter Gutmann, cryptlib. _________________________________________________________________ Secure Deletion of Data from Magnetic and Solid-State Memory / Peter Gutmann / pgut001@cs.auckland.ac.nz secure-delete-3.1.orig/rm-fileutil-4.1.diff0100644000175000017500000001616507655463376020702 0ustar robertlerobertle--- remove.c.orig 2003-05-05 15:15:48.000000000 +0200 +++ remove.c 2003-05-05 15:26:08.000000000 +0200 @@ -26,6 +26,12 @@ #include #include +/* SRM BEGIN */ +#include +#include +#include +/* SRM END */ + #if HAVE_STDBOOL_H # include #else @@ -81,6 +87,18 @@ int euidaccess (); int yesno (); +/* SRM BEGIN */ +#define BLOCKSIZE 32769 +#define DIR_SEPERATOR '/' +#define FLUSH sync() +#define RANDOM_DEVICE "/dev/urandom" +static int secure = 0; +static char fillbuf[BLOCKSIZE]; +static FILE *devrandom; +/* Functions */ +static int secure_delete(); +/* SRM END */ + extern char *program_name; /* state initialized by remove_init, freed by remove_fini */ @@ -605,6 +623,103 @@ return status; } +/* SRM BEGIN */ +void fill_buf(char pattern[3]) { + int loop; + int where; + for (loop = 0; loop < (BLOCKSIZE / 3); loop++) { + where = loop * 3; + fillbuf[where] = pattern[0]; + fillbuf[where+1] = pattern[1]; + fillbuf[where+2] = pattern[2]; + } +} + +void random_buf() { + int loop; + if (devrandom != NULL) + for (loop = 0; loop < BLOCKSIZE; loop++) + fillbuf[loop] = (unsigned char) (256.0*rand()/(RAND_MAX+1.0)); + else + fread(&fillbuf, BLOCKSIZE, 1, devrandom); +} + +/* overwriting the file several times */ + static int + secure_delete (delfile) + char *delfile; + { + unsigned char write_modes[27][3] = { + {"\x55\x55\x55"}, {"\xaa\xaa\xaa"}, {"\x92\x49\x24"}, {"\x49\x24\x92"}, + {"\x24\x92\x49"}, {"\x00\x00\x00"}, {"\x11\x11\x11"}, {"\x22\x22\x22"}, + {"\x33\x33\x33"}, {"\x44\x44\x44"}, {"\x55\x55\x55"}, {"\x66\x66\x66"}, + {"\x77\x77\x77"}, {"\x88\x88\x88"}, {"\x99\x99\x99"}, {"\xaa\xaa\xaa"}, + {"\xbb\xbb\xbb"}, {"\xcc\xcc\xcc"}, {"\xdd\xdd\xdd"}, {"\xee\xee\xee"}, + {"\xff\xff\xff"}, {"\x92\x49\x24"}, {"\x49\x24\x92"}, {"\x24\x92\x49"}, + {"\x6d\xb6\xdb"}, {"\xb6\xdb\x6d"}, {"\xdb\x6d\xb6"} + }; + unsigned char std_array[3] = "\xff\xff\xff"; + FILE *f; + int file; + unsigned long writes; + unsigned long counter; + unsigned long filesize; + struct stat filestat; + int turn; + int result; + unsigned char ch; + char newname[512]; + +/* open the file, get the filesize, calculate the numbers of needed writings */ + if (lstat(delfile, &filestat)) + return 1; + if (! S_ISREG(filestat.st_mode)) + return 1; + if ((f = fopen(delfile, "r+b")) == NULL) + return 1; + filesize = filestat.st_size; + writes = (1 + (filesize / BLOCKSIZE)); + file=fileno(f); + (void) setvbuf(stdout, NULL, _IONBF, 0); + devrandom = fopen(RANDOM_DEVICE, "r"); + + if (secure > 1) { + fill_buf(std_array); + for (counter=1; counter<=writes; counter++) + fwrite(&fillbuf, 1, BLOCKSIZE, f); + fflush(f); + fsync(file); + } + +/* do the overwriting stuff */ + for (turn=0; turn<=36; turn++) { + rewind(f); + if ((secure < 3) && (turn > 0)) break; + if ((turn>=5) && (turn<=31)) { + fill_buf(write_modes[turn-5]); + for (counter=1; counter<=writes; counter++) + fwrite(&fillbuf, 1, BLOCKSIZE, f); + } else { + for (counter=1; counter<=writes; counter++) { + random_buf(); + fwrite(&fillbuf, 1, BLOCKSIZE, f); + } + } + fflush(f); + if (fsync(file) < 0) + FLUSH; + } + (void) fclose(f); + (void) fclose(devrandom); +/* Hard Flush -> Force cached data to be written to disk */ + FLUSH; +/* open + truncating the file, so an attacker doesn't know the diskblocks */ + if ((file = open(delfile, O_WRONLY | O_TRUNC)) >= 0) + close(file); + return 0; +} +/* SRM END */ + /* Query the user if appropriate, and if ok try to remove the file or directory specified by FS. Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED if not. */ @@ -646,9 +761,16 @@ return RM_USER_DECLINED; } - if (x->verbose) - printf (_("removing %s\n"), quote (full_filename (pathname))); + if (x->verbose) { + if (secure) + printf (_("wiping %s\n"), quote (full_filename (pathname))); + else + printf (_("removing %s\n"), quote (full_filename (pathname))); + } + if (secure) + secure_delete(pathname); + if (unlink (pathname) && (errno != ENOENT || !x->ignore_missing_files)) { error (0, errno, _("cannot unlink %s"), quote (full_filename (pathname))); @@ -821,6 +943,8 @@ { mode_t filetype_mode; + secure = x->secure; + if (user_specified_name) { /* CAUTION: this use of base_name works only because any --- rm.c.orig 2003-05-05 14:58:35.000000000 +0200 +++ rm.c 2003-05-05 15:16:31.000000000 +0200 @@ -58,7 +58,7 @@ #define PROGRAM_NAME "rm" #define AUTHORS \ - "Paul Rubin, David MacKenzie, Richard Stallman, and Jim Meyering" + "Paul Rubin, David MacKenzie, Richard Stallman, Jim Meyering, and van Hauser" void strip_trailing_slashes (); @@ -71,6 +71,9 @@ {"force", no_argument, NULL, 'f'}, {"interactive", no_argument, NULL, 'i'}, {"recursive", no_argument, NULL, 'r'}, +/* SRM BEGIN */ + {"secure", no_argument, NULL, 's'}, +/* SRM END */ {"verbose", no_argument, NULL, 'v'}, {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, @@ -93,6 +96,7 @@ -f, --force ignore nonexistent files, never prompt\n\ -i, --interactive prompt before any removal\n\ -r, -R, --recursive remove the contents of directories recursively\n\ + -s, --secure secure overwrite (-sss for full security)\n\ -v, --verbose explain what is being done\n\ --help display this help and exit\n\ --version output version information and exit\n\ @@ -105,10 +109,11 @@ \n\ Note that if you use rm to remove a file, it is usually possible to recover\n\ the contents of that file. If you want more assurance that the contents are\n\ -truly unrecoverable, consider using shred.\n\ +truly unrecoverable, use the -s option.\n\ "), program_name, program_name); puts (_("\nReport bugs to .")); + puts (_("\nWith secure_delete patch by van Hauser ")); } exit (status); } @@ -122,6 +127,7 @@ x->recursive = 0; x->stdin_tty = isatty (STDIN_FILENO); x->verbose = 0; + x->secure = 0; } int @@ -140,7 +146,7 @@ rm_option_init (&x); - while ((c = getopt_long (argc, argv, "dfirvR", long_opts, NULL)) != -1) + while ((c = getopt_long (argc, argv, "dfirsvR", long_opts, NULL)) != -1) { switch (c) { @@ -161,6 +167,11 @@ case 'R': x.recursive = 1; break; +/* SRM BEGIN */ + case 's': + x.secure++; + break; +/* SRM END */ case 'v': x.verbose = 1; break; --- remove.h.orig 2003-05-05 15:13:47.000000000 +0200 +++ remove.h 2003-05-05 15:14:13.000000000 +0200 @@ -18,6 +18,9 @@ Only works for the super-user. */ int unlink_dirs; + /* If nonzero, secure overwrites file contents */ + int secure; + /* If nonzero, display the name of each file removed. */ int verbose; }; secure-delete-3.1.orig/configure0100755000175000017500000000007007750313150017166 0ustar robertlerobertle#!/bin/sh echo "Just run \"make\" and \"make install\""