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