1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
|
/* Secure Delete Library - by van Hauser / [THC], vh@thc.org
*
* Secure Delete Library provides the following public functions:
*
* void sdel_init(int secure_random)
* Initializiation function for sdel_overwrite. It needs to be called
* once at program start, not for each file to be overwritten.
* Options:
* secure_random - if != 0 defines that the secure random number
* generator RANDOM_DEVICE should be used
*
* void sdel_finnish()
* Clean-up function, if sdel_init() was called in a program. It needs
* only to be called at the end of the program.
*
* int sdel_overwrite(int mode, int fd, long start, unsigned long bufsize,
* unsigned long length, int zero)
* This is the heart of sdel-lib. It overwrites the target file
* descriptor securely to make life hard even for the NSA.
* Read the next paragraph for the techniques.
* Options:
* mode = 0 - once overwrite with random data
* 1 - once overwrite with 0xff, then once with random data
* 2 - overwrite 38 times with special values
* fd - filedescriptor of the target to overwrite
* start - where to start overwriting. 0 is from the beginning
* this is needed for wiping swap spaces etc.
* bufsize - size of the buffer to use for overwriting, depends
* on the filesystem
* length - amount of data to write (file size), 0 means until
* an error occurs
* zero - last wipe is zero bytes, not random
* returns 0 on success, -1 on errors
*
* int sdel_unlink(char *filename, int directory, int truncate, int slow)
* First truncates the file (if it is not a directory), then renames it
* and finally rmdir/unlinks the target.
* Options:
* filename - filename/directory to unlink/rmdir
* directory - if != 0, it is a directory
* truncate - if != 0, it truncates the file
* slow - is either O_SYNC (see open(2)) or 0
* returns 0 on success, -1 on errors
*
* Compiles clean on OpenBSD, Linux, Solaris, AIX and I guess all others.
*
*/
/*
* For security reasons full 32kb blocks are written so that the whole block
* on which the file(s) live are overwritten. (change #define #BLOCKSIZE)
* Standard mode is a real security wipe for 38 times, flushing
* the caches after every write. The wipe technique was proposed by Peter
* Gutmann at Usenix '96 and includes 10 random overwrites plus 28 special
* defined characters. Take a look at the paper of him, it's really worth
* your time.
*
* Read the manual for limitations.
*/
#include "sdel-lib.h"
//
// STARTING FUNCTIONS
//
void __sdel_fill_buf(char pattern[3], unsigned long bufsize, char *buf) {
int loop;
int where;
for (loop = 0; loop < (bufsize / 3); loop++) {
where = loop * 3;
*buf++ = pattern[0];
*buf++ = pattern[1];
*buf++ = pattern[2];
}
}
void __sdel_random_buf(unsigned long bufsize, char *buf) {
int loop;
if (devrandom == NULL)
for (loop = 0; loop < bufsize; loop++)
*buf++ = (unsigned char) (256.0*rand()/(RAND_MAX+1.0));
else
fread(buf, bufsize, 1, devrandom);
}
void __sdel_random_filename(char *filename) {
int i;
for (i = strlen(filename) - 1;
(filename[i] != DIR_SEPERATOR) && (i >= 0);
i--)
if (filename[i] != '.') /* keep dots in the filename */
filename[i] = 97+(int) ((int) ((256.0 * rand()) / (RAND_MAX + 1.0)) % 26);
}
void sdel_init(int secure_random) {
(void) setvbuf(stdout, NULL, _IONBF, 0);
(void) setvbuf(stderr, NULL, _IONBF, 0);
if (BLOCKSIZE<16384)
fprintf(stderr, "Programming Warning: in-compiled blocksize is <16k !\n");
if (BLOCKSIZE % 3 > 0)
fprintf(stderr, "Programming Error: in-compiled blocksize is not a multiple of 3!\n");
srand( (getpid()+getuid()+getgid()) ^ time(0) );
devrandom = NULL;
#ifdef RANDOM_DEVICE
if (secure_random) {
if ((devrandom = fopen(RANDOM_DEVICE, "r")) != NULL)
if (verbose)
printf("Using %s for random input.\n", RANDOM_DEVICE);
}
#endif
__internal_sdel_init = 1;
}
void sdel_finnish() {
if (devrandom != NULL) {
fclose(devrandom);
devrandom = NULL;
}
if (! __internal_sdel_init) {
fprintf(stderr, "Programming Error: sdel-lib was not initialized before calling sdel_finnish().\n");
return;
}
__internal_sdel_init = 0;
}
void sdel_verbose(int turnv, int termwidthv, double counterv, double writesv){
double progress=(counterv/writesv)*1.0;
if (progress>0.0 && progress<=1.0) {
int progpos=termwidthv*progress;
if (turnv<10)
printf("pass:%i [",turnv);
else
printf("pass:%i[",turnv);
for (int progi=0; progi<termwidthv; progi++) {
if (progi<progpos)
printf("=");
else if (progi==progpos)
printf(">");
else
printf(" ");
}
int progout=(int)floor(progress*100.0);
if (progout<10)
printf("]%i%% \r", progout);
else if (progout<100)
printf("]%i%% \r", progout);
else
printf("]%i%%\r", progout);
fflush(stdout);
}
}
/*
* secure_overwrite function parameters:
* mode = 0 : once overwrite with random data
* 1 : once overwrite with 0xff, then once with random data
* 2 : overwrite 38 times with special values
* fd : filedescriptor of the target to overwrite
* start : where to start overwriting. 0 is from the beginning
* bufsize : size of the buffer to use for overwriting, depends on the filesystem
* length : amount of data to write (file size), 0 means until an error occurs
*
* returns 0 on success, -1 on errors
*/
int sdel_overwrite(int mode, int fd, long start, unsigned long bufsize, unsigned long length, int zero) {
unsigned long writes;
unsigned long counter;
int turn;
int termwidth = 50;
int last = 0;
char buf[65535];
FILE *f;
if (! __internal_sdel_init)
fprintf(stderr, "Programming Error: sdel-lib was not initialized before sdel_overwrite().\n");
if ((f = fdopen(fd, "r+b")) == NULL)
return -1;
/* calculate the number of writes */
if (length > 0)
writes = (1 + (length / bufsize));
else
writes = 0;
/* do the first overwrite */
if (verbose)
printf("\n");
if (start == 0)
rewind(f);
else
if (fseek(f, start, SEEK_SET) != 0)
return -1;
if (mode != 0 || zero) {
if (mode == 0)
__sdel_fill_buf(std_array_00, bufsize, buf);
else
__sdel_fill_buf(std_array_ff, bufsize, buf);
if (writes > 0)
for (counter=1; counter<=writes; counter++){
fwrite(&buf, 1, bufsize, f); // dont care for errors
if (verbose)
sdel_verbose(0,termwidth,(double)counter,(double)writes);
}
else
do {} while(fwrite(&buf, 1, bufsize, f) == bufsize);
fflush(f);
if (fsync(fd) < 0)
FLUSH;
if (mode == 0)
return 0;
}
/* do the rest of the overwriting stuff */
for (turn = 0; turn <= 36; turn++) {
if (start == 0)
rewind(f);
else
if (fseek(f, start, SEEK_SET) != 0)
return -1;
if ((mode < 2) && (turn > 0))
break;
if ((turn >= 5) && (turn <= 31)) {
__sdel_fill_buf(write_modes[turn-5], bufsize, buf);
if (writes > 0)
for (counter = 1; counter <= writes; counter++){
fwrite(&buf, 1, bufsize, f); // dont care for errors
if (verbose)
sdel_verbose((turn+1),termwidth,(double)counter,(double)writes);
}
else
do {} while(fwrite(&buf, 1, bufsize, f) == bufsize);
} else {
if (zero && ((mode == 2 && turn == 36) || mode == 1)) {
last = 1;
__sdel_fill_buf(std_array_00, bufsize, buf);
}
if (writes > 0) {
for (counter = 1; counter <= writes; counter++) {
if (! last)
__sdel_random_buf(bufsize, buf);
fwrite(&buf, 1, bufsize, f); // dont care for errors
if (verbose)
sdel_verbose((turn+1),termwidth,(double)counter,(double)writes);
}
} else {
do {
if (! last)
__sdel_random_buf(bufsize, buf);
} while (fwrite(&buf, 1, bufsize, f) == bufsize); // dont care for errors
}
}
fflush(f);
if (fsync(fd) < 0)
FLUSH;
}
(void) fclose(f);
/* Hard Flush -> Force cached data to be written to disk */
FLUSH;
if (verbose)
printf("\n");
return 0;
}
/*
* secure_unlink function parameters:
* filename : the file or directory to remove
* directory : defines if the filename poses a directory
* truncate : truncate file
* slow : do things slowly, to prevent caching
*
* returns 0 on success, -1 on errors.
*/
int sdel_unlink(char *filename, int directory, int truncate, int slow) {
int fd;
int turn = 0;
int result;
char newname[strlen(filename) + 1];
struct stat filestat;
/* open + truncating the file, so an attacker doesn't know the diskblocks */
if (! directory && truncate)
if ((fd = open(filename, O_WRONLY | O_TRUNC | slow)) >= 0)
close(fd);
/* Generate random unique name, renaming and deleting of the file */
strcpy(newname, filename); // not a buffer overflow as it has got the exact length
do {
__sdel_random_filename(newname);
if ((result = lstat(newname, &filestat)) >= 0)
turn++;
} while ((result >= 0) && (turn <= 100));
if (turn <= 100) {
result = rename(filename, newname);
if (result != 0) {
fprintf(stderr, "Warning: Couldn't rename %s - ", filename);
perror("");
strcpy(newname, filename);
}
} else {
fprintf(stderr,"Warning: Couldn't find a free filename for %s!\n",filename);
strcpy(newname, filename);
}
if (directory) {
result = rmdir(newname);
if (result) {
printf("Warning: Unable to remove directory %s - ", filename);
perror("");
(void) rename(newname, filename);
} else
if (verbose)
printf("Removed directory %s ...", filename);
} else {
result = unlink(newname);
if (result) {
printf("Warning: Unable to unlink file %s - ", filename);
perror("");
(void) rename(newname, filename);
} else
if (verbose)
printf(" Removed file %s ...", filename);
}
if (result != 0)
return -1;
return 0;
}
void sdel_wipe_inodes(char *loc, char **array) {
char *template = malloc(strlen(loc) + 16);
int i = 0;
int fail = 0;
int fd;
if (verbose)
printf("Wiping inodes ...");
array = malloc(MAXINODEWIPE * sizeof(template));
strcpy(template, loc);
if (loc[strlen(loc) - 1] != '/')
strcat(template, "/");
strcat(template, "xxxxxxxx.xxx");
while(i < MAXINODEWIPE && fail < 5) {
__sdel_random_filename(template);
if (open(template, O_CREAT | O_EXCL | O_WRONLY, 0600) < 0)
fail++;
else {
array[i] = malloc(strlen(template));
strcpy(array[i], template);
i++;
}
}
FLUSH;
if (fail < 5) {
fprintf(stderr, "Warning: could not wipe all inodes!\n");
}
array[i] = NULL;
fd = 0;
while(fd < i) {
unlink(array[fd]);
free(array[fd]);
fd++;
}
free(array);
array = NULL;
FLUSH;
if (verbose)
printf(" Done ... ");
}
|