/* Secure RM - by van Hauser / [THC], vh@reptile.rug.ac.be
 *
 * 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 -s overwrites 2 times the data.
 * The option -s twice does a real security wipe for 39 times, flushing
 * the caches after every write. The wipe technique was proposed by Peter
 * Gutmann at Usenix '96 and includes 10 random overwrites plus 29 special
 * defined characters. Take a look at the paper of him, it's really worth
 * your time. 
 *
 * Advice : set "alias rm 'srm -ssv'"
 *
 * Read the manual for limitations.
 * Don't make this program suid/sgid !
 * Compiles clean on OpenBSD, Linux, Solaris and AIX
 *
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>

#define BLOCKSIZE 32768
#define DIR_SEPERATOR '/'
#define FLUSH sync();

char *VERSION = "v1.8 by van Hauser / THC  <vh@reptile.rug.ac.be>  http://r3wt.base.org";
char newname[512];
char rmfile[200];
char *prog;
FILE *f;
int file;
int verbose = 0;

void help () {
    printf("%s %s\n\n",prog, VERSION);
    printf("Syntax: %s [-ssv] file1 file2 etc.\n\n", prog);
    printf("%s does a secure overwrite/rename/delete of the target file(s).\n", prog);
    printf("Option -ss does real secure overwrite (39 times), -v is verbose mode.\n");
    exit(1);
}

int smash_it(char *filename, int mode) 
{
    unsigned char write_modes[27][3] = {
        {"\x55\x55\x55"}, {"\xaa\xaa\xaa"}, {"\x92\x49\x24"}, {"\x49\x24\x92"},
        {"\x24\x92\x49"}, {"\x00\x00\x00"}, {"\x11\x11\x11"}, {"\x22\x22\x22"},
        {"\x33\x33\x33"}, {"\x44\x44\x44"}, {"\x55\x55\x55"}, {"\x66\x66\x66"},
        {"\x77\x77\x77"}, {"\x88\x88\x88"}, {"\x99\x99\x99"}, {"\xaa\xaa\xaa"},
        {"\xbb\xbb\xbb"}, {"\xcc\xcc\xcc"}, {"\xdd\xdd\xdd"}, {"\xee\xee\xee"},
        {"\xff\xff\xff"}, {"\x92\x49\x24"}, {"\x49\x24\x92"}, {"\x24\x92\x49"},
        {"\x6d\xb6\xdb"}, {"\xb6\xdb\x6d"}, {"\xdb\x6d\xb6"}
    };

    unsigned char std_array[3] = "\xff\xff\xff";
    unsigned long writes;
    unsigned long counter;
    unsigned long filesize;
    struct stat filestat;
    int turn;
    int result;
    int i;
    unsigned char ch;

/* open the file, get the filesize, calculate the numbers of needed writings */
    if ((f = fopen(filename, "r+b")) == NULL)
        return 1;
    (void) stat(filename, &filestat);
    filesize = filestat.st_size;
    writes = (1 + (filesize / BLOCKSIZE)) * ((BLOCKSIZE+2)/3);
    file=fileno(f);

/* do the overwriting stuff */
    if (mode != 0) { 
        for (turn=0; turn<=35; turn++) {
            rewind(f); 
            if ((mode == 1) && (turn > 0)) break;
            if ((turn>=5) && (turn<=31)) {
               for (counter=1; counter<=writes; counter++) 
                    fwrite(&write_modes[turn-5][0], 1, 3, f);
	    } else {
	       for (counter=1; counter<=writes*3; counter++) {
 		  ch = (unsigned char) (256.0*rand()/(RAND_MAX+1.0));
	          fwrite(&ch, 1, 1, f);
	       }  
	    }
	    fflush(f);
            fsync(file);
            if (verbose)
                printf("*");
	}
	rewind(f);
    }

    for (counter=1; counter<=writes; counter++)
        fwrite(std_array, 1, 3, f);
    if (verbose)
        printf("*");
/* Soft Flush -> Write buffered data */
    fflush(f);
    fsync(file);
    (void) fclose(f);
/* Hard Flush -> Force cached data to be written to disk */
    FLUSH
/* open + truncating the file, so an attacker doesn't know the diskblocks */
   if ((file = open(filename, O_WRONLY | O_TRUNC)) >= 0)
	close(file);

/* Generate random unique name, renaming and deleting of the file */
    strncpy(newname, filename, sizeof(newname));
    turn = 0;

    do {
        for (i=strlen(newname)-1; (newname[i] != DIR_SEPERATOR)&&(i>=0); i--)
/*             if (newname[i] != '.') */ /* only for msdos systems */
                newname[i] = 97+(int) (27.0*rand()/(RAND_MAX+1.0));
        result = stat(newname, &filestat);
        turn++;
    } while ((result >= 0)&&(turn<=100));
    if (turn<=100) {
       result = rename(filename, newname);
       if (result !=0) {
          fprintf(stderr,"Warning: Couldn't rename %s. ", filename);
          perror("");
          strncpy(newname, filename, sizeof(newname));
       } else *filename = '\0';
    } else {
       fprintf(stderr,"Warning: Couldn't find a free filename for %s!\n",filename);
       strncpy(newname, filename, sizeof(newname));
    }
    result = unlink(newname);
    newname[0] = '\0';
    if (result != 0)
        return 2;
    return 0;
}

void cleanup(int signo) {
    fprintf(stderr,"Terminated by signal. Clean exit.\n");
    if (f) {
        fflush(f);
        fsync(file);
        (void) fclose(f);
    }
    if (file >= 0)
    	close(file);
    if (newname[0] != '\0')
        if (unlink(newname) != 0 )
            fprintf (stderr,"Warning: Can't remove %s!\n",newname);
    if (rmfile[0] != '\0')
	fprintf (stderr,"Warning: I won't remove %s, because the secure deletion wasn't completed!\n",rmfile);
    FLUSH
    fflush(stdout);
    fflush(stderr);
    exit(1);
}

int main (int argc, char *argv[])
{
    int errors = 0;
    int result;
    int secure = 0;
    int loop;
    prog = argv[0];
    if (argc < 2)
        help();
    while (1) {
        result = getopt(argc, argv, "SsVv");
        if (result<0) break;
        switch (result) {
	    case 'S' :
            case 's' : secure++;
            	       break;
	    case 'V' :
            case 'v' : verbose++;
            	       break;
            default :  help();       
        }
    }
    loop = optind;
    if (loop == argc)
        help();
    (void) setvbuf(stdout, NULL, _IONBF, 0);
    srand(getpid()+getuid());

    signal(SIGINT, cleanup);
    signal(SIGTERM, cleanup);
    signal(SIGHUP, cleanup);

    while (loop < argc) {
        strncpy(rmfile, argv[loop], sizeof(rmfile));
        if (verbose) printf("Deleting %s ", rmfile);
        result = (int) smash_it(rmfile, secure);
        switch (result) {
            case 0 : if (verbose) printf(" Done\n");
                     break;
            case 1 : perror(rmfile);
            	     break;
            case 2 : fprintf(stderr,"Couldn't delete %s. ", rmfile);
            	     perror("");
                     break;
            default: fprintf(stderr,"Unknown error\n");
        }
        loop++;
        if (result)
            errors++;
    }
    if (errors)
        return 1;
    else
        return 0;
}
