/* Secure SWAP cleaner - by van Hauser / [THC], vh@reptile.rug.ac.be
 *
 * Note that this program is beta. It was tested with linux, but I can't
 * tell for other platforms. Read the statement at #define SWAP_PAGESIZE
 * on how to use this program on other unix machines.
 *
 * of course: you have to turn of the swapspace before using this program !
 *
 * Secure SWAP overwrites all data on your swap device.
 * Without any -s option, this is done only once.
 * The option -s does 2 overwritings.
 * 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 : use always the options -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>

/* SWAP_PAGESIZE is an important variable. You have to set this
 * to your header length of your swapdevice. For Linux this is 4096,
 * I don't know for the other OSs. To be sure, set this to 0 and
 * recreate your swapspace afterwards (for linux: mkswap /dev/swapdevice)
 */
#define SWAP_PAGESIZE 4096
#define FLUSH sync();

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

void help() {
    printf("%s %s\n\n",prog, VERSION);
    printf("Syntax: %s [-ssv] /dev/of_swap_device\n\n", prog);
    printf("%s does a secure overwrite of the swap space\n", prog);
    printf("Option -ss does real secure overwrite (39 times), -v is verbose mode.\n");
    printf("You must disable the swapspace before using this program! *beta*\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";
    int turn;
    unsigned char ch;
    struct stat stats;

    if ((file = fopen (filename, "r+")) == 0) {
        fprintf(stderr, "can't open %s for writing. ",filename);
        perror("");
        exit(1);
    }
    stat(filename, &stats);
    if (!S_ISBLK(stats.st_mode)) {
        fprintf(stderr, "not a block device : %s\n", filename);
        exit(1);
    }

    if (verbose)
        printf("Writing to swap device %s: ", filename);

    if (mode != 0) {
        for (turn=0; turn<=35; turn++) {
            if (fseek(file, SWAP_PAGESIZE, SEEK_SET) < 0) {
                fprintf(stderr,"can't seek to offset %d on %s\n",SWAP_PAGESIZE,filename);
                exit(1);
            }
            if ((mode == 1) && (turn > 0)) break;
            if ((turn>=5) && (turn<=31))
                do {} while (fwrite(&write_modes[turn-5][0], 1, 3, file) == 3);
            else {
                do {
                    ch = (unsigned char) (256.0*rand()/(RAND_MAX+1.0));
                } while (fwrite(&ch, 1, 1, file) == 1);
    	    }
    	    FLUSH
    	    if (verbose)
    	        printf("*");
    	}
    }
    
    if (fseek(file, SWAP_PAGESIZE, SEEK_SET) < 0) {
        fprintf(stderr,"can't seek to offset %d on %s\n",SWAP_PAGESIZE,filename);
	exit(1);
    }
    
    do {} while (fwrite(&std_array, 1, 3, file) == 3);

    if (verbose)
        printf("*");

    fflush(file);
    fclose(file);
    FLUSH
    
    if (verbose)
	printf(" done\n");
    
    return 0;
}

void cleanup() {
    fprintf(stderr,"Terminated by signal. Clean exit.\n");
    if (file) {
        fflush(file);
        FLUSH
        fclose(file);
    }
    FLUSH
    fflush(stdout);
    fflush(stderr);
    exit(1);
}

int main (int argc, char *argv[]) {
    int secure = 0;
    int result;
    
    prog = argv[0];
    if (argc==1)
        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();
        }
    }

    if ((optind+1) != argc)
        help();
    (void) setvbuf(stdout, NULL, _IONBF, 0);
    signal(SIGINT, cleanup);
    signal(SIGTERM, cleanup);
    signal(SIGHUP, cleanup);

    smash_it(argv[optind], secure);
    /* thats all */
    exit(0);
}
