pax_global_header00006660000000000000000000000064130065606220014512gustar00rootroot0000000000000052 comment=796b62293e007546e051619bd03f5ba338ef28e5 wipe-0.24/000077500000000000000000000000001300656062200124035ustar00rootroot00000000000000wipe-0.24/.gitignore000066400000000000000000000000161300656062200143700ustar00rootroot00000000000000*.o version.h wipe-0.24/BUGS000066400000000000000000000015051300656062200130670ustar00rootroot00000000000000 Known Bugs and Problems Tue Jul 27 14:28:16 EEST 1999 Some file systems, such as ext2 can report incorrect block sizes when examining files via stat (); for example, on a 1024-byte ext2 file system, the file system block size is reported as being 4096. This can cause minor problems (short writes) when wiping a file whose size is not a multiple of the reported block size on a file system having no more free blocks, since wipe will by default round up the size to the next multiple of the reported block size in order to erase possible data remaining beyond the end of the file (this can happen if the file shrinked, for example). I can't think of any non-invasive method for getting the real block size. Since this is a very small problem, I think that it is not worth adding an option for specifying the real block size. wipe-0.24/CHANGES000066400000000000000000000153131300656062200134010ustar00rootroot00000000000000Sat Feb 14 18:30:16 CET 1998 version 0.02 - Fixed behavioural bug: wouldn't chmod directories. Fri Jul 24 13:45:02 EEST 1998 version 0.03 - Added silent option. Thu Aug 6 11:35:13 EEST 1998 version 0.10 - Major changes in this version added many conditional compilation statements for portability. now has got auxiliary random seed sources, which can be used if a /dev/random device is not available. now can use RC6 for generating the cryptographically-strong random stream for overwriting the data, as well as arcfour. restructured source code. currently compiles under Linux 2.0.35, SunOS 5.5.1 and AIX 4.1. Tue Aug 11 11:01:36 EEST 1998 version 0.11 - Added compilation settings for Solaris 2.6 x86/SPARC, FreeBSD 2.2.6-STABLE; fixed a bug in the pipe handling in random.c (thanks to C.Mason for these). lengthened the period of the RC6-based PRNG from an average 2^127 to a full 2^128 (thanks to John Kelsey for explaining the period issues). Wed Apr 14 15:32:54 EEST 1999 version 0.12 - Fixed a very SERIOUS bug in fill_random: the upper half of the target buffer was NOT filled, therefore HALF OF THE DATA was not subject to the random passes. now wipe works on block as well as character devices. wipe also tries to wipe file names and file sizes. new options for letting the wiped files on the filesystem and inhibiting file name and file size wiping. Fri Jun 18 14:47:27 CEST 1999 version 0.13 - This version is SERIOUSLY BROKEN. curiously the serious bug that should have been fixed in version 0.12 popped up again. Thanks to Michael S. Rhee for his diagnostic. I did some major changes in wipe's inner workings. It now precalculates the different fixed-pattern buffers. It is now able to do asynchronous i/o to some extent, so that it can fill the random buffers while the OS or the hardware is busy writing out changes. However asynchronous i/o (i.e. the O_NONBLOCK flag) is not supported in current Linux kernels. The way to do it would be to use a separate thread (or a process but that would require use shared memory). However it is faster now. Sun Jun 20 17:16:30 CEST 1999 version 0.14 - Consider version 0.13 as SERIOUSLY BROKEN. Filename wiping has been cleaned up. Two extra options for filename wiping have been added. Various bugs have been fixed. This version is bug-free and 100% reliable. Mon Jul 26 22:14:45 EEST 1999 version 0.15 - Some minor fixes. Thanks to Thomas Schoepf and Alexey Marinichev for their bug-reports and patches: wipe was crashing when it was operating on a single zero-length file. Also, wipe was screwing up when attempting to rename a file (during filename wiping). Furthermore, wipe will no more ask for confirmation over stderr if stderr is not a tty; it will print an error message and exit. Use "-f" (force) option in scripts. Also improved general behaviour and presentation. Added well-defined return codes. Fixed a bug in length calculation. New behaviour: wipe will now by default round up lengths to the next multiple of file system block size so as to wipe data possibly remaining in the last block of a file; this behaviour can be inhibited with the new "-e" option. Thu Dec 30 21:50:59 CET 1999 version 0.16 - Included fix by Jason Axley for compiling under Solaris 2.6. Included patch sent by Paul H. Hargrove fixing a serious bug (wipe-0.15 is making 34 random passes rather than the proper mix of random and deterministic passes). Mon Nov 25 23:13:45 CET 2002 version 0.17 - Removed somewhat useless REMOVE_ON_INT define. Cosmetic changes. Added a -D option to make wipe follow symlinks given on the command line. Improved wiping statistics ; now reports number of symlinks and special files encoun- tered separately. Updated e-mail addresses and URLs (wow, free web hosting dies FAST !). Plus CVS, reinden- tation and Vim modelines. I'd call this lifting. Oh, and it now should work on devices and files greater than 2 gigabytes. And I added some randomisation when it tries the size of a file by resizing it. Mon Feb 2 14:40:55 CET 2003 version 0.18 - Arf, an important bugfix was lost during the v0.17 cleanup. When called with -r option, wipe would traverse to parent directories ! Fixed, again (thanks to Thomas). Sat Sep 13 14:53:11 CEST 2003 version 0.19 - wipe will hopefully work on large files now Sat Jun 12 19:21:01 CEST 2004 version 0.20 - Thanks to Jim Paris for a patch fixing the large file issue. Bumped up the version number a bit. Wed Sep 20 17:32:02 CEST 2006 version 0.21 - Included Debian patch for severe bug that affected the computation of the size of large partitions. Sun Nov 07 09:30:10 EST 2010 version 0.22 - Include patch by Karako Miklos to skip a number of patches. Also did a clean up of headers (remove obsolete addresses and CVS tags). Tue Nov 11 08:06:35 EST 2010 version 0.23 - To conform to Gutmann's paper, randomize only deterministic passes (as pointed out by Karako) and not the whole order. (pointed out by K. Miklos). Fix unreadable message displayed with the -X option. Wed Nov 2 23:24:07 PDT 2016 version 0.24 - Integrated Debian patches from 0.22-4 per Joao Eriberto Mota Filho's suggestion, except 50_hide-filenames.patch. wipe-0.24/GPL000066400000000000000000000431271300656062200127570ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy 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; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. wipe-0.24/Makefile000066400000000000000000000135561300656062200140550ustar00rootroot00000000000000# Makefile for wipe by Berke Durak # # echo berke1lambda-diode2com|tr 12 @. # # define HAVE_OSYNC if an O_SYNC bit can be specified for the open () call # (see open(2)) # # define HAVE_DEV_URANDOM if /dev/urandom is available; will use a simple # getpid() ^ clock() like scheme to seed the pseudorandom generator # otherwise. # # define HAVE_RANDOM if the random () library call is available on your # system. # # define HAVE_STRCASECMP if the strcasecmp () library call is available # on your system. # # the REMOVE_ON_INT option has been removed since version 0.17, # as it is difficult to maintain and has no significant purpose IMHO. # # define WEAK_RC6 if you wish to use four-round RC6 instead of the # standard 20 rounds # # define RC6_ENABLED ONLY IF RC6 HAS BEEN ACCEPTED AS THE ADVANCED ENCRYPTION # STANDARD and if you wish it to be available as a PRNG. WARNING. The RC6 # algorythm is patented by RSADSI; unless it is selected as the AES, # it requires licensing from RSADSI, and you might expose yourself to legal # problems if you use it without checking with them. I take no responsibility # on this. I just implemented it since its specifications were freely available # and it was easy to implement. by default this is disabled. # # define SYNC_WAITS_FOR_SYNC if the sync () system call waits for physical # i/o to be completed before returning, as under Linux. # # define FIND_DEVICE_SIZE_BY_BLKGETSIZE if ioctl BLKGETSIZE is available # for determinating the block size of a device, as under Linux. # # define SIXTYFOUR,__USE_LARGEFILE and __USE_FILE_OFFSET64 to be able to # wipe devices or files greater than 4Gb (works under Linux) # -------------------------------------------------------------------------- # Linux 2.0.x # CC_LINUX=gcc CCO_LINUX=-Wall -DHAVE_DEV_URANDOM -DHAVE_OSYNC -DHAVE_STRCASECMP -DHAVE_RANDOM -DWEAK_RC6 -DSYNC_WAITS_FOR_SYNC -DFIND_DEVICE_SIZE_BY_BLKGETSIZE -DSIXTYFOUR -D__USE_LARGEFILE -D_FILE_OFFSET_BITS=64 # default should be to turn off debugging and to turn on optimization. #CCO_LINUX+=-O9 -pipe -fomit-frame-pointer -finline-functions -funroll-loops -fstrength-reduce CCO_LINUX+=$(CFLAGS) $(LDFLAGS) $(CPPFLAGS) #CCO_LINUX+=-DDEBUG -g CCOC_LINUX=-c # -------------------------------------------------------------------------- # SunOS 5.5.1 # CC_SUNOS=gcc CCO_SUNOS=-Wall -O6 -pipe -fomit-frame-pointer CCOC_SUNOS=-c # -------------------------------------------------------------------------- # AIX 4.1 # CC_AIX=gcc CCO_AIX=$(CFLAGS) CCOC_AIX=-c # -------------------------------------------------------------------------- # Generic UNIX (gcc) # CC_GENERIC=gcc CCO_GENERIC=-Wall -O6 -pipe -fomit-frame-pointer -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 $(CFLAGS) $(LDFLAGS) $(CPPFLAGS) CCOC_GENERIC=-c # Thanks to Chris L. Mason for these: # # -------------------------------------------------------------------------- # Solaris 2.6 sparc # CC_SOLARISSP=gcc CCO_SOLARISSP=-Wall -O6 -pipe -fomit-frame-pointer CCOC_SOLARISSP=-c # -------------------------------------------------------------------------- # Solaris 2.6 x86 # CC_SOLARISX86=gcc CCO_SOLARISX86=-Wall -O6 -fomit-frame-pointer CCOC_SOLARISX86=-c # -------------------------------------------------------------------------- # FreeBSD 2.2.6-STABLE # CC_FREEBSD=gcc CCO_FREEBSD=-Wall -O6 -fomit-frame-pointer CCOC_FREEBSD=-c # -------------------------------------------------------------------------- # Digital/Compaq UNIX Alpha # # Thanks to Cyrus Durgin for this entry. CC_DIGITALALPHA=gcc CCO_DIGITALALPHA=-Wall -O6 -fomit-frame-pointer -DHAVE_OSYNC -DHAVE_STRCASECMP CCOC_DIGITALALPHA=-c # OBJECTS=wipe.o arcfour.o md5.o misc.o random.o TARGETS=wipe wipe.tr-asc.1 all : @echo "Please type $(MAKE) where can be one of:"; \ echo " linux -- for Linux (kernel 2.0.x or higher)"; \ echo " sunos -- for SunOS (tested on 5.5.1)"; \ echo " aix -- for AIX (tested on 4.2)"; \ echo " solarissp -- for Solaris SPARC (tested on 2.6)"; \ echo " solarisx86 -- for Solaris x86 (tested on 2.6)"; \ echo " freebsd -- for FreeBSD (tested on 2.2.6-STABLE)"; \ echo " digitalalpha -- for Digital/Compaq UNIX Alpha"; \ echo " generic -- for generic unix" linux : $(MAKE) $(TARGETS) "CC=$(CC_LINUX)" "CCO=$(CCO_LINUX)" "CCOC=$(CCOC_LINUX)" sunos : $(MAKE) $(TARGETS) "CC=$(CC_SUNOS)" "CCO=$(CCO_SUNOS)" "CCOC=$(CCOC_SUNOS)" aix : $(MAKE) $(TARGETS) "CC=$(CC_AIX)" "CCO=$(CCO_AIX)" "CCOC=$(CCOC_AIX)" freebsd : $(MAKE) $(TARGETS) "CC=$(CC_FREEBSD)" "CCO=$(CCO_FREEBSD)" "CCOC=$(CCOC_FREEBSD)" solarissp : $(MAKE) $(TARGETS) "CC=$(CC_SOLARISSP)" "CCO=$(CCO_SOLARISSP)" "CCOC=$(CCOC_SOLARISSP)" solarisx86 : $(MAKE) $(TARGETS) "CC=$(CC_SOLARISX86)" "CCO=$(CCO_SOLARISX86)" "CCOC=$(CCOC_SOLARISX86)" digitalalpha : $(MAKE) $(TARGETS) "CC=$(CC_DIGITALALPHA)" "CCO=$(CCO_DIGITALALPHA)" "CCOC=$(CCOC_DIGITALALPHA)" generic : $(MAKE) $(TARGETS) "CC=$(CC_GENERIC)" "CCO=$(CCO_GENERIC)" "CCOC=$(CCOC_GENERIC)" wipe : $(OBJECTS) $(CC) $(CCO) $(OBJECTS) -o wipe wipe.o : wipe.c random.h misc.h version.h $(CC) $(CCO) $(CCOC) wipe.c -o wipe.o version.h: always if which git >/dev/null 2>&1 ; then \ git rev-list --max-count=1 HEAD | sed -e 's/^/#define WIPE_GIT "/' -e 's/$$/"/' >version.h ; \ else \ echo '#define WIPE_GIT "(unknown, compiled without git)"' >version.h ; \ fi random.o : random.c misc.h md5.h $(CC) $(CCO) $(CCOC) random.c -o random.o rc6.o : rc6.c rc6.h $(CC) $(CCO) $(CCOC) rc6.c -o rc6.o arcfour.o : arcfour.c arcfour.h $(CC) $(CCO) $(CCOC) arcfour.c -o arcfour.o md5.o : md5.c md5.h $(CC) $(CCO) $(CCOC) md5.c -o md5.o misc.o : misc.c misc.h $(CC) $(CCO) $(CCOC) misc.c -o misc.o wipe.tr-asc.1 : wipe.tr.1 ./trtur wipe.tr-asc.1 clean : rm -f wipe $(OBJECTS) wipe.tr-asc.1 version.h install: install -m755 -o root -g root wipe $(DESTDIR)/usr/bin .PHONY: always clean install wipe-0.24/README000066400000000000000000000166131300656062200132720ustar00rootroot00000000000000wipe 0.24 by Berke Durak Wed Nov 02 2016 WHAT IS WIPE ? "wipe" is a short, nice tool for securely wiping out files from magnetic media. Purpose: to quickly wipe out traces of your latest dissident activities (cryptography etc.) when you realise that your local SSP (State Security Police, aka. NSA, DST, CIA, MIT (Turkish Intelligence Agency), Mossad, ...) is knocking at your door. QUICK START See file "QUICKSTART". RTFM Please READ THE MAN PAGE for more detailed information on wipe. Also, if you haven't done it yet, read Peter Gutmann's article on "Secure Deletion of Data from Magnetic and Solid-State Memory", included in this directory, which can also be retrieved from http://www.cs.auckland.ac.nz/~pgut001/pubs/secure_del.html CHANGES See the file CHANGES for a short history of wipe. You can get the latest version of wipe at these addresses: http://lambda-diode.com/software/wipe/ COPYING Wipe is under the GNU Public License (see file COPYING). PROBLEMS Wiping is a tricky affair. "wipe" tries to do everything that a portable user-level program can do to securely erase the given files. However, there are several limitations: 1.Since file meta information such as file name, size, creation/modification/access dates as well as directory structures are filesystem-specific, and as file systems tend to have extremely various architectures, there is no hope in seeing a uniform interface for accessing low-level filesystem data. Therefore a portable program must be filesystem-independent. It must thus use standard file operations in a way that will make most file systems effectively overwrite the desired portions of the magnetic media. 2.Since IDE and SCSI hard disks are driven by their own logic, nothing guarantees that the required data is written out effectively at the required place, i.e. over the old data. 3.The successfull erasure of off-track data is a function of drive temperature, usage history, drive mechanics and luck. Therefore I cannot and will not guarantee you that the files erased with wipe are unrecoverable. SOME RECENT VERSIONS (0.11, 0.12, 0.13) HAVE SERIOUS BUGS: I was too lazy to check if wipe was still working as expected after doing various improvements. It was not. Mea culpa. The current version (0.14) has been more or less verified, in different wipe modes, on different files and block devices. You can use "strace" on wipe to check that it effectively does the announced writes with different random data. From a software-level, wipe seems to do what it claims to do, i.e. overwriting, renaming, truncating, etc. Verification at the hardware-level requires specialised hardware. I don't have hardware. If anyone has got access to such hardware and is willing to check the effectiveness of wipe (and other secure deletion tools) PLEASE INFORM THE PUBLIC ABOUT IT. Verifications made on an Ext2 file system mounted through loopback shows that wipe 0.14 correctly erases the data blocks of files. However, file name wiping did not work as well as expected: plain filenames were still discernable at the block level after wiping a large directory tree. But filling the filesystem with a maximum size file and wiping it (as a crude but portable way to wipe out free blocks) erased the remaining plain filenames. There will be problems with files having "holes" in them; as wipe will try to completely overwrite those files with random data, the holes will get filled, possibly exceeding available disk space. Briefly: you can reasonably expect that the DATA contained in your files is EFFECTIVELY WIPED. However on complex file systems like Ext2 it is likely that FILE META-INFORMATION is NOT securely erased, or even not overwritten at all. RECOMMANDATIONS FOR MAXIMUM SAFETY For maximum safety, use an encrypted file system. Use wipe to clean whole partitions. For example, if I had guilty stuff on /dev/hda3, assuming that /dev/hda3 is less than, say 70% full, I would first mount /dev/hda3 on /mount, then do wipe -cfrsF /mount/ to wipe the contents of the data files using 34 passes, then wipe -kqZ -Q 1 /dev/hda3 to erase file meta-information. However if /dev/hda3 is more than 70% full, it might be quicker to erase the whole hard disk using 34 passes: wipe -kZ /dev/hda3 If you don't have to hide that you have guilty stuff but just want to be sure that someone else won't get at the CONTENTS of that guilty stuff, or in other words if you don't care about wiping file names, sizes etc. but just want to wipe out file contents, simply do wipe -cfrZF /mount/. And, last but not least, think of TEMPEST monitoring. I don't know how feasible it is to remotely eavesdrop your computer's internal bus or your IDE ribbon but using the "-s" (silent) option will prevent wipe from outputting to your monitor the names of all the files it erases. Eavesdropping your CRT is much easier for amateurs as well as for professionals. REQUIREMENTS wipe used to require Linux kernel 2.0.x or newer, in order to use the O_SYNC bit, and the /dev/random device. Since version 0.10, it no longer expressely requires O_SYNC, uses strong PRNGs and can gather the required seeds from different sources, including /dev/random-like devices, by hashing the output of a user-defined command or, in the worst case, by hashing its PID, the local date/time and its environment variables. Thanks to Chris L. Mason who initially motivated me to make wipe portable and helped me with compilation tips, bug reports, suggestions and patches. He maintains a site with reviews on UNIX software at http://www.unixzone.com THANKS Many thanks to the following people who sent in bug reports, compilation tips, and even whole patches ! In alphabetical order, hoping to not forget anyone, they are: Alexey Marinichev Chris L. Mason Jason Axley Erik Vogan, 64-bit offset fix Michael S. Rhee Paul H. Hargrove Peter Miller, for contributing an ETA patch Thomas Schoepf Joao Eriberto Mota Filho INSTALLATION To compile wipe, type "make" to get a list of supported unices. If your operating system appears in the list, type "make "; otherwise try "make generic". If this does not work, you'll have to hand-edit the Makefile: please mail me about your results. You can then copy the "wipe" executable in /usr/local/bin/ or in ~/bin if you wish. Install the man page in /usr/local/man/man1/, or use "man -l wipe.1" to read the manual page from wipe's directory. On systems lacking a /dev/random-like device, the shell script "randompipe.sh" can be used with the -R and -S options or the WIPE_SEEDPIPE environment variable. For more info, see the man page. OTHER WIPE IMPLEMENTATIONS There are several file-wiping tools available for Windows. There are two other ones I know for Linux: Calvin Clark's wipe 1.0beta, and Tom Viers' wipe v0.55beta3. These have exactly the same semantics as mine, i.e. their aim is to overwrite files with data in order to prevent recovery of their contents. Tom Viers' wipe is very similar to mine and is also based on Peter Gutmann's article. However, Calvin's wipe does simply write zeroes out on the file, which is not secure at all (RTF article !). There is also Van Hauser's srm (secure remove) available at http://r3wt.base.org, and which uses /dev/urandom as a PRNG. AUTHOR I can be reached at . Send bug reports, ideas for improvement, compilation problems and other comments to this address. wipe-0.24/arcfour.c000066400000000000000000000023121300656062200142060ustar00rootroot00000000000000/* wipe * * by Berke Durak * * Arcfour implementation, beta. * */ #include "arcfour.h" void arcfour_SetupKey (u8 *k, int n, struct arcfour_KeySchedule *ks) { int i, j; u8 kt[256]; for (i = 0, j = 0; i<256; i++) { ks->s[i] = i; kt[i] = k[j]; j ++; if (j == n) j = 0; } for (i = 0, j = 0; i<256; i++) { u8 t; j = 0xff & (j + ks->s[i] + kt[i]); t = ks->s[i]; ks->s[i] = ks->s[j]; ks->s[j] = t; } ks->i = i; ks->j = j; } inline u8 arcfour_GetByte (struct arcfour_KeySchedule *ks) { int i, j; u8 x; i = ks->i; j = ks->j; i = 0xff & (i+1); j = (j + ks->s[i]) & 0xff; x = ks->s[i]; ks->s[i] = ks->s[j]; ks->s[j] = x; ks->i = i; ks->j = j; return ks->s[(ks->s[i] + ks->s[j]) & 0xff]; } void arcfour_Fill (struct arcfour_KeySchedule *ks, u8 *b, int n) { while (n--) *(b++) = arcfour_GetByte (ks); } #ifdef TEST_ARCFOUR #define TESTKEY "thisisamoddafokkingtest" #include int main (int argc, char **argv) { int i; struct arcfour_KeySchedule ks; arcfour_SetupKey (TESTKEY, strlen (TESTKEY), &ks); for (i = 0; i<10240; i++) { putchar (arcfour_GetByte (&ks)); } return 0; } #undef TESTKEY #endif /* vim:set sw=4:set ts=8: */ wipe-0.24/arcfour.h000066400000000000000000000007701300656062200142210ustar00rootroot00000000000000/* wipe * * by Berke Durak * * Arcfour implementation, beta. * */ #ifndef ARCFOUR_H #define ARCFOUR_H #include #ifndef U32U16U8 typedef unsigned long u32; typedef unsigned short u16; typedef unsigned char u8; #define U32U16U8 #endif struct arcfour_KeySchedule { int i, j; u8 s[256]; }; void arcfour_SetupKey (u8 *k, int n, struct arcfour_KeySchedule *ks); u8 arcfour_GetByte (struct arcfour_KeySchedule *ks); void arcfour_Fill (struct arcfour_KeySchedule *ks, u8 *b, int n); #endif wipe-0.24/examples/000077500000000000000000000000001300656062200142215ustar00rootroot00000000000000wipe-0.24/examples/wipefd0000066400000000000000000000000551300656062200155020ustar00rootroot00000000000000#!/bin/sh exec wipe -kqfZ -l 1440K /dev/fd0 wipe-0.24/examples/wswap.pl000066400000000000000000000052411300656062200157210ustar00rootroot00000000000000#!/usr/bin/perl my $wipe_cmd = "wipe -kfZ -q"; my $swapoff_cmd = "/sbin/swapoff"; my $swapon_cmd = "/sbin/swapon"; my $mkswap_cmd = "/sbin/mkswap"; if ($#ARGV != 0) { print STDERR "specify device special file to wipe\n"; die; } my $to_wipe = $ARGV[0]; sub check_swap_in_proc_swap { open PROC, ") { if ($line =~ /^(\S+)\s+(\w+)\s+(\d+)\s/) { if ($1 == $to_wipe) { print "Device $to_wipe is listed as a swap device of type\n"; print "$2 of size $3 in /proc/swaps.\n"; close PROC; return 1; } } } print "Device $to_wipe is NOT listed in /proc/swaps\n"; close PROC; return 0; } # check that $to_wipe is a swap partition listed in /etc/fstab... sub check_swap_in_fstab { my $result = 0; open FSTAB, ") { next if (/^\#/ =~ $line); # ignore comments my ($name, $mountpoint, $type, $etc) =split (/\s+/, $line); if ($name eq $to_wipe) { if ($type eq "swap") { print STDERR "Device $to_wipe is listed as a " . "\"swap\" partition in /etc/fstab.\n"; $result = 1; last; } else { print STDERR "Device $to_wipe is listed as a \"$type\" partition " . "in /etc/fstab,\nnot as a \"swap\" partition.\n"; $result = -1; last; } } } close FSTAB; if (!$result) { print STDERR "Device $to_wipe is NOT listed in /etc/fstab.\n"; } if ($result < 0) { $result = 0 }; return $result; } if (&check_swap_in_fstab ()) { my $cookie1 = int (rand (10000)); my $cookie2 = int (rand (100)); my $cookie = $cookie1 + $cookie2; print "Read CAREFULLY. Do you want to WIPE (i.e. ERASE) the contents\n"; print "of the special device:\n"; print "\t\t$to_wipe ?\n"; print "If yes, compute the sum $cookie1 + $cookie2 and type "; print "\"yes\" followed by\na space and the sum.\n\n"; my $response = ; my $on = 0; if ($response =~ /^yes $cookie$/) { print "\nAcknowledged, wiping $to_wipe.\n"; if (&check_swap_in_proc_swap ()) { print "Turning off swap partition...\n"; system ($swapoff_cmd . " " . $to_wipe) and die "Failed to turn off swap partition.\n"; $on = 1; } system ($wipe_cmd . " " . $to_wipe) and die "Failed to wipe.\n"; print "Recreating swap partition...\n"; system ($mkswap_cmd . " " . $to_wipe) and die "Failed to recreate swap partition.\n"; if ($on) { print "Turning on swap partition...\n"; system ($swapon_cmd . " " . $to_wipe) and die "Failed to turn on swap partition.\n"; } print "Success.\n"; } else { print "\nNot acknowledged. Quitting.\n"; } } wipe-0.24/md5.c000066400000000000000000000260051300656062200132370ustar00rootroot00000000000000/* *********************************************************************** ** md5.c -- the source code for MD5 routines ** ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** ** Created: 2/17/90 RLR ** ** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version ** *********************************************************************** */ /* * Edited 7 May 93 by CP to change the interface to match that * of the MD5 routines in RSAREF. Due to this alteration, this * code is "derived from the RSA Data Security, Inc. MD5 Message- * Digest Algorithm". (See below.) */ /* *********************************************************************** ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** ** ** ** License to copy and use this software is granted provided that ** ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** ** Digest Algorithm" in all material mentioning or referencing this ** ** software or this function. ** ** ** ** License is also granted to make and use derivative works ** ** provided that such works are identified as "derived from the RSA ** ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** ** material mentioning or referencing the derived work. ** ** ** ** RSA Data Security, Inc. makes no representations concerning ** ** either the merchantability of this software or the suitability ** ** of this software for any particular purpose. It is provided "as ** ** is" without express or implied warranty of any kind. ** ** ** ** These notices must be retained in any copies of any part of this ** ** documentation and/or software. ** *********************************************************************** */ #include "md5.h" /* *********************************************************************** ** Message-digest routines: ** ** To form the message digest for a message M ** ** (1) Initialize a context buffer mdContext using MD5Init ** ** (2) Call MD5Update on mdContext and M ** ** (3) Call MD5Final on mdContext ** ** The message digest is now in the bugffer passed to MD5Final ** *********************************************************************** */ static unsigned char PADDING[64] = { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* F, G, H and I are basic MD5 functions */ #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) #define G(x, y, z) (((x) & (z)) | ((y) & (~z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define I(x, y, z) ((y) ^ ((x) | (~z))) /* ROTATE_LEFT rotates x left n bits */ #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ /* Rotation is separate from addition to prevent recomputation */ #define FF(a, b, c, d, x, s, ac) \ {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define GG(a, b, c, d, x, s, ac) \ {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define HH(a, b, c, d, x, s, ac) \ {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define II(a, b, c, d, x, s, ac) \ {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } /* The routine MD5Init initializes the message-digest context mdContext. All fields are set to zero. */ void MD5Init ( MD5_CTX *mdContext) { mdContext->i[0] = mdContext->i[1] = (UINT4)0; /* Load magic initialization constants. */ mdContext->buf[0] = (UINT4)0x67452301L; mdContext->buf[1] = (UINT4)0xefcdab89L; mdContext->buf[2] = (UINT4)0x98badcfeL; mdContext->buf[3] = (UINT4)0x10325476L; } /* The routine MD5Update updates the message-digest context to account for the presence of each of the characters inBuf[0..inLen-1] in the message whose digest is being computed. */ void MD5Update (register MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen) { register int i, ii; int mdi; UINT4 in[16]; /* compute number of bytes mod 64 */ mdi = (int)((mdContext->i[0] >> 3) & 0x3F); /* update number of bits */ if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0]) mdContext->i[1]++; mdContext->i[0] += ((UINT4)inLen << 3); mdContext->i[1] += ((UINT4)inLen >> 29); while (inLen--) { /* add new character to buffer, increment mdi */ mdContext->in[mdi++] = *inBuf++; /* transform if necessary */ if (mdi == 0x40) { for (i = 0, ii = 0; i < 16; i++, ii += 4) in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | (((UINT4)mdContext->in[ii+2]) << 16) | (((UINT4)mdContext->in[ii+1]) << 8) | ((UINT4)mdContext->in[ii]); Transform (mdContext->buf, in); mdi = 0; } } } /* The routine MD5Final terminates the message-digest computation and ends with the desired message digest in mdContext->digest[0...15]. */ void MD5Final (unsigned char digest[16], MD5_CTX *mdContext) { UINT4 in[16]; int mdi; unsigned int i, ii; unsigned int padLen; /* save number of bits */ in[14] = mdContext->i[0]; in[15] = mdContext->i[1]; /* compute number of bytes mod 64 */ mdi = (int)((mdContext->i[0] >> 3) & 0x3F); /* pad out to 56 mod 64 */ padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); MD5Update (mdContext, PADDING, padLen); /* append length in bits and transform */ for (i = 0, ii = 0; i < 14; i++, ii += 4) in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | (((UINT4)mdContext->in[ii+2]) << 16) | (((UINT4)mdContext->in[ii+1]) << 8) | ((UINT4)mdContext->in[ii]); Transform (mdContext->buf, in); /* store buffer in digest */ for (i = 0, ii = 0; i < 4; i++, ii += 4) { digest[ii] = (unsigned char) (mdContext->buf[i] & 0xFF); digest[ii+1] = (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); digest[ii+2] = (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); digest[ii+3] = (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); } } /* Basic MD5 step. Transforms buf based on in. Note that if the Mysterious Constants are arranged backwards in little-endian order and decrypted with the DES they produce OCCULT MESSAGES! */ void Transform(register UINT4 *buf,register UINT4 *in) { register UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; /* Round 1 */ #define S11 7 #define S12 12 #define S13 17 #define S14 22 FF ( a, b, c, d, in[ 0], S11, 0xD76AA478L); /* 1 */ FF ( d, a, b, c, in[ 1], S12, 0xE8C7B756L); /* 2 */ FF ( c, d, a, b, in[ 2], S13, 0x242070DBL); /* 3 */ FF ( b, c, d, a, in[ 3], S14, 0xC1BDCEEEL); /* 4 */ FF ( a, b, c, d, in[ 4], S11, 0xF57C0FAFL); /* 5 */ FF ( d, a, b, c, in[ 5], S12, 0x4787C62AL); /* 6 */ FF ( c, d, a, b, in[ 6], S13, 0xA8304613L); /* 7 */ FF ( b, c, d, a, in[ 7], S14, 0xFD469501L); /* 8 */ FF ( a, b, c, d, in[ 8], S11, 0x698098D8L); /* 9 */ FF ( d, a, b, c, in[ 9], S12, 0x8B44F7AFL); /* 10 */ FF ( c, d, a, b, in[10], S13, 0xFFFF5BB1L); /* 11 */ FF ( b, c, d, a, in[11], S14, 0x895CD7BEL); /* 12 */ FF ( a, b, c, d, in[12], S11, 0x6B901122L); /* 13 */ FF ( d, a, b, c, in[13], S12, 0xFD987193L); /* 14 */ FF ( c, d, a, b, in[14], S13, 0xA679438EL); /* 15 */ FF ( b, c, d, a, in[15], S14, 0x49B40821L); /* 16 */ /* Round 2 */ #define S21 5 #define S22 9 #define S23 14 #define S24 20 GG ( a, b, c, d, in[ 1], S21, 0xF61E2562L); /* 17 */ GG ( d, a, b, c, in[ 6], S22, 0xC040B340L); /* 18 */ GG ( c, d, a, b, in[11], S23, 0x265E5A51L); /* 19 */ GG ( b, c, d, a, in[ 0], S24, 0xE9B6C7AAL); /* 20 */ GG ( a, b, c, d, in[ 5], S21, 0xD62F105DL); /* 21 */ GG ( d, a, b, c, in[10], S22, 0x02441453L); /* 22 */ GG ( c, d, a, b, in[15], S23, 0xD8A1E681L); /* 23 */ GG ( b, c, d, a, in[ 4], S24, 0xE7D3FBC8L); /* 24 */ GG ( a, b, c, d, in[ 9], S21, 0x21E1CDE6L); /* 25 */ GG ( d, a, b, c, in[14], S22, 0xC33707D6L); /* 26 */ GG ( c, d, a, b, in[ 3], S23, 0xF4D50D87L); /* 27 */ GG ( b, c, d, a, in[ 8], S24, 0x455A14EDL); /* 28 */ GG ( a, b, c, d, in[13], S21, 0xA9E3E905L); /* 29 */ GG ( d, a, b, c, in[ 2], S22, 0xFCEFA3F8L); /* 30 */ GG ( c, d, a, b, in[ 7], S23, 0x676F02D9L); /* 31 */ GG ( b, c, d, a, in[12], S24, 0x8D2A4C8AL); /* 32 */ /* Round 3 */ #define S31 4 #define S32 11 #define S33 16 #define S34 23 HH ( a, b, c, d, in[ 5], S31, 0xFFFA3942L); /* 33 */ HH ( d, a, b, c, in[ 8], S32, 0x8771F681L); /* 34 */ HH ( c, d, a, b, in[11], S33, 0x6D9D6122L); /* 35 */ HH ( b, c, d, a, in[14], S34, 0xFDE5380CL); /* 36 */ HH ( a, b, c, d, in[ 1], S31, 0xA4BEEA44L); /* 37 */ HH ( d, a, b, c, in[ 4], S32, 0x4BDECFA9L); /* 38 */ HH ( c, d, a, b, in[ 7], S33, 0xF6BB4B60L); /* 39 */ HH ( b, c, d, a, in[10], S34, 0xBEBFBC70L); /* 40 */ HH ( a, b, c, d, in[13], S31, 0x289B7EC6L); /* 41 */ HH ( d, a, b, c, in[ 0], S32, 0xEAA127FAL); /* 42 */ HH ( c, d, a, b, in[ 3], S33, 0xD4EF3085L); /* 43 */ HH ( b, c, d, a, in[ 6], S34, 0x04881D05L); /* 44 */ HH ( a, b, c, d, in[ 9], S31, 0xD9D4D039L); /* 45 */ HH ( d, a, b, c, in[12], S32, 0xE6DB99E5L); /* 46 */ HH ( c, d, a, b, in[15], S33, 0x1FA27CF8L); /* 47 */ HH ( b, c, d, a, in[ 2], S34, 0xC4AC5665L); /* 48 */ /* Round 4 */ #define S41 6 #define S42 10 #define S43 15 #define S44 21 II ( a, b, c, d, in[ 0], S41, 0xF4292244L); /* 49 */ II ( d, a, b, c, in[ 7], S42, 0x432AFF97L); /* 50 */ II ( c, d, a, b, in[14], S43, 0xAB9423A7L); /* 51 */ II ( b, c, d, a, in[ 5], S44, 0xFC93A039L); /* 52 */ II ( a, b, c, d, in[12], S41, 0x655B59C3L); /* 53 */ II ( d, a, b, c, in[ 3], S42, 0x8F0CCC92L); /* 54 */ II ( c, d, a, b, in[10], S43, 0xFFEFF47DL); /* 55 */ II ( b, c, d, a, in[ 1], S44, 0x85845DD1L); /* 56 */ II ( a, b, c, d, in[ 8], S41, 0x6FA87E4FL); /* 57 */ II ( d, a, b, c, in[15], S42, 0xFE2CE6E0L); /* 58 */ II ( c, d, a, b, in[ 6], S43, 0xA3014314L); /* 59 */ II ( b, c, d, a, in[13], S44, 0x4E0811A1L); /* 60 */ II ( a, b, c, d, in[ 4], S41, 0xF7537E82L); /* 61 */ II ( d, a, b, c, in[11], S42, 0xBD3AF235L); /* 62 */ II ( c, d, a, b, in[ 2], S43, 0x2AD7D2BBL); /* 63 */ II ( b, c, d, a, in[ 9], S44, 0xEB86D391L); /* 64 */ buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } wipe-0.24/md5.h000066400000000000000000000064151300656062200132470ustar00rootroot00000000000000/* *********************************************************************** ** md5.h -- header file for implementation of MD5 ** ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** ** Created: 2/17/90 RLR ** ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** ** Revised (for MD5): RLR 4/27/91 ** ** -- G modified to have y&~z instead of y&z ** ** -- FF, GG, HH modified to add in last register done ** ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** ** -- distinct additive constant for each step ** ** -- round 4 added, working mod 7 ** *********************************************************************** */ /* * Edited 7 May 93 by CP to change the interface to match that * of the MD5 routines in RSAREF. Due to this alteration, this * code is "derived from the RSA Data Security, Inc. MD5 Message- * Digest Algorithm". (See below.) Also added argument names * to the prototypes. */ /* *********************************************************************** ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** ** ** ** License to copy and use this software is granted provided that ** ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** ** Digest Algorithm" in all material mentioning or referencing this ** ** software or this function. ** ** ** ** License is also granted to make and use derivative works ** ** provided that such works are identified as "derived from the RSA ** ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** ** material mentioning or referencing the derived work. ** ** ** ** RSA Data Security, Inc. makes no representations concerning ** ** either the merchantability of this software or the suitability ** ** of this software for any particular purpose. It is provided "as ** ** is" without express or implied warranty of any kind. ** ** ** ** These notices must be retained in any copies of any part of this ** ** documentation and/or software. ** *********************************************************************** */ /* typedef a 32-bit type */ #ifdef __alpha typedef unsigned int UINT4; #else typedef unsigned long int UINT4; #endif /* Data structure for MD5 (Message-Digest) computation */ typedef struct { UINT4 buf[4]; /* scratch buffer */ UINT4 i[2]; /* number of _bits_ handled mod 2^64 */ unsigned char in[64]; /* input buffer */ } MD5_CTX; void MD5Init(MD5_CTX *mdContext); void MD5Update(MD5_CTX *mdContext, unsigned char *bug, unsigned int len); void MD5Final(unsigned char digest[16], MD5_CTX *mdContext); void Transform(UINT4 *buf, UINT4 *in); wipe-0.24/misc.c000066400000000000000000000026301300656062200135030ustar00rootroot00000000000000/* wipe * * by Berke Durak * * General-purpose miscellaneous routines: debugging, etc. * */ #include #include #include #include #include #include "misc.h" void *xmalloc (size_t l) { void *m; m = malloc (l); if (!m) { errorf (0, "could not allocate %ld bytes", l); exit (EXIT_FAILURE); } return m; } int errorf (unsigned long e, char *fmt, ...) { va_list arg; int en; en = (e & ERF_ERN)?errno:e & ERF_MASK; va_start (arg, fmt); fprintf (stderr, "Error: "); vfprintf (stderr, fmt, arg); if (en) fprintf (stderr, " (%s -- error %d)", strerror (en), en); fputc ('\n', stderr); va_end (arg); if (e & ERF_EXIT) exit (EXIT_FAILURE); if (e & ERF_RET0) return 0; return -1; } void informf (char *fmt, ...) { va_list arg; va_start (arg, fmt); vfprintf (stderr, fmt, arg); fputc ('\n', stderr); va_end (arg); } char *msprintf (char *fmt, ...) { char *s; va_list arg; char buf[1024]; va_start (arg, fmt); vsprintf (buf, fmt, arg); s = xmalloc (strlen (buf)+1); strcpy (s, buf); va_end (arg); return s; } #ifdef DEBUG void debug_pf (char *fmt, ...) { va_list arg; va_start (arg, fmt); fprintf (stderr, "debug: "); vfprintf (stderr, fmt, arg); fputc ('\n', stderr); va_end (arg); } #define debugf(x, y...) debug_pf(x,## y) #else #define debugf(x, y...) #endif /* vim:set sw=4:set ts=8: */ wipe-0.24/misc.h000066400000000000000000000010631300656062200135070ustar00rootroot00000000000000/* wipe * * by Berke Durak * * General-purpose miscellaneous routines: debugging, etc. * */ #ifndef MISC_H #define MISC_H #include #define ERF_MASK 0x0000ffff #define ERF_EXIT 0x80000000 #define ERF_RET0 0x40000000 #define ERF_ERN 0x20000000 int errorf (unsigned long e, char *fmt, ...); void informf (char *fmt, ...); char *msprintf (char *fmt, ...); void *xmalloc (size_t l); #ifdef DEBUG void debug_pf (char *fmt, ...); #define debugf(x, y...) debug_pf(x,## y) #else #define debugf(x, y...) #endif #endif /* vim:set sw=4:set ts=8: */ wipe-0.24/random.c000066400000000000000000000167101300656062200140340ustar00rootroot00000000000000/* wipe * * by Berke Durak * * Cryptographically-strong (as well as weak) random data generation * */ /* As of version 0.10, different methods for generating * the required random data can be selected at compile-time. * * This improvement was motivated by security and portability * reasons. * * Prior to version 0.10, cryptographically _weak_ random data * was generated using the following method: * * - libc's random () PRNG was seeded with data from /dev/urandom * (and if not available using getpid () ^ clock () !) * - since random () returned only 31 bits of data, for speed reasons, * the low 24 bits were used. * * I don't know how much the cryptographical strength of the random * sequences used to overwrite the magnetic data affect the chances * of recovery. * * Besides this potential problem, there is something much more * worrying: since wipe is a user-mode program, it uses write () * to overwrite the data. However nothing guarantees that the * same file blocks are used. I guess a normal filesystem would * not waste its time reallocating file data blocks when overwriting * the old ones would suffice but I can't guarantee this. * * The only way to solve this would be to do block device-level * wiping, which requires deep knowledge of the file system structure. * The best solution would be to have file-systems incorporate * secure wiping options. On Linux, the ext2fs provides a "s" * file attribute, which makes the file system overwrite the * file with zeroes when it is unlinked: this could be changed * to implement a secure erasure mechanism. */ #include #include #include #include #include #include #include #include "misc.h" //#include "rc6.h" #include "arcfour.h" #include "md5.h" #include "random.h" #ifdef RC6_ENABLED static struct rc6_KeySchedule rand_rc6; static u32 rand_rc6_array[2][4]; static int rand_rc6_i; #endif u32 (*rand_Get32p) (void); void (*rand_Fillp) (u8 *b, int n); static struct arcfour_KeySchedule rand_arcfour; static u32 rand_extra; static u32 rand_extra_i; static void rand_Get128BitsPID (u8 buf[16]) { MD5_CTX md5; pid_t p; time_t t; char **pt; debugf ("using poor man's 128 bit random seeder"); MD5Init (&md5); p = getpid (); t = time (0); for (pt = environ; *pt; pt++) MD5Update (&md5, (u8 *) *pt, strlen (*pt)); MD5Update (&md5, (u8 *) &p, sizeof (p)); MD5Update (&md5, (u8 *) &t, sizeof (t)); MD5Final (buf, &md5); } static void rand_Get128BitsDevRandom (u8 buf[16]) { int fd; debugf ("getting 128 bits from %s", o_devrandom); fd = open (o_devrandom, O_RDONLY); if (fd < 0) errorf (ERF_ERN|ERF_EXIT, "could not open \"%s\" for random seeding", o_devrandom); if (16 != read (fd, buf, 16)) errorf (ERF_ERN|ERF_EXIT, "short read or read error from \"%s\" for random seeding", o_devrandom); (void) close (fd); /* we got what we wanted, why should we check for errors ? */ } #define MINIMUM_PIPE_BYTES 128L static void rand_Get128BitsPipe (u8 buf128[16]) { FILE *f; MD5_CTX md5; u8 buf[1024]; size_t r, totr; debugf ("getting 128 bits by hashing output of %s", o_randomcmd); MD5Init (&md5); f = popen (o_randomcmd, "r"); if (!f) errorf (ERF_ERN|ERF_EXIT, "could not popen () command \"%s\" for random seeding", o_randomcmd); for (totr = 0; ;) { r = fread (buf, 1, sizeof (buf), f); if (r > 0) { MD5Update (&md5, buf, r); totr += r; } if (r < sizeof(buf)) { if (!feof (f)) errorf (ERF_ERN|ERF_EXIT, "error reading from pipe \"%s\"", o_randomcmd); break; } } if (totr < MINIMUM_PIPE_BYTES) errorf (ERF_EXIT, "pipe command \"%s\" must output at least %ld bytes (got %ld)", o_randomcmd, (long) MINIMUM_PIPE_BYTES, totr); if (pclose (f)) errorf (ERF_ERN|ERF_EXIT, "error on pclose ()"); MD5Final (buf128, &md5); } #ifdef RC6_ENABLED inline static u32 rand_Get32_rc6 () { rand_rc6_i ++; if (rand_rc6_i == 4) rand_rc6_i = 0; if (!rand_rc6_i) { /* elegant incrementation tip by Ben Laurie */ randa_rc6_array[0][0]++ || randa_rc6_array[0][1]++ || randa_rc6_array[0][2]++ || randa_rc6_array[0][3]++; rc6_Encrypt (rand_rc6_array[0], rand_rc6_array[1], &rand_rc6); } return rand_rc6_array[rand_rc6_i]; } #endif static u32 rand_Get32_arcfour () { u32 x; x = 0; x |= arcfour_GetByte (&rand_arcfour); x <<= 8; x |= arcfour_GetByte (&rand_arcfour); x <<= 8; x |= arcfour_GetByte (&rand_arcfour); x <<= 8; x |= arcfour_GetByte (&rand_arcfour); return x; } inline static u32 rand_Get32_libc () { u32 r; #ifdef HAVE_RANDOM r = random (); #else r = rand (); #endif if (!rand_extra_i) { #ifdef HAVE_RANDOM rand_extra = random () << 1; #else rand_extra = rand () << 1; #endif rand_extra_i = 31; } else rand_extra_i --; r ^= rand_extra & 0x80000000; rand_extra <<= 1; return r; } #if RC6_ENABLED static void rand_Fill_rc6 (u8 *b, int n) { u32 l; /* there used to be a stupid bug here, thanks to Michael S.Ree for * pointing this out. */ for (; n >= 4; n -= 4) { l = rand_Get32_rc6 (); *((u32 *) b) = l; b = (char *) (((u32 *) b) + 1); } if (n) { l = rand_Get32_rc6 (); while (n--) { *(b++) = l & 0xff; l >>= 8; } } } #endif static void rand_Fill_libc (u8 *b, int n) { u32 l; /* there used to be a stupid bug here, thanks to Michael S.Ree for * pointing this out. */ for (; n >= 4; n -= 4) { l = rand_Get32_libc (); *((u32 *) b) = l; b = (u8 *) (((u32 *) b) + 1); } if (n) { l = rand_Get32 (); while (n--) { *(b++) = l & 0xff; l >>= 8; } } } static void rand_Fill_arcfour (u8 *b, int n) { arcfour_Fill (&rand_arcfour, b, n); } #if 0 u32 rand_Get32 () { return rand_Get32p (); } #endif void rand_Init () { u8 key[16]; switch (o_randseed) { case RANDS_DEVRANDOM: debugf ("using random device %s", o_devrandom); rand_Get128BitsDevRandom (key); break; case RANDS_PIPE: debugf ("using random seed pipe %s", o_devrandom); rand_Get128BitsPipe (key); break; case RANDS_PID: debugf ("using pid as random seed"); rand_Get128BitsPID (key); break; } switch (o_randalgo) { case RANDA_LIBC: debugf ("using libc random generator"); #ifdef HAVE_RANDOM (void) srandom (key[0] | key[1]<<8 | key[2]<<16 | key[3]<<24); #else srand (key[0] | key[1]<<8 | key[2]<<16 | key[3]<<24); #endif rand_Get32p = rand_Get32_libc; rand_Fillp = rand_Fill_libc; rand_extra = key[4] | key[5]<<8 | key[6]<<16 | key[7]<<24; rand_extra_i = 32; break; #ifdef RC6_ENABLED case RANDA_RC6: debugf ("using rc6 random generator"); rc6_SetupKey (key, &rand_rc6); rand_rc6_array [0] = rand_rc6_array [1] = rand_rc6_array [2] = rand_rc6_array [3] = 0; rand_rc6_i = 3; rand_Get32p = rand_Get32_rc6; rand_Fillp = rand_Fill_rc6; break; #endif case RANDA_ARCFOUR: debugf ("using arcfour random generator"); arcfour_SetupKey (key, sizeof (key), &rand_arcfour); /* "give the crank those extra turns after initialisation" * suggestion of Jim Gillogly */ { int i; for (i = 0; i&1 ;; AIX) (ps -Al & ls -alR /tmp & w & date &) 2>&1; ;; Linux) (ls -alR /proc & w & ps -Al & date &) 2>&1 ;; *) (ls -alR /tmp & w & date &) 2>&1 esac wipe-0.24/trtur000077500000000000000000000003361300656062200135130ustar00rootroot00000000000000#!/bin/sh # g u i c s G U I o C S i O o e e u e a o tr "\360\374\375\347\376\320\334\335\366\307\336\356\342\326\351\352\350\371\340\364\373\311\357" \ "guicsGUIoCSiOOeeeuaouei" $* wipe-0.24/wipe.1000066400000000000000000000377501300656062200134450ustar00rootroot00000000000000.TH WIPE 1 "Sun Nov 7 09:41:23 EST 2010" "Linux" "User Commands" .SH NAME wipe \- securely erase files from magnetic media .SH SYNOPSIS wipe [options] path1 path2 ... pathn .br .SH "CURRENT\-VERSION" This manual page describes version .B 0.22 of .B wipe , released November 2010. .SH DESCRIPTION Recovery of supposedly erased data from magnetic media is easier than what many people would like to believe. A technique called Magnetic Force Microscopy (MFM) allows any moderately funded opponent to recover the last two or three layers of data written to disk; .B wipe repeatedly overwrites special patterns to the files to be destroyed, using the fsync() call and/or the O_SYNC bit to force disk access. In normal mode, 34 patterns are used (of which 8 are random). These patterns were recommended in an article from Peter Gutmann (pgut001@cs.auckland.ac.nz) entitled "Secure Deletion of Data from Magnetic and Solid-State Memory". The normal mode takes 35 passes (0-34). A quick mode allows you to use only 4 passes with random patterns, which is of course much less secure. .SH NOTE ABOUT JOURNALING FILESYSTEMS AND SOME RECOMMENDATIONS (JUNE 2004) Journaling filesystems (such as Ext3 or ReiserFS) are now being used by default by most Linux distributions. No secure deletion program that does filesystem-level calls can sanitize files on such filesystems, because sensitive data and metadata can be written to the journal, which cannot be readily accessed. Per-file secure deletion is better implemented in the operating system. Encrypting a whole partition with cryptoloop, for example, does not help very much either, since there is a single key for all the partition. Therefore .B wipe is best used to sanitize a harddisk before giving it to untrusted parties (i.e. sending your laptop for repair, or selling your disk). Wiping size issues have been hopefully fixed (I apologize for the long delay). Be aware that harddisks are quite intelligent beasts those days. They transparently remap defective blocks. This means that the disk can keep an albeit corrupted (maybe slightly) but inaccessible and unerasable copy of some of your data. Modern disks are said to have about 100% transparent remapping capacity. You can have a look at recent discussions on Slashdot. I hereby speculate that harddisks can use the spare remapping area to secretly make copies of your data. Rising totalitarianism makes this almost a certitude. It is quite straightforward to implement some simple filtering schemes that would copy potentially interesting data. Better, a harddisk can probably detect that a given file is being wiped, and silently make a copy of it, while wiping the original as instructed. Recovering such data is probably easily done with secret IDE/SCSI commands. My guess is that there are agreements between harddisk manufacturers and government agencies. Well-funded mafia hackers should then be able to find those secret commands too. Don't trust your harddisk. Encrypt all your data. Of course this shifts the trust to the computing system, the CPU, and so on. I guess there are also "traps" in the CPU and, in fact, in every sufficiently advanced mass-marketed chip. Wealthy nations can find those. Therefore these are mainly used for criminal investigation and "control of public dissent". People should better think of their computing devices as facilities lended by the DHS. .SH IMPORTANT WARNING -- READ CAREFULLY The author, the maintainers or the contributors of this package can NOT be held responsible in any way if .B wipe destroys something you didn't want it to destroy. Let's make this very clear. I want you to assume that this is a nasty program that will wipe out parts of your files that you didn't want it to wipe. So whatever happens after you launch .B wipe is your entire responsibility. In particular, no one guarantees that .B wipe will conform to the specifications given in this manual page. Similarly, we cannot guarantee that .B wipe will actually erase data, or that wiped data is not recoverable by advanced means. So if nasties get your secrets because you sold a wiped harddisk to someone you don't know, well, too bad for you. The best way to sanitize a storage medium is to subject it to temperatures exceeding 1500K. As a cheap alternative, you might use .B wipe at your own risk. Be aware that it is very difficult to assess whether running .B wipe on a given file will actually wipe it -- it depends on an awful lot of factors, such as : the type of file system the file resides on (in particular, whether the file system is a journaling one or not), the type of storage medium used, and the least significant bit of the phase of the moon. Wiping over NFS or over a journalling filesystem (ReiserFS etc.) will most probably not work. Therefore I strongly recommend to call .B wipe directly on the corresponding block device with the appropriate options. However .I THIS IS AN EXTREMELY DANGEROUS THING TO DO. Be sure to be sober. Give the right options. In particular : don't wipe a whole harddisk (eg. wipe -kD /dev/hda is bad) since this will destroy your master boot record. Bad idea. Prefer wiping partitions (eg. wipe -kD /dev/hda2) is good, provided, of course, that you have backed up all necessary data. .PP .SH "COMMAND\-LINE OPTIONS" .TP 0.5i .B -f (force; disable confirmation query) By default .B wipe will ask for confirmation, indicating the number of regular and special files and directories specified on the command line. You must type "yes" for confirmation, "no" for rejection. You can disable the confirmation query with the .B -f (force) option. .TP 0.5i .B -r (recurse into subdirectories) Will allow the removal of the entire directory tree. Symbolic links are not followed. .TP 0.5i .B -c (chmod if necessary) If a file or directory to be wiped has no write permissions set, will do a chmod to set the permission. .TP 0.5i .B -i (informational, verbose mode) This enables reporting to stdout. By default all data is written to stderr. .TP 0.5i .B -s (silent mode) All messages, except the confirmation prompt and error messages, are suppressed. .TP 0.5i .B -q (quick wipe) If this option is used, .B wipe will only make (by default) 4 passes on each file, writing random data. See option .B -Q . .TP 0.5i .B -Q Sets the number of passes for quick wiping. Default is 4. This option requires -q. .TP 0.5i .B -a (abort on error) The program will exit with EXIT_FAILURE if a non-fatal error is encountered. .TP 0.5i .B -R (set random device OR random seed command) With this option which requires an argument you can specify an alternate /dev/random device, or a command who's standard output will be hashed using MD5-hashed. The distinction can be made using the -S option. .TP 0.5i .B -S (random seed method) This option takes a single-character argument, which specifies how the random device/random seed argument is to be used. The default random device is /dev/random. It can be set using the -R option. .PP .PD 0 The possible single-character arguments are: .TP 0.5i .B r If you want the argument to be treated like a regular file/character device. This will work with /dev/random, and might also work with FIFOs and the like. .TP 0.5i .B c If you want the argument to be executed as a command. The output from the command will be hashed using MD5 to provide the required seed. See the WIPE_SEEDPIPE environment variable for more info. .TP 0.5i .B p If you want wipe to get its seed by hashing environment variables, the current date and time, its process id. etc. (the random device argument will not be used). This is of course the least secure setting. .TP 0.5i .B -M (select pseudo-random number generator algorythm) .PP .PD 0 During the random passes, .B wipe overwrites the target files with a stream of binary data, created by the following choice of algorythms: .TP 0.5i .B l will use (depending on your system) your libc's random() or rand() pseudorandom generator. Note that on most systems, rand() is a linear congruential generator, which is awfully weak. The choice is made at compile-time with the HAVE_RANDOM define (see the Makefile). .TP 0.5i .B a will use the Arcfour stream cipher as a PRNG. Arcfour happens to be compatible with the well-known RC4 cipher. This means that under the same key, Arcfour generates exactly the same stream as RC4... .TP 0.5i .B r will use the fresh RC6 algorythm as a PRNG; RC6 is keyed with the 128-bit seed, and then a null block is repeatedly encrypted to get the pseudo-random stream. I guess this sould be quite secure. Of course RC6 with 20 rounds is slower than random(); the compile-time option WEAK_RC6 allows you to use a 4-round version of RC6, which is faster. In order to be able to use RC6, wipe must be compiled with ENABLE_RC6 defined; see the Makefile for warnings about patent issues. In all cases the PRNG is seeded with the data gathered from the random device (see -R and -S options). .TP 0.5i .B -l As there can be some problems in determining the actual size of a block device (as some devices do not even have fixed sizes, such as floppy disks or tapes), you might need to specify the size of the device by hand; is the device capacity expressed as a number of bytes. You can use .B K (Kilo) to specify multiplication by 1024, .B M (Mega) to specify multiplication by 1048576, .B G (Giga) to specify multiplication by 1073741824 and .B b (block) to specify multiplication by 512. Thus .TP 2.0i 1024 = 2b = 1K 20K33 = 20480+33 = 20513 114M32K = 114*1024*1024+32*1024. .TP 0.5i .B -o This allows you to specify an offset inside the file or device to be wiped. The syntax of is the same as for the .B -l option. .TP 0.5i .B -e Use exact file size: do not round up file size to wipe possible remaining junk on the last block. .TP 0.5i .B -Z Don't try to wipe file sizes by repeatedly halving the file size. Note that this is only attempted on regular files so there is no use if you use .B wipe for cleaning a block or special device. .TP 0.5i .B -X Skip a given number of passes. This is useful to continue wiping from a given point when you have been wiping, say, a large disk and had to interrupt the operation. Used with -x. .TP 0.5i .B -x ,..., Specify the pass order. When .B wipe is interrupted, it will print the current randomly selected pass order permutation and the pass number as appropriate -x and -X arguments. .TP 0.5i .B -F Don't try to wipe file names. Normally, .B wipe tries to cover file names by renaming them; this does NOT guarantee that the physical location holding the old file name gets overwritten. Furthermore, after renaming a file, the only way to make sure that the name change is physically carried out is to call sync (), which flushes out ALL the disk caches of the system, whereas for ading and writing one can use the O_SYNC bit to get synchronous I/O for one file. As sync () is very slow, calling sync () after every rename () makes filename wiping extremely slow. .TP 0.5i .B -k Keep files: do not unlink the files after they have been overwritten. Useful if you want to wipe a device, while keeping the device special file. This implies .B -F. .TP 0.5i .B -D Dereference symlinks: by default, wipe will never follow symlinks. If you specify -D however, wipe will consent to, well, wipe the targets of any symlinks you might happen to name on the command line. You can't specify both -D and -r (recursive) options, first because of possible cycles in the symlink-enhanced directory graph, I'd have to keep track of visited files to guarantee termination, which, you'll easily admit, is a pain in C, and, second, for fear of having a (surprise!!) block device buried somewhere unexpected. .TP 0.5i .B -v Show version information and quit. .TP 0.5i .B -h Display help. .SH EXAMPLES .PP .TP 0.5i .B wipe -rcf /home/berke/plaintext/ Wipe every file and every directory (option -r) listed under /home/berke/plaintext/, including /home/berke/plaintext/. Regular files will be wiped with 34 passes and their sizes will then be halved a random number of times. Special files (character and block devices, FIFOs...) will not. All directory entries (files, special files and directories) will be renamed 10 times and then unlinked. Things with inappropriate permissions will be chmod()'ed (option -c). All of this will happen without user confirmation (option -f). .TP 0.5i .B wipe -kq /dev/hda3 Assuming /dev/hda3 is the block device corresponding to the third partition of the master drive on the primary IDE interface, it will be wiped in quick mode (option -q) i.e. with four random passes. The inode won't be renamed or unlinked (option -k). Before starting, it will ask you to type ``yes''. .TP 0.5i .B wipe -kqD /dev/floppy Since .B wipe never follows symlinks unless explicitly told to do so, if you want to wipe /dev/floppy which happens to be a symlink to /dev/fd0u1440 you will have to specify the -D option. Before starting, it will ask you to type ``yes''. .TP 0.5i .B wipe -rfi >wipe.log /var/log/* Here, wipe will recursively (option -r) destroy everything under /var/log, excepting /var/log. It will not attempt to chmod() things. It will however be verbose (option -i). It won't ask you to type ``yes'' because of the -f option. .TP 0.5i .B wipe -kq -l 1440K /dev/fd0 Due to various idiosyncracies of the operating system, it's not always easy to obtain the number of bytes a given device might contain (in fact, that quantity can be variable). This is why you sometimes need to tell .B wipe the amount of bytes to destroy. That's what the -l option is for. Plus, you can use b,K,M and G as multipliers, respectively for 2^9 (512), 2^10 (1024 or a Kilo), 2^20 (a Mega) and 2^30 (a Giga) bytes. You can even combine more than one multiplier !! So that 1M416K = 1474560 bytes. .SH BUGS/LIMITATIONS .PP .B Wipe should work on harddisks and floppy disks; however the internal cache of some harddisks might prevent the necessary writes to be done to the magnetic surface. It would be funny to use it over NFS. Under CFS (Cryptographic File System) the fsync() call has no effect; wipe has not much use under it anyway - use wipe directly on the corresponding encrypted files. Also, under Linux, when using a device mounted thru a loopback device, synchronous I/O does not get propagated cleanly. For wiping floppy disks, at least under Linux, there is no way, besides obscure floppy-driver specific ioctl's to determine the block size of the disk. In particular, the BLKGETSIZE ioctl is not implemented in the floppy driver. So, for wiping floppies, you must specify the size of the floppy disk using the -l option, as in the last example. This option is normally not needed for other fixed block devices, like IDE and SCSI devices. File name wiping is implemented since version 0.12. I don't know how efficient it is. It first changes the name of the file to a random- generated name of same length, calls sync (), then changes the name to a random-generated name of maximal length. File size wiping is implemented by repeatedly truncating the file to half of its size, until it becomes empty; sync () is called between such operations. Note that it is still not possible to file creation date and permission bits portably. A wipe utility working at the block device level could be written using the ext2fs library. .SH AUTHOR AND LICENCE .B Wipe was written by Berke Durak (to find my email address, just type .B echo berke1ouvaton2org|tr 12 @. in a shell). .B Wipe is released under the conditions of the GNU General Public License. .SH FILES .B /dev/random is used by default to seed the pseudo-random number generators. .SH ENVIRONMENT VARIABLES .B WIPE_SEEDPIPE If set, .B wipe will execute the command specified in it (using popen()), and will hash the command's output with the MD5 message-digest algorythm to get a 128-bit seed for its PRNG. For example, on systems lacking a /dev/random device, this variable might be set in /etc/profile to a shell script which contains various commands such as ls, ps, who, last, etc. and which are run asynchronously in order to get an output as less predictable as possible. .SH SEE ALSO open(2), fsync(2), sync(8), bdflush(2), update(8), random(3) .br wipe-0.24/wipe.c000066400000000000000000001512111300656062200135140ustar00rootroot00000000000000/* wipe * * by Berke Durak * * With many thanks to the following contributors: * Jason Axley * Alexey Marinichev * Chris L. Mason * Karako Miklos * Jim Paris * Thomas Schoepf * * Author may be contacted at 'echo berke1lambda-diode|com 12 @.' * * URL for wipe: http://lambda-diode.com/software/wipe * Git repository: http://github.com/berke/wipe * git clone https://github.com/berke/wipe.git * * Securely erase files from magnetic media * Based on data from article "Secure Deletion of Data from Magnetic * and Solid-State Memory" from Peter Gutmann. * */ /*** defines */ #define WIPE_VERSION "0.23" #define WIPE_DATE "2016-11-03" /* exit codes */ #define WIPE_EXIT_COMPLETE_SUCCESS 0 #define WIPE_EXIT_FAILURE 1 #define WIPE_EXIT_MINOR_ERROR 2 #define WIPE_EXIT_USER_ABORT 3 #define WIPE_EXIT_MANIPULATION_ERROR 4 /* NAME_MAX_TRIES is the maximum number of attempts made to find a free file * name in the current directory, by checking for random file names. */ #define NAME_MAX_TRIES 10 /* NAME_MAX_PASSES is the maximum number of times a filename is renamed. * The actual rename() calls made on a file might be less if during * the wiping process wipe fails to find a free file name after NAME_TRIES. */ #define NAME_MAX_PASSES 1 /* BUFSIZE determines the default buffer size */ #define BUFLG2 14 #define BUFSIZE (1< #include #include #include #ifdef FIND_DEVICE_SIZE_BY_BLKGETSIZE #include #ifdef memset #undef memset #endif #endif #ifdef HAVE_GETOPT #include #endif #include #include #include #include #include #include #include #include #include #include #include "random.h" #include "misc.h" #include "version.h" /* includes ***/ /*** more defines */ #define DEVRANDOM "/dev/urandom" #define QUICKPASSES 4 /* Fix sent by Jason Axley: NAME_MAX is not defined under Solaris 2.6 so we need to * define it to be something reasonable for Solaris and UFS. I define * it here to be MAXNAMLEN, since that seems reasonable (much more * reasonable than the POSIX value of 14!) and its the same thing done * under linux glibc. The quote from the Solaris /usr/include/limits.h * is included below for your edification: * * "POSIX 1003.1a, section 2.9.5, table 2-5 contains [NAME_MAX] and the * related text states: * * A definition of one of the values from Table 2-5 shall be omitted from the * on specific implementations where the corresponding value is * equal to or greater than the stated minimum, but where the value can vary * depending on the file to which it is applied. The actual value supported for * a specific pathname shall be provided by the pathconf() (5.7.1) function. * * This is clear that any machine supporting multiple file system types * and/or a network can not include this define, regardless of protection * by the _POSIX_SOURCE and _POSIX_C_SOURCE flags. * * #define NAME_MAX 14". */ #ifndef NAME_MAX #define NAME_MAX MAXNAMLEN #endif /* more defines ***/ /*** passinfo table */ /* P.Gutmann, in his article, was recommening to make * the DETERMINISTIC passes (i.e. those that don't write random data) * in random order, and to make 4 random passes before and after these. * * Here, the deterministic and the 8 random passes are made in random * order, unless, of course, the "quick" mode is selected. */ struct passinfo_s { int type; int len; char *pat; } passinfo[] = { { 0, 0, 0, }, /* 1 random */ { 0, 0, 0, }, /* 2 random */ { 0, 0, 0, }, /* 3 random */ { 0, 0, 0, }, /* 4 random */ #define FIRST_DETERMINISTIC_PASS 4 { 1, 1, "\x55", }, /* 5 RLL MFM */ { 1, 1, "\xaa", }, /* 6 RLL MFM */ { 1, 3, "\x92\x49\x24", }, /* 7 RLL MFM */ { 1, 3, "\x49\x24\x92", }, /* 8 RLL MFM */ { 1, 3, "\x24\x92\x49", }, /* 9 RLL MFM */ { 1, 1, "\x00", }, /* 10 RLL */ { 1, 1, "\x11", }, /* 11 RLL */ { 1, 1, "\x22", }, /* 12 RLL */ { 1, 1, "\x33", }, /* 13 RLL */ { 1, 1, "\x44", }, /* 14 RLL */ { 1, 1, "\x55", }, /* 15 RLL */ { 1, 1, "\x66", }, /* 16 RLL */ { 1, 1, "\x77", }, /* 17 RLL */ { 1, 1, "\x88", }, /* 18 RLL */ { 1, 1, "\x99", }, /* 19 RLL */ { 1, 1, "\xaa", }, /* 20 RLL */ { 1, 1, "\xbb", }, /* 21 RLL */ { 1, 1, "\xcc", }, /* 22 RLL */ { 1, 1, "\xdd", }, /* 23 RLL */ { 1, 1, "\xee", }, /* 24 RLL */ { 1, 1, "\xff", }, /* 25 RLL */ { 1, 3, "\x92\x49\x24", }, /* 26 RLL MFM */ { 1, 3, "\x49\x24\x92", }, /* 27 RLL MFM */ { 1, 3, "\x24\x92\x49", }, /* 28 RLL MFM */ { 1, 3, "\x6d\xb6\xdb", }, /* 29 RLL */ { 1, 3, "\xb6\xdb\x6d", }, /* 30 RLL */ { 1, 3, "\xdb\x6d\xb6", }, /* 31 RLL */ #define LAST_DETERMINISTIC_PASS 30 { 0, 0, 0, }, /* 32 random */ { 0, 0, 0, }, /* 33 random */ { 0, 0, 0, }, /* 34 random */ { 0, 0, 0, }, /* 35 random */ }; #define NUM_DETERMINISTIC_PASSES (LAST_DETERMINISTIC_PASS - FIRST_DETERMINISTIC_PASS + 1) #define MAX_PASSES (sizeof(passinfo)/(sizeof(*passinfo))) #define BUFT_RANDOM (1<<0) #define BUFT_USED (1<<1) /* Passinfo table ***/ /*** errno, num_* statistics, middle_of_line */ extern int errno; int num_errors = 0; int num_files = 0; int num_dirs = 0; int num_spec = 0; int num_symlinks = 0; int middle_of_line = 0; /* errno, num_* statistics, middle_of_line ***/ /*** options */ char *progname = 0; int o_force = 0; int o_dochmod = 0; int o_errorabort = 0; int o_recurse = 0; int o_dereference_symlinks = 0; int o_quick = 0; int o_quick_passes = QUICKPASSES; int o_quick_passes_set = 0; int o_verbose = 0; int o_silent = 0; char *o_devrandom = DEVRANDOM; int o_randseed = RANDS_DEVRANDOM; int o_randalgo = RANDA_ARCFOUR; int o_randseed_set = 0; int o_no_remove = 0; int o_dont_wipe_filenames = 0; int o_name_max_tries = NAME_MAX_TRIES; int o_name_max_passes = NAME_MAX_PASSES; int o_dont_wipe_filesizes = 0; off_t o_wipe_length; off_t o_wipe_offset = 0; int o_lg2_buffer_size = BUFLG2; int o_buffer_size = 1<= 4; n -= 4) { l = rand_Get32 (); *(b++) = table[l & mask]; l >>=8; *(b++) = table[l & mask]; l >>=8; *(b++) = table[l & mask]; l >>=8; *(b++) = table[l & mask]; } if (n) { l = rand_Get32 (); while (n--) { *(b++) = table[l & mask]; l >>= 8; } } } /* fill_random_from_table ***/ /*** fill_random */ /* fills buffer b with n (pseudo-)random bytes. */ inline static void fill_random (char *b, int n) { rand_Fill ((u8 *) b, n); } /* fill_random ***/ /*** fill_pattern */ /* this could be seriously optimised */ void fill_pattern (char *b, int n, char *p, int l) { int i; char *p1, *p2; for (p1 = p, p2 = p + l, i = 0; irandom_buffers[i++].buffer)); for (i = 0; in_buffers; free (wi->buffers[i++].buffer)); } /* shut_wipe_info ***/ /*** dirty_all_buffers */ void dirty_all_buffers (struct wipe_info *wi) { int i; for (i = 0; irandom_buffers[i++].type |= BUFT_USED); } /* dirty_all_buffers ***/ /*** revitalize_random_buffers */ int revitalize_random_buffers (struct wipe_info *wi) { int i; for (i = 0; irandom_buffers[i].type & BUFT_USED) { fill_random (wi->random_buffers[i].buffer, wi->random_length); wi->random_buffers[i].type &= ~BUFT_USED; return 0; } } return -1; /* there were no buffers to revitalize */ } /* revitalize_random_buffers ***/ /*** wipe_pattern_buffer */ struct wipe_pattern_buffer *get_random_buffer (struct wipe_info *wi) { int i; for (i = 0; irandom_buffers[i].type & BUFT_USED)) { wi->random_buffers[i].type |= BUFT_USED; return &wi->random_buffers[i]; } } /* no random buffers available, revitalize buffer 0 */ fill_random (wi->random_buffers[0].buffer, wi->random_length); /* no need to set used flag */ return &wi->random_buffers[0]; } /* wipe_pattern_buffer ***/ /*** init_wipe_info */ void init_wipe_info (struct wipe_info *wi) { int i, j; wi->n_passes = o_quick?o_quick_passes:MAX_PASSES; /* allocate buffers for random patterns */ for (i = 0; irandom_buffers[i].type = BUFT_RANDOM; wi->random_buffers[i].buffer = malloc (o_buffer_size); if (!wi->random_buffers[i].buffer) { fprintf (stderr, "could not allocate buffer [1]"); exit (EXIT_FAILURE); } } /* allocate buffers for periodic patterns */ if (!o_quick) for (wi->n_buffers = 0, i = 0; in_passes; i++) { if (!passinfo[i].type) { wi->passes[i] = 0; /* random pass... */ } else { /* look if this pattern has already been allocated */ for (j = 0; jn_buffers; j++) { if (wi->buffers[i].pat_len == passinfo[i].len && !memcmp (wi->buffers[i].buffer, passinfo[i].pat, passinfo[i].len)) break; } if (j >= wi->n_buffers) { /* unfortunately we'll have to allocate a new buffers */ j = wi->n_buffers ++; wi->buffers[j].type = 1; /* periodic */ wi->buffers[j].buffer = malloc (o_buffer_size); if (!wi->buffers[j].buffer) { fprintf (stderr, "could not allocate buffer [2]"); exit (EXIT_FAILURE); } wi->buffers[j].pat_len = passinfo[i].len; /* fill in pattern */ fill_pattern (wi->buffers[j].buffer, o_buffer_size, passinfo[i].pat, passinfo[i].len); } wi->passes[i] = &wi->buffers[j]; } } } /* init_wipe_info ***/ #define fnerror(x) { num_errors++; if(middle_of_line) fputc('\n', stderr); fprintf (stderr, "\r%.32s: " x ": %.32s\n", fn, strerror (errno)); } #define fnerrorq(x) { num_errors++; if(middle_of_line) fputc('\n', stderr); fprintf (stderr, "\r%.32s: " x "\n", fn); } #define FLUSH_MIDDLE if (middle_of_line) { fputc ('\n', stderr); \ middle_of_line = 0; } /*** directory_name */ #if 0 inline static char *directory_name (char *fn) { char *dn; char *sp; int dl; sp = strrchr (fn, '/'); if (!sp) return strdup (""); dl = sp - fn + 1 + 1; dn = malloc (dl); if (!dn) return 0; memcpy (dn, fn, dl - 1); dn[dl] = 0; return dn; } #else inline static int directory_name_length (char *fn) { char *bn; bn = strrchr (fn, '/'); if (!bn) return 0; else return bn - fn + 1; } #endif /* directory_name ***/ static char valid_filename_chars[64] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-."; /*** wipe_filename_and_remove */ /* actually, after renaming a file, the only way to make sure that the * name change is physically carried out is to call sync (), which flushes * out ALL the disk caches of the system, whereas for * reading and writing one can use the O_SYNC bit to get syncrhonous * I/O for one file. as sync () is very slow, calling sync () after * every rename () makes wipe extremely slow. */ static int wipe_filename_and_remove (char *fn) { int i, j, k, l; int r = -1; int fn_l, dn_l; /* char *dn; */ char *buf[2]; struct stat st; int t_l; /* target length */ /* dn = directory_name (fn); */ fn_l = strlen (fn); dn_l = directory_name_length (fn); buf[0] = malloc (fn_l + NAME_MAX + 1); buf[1] = malloc (fn_l + NAME_MAX + 1); r = 0; t_l = fn_l - dn_l; /* first target length */ if (buf[0] && buf[1]) { strcpy (buf[0], fn); strcpy (buf[1], fn); for (j = 1, i = 0; i < o_name_max_passes; j ^= 1, i++) { for (k = o_name_max_tries; k; k--) { l = t_l; fill_random_from_table (buf[j] + dn_l, l, valid_filename_chars, 0x3f); buf[j][dn_l + l] = 0; if (stat (buf[j], &st)) break; } if (k) { if (!o_silent) { fprintf (stderr, "\rRenaming %32.32s -> %32.32s", buf[j^1], buf[j]); middle_of_line = 1; fflush (stderr); } if (rename (buf[j^1], buf[j])) { FLUSH_MIDDLE fprintf (stderr, "%.32s: could not rename '%s' to '%s': %s (%d)\n", fn, buf[j^1], buf[j], strerror (errno), errno); r = -1; break; } (void) sync (); } else { /* we could not find a target name of desired length, so * increase target length until we find one. */ t_l ++; j ^= 1; } } if (remove (buf[j^1])) r = -1; } free (buf[0]); free (buf[1]); return r; } /* wipe_filename_and_remove ***/ #include static double get_time_of_day(void) { struct timeval tv; gettimeofday(&tv, 0); return (tv.tv_usec * 1e-6 + tv.tv_sec); } static double eta_start_time; static void eta_begin() { eta_start_time = get_time_of_day(); } static void eta_progress(char *buf, size_t bufsiz, double frac) { double now; double elapsed; double total; double seconds_per_year; double remaining; long lsec; int sec; long lmin; int min; long lhours; int hours; long ldays; int days; int weeks; now = get_time_of_day(); elapsed = now - eta_start_time; buf[0] = '\0'; if (elapsed < 5 && frac < 0.1) return; total = elapsed / frac; seconds_per_year = 365.25 * 24 * 60 * 60; remaining = total - elapsed; if (remaining > seconds_per_year) { snprintf(buf, bufsiz, " ETA %g years", remaining / seconds_per_year); return; } lsec = remaining; sec = lsec % 60; lmin = lsec / 60; min = lmin % 60; lhours = lmin / 60; hours = lhours % 24; ldays = lhours / 24; days = ldays % 7; weeks = ldays / 7; if (weeks > 0) { snprintf(buf, bufsiz, " ETA %dw %dd", weeks, days); return; } if (days > 0) { snprintf(buf, bufsiz, " ETA %dd %dh", days, hours); return; } if (hours > 0) { snprintf(buf, bufsiz, " ETA %dh%02dm", hours, min); return; } snprintf(buf, bufsiz, " ETA %dm%02ds", min, sec); } static void pad(char *buf, size_t bufsiz) { size_t len; len = strlen(buf); while (len + 1 < bufsiz) buf[len++] = ' '; buf[len] = '\0'; } static void backspace(char *dst, const char *src) { while (*src++) *dst++ = '\b'; *dst = '\0'; } void wipe_continuation_message(void *arg) { int i; struct wipe_info *wi = arg; fprintf (stderr, "*** If you want to resume wiping while preserving the pass order, use these options:\n"); fprintf (stderr, "*** -X %d -x ", wi->current_pass); for (i = 0; in_passes; i++) { if (i > 0) fprintf (stderr, ","); fprintf (stderr, "%d", wi->p[i]); } fprintf(stderr, "\n"); fflush (stderr); } /*** dothejob -- nonrecursive wiping of a single file or device */ /* determine parameters of region to be wiped * file descriptor, * offset, * size. * * * compute the number of entire buffers needed to cover the region, * and the size of the remaining area. * * if pattern buffers have not been computed, compute them. * * compute one pattern buffer (either fill it or fetch it). * send an asynchronous write. * * select */ #define max(x,y) ((x>y)?x:y) static int dothejob (char *fn) { int fd; int bpi = 0; /* block progress indicator enabled ? */ int i; off_t j; static struct wipe_info wi; static int wipe_info_initialized = 0; struct wipe_pattern_buffer *wpb = 0; int *p = wi.p; struct stat st; off_t buffers_to_wipe; /* number of buffers to write on device */ int first_buffer_size; int last_buffer_size; int this_buffer_size; time_t lt = 0, t; fd_set w_fd; /* passing a null filename pointer means: free your internal buffers, please. */ /* thanks to Thomas Schoepf and Alexey Marinichev for pointing this out */ if (!fn) { if (wipe_info_initialized) shut_wipe_info (&wi); abort_handler = NULL; return 0; } /* to do a cryptographically strong random permutation on the * order of the deterministic passes, we need * lg_2(NUM_DETERMINISTIC_PASSES!) bits of entropy: * this means about 128 bits. * * rand_seed () should give 31 bits of entropy per call * we need a maximum of lg_2(NUM_DETERMNISTIC_PASSES) bits of entropy per random * value. therefore we should call rand_seed () at least for * every 31/lg_2(NUM_DETERMNISTIC_PASSES) ~= 6 random values. * * on the other way, as we'll constantly force disk access, * /dev/urandom should have no difficulty providing us good random * values. */ if (!o_quick && o_pass_order[0] < 0) { for (i = 0; i= 0) { for (i = 0; i %d", l); o_wipe_length = l; #else off_t l; /* find device size by seeking... might work on some devices */ l = lseek (fd, 0, SEEK_END); if (!l) { fnerror ("could not get device block size with lseek; check option -l"); } debugf ("lseek -> %d", l); o_wipe_length = l; #endif } #endif debugf ("block device block size %d", st.st_blksize); } else { /* not a block device */ o_wipe_length = st.st_size; if (!o_wipe_exact_size) { o_wipe_length += st.st_blksize - (o_wipe_length % st.st_blksize); } } o_wipe_length -= o_wipe_offset; } /* don't do anything to zero-sized files */ if (!o_wipe_length) { goto skip_wipe; } #if SIXTYFOUR debugf ("o_wipe_length = %Ld", o_wipe_length); #else debugf ("o_wipe_length = %ld", o_wipe_length); #endif /* compute number of writes... */ { int fb, lb; fb = o_wipe_offset >> o_lg2_buffer_size; lb = (o_wipe_offset + o_wipe_length + o_buffer_size - 1) >> o_lg2_buffer_size; buffers_to_wipe = lb - fb; debugf ("fb = %d lb = %d", fb, lb); if (buffers_to_wipe == 1) { last_buffer_size = first_buffer_size = o_wipe_length; } else { first_buffer_size = o_buffer_size - (o_wipe_offset & (o_buffer_size - 1)); last_buffer_size = (o_wipe_offset + o_wipe_length) & (o_buffer_size - 1); if (!last_buffer_size) last_buffer_size = o_buffer_size; } } debugf ("buffers_to_wipe = %d first_buffer_size = %d last_buffer_size = %d", buffers_to_wipe, first_buffer_size, last_buffer_size); /* initialize wipe info */ if (!wipe_info_initialized) { init_wipe_info (&wi); wipe_info_initialized = 1; abort_handler = wipe_continuation_message; abort_handler_arg = &wi; } /* if the possibly existing leftover random buffers * from the last wipe are shorter than what we need for * now, mark all of them as used. * * ok it's possible to have a smarter random buffer management, * but it only helps on small files (i.e. files smaller than the * buffer size) so it's not really worth it (in other words: * you can't escape from linear asymptotic complexity:) */ { int x; if (buffers_to_wipe <= 2) x = max (first_buffer_size, last_buffer_size); else x = o_buffer_size; if (x > wi.random_length) dirty_all_buffers (&wi); wi.random_length = x; } debugf ("buffers_to_wipe = %d, o_buffer_size = %d, wi.n_passes = %d", buffers_to_wipe, o_buffer_size, wi.n_passes); /* do the passes */ eta_begin(); for (i = o_skip_passes; i2) && j<(buffers_to_wipe>>1))) { char buf1[30]; char buf1_bs[sizeof (buf1)]; char buf2[18]; char buf2_bs[sizeof(buf2)]; snprintf(buf1, sizeof(buf1), "[%8ld / %8ld]", (long) j, (long)buffers_to_wipe); backspace(buf1_bs, buf1); eta_progress(buf2, sizeof(buf2), ((double) (i - o_skip_passes) + ((double)j / buffers_to_wipe)) / (wi.n_passes - o_skip_passes)); if (buf2[0]) pad(buf2, sizeof(buf2)); backspace(buf2_bs, buf2); fprintf(stderr, "%s%s%s%s", buf1, buf2, buf2_bs, buf1_bs); fflush (stderr); lt = t; bpi = 1; } } /* get a fresh random buffer */ { if (o_quick || !wi.passes[p[i]]) { wpb = get_random_buffer (&wi); } else { wpb = wi.passes[p[i]]; } for (;;) { wr = write (fd, wpb->buffer, this_buffer_size); /* asynchronous write */ if (wr < 0) { if (errno == EAGAIN) { /* since we are idle we can do some socially useful * work. */ if (revitalize_random_buffers (&wi)) { /* there isn't anything we can do to occupy ourselves, * so we'll just wait until our previous write requests * are queued, and retry. */ FD_ZERO (&w_fd); FD_SET (fd, &w_fd); if (select (fd + 1, 0, &w_fd, 0, 0) < 0) { fnerror ("select"); close (fd); return -1; } /* we MUST have FD_ISSET(&w_fd) */ } } else { fnerror ("write error"); exit (EXIT_FAILURE); } } else if (wr != this_buffer_size) { /* argh short write... what does this mean exactly with * non-blocking i/o ? */ debugf ("short write, expecting %d got %d", this_buffer_size, wr); fnerror ("short write"); close (fd); return -1; } else break; } } #ifndef HAVE_OSYNC if (fsync (fd)) { fnerror ("fsync error [1]"); close (fd); return -1; } #endif } if (fsync (fd)) { fnerror ("fsync error [2]"); close (fd); return -1; } } /* skipping parameters are only meant for first file */ o_skip_passes = 0; o_pass_order[0] = 1; /* try to wipe out file size by truncating at various sizes... */ skip_wipe: if (S_ISREG(st.st_mode) && !o_dont_wipe_filesizes) { off_t s; u32 x; s = st.st_size; x = rand_Get32 (); while (s) { s >>= 1; x >>= 1; if (x & 1) { if (ftruncate (fd, s)) { fnerror ("truncate"); close (fd); return -1; } } } } close (fd); } /* if this is a symbolic link, then remove the target of the link */ /* of course, we have a race condition here. */ /* no user should be able to control a link you're wiping... */ if (o_dereference_symlinks) { struct stat st2; if (lstat (fn, &st2)) { fnerror("lstat error"); return -1; } if (S_ISLNK(st2.st_mode)) { int m; char buf[NAME_MAX+1]; num_symlinks ++; m = readlink (fn, buf, NAME_MAX); if (m < 0) { fnerror ("readlink"); return -1; } buf[m] = 0; if (do_remove (buf)) { fnerror("remove"); return -1; } } } /* remove link or file */ if (do_remove (fn)) { fnerror("remove"); return -1; } if (!o_silent) { fprintf (stderr, "\r \r"); middle_of_line = 0; } if (S_ISLNK(st.st_mode)) { num_symlinks ++; if (o_verbose) { printf ("Not following symbolic link %.32s\n", fn); middle_of_line = 0; } } else { num_files ++; if (o_verbose) { printf ("File %.32s (%ld bytes) wiped\n", fn, (long) st.st_size); middle_of_line = 0; } } return 0; } int recursive (char *fn) { int r = 0; struct stat st; char *olddir; if (!strcmp(fn,".") || !strcmp(fn,"..")) { printf("Will not remove %s\n", fn); return 0; } if (!strcmp(fn,".") || !strcmp(fn,"..")) { printf("Will not remove %s\n", fn); return 0; } if (lstat (fn, &st)) { fnerror ("stat error"); return -1; } if (S_ISDIR(st.st_mode)) { DIR *d; struct dirent *de; olddir = getcwd (0, 4096); if (!olddir) { fnerror("getcwd"); return -1; } if (o_verbose) { printf ("Entering directory '%s'\n", fn); middle_of_line = 0; } /* fix on 14.02.1998 -- check r/w/x permissions on directory, fix if necessary */ if ((st.st_mode & 0700) != 0700) { if (o_dochmod) { if (o_verbose) { printf ("Changing permissions from %04o to %04o\n", (int) st.st_mode, (int) st.st_mode | 0700); middle_of_line = 0; } if (chmod (fn, st.st_mode | 0700)) { fnerror ("chmod [1]"); return -1; } } else { fnerrorq ("directory mode is not u+rwx; use -c option"); return -1; } } d = opendir (fn); if (!d) { if (errno == EACCES && o_dochmod) { if (chmod (fn, st.st_mode | 0700)) { fnerror("chmod [2]"); return -1; } else d = opendir (fn); } else { fnerror("opendir"); return -1; } } if (!d) { fnerror("opendir after chmod"); return -1; } if (chdir (fn)) { fnerror("chdir"); return -1; } errno = 0; num_dirs ++; while ((de = readdir (d))) { if (strcmp (de->d_name, ".") && strcmp (de->d_name, "..")) { if (recursive (de->d_name)) { /* num_errors ++; */ r = -1; if (o_errorabort) break; } } errno = 0; } if (errno) { fnerror("readdir"); return -1; } closedir (d); if (o_verbose) { printf ("Going back to directory %s\n", olddir); middle_of_line = 0; } if (chdir (olddir)) { fnerror("chdir .."); return -1; } free (olddir); if (!r && !o_no_remove && rmdir (fn)) { fnerror ("rmdir"); return -1; } } else { if (S_ISREG(st.st_mode)) { int rc = dothejob (fn); abort_handler = NULL; return rc; } else if (S_ISLNK(st.st_mode)) { num_symlinks ++; } else { if (o_verbose) { printf ("Not wiping special file %s in recursive mode\n", fn); middle_of_line = 0; } num_spec ++; } if (do_remove (fn)) { fnerror ("remove"); return -1; } } return r; } /* dothejob ***/ /*** banner */ void banner () { fprintf (stderr, "This is wipe version " WIPE_VERSION ".\n" "\n" "Author: Oguz Berke Antoine Durak.\n" "Author's e-mail address: echo berke1lambda-diode2com|tr 12 @.\n" "Web site: http://lambda-diode.com/software/wipe/\n" "Release date: " WIPE_DATE "\n" "Git version: " WIPE_GIT "\n" "\n" "Based on data from \"Secure Deletion of Data from Magnetic and Solid-State\n" "Memory\" by Peter Gutmann .\n"); } /* banner ***/ #define OPTSTR "x:X:DfhvrqspciR:S:M:kFZl:o:b:Q:T:P:e" /*** reject and usage */ void reject (char *msg, ...) { va_list va; va_start(va, msg); fprintf (stderr, "Invocation error (-h for help): "); vfprintf(stderr, msg, va); fprintf(stderr, "\n"); va_end(va); exit(WIPE_EXIT_MANIPULATION_ERROR); } void usage (void) { fprintf (stderr, "Usage: %s [options] files...\n" "Options:\n" "\t\t-a Abort on error\n" "\t\t-b Set the size of the individual i/o buffers\n" "\t\t\tby specifying its logarithm in base 2. Up to 30 of these\n" "\t\t\tbuffers might be allocated\n" "\t\t-c Do a chmod() on write-protected files\n" "\t\t-D Dereference symlinks (conflicts with -r)\n" "\t\t-e Use exact file size: do not round up file size to wipe\n" "\t\t\tpossible junk remaining on the last block\n" "\t\t-f Force, i.e. don't ask for confirmation\n" "\t\t-F Do not attempt to wipe filenames\n" "\t\t-h Display this help\n" "\t\t-i Informative (verbose) mode\n" "\t\t-k Keep files, i.e. do not remove() them after overwriting\n" "\t\t-l Set wipe length to bytes, where is\n" "\t\t\tan integer followed by K (Kilo:1024), M (Mega:K^2) or\n" "\t\t\tG (Giga:K^3)\n" "\t\t-M (l|r) Set PRNG algorithm for filling blocks (and ordering passes)\n" "\t\t\tl Use libc's " #ifdef HAVE_RANDOM "random()" #else "rand()" #endif " library call\n" #ifdef RC6_ENABLED "\t\t\tr Use RC6 encryption algorithm\n" #endif "\t\t\ta Use arcfour encryption algorithm\n" "\t\t-o Set wipe offset to , where has the\n" "\t\t\tsame format as \n" "\t\t-P Set number of passes for filename wiping.\n" "\t\t\tDefault is 1.\n" "\t\t-Q set number of passes for quick wipe\n" "\t\t-q Quick wipe, less secure, 4 random passes by default\n" "\t\t-r Recurse into directories -- symlinks will not be followed\n" "\t\t-R Set random device (or random seed command with -S c)\n" "\t\t-S (r|c|p) Random seed method\n" "\t\t\t r Read from random device (strong)\n" "\t\t\t c Read from output of random seed command\n" "\t\t\t p Use pid(), clock() etc. (weakest)\n" "\t\t-s Silent mode -- suppresses all output\n" "\t\t-T Set maximum number of tries for free\n" "\t\t\tfilename search; default is 10\n" "\t\t-v Show version information\n" "\t\t-Z Do not attempt to wipe file size\n" "\t\t-X Skip this number of passes (useful for continuing a wiping operation)\n" "\t\t-x Define pass order\n", progname ); exit (WIPE_EXIT_COMPLETE_SUCCESS); } /* reject and usage ***/ /*** parse_length_offset_description */ int parse_length_offset_description (char *d, off_t *v) { off_t s = 0, t = 0; char c; for (;;) { c = *d; switch (c) { case 0: *v = s + t; debugf ("parse length description: *v = %ld", *v); return 0; case '0' ... '9': s *= 10; s += c & 0x0f; break; case 'K': t += s << 10; s = 0; break; case 'M': t += s << 20; s = 0; break; case 'G': t += s << 30; s = 0; break; case 'b': /* b for block */ t += s << 9; s = 0; break; default: fprintf (stderr, "error: character '%c' is illegal in length description \"%s\"\n", c, d); return -1; } d ++; } } /* parse_length_offset_description ***/ /*** main */ int main (int argc, char **argv) { int i; int n, ndir, nreg, nlnk; int c; struct stat st; /* basic setup */ progname = strrchr (*argv, '/'); if (progname) progname ++; else progname = *argv; signal (SIGINT, signal_handler); signal (SIGTERM, signal_handler); /* parse options */ for (;;) { c = getopt (argc, argv, OPTSTR); if (c<0) break; switch (c) { case 'X': o_skip_passes = atoi(optarg); if (o_skip_passes <= 0 || o_skip_passes > MAX_PASSES) { reject ("number of skipped passes must be between 0 and %d which %d is not", MAX_PASSES, o_skip_passes); } break; case 'x': { int p_index = 0; char *pch; int current_int; debugf ("splitting string \"%s\" into tokens:", optarg); pch = strtok (optarg, ","); while (pch != NULL) { if (p_index >= MAX_PASSES) { fprintf (stderr, "Too many arguments: %s\n", pch); exit (WIPE_EXIT_MANIPULATION_ERROR); } current_int = atoi(pch); if (current_int < 0 || current_int > MAX_PASSES) { fprintf (stderr, "Invalid pass number: %s\n", pch); exit (WIPE_EXIT_MANIPULATION_ERROR); } o_pass_order[p_index] = current_int; debugf (" p[%d] = %d, (pch: %s)", p_index, current_int, pch); pch = strtok(NULL, ","); p_index ++; } debugf ("result: "); for (i = 0; i < p_index; i++) { debugf ("%d ", o_pass_order[i]); } if (p_index < MAX_PASSES) { reject("not enough arguments for -x (got %d, need %d)\n", p_index, MAX_PASSES); } } break; case 'c': o_dochmod = 1; break; case 'D': o_dereference_symlinks = 1; break; case 'e': o_wipe_exact_size = 1; break; case 'F': o_dont_wipe_filenames = 1; break; case 'f': o_force = 1; break; case 'i': o_verbose = 1; break; case 'k': o_no_remove = 1; break; case 'P': o_name_max_passes = atoi (optarg); break; case 'q': o_quick = 1; break; case 'R': o_devrandom = optarg; o_randseed_set = 1; break; case 'r': o_recurse = 1; break; case 's': o_silent = 1; break; case 'T': o_name_max_tries = atoi (optarg); break; case 'v': banner (); exit (0); case 'Z': o_dont_wipe_filesizes = 1; break; case 'b': o_lg2_buffer_size = atoi (optarg); if (o_lg2_buffer_size < 9) reject ("the power of 2 specified must be greater than 8 " "to ensure a buffer size of at least 512 bytes"); else if (o_lg2_buffer_size > 30) { reject ("A power of two over 30 is probably useless " "since it means a buffer over one gigabyte"); } o_buffer_size = 1<= argc) reject ("wrong number of arguments"); if (o_recurse && o_dereference_symlinks) reject ("options -D and -r are mutually exclusive"); /* automatic detection of a suitable random device */ if (!o_randseed_set) { char *t; t = getenv ("WIPE_SEEDPIPE"); if (t || access (o_devrandom, R_OK)) { o_devrandom = t; if (access (o_devrandom, X_OK)) o_randseed = RANDS_PID; else o_randseed = RANDS_PIPE; } } /* initialise PRNG */ rand_Init (); /* stat specified files/directories */ n = argc-optind; ndir = nreg = nlnk = 0; for (i = optind; i1?"ies":"y"); else { fprintf (stderr, "Use -r option to wipe directories\n"); exit (EXIT_FAILURE); } } if (o_skip_passes) { fprintf (stderr, "Will skip %d passes\n", o_skip_passes); fflush (stderr); } if (!o_force) { char buf2[80]; char buf[80]; char *b = buf; if (nreg) { sprintf (b, "%d regular file%s", nreg, (nreg>1)?"s":""); b += strlen (b); } if (ndir && o_recurse) { sprintf (b, "%s%d director%s", (b != buf)?((n-nreg-ndir)?", ":" and "):"", ndir, ndir!=1?"ies":"y"); b += strlen (b); } if (nlnk) { sprintf (b, "%s%d symlink%s%s:", (b != buf)?" and ":"", nlnk, nlnk!=1?"s":"", (o_dereference_symlinks? (nlnk!=1?" and their targets":" and its target"): (nlnk!=1?" (without following the links)":" (without following the link)"))); b += strlen(b); } if (n-nreg-ndir-nlnk) { sprintf (b, "%s%d special file%s", (b != buf)?" and ":"", n-nreg-ndir, (n-nreg-ndir-nlnk)!=1?"s":""); } /* if stderr is a tty, ask for confirmation */ #define CONFIRMATION_MAX_RETRIES 5 if (isatty (fileno (stderr))) { for (i = 0; i .br .SH UYARI Bu türkçe elkitabı sayfası ingilizce olanından daha eskidir ve programa son eklenen bir kaç seçeneğe değinmemektedir. .SH AÇIKLAMA Manyetik ortamdan silindiği düşünülen bilgilerin geri alınması çoğu kişinin sandığından daha kolaydır. Bir dosyanın data bloklarının üzerine başka bilgiler yazılmış olsa bile, Manyetik Güç Mikroskopisi adı verilen ve asgari kaynaklara sahip olan herhangi bir düşmanın erişiminde olan bir teknik, disk'e yazılmış son iki veya üç katmanın kolayca elde edilmesini sağlamaktadır. .B Wipe yokedilecek dosyanın üzerine tekrar tekrar özel bit örüntüleri yazar; sistemin fsync() çağrısını ve open()'in O_SYNC parametresini kullanarak manyetik medya'ya bu örüntülerin aktarılmasını sağlar. Olağan konumda, 8'i eşit dağılımlı rasgele bit örüntüleri olmak üzere toplam 34 örüntü dosyaya üstüste yazılır. Bu örüntüler, Peter Gutmann'ın (pgut001@cs.auckland.ac.nz) "Bilginin Manyetik ve Katıhal Bellekten Güvenli Olarak Silinmesi" adlı yazısında önerilmiştir. Çok daha az güvenli olmakla beraber, rasgele örüntülerle sadece 4 geçiş yapan hızlı bir konum da kullanılabilir. .PP .SH "KOMUT SATIRI SEÇENEKLERİ" .TP 0.5i .B -f (onay sorusunu etkisizleştir) Bu seçenek belirtilmediği takdirde .B wipe silinecek dosya ve dizinlerin sayılarını belirtip onay istiyecektir. Bu onay sorusuna "yes" veya "no" cevabı vermeniz gerekiyor. .TP 0.5i .B -r (altdizinlere gir) Tüm altdizinleriyle beraber dizinlerin silinmesine olanak sağlar. Simgesel bağlar takip edilmez. .TP 0.5i .B -c (gerekirse dosya izin bitlerini değiştir) Bu seçenek, eğer silinecek bir dosyanın (dizinin) yazma (yazma, okuma veya çalıştırma) izin bitleri kapalı ise izinleri değiştirmek için chmod () sistem çağrısını kullanılmasını sağlar. .TP 0.5i .B -i (ayrıntılı bilgi konumu) Bu konum stdout'a ayrıntılı bilgi yazılmasını sağlar. Olağan konumda bütün iletiler stderr'e yazılır. .TP 0.5i .B -s (sessiz çalışma konumu) Bu konumda hata iletileri ile başlangıçtaki onay sorusu dışındaki bütün iletileri kaldırır. .TP 0.5i .B -q (hızlı silme konumu) Bu konumda .B wipe her dosya üstünde sadece 4 geçiş yapacaktır. Bu geçişlerde rasgele bilgi yazacaktır. .TP 0.5i .B -a (hata durumda işlemi iptal et) Program kurtarılamaz bir hata durumunda EXIT_FAILURE koduyla çıkacaktır. .TP 0.5i .B -R Bir tane parametre gerektiren bu seçenekle farklı bir /dev/random aygıtı veya olağan çıktısı MD5 öz çıkarma algoritmasıyla kıyılacak bir komut belirtebilirsiniz. Bu parametrenin tam anlamı -S seçeneği ile belli edilir. .TP 0.5i .B -S (rasgele tohumlama yöntemi) Bu seçenek tek harflik bir parametre alır; bu harf rasgele bilgi aygıtı/rasgele tohum komutunu parametresinin nasıl kullanılacağını belirler. Olağan rasgele bilgi aygıtı /dev/random'dur, farklı değerleri -R seçeneği ile belirtilebilir. .PP .PD 0 Mümkün olan tek harflik seçenekler şunlardır: .TP 0.5i .B r Rasgele bilgi aygıtı/rasgele tohum komutu parametresini okunacak olağan bir dosya veya karakter aygıtı olarak muamele görmesini sağlar. Bu /dev/random aygıtları ve FIFO (ilk giren ilk çıkar kuyrukları) veya UNIX socket'ler için uygun seçimdir. .TP 0.5i .B c Sözkonusu parametrenin bir komut olarak çalıştırılması için. Gerekli tohumlar komutun çıktısının MD5 ile kıyılmasından elde edilecektir. Daha ayrıntılı bilgi için WIPE_SEEDPIPE çevre değişkenine bakınız. .TP 0.5i .B p Daha uygun hiç bir rasgele bilgi kaynağı yoksa daha az güvenli olan bu seçeneği belirtiniz: wipe gerekli tohumlarını çevre değişkenlerini, tarihi ve zamanı, program numarasını vs. kullanarak elde edecektir. .TP 0.5i .B -M (l|a|r|p) (sözde rasgele sayı üretici algoritması) .PP .PD 0 Rasgele geçişlerde dosyaların üzerine rasgele rasgele bilgi katarı belirteceğiniz harfle aşağıdaki algoritmalarla üretilecektir: .TP 0.5i .B l sisteminize göre C kütüphanesinin random() veya rand() üreticisini kullanır. Çoğu sistemde rand() doğrusal eşleşik ve dolayısıyla son derece zayıf bir üreticidir. rand() ile random() arasındaki seçim derlemede HAVE_RANDOM tanımlaması ile belirlenir (Makefile'a bakınız). .TP 0.5i .B a Arcfour katar şifresini SRSÜ olarak kullanır. Arcfour tanınmış RC4 şifresiyle uyumludur: bu aynı anahtar ile tamamen aynı katarı üreteceği anlamına gelir... .TP 0.5i .B r yeni çıkan RC6 algoritmasını SRSÜ olarak kullanır; RC6 128 bit'lik tohumla anahtarlandıktan sonra, rasgele bilgi katarı sıfırlardan oluşan bir bloğun üstüste şifrelenmesinden elde edilir. Doğal olarak 20 döngülük RC6 C kütüphanesinin random()'undan daha yavaştır; derleme zamanında WEAK_RC6 tanımını yaparsanız 20 yerine 4 döngü kullanılır. Dikkat! Patent sorunlarından dolayı, RC6'yı kullanabilmeniz için Makefile'ı okuyup, orda belirtilen koşullar uygun ise wipe'ı ENABLE_RC6 ile derlemeniz gerekir. Her durumda SRSÜ rasgele bilgi aygıtının çıktısıyla tohumlanır (-R ve -S seçeneklerine bakınız). .TP 0.5i .B -v (sürüm) Sürüm bilgisini gösterir ve çıkar. .TP 0.5i .B -h (yardım) Bununla kısa bir yardım bilgisi gösterir. .SH ÖRNEKLER .PP wipe -rcf /home/berke/plaintext/* wipe -q /dev/hda3 wipe -rfi >wipe.log /var/log/auth.* .SH HATALAR/SINIRLAMALAR .PP .B Wipe'ın sabit diskler ve disketler üzerine etkili olması gerekir; ancak bazı sabit disklerin cep bellekleri manyetik ortama gerekli yazımların yapılmasını engelliyebilir. .B Wipe'ı NFS üzerinden kullanmak pek mantıklı değildir. CFS altında ise (Cryptographic File System - Şifreli Kütük Sistemi) .B wipe'ın pek bir yararı olmaz; gerçek dosya sistemi altındaki ilgili şifreli dosyalar üzerinde .B wipe'ın doğrudan kullanılması çok daha yararlı olacaktır. Dikkat edilmesi gereken bir nokta ise dosyaların adları, yaratılış, değişim ve erişim tarihleri ile boyutları ile ilgili bilgileri silen ve dosya sisteminin inceliklerinden bağımsız olup kullanıcı konumunda çalışabilecek bir silme programı yapmak imkansız. Bu ancak, çekirdeğin dosyaların güvenli olarak silinmesini sağlayan çağrılar sunmasıyla gerçekleşebilir. Bu durumda masumane dosya isimleri kullanmanız önerilir. .SH PROGRAMI YAZAN .B Wipe isimli komutçuk Berke Durak (berke@ouvaton.org) tarafından yazılmıştır ve GNU Genel Kamu Lisansı koşulları altında sunulmaktadır. .B Wipe Peter Gutmann'ın bir yazısındaki bilgilere dayanmaktadır. .SH DOSYALAR .B /dev/urandom libc'nin random() adlı toplanır geribeslemeli sözde rasgele sayı üreticisini tohumlamak için kullanılıyor. Yazılan her 1024 sekizli için /dev/urandom'dan 32 bitlik bir tohum alınır. .SH GÖNDERMELER open(2), fsync(2), sync(8), bdflush(2), update(8), random(3) .br