move srm to own dir
This commit is contained in:
21
srm/LICENSE
Normal file
21
srm/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
CODE LICENSE
|
||||
This code is released under the ISC license.
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
the ISC license:
|
||||
Copyright (c) 2011 Kyle Isom <coder@kyleisom.net>
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
54
srm/Makefile.in
Normal file
54
srm/Makefile.in
Normal file
@@ -0,0 +1,54 @@
|
||||
VERSION := 1.3.1
|
||||
CC := gcc
|
||||
TARGET := srm
|
||||
OBJS :=
|
||||
LIBS :=
|
||||
|
||||
PREFIX ?= $PREFIX
|
||||
MANDIR ?= $MANDIR
|
||||
|
||||
CFLAGS += -Wall -Wextra -pedantic -Wshadow -Wpointer-arith -Wcast-align
|
||||
CFLAGS += -Wwrite-strings -Wmissing-prototypes -Wmissing-declarations
|
||||
CFLAGS += -Wnested-externs -Winline -Wno-long-long -Wunused-variable
|
||||
CFLAGS += -Wstrict-prototypes -Werror -ansi -static
|
||||
CFLAGS += -D$(TARGET)_VERSION="\"$(TARGET) version $(VERSION)\""
|
||||
CFLAGS += OS_CFLAGS
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(TARGET).o $(OBJS)
|
||||
${CC} -o $(TARGET) ${CFLAGS} ${LDFLAGS} $(LIBS) $(OBJS) $(TARGET).o
|
||||
|
||||
install: $(TARGET)
|
||||
install -m 0755 $(TARGET) $(PREFIX)/bin/$(TARGET)
|
||||
install -m 0755 -d $(MANDIR)/man1
|
||||
install -m 0444 $(TARGET).1 $(MANDIR)/man1/$(TARGET).1
|
||||
|
||||
uninstall:
|
||||
-rm -f $(PREFIX)/bin/$(TARGET)
|
||||
-rm -f $(MANDIR)/man1/$(TARGET).1
|
||||
|
||||
lint:
|
||||
-mkdir security
|
||||
-rats -w 3 $(TARGET).[ch] > security/rats.out
|
||||
-lint -fhrs $(TARGET).c > security/lint.out
|
||||
-splint +posixlib $(TARGET).[ch] > security/splint.out
|
||||
|
||||
dist: clean
|
||||
-mkdir $(TARGET)-$(VERSION)
|
||||
-cp * $(TARGET)-$(VERSION)
|
||||
-cd $(TARGET)-$(VERSION) && make distclean && cd ..
|
||||
-tar czf $(TARGET)-$(VERSION).tgz $(TARGET)-$(VERSION)
|
||||
|
||||
distclean: clean
|
||||
-rm -f Makefile
|
||||
|
||||
htmldoc:
|
||||
-mandoc -Thtml $(TARGET).1 > $(TARGET).1.html
|
||||
|
||||
tags:
|
||||
ctags *.[ch]
|
||||
|
||||
.c.o:
|
||||
$(CC) -c ${CFLAGS} $?
|
||||
|
||||
.PHONY: clean all install lint uninstall dist distclean htmldoc tags
|
||||
36
srm/README
Normal file
36
srm/README
Normal file
@@ -0,0 +1,36 @@
|
||||
srm - securely wipe files
|
||||
--------------------------
|
||||
srm is a utility to overwrite files with random data in one or more passes.
|
||||
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
None.
|
||||
|
||||
|
||||
Compatibility
|
||||
-------------
|
||||
srm has been tested on the following operating systems:
|
||||
* OpenBSD (5.1-snap)
|
||||
* OS X (10.8)
|
||||
* Linux (Debian 6.0)
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
make build install
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
srm [-v] [-n number] file list
|
||||
|
||||
options:
|
||||
-n <number of passes>: specify number of passes
|
||||
(default is 3 passes)
|
||||
-v: verbose mode. display list of failures and wiped files after wiping
|
||||
|
||||
|
||||
Known bugs / caveats
|
||||
--------------------
|
||||
srm can't recursively remove files, i.e. it can't remove directories.
|
||||
57
srm/config.sh
Executable file
57
srm/config.sh
Executable file
@@ -0,0 +1,57 @@
|
||||
#!/bin/sh
|
||||
|
||||
TARGET="$(cat Makefile.in | grep 'TARGET :=' | awk -F' ' '{ print $3; }')"
|
||||
echo "configuring ${TARGET}"
|
||||
|
||||
which sed 2>/dev/null 1>/dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "cannot find sed!" 1>&2
|
||||
fi
|
||||
|
||||
OPSYS=$(uname -s)
|
||||
|
||||
echo "Configuring for ${OPSYS}..."
|
||||
if [ "x${OPSYS}" = "xLinux" ]; then
|
||||
OS_CFLAGS="-D_BSD_SOURCE -D_POSIX_SOURCE -D_XOPEN_SOURCE"
|
||||
elif [ "x${OPSYS}" = "xDarwin" ]; then
|
||||
OS_CFLAGS="-D_DARWIN_C_SOURCE"
|
||||
else
|
||||
OS_CFLAGS=""
|
||||
fi
|
||||
|
||||
if [ -z "${OS_CFLAGS}" ]; then
|
||||
echo "${OPSYS} requires no extra build flags."
|
||||
else
|
||||
echo "${OPSYS} requires build flags ${OS_CFLAGS}"
|
||||
fi
|
||||
|
||||
if [ -z "${PREFIX}" ]; then
|
||||
PREFIX="/usr/local"
|
||||
fi
|
||||
|
||||
if [ "${PREFIX}" = "/usr" ]; then
|
||||
MANDIR="$(PREFIX)/share/man"
|
||||
elif [ "${PREFIX}" = "/usr/local" ]; then
|
||||
if [ "${OPSYS}" = "Darwin" ]; then
|
||||
MANDIR="${PREFIX}/share/man"
|
||||
else
|
||||
MANDIR="${PREFIX}/man"
|
||||
fi
|
||||
else
|
||||
MANDIR="${PREFIX}/man"
|
||||
fi
|
||||
|
||||
if [ "$1" = "DEBUG" ]; then
|
||||
OS_CFLAGS="${OS_CFLAGS} -g -O0"
|
||||
fi
|
||||
|
||||
echo "prefix: ${PREFIX}"
|
||||
echo "mandir: ${MANDIR}"
|
||||
|
||||
echo "writing new Makefile"
|
||||
cat Makefile.in | sed -e "s|OS_CFLAGS|${OS_CFLAGS}|" | \
|
||||
sed -e "s|\$PREFIX|${PREFIX}|" | \
|
||||
sed -e "s|\$MANDIR|${MANDIR}|" > Makefile
|
||||
|
||||
|
||||
echo "done."
|
||||
84
srm/srm.1
Normal file
84
srm/srm.1
Normal file
@@ -0,0 +1,84 @@
|
||||
.Dd $Mdocdate$
|
||||
.Dt SRM 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm srm
|
||||
.Nd securely delete files
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl h
|
||||
.Op Fl n Ar number
|
||||
.Op Fl r
|
||||
.Op Fl v
|
||||
.Op Fl V
|
||||
.Ar files
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is a simple secure file deletion tool. It overwrites the file with several
|
||||
passes of random data before unlinking it. If no options are specified, Nm
|
||||
defaults to three passes.
|
||||
.Nm
|
||||
supports the following options:
|
||||
.Bl -tag -width .Ds
|
||||
.It Fl h
|
||||
Display a brief help message.
|
||||
.It Fl n Ar number
|
||||
Specify the number of times to overwrite each target with random data.
|
||||
.It Fl r
|
||||
Recursive mode. Delete any directories and all subdirectories underneath.
|
||||
.It Fl v
|
||||
Verbose mode. Displays a list of both files that failed to wipe and files that
|
||||
were successfully wiped.
|
||||
.It Fl V
|
||||
Print version information.
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
.Ex -std
|
||||
The exit values are standard
|
||||
.Xr sysexits 3
|
||||
values.
|
||||
.Sh EXAMPLES
|
||||
Wipe files
|
||||
.Pa foo
|
||||
and
|
||||
.Pa bar
|
||||
with three passes:
|
||||
.Dl $ srm foo bar
|
||||
Wipe files
|
||||
.Pa baz
|
||||
and
|
||||
.Pa quux
|
||||
with ten passes:
|
||||
.Dl $ srm -n 10 baz quux
|
||||
Wipe all PGP keys, i.e. files with extension
|
||||
.Pa *.asc :
|
||||
.Dl $ srm *.asc
|
||||
Recursive deletes aren't implemented yet. A workaround is to use
|
||||
.Nm
|
||||
and
|
||||
.Xr find 1 ,
|
||||
for example to delete all
|
||||
.Pa *.pgp
|
||||
files:
|
||||
.Dl $ find . -iname '*.pgp' -exec srm '{}' \;
|
||||
.Sh DIAGNOSTICS
|
||||
.Nm
|
||||
uses the standard
|
||||
.Xr err 3
|
||||
facilities to report any errors that occur.
|
||||
.Sh SEE ALSO
|
||||
The srm page on
|
||||
.Lk http://www.tyrfingr.is/projects/srm/ "tyrfinger" .
|
||||
.Sh STANDARDS
|
||||
.Nm
|
||||
conforms to
|
||||
.St -ansiC .
|
||||
.Sh AUTHORS
|
||||
.Nm
|
||||
was written by
|
||||
.An "Kyle Isom" Aq Mt kyle@tyrfingr.is .
|
||||
.Sh BUGS
|
||||
None known. Report bugs to the author.
|
||||
.Sh LICENSE
|
||||
.Nm
|
||||
is released under an ISC license.
|
||||
369
srm/srm.c
Normal file
369
srm/srm.c
Normal file
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2012 Kyle Isom <kyle@tyrfingr.is>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <err.h>
|
||||
#include <libgen.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#define DEFAULT_PASSES 3
|
||||
#define DEV_RANDOM "/dev/urandom"
|
||||
#define MAX_CHUNK 4096
|
||||
|
||||
|
||||
static int do_wipe(char *, size_t, int);
|
||||
static int wipe(char *);
|
||||
static int rmdirs(const char *, size_t);
|
||||
static void usage(void);
|
||||
static void version(void);
|
||||
|
||||
extern char *__progname;
|
||||
|
||||
|
||||
/*
|
||||
* overwrite a file with one or more passes of random data, then unlink it.
|
||||
*/
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
size_t passes, tmp_passes, wiped, failed, i;
|
||||
char **file_ptr, **wipe_success, **wipe_fail;
|
||||
int retval, opt, verbose, recur;
|
||||
|
||||
passes = DEFAULT_PASSES;
|
||||
retval = EX_DATAERR;
|
||||
wiped = 0;
|
||||
failed = 0;
|
||||
verbose = 0;
|
||||
recur = 0;
|
||||
|
||||
if (argc == 1)
|
||||
usage();
|
||||
|
||||
while (-1 != (opt = getopt(argc, argv, "hn:rvV"))) {
|
||||
switch( opt ) {
|
||||
case 'h':
|
||||
usage();
|
||||
break; /* don't technically need it but meh */
|
||||
case 'n':
|
||||
tmp_passes = atoi(optarg);
|
||||
passes = tmp_passes > 0 ? tmp_passes : passes;
|
||||
break;
|
||||
case 'r':
|
||||
recur = 1;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
case 'V':
|
||||
version();
|
||||
exit(EX_OK);
|
||||
default:
|
||||
usage();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
file_ptr = argv;
|
||||
wipe_success = calloc(argc, sizeof(char *));
|
||||
wipe_fail = calloc(argc, sizeof(char *));
|
||||
|
||||
|
||||
while (NULL != *file_ptr) {
|
||||
printf("%s: ", *file_ptr);
|
||||
fflush(stdout);
|
||||
|
||||
if (EX_OK != do_wipe(*file_ptr, passes, recur)) {
|
||||
wipe_fail[failed] = strdup(*file_ptr);
|
||||
failed++;
|
||||
} else {
|
||||
wipe_success[wiped] = strdup(*file_ptr);
|
||||
wiped++;
|
||||
printf(" OK!");
|
||||
}
|
||||
file_ptr++;
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
if (0 < wiped) {
|
||||
printf("success: ");
|
||||
for( i = 0; i < wiped; ++i ) {
|
||||
printf("%s ", wipe_success[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (0 < failed) {
|
||||
printf("failures: ");
|
||||
for( i = 0; i < failed; ++i ) {
|
||||
printf("%s ", wipe_fail[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* free allocated memory */
|
||||
for (i = 0; i < failed; ++i) {
|
||||
free(wipe_fail[i]);
|
||||
wipe_fail[i] = NULL;
|
||||
}
|
||||
free(wipe_fail);
|
||||
wipe_fail = NULL;
|
||||
|
||||
for (i = 0; i < wiped; ++i) {
|
||||
free(wipe_success[i]);
|
||||
wipe_success[i] = NULL;
|
||||
}
|
||||
free(wipe_success);
|
||||
wipe_success = NULL;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* takes a filename and the number of passes to wipe the file
|
||||
*/
|
||||
int
|
||||
do_wipe(char *filename, size_t passes, int recur)
|
||||
{
|
||||
struct stat sb;
|
||||
size_t filesize, i;
|
||||
int retval;
|
||||
|
||||
retval = EX_IOERR;
|
||||
if (-1 == stat(filename, &sb)) {
|
||||
warn("%s", filename);
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (recur && sb.st_mode & S_IFDIR) {
|
||||
if (EX_OK != rmdirs(filename, passes)) {
|
||||
printf("!");
|
||||
return retval;
|
||||
} else {
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
retval = EX_OK;
|
||||
}
|
||||
|
||||
} else {
|
||||
filesize = sb.st_size;
|
||||
|
||||
for (i = 0; i < passes; ++i) {
|
||||
if (EX_OK != wipe(filename)) {
|
||||
printf("!");
|
||||
return retval;
|
||||
} else if (-1 == stat(filename, &sb)) {
|
||||
printf("!");
|
||||
return retval;
|
||||
} else if (filesize != (size_t)sb.st_size) {
|
||||
printf("!");
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
if (-1 == truncate(filename, (off_t)0))
|
||||
warn("%s", filename);
|
||||
|
||||
if (-1 == unlink(filename)) {
|
||||
warn("%s", filename);
|
||||
} else {
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
retval = EX_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* takes a filename and attempts to overwrite it with random data
|
||||
*/
|
||||
int
|
||||
wipe(char *filename)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
size_t chunk, filesize, rdsz, wiped, wrsz;
|
||||
FILE *devrandom, *target;
|
||||
int retval;
|
||||
char *rdata;
|
||||
|
||||
retval = EX_IOERR;
|
||||
wiped = 0;
|
||||
|
||||
if (-1 == stat(filename, &sb))
|
||||
return retval;
|
||||
|
||||
filesize = sb.st_size;
|
||||
|
||||
/*
|
||||
* open devrandom first: if this fails, we don't want to touch the
|
||||
* target file.
|
||||
*/
|
||||
devrandom = fopen(DEV_RANDOM, "r");
|
||||
if (NULL == devrandom || -1 == ferror(devrandom)) {
|
||||
warn("failed to open PRNG %s!", DEV_RANDOM);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* for security purposes, we want to make sure to actually overwrite
|
||||
* the file. r+ gives us read/write but more importantly, sets the
|
||||
* write stream at the beginning of the file. a side note is that when
|
||||
* overwriting a file, its size will never seem to change.
|
||||
*/
|
||||
target = fopen(filename, "r+");
|
||||
if (NULL == target || -1 == ferror(target)) {
|
||||
warn("failed to open %s", filename);
|
||||
fclose(devrandom);
|
||||
return retval;
|
||||
}
|
||||
|
||||
rewind(target);
|
||||
|
||||
/*
|
||||
* wait to calloc until we really need the data - makes cleaning up less
|
||||
* tricky.
|
||||
*/
|
||||
rdata = calloc(MAX_CHUNK, sizeof(char));
|
||||
if (NULL == rdata) {
|
||||
warn("could not allocate random data memory");
|
||||
fclose(devrandom);
|
||||
fclose(target);
|
||||
return retval;
|
||||
}
|
||||
|
||||
while (wiped < filesize) {
|
||||
chunk = filesize - wiped;
|
||||
chunk = chunk > MAX_CHUNK ? MAX_CHUNK : chunk;
|
||||
|
||||
rdsz = fread( rdata, sizeof(char), chunk, devrandom );
|
||||
wrsz = fwrite( rdata, sizeof(char), chunk, target );
|
||||
|
||||
if (-1 == stat(filename, &sb)) {
|
||||
warn(" stat on %s failed", filename);
|
||||
break;
|
||||
}
|
||||
if ((rdsz != wrsz) || (filesize != (unsigned int)sb.st_size)) {
|
||||
warn("invalid read/write size");
|
||||
break;
|
||||
}
|
||||
|
||||
wiped += chunk;
|
||||
}
|
||||
|
||||
if ((0 != fclose(devrandom)) || (0 != fclose(target)))
|
||||
warn("%s", filename);
|
||||
else
|
||||
retval = EX_OK;
|
||||
|
||||
free(rdata);
|
||||
rdata = NULL;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* remove a directory, all files in it, and all subdirectories.
|
||||
*/
|
||||
int
|
||||
rmdirs(const char *path, size_t passes)
|
||||
{
|
||||
char child[FILENAME_MAX + 1];
|
||||
struct dirent *dp;
|
||||
DIR *dirp;
|
||||
int fail = 0;
|
||||
|
||||
if (NULL == (dirp = opendir(path)))
|
||||
return EX_IOERR;
|
||||
while (NULL != (dp = readdir(dirp))) {
|
||||
if (0 == strncmp("..", dp->d_name, 3))
|
||||
continue;
|
||||
if (0 == strncmp(".", dp->d_name, 2))
|
||||
continue;
|
||||
snprintf(child, FILENAME_MAX, "%s/%s", path, dp->d_name);
|
||||
if (DT_DIR == dp->d_type) {
|
||||
fail = rmdirs(child, passes);
|
||||
if (EX_IOERR == fail)
|
||||
break;
|
||||
} else {
|
||||
fail = do_wipe(child, passes, 1);
|
||||
if (EX_IOERR == fail)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (-1 == closedir(dirp))
|
||||
return EX_IOERR;
|
||||
if (EX_IOERR == fail)
|
||||
return EX_IOERR;
|
||||
|
||||
if (-1 == rmdir(path))
|
||||
return EX_IOERR;
|
||||
else
|
||||
return EX_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* print a quick usage message
|
||||
*/
|
||||
void
|
||||
usage()
|
||||
{
|
||||
version();
|
||||
printf("usage: %s [-v] [-n number] files\n", __progname);
|
||||
printf(" -n passes specify number of passes\n");
|
||||
printf(" (default is %d passes)\n", DEFAULT_PASSES);
|
||||
printf(" -r recursive delete\n");
|
||||
printf(" -v display list of failures and wiped files"
|
||||
" after wiping\n"
|
||||
" (verbose mode).\n");
|
||||
printf(" -V print version information.");
|
||||
|
||||
printf("\n");
|
||||
exit(EX_USAGE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* print program version information
|
||||
*/
|
||||
void
|
||||
version()
|
||||
{
|
||||
printf("%s\n", srm_VERSION);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user