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

Annotation of /trunk/PTD/wptWipeFile.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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


1 werner 46 /* wptWipeFile.cpp - Secure file removal
2 twoaday 273 * Copyright (C) 2001-2006 Timo Schulz
3 werner 46 * Copyright (C) 2000 Matt Gauthier
4     *
5 twoaday 273 * (This code based on the sunlink.c file from the SRM project)
6     *
7 werner 46 * 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 twoaday 273 /* 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 twoaday 53 #ifdef _MSC_VER
46 werner 46 typedef unsigned __int64 DDWORD;
47 twoaday 53 #else
48     typedef unsigned long long DDWORD;
49     #endif
50 werner 46
51     typedef struct {
52 twoaday 273 HANDLE fd; /* file descriptor. */
53     DDWORD filesize; /* file size of the file. */
54 werner 46 DDWORD offset;
55     BYTE *buffer;
56     DWORD buffsize;
57     const char *name;
58     int n_passes;
59     } wipe_context_s;
60    
61 twoaday 273 #define SBOX_SIZE 256
62 twoaday 53 struct arcfour_s {
63     BYTE *seed;
64     int pos;
65 twoaday 273 BYTE sbox[SBOX_SIZE];
66 twoaday 53 DWORD i;
67     DWORD j;
68     };
69 werner 46
70 twoaday 53
71 werner 46 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 twoaday 53 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 twoaday 273 add_random (BYTE *seed, int *seed_pos, BYTE *buf, int buf_len)
86 twoaday 53 {
87     int i, s_pos = *seed_pos;
88    
89     for (i=0; i < buf_len; i++)
90 twoaday 273 seed[(s_pos++ % SBOX_SIZE)] ^= buf[i];
91     *seed_pos = s_pos % SBOX_SIZE;
92 twoaday 53 }
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 twoaday 273 n = GetWindowText (h, buf, DIM (buf)-1);
108 twoaday 53 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 twoaday 273 for (ctx->i=0; ctx->i < SBOX_SIZE; ctx->i++)
195 twoaday 53 ctx->sbox[ctx->i] = (BYTE)ctx->i;
196 twoaday 273 for (ctx->i=0; ctx->i < SBOX_SIZE; ctx->i++) {
197 twoaday 53 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 twoaday 273 BYTE buf[SBOX_SIZE];
216    
217 twoaday 53 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 twoaday 273 BYTE c = 0;
236 twoaday 53
237 twoaday 273 while (!isalnum ((int)c))
238 twoaday 53 c = rnd_byte ();
239 twoaday 273 return c % 127;
240 twoaday 53 }
241    
242    
243 werner 46 /* 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 twoaday 273 randomize_buffer (BYTE *buf, size_t bufsize)
278 werner 46 {
279     const int blocksize = 512;
280     int blocks = bufsize / blocksize;
281     int mod = bufsize % blocksize;
282 twoaday 53 int i;
283    
284 werner 46 while (blocks--) {
285 twoaday 53 for (i=0; i < blocksize; i++)
286     buf[i] = rnd_byte ();
287 werner 46 buf += blocksize;
288     }
289 twoaday 53 for (i=0; i < mod; i++)
290     buf[i] = rnd_byte ();
291 werner 46 }
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 twoaday 53 randomize_buffer (ctx->buffer, ctx->buffsize);
302 werner 46 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 twoaday 273 }
314 werner 46
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 twoaday 273 }
329 werner 46
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 twoaday 53 c = random_char ();
360 werner 46 *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 twoaday 53 static DDWORD
384 werner 46 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 twoaday 55 return (DDWORD)-1;
391 werner 46 fclose (fp);
392     return statbuf.st_size;
393     }
394 twoaday 55 return (DDWORD)-1;
395 werner 46 }
396    
397    
398     static int
399 twoaday 273 _secure_unlink (const char *path, const int mode,
400     const int passes, HANDLE *r_fd)
401 werner 46 {
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 twoaday 273 ctx.buffer = new BYTE[ctx.buffsize];
412 werner 46 if (!ctx.buffer)
413 twoaday 273 BUG (NULL);
414 werner 46
415     ctx.filesize = GetFileSize64 (path);
416     if (!ctx.filesize) {
417     free_if_alloc (ctx.buffer);
418 twoaday 273 remove (path);
419 werner 46 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 twoaday 273 return WPTERR_FILE_CREAT;
427 werner 46 }
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 twoaday 273 case WIPE_MODE_PASSES:
436     ctx.n_passes = passes;
437     overwrite_random (passes, &ctx);
438     break;
439    
440 werner 46 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 twoaday 273 wipememory (ctx.buffer, ctx.buffsize); /* burn the last evidence */
499 werner 46 free_if_alloc (ctx.buffer);
500    
501     return rename_unlink (path);
502     }
503    
504    
505 twoaday 273 /* Try to delete a file in a secure way with the given mode @mode. */
506 werner 46 extern "C" int
507 twoaday 273 secure_unlink (const char *path, const int mode, const int passes)
508 werner 46 {
509 twoaday 53 /* 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 twoaday 273 return _secure_unlink (path, mode, passes, NULL);
518 werner 46 }
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 twoaday 273 wipe_freespace (const char *drive, HANDLE *r_fd,
537 werner 46 void (*cb)(void *, DDWORD, DDWORD), void * cb_value)
538     {
539     ULARGE_INTEGER caller, total, frees;
540     LONG hpart=0;
541     HANDLE fd;
542 twoaday 273 const char *fmt;
543     char *file;
544 werner 46 int disktyp = GetDriveType (drive);
545     int rc;
546    
547 twoaday 273 /* XXX: needs to be modernized or better extracted to a separate
548     program module. */
549 werner 46 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 twoaday 273 fmt = "%stemp_winpt.tmp";
558     file = new char[strlen (drive)+strlen (fmt)+8+1];
559 werner 46 if (!file)
560     BUG (0);
561 twoaday 273 sprintf (file, fmt, drive);
562 werner 46 fd = CreateFile (file,
563 twoaday 273 GENERIC_READ|GENERIC_WRITE,
564     FILE_SHARE_READ|FILE_SHARE_WRITE,
565     NULL, CREATE_ALWAYS, 0, NULL);
566 werner 46 if (fd == INVALID_HANDLE_VALUE) {
567     free_if_alloc (file);
568 twoaday 273 return WPTERR_FILE_CREAT;
569 werner 46 }
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 twoaday 273 rc = _secure_unlink (file, WIPE_MODE_PASSES, 2, r_fd);
580 werner 46 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