Files
hiddencrypto/.secure-delete/srm.c
T
2024-09-12 22:24:27 -06:00

310 lines
10 KiB
C

/* Secure RM - by van Hauser / [THC], vh@thc.org
*
* Secure ReMove first overwrites then renames and finally deletes the target
* file(s) specified via parameters.
* For security reasons full 32kb blocks are written so that the whole block
* on which the file(s) live are overwritten. (change #define #BLOCKSIZE)
* The option -l overwrites two times the data.
* The option -ll overwrites the data once.
* Standard mode is a real security wipe for 38 times, flushing
* the caches after every write. The wipe technique was proposed by Peter
* Gutmann at Usenix '96 and includes 10 random overwrites plus 28 special
* defined characters. Take a look at the paper of him, it's really worth
* your time.
*
* Advice : set "alias rm 'srm -v'"
*
* Read the manual for limitations.
* Compiles clean on OpenBSD, Linux, Solaris, AIX and I guess all others.
*
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "sdel.h"
int slow = O_SYNC;
int recursive = 0;
int zero = 0;
unsigned long bufsize = BLOCKSIZE;
int fd;
void help () {
printf("srm %s (c) 1997-2003 by %s <%s>\n\n", VERSION, AUTHOR, EMAIL);
printf("Syntax: %s [-dflrvz] file1 file2 etc.\n\n", prg);
printf("Options:\n");
printf("\t-d ignore the two dot special files \".\" and \"..\".\n");
printf("\t-f fast (and insecure mode): no /dev/urandom, no synchronize mode.\n");
printf("\t-l lessens the security (use twice for total insecure mode).\n");
printf("\t-r recursive mode, deletes all subdirectories.\n");
printf("\t-v is verbose mode.\n");
printf("\t-z last wipe writes zeros instead of random data.\n");
printf("\nsrm does a secure overwrite/rename/delete of the target file(s).\n");
printf("Default is secure mode (38 writes).\n");
printf("You can find updates at %s\n", WEB);
exit(1);
}
int smash_it(char *filename, int mode) {
struct stat filestat;
struct stat controlstat;
int i_am_a_directory = 0;
/* get the file stats */
if (lstat(filename, &filestat))
return 1;
if (S_ISREG(filestat.st_mode) && filestat.st_nlink > 1) {
fprintf(stderr, "Error: File %s - file is hardlinked %d time(s), skipping!\n", filename, filestat.st_nlink - 1);
return -1;
}
/* if the blocksize on the filesystem is bigger than the on compiled with, enlarge! */
if (filestat.st_blksize > bufsize) {
if (filestat.st_blksize > 65532) {
bufsize = 65535;
} else {
bufsize = (((filestat.st_blksize / 3) + 1) * 3);
}
}
/* handle the recursive mode */
if (recursive)
if (S_ISDIR(filestat.st_mode)) {
DIR *dir;
struct dirent *dir_entry;
struct stat cwd_stat;
char current_dir[4097];
int res;
int chdir_success = 1;
if (verbose) printf("DIRECTORY (going recursive now)\n");
getcwd(current_dir, 4096);
current_dir[4096] = '\0';
/* a won race will chmod a file to 0700 if the user is owner/root
I'll think about a secure solution to this, however, I think
there isn't one - anyone with an idea? */
if (chdir(filename)) {
(void) chmod(filename, 0700); /* ignore permission errors */
if (chdir(filename)) {
fprintf(stderr,"Can't chdir() to %s, hence I can't wipe it.\n", filename);
chdir_success = 0;
}
}
if (chdir_success) {
lstat(".", &controlstat);
lstat("..", &cwd_stat);
if ( (filestat.st_dev != controlstat.st_dev) || (filestat.st_ino != controlstat.st_ino) ) {
fprintf(stderr, "Race found! (directory %s became a link)\n", filename);
} else {
if ((dir = opendir (".")) != NULL) {
(void) chmod(".", 0700); /* ignore permission errors */
dir = opendir (".");
}
if (dir != NULL) {
while ((dir_entry = readdir(dir)) != NULL)
if (strcmp(dir_entry->d_name, ".") && strcmp(dir_entry->d_name, "..")) {
if (verbose) printf("Wiping %s ", dir_entry->d_name);
if ( (res = smash_it(dir_entry->d_name, mode)) > 0) {
if (res == 3)
fprintf(stderr,"File %s was raced, hence I won't wipe it.\n", dir_entry->d_name);
else {
fprintf(stderr,"Couldn't delete %s. ", dir_entry->d_name);
perror("");
}
} else
if (verbose) printf(" Done\n");
}
closedir(dir);
}
}
if(chdir(current_dir) != 0) {
fprintf(stderr, "Error: Can't chdir to %s (aborting) - ", current_dir);
perror("");
exit(1);
}
/*
lstat(current_dir, &controlstat);
if ( (cwd_stat.st_dev != controlstat.st_dev) || (cwd_stat.st_ino != controlstat.st_ino) ) {
fprintf(stderr, "Race found! (directory %s was exchanged or its your working directory)\n", current_dir);
exit(1);
}
*/
i_am_a_directory = 1;
}
}
/* end of recursive function */
if (S_ISREG(filestat.st_mode)) {
/* open the file for writing in sync. mode */
if ((fd = open(filename, O_RDWR | O_LARGEFILE | slow)) < 0) {
/* here again this has a race problem ... hmmm */
/* make it writable for us if possible */
(void) chmod(filename, 0600); /* ignore errors */
if ((fd = open(filename, O_RDWR | O_LARGEFILE | slow)) < 0)
return 1;
}
fstat(fd, &controlstat);
if ((filestat.st_dev != controlstat.st_dev) || (filestat.st_ino != controlstat.st_ino) || (! S_ISREG(controlstat.st_mode))) {
close(fd);
return 3;
}
if (sdel_overwrite(mode, fd, 0, bufsize, filestat.st_size > 0 ? filestat.st_size : 1, zero) == 0)
return sdel_unlink(filename, 0, 1, slow);
} /* end IS_REG() */
else {
if (S_ISDIR(filestat.st_mode)) {
if (i_am_a_directory == 0) {
fprintf(stderr,"Warning: %s is a directory. I will not remove it, because the -r option is missing!\n", filename);
return 0;
} else
return sdel_unlink(filename, 1, 0, slow);
} else
if (! S_ISDIR(filestat.st_mode)) {
fprintf(stderr,"Warning: %s is not a regular file, rename/unlink only!", filename);
if (! verbose)
printf("\n");
return sdel_unlink(filename, 0, 0, slow);
}
}
return 99; // not reached
}
void cleanup(int signo) {
fprintf(stderr,"Terminated by signal. Clean exit.\n");
if (fd >= 0)
close(fd);
FLUSH;
exit(1);
}
int main (int argc, char *argv[]) {
int errors = 0;
int dot = 0;
int result;
int secure = 2; /* Standard is now SECURE mode (38 overwrites) [since v2.0] */
int loop;
struct rlimit rlim;
prg = argv[0];
if (argc < 2 || strncmp(argv[1], "-h", 2) == 0|| strncmp(argv[1], "--h", 3) == 0)
help();
while (1) {
result = getopt(argc, argv, "DdFfLlRrSsVvZz");
if (result < 0) break;
switch (result) {
case 'd' :
case 'D' : dot = 1;
break;
case 'F' :
case 'f' : slow = 0;
break;
case 'L' :
case 'l' : if (secure) secure--;
break;
case 'R' :
case 'r' : recursive++;
break;
case 'S' :
case 's' : secure++;
break;
case 'V' :
case 'v' : verbose++;
break;
case 'Z' :
case 'z' : zero++;
break;
default : help();
}
}
loop = optind;
if (loop == argc)
help();
signal(SIGINT, cleanup);
signal(SIGTERM, cleanup);
signal(SIGHUP, cleanup);
sdel_init(slow);
if (verbose) {
char type[15] = "random";
if (zero) strcpy(type, "zero");
switch (secure) {
case 0 : printf("Wipe mode is insecure (one pass [%s])\n",type);
break;
case 1 : printf("Wipe mode is insecure (two passes [0xff/%s])\n",type);
break;
default: printf("Wipe mode is secure (38 special passes)\n");
}
}
#ifdef RLIM_INFINITY
#ifdef RLIMIT_FSIZE
rlim.rlim_cur = RLIM_INFINITY;
rlim.rlim_max = RLIM_INFINITY;
if (setrlimit(RLIMIT_FSIZE, &rlim) != 0)
fprintf(stderr, "Warning: Could not reset ulimit for filesize.\n");
#else
fprintf(stderr, "Warning: Not compiled with support for resetting ulimit filesize.\n");
#endif
#endif
while (loop < argc) {
char rmfile[strlen(argv[loop]) + 1];
strcpy(rmfile, argv[loop]);
loop++;
if (strcmp("/", rmfile) == 0) {
fprintf(stderr,"Warning: Do you really want to remove the ROOT directory??\n");
fprintf(stderr,"I'm giving you 5 seconds to abort ... press Control-C\n");
sleep(6);
fprintf(stderr,"Doing my evil work now, don't whimp later, you had been informed!\n");
}
if (dot)
if ((strcmp(".", rmfile) == 0) || (strcmp("..", rmfile) == 0))
continue;
if (verbose)
printf("Wiping %s ", rmfile);
result = (int) smash_it(rmfile, secure);
switch (result) {
case 0 : if (verbose) printf(" Done\n");
break;
case 1 : fprintf(stderr, "Error: File %s - ", rmfile);
perror("");
break;
case -1: break;
case 3 : fprintf(stderr, "File %s was raced, hence I won't wipe it!\n", rmfile);
break;
default: fprintf(stderr, "Unknown error\n");
}
if (result)
errors++;
}
sdel_finnish();
if (errors)
return 1;
else
return 0;
}