/[winpt]/trunk/PTD/wptWipeFile.cpp
ViewVC logotype

Contents of /trunk/PTD/wptWipeFile.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 273 - (show annotations)
Fri Dec 8 10:22:17 2006 UTC (18 years, 2 months ago) by twoaday
File size: 14851 byte(s)


1 /* wptWipeFile.cpp - Secure file removal
2 * Copyright (C) 2001-2006 Timo Schulz
3 * Copyright (C) 2000 Matt Gauthier
4 *
5 * (This code based on the sunlink.c file from the SRM project)
6 *
7 * WinPT software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * WinPT is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with WinPT; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20 */
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <windows.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <sys/stat.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <direct.h>
32
33 #include "wptW32API.h"
34 #include "wptErrors.h"
35 #include "wptTypes.h"
36
37 /* WARNING: Because modern file systems, for example NTFS, use a lot of
38 features to provide better performance and journaling to recover
39 the FS after a crash, secure file deleting is extremly
40 difficult. It can happen that parts of the file are still in
41 plaintext after wiping the file. To avoid information leakage,
42 it is recommend to wipe the free space of the disk to destroy
43 left over pattern. */
44
45 #ifdef _MSC_VER
46 typedef unsigned __int64 DDWORD;
47 #else
48 typedef unsigned long long DDWORD;
49 #endif
50
51 typedef struct {
52 HANDLE fd; /* file descriptor. */
53 DDWORD filesize; /* file size of the file. */
54 DDWORD offset;
55 BYTE *buffer;
56 DWORD buffsize;
57 const char *name;
58 int n_passes;
59 } wipe_context_s;
60
61 #define SBOX_SIZE 256
62 struct arcfour_s {
63 BYTE *seed;
64 int pos;
65 BYTE sbox[SBOX_SIZE];
66 DWORD i;
67 DWORD j;
68 };
69
70
71 void (*progress_cb) (void *, DDWORD, DDWORD);
72 static void *progress_cb_value = NULL;
73
74 void (*unlink_cb)(void *, const char *, int, int, int) = NULL;
75 static void *unlink_cb_value = NULL;
76
77 static int init_done = 0;
78 static struct arcfour_s rnd;
79
80
81 /* Add the random from buffer @buf with the length @buf_len
82 into the seed @seed. The seed length (256) used to
83 emulate a circular movment. */
84 static void
85 add_random (BYTE *seed, int *seed_pos, BYTE *buf, int buf_len)
86 {
87 int i, s_pos = *seed_pos;
88
89 for (i=0; i < buf_len; i++)
90 seed[(s_pos++ % SBOX_SIZE)] ^= buf[i];
91 *seed_pos = s_pos % SBOX_SIZE;
92 }
93
94
95 /* Enumerate all child windows. Store their text, their thread ID,
96 their placement and their dimension. */
97 static BOOL CALLBACK
98 child_proc (HWND h, LPARAM l)
99 {
100 struct arcfour_s *cb = (struct arcfour_s*)l;
101 DWORD a = (DWORD)h;
102 WINDOWPLACEMENT p;
103 RECT r;
104 char buf[256];
105 int n;
106
107 n = GetWindowText (h, buf, DIM (buf)-1);
108 if (n > 0)
109 add_random (cb->seed, &cb->pos, (BYTE*)buf, n);
110 add_random (cb->seed, &cb->pos, (BYTE*)&a, 4);
111 a = GetWindowThreadProcessId (h, NULL);
112 add_random (cb->seed, &cb->pos, (BYTE*)&a, 4);
113 GetWindowPlacement (h, &p);
114 add_random (cb->seed, &cb->pos, (BYTE*)&p, sizeof (p));
115 GetWindowRect (h, &r);
116 add_random (cb->seed, &cb->pos, (BYTE*)&r, sizeof (r));
117 return TRUE;
118 }
119
120
121 /* Initialize the seed with all kind of system variables. */
122 static void
123 init_random (unsigned char *seed)
124 {
125 int pos=0;
126 DWORD buf[16];
127 int i=0;
128
129 buf[i++] = (DWORD)GetActiveWindow ();
130 buf[i++] = (DWORD)GetCapture ();
131 buf[i++] = (DWORD)GetClipboardOwner ();
132 buf[i++] = (DWORD)GetClipboardViewer ();
133 buf[i++] = (DWORD)GetCurrentProcess ();
134 buf[i++] = (DWORD)GetCurrentProcessId ();
135 buf[i++] = (DWORD)GetCurrentThread ();
136 buf[i++] = (DWORD)GetDesktopWindow ();
137 buf[i++] = (DWORD)GetFocus ();
138 buf[i++] = (DWORD)GetMessagePos ();
139 buf[i++] = (DWORD)GetOpenClipboardWindow ();
140 buf[i++] = (DWORD)GetProcessHeap ();
141 buf[i++] = (DWORD)GetProcessWindowStation ();
142 buf[i++] = (DWORD)GetQueueStatus (QS_ALLEVENTS);
143 buf[i] = (DWORD)GetTickCount ();
144 add_random (seed, &pos, (BYTE*)buf, 4*i);
145
146 {
147 POINT p;
148 GetCursorPos (&p);
149 add_random (seed, &pos, (BYTE*)&p, sizeof (p));
150 GetCaretPos (&p);
151 add_random (seed, &pos, (BYTE*)&p, sizeof (p));
152 }
153
154 {
155 STARTUPINFO inf;
156 inf.cb = sizeof (inf);
157 GetStartupInfo (&inf);
158 add_random (seed, &pos, (BYTE*)&inf, sizeof (inf));
159 }
160
161 {
162 MEMORYSTATUS st;
163
164 st.dwLength = sizeof (st);
165 GlobalMemoryStatus (&st);
166 add_random (seed, &pos, (BYTE*)&st, sizeof (st));
167 }
168
169 {
170 LARGE_INTEGER in;
171
172 QueryPerformanceFrequency (&in);
173 add_random (seed, &pos, (BYTE*)&in, sizeof (in));
174 QueryPerformanceCounter (&in);
175 add_random (seed, &pos, (BYTE*)&in, sizeof (in));
176 }
177 {
178 rnd.seed = seed;
179 rnd.pos = pos;
180 EnumChildWindows (GetDesktopWindow (), child_proc, (LPARAM)&rnd);
181 }
182
183 }
184
185
186 /* Initialize cipher with the seed as the key. */
187 static void
188 init_arcfour (struct arcfour_s *ctx, BYTE *key)
189 {
190 BYTE t;
191
192 ctx->i = 0;
193 ctx->j = 0;
194 for (ctx->i=0; ctx->i < SBOX_SIZE; ctx->i++)
195 ctx->sbox[ctx->i] = (BYTE)ctx->i;
196 for (ctx->i=0; ctx->i < SBOX_SIZE; ctx->i++) {
197 ctx->j += (ctx->j+ctx->sbox[ctx->i]+key[ctx->i]);
198 ctx->j &= 255;
199 t = ctx->sbox[ctx->i];
200 ctx->sbox[ctx->i] = ctx->sbox[ctx->j];
201 ctx->sbox[ctx->j] = t;
202 }
203 }
204
205
206 /* Generate a single random byte. If the cipher is not
207 init, do an init first. */
208 static BYTE
209 rnd_byte (void)
210 {
211 struct arcfour_s *ctx = &rnd;
212 BYTE t;
213
214 if (!init_done) {
215 BYTE buf[SBOX_SIZE];
216
217 init_random (buf);
218 init_arcfour (ctx, buf);
219 init_done = 1;
220 }
221
222 ctx->i++; ctx->i &= 255;
223 ctx->j += ctx->sbox[ctx->i]; ctx->j &= 255;
224 t = ctx->sbox[ctx->i];
225 ctx->sbox[ctx->i] = ctx->sbox[ctx->j];
226 ctx->sbox[ctx->j] = t;
227 return ctx->sbox[(ctx->sbox[ctx->i] + ctx->sbox[ctx->j]) & 255];
228 }
229
230
231 /* Generate a single alpha-num charactor. */
232 static char
233 random_char (void)
234 {
235 BYTE c = 0;
236
237 while (!isalnum ((int)c))
238 c = rnd_byte ();
239 return c % 127;
240 }
241
242
243 /* Use the file handle in the context to overwrite a file
244 with prepared buffer contents. */
245 static void
246 overwrite (wipe_context_s *ctx)
247 {
248 DDWORD blocks = 0, mod = 0;
249 DWORD nwritten = 0;
250 LONG size_high = 0;
251
252 blocks = ctx->filesize / ctx->buffsize;
253 mod = ctx->filesize % ctx->buffsize;
254 SetFilePointer (ctx->fd, 0, &size_high, FILE_BEGIN);
255 while (blocks--) {
256 if (!WriteFile (ctx->fd, ctx->buffer, ctx->buffsize, &nwritten, NULL))
257 break;
258 ctx->offset += nwritten;
259 if (unlink_cb)
260 unlink_cb (unlink_cb_value, ctx->name, 0, (unsigned)ctx->offset,
261 (unsigned)ctx->filesize*ctx->n_passes);
262 }
263 if (mod) {
264 WriteFile (ctx->fd, ctx->buffer, (DWORD)mod, &nwritten, NULL);
265 ctx->offset += nwritten;
266 if (unlink_cb)
267 unlink_cb (unlink_cb_value, ctx->name, 0, (unsigned)ctx->offset,
268 (unsigned)ctx->filesize*ctx->n_passes);
269 }
270 FlushFileBuffers (ctx->fd);
271 SetFilePointer (ctx->fd, 0, &size_high, FILE_BEGIN);
272 }
273
274
275 /* fill the buffer with random of the given level. */
276 static void
277 randomize_buffer (BYTE *buf, size_t bufsize)
278 {
279 const int blocksize = 512;
280 int blocks = bufsize / blocksize;
281 int mod = bufsize % blocksize;
282 int i;
283
284 while (blocks--) {
285 for (i=0; i < blocksize; i++)
286 buf[i] = rnd_byte ();
287 buf += blocksize;
288 }
289 for (i=0; i < mod; i++)
290 buf[i] = rnd_byte ();
291 }
292
293
294 /* performs a random overwrite. */
295 static void
296 overwrite_random (int npasses, wipe_context_s * ctx)
297 {
298 int i;
299
300 for (i = 0; i < npasses; i++) {
301 randomize_buffer (ctx->buffer, ctx->buffsize);
302 overwrite (ctx);
303 }
304 }
305
306
307 /* perform an overwrite with a specific byte (like 0x00). */
308 static void
309 overwrite_byte (int byte, wipe_context_s * ctx)
310 {
311 memset (ctx->buffer, byte, ctx->buffsize);
312 overwrite (ctx);
313 }
314
315
316 /* perform an overwrite with a specific byte triple (like 0x00, 0xFF, 0xAA). */
317 static void
318 overwrite_bytes (int byte1, int byte2, int byte3, wipe_context_s * ctx)
319 {
320 DWORD i;
321
322 memset (ctx->buffer, byte1, ctx->buffsize);
323 for (i = 1; i < ctx->buffsize; i += 3) {
324 ctx->buffer[i] = byte2;
325 ctx->buffer[i+1] = byte3;
326 }
327 overwrite (ctx);
328 }
329
330
331 /* For the case the file is not a regular file (this is true for
332 devices or directories) this function tries to rename the file
333 to random pattern and then it will be delete (without random!). */
334 extern "C" int
335 rename_unlink (const char *path)
336 {
337 struct stat statbuf;
338 char *new_name = NULL, *p = NULL, c;
339 int i = 0, rc = 0;
340 int is_dir = 0;
341
342 if (GetFileAttributes (path) & FILE_ATTRIBUTE_DIRECTORY)
343 is_dir = 1;
344
345 new_name = new char[strlen (path)+15];
346 if (!new_name)
347 BUG (0);
348
349 strcpy (new_name, path);
350 p = strrchr (new_name, '\\');
351 if (p != NULL) {
352 p++;
353 *p = '\0';
354 }
355 else
356 p = new_name;
357 do {
358 while (i < 14) {
359 c = random_char ();
360 *p = c;
361 p++;
362 i++;
363 }
364 *p = '\0';
365 } while (stat (new_name, &statbuf) == 0);
366
367 if (rename (path, new_name) == -1) {
368 rc = WPTERR_FILE_READ;
369 goto leave;
370 }
371 if (is_dir && RemoveDirectory (new_name) == FALSE)
372 rc = WPTERR_FILE_REMOVE;
373 else if (!DeleteFile (new_name))
374 rc = WPTERR_FILE_REMOVE;
375
376 leave:
377 free_if_alloc (new_name);
378 return rc;
379 }
380
381
382 /* return the filesize as an 64-bit integer. */
383 static DDWORD
384 GetFileSize64 (const char * path)
385 {
386 FILE *fp = fopen (path, "r");
387 if (fp) {
388 struct _stati64 statbuf;
389 if (_fstati64 (fileno (fp), &statbuf) == -1)
390 return (DDWORD)-1;
391 fclose (fp);
392 return statbuf.st_size;
393 }
394 return (DDWORD)-1;
395 }
396
397
398 static int
399 _secure_unlink (const char *path, const int mode,
400 const int passes, HANDLE *r_fd)
401 {
402 wipe_context_s ctx;
403 LONG size_high = 0;
404
405 if (GetFileAttributes (path) & FILE_ATTRIBUTE_DIRECTORY)
406 return rename_unlink (path);
407
408 memset (&ctx, 0, sizeof (ctx));
409 ctx.name = path;
410 ctx.buffsize = 16384;
411 ctx.buffer = new BYTE[ctx.buffsize];
412 if (!ctx.buffer)
413 BUG (NULL);
414
415 ctx.filesize = GetFileSize64 (path);
416 if (!ctx.filesize) {
417 free_if_alloc (ctx.buffer);
418 remove (path);
419 return 0;
420 }
421
422 ctx.fd = CreateFile (path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
423 OPEN_ALWAYS, 0, NULL);
424 if (ctx.fd == INVALID_HANDLE_VALUE) {
425 free_if_alloc (ctx.buffer);
426 return WPTERR_FILE_CREAT;
427 }
428 else if (r_fd)
429 *r_fd = ctx.fd;
430
431 if (unlink_cb)
432 unlink_cb (unlink_cb_value, ctx.name, 0, 0, 0);
433
434 switch (mode) {
435 case WIPE_MODE_PASSES:
436 ctx.n_passes = passes;
437 overwrite_random (passes, &ctx);
438 break;
439
440 case WIPE_MODE_FAST:
441 ctx.n_passes = 1;
442 overwrite_random (1, &ctx);
443 break;
444
445 case WIPE_MODE_SIMPLE:
446 ctx.n_passes = 2;
447 overwrite_random (2, &ctx);
448 break;
449
450 case WIPE_MODE_DOD:
451 ctx.n_passes = 5;
452 overwrite_random (1, &ctx);
453 overwrite_byte ((~1) & 0xFF, &ctx);
454 overwrite_random (1, &ctx);
455 overwrite_byte ((~4) & 0xFF, &ctx);
456 overwrite_random (1, &ctx);
457 break;
458
459 case WIPE_MODE_GUTMANN:
460 ctx.n_passes = 39;
461 overwrite_random (4, &ctx);
462 overwrite_byte( 0x55, &ctx );
463 overwrite_byte ( 0xAA, &ctx );
464 overwrite_bytes( 0x92, 0x49, 0x24, &ctx );
465 overwrite_bytes( 0x49, 0x24, 0x92, &ctx );
466 overwrite_bytes( 0x24, 0x92, 0x49, &ctx );
467 overwrite_byte( 0x00, &ctx );
468 overwrite_byte( 0x11, &ctx );
469 overwrite_byte( 0x22, &ctx );
470 overwrite_byte( 0x33, &ctx );
471 overwrite_byte( 0x44, &ctx );
472 overwrite_byte( 0x55, &ctx );
473 overwrite_byte( 0x66, &ctx );
474 overwrite_byte( 0x77, &ctx );
475 overwrite_byte( 0x88, &ctx );
476 overwrite_byte( 0x99, &ctx );
477 overwrite_byte( 0xAA, &ctx );
478 overwrite_byte( 0xBB, &ctx );
479 overwrite_byte( 0xCC, &ctx );
480 overwrite_byte( 0xDD, &ctx );
481 overwrite_byte( 0xEE, &ctx );
482 overwrite_byte( 0xFF, &ctx );
483 overwrite_bytes( 0x92, 0x49, 0x24, &ctx );
484 overwrite_bytes( 0x49, 0x24, 0x92, &ctx );
485 overwrite_bytes( 0x24, 0x92, 0x49, &ctx );
486 overwrite_bytes( 0x6D, 0xB6, 0xDB, &ctx );
487 overwrite_bytes( 0xB6, 0xDB, 0x6D, &ctx );
488 overwrite_bytes( 0xDB, 0x6D, 0xB6, &ctx );
489 overwrite_random( 4, &ctx );
490 break;
491 }
492
493 /* Set file length to zero so allocated clusters cannot be trailed */
494 SetFilePointer (ctx.fd, 0, &size_high, FILE_BEGIN);
495 SetEndOfFile (ctx.fd);
496 CloseHandle (ctx.fd);
497
498 wipememory (ctx.buffer, ctx.buffsize); /* burn the last evidence */
499 free_if_alloc (ctx.buffer);
500
501 return rename_unlink (path);
502 }
503
504
505 /* Try to delete a file in a secure way with the given mode @mode. */
506 extern "C" int
507 secure_unlink (const char *path, const int mode, const int passes)
508 {
509 /* If the file has one of the following attributes, the
510 chance the file really gets overwritten is very low so
511 we just to an unlink to spare time and entropy. */
512 DWORD attr = GetFileAttributes (path);
513 if ((attr & FILE_ATTRIBUTE_COMPRESSED) ||
514 (attr & FILE_ATTRIBUTE_ENCRYPTED) ||
515 (attr & FILE_ATTRIBUTE_SPARSE_FILE))
516 return DeleteFile (path); /* XXX */
517 return _secure_unlink (path, mode, passes, NULL);
518 }
519
520
521 /* Set the callback @cb for the wipe function. The callback is call every time
522 the wipe function writes data to the file. */
523 extern "C" void
524 secure_unlink_set_cb (void (*cb)(void *, const char *, int, int, int),
525 void *cb_value)
526 {
527 unlink_cb = cb;
528 unlink_cb_value = cb_value;
529 }
530
531
532 /* wipe all free space of the given drive by creating a temp file
533 which has the size of the free space. This file will be then
534 overwritten with random and static pattern. */
535 extern "C" int
536 wipe_freespace (const char *drive, HANDLE *r_fd,
537 void (*cb)(void *, DDWORD, DDWORD), void * cb_value)
538 {
539 ULARGE_INTEGER caller, total, frees;
540 LONG hpart=0;
541 HANDLE fd;
542 const char *fmt;
543 char *file;
544 int disktyp = GetDriveType (drive);
545 int rc;
546
547 /* XXX: needs to be modernized or better extracted to a separate
548 program module. */
549 if (disktyp != DRIVE_FIXED && disktyp != DRIVE_REMOVABLE)
550 return -1;
551 if (!GetDiskFreeSpaceEx (drive, &caller, &total, &frees))
552 return -1;
553
554 /* disk is full */
555 if (!frees.LowPart)
556 return 0;
557 fmt = "%stemp_winpt.tmp";
558 file = new char[strlen (drive)+strlen (fmt)+8+1];
559 if (!file)
560 BUG (0);
561 sprintf (file, fmt, drive);
562 fd = CreateFile (file,
563 GENERIC_READ|GENERIC_WRITE,
564 FILE_SHARE_READ|FILE_SHARE_WRITE,
565 NULL, CREATE_ALWAYS, 0, NULL);
566 if (fd == INVALID_HANDLE_VALUE) {
567 free_if_alloc (file);
568 return WPTERR_FILE_CREAT;
569 }
570 hpart = frees.HighPart;
571 SetFilePointer (fd, frees.LowPart, &hpart, FILE_BEGIN);
572 SetEndOfFile (fd);
573 CloseHandle (fd);
574
575 if (cb && cb_value) {
576 progress_cb = cb;
577 progress_cb_value = cb_value;
578 }
579 rc = _secure_unlink (file, WIPE_MODE_PASSES, 2, r_fd);
580 free_if_alloc (file);
581 return rc;
582 }

Properties

Name Value
svn:eol-style native

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26