1/* srm */
2/* Copyright (c) 2000 Matthew D. Gauthier
3 * Portions copyright (c) 2007 Apple Inc.  All rights reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 * IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * Except as contained in this notice, the name of the contributors shall
25 * not be used in advertising or otherwise to promote the sale, use or
26 * other dealings in this Software without prior written authorization.
27 */
28
29#include <errno.h>
30#include <fcntl.h>
31#include <stdarg.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <sys/ioctl.h>
36#include <sys/stat.h>
37#include <sys/types.h>
38#include <sys/mount.h>
39#include <time.h>
40#include <unistd.h>
41
42#if HAVE_LINUX_EXT2_FS_H
43#include <linux/ext2_fs.h>
44#endif
45
46#if HAVE_SYS_VFS_H
47#include <sys/vfs.h>
48#endif
49
50#if HAVE_SYS_PARAM_H && HAVE_SYS_MOUNT_H
51#include <sys/param.h>
52#include <sys/mount.h>
53#endif
54
55#if __APPLE__
56#include <sys/disk.h>
57#endif
58
59#if HAVE_CHFLAGS
60/* define unsupported flags as 0 */
61# if !defined UF_IMMUTABLE
62#  define UF_IMMUTABLE 0
63# endif
64# if !defined UF_APPEND
65#  define UF_APPEND 0
66# endif
67# if !defined UF_NOUNLINK
68#  define UF_NOUNLINK 0
69# endif
70# if !defined SF_IMMUTABLE
71#  define SF_IMMUTABLE 0
72# endif
73# if !defined SF_APPEND
74#  define SF_APPEND 0
75# endif
76# if !defined SF_NOUNLINK
77#  define SF_NOUNLINK 0
78# endif
79#endif
80
81#include "removefile.h"
82#include "removefile_priv.h"
83
84static int
85init_write_buffer(struct stat *statbuf, struct statfs *fs_stats, removefile_state_t state) {
86  u_int32_t tmp_buffsize;
87
88  state->file_size = statbuf->st_size;
89  state->buffsize = statbuf->st_blksize;
90
91#if HAVE_SYS_PARAM_H
92  /* try to determine an optimal write buffer size */
93  state->buffsize = (u_int32_t)(statbuf->st_size / statbuf->st_blksize) * statbuf->st_blksize;
94  if ((statbuf->st_size % statbuf->st_blksize) != 0) {
95    /* add full size of last block */
96    state->buffsize += statbuf->st_blksize;
97  } else if (state->buffsize < statbuf->st_blksize) {
98    /* no smaller than one device block */
99    state->buffsize = statbuf->st_blksize;
100  }
101  tmp_buffsize = MAXBSIZE;
102  if (state->buffsize > tmp_buffsize) {
103    /* no larger than the largest file system buffer size */
104    state->buffsize = tmp_buffsize;
105  }
106#endif
107
108  /* Allocated buffer must be at least 2 bytes larger than logical buffsize.
109     This lets us align repeating 3-byte patterns across multiple buffer
110     writes by using a variable offset (0..2) from the start of the buffer. */
111
112  tmp_buffsize = state->buffsize + 4;
113
114  if (state->buffer) {
115    if (tmp_buffsize > state->allocated_buffsize) {
116      free(state->buffer);
117      state->buffer = NULL;
118    } else {
119      return 0; /* use existing buffer */
120    }
121  }
122  if ((state->buffer = (unsigned char *)malloc(tmp_buffsize)) == NULL) {
123    errno = ENOMEM;
124    return -1;
125  }
126  state->allocated_buffsize = tmp_buffsize;
127  return 0;
128}
129
130static void
131flush(int fd) {
132  /* force buffered writes to be flushed to disk */
133#if defined F_FULLFSYNC
134  /* F_FULLFSYNC is equivalent to fsync plus device flush to media */
135  if (fcntl(fd, F_FULLFSYNC, NULL) != 0) {
136    /* we're not on a fs that supports this; fall back to plain fsync */
137    fsync(fd);
138  }
139#elif HAVE_FDATASYNC
140  fdatasync(fd);
141#else
142  fsync(fd);
143#endif
144}
145
146static unsigned char *align_buffer(unsigned char *buf, off_t pos) {
147  /* return a pointer to the start of the buffer which should be written,
148     offset from the given buffer by 0, 1, or 2 bytes, so that the 3-byte
149     pattern which the buffer contains is aligned with the previous write. */
150  return (unsigned char *)((uintptr_t)buf + (unsigned int)(pos % 3));
151}
152
153#if 0 /* UNUSED */
154void verification_failure(off_t count) {
155  if (sizeof(off_t) == 4)
156    printf("warning: failed to verify write at offset %d\n", count);
157  else if (sizeof(off_t) == 8)
158    printf("warning: failed to verify write at offset %lld\n", count);
159  else
160    printf("warning: previous write failed to verify!\n");
161  fflush(stdout);
162}
163#endif /* UNUSED */
164
165static void
166overwrite(int stage, removefile_state_t state) {
167  ssize_t i;
168  off_t count = 0;
169  unsigned char *buffptr = state->buffer;
170
171	// break out of the function early if cancel is detected
172	if (__removefile_state_test_cancel(state)) return;
173
174  lseek(state->file, 0, SEEK_SET);
175  while (count < state->file_size - state->buffsize) {
176    if (stage == 1 /* W_RANDOM */) {
177      __removefile_randomize_buffer(state->buffer, state->buffsize, state);
178    } else if (stage == 2 /* W_TRIPLE */) {
179      buffptr = align_buffer(state->buffer, count);
180    }
181    i = write(state->file, buffptr, state->buffsize);
182    if (i > 0)
183      count += i;
184
185    // break out of the loop early if cancel is detected
186    if (__removefile_state_test_cancel(state)) return;
187  }
188  if (stage == 1 /* W_RANDOM */) {
189     __removefile_randomize_buffer(state->buffer, (size_t)(state->file_size - count), state);
190  } else if (stage == 2 /* W_TRIPLE */) {
191    buffptr = align_buffer(state->buffer, count);
192  }
193  i = write(state->file, buffptr, (size_t)(state->file_size - count));
194  /*
195   * Only flush the data if we're doing more than one pass of writes.
196   */
197  if ((state->unlink_flags & (REMOVEFILE_SECURE_7_PASS | REMOVEFILE_SECURE_35_PASS | REMOVEFILE_SECURE_3_PASS)) != 0)
198    flush(state->file);
199  lseek(state->file, 0, SEEK_SET);
200}
201
202static void
203overwrite_random(int num_passes, removefile_state_t state) {
204  int i;
205
206  for (i = 0; i < num_passes; i++) {
207    overwrite(1 /* W_RANDOM */, state);
208  }
209}
210
211static void
212overwrite_byte(int byte, removefile_state_t state) {
213  memset(state->buffer, byte, state->buffsize);
214  overwrite(0 /* W_SINGLE */, state);
215}
216
217static void
218overwrite_bytes(unsigned int byte1, unsigned int byte2, unsigned int byte3, removefile_state_t state) {
219  u_int32_t val[3], *p = (u_int32_t *)state->buffer;
220  unsigned int i, mod12buffsize = state->allocated_buffsize - (state->allocated_buffsize % 12);
221
222  val[0] = (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte1;
223  val[1] = (byte2 << 24) | (byte3 << 16) | (byte1 << 8) | byte2;
224  val[2] = (byte3 << 24) | (byte1 << 16) | (byte2 << 8) | byte3;
225
226  /* fill buffer 12 bytes at a time, optimized for 4-byte alignment */
227  for (i = 0; i < mod12buffsize; i += 12) {
228    *p++ = val[0];
229    *p++ = val[1];
230    *p++ = val[2];
231  }
232  while (i < state->allocated_buffsize) {
233    state->buffer[i] = ((unsigned char *)&val[0])[i % 3];
234    i++;
235  }
236  overwrite(2 /* W_TRIPLE */, state);
237}
238
239static void
240overwrite_file(removefile_state_t state) {
241  if (state->unlink_flags & REMOVEFILE_SECURE_35_PASS) {
242    /* Gutmann 35-pass overwrite */
243    overwrite_random(4, state);
244    overwrite_byte(0x55, state);
245    overwrite_byte(0xAA, state);
246    overwrite_bytes(0x92, 0x49, 0x24, state);
247    overwrite_bytes(0x49, 0x24, 0x92, state);
248    overwrite_bytes(0x24, 0x92, 0x49, state);
249    overwrite_byte(0x00, state);
250    overwrite_byte(0x11, state);
251    overwrite_byte(0x22, state);
252    overwrite_byte(0x33, state);
253    overwrite_byte(0x44, state);
254    overwrite_byte(0x55, state);
255    overwrite_byte(0x66, state);
256    overwrite_byte(0x77, state);
257    overwrite_byte(0x88, state);
258    overwrite_byte(0x99, state);
259    overwrite_byte(0xAA, state);
260    overwrite_byte(0xBB, state);
261    overwrite_byte(0xCC, state);
262    overwrite_byte(0xDD, state);
263    overwrite_byte(0xEE, state);
264    overwrite_byte(0xFF, state);
265    overwrite_bytes(0x92, 0x49, 0x24, state);
266    overwrite_bytes(0x49, 0x24, 0x92, state);
267    overwrite_bytes(0x24, 0x92, 0x49, state);
268    overwrite_bytes(0x6D, 0xB6, 0xDB, state);
269    overwrite_bytes(0xB6, 0xDB, 0x6D, state);
270    overwrite_bytes(0xDB, 0x6D, 0xB6, state);
271    overwrite_random(4, state);
272  } else if (state->unlink_flags & REMOVEFILE_SECURE_7_PASS) {
273    /* DoD-compliant 7-pass overwrite */
274    overwrite_byte(0xF6, state);
275    overwrite_byte(0x00, state);
276    overwrite_byte(0xFF, state);
277    overwrite_random(1, state);
278    overwrite_byte(0x00, state);
279    overwrite_byte(0xFF, state);
280    overwrite_random(1, state);
281  } else if (state->unlink_flags & REMOVEFILE_SECURE_3_PASS) {
282    /* DOE M2051-2 or DOD 5220.22-M */
283    overwrite_random(2, state);
284    overwrite_byte(0xAA, state);
285  } else if (state->unlink_flags & REMOVEFILE_SECURE_1_PASS) {
286    overwrite_random(1, state);
287  } else if (state->unlink_flags & REMOVEFILE_SECURE_1_PASS_ZERO) {
288    overwrite_byte(0, state);
289  }
290}
291
292int
293__removefile_sunlink(const char *path, removefile_state_t state) {
294  struct stat statbuf;
295  struct statfs fs_stats;
296#if HAVE_LINUX_EXT2_FS_H
297  int flags = 0;
298#endif
299  int fmode = O_WRONLY;
300  struct flock flock;
301
302  if (lstat(path, &statbuf) == -1)
303    return -1;
304  if (!S_ISREG(statbuf.st_mode))
305    return __removefile_rename_unlink(path, state);
306  if (statbuf.st_nlink > 1) {
307    return __removefile_rename_unlink(path, state);
308  }
309
310  if ( (state->file = open(path, fmode)) == -1) /* BSD doesn't support O_SYNC */
311    return -1;
312  if (fcntl(state->file, F_WRLCK, &flock) == -1) {
313    close(state->file);
314    state->file = -1;
315    return -1;
316  }
317
318  if (fstatfs(state->file, &fs_stats) == -1 && errno != ENOSYS) {
319    close(state->file);
320    state->file = -1;
321    return -1;
322  }
323
324#if HAVE_LINUX_EXT2_FS_H
325  if (fs_stats.f_type == EXT2_SUPER_MAGIC)
326    if (ioctl(state->file, EXT2_IOC_GETFLAGS, &flags) == -1) {
327      close(state->file);
328      state->file = -1;
329      return -1;
330    }
331
332  if ( (flags & EXT2_UNRM_FL) || (flags & EXT2_IMMUTABLE_FL) ||
333      (flags & EXT2_APPEND_FL) )
334    {
335      close(state->file);
336      state->file = -1;
337      errno = EPERM;
338      return -1;
339    }
340
341#endif /* HAVE_LINUX_EXT2_FS_H */
342
343/* chflags(2) turns out to be a different system call in every BSD
344   derivative. The important thing is to make sure we'll be able to
345   unlink it after we're through messing around. Unlinking it first
346   would remove the need for any of these checks, but would leave the
347   user with no way to overwrite the file if the process was
348   interrupted during the overwriting. So, instead we assume that the
349   open() above will fail on immutable and append-only files and try
350   and catch only platforms supporting NOUNLINK here.
351
352   FreeBSD - supports NOUNLINK (from 4.4 on?)
353   MacOS X - doesn't support NOUNLINK (as of 10.3.5)
354   OpenBSD - doesn't support NOUNLINK (as of 3.1)
355   Tru64   - unknown
356
357   Note: unsupported flags are defined as 0 at the top of this file,
358   so a specific platform check is not required here.
359*/
360
361#if HAVE_CHFLAGS
362  if ((statbuf.st_flags & UF_IMMUTABLE) ||
363      (statbuf.st_flags & UF_APPEND) ||
364      (statbuf.st_flags & UF_NOUNLINK) ||
365      (statbuf.st_flags & SF_IMMUTABLE) ||
366      (statbuf.st_flags & SF_APPEND) ||
367      (statbuf.st_flags & SF_NOUNLINK))
368    {
369      close(state->file);
370      state->file = -1;
371      errno = EPERM;
372      return -1;
373    }
374#endif /* HAVE_CHFLAGS */
375
376  if (init_write_buffer(&statbuf, &fs_stats, state) == -1) {
377    close(state->file);
378    state->file = -1;
379    return -1;
380  }
381#if defined F_NOCACHE
382  /* before performing file I/O, set F_NOCACHE to prevent caching */
383  (void)fcntl(state->file, F_NOCACHE, 1);
384#endif
385  overwrite_file(state);
386#if HAVE_LINUX_EXT2_FS_H
387  ioctl(state->file, EXT2_IOC_SETFLAGS, EXT2_SECRM_FL);
388#endif
389
390  close(state->file);
391  state->file = -1;
392#if __APPLE__
393  /* Also overwrite the file's resource fork, if present. */
394  {
395    static const char *RSRCFORKSPEC = "/..namedfork/rsrc";
396    off_t rsrc_fork_size;
397    size_t rsrc_path_size = strlen(path) + strlen(RSRCFORKSPEC) + 1;
398    char *rsrc_path = (char *)alloca(rsrc_path_size);
399    if (rsrc_path == NULL) {
400      errno = ENOMEM;
401      return -1;
402    }
403    if (snprintf(rsrc_path, MAXPATHLEN,
404        "%s%s", path, RSRCFORKSPEC ) > MAXPATHLEN - 1) {
405      errno = ENAMETOOLONG;
406      return -1;
407    }
408
409    if (lstat(rsrc_path, &statbuf) != 0) {
410      int err = errno;
411      if (err == ENOENT || err == ENOTDIR) {
412        rsrc_fork_size = 0;
413      } else {
414        return -1;
415      }
416    } else {
417      rsrc_fork_size = statbuf.st_size;
418    }
419
420    if (rsrc_fork_size > 0) {
421
422      if ((state->file = open(rsrc_path, O_WRONLY)) == -1) {
423        return -1;
424      }
425      if (fcntl(state->file, F_WRLCK, &flock) == -1) {
426        close(state->file);
427	state->file = -1;
428        return -1;
429      }
430
431      if (init_write_buffer(&statbuf, &fs_stats, state) == -1) {
432        close(state->file);
433	state->file = -1;
434        return -1;
435      }
436
437    #if defined F_NOCACHE
438      /* before performing file I/O, set F_NOCACHE to prevent caching */
439      (void)fcntl(state->file, F_NOCACHE, 1);
440    #endif
441
442      overwrite_file(state);
443
444      close(state->file);
445      state->file = -1;
446    }
447  }
448#endif /* __APPLE__ */
449
450	if (__removefile_state_test_cancel(state)) {
451		errno = ECANCELED;
452		return -1;
453	}
454
455  return __removefile_rename_unlink(path, state);
456}
457