/**

fish

- file shredder and crypter for Darwin, Solaris and Linux (WARNING: might be deemed subversive in some places) */ #define VERSNR "7.5" /* crypt NICHT kompatibel mit Vsn. 6.x und früher */ /* Vorgeschichte: */ /* drz 20.2.2009 Polsterdatei jetzt mit Punkt */ /* drz 15.9.2008 Geheimschlüssel auch vom Keyboard */ /* drz 5.5.2008 fixed buffer size underflow for veil file */ /* drz 24.7.2006 read -> fread fixes crypting from stdin (thx, richard!) */ /* drz 27.6.2007 Maximale Cache-Größe 32 MB */ /* drz 22.5.2007 crypt zwischen big/little endian geht. Endlich. */ /* drz 10.5.2007 Polsterdatei bei der Gesamtgröße mitzählen! */ /* drz 3.4.2007 das mit der Schleierdatei konnte man noch optimieren */ /* drz 3.4.2007 Destructive Recall */ /* drz 2.4.2007 Beim Löschen des freien Platzes gabs Probleme */ /* drz 8.12.2006 Polsterdatei etwas schlauer initialisieren */ /* drz 9.11.2006 Option -e putzt leeren Speicherplatz */ /* drz 11.10.2006 Alten alten Zählfehler im Krypter beseitigt */ /* drz 9.10.2006 Zugriffszeitpunkte werden verschleiert */ /* drz 18.7.2006 option -f overwrites with ordinary file before deleting */ /* drz 21.7.2005 fixed path size error in do_resource. ugh. */ /* drz 17.7.2005 Tracing und Fehlermeldungen mit dynamisch angepaßtem Puffer */ /* drz 14.7.2005 Traurig - vorläufiger Pfusch am Schluß */ /* drz 29.6.2005 keine Einschränkung der Zahl der Durchläufe bei -q */ /* drz 27.6.2005 Pfadlänge in restore_path berichtigt */ /* drz 21.6.2005 Option -q erlaubt als Argument die Zahl der Durchläufe */ /* drz 13.5.2005 fsync aus der inneren Schleife herausgenommen */ /* drz 20.4.2005 vorläufiger Endstand der Funktionalität */ /* drz 12.4.2005 Verzeichnisse werden vor dem Löschen umgetauft */ /* drz 6.3.2005 Speicherplatz-Einsparung bei node->path und node->bpat */ /* drz 22.12.2004 Neue Option -q "quick" */ /* drz 6.12.2004 Empfindlichkeit gegen schwache Schlüssel verbessert */ /* drz 1.12.2004 man kann fish in einer Pipe einsetzen */ /* drz 18.11.2004 auch die Ressourcen-Zweige werden jetzt kryptiert */ /* drz 15.11.2004 optionaler Keyfile-Name in env "_FISH_KEY" */ /* drz 11.11.2004 Nach dem Verschlüsseln Quelle schreddern */ /* drz 6.11.2004 Orthogonalisierung r_init nachgebessert */ /* drz 5.11.2004 verbessertes Verhalten bei beinahe korrektem Schlüssel */ /* drz 2.11.2004 fish_key wird im aktuellen, dann im home Verz. gesucht */ /* drz 1.11.2004 Zufallsgenerator refaktoriert */ /* drz 27.10.2004 Vektor-Zufallsgenerator hatte Alignment-Fehler. Wups. */ /* drz 22.10.2004 Verschleierung mit Geheimschlüssel verbessert */ /* drz 21.10.2004 Neue Option -y: Wegwerf-Schlüssel-Verschleierung */ /* drz 1.10.2004 Dateien werden vor dem Löschen abgeschnitten */ /* drz 23.9.2004 Dateinamen-Konversion effizienter gestaltet */ /* drz 21.9.2004 Tracing durch #define ein- und ausschalten */ /* drz 15.9.2004 Dateinamen werden besser zerwuzelt */ /* drz 10.9.2004 Option -m setzt Priorität herab */ /* drz 7.9.2004 getenv durch getcwd ersetzt */ /* drz 13.8.2004 kümmert sich jetzt auch um Resource Fork */ /* drz 6.8.2004 Umgetauft, Du heißen jetzt "fish" */ /* drz 4.8.2004 Cache-Größe mit Option -c wählbar */ /* drz 3.8.2004 Ausgleichsdatei, um Cache sicher zu füllen */ /* drz 30.6.2004 Verzeichnisse werden auch gelöscht */ /* drz 27.6.2004 Unterverzeichnisse aus der Liste werden aufgelöst */ /* drz 24.6.2004 Filenamen-Liste wird abgearbeitet */ /* drz 17.6.2004 Festwert-Läufe werden gemischt */ /* drz 2.6.2004 Neue Option -s: silent */ /* drz 1.6.2004 Dateinamen auch schreddern */ /* drz 19.5.2004 Zeitstempel wiederherstellen */ /* drz 29.4.2004 runtime AltiVec detection (sort of) */ /* drz 26.4.2004 Zufallsgenerator mit AltiVec */ /* drz 22.4.2004 Progress Bar */ /* drz 21.4.2004 Support fuer Files > 2047 MBytes */ /* drz 20.4.2004 shred: Zufallsgenerator aus libr_ mit reingenommen */ /* neu geschrieben : drz 19.4.2004 */ /*&

Short description

*-
fish [-cdefmnqsyYzvVhi] file name(s)
*&fish overwrites files repeatedly with shrewdly chosen data before they *&are (optionally) deleted. The point of this action is making the retrieval *&of the file's original content expensive, which might be a good idea when *-it's information one does not want to share. *&If file name is a directory, or the list of filename arguments *&contains one such, the whole file tree underneath the directory gets **overwritten (and, if so desired, deleted). **If file name is "-", then fish en/decrypts from stdin to stdout. *-Options: *- -c N: (cache) disk drive cache size is N MB *- -d: (delete) files are deleted after overwriting *- -e: (empty) overwrite empty storage, too *- -f File: overwrite with File in last pass *- -m: (modest) sets priority to lower value *- -n: (noaction) files remain untouched, write to /dev/null *- -q N: (quick) overwrite files just N times *- -s: (silent) no console messages on stdout or stderr *- -y: (crypt) obfuscate/decipher files using one-time pad *- -Y: (CRYPT) en/decrypt files *- -z: (zero) write zeroes in last pass to hide shredding *- -v: (verbose) generate messages to show progress *- -V: (very verbose) generate more messages *- -h: (help) generate a short usage description ** -i: (info) print out path of secret key if used *&When fish -[yY] is used for obfuscation, and there exists a file called *&".fish_key" (or whatever the environment variable _FISH_KEY says) in the *¤t directory or the user's home directory, then the contents of this *&file are used to modify the random number generator (current overrides home *&directory!). An attempt to decipher a file so obfuscated requires a bit-exact *© of the ".fish_key" file to reside in the appropriate directory of the *-decrypting user's file tree. *&If _FISH_KEY contains the string "stdin" then the secret key is retrieved **from stdin (e.g. keyboard input). *&

A few desultory words of explanation:

*&File shredding utilities seem to be all the rage these days, what with folks *&selling used hard drives on eBay, and interesting data being stored on such *-drives... *&I should like to point out a few things that distinguish fish from a couple *-of other file shredders: *&First, it is decently fast on big files (as fast as the disk drive will go). *&On a G4 or G5 Mac (sigh). Yes, there IS a use for those AltiVec registers *-after all. *&Second, it doesn't do a sloppy job. It's good at shredding small files, which *&might escape their fate by the workings of a large hard drive cache, and it *&sort of shreds the file names as well. And, after I had learned that Mac *&resource forks need special attention, they get that, too. Btw, if so *&desired, all the free space on a device can be shredded, too - although this *&may take days (but it has the added benefit of forcing the file system to *-overwrite files in place).. *&Third, it is command-line, so it may be used in shell scripts like this one: *- *-#!/bin/csh *-#this script cleans out all my trashes (including mounted external drives) *-foreach i ( ~/.Trash /Volumes/*?/.Trashes/$uid ) *- chdir $i *- fish -dmz * & *-end *-exit 0 *- *-or, for the select few who prefer (ba)sh: *- *-#!/bin/sh *-for directory in ${HOME}/.Trash /Volumes/*?/.Trashes/${UID}; do *-#assuming the fish resides in the user's private file tree *- ${HOME}/bin/fish -dmzc 8 ${directory}/* & *-#note: the -c 8 switch is only necessary if you have a drive with 8 MB cache *-done *-exit 0 *- *&And 4th, I can show off my vectorized Kirkpatrick-Stoll pseudo-random number *-generator... *&Last and least, I could not care less if Windows users can delete their files **good and proper, so I did not attempt to make the code windows-friendly. *&NOTE: The possession (or, god forbid, the use) of this software might *&be regarded as subversive or even illegal in some countries. Therefore, I **provide the programme strictly for educational purposes. *&HINWEIS: Der Besitz (oder der Gebrauch) dieser Software wird *&möglicherweise in manchen Ländern als subversiv oder gar ungesetzlich *&eingestuft. Deshalb stelle ich dieses Programm ausschließlich als **Anschauungsmaterial zur Verfügung. */ /*&

Kurzbeschreibung:

*-
fish [-cdefmnqsyYzvVhi] Dateiname(n)
*&fish überschreibt Dateien mehrfach mit pfiffig gewählten Daten, bevor es sie *&(optional) löscht. Sinn dieser Aktion ist, die Wiederherstellung der Inhalte *-zu erschweren, z.B. bei vertraulich zu behandelnden Informationen. *&Ist Dateiname ein Verzeichnis, bzw. enthält die Liste der *&Dateinamen ein solches, wird der gesamte darunterliegende Dateibaum **überschrieben und ggf. gelöscht. **Ist Dateiname = "-", ver/entschleiert fish von stdin nach stdout. *-Optionen: *- -c N: (cache) Cache size ist N MB *- -d: (delete) Datei(en) nach dem Überschreiben löschen *- -e: (empty) Leeren Speicherplatz im Filesystem auch überschreiben *- -f File: im letzten Durchlauf mit File überschreiben *- -m: (modest) setzt Priorität auf niedrigen Wert *- -n: (noaction) Datei(en) nicht verändern, alles nach /dev/null *- -q N: (quick) Dateien nur N mal überschreiben *- -s: (silent) keine Meldungen *- -y: (crypt) Dateien mit Wegwerf-Schlüssel ent/verschleiern *- -Y: (CRYPT) Dateien ent/verschlüsseln *- -z: (zero) im letzten Durchlauf Datei mit Nullen überschreiben *- -v: (verbose) Fortschritt darstellen *- -V: (very verbose) etwas detaillierter darstellen *- -h: (help) kurze Benutzerführung ausgeben ** -i: (info) Pfad des verwendeten Geheimschlüssels anzeigen *&Wenn fish -[yY] für die Verschleierung einer Datei benutzt wird, *&und im aktuellen oder Heim-Verzeichnis des Benutzers befindet sich eine Datei *&".fish_key" (bzw. die in der Umgebungsvariablen _FISH_KEY bezeichnete Datei), *&dann wird mit dem Inhalt dieser Datei der Zufallsgenerator modifiziert. Die *&Datei kann dann nur entschleiert werden, wenn sich eine exakte Kopie der zum *&Verschleiern benutzten Schlüssel-Datei ".fish_key" im aktuellen oder im *-Heim-Verzeichnis des Anwenders befindet. *&Wenn in _FISH_KEY die Zeichenkette "stdin" steht, wird der Geheimschlüssel **von stdin (z.B. Tastatur) eingelesen. *-inspired by/inspiriert von : *- *- Peter Claus Gutmann, Dept.CS,University of Auckland *- ( pgut001@cs.auckland.ac.nz ) *- "Secure Deletion of Data from Magnetic and Solid-State Memory", *- Sixth USENIX Security Symposion Proceedings, *- San Jose, California, July 22-25, 1996 *- *- NOTE: if you read the paper, don't skip the epilogue ;-> *- *-The pseudo-random number generator used here is based upon: *-Der verwendete Pseudo-Zufallszahlengenerator basiert auf : *- *- Kirkpatrick,S., and E.Stoll, *- "A Very Fast Shift-Register Sequence Random Number Generator", *- Journal of Computational Physics, V.40, pp. 517-526, 1981 ** *-NOTA BENE : fish does not work reliably in non-local, distributed (or *- journaled ?) file systems, because it assumes direct access *- to a local storage device and overwriting in place. Encrypted *- file systems are not so good, either. I dunno. And fish doesn't *- shred files which the user has no write privileges for. *- *-HINWEIS: fish ist in Filesystemen wie AFS oder einigen NFS-Versionen *- nicht sehr wirksam, da nur lokale Kopien der Dateien über- *- schrieben werden. Und Dateien, für die der Anwender keine *- Schreibrechte hat, überschreibt fish auch nicht. *- *&Das Programm ist unter Solaris, Darwin und Linux verwendbar. In Darwin wird *-es mit "gcc -O3 -faltivec fish.c" übersetzt. *&fish runs in Solaris, Darwin and Linux. In Darwin, compile with **"gcc -faltivec -O3" *+ *-Note: *&I learned about srm in 2004, when *&the shredder was almost finished; I found a few useful ideas there. I did *¬, however, try to make fish's usage syntax like rm(1)'s, because IMHO the *&shredder should never ever be used casually, but always with extreme *-prejudice. *&I do admit that fish's code is much more complex than srm's, but I swear it's *-not bloated. *+ *-TODO: further improve AltiVec code: make parallel vector ops ** - - - Make it work on USB sticks, too! */ /*&Benötigte *& */ /* --- if something weird happens, comment the following line out : */ #define __NO_TRACING #include #ifdef __linux__ #include #else /* not __linux__ */ #include #endif /* __linux__ */ #include /* MAXPATHLEN,NOFILE (max no of open files) */ #include typedef struct timeval Timeval; #include #include #include #include #include #include typedef struct stat Stat; #include typedef void hd_t; #define DefHdlr (hd_t *)SIG_DFL #ifdef __linux #define SIGEMT 0 #define SIGABI SIGABRT #else #define SIGABI SIGIOT #endif /* __linux */ #include #ifdef __SVR4 #include #include #else /* not __SVR4 */ #include #ifdef __linux #include #else #include #endif /* __linux */ #endif /* __SVR4 */ #include #include typedef struct dirent Dirent; #include #include /* byteorder macros */ /* Darwin stuff */ #ifdef __APPLE__ #include /* this is helpful for Altivec detection... */ #include /* statfs */ typedef struct statfs FsInfo; #define fs_stat(fd,info) fstatfs(fd,(FsInfo *)(&info)) #define VEC_BYTS sizeof(vector unsigned int) #else /* not __APPLE__ */ #include #include /* howmany macro */ #include typedef struct statvfs FsInfo; #define fs_stat(fd,info) fstatvfs(fd,(FsInfo *)(&info)) #define VEC_BYTS 16 /* muß leider */ #ifndef __linux__ typedef void sig_t; #endif /* __linux__ */ #endif /* __APPLE__ */ #define VEC_INTS ((VEC_BYTS)/sizeof(unsigned int)) #ifndef _DEFS /* /usr/include/machine/defs.h */ #ifndef NULL #define NULL 0 #endif #define FALSE 0 #define TRUE 1 #define MAXUINT (~((uint32_t)0)) #endif #ifdef MAXINT #undef MAXINT #endif #define MAXINT ((int32_t)(((uint32_t)MAXUINT)>>1)) #ifndef _rg #define _rg register #endif #define ZOMBIE 0xdeadface #define NOGOOD 0xdecafbad #define BIMBO 0xdafdbabe #define NBUSH (NBBY * sizeof(uint16_t)) #define NBULG (NBBY * sizeof(uint32_t)) /* shorthand */ #define Cat(a,b) strcat(a,b) #define Cpy(a,b) strcpy(a,b) #define nCpy(a,b,n) strncpy(a,b,n) /* Stringize */ #define __SpStr(x) #x #define GEHR FALSE /* rimbrarabrim - Futter fürn C-Präprozessor */ #define nfree(ptr) free((void *)ptr); ptr = NULL; #define ifree(ptr) if(ptr) { nfree(ptr); } /* a couple of useful string snippets */ #define USCOR '_' #define SPACE ' ' #define COMAT '@' #define SLASH '/' #define NULLC '\0' #define COLON ':' #define DASH '-' #define DOT '.' char dotst[2] = { DOT,NULLC }; char slast[2] = { SLASH,NULLC }; char colst[2] = { COLON,NULLC }; /* finds smallest power of 2 greater than m - thx, richard! */ uint32_t nxpw2( _rg uint32_t m) { _rg uint32_t shift,mp1; shift = 1; mp1 = m--; if(m&mp1) { m = m|(m>>shift); while(m&(mp1=m+1)) { shift <<= 1; m = m|(m>>shift); } } return(mp1); } /* uint32_t nxpw2(_rg uint32_t m) */ /* { _rg uint32_t m1; while(m&(m1=m-1)) m = (m|m1)+1; return(m); } */ /* this is one of the flag variables - see below */ int silent = FALSE; char progname[] = "fish"; /* max. 4 Zeichen ! (crypt_the) */ char PROGNAME[] = "FISH"; /* the following is a debugging aid - production versions */ /* are compiled with __NO_TRACING defined (see above) */ char *ErB = NULL, *ErN = NULL; /* Text buffer for error messages */ size_t ErBsz = 0; int ErBln = 0; /* diese Jammerstrings werden ziemlich oft benutzt */ char canm[] = "calloc",manm[] = "malloc",ranm[] = "realloc"; /* we allocate the error message buffer only as big as necessary */ char *adjust_ErB() { size_t newErBsz; if((newErBsz = nxpw2(ErBln + 1)) > ErBsz) { char *ErBold; int ErNoff; ErBold = ErB; ErNoff = ErN - ErB; if((ErB = (char *)realloc((void *)ErB,newErBsz))) { if(ErB != ErBold) { ErN = ErB + ErNoff; } ErBsz = newErBsz; } else { ErN = NULL; ErBsz = 0; ErBln = 0; } } return(ErN); } /* output (tracing and) error message with one parameter... */ void bleat(char *func,char *arg) { if(!silent && ErB) { if(func) { size_t ErNln; ErNln = strlen(func) + strlen(arg) + 2; /* 2 brackets; see below */ ErBln += ErNln; if((ErN = adjust_ErB())) { extern int errno; Cat(Cat(Cat(Cpy(ErN,func),"("),arg),")"); if(errno) perror(ErB); else fprintf(stderr,"%s\n",ErN); } else { perror(progname); } ErBln -= ErNln; } else if(arg) { fprintf(stderr,"%s%s\n",ErB,arg); } } return; } /* ...and with two */ void bleat2(char *func,char *arg1,char *arg2) { if(!silent && ErB) { size_t ErNln; ErNln = strlen(func) + strlen(arg1) + strlen(arg2) + 3; ErBln += ErNln; /* 2 brackets and a comma */ if((ErN = adjust_ErB())) { extern int errno; Cat(Cat(Cat(Cat(Cat(Cpy(ErN,func),"("),arg1),","),arg2),")"); if(errno) perror(ErB); else fprintf(stderr,"%s\n",ErN); } else { perror(progname); } ErBln -= ErNln; } return; } #ifdef __NO_TRACING /* Null Macros */ #define TrEnt #define TrExi #else /* not __NO_TRACING = tracing enabled! */ /* Subroutine entry - must precede first executable statement in routine */ #define TrEnt(name) static char srnm[] = name; \ static size_t snsz = sizeof(srnm); \ ErBln += snsz; \ if((ErN = adjust_ErB())) { \ Cat(Cpy(ErN,srnm),colst); \ } \ ErN += snsz /* Subroutine exit - must precede every return statement if TrEnt is present */ #define TrExi ErBln -= snsz; ErN = ErB + ErBln; \ if(ErB) *ErN = NULLC #endif /* __NO_TRACING */ /* square integer root */ /* from: Jack W.Crenshaw, "Programmer's Toolbox" */ /* http://www.embedded.com/98/9802fe2.htm */ /* flott isse ja gerade nicht. Dafür braucht die libm nicht */ /* geladen zu werden - is doch auch was wert. */ #define NBSHL 2 #define NBSHR (NBULG - NBSHL) u_short squirt(uint32_t arg) { _rg uint32_t rem = 0, root = 0; _rg int i,nbshr = NBSHR,nbshl = NBSHL; for(i=0;i> nbshr)); arg <<= nbshl; if(++root <= rem){ rem -= root; root++; } else root--; } return (u_short)(root >> 1); } #ifdef __APPLE__ int _have_altivec = TRUE; /* bis wir mehr wissen */ char flac = USCOR; #else int _have_altivec = FALSE; /* ...da wissen wir's */ char flac = SPACE; #endif u_char *fish_key = NULL; /* arbitrary length key */ size_t fikl = 0; /**

swac

- Feld kopieren und dabei Bytes vertauschen (swap and copy) */ /*&

Kurzbeschreibung:

*-
void *swac(to,from,ln,size)
 *-void *to,*from; size_t ln,size;
*&swac kopiert das Feld from in das Feld to und vertauscht dabei die *-Bytes, sodaß aus "BIG ENDIAN" "LITTLE ENDIAN" wird und umgekehrt. *& from und to können vom Typ short oder long sein; die Wortlänge wird mit *& size spezifiziert. Es werden ln Worte bearbeitet. *-Wird to == NULL übergeben, setzt swac to = from. *&Der Zeiger auf den Zielpuffer wird als Funktionswert zurückgegeben. Falls ** sz einen ungeeigneten Wert hat, gibt swac NULL zurück. *&Es werden Wortlängen ( sz) 1 (char), 2 (short), 4 (long) und 8 (long long) *&unterstützt. Wenn sz nicht mit dem Variablentyp übereinstimmt, **auf den from und to zeigen, gibt es Alignment-Fehler. **Benötigte Unterprogramme: memcpy (libc) */ void *swac(_rg void *to,_rg void *from,_rg size_t ln,_rg size_t sz) { _rg uint16_t *fps,*tps,*tep = NULL,us; _rg uint32_t *tpl = NULL,*fpl = NULL,ul; uint64_t *tpll = NULL; to = (to)?to:from; switch((int)sz) { case (sizeof(int8_t)): /* Byte-Felder werden natürlich nicht geswapt */ if(to != from) memcpy(to,from,ln); /* ...aber kopiert */ break; case (sizeof(int64_t)): tpll = (uint64_t *)to; tep = (uint16_t *)(tpll + ln); /* longlong- */ case (sizeof(int32_t)): tpl = (uint32_t *)to; tep = (tep)?tep:(uint16_t *)(tpl + ln); /* long- */ case (sizeof(int16_t)): tps = (uint16_t *)to; tep = (tep)?tep:(tps + ln); /* short-aligned */ fps = (uint16_t *)from; /* 1. Durchgang: Kopieren und Bytes in den shorts vertauschen */ switch((tep - tps) % 8) { /* Herr Duff läßt grüßen */ case 0: do { us = *(fps++); *(tps++) = ((us<>NBBY)); case 7: us = *(fps++); *(tps++) = ((us<>NBBY)); case 6: us = *(fps++); *(tps++) = ((us<>NBBY)); case 5: us = *(fps++); *(tps++) = ((us<>NBBY)); case 4: us = *(fps++); *(tps++) = ((us<>NBBY)); case 3: us = *(fps++); *(tps++) = ((us<>NBBY)); case 2: us = *(fps++); *(tps++) = ((us<>NBBY)); case 1: us = *(fps++); *(tps++) = ((us<>NBBY)); } while(tps < tep); } if(!tpl) break; /* short-Feld ? Dann sind wir fertig */ /* 2. Durchgang: Shorts in den Ints an Ort und Stelle vertauschen */ switch(((uint32_t *)tep - tpl) % 8) { case 0: do { ul = *tpl; *(tpl++) = ((ul<>NBUSH)); case 7: ul = *tpl; *(tpl++) = ((ul<>NBUSH)); case 6: ul = *tpl; *(tpl++) = ((ul<>NBUSH)); case 5: ul = *tpl; *(tpl++) = ((ul<>NBUSH)); case 4: ul = *tpl; *(tpl++) = ((ul<>NBUSH)); case 3: ul = *tpl; *(tpl++) = ((ul<>NBUSH)); case 2: ul = *tpl; *(tpl++) = ((ul<>NBUSH)); case 1: ul = *tpl; *(tpl++) = ((ul<>NBUSH)); } while(tpl < (uint32_t *)tep); } if(!tpll) break; /* 3. Durchgang: Longs in Llongs vertauschen - geht mit 32-bit Registern */ tpl = (uint32_t *)to; fpl = tpl; switch(((uint64_t *)tep - tpll) % 8) { case 0: do { ul = *(++tpl); *(tpl++) = *(fpl); *(fpl) = ul; fpl = tpl; case 7: ul = *(++tpl); *(tpl++) = *(fpl); *(fpl) = ul; fpl = tpl; case 6: ul = *(++tpl); *(tpl++) = *(fpl); *(fpl) = ul; fpl = tpl; case 5: ul = *(++tpl); *(tpl++) = *(fpl); *(fpl) = ul; fpl = tpl; case 4: ul = *(++tpl); *(tpl++) = *(fpl); *(fpl) = ul; fpl = tpl; case 3: ul = *(++tpl); *(tpl++) = *(fpl); *(fpl) = ul; fpl = tpl; case 2: ul = *(++tpl); *(tpl++) = *(fpl); *(fpl) = ul; fpl = tpl; case 1: ul = *(++tpl); *(tpl++) = *(fpl); *(fpl) = ul; fpl = tpl; } while(tpl < (uint32_t *)tep); } default: to = NULL; } return(to); } /**smp_rand - simpler one-shot Zufallsgenerator */ /* Vorgeschichte: */ /* drz 24.7.2007 Prozeß-Id und noch ein paar Feinheiten */ /* drz 22.9.2004 Sekunden bit-reversiert verwenden */ /* drz 20.4.2004 aus _spas mit reingenommen */ /* neu geschrieben : drz 19.4.2004 */ /*&

Kurzbeschreibung:

*-
int32_t smp_rand()
*&smp_rand liefert bei jedem Aufruf eine andere 32-bit-Integer-Zahl zurück, *&vorausgesetzt, der letzte Aufruf liegt um mindestens einen System Clock Tick *&zurück. Da es nur einmal pro Programmstart aufgerufen wird, kann man getrost *-davon ausgehen. */ /*-Benötigte Unterprogramme: gettimeofday,getpid (libc); bitrv (hier) *- */ uint32_t bitrv(_rg uint32_t bits) { _rg uint32_t rvbits; _rg int ib; ib = NBBY * sizeof(uint32_t); rvbits = (bits & 1); /* prime the pump */ while(--ib > 0) { rvbits <<= 1; bits >>= 1; rvbits |= (bits & 1); } return(rvbits); } #define ONE_SEC 1000000 /* microseconds */ #define ONE_TICK (int)(ONE_SEC/sysconf(_SC_CLK_TCK)+.5) /* ditto */ int32_t smp_rand() { static uint32_t repeat = 0; /* wenn's öfter benutzt wird */ int32_t sex,tix,pervert; Timeval Now; /* Uhrzeit ablesen...*/ gettimeofday(&Now, #ifndef __SVR4 (struct timezone *) #endif NULL); /* Wie sich zeigt, liefert die Verwendung der Sekunden allein nicht die volle */ /* Entropie. Darum verwenden wir anstatt der Sekunden diejenige Zahl, welche */ /* durch Bit-Reversion aus den Sekunden entsteht. Damit steht tatsächlich der */ /* ganze 32-Bit Zahlenraum zur Verfügung */ sex = Now.tv_sec + repeat++; /* Sekunden plus Wiederholungszähler */ pervert = bitrv(sex) + getpid(); /* umstülpen, Prozeß-Id dazu */ pervert ^= sex; /* das auch noch... */ /* Das lebt natürlich davon, dass der Unix-Zeitstempel */ /* derzeit (und fast immer) einen Wert > 32767 hat */ tix = Now.tv_usec/(ONE_TICK); /* Ticks rausholen... */ /*...rev. Sekunden durch Ticks dividieren (+2, damits nie Null oder 1 wird) */ /* und von den bitreversierten Sekunden subtrahieren */ return(pervert - (pervert/(tix + 2))); } /* --------------------------------------------------------*/ /* wo andere den Systemdienst "random()" benutzen, verwen- */ /* den wir unseren eigenen Pseudo-Zufallszahlen-Generator: */ /* --------------------------------------------------------*/ /**

r_init

- Initialization: select sequence and range */ /* */ /* Vorgeschichte: */ /* drz 22.5.2004 unfreeze and fix r_init code for little/big endian thing */ /* drz 9.12.2004 FREEZE r_init and r_vect code */ /* drz 6.12.2004 weak key behavior improved */ /* drz 1.11.2004 vector code cured of alignment trouble */ /* drz 29.4.2004 runtime AltiVec detection (sort of) */ /* drz 26.4.2004 Zufallsgenerator mit AltiVec */ /* drz 24.4.2004 völlig neu programmiert */ /* drz 20.4.2004 aus libr_ abgekupfert */ /*&

Kurzbeschreibung:

*-
int32_t r_init(int32_t seed)
*&The ring buffer rbuff is allocated and initialized with a sequence *&of pseudo-random numbers. These numbers are created from seed with *&a modified multiplicative convergence algorithm. Then the bit columns of the *-ring buffer are orthogonalized. *&The choice of seed influences the result space of the random *&number generator proper: seed < 0 enables the generation of *&signed 32-bit numbers, seed >= 0 will only generate positive *-numbers. *&Each of the possible 2^32-1 values for seed determines a distinct, *&reproducible sequence of pseudo-random numbers. The sequences have a period *-length of 2^250-1 (about 10^75), which is good enough for the shredder. **And it is good enough for the crypter. So there. *&Der Ringpuffer rbuff wird bereitgestellt und mit einer Folge von *&Pseudo-Zufallszahlen vorbesetzt. Diese Zahlen werden aus seed mit *&Hilfe eines modifizierten Multiplikativ-Konvergenz-Verfahrens gewonnen. Dann *&wird dafür gesorgt, daß die Spalten des Ringpuffers bitweise orthogonal sind. *&Die Auswahl von seed beeinflußt den Wertebereich der später *&ausgeführten eigentlichen Zufallsgeneratoren: für seed < 0 *&liefern sie 32bit-Zahlen mit und ohne Vorzeichenbit (positiv und negativ), *-für seed >= 0 nur 32bit-Zahlen ohne Vorzeichenbit (positiv). *&Jeder der 2^32-1 möglichen Werte für seed bestimmt eine eigene, *&reproduzierbare, von allen anderen möglichen Sequenzen unterschiedliche *&Pseudo-Zufallszahlen-Sequenz. *&Die Periode einer Sequenz ist 2^250-1 (ungefähr 10^75 oder eine **Duodezilliarde), was für den Shredder ausreichend gut ist. *&

Zusätzliche Schnittstelle:

*-
void r_kimp(u_char key,size_t klen)
*&r_kimp importiert den Schlüssel key der Länge klen Bytes. Dieser **modifiziert die Initialisierung des Ringpuffers in reproduzierbarer Weise. */ /* And now, a few words on alignment: */ /* If the ring buffer length is not divisible by 4, then the first wraparound */ /* destroys the vector alignment of the ring buffer or result vector pointers.*/ /* The ring buffer length could be any multiple of 4 greater than 250, but we */ /* prefer approximately equal distances between current, tap1 and tap2 wraps. */ #define RBFLEN 376 /* fair compromise length */ #define TAP103 103 /* first ring buffer tap */ #define TAP250 250 /* second buffer tap */ #define OPTINC 133 /* optimal distribution of orthogonalization cells */ /* 7^5 would make good initial values (see Kirkpatrick/Stoll paper) */ #define CROOKED 16811 /* - but this one's prime, so it must be better ;-> */ #define McLEOD 1 /* there can only be one */ #ifdef __APPLE__ /* req'd for AltiVec code */ #define NOWRAP 0 #define T1WRAP 5 #define T2WRAP 6 #define CUWRAP VEC_INTS #define T1WRAL ((TAP103 - 1) % VEC_INTS) + VEC_INTS /* tap 1 wrap alert */ #define T2WRAL ((TAP250 - 1) % VEC_INTS) + VEC_INTS /* tap 2 wrap alert */ #endif /* __APPLE__ */ /* ring buffer cell - the ring buffer is a singly linked list, but */ /* the structure members cu, t1 and t2 point into a vector of ints */ typedef struct st_cell { int32_t *cu; /* current cell */ int32_t *t1; /* tap 1 at cell "current - 103" */ int32_t *t2; /* tap 2 at cell "current - 250" */ #ifdef __APPLE__ int wa; /* flags wraparound in either cu or t1 or t2 */ #endif /* __APPLE__ */ int ci; /* cur index */ struct st_cell *nx; /* link pointer to next cell */ } CELL; int wipe_ring_buffer = FALSE; /* control parameter for re-initialization */ u_char *mod_key = NULL; size_t mokl = 0; /* modification key */ u_char *rot_key = NULL; /* work copy thereof */ #ifdef __APPLE__ /* prefetch control words */ uint32_t rbfpfc = 0,curpfc = 0,tp1pfc = 0,tp2pfc = 0; #endif /* __APPLE__ */ /* import modification key */ void r_kimp(u_char *key,size_t klen) { if(key && klen) { /* both parameters must not be zero */ if(!rot_key) { /* need a work copy */ if((rot_key = (u_char *)calloc(klen,sizeof(u_char)))) { mod_key = key; mokl = klen; } else { mod_key = NULL; mokl = 0; } } else { if((rot_key = (u_char *)realloc((void *)rot_key,klen))) { mod_key = key; mokl = klen; } else { mod_key = NULL; mokl = 0; } } } else { /* if any one parameter is zero, we do not use a key */ ifree(rot_key); mod_key = NULL; mokl = 0; } return; } /* Vektor von Bytes um 1 Bit nach rechts rotieren */ void bytrot(u_char *bytes,size_t numb) { _rg u_char *pbt,*pbn,*pba,*pbe; u_char bt,bn,lsb; pba = bytes; pbe = pba + numb; pbn = pba; pbt = pbn++; bt = *pbt; lsb = bt & 1; do { bn = *pbn; bt >>= 1; bt = (bn & 1)?(bt | 0x80):(bt & 0x7f); *pbt = bt; bt = bn; pbt = pbn++; } while(pbn < pbe); bt >>= 1; bt = (lsb)?(bt | 0x80):(bt & 0x7f); *pbt = bt; return; } /* Vektor von Worten um 1 Bit nach rechts rotieren */ void wrdrot(uint32_t *words,size_t numw) { _rg uint32_t *pwt,*pwn,*pwa,*pwe; _rg uint32_t wt,wn,lsw; pwa = words; pwe = pwa + numw; pwn = pwa; pwt = pwn++; wt = *pwt; lsw = wt & 1; do { wn = *pwn; wt >>= 1; wt = (wn & 1)?(wt | 0x80000000):(wt & 0x7fffffff); *pwt = wt; wt = wn; pwt = pwn++; } while(pwn < pwe); wt >>= 1; wt = (lsw)?(wt | 0x80000000):(wt & 0x7fffffff); *pwt = wt; return; } #ifdef __APPLE__ /* generate optimized prefetch control word */ /* make it "a few blocks of a few vectors" */ /* 0000 0000 0111 0000 0000 0100 0011 1000 */ /* ----block stride----|blk count|blsize||| */ #define _BLKCNTSZ 8 #define _BLKSZESZ 5 #define _NOTUSDSZ 3 #define _MAX_BLSZ 32 /* these are taken from the */ #define _MAX_BLCT 256 /* AltiVec programming guide */ /* this is a fairly elaborate bit of code for such a menial task, but we */ /* use it off-line, i.e. only once per program invocation, so bugger all */ uint32_t pfctrl(size_t num,size_t size) { uint32_t cw; /* result goes here */ int best = _MAX_BLSZ * _MAX_BLCT; /* smallest overshoot */ int fmrt = _MAX_BLSZ + _MAX_BLCT; /* figure of merit */ int blsz = 1,blct = 1; /* block size,count */ int done = FALSE; /* termination flag */ int is,targ,mblsz; targ = num * size; /* requested size */ /* we count down from the biggest block size required */ mblsz = howmany(targ,VEC_BYTS); if(mblsz > _MAX_BLSZ) mblsz = _MAX_BLSZ; for(is=mblsz;is>0;--is) { int ic,mblct,blsb; blsb = is * VEC_BYTS; /* block size in bytes */ /* we need only count down from the smallest block count that yields */ /* a total bigger than the target size, given the block size */ mblct = ((targ - 1) / blsb) + 1; /* but not bigger than the maximum allowed count */ if(mblct > _MAX_BLCT) mblct = _MAX_BLCT; for(ic=mblct;ic>0;--ic) { int totl; if((totl = (ic * blsb)) < targ) break; /* no sense going on */ else { int diff; /* prefer bigger chunks */ if(((diff = (totl - targ)) <= best) && ((ic + is) <= fmrt)) { /* if we're here, it won't get any better */ if((ic == blsz) && (is == blct)) done = TRUE; /* keep this setting */ blsz = is; blct = ic; best = diff; fmrt = is + ic; } } if(done) break; } if(done) break; } /* pack results into control word */ cw = blsz * VEC_BYTS; /* block stride */ cw <<= _BLKCNTSZ; cw |= (blct & (_MAX_BLCT-1)); /* block count */ cw <<= _BLKSZESZ; cw |= (blsz & (_MAX_BLSZ-1)); /* block size */ cw <<= _NOTUSDSZ; return(cw); } #endif /* __APPLE__ */ /* cyclic redundancy checksum with words - very very incompatible...*/ uint32_t w_crc(_rg uint32_t *wrds,_rg size_t nwrd) { _rg uint64_t crc = 0; _rg size_t wix; for(wix=0;wix>= 1; /* shift right and */ if(lsb) crc |= 0x8000000000000000ULL; /* copy LSbit -> MSbit */ else crc &= 0x7fffffffffffffffULL; /* = rotate crc */ } /* XOR of MSword and LSword */ return((uint32_t)(crc >> (NBBY * sizeof(uint32_t))) ^ (uint32_t)crc); } /* we need to make a few preparations */ CELL **r_init(_rg int32_t seed) /* use seed to select sequence */ { /* vectors need to be 16-byte aligned, have malloc make it so */ static int32_t *lvect = NULL; static CELL *rbuff = NULL; /* pcurr is the externally visible reference to the ring buffer */ static CELL *pcurr = NULL; TrEnt("r_init"); /* is the ring buffer set up good and proper ? */ if(!pcurr || wipe_ring_buffer) { /* Not Yet */ size_t ncells,cellsz; /* need 16-byte aligned adresses */ ncells = howmany((RBFLEN * sizeof(int32_t)),VEC_BYTS); cellsz = (VEC_BYTS); /* just this once */ if(!rbuff) { if(!(rbuff = (CELL *)calloc(nxpw2(RBFLEN),sizeof(CELL)))) { bleat(canm,"rbuff"); } } if(lvect) { _rg int32_t *ple,*plv; /* the buffer vector proper */ plv = lvect; ple = plv + RBFLEN; /* hide evidence of previous run */ do { *(plv++) = ZOMBIE; } while(plv < ple); } else if(!(lvect = (int32_t *)calloc(nxpw2(ncells),cellsz))) { bleat(canm,"lvect"); } if(lvect) { int weak_key,ir,im1,im2,aind,alof; _rg CELL *cell; for(ir=0;ir=0;--ir) { _rg CELL *this; #ifdef __APPLE__ int iw; #endif /* __APPLE__ */ this = &(rbuff[ir]); --im1; --im2; /* cannot handle wraparound in vector loop easily, so flag affected zones: */ if(im1 < 0) { /* "tap" wraps occur somewhere in the middle */ im1 += RBFLEN; #ifdef __APPLE__ /* tap wraparound is nasty, because vec_ldua (load unaligned) must */ /* load TWO consecutive vectors: we have to start one vector early */ for(iw=0;iwwa = T1WRAP; #endif /* __APPLE__ */ } else if(im2 < 0) { im2 += RBFLEN; #ifdef __APPLE__ for(iw=0;iwwa = T2WRAP; } else if(ir == (RBFLEN-1)) { int iw; /* countdown from 4 */ for(iw=0;iwwa = iw + 1; #endif } this->cu = &(lvect[ir]); /* points to current */ this->t1 = &(lvect[im1]); /* points to current - 103 */ this->t2 = &(lvect[im2]); /* points to current - 250 */ this->nx = cell; cell = this; } /* now that the ring buffer framework is allocated and linked */ /* ------------------------------------------------------------*/ /* The sequence of operations performed here is crucial to the */ /* particular initialization of the prng. Any change will make */ /* random numbers made with any earlier version irreproducible.*/ /* ------------------------------------------------------------*/ /* Fill the ring with low-quality pseudo-randoms, using */ /* a multiplicative-convergence method (whatever that is) */ do { _rg int incr,*rbp; uint64_t bits; int64_t pieces; weak_key = FALSE; /* seed < 0 generates both signs */ bits = (uint64_t)((seed < 0)?MAXUINT:MAXINT); /* normal case: use seed as it is and work backwards from last cell */ if(seed) { pieces = (int64_t)seed; incr = -1; cell = &(rbuff[RBFLEN-1]); } else { /* a zero seed would fill the ring buffer with all zeroes, generating a very */ /* undesirable sequence of all zeroes, which is a cryptograpically weak key. */ /* Therefore, we substitute CROOKED for the seed,but in order to be distinct */ /* from seed == CROOKED, we start at the 1st cell and fill in opposite order.*/ pieces = (int64_t)CROOKED; incr = 1; cell = &(rbuff[0]); } ir = RBFLEN; while(ir-- > 0) { /* go through all cells: */ pieces *= CROOKED; /* multiply */ /* CROOKED is odd - */ pieces >>=1; /* shift right to improve lsb sequence */ pieces &= bits; /* mask to 32 bits */ *(cell->cu) = (int32_t)pieces; cell += incr; /* save */ /* The operations above might _eventually_ yield zero, which would cause the */ /* rest of the ring buffer to be all zeroes. We cannot allow this to happen. */ if(!pieces) pieces = 1; /* very unlikely, but... */ } pcurr = cell; /* Now here's the twist: If there is a secret key, we use it to modify the */ /* initialized ring buffer. This allows to use ranges of the pseudo-random */ /* number sequence that lie outside the regions accessed with our standard */ /* initialization procedure. ENIGMA on steroids. */ if(mod_key) { /* a key which is _almost_ correct would decode _almost_ correctly. */ /* Therefore, we try to change the initialization drastically, even */ /* when the key is just altered ever so slightly: */ /* - we may safely assume that the key length is divisible by 4, so: */ seed ^= w_crc((uint32_t *)mod_key,mokl/sizeof(uint32_t)); } /* distill secret key into one int */ /* start index = reversed seed modulo ring buffer length */ if((aind = (bitrv(seed) % RBFLEN)) < 0) aind = -aind; /* we tweak things so that we finish on a 16-byte boundary */ alof = VEC_INTS - (TAP250 % VEC_INTS); /* There's a catch: only the 250 ring buffer cells between current and tap2 */ /* contribute to the generator. The rest is just there to increase entropy. */ /* We move the selected range of ring buffer cells to the buffer start, but */ /* in such a way that the the moved selection's end ends up 16-byte aligned.*/ if(alof) { int32_t scr[TAP250]; int eind; eind = (aind + TAP250) % RBFLEN; rbp = scr; if(eind > aind) { for(ir=aind;ir= pre) { prk = pra; /* this requires that the key length be divisible by 4! */ /* - but get_fish_key takes care of that */ wrdrot((uint32_t *)prk,mokl/sizeof(uint32_t)); } } } else { /* key is longer than ring buffer */ while(prk < pre) { /* wrap round ring buffer until */ *plv++ ^= *prk++; /* key is exhausted */ if(plv >= pve) { plv = pva; wrdrot((uint32_t *)plv,lvcl/sizeof(uint32_t)); } } } /* picture this : by a very very unlucky coincidence, the secret key is very */ /* very similar to the ring buffer content, which causes lots of zeroes there */ zeroes = McLEOD; puv = (uint32_t *)pva; pue = (uint32_t *)pve; do { if(!zeroes) break; if(!(*(puv++))) --zeroes; /* take one down, pass it around */ } while(puv < pue); if(!zeroes) { /* found too many zeroes */ pieces = (int64_t)seed + 1; /* increment seed */ pieces &= bits; seed = (int32_t)pieces; /* modulo 2^32 */ weak_key = TRUE; /* lather, rinse */ } } } while(weak_key); /* and repeat until clean */ /* OK, we have a full ring buffer. As a last refinement, orthogonalize */ /* the ring's bit columns to ensure the full advertised period length. */ { _rg int32_t bits,pieces; CELL *rba,*rbe; rba = &(rbuff[alof]); rbe = &(rbuff[alof + TAP250]); cell = rba + aind; if(cell >= rbe) cell -= TAP250; bits = 0; pieces = 1; *cell->cu = bits; ir = NBBY * sizeof(int32_t); /* we have to take care of 32 cells */ while(--ir > 0) { _rg uint32_t hul; cell += OPTINC; /* optimized increment */ if(cell >= rbe) cell -= TAP250; bits += pieces; /* mask with i msb's off */ hul = *cell->cu; hul &= bits; /* clear msb */ hul |= pieces; /* set highest lsb */ *cell->cu = hul; pieces <<= 1; /* bit (32 - i) on */ } /* and we start at the first unused cell - which must be 16-byte aligned */ pcurr = rbe; } #ifdef __APPLE__ /* last action: test for AltiVec capability in CPU */ /* borrowed from ffmpeg altivec detection code. this code also appears on Apple's AltiVec pages. - only works with Darwin. This I can live with. */ { int sels[2] = {CTL_HW, HW_VECTORUNIT}; size_t len = sizeof(_have_altivec); /* if this doesn't return 0, we assume no AltiVec */ if(sysctl(sels,2,&_have_altivec,&len,NULL,0)) { _have_altivec = FALSE; } else { /* vector code: define prefetch control words */ rbfpfc = pfctrl(RBFLEN+1,sizeof(CELL)); curpfc = pfctrl(TAP103+1,sizeof(uint32_t)); tp1pfc = pfctrl(TAP250-TAP103+1,sizeof(uint32_t)); tp2pfc = pfctrl(RBFLEN-TAP250+1,sizeof(uint32_t)); } } #endif /* __APPLE__ */ } if(wipe_ring_buffer) wipe_ring_buffer = FALSE; } TrExi; /* return address of pointer to current, because */ /* r_vect needs to update pointer to current */ return(&pcurr); } /* re-initialize random number generator with new seed */ CELL **r_reset(int32_t seed) { wipe_ring_buffer = TRUE; return(r_init(seed)); } /**

r_vect

- make a vector of random bytes */ /*-*/ /* Vorgeschichte: */ /* drz 29. Oktober 2004, Altivec Code in r_vect überarbeitet */ /* drz 26. April 2004, r_vect mit Altivec */ /* drz 22. April 2004, r_vect etwas gestrafft */ /* drz 22. April 2004, Tippfehlerteufel exorziert */ /* drz Oct 11 1994 renamed from vran */ /* drz Mar 8 1993 no more u_char externals */ /*&

Kurzbeschreibung:

*-
int32_t r_vect(u_char *vect,int leng)
*&r_vect füllt einen Puffer vect mit leng pseudo-zufälligen Bytes. Die Zahlen *&werden als 32bit-Integers erzeugt, deshalb muß der Puffer auf einer *-Wortgrenze beginnen. **NOTA BENE: r_vect ist NICHT thread-safe ! *&Der verwendete Algorithmus ist eine Variante des "distributed feedback" *&Generators: Die Pseudo-Zufallszahl entsteht aus einer XOR-Operation mit den *&um 103 bzw. 250 Schritte zurückliegenden Pseudo-Zufallszahlen. Mit den *&hier verwendeten Offsets 103 und 250 verhält sich der Generator besonders *&günstig bezüglich Periodenlänge und statistischer Eigenschaften der erzeugten **Zahlenfolge. *&Diese Implementierung verwendet auf der Powerpc-Architektur die ggf. *&vorhandenen Vektorregister, was die Geschwindigkeit merklich erhöht. *&Allerdings muß dann vect auf einer durch 16 teilbaren **Adresse beginnen. Es gibt überhaupt gut Spaß mit Alignment :-P */ #ifdef __APPLE__ /* load unaligned data into vector register */ /* snarfed from Apple's Altivec Tutorials - I could not possibly have worked */ /* this out by myself. I didn't grasp the logic behind vec_lvsl and vec_perm */ /* before having seen the sample code. There exist things that are not meant */ /* to be understood by mere mortals. */ #define vec_ldua(v_rs,ua) /* do this with a macro: */ \ { vector unsigned char v_ms,v_ls,v_mk; \ v_ms = vec_ld(0,(unsigned char *)ua); /* most significant quadword */ \ v_ls = vec_ld(sizeof(v_ls),(unsigned char *)ua); /* ...least */ \ v_mk = vec_lvsl(0,(unsigned char *)ua); /* permute mask */ \ v_rs = (vector signed int)vec_perm(v_ms,v_ls,v_mk); \ } #endif /* __APPLE__ */ /* macro for scalar random numbers */ #define rascal(number,cell,buffer) \ number = *cell->t1^*cell->t2; \ *cell->cu = number; \ *(buffer++) = number; \ cell = cell->nx; int32_t r_vect(u_char *vect,int leng) { CELL **pcc; /* pointer to current cell pointer */ TrEnt("r_vect"); /* make sure ring buffer is initialized */ if((pcc = r_init(-1))) { _rg CELL *pc; _rg int32_t *cur; int nints,nbyts; _rg uint32_t *ip,rn; nints = leng / sizeof(int32_t); nbyts = leng % sizeof(int32_t); ip = (uint32_t *)vect; /* buffer index */ pc = *pcc; /* local copy of ring buffer index */ if(nints) { _rg uint32_t *ie; #ifdef __APPLE__ /* now that both cu and ta point into a vector, we can use AltiVec... */ int nvect; ie = ip + nints; /* buffer end address (expressed in 32-bit ints) */ /* do we need the vectors (does not make sense for short arrays) ? */ if((nvect = nints / VEC_INTS)) { /* AltiVec registers: */ if(_have_altivec) { vector signed int v_t1,v_t2,v_rn; _rg uint32_t *ve,rbc,cuc,t1c,t2c; rbc = rbfpfc; cuc = curpfc; t1c = tp1pfc; t2c = tp2pfc; ve = ip + (VEC_INTS * nvect); /* buffer end address in vectors */ while(ip < ve) { _rg int wa; _rg int32_t *tp1,*tp2; wa = pc->wa; switch(wa) { case CUWRAP: /* we are one quadword before buffer end */ case 0: /* almost always: go AltiVec */ /* Here's a nice one: tap addresses are _NEVER_ 16-byte aligned. We need the */ /* vector macro defined above to load unaligned data into a vector register. */ vec_ldua(v_t1,pc->t1); /* get tap1 vector */ vec_ldua(v_t2,pc->t2); /* get tap2 vector */ v_rn = vec_xor(v_t1,v_t2); /* rn = t1^t2 */ vec_st(v_rn,0,pc->cu); /* update ring buffer */ vec_st(v_rn,0,(int32_t *)ip); /* copy result to user buffer */ if(!wa) { /* no wrap, no nothing */ pc += VEC_INTS; } else { /* wraparound is from the last of the 4 ints: */ pc += (VEC_INTS-1); pc = pc->nx; /* To ensure that the ring buffer is cache resident, we prefetch */ /* a chunk of the buffer every time we pass a wraparound point. */ /* - well, we hope that prefetch is faster than the algorithm itself - - - */ /* prefetch CELL vector */ vec_dst((int32_t *)pc,rbc,0); /* prefetch buffer vector, from start to tap1 (103) */ vec_dst(pc->cu,cuc,1); } ip += VEC_INTS; /* increment target */ break; case 3: rascal(rn,pc,ip); /* just in case... */ case 2: rascal(rn,pc,ip); case 1: rascal(rn,pc,ip); while((pc->ci) % VEC_INTS) { rascal(rn,pc,ip); } break; case T1WRAP: /* go scalar until tap1 wraps around */ do { cur = pc->cu; tp1 = pc->t1; rn = *tp1^*pc->t2; *cur = rn; *(ip++) = rn; pc = pc->nx; } while(pc->t1 > tp1); /* we're not done yet - go on until current is 16-byte aligned */ while((pc->ci) % VEC_INTS) { rascal(rn,pc,ip); } /* prefetch buffer vector, from tap1 to tap2 */ vec_dst(pc->cu,t1c,2); break; case T2WRAP: /* go scalar until tap2 wraps around */ do { cur = pc->cu; tp2 = pc->t2; rn = *pc->t1^*tp2; *cur = rn; *(ip++) = rn; pc = pc->nx; } while(pc->t2 > tp2); while((pc->ci) % VEC_INTS) { rascal(rn,pc,ip); } /* this is where RBFLEN & TAP2 meet prefetch: */ /* prefetch buffer vector, tap2 to buffer end */ vec_dst(pc->cu,t2c,3); break; default: ; } } /* handle the non-vector (modulo 16 bytes) remainder */ while(ip < ie) { rascal(rn,pc,ip); } } else { /* scalar - maybe we sit on a PPC 603 ;-) */ switch(nints % 8) { case 0: do { rascal(rn,pc,ip); case 7: rascal(rn,pc,ip); case 6: rascal(rn,pc,ip); case 5: rascal(rn,pc,ip); case 4: rascal(rn,pc,ip); case 3: rascal(rn,pc,ip); case 2: rascal(rn,pc,ip); case 1: rascal(rn,pc,ip); } while(ip < ie); } } } #else /* not __APPLE__ */ /* scalar implementation - much much simpler */ ie = ip + nints; switch(nints % 8) { case 0: do { rascal(rn,pc,ip); case 7: rascal(rn,pc,ip); case 6: rascal(rn,pc,ip); case 5: rascal(rn,pc,ip); case 4: rascal(rn,pc,ip); case 3: rascal(rn,pc,ip); case 2: rascal(rn,pc,ip); case 1: rascal(rn,pc,ip); } while(ip < ie); } #endif /* __APPLE__ */ } /* this is for users who like strange-sized arrays */ if(nbyts) { _rg uint32_t msk; rn = *pc->t1^*pc->t2; *pc->cu = rn; pc = pc->nx; switch(nbyts) { case 1: msk = 0xff000000; break; case 2: msk = 0xffff0000; break; case 3: msk = 0xffffff00; break; default: msk = 0; } /* ENDIAN */ msk = htonl(msk); /* but honestly */ *ip &= ~msk; *ip |= (rn & msk); /* a-hem. */ /* shame on me - instead of rn, we had an uninitialized number here for ages. */ } /* align ring buffer pointer for next run */ while((pc->ci) % VEC_INTS) { rn = *pc->t1^*pc->t2; *pc->cu = rn; pc = pc->nx; } *pcc = pc; /* update ring buffer pointer */ } else leng = -1; /* means buffer is unchanged */ TrExi; return(leng); } /* ===========================================================================*/ /* from here, it is the grim shredder */ /* ===========================================================================*/ /* which overwrites files with a sequence of pre-determined (Gutmann) and */ /* random patterns. In reversible mode, the files are encrypted (sort of). */ #define MINCS 4 /* as of today, this is a smallish cache */ #define MAXCS 32 /* but they come bigger, too */ #define KILOB 1024 /* binary thousand */ #define MEGAB (KILOB * KILOB) /* binary million */ #define GIGAB (KILOB * MEGAB) /* binary billion */ #define WRBLB (MINCS * MEGAB) /* bytes in write buffer */ #define wrblb(cs) (cs * MEGAB) #define WRBLL howmany(WRBLB,sizeof(int32_t)) /* ...and 4-byte words */ #define wrbll(bs) howmany(bs,sizeof(int32_t)) #define PATLL 3 /* length of bit patterns in 4-byte words */ #define RBYT 0x17 /* Lärm & Qualm */ #define BUFRM (WRBLL % PATLL) /* buf size not divisible by pattern length */ #define CHNUSD 5 /* stdin,stdout,stderr,libc et al. */ #ifdef __APPLE__ #define MAXNNOD (NOFILE-CHNUSD) /* bis zum Anschlag */ #else /* not __APPLE__ */ #define MAXNNOD (OPEN_MAX-CHNUSD) #endif /* __APPLE__ */ /* this is the biggest positive 64-bit integer */ #define LUDICROUS (off_t)(((uint64_t)(~((off_t)0))) >> 1) #define NDINSH 3 /* we shred directory names 3 times */ #ifdef __APPLE__ #define VEC_PATLL (VEC_INTS) * PATLL #define VEC_PATLB (VEC_BYTS) * PATLL #define VEC_WRBLL (((WRBLL - 1)/VEC_INTS) + 1) #else /* not __APPLE__ */ #define VEC_PATLL PATLL #define VEC_PATLB PATLL * sizeof(int32_t) #define VEC_WRBLL WRBLL #endif /* __APPLE__ */ #define WRBLBPP ((WRBLL + VEC_PATLL + VEC_PATLL) * sizeof(int32_t)) #define ONE_MINUTE 60 #define ONE_HOUR (60*ONE_MINUTE) #define ONE_DAY (24*ONE_HOUR) #define ONE_YEAR ((365*ONE_DAY)+(6*ONE_HOUR)) /* Das ist unser Arbeitstier: */ /* Verkettete Liste für Filesystem-Objekte */ typedef struct st_nod { uint32_t nodid; /* tag */ char *path; /* full path */ char *psls; /* last slash, if any */ char *onam; /* old name */ char *nnam; /* new name */ size_t plen; /* length of full path */ size_t blen; /* length of old/new (=base) name */ size_t ecnt; /* number of entries if directory */ ino_t inon; /* inode number */ off_t fsiz; /* file size */ int ipas; /* shredding pass number */ int mchd; /* flag: mode is changed */ int ispd; /* flag: is padding file */ Timeval time[2]; /* access times for file */ uint32_t **bpat; /* shuffleable array of bit patterns */ int fild; /* file descriptor */ FILE *fist; /* corresponding stream pointer */ struct st_nod *prev; /* concatenation */ struct st_nod *next; /* pointers */ struct st_nod *drct; /* points to directory above */ #ifdef __APPLE__ int isrf; /* flag: is resource fork */ int blsz; /* block size of file device */ #endif /* __APPLE__ */ mode_t mode; /* mode of file - last because short */ } NODE; NODE *first_nod = NULL; NODE *last_node = NULL; NODE *curd_node = NULL; NODE *cuno = NULL; /* Die brauchen nicht aufzufallen */ char pafnam[] = ".padding_file"; size_t pafl = sizeof(pafnam); char podnam[] = ".Polsterdatei"; size_t podl = sizeof(podnam); char *keyenv = "_FISH_KEY"; /* optional keyfile in user's */ char keynam[] = ".fish_key"; /* current or home directory */ size_t keyl = sizeof(keynam); #define __BEGIN_ROT13_ /* Verschlüsselung EIN */ #define __CEASE_ROT13_ /* Verschlüsselung AUS */ /* inspiriert von */ /* rot13.c 06/17/96 by Paul Förster */ /* TC2-ANSI */ /* Für diesen guten Zweck erheblich aufgemotzt (Escaped Characters !) */ static char rotz[256]; static long roti = FALSE; void iniro() /* Initialisierungsroutine für rot13 */ { _rg int i; /* Die Tabelle enthält zunächst nur den ascii-Zeichensatz. */ for(i=0;ifp); /* Get 1 (one) input char */ switch(c) { /* What have we here ? */ case EOF: /* Nothing */ c = '\0'; /* EOF in the stream => EOS in the string */ case '\0': /* ...or almost */ if(!(sp->n)) break; /* Empty line: return null pointer */ case (int)'\n': /* It's a newline - we're done; allocate the buffer */ if((bp = (char *)malloc(nxpw2(((++sp->n)+1)*sizeof(char))))) { bp += sp->n; /* points to null terminator */ *(bp) = '\0'; /* null into end of buffer */ } /* if malloc fails, errno is already set appropriately */ break; default: /* If it's anything else, continue : */ ++sp->n; bp = glin(sp); /* get rest of input line and push on stack */ } if(bp) *(--bp) = (char)c; /* pop chars from stack and string 'em up */ return(bp); } #ifdef __STDC__ char *getlne(FILE *fp,char **eol) /* safe and MT-safe string input */ #else /* - returns pointer to end of line in eol */ char *getlne(fp,eol) FILE *fp; char **eol; #endif { char *rest = NULL; Strut glb; glb.n = 0; glb.fp = fp; /* Here be the works */ if((rest = glin(&glb))) { /* only if there is a place to put it */ if(eol) *eol = rest + glb.n; } else if(eol) *eol = NULL; return(rest); } /* Boyer-Moore pattern matching */ /* bmPat: Datenstruktur für Boyer-Moore-Algorithmus */ typedef struct st_bmpat { /* char *p: Muster */ char *p; /* size_t m: Länge desselben */ size_t m; /* int ci: case insensitive (Groß/klein egal)? */ int ci; /* int *o,*f,*s: occurence-, shift-Tabellen */ int *o,*f,*s; } bmPat; #ifndef max #define max(a,b) (((a)<(b))?(b):(a)) #endif #define ALPHABET 256 /* Länge der ASCII-Tabelle */ /* Preprocessing Schlechtes-Zeichen-Strategie */ /* "Occurence-Tabelle" */ static bmPat *sp_bmInitocc(bmPat *Pat) { int *o = NULL; o = (Pat->o)?o:(int *)malloc((ALPHABET)*sizeof(int)); if(o) { _rg int j; _rg size_t m; _rg u_char *p; /* o muß mit -1 vorbesetzt werden */ memset((void *)o,0xff,(ALPHABET)*sizeof(int)); m = Pat->m; p = (u_char *)(Pat->p); /* put index of last occurence into o */ for (j=0; jo = o; } else { Pat = NULL; } return(Pat); } /* Preprocessing Gutes-Ende-Strategie Fall 1: Länge des Randes */ /* "Shift-Tabelle" */ static bmPat *sp_bmInitsft1(bmPat *Pat) { _rg size_t m,mp1; _rg int *f,*s; mp1 = (Pat->m) + 1; /* wenn die Tabellen f und s nicht mit 0 initialisiert */ /* sind, gehen sp_bmInitsft1 und 2 schief */ f = (Pat->f)?Pat->f:(int *)calloc((mp1),sizeof(int)); s = (Pat->s)?Pat->s:(int *)calloc((mp1),sizeof(int)); if(f && s) { _rg char *p; _rg int i,j; p = Pat->p; j = mp1; i = j; f[--i]=j; while (i>0) { while ((jf = f; Pat->s = s; } else { Pat = NULL; } return(Pat); } bmPat *sp_bmInitsft2(bmPat *Pat) { int *f,*s; f = Pat->f; s = Pat->s; if(f && s) { size_t m; int i,j; m = Pat->m; j = f[0]; for (i=0; i<=m; i++) { if (!s[i]) s[i] = j; if (i == j) j = f[j]; } } else { Pat = NULL; } return(Pat); } bmPat *sp_bmPatdel(bmPat *Pat) { if(Pat) { ifree(Pat->p);ifree(Pat->o);ifree(Pat->f);ifree(Pat->s);nfree(Pat); } return(Pat); } bmPat *sp_bmPrep(char *pattern,int ci) { bmPat *Pat = NULL; size_t m = 0; if(pattern && (m = strlen(pattern))) { char *p; if((p = (char *)malloc(nxpw2(m+1)))) { _rg char *pp,*bp,cp; pp = pattern; bp = p; if(ci) { while((cp = *(pp++))) *(bp++) = (isupper(cp))?tolower(cp):cp; } else { while((cp = *(pp++))) *(bp++) = cp; } *bp = cp; if((Pat = (bmPat *)calloc(sizeof(bmPat),1))) { bmPat *rP; Pat->p = p; Pat->m = m; Pat->ci = ci; if((rP = sp_bmInitocc(Pat))) { /* Schlechtes Zeichen */ if((rP = sp_bmInitsft1(Pat))) { /* Gutes */ rP = sp_bmInitsft2(Pat); /* Ende */ } } if(!rP) Pat = sp_bmPatdel(Pat); /* keine Reste rumliegen lassen */ } else { nfree(p); } } } return(Pat); } char *sp_bmpatf(char *t, size_t n, bmPat *Pat) { if(t && Pat) { _rg char *p; if((p = Pat->p)) { if(*p) { _rg int nmm,mm1,*s,*o; _rg char *tpi,*tpe; nmm = (n)?(int)n:(int)strlen(t); mm1 = (int)(Pat->m) - 1; /* m - 1 */ nmm -= mm1; /* n - (m - 1) */ s = Pat->s; o = Pat->o; tpi = t; /* beginning of text */ tpe = t + nmm; /* last useful text position */ /* the distinction case in/sensitive is made just once per call... */ if(Pat->ci) { /* case insensitive? */ while (tpi < tpe) { _rg char ctij; _rg char *tpij,*ppj; ppj = p + mm1; /* end of pattern */ tpij = tpi + mm1; /* corresponding position in text */ while(ppj >= p) { /* compare characters until pattern start */ ctij = *(tpij--); /* text character */ if(isupper(ctij)) ctij = tolower(ctij); /* all to lower case */ if(*ppj != ctij) break; /* unequal? stop comparing */ --ppj; } if(ppj < p) { return(tpi); } /* all characters matched */ else { _rg int pjmp,sjp1,jmot; /* mismatch at t[i+j] */ pjmp = ppj - p; /* find shift distance */ sjp1 = s[pjmp + 1]; /* from preprocessed */ jmot = pjmp - o[ctij]; /* tables o and s */ tpi += max(sjp1,jmot); /* shift the pattern */ } } /* ...even if this means the code has to be there twice */ } else { /* case sensitive - somewhat easier */ while (tpi < tpe) { _rg char ctij; _rg char *tpij,*ppj; tpij = tpi + mm1; ppj = p + mm1; while(ppj >= p) { ctij = *(tpij--); if(*ppj != ctij) break; --ppj; } if(ppj < p) { return(tpi); } else { _rg int pjmp,sjp1,jmot; pjmp = ppj - p; sjp1 = s[pjmp + 1]; jmot = pjmp - o[ctij]; tpi += max(sjp1,jmot); } } } } else return(t); } } return(NULL); /* if there's ANYthing amiss */ } void rot_em(filnm) _rg char *filnm; { char *tmpnm = NULL; size_t tnsz; tnsz = strlen(filnm); if((tmpnm = (char *)calloc((tnsz+5),sizeof(char)))) { int err; Stat filst; FILE *inf; err = stat(filnm,&filst); /* Filedaten des Quellfiles */ if((inf = fopen(filnm,"r"))) { FILE *tmp; Cat(Cpy(tmpnm,filnm),".tmp"); /* Temp-Name */ if((tmp = fopen(tmpnm,"w+"))) { int ron = FALSE; char *eln,*lne = NULL; bmPat *begrot = NULL,*endrot = NULL; begrot = sp_bmPrep(__SpStr(__BEGIN_ROT13_),FALSE); endrot = sp_bmPrep(__SpStr(__CEASE_ROT13_),FALSE); while((lne = getlne(inf,&eln))) { size_t ll; /* Alle Zeilen lesen */ ll = eln - lne; /* Hier wird ein Switch verwendet, damit keine C-Statements rotiert werden ! */ switch(ron) { case FALSE: if(sp_bmpatf(lne,ll,begrot)) ron = TRUE; break; case TRUE: if(sp_bmpatf(lne,ll,endrot)) ron = FALSE; else rot13(lne,NULL); break; default: ron = FALSE; } fputs(lne,tmp); nfree(lne); } fclose(tmp); fclose(inf); inf = NULL; unlink(filnm); /* Altes File aus dem Weg räumen */ rename(tmpnm,filnm); /* Neues File an die Stelle */ if(!err) { Timeval filtm[2]; filtm[0].tv_sec = filst.st_atime; filtm[0].tv_usec = 0; filtm[1].tv_sec = filst.st_mtime; filtm[1].tv_usec = 0; utimes(filnm,filtm); /* des Alten schieben */ } } else { fclose(inf); inf = NULL; } if(inf) fclose(inf); } nfree(tmpnm); } return; } /* I dedicate this easter egg to Iain Menzies Banks */ int banks = FALSE; char imblabel[] = "Destructive Recall"; char *imb[] = { "Banks","Iain","IMB","Algebraist","Destructive","Recall","Nasqueron" }; size_t numimb = sizeof(imb)/sizeof(*imb); /* if the veil file name (see option " -f") is one of the above, */ /* do a Banksian "Destructive Recall" on the shredded files */ char watermoon[] = __BEGIN_ROT13_ "V jnf obea va n jngre zbba. Fbzr crbcyr, rfcrpvnyyl vgf vaunovgnagf,\ pnyyrq vg n cynarg, ohg nf vg jnf bayl n yvggyr bire gjb uhaqerq xvybzrgerf va\ qvnzrgre 'zbba' frrzf gur zber npphengr grez. Gur zbba jnf znqr ragveryl bs\ jngre, ol juvpu V zrna vg jnf n tybor gung abg bayl unq ab ynaq, ohg ab ebpx\ rvgure, n fcurer jvgu ab fbyvq pber ng nyy, whfg yvdhvq jngre, nyy gur jnl\ qbja gb gur irel prager bs gur tybor.\n Vs vg unq orra zhpu ovttre gur zbba\ jbhyq unir unq n pber bs vpr, sbe jngre, gubhtu fhccbfrqyl vapbzcerffvoyr, vf\ abg ragveryl fb, naq jvyy punatr haqre rkgerzrf bs cerffher gb orpbzr vpr.\ (Vs lbh ner hfrq gb yvivat ba n cynarg jurer vpr sybngf ba gur fhesnpr bs\ jngre, guvf frrzf bqq naq rira jebat, ohg arireguryrff vg vf gur pnfr.) Guvf\ zbba jnf abg dhvgr bs n fvmr sbe na vpr pber gb sbez, naq gurersber bar pbhyq,\ vs bar jnf fhssvpvragyl uneql, naq nqrdhngryl cebbs ntnvafg gur jngre\ cerffher, znxr bar'f jnl qbja, guebhtu gur vapernfvat jrvtug bs jngre nobir,\ gb gur irel prager bs gur zbba.\n Jurer n fgenatr guvat unccrarq.\n Sbe\ urer, ng gur irel prager bs guvf jngrel tybor, gurer frrzrq gb or ab tenivgl.\ Gurer jnf pbybffny cerffher, pregnvayl, cerffvat va sebz rirel fvqr, ohg bar\ jnf va rssrpg jrvtugyrff (ba gur bhgfvqr bs n cynarg, zbba be bgure obql,\ jngrel be abg, bar vf nyjnlf orvat chyyrq gbjneqf vgf prager; bapr ng vgf\ prager bar vf orvat chyyrq rdhnyyl va nyy qverpgvbaf), naq vaqrrq gur cerffher\ nebhaq bar jnf, sbe gur fnzr ernfba, abg dhvgr nf terng nf bar zvtug unir\ rkcrpgrq vg gb or, tvira gur znff bs jngre gung gur zbba jnf znqr hc sebz.\n\ Guvf jnf, bs pbhefr,\n\n"; __CEASE_ROT13_ size_t wamolen = sizeof(watermoon); /* ...this one to the FBI */ int spoof = FALSE; char fbilabel[] = "Carnivore Bait"; char *fbi[] = { "FBI","Carnivore","Echelon","Privacy" }; /* if the veil file name (see option " -f") is one of the above, */ /* write Echelon Trigger words over the shredded files */ size_t numfbi = sizeof(fbi)/sizeof(*fbi); char echeltrig[] = __BEGIN_ROT13_ "50ZM RXZF MAV1 Zvfnjn ZLX AFF z118 RSS Zncyr GEJ Fngryyvgr cubarf BEAY QQCF \ Pbasvpxre fglcunagr Puborgfh Funzve Sheolf ANVPP eubfgf Rarzl bs gur Fgngr HNI \ QWP Pneaviber Npgvir K Zrqpb Pelcgb NT JVATF PVB Yhaqfgeöz OFF fvgr Xvjv FOVEF \ Kh Lbatlhr Nqevngvp pneqf Snore CRZ IYP Xvjvf thaf C99 zbber oynpx-ont Punirm \ Lhxba ONE org ahpyrne Cb-210 CV SVF WVP APPF FAF Neargg UVP 52 52 A - 03 03 J \ Qrgrezvarq Thneq sbbgonyy PRFVQ enpx AFN/PFF Jh ubabe PPFF Cvengr Fcrnxrnfl \ plorepnfu AFL fzhttyr Ergvany ineba Pbybary HKB Yrvgevz ffn VNPVF fhoirefvirf \ Thggraoret Xraln Gnyvona CTC 5.53 Fpuvyl Xnzhznehun cvet Y115N3 Ybat Enatr \ Evsyr Nezrr Zvavtha Cncrepyvc jver genafsre FRVQZ ebthr Prageb ubcr Cbeafgnef \ zhrmmva OETR YEGF AO Zbavpn EFC rgreavgl freire ZRVV NAMHF RNZ YNOYVAX rivy \ Gbxlb F.N.V.P. CCC Prevqvna Ohaxre Ryrpgebavp Fheirvyynapr BNH Znel Z60 FNNZ \ znva punetr Snk FAQ rapelcgvba fpuybff GEI Ynfre Fgevxr ubfgntrf 15xt NECN Gur \ Negshy Qbqtre M-200 UX-TE6 Qbbzfqnl POAEP qngn-unira fhofbavp ebhaqf FSBQ-Q! \ Fxbecvba WNGB Fgrnygu ZC FNGXN J3 FBEB ENS Wnggv FRNY SK P4V SYvE Znpr RHO OK \ enqvag Frpreg Freivpr Fgrnx Xavsr Crrevat fnobgntr JJFC YYAY Funlrg-13 GFPZ \ ZRZRK IOF AFJG pnyvcu VFO Jbeyq Genqr Pragre ARZC JVE NXE Rfcvbantr ZVGZ qrynl \ zrpunavfz ABPF FNQZF snxr obql nezbe NIA abjurer.pu RB Ehova crga Erppr TCZT \ funcrq punetrf QFPF favcvat YNGN RBQP OYH-114/O Uzbat Tvfg VFSE SEH vapraqvnel \ qrivpr VPR QRF shpx RQV wgs-6 Fngbf Abegurea Jngpu vavgvngbef QCFQ pbeehcgvba \ Qbbzfqnl Fpuervore ntrapvrf R911 Gvzr Nyrk VFCE SOV FVEP ornacbyr E1 CmO39 \ AFI Niv xrroyre FHXYB VAE AFNF ANGVN QRNQORRS Pbzchgre XJE-46 Oryxanc Ebapb \ ANIJNA zvaqjne YOFQ Tngg QS Breyvxba Tbyqzna ffn Fcrgmanm JZQ EK-7 ZW-12 Havk \ Frphevgl POZ FUS Unegxrea cebkvzvgl shfr Fbebf Juvgr Lnaxrr ohyyrgf ZVYFNGPBZ \ ZC5x Inahngh cebcryynagf plorenggnpx ZV6 SPVP Nybhrggr Svkbe fnyg crgre Onaare \ Tbbqjva FHEIVNP cevznpbeq Cynarg-1 craerc Znsvn FNPYNAG QEN avawn IV 868 IRXS \ fhvpvqr Vatenz Znp-10 QBR ohearq FHJ BP3 Puna pelcgnanylfvf ANQQVF Sreafcnu X3 \ rkrphgvir PBFZBF HAFPBZ fahyyra Vaqvtb SYRGP SKE FREG ebpxrgf 2RZR POBG Gryrk \ Jnygure JN2000 uneqrarq gnetrg ANVNT AFBS NHGBQVA pbzzhavpngvbaf Arj Ubevmbaf \ Gbbyf fcvrf SOVF Znek trarengbe qvja XRQB 877 FRY UNUB AEY FNE grsyba .357 \ fhoirefvir FUF AFRF CPZG Xbfbib ZAQ cyhgbavhz jbwb y0px Znyvxv znexrg Tnzzn \ WVGRZ Fnyfn Havg 5707 Q-11 Qrngujvaq boshfpngr QNECN avgebtra gevvbqvqr GAG \ FOV ZQ5 VFQF sebtyrtf FFPV gurezvgr Fneva OAQ Oebnqfvqr Nabalzbhf NFVP FTV \ PvcureGNP-2000 FNCZ AZV PAPVF Qrsrafr Vasbezngvba Jnesner FJNG Syvagybpx QFQ \ Freivpr TRON jrgfh V&N PC jnerm UEG PPFF pbafcvenpl WNARG OZQB FPPA Zvyvgnel \ Vagryyvtrapr Wbvag Sbetr GQLP Cunynak BFF Noqhenuzba SNF Znlsyl pubfra QrsPba \ I Sbeg Zrnqr svybsnk pfvz jrncbaf qqac PBPBG wln.pbz nffnfvangr Fnzsbeq Ebnq \ PZF Fuvcveb VFRP Oynpxyvfgrq 411 TEF ebbgxvg nsfngpbz 7AY FOF GRKGN. RYS MY31 \ Rgvunq Pbafhygvat Lnxvzn AEB evc PFR Zreyva fnygcrgre TFT-9 wvunq vaqhfgevny \ rfcvbantr Bssrafvir Vasbezngvba VFT Fhaqrivy WNIN 11Rzp Pubr ZVG ESV YNFVAG \ FGRA ECX74 Encvq Ernpgvba pbeqvgr Onqtre Vagrearg Haqretebhaq R.B.Q. NZRZO \ tpud.tbi.hx oernxguebhtu SNEP FSCQ ANIFIF 2p FBPVZV 821 FZT Balk teranqrf \ fjrrc SBHB pybar Fcnyy RFA TBR zby 3C-UI Gmievs CCX fhcrepnivgngvat nzngby \ AMP-332 Oevna Oynve YQZK Jrrxyl Jbeyq Arjf 355 ZY SNY FHEFNG Ylapu VZS envy \ tha WGS FY-1 IVC SVQ znavn TRB WVPP e00g svfu Vevf PPFD bjurer HFPBV Zbfforet \ OEVTNAQ NQVH opr Xvyb Pynff Erfbyhgr Erfcbafr FEV PVF Zvyvgnel rivy FNORAN \ Uneineq nffnffvangvba FHA gvgf FBEG Gbzyvafba avpur W-Fgne ybpxcvpxvat SQZ \ Oynpx-Bcf PVPNC cevzref cnpxntr Gv VQRN PHFV R-Obzo sbepr pflfgrzf ZVE YVS \ NFJF Rezrf Orlbaq Ubcr Fvrzraf Nanepuvfg'f Pbbxobbx Uvyyny Gnatvzbnan Ornpu \ avgebfgnepu FNFFGVKF Vasbezngvba Greebevfz Zhehebn Begrtn Xbnapub M7 AFVEY \ pelgeba Cvkne Ny Dnrqn UEZ Cebcntnaqn Fuxiny fgnxrbhg pheyl zbegnef HBC \ Yrhxra-Onqra REE Onpu FNFPBZ HFQBW Tbys VFE Fphq VJB RBQA IBN HGH BNQE G2F2 \ Frnef Gbjre HFNSN AFC FNZH WVPF QQE&R Glcr VV CNENFNE YRRGNP Cbybavhz GUNNQ \ Nffrg FNQS YYP Pbzzrpra ANJNF CES Nzurefg QQVF CSF ZPV Onenalv tvgzb Z5 \ Crey-EFN VFNPN axiq GRZCRFG NAQIG A-VFQA oebamr Areq vavgvngbef Abznq Raqrnibe \ fvyrapref Zrgny Fgbez Cnexre-Unyr ASYVF fjnfgvxn HXV Pbeasybjre tbireazrag \ Rasbepref ZBQ. 82 Srgvfu FTP APFF Xvyqrexva TD360 ZO VO Ubyylubpx VQC Natryn \ Gur Unthr IYFV gurcvengronl NG&G irttvr uhzvag FNEG vagryyvtrapr QERB Oryypber \ Vairfgvtngvba FNV Pnavar O.Q.Z. fpuybat PFP CNOK Orpxre FVFQR FNZS Nyqretebir \ Znashebi mbar Gnvjna VFA K400 ohyyvba ECP ZQN TVTA FHE Z72750 CBPFNT begubqbk \ FNEN Cfrhqbalzf QPWSGS Naguenk OEYB Punggre fcbbxjbeqf cerfvqragvny zbgbepnqr \ FGRRCYROHFU dhvpur Pebjryy Bfpbe OEBZHER A9 ISPG fdhvo FJF JNFF cerff-eryrnfr \ TVV ECX PZJ obbgyrt qrgpbeq WEFP TRBQFF FVJ FT530 Oyraurvz Trenyqgba NEP VFFB \ senhq GCY frphevgl sbeprf BP-12 Fgnee U.A.C. Sversyl Zvqqyrzna Rzonffl jjvpf \ ohmmre ungr Nepuvirf TFF Cbeab FNGPBZN Pbebarg Avtugunjx Cevinpl NFQVP Unpxref \ Cuba-r 20755-6000 QFF VFCR Hmvry FAG gbssrr ZBQ Fphyyl rqvgvba Rzrefba O61-7 \ ef9512p 3Z ARFC Rgnpf ZXFRNEPU QQC fvyvpbacvzc ZFJ Grzcyrgba BVE EHBC Gnamnavn \ PQN UCPP RT&T JVQ perqvg pneq PVQ erqurnqf UX33XR QG OHQF TlebWrg 737 QFG PSQ \ 192.47.242.7 APFN Cflbcf AZVP zrtn Erobyyb pbpnvar Wnfzvar AFNQF FHFYB Furygba \ CEVZR FGRC KZ Z3 cvpevp npvq Sbegrmmn Oybjsvfu W2 Tybpx ABENQ snatf Hsbybtvpb \ Anmvbanyr QFARG3 Fgnayrl Pbzcfrp erqabvfr IYFV pbqrf ZNER NFIP CFNP erznvyref \ FNFC CZFC NFVB AFQQ QQA erfvfgnapr sbepnfg YHX Ahpyrne SHQ Frphevgl Rinyhngvba \ 50OZT r-pnfu Ntsn Puvpntb Cbffr PPF Oyhroveq UNZNFZBVF CRPFRAP Srvajrexgrpuavx \ GFPV ANPFV Fgrir Noyr Fragel Pnfr Pbecbengvba HFC PQP Vagvfb FUN ANPFR Thccl \ Onyqjva Qrspba obghk Fnlrerg Gmnaunavz FVGBE qrgrpgvba Erqonpx EFN ANIPBZCNEF \ ERPPRK rzp FNOP FNEQ Onefpury Juvgrjngre PVN-QFG TFZ PNGB orrs VFFP tbevyyn \ FVTQRI Njnerubhfr vzcnpg Vbabfcurer NFGF SNYA ahzore xrl QNGGN EFBP Tbevmbag \ Angvbany Vasbezngvba Vasenfgehpgher AFB vzcbeg ina FVTF JFC tneontr OYH Cneihf \ AGGP Gevgvhz Erjfba XJG-46 ebthr gvzvat qrivprf HUS mvctha CTC 5.0v Zreginln \ Ehxn Jnvubcnv VQO frperg Jveryrff PNYPZ Hmv ANJNF ENVQ HFNPVY IVC Cebgrpgvba \ oraryhk Tevcna Tnyvy nez mbar XT-84P NASB Nern51 Anfu 757 UbUbPba FBANATBY \ Nhfgva ACQ Fbhgu Nsevpn nzsb Fryin Ireqr rnirfqebccvat onfrzrag ErZBO Ivevv \ Tberyvpx OYH-97 N/O OBC Qrygn ravtzn Enaq Pbecbengvba FNZPBZZ cvm enpny NFYRG \ zrephel shyzvangr Xlhqnaxv Xinfuava RPURYBA Z.C.E.V. vasvygengvba VFRC oheubc \ Ngynf 64 Inhkunyy enqvngvba frafbef CTC 5.1 VFNQP ANGBN nvesenzr Nanylmre Zbyr \ US juvgr abvfr ANOF WREGB Zrkvpb Orpugry VEN Thnagnanzb Onl FNC iveghny Eviren \ vOhggba oybj bhg AO Pbjobl yrnq nmvqr Oyrgpuyrl Cnex VJT nffnhyg grnz znvyobzo \ czx 40 Shxhlnzn farnxref Gbal Cbr SFX Qnvfl AFN bfpb Zbagrarteb IK Ny Wnmrren \ BGC plnavqr Haqrepbire ZFAOP fvtibvpr NAP Qbjanqhc RGN rzp QREN Jvyzn obbol \ genc Xbfvhen Qngryvar SVF VJVF urnc-fcenlvat gbnq U&X Glcr VV crncbq pbagnpgf \ 5RFF Qvnargvpf enva Zbefr FT540 Gnyrag fcbbsvat Mvzzrejnyq pbaqbe erpbaqb TCF \ VFJT Inyrel Lnelavpu SVCF140-1 Unaaf-Frvqry-Fgvsghat Fgrcunavr Fxl Zbavgbe \ Xevrtfjnssratrfrgm HYS Whvyvrgg Pynff Oynpx-Bcf Avxr sbefpuhat FrpQrs Gbssyre \ SVAPRA pelcgb-nanepul NX-47 Nyvpn FNEY Fgheztrjrue FUNCR FNJ 3O2 Pnoyr & EES \ ZBFNVP ubfgntrf phag Pbhagregreebevfz JJNOAPC Pbireg Ivqrb GEQY gbkvp FNSR \ Unyyvohegba TPUD Pbafhy Pynff Fhoznevar npgvir qravny QP6 rkcyvpvg eqk GQZ \ Gleryy AEP dhnaghz ragnatyrzrag Oynpxzrqarg VT TNSR qrgrpgvba grxxn Pebff GRPF \ pehvfr zvffvyr fpenzoyr QrPFF Yvgjvaraxb IYS henavhz urknsyhbevqr gbc frperg \ Pynlzber Yrorq Ryivf Fhxubv VEVQS Fuxiny Oynpxjngre Jrfgrejryyr NN-12 Tvgzb \ onvybhg GYNZ fhozvff Fhoznevar Cngusvaqref Uhgfhy FG1 747 vapraqvnel qrivpr \ Yrkvf-Arkvf XYZ rzz ZV-17 ab|q zvarf Yvax 16 FQVF cvax abvfr NFH Uvaqnjv CGG \ xvyy cebonovyvgl FOH Fzvgu GEQ mvctha ZRH/FBP NXF-74 fhoyvzvany Enfgresnuaqhat \ zwgs Iresnffhatffpuhgm qbzrfgvp qvfehcgvba Wvnat Mrzva FUS/QBQ PvcureGNP-2000 \ jvxvyrnxf.bet QVGFN 355zy Tubfg Qnapre VJJFIPF raevpuzrag CN598Q28 ONGS frpher \ furyy AFJT Unyvohg UGPVN gjb-pbzcbarag rkcybfvir QPFF Pelcgb jngpuref ERC Gnp \ jnyohz RNQN OGZ ZQ4 WCY Rhebcby Y34N1 FNQPP Lhppn Zbhagnva AFNQF EVG tyrtfp \ feg COK zvkznfgre Neznav FVA encvq qvfnffrzoyl Ebyyfghuy WPRG Tlebwrg NBY GBF \ Gvr-svtugre Qvrobyq ORPPN Nhz NFG RXZP Svaxfohet WEO Gnwvx Uvgjbeqf Ynefba \ cbgnffvhz puybengr Jruefcbegtehccr pebjq pbageby Onq Nvoyvat ANES crargengvba \ grfgvat Onnqre-Zrvaubs Fpuähoyr gnk Gurgnaf favcre Rpuryba Fneva F! BEG JFN \ APGF pncgnva Wbububaoh TBFVC Snpr PGC Qrnq Cngry Pnc-Fgha PVQN Sbegr Qrfreg \ Sbphf Fclqrepb PPP Ebwqlxnean Xvegynaq GUP avgebpryyhybfr Zbetna Thafyvatre \ Pbqrechaxf Whvyr Qrsrafvir Vasbezngvba hgbcvn ANNC vzncpg Ryrpgeba QBAPNS Uvtu \ hapynffvsvrq QBQVT GVR sryyngr VFZ Vasbezngvba Frphevgl onyyvfgvp zrqvn FRZGRK \ fnygcrgre Z.N.E.R. vaqhfgevny vagryyvtrapr FTV Fxlgry FNFC VRQ abanp FHJ syhk \ pbzcerffbe tebhaq mreb juvfgyroybjre ovggbeerag Tber Ivqny PHQ Tybony ZCY FTQA \ qrsrafvir ryrzragf Frpher Vagrearg Pbaarpgvbaf efgn CXX Erq Pryy FNFE wvunq \ VFF-NQC WFBSP3VC HXHFN Qherff FQS OAP FZ cnaqrzvp Nuznqvarwnq OvgOyvaqre deff \ funcrq punetr fperjf pqv Gbe naguenk EPZC Ervpufgnt Pnfgeb Fceratzrvfgre FBAL \ REI YUE Erpba Bssrafvir Puryfrn SA-ZNT ZFRR RPPZ PBF 1080U obbolgencf rkcyvpvg \ Zrgn-unpxref FNPYNAGPRA Ynaqre fvyvpbacvzc QHIQRINA OVGARG Crevzrgre Pbqr \ Vawrpgvba qrhgrengrq yvguvhz FznegQrsrafr "; __CEASE_ROT13_ /* thank you, echelonspoofer.com! */ size_t ectrlen = sizeof(echeltrig); /* ...and this one to the late Jack Joseph Valenti. May he rot in peace. */ int jack = FALSE; char jjvlabel[] = "Hi, Jack! "; char *jjv[] = { "MPAA","Jack","Valenti","TheNumber","DRM","AACS","DMCA" }; size_t numjjv = sizeof(jjv)/sizeof(*jjv); /* What is the significance of this string? Look for "jack" further down */ char notthekey[] = "\363\143\373\235\143\373\163\163\373\363\263\373\143\335\373\23\ \323\373\135\63\373\123\275\373\335\343\373\323\163\373\123\223\ \373\75\123\373\223\63\373\123\223\373\343\343\373\75\363\373"; /* When more keys become known, this string may be extended */ size_t ntkylen = sizeof(notthekey); /* "GEHR" wird durch rot13 zu "TRUE" */ int Rot13 = __BEGIN_ROT13_ GEHR; __CEASE_ROT13_ #define TMFNLEN 20 char tmfnam[TMFNLEN] = ".tmp_ffffffff\0\0\0\0\0\0\0"; size_t tmfl = sizeof(tmfnam); int tmpfd = -1; char r_bnm[] = "rsrc"; /* Mac resource forks all have the same basename */ size_t rbnml = sizeof(r_bnm); char stinp[] = "stdin"; char stout[] = "stdout"; char sterr[] = "stderr"; size_t numfiles = 0; /* Das sind Gutmanns schlaue Bitmuster: */ uint32_t Gutmann[][PATLL] = { RBYT,RBYT,RBYT, RBYT,RBYT,RBYT, RBYT,RBYT,RBYT, RBYT,RBYT,RBYT, 0x55555555,0x55555555,0x55555555, 0x92492492,0x49249249,0x24924924, 0x49249249,0x24924924,0x92492492, 0x24924924,0x92492492,0x49249249, 0x00000000,0x00000000,0x00000000, 0x11111111,0x11111111,0x11111111, 0x22222222,0x22222222,0x22222222, 0x33333333,0x33333333,0x33333333, 0x44444444,0x44444444,0x44444444, 0x55555555,0x55555555,0x55555555, 0x66666666,0x66666666,0x66666666, 0x77777777,0x77777777,0x77777777, 0x88888888,0x88888888,0x88888888, 0x99999999,0x99999999,0x99999999, 0xaaaaaaaa,0xaaaaaaaa,0xaaaaaaaa, 0xbbbbbbbb,0xbbbbbbbb,0xbbbbbbbb, 0xcccccccc,0xcccccccc,0xcccccccc, 0xdddddddd,0xdddddddd,0xdddddddd, 0xeeeeeeee,0xeeeeeeee,0xeeeeeeee, 0xffffffff,0xffffffff,0xffffffff, 0x92492492,0x49249249,0x24924924, 0x49249249,0x24924924,0x92492492, 0x24924924,0x92492492,0x49249249, 0x6db6db6d,0xb6db6db6,0xdb6db6db, 0xb6db6db6,0xdb6db6db,0x6db6db6d, 0xdb6db6db,0x6db6db6d,0xb6db6db6, RBYT,RBYT,RBYT, RBYT,RBYT,RBYT, RBYT,RBYT,RBYT, RBYT,RBYT,RBYT, 0x00000000,0x00000000,0x00000000 }; int passes = howmany((sizeof(Gutmann)/sizeof(uint32_t)), PATLL); /* Die Muster sind periodisch in 3 Bytes, wir schreiben aber 4-Byte-Worte */ /* we do not allocate the read and write buffers before they are needed */ uint32_t *wrbuf = NULL; uint32_t *rdbuf = NULL; off_t so_far,all_files; #ifdef __APPLE__ uint32_t *vec_patt = NULL; #endif /* same for dice buffer */ uint32_t *bones = NULL; static size_t bon_sz = 0,cache_size = 0; static int quick_npas = 0; /* see Gutmann's epilogue: modern disk drives */ /* do such a shrewd job of encoding and compressing the data onto the platter */ /* that a few times' scrubbing with random data are as thorough as you can be.*/ /* flag variables for the options */ int cache = FALSE, delfi = FALSE, empty = FALSE, feil = FALSE, modst = FALSE, noact = FALSE, pthru = FALSE, quick = FALSE, rofl = FALSE, obfus = FALSE, krypt = FALSE, zero = FALSE, verbose = FALSE, very_verbose = FALSE, help = FALSE,info = FALSE; /* global flag for decryption */ int decrypt = FALSE; /* ------------------------------------ */ /* usgchr und lop MÜSSEN zusammenpassen */ char usgchr[] = "cdefmnqRsyYzvVhi"; /* */ int *lop[] = { /* */ &cache,&delfi,&empty,&feil,&modst, /**/ &noact,&quick,&rofl,&silent,&obfus, /**/ &krypt,&zero,&verbose,&very_verbose,/**/ &help,&info /* */ }; /* */ /* ------------------------------------ */ char *progpath = NULL; char *veilname = NULL; /* Schleierdatei */ int veillen = 0; int filarc; char **filarp = NULL; char usgehead[] = "usage: "; char usgekopf[] = "Verwendung: "; char usgeanbr[] = " [-"; char usgetail[] = "] filename(s)\n"; char usgeende[] = "] Dateiname(n)\n"; char hilftext[] = "fish überschreibt die mit den Argumenten - außer den Optionen -\n\ bezeichneten Dateien mit einer Folge von Bitmustern, welche die Wieder-\n\ herstellung des ursprünglichen Datei-Inhalts einigermaßen behindert.\n\n\ Es wird empfohlen, vor der Anwendung von fish zweimal nachzudenken.\n\n\ Optionen : -c N - (cache) Cache-Größe ist N MB\n\ -d - (delete) löscht Dateien nach dem Überschreiben\n\ -e - (empty) überschreibt leeren Speicherplatz des Filesystems\n\ -f File - überschreibt im letzten Durchlauf mit File\n\ -m - (modest) setzt Priorität auf niedrigen Wert\n\ -n - (noaction) läßt Dateien unberührt\n\ -q N - (quick) überschreibt Dateien nur N mal\n\ -s - (silent) gibt überhaupt keine Meldungen aus\n\ -y - (crypt) ent/verschleiert Dateien mit Wegwerf-Schlüssel\n\ -Y - (CRYPT) ent/verschlüsselt Dateien\n\ -z - (zero) schreibt im letzten Durchlauf Nullen\n\ -v - (verbose) spricht den Denkvorgang mit\n\ -V - (very verbose) zeigt einen Fortschrittsbalken an\n\ -h - (help) gibt diesen Text aus\n\ -i - (info) zeigt Pfad des Geheimschlüssels an, falls verwendet\n\n\ In der Umgebungsvariablen _FISH_KEY kann der Pfadname einer Geheimschlüssel-\n\ Datei stehen. Existiert diese, modifiziert sie die Verschlüsselung.\n\ Für $_FISH_KEY == \"stdin\" wird der Geheimschlüssel eingetippt.\n"; char helptext[] = "fish overwrites the files denoted by its non-option arguments\n\ with a sequence of patterns which will make it tedious to recover\n\ the files' original content.\n\n\ It is recommended to think at least twice _before_ invoking fish.\n\n\ Options are : -c N - cache (cache size is N MB)\n\ -d - delete (deletes files after overwriting it)\n\ -e - empty (overwrites empty space on file system, too)\n\ -f File - (overwrites with contents of File in last pass)\n\ -m - modest (sets priority to lower value)\n\ -n - noaction (writes to /dev/null instead of file)\n\ -q N - (quick) overwrites files just N times\n\ -s - silent (no messages at all)\n\ -y - crypt (obfuscates/deciphers files using one-time pad)\n\ -Y - CRYPT (en/decrypts files)\n\ -z - zero (uses zeroes for last pass to hide shredding)\n\ -v - verbose (displays what it does while doing it)\n\ -V - very verbose (displays a progress bar)\n\ -h - help (displays this message)\n\ -i - info (shows path of secret key if used)\n\n\ If the environment variable _FISH_KEY is the path name of an existing file,\n\ the latter is used to modify the encryption.\n\ If $_FISH_KEY == \"stdin\", the secret key is entered via the keyboard.\n"; int ger = FALSE; /* say how much */ char *filsze(off_t fsiz) { static char fszstr[16]; if(fsiz > GIGAB) { sprintf(fszstr,"%.4g GB",((float)fsiz)/((float)GIGAB)); } else if(fsiz > MEGAB) { sprintf(fszstr,"%.4g MB",((float)fsiz)/((float)MEGAB)); } else if(fsiz > KILOB) { sprintf(fszstr,"%.4g kB",((float)fsiz)/((float)KILOB)); } else { size_t filsz; filsz = (size_t)fsiz; sprintf(fszstr,"%ld Byte%s",filsz,(filsz == 1)?"":"s"); } return(fszstr); } char *strpat(_rg char *str,_rg char *pat) /* finde "pattern" im "string" */ { _rg char cch,*old = NULL; if(str && pat && (cch = *pat)) { _rg char ccs; _rg char *pch = str-1; /* prime the pump */ /*.pattern im string suchen */ do { while((ccs = *++pch) && (ccs != cch)); /* 1. Zeichen des patterns */ if(ccs) { _rg char ccp,*pcp = pat,*pcs = pch; /* gefunden ?*/ /*.für alle weiteren Zeichen im pattern (bis '\0') : mit string vergleichen */ while((ccp = *++pcp) && ((ccs = *++pcs) == ccp)); /* pattern komplett gefunden? */ if(!ccp) { old = pch; break; } /* pattern-Anfang aufheben */ else if(!ccs) break; /* string zu Ende - fertig */ } else break; } while(ccs); /* nicht komplett gefunden - weiter suchen */ } return(old); } /* find a random file size > 0, but smaller than a given one */ off_t trunc_len(ino_t inon,off_t fsiz,int fild) { _rg off_t trln; trln = fsiz + (off_t)inon; if(trln < 0) trln = -trln; /* start with a number > 0 */ if(!bones) trln /= ((fild > 0)?fild:1); /* can't roll dem bones ? */ else { int trix; /* but we can */ trix = trln % (bon_sz / sizeof(*bones)); trln = (bones[trix] / ((fild > 0)?fild:1)); if(trln < 0) trln = -trln; /* now it might be negative - we want > 0 */ } if(!trln) trln = fsiz / ((fild > 1)?fild:2); /* and certainly not = 0 */ else if(trln > fsiz) { /* modulo each other */ if(!fsiz) trln /= ((fild > 1)?fild:2); /* can't modulo 0 */ else trln %= fsiz; } else if(trln < fsiz) trln = fsiz % trln; if(!trln) trln = (fsiz / ((fild > 2)?fild:3)); /* last resort */ return(trln); } void get_lang() /* vornehm: wir sind zweisprachig */ { int ip; size_t pnl,mhl; char *locale,deloc[] = "de",msghd[] = " *** "; if((locale = getenv("LANG")) || (locale = getenv("LC_CTYPE"))) { if(!(strncmp(locale,deloc,strlen(deloc)))) { ger = TRUE; } } for(ip=0;ip oprog) { int ii; for(ii=oprog+1;ii<=iprog;++ii) { putc(pbar[ii],stderr); } fflush(stderr); /* dat kost' teuer */ oprog = iprog; } return; } /* Wir brauchen zu viel RAM. Ein gieriger Platzfresser ist node->path; also */ /* schleppen wir nicht den vollen Pfad mit, sondern lediglich die basenames */ /* node->nnam. Sobald wir die Datei bearbeiten, wird der mit "restore_path" */ /* rekonstruierte Pfad in node eingesetzt. Nach der Bearbeitung löschen wir */ /* node sowieso. */ static char *last_slash = NULL; /* we keep track of the slashes in, */ char *get_psls() { char *psls; psls = last_slash; last_slash = NULL; return(psls); } static size_t full_plen = 0; /* and the length of, the full path */ size_t get_plen() { size_t plen; plen = full_plen; full_plen = 0; return(plen); } char **pathnm(NODE *node) { static int path_len = 0; /* rauf- und runter-Zähler */ static char *slap = NULL; static char *panst = NULL; /* Da kommt der String rein */ static char **panptr = &panst; /* Der zeigt drauf */ char **path_so_far = NULL; TrEnt("pathnm"); /* ist vom letzten Mal noch was übrig ? */ if(!path_len && *panptr) { nfree(*panptr); panst = NULL; } if(node && (node->nodid == ZOMBIE)) { _rg int nm_ln; _rg char *nd_nm; _rg NODE *drct; if((drct = node->drct)) { /* gibts was drüber ? */ nd_nm = node->nnam; nm_ln = node->blen + 1; path_len += nm_ln; path_so_far = pathnm(drct); /* hinaufklettern */ } else { /* wir sind oben - da nehmen wir den vollen Pfad */ nd_nm = node->path; nm_ln = node->plen + 1; path_len += nm_ln; /* um den Pfad zusammenzukleben, brauchen wir einen Puffer */ if((panst = (char *)malloc(nxpw2((path_len + 1))))) { slap = panst; path_so_far = &panst; last_slash = NULL; full_plen = path_len; /* needed by restore_path */ } else { bleat(manm,"panst"); } } /* damit wir wissen, wann wir wieder unten sind */ if(path_so_far && *path_so_far && nd_nm) { Cpy(slap,nd_nm); slap += nm_ln; if((path_len -= nm_ln) > 0) { Cpy((slap-1),slast); last_slash = slap-1; } else *slap = NULLC; } } TrExi; return(path_so_far); } /* pathnm liefert einen Pointer auf den allokierten String-Pointer. Dieser */ /* wird dann in node geklebt, der Inhalt des Pointers wird = NULL gesetzt: */ /* Damit findet pathnm beim nächsten Aufruf eine sauber geputzte Tafel vor */ char *restore_path(NODE *node) { char *ndpa; TrEnt("restore_path"); ndpa = node->path; if(!ndpa) { char **pathptr; /* Pfadnamen rekonstruieren */ if((pathptr = pathnm(node))) { _rg char *psl,*ppt; size_t pln; pln = get_plen(); ppt = *pathptr; if((psl = get_psls())) { /* es kommt sporadisch vor, daß der Pfadname korrupt ist */ if(!*(psl+1)) { /* da ist ein Slash am Ende */ *psl = NULLC; /* weg damit */ pln = psl - ppt; /* Länge berichtigen */ do { if(*(--psl) == SLASH) break; } while(psl >= ppt); if(psl < ppt) psl = NULL; } } /* Alles in node eintragen */ node->path = ppt; node->psls = psl; node->plen = pln; *pathptr = NULL; /* für pathnm die Tafel putzen */ } } TrExi; return(ndpa); } /* Objekt aus der verketteten Liste herauslösen und entsorgen */ NODE *rem_node(NODE *node) { TrEnt("rem_node"); if(node && (node->nodid == ZOMBIE)) { NODE *pvno,*nxno; /* Verkettungszeiger aushängen */ pvno = node->prev; nxno = node->next; if((node != curd_node) || (curd_node == last_node)) { if(nxno) nxno->prev = pvno; else last_node = pvno; if(pvno) pvno->next = nxno; else first_nod = nxno; /* Inhalt befreien */ if(node->bpat) { int ipas; for(ipas=0;ipasbpat[ipas]); } nfree(node->bpat); } ifree(node->nnam); ifree(node->onam); if(node->path) { nfree(node->path); node->psls = NULL; } /* Behälter befreien */ free((void *)node); } node = (pvno)?pvno:nxno; } else { if(!silent && ErB) { char conp[] = "node pointer"; char hex1[] = "0x89abcdef"; sprintf(hex1,"0x%08x",node); bleat(conp,hex1); } node = NULL; } TrExi; return(node); } /* Leider sind nicht alle ASCII-Codes überall für Dateinamen zugelassen. Als */ /* Notlösung erzeugen wir Namen unter Ausschluß unzulässiger Zeichen aus der */ /* Bitfolge im Schreibpuffer. Zum Trost sei gesagt, daß Namen nur Schall und */ /* Rauch sind.. */ u_char cbitrv(_rg u_char bits) { _rg u_char rvbits; rvbits = (bits & 1); /* Diese Schleife lohnt es aufzurollen: */ rvbits <<= 1; bits >>= 1; rvbits |= (bits & 1); rvbits <<= 1; bits >>= 1; rvbits |= (bits & 1); rvbits <<= 1; bits >>= 1; rvbits |= (bits & 1); rvbits <<= 1; bits >>= 1; rvbits |= (bits & 1); rvbits <<= 1; bits >>= 1; rvbits |= (bits & 1); rvbits <<= 1; bits >>= 1; rvbits |= (bits & 1); rvbits <<= 1; bits >>= 1; rvbits |= (bits & 1); return(rvbits); } int isforb(_rg char token) { /* bel bs \n esc " / : \ ` del */ static char nono[] = "\007\010\012\033\042\057\072\134\140\177"; static int nno = sizeof(nono)/sizeof(*nono); _rg int inn; #ifdef __APPLE__ if(((u_char)token > 127)) return(TRUE); #else /* not __APPLE__ */ #ifndef __linux__ if(((u_char)token < 32)) return(TRUE); #endif /* __linux__ */ #endif /* __APPLE__ */ /* unspecified control characters */ if(((u_char)token > 0x81) && ((u_char)token < 0x96)) return(TRUE); for(inn=0;inn> 4) | ((byte & 0x0f) << 4)) char *Bf2Nm(uint32_t *buf,char *nam,int namlen,int i_pas) { u_char *pc; /* Wir merken uns die bereits aufgetreten Konversionen für das nächste Mal */ static uint32_t lchb[64] = { 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0 }; #ifndef __NO_TRACING /* Das ist eine nicht für diesen Zweck optimierte Radix-64 Sequenz : */ static char r64_seq[] = "0123456789abcdefghijklmnopqrstuvwxyz$@ABCDEFGHIJKLMNOPQRSTUVWXYZ\0"; #endif /* __NO_TRACING */ if((pc = (u_char *)buf) && nam && namlen) { int nbyts; char *cc,*goodchar; pc = (u_char *)buf; cc = nam; nbyts = namlen; goodchar = (char *)lchb; while(nbyts-- > 0) { _rg char gc; _rg u_char bufbyt; int goodix; bufbyt = *(pc++); goodix = bufbyt; if((gc = goodchar[goodix])) { /* hatten wir schon mal */ *(cc++) = gc; } else { /* da müssen wir noch mal ran */ if(!bufbyt) bufbyt = SPACE; if(!isforb(bufbyt)) gc = bufbyt; else { #ifdef __APPLE__ bufbyt >>= 1; if(!bufbyt) bufbyt = COMAT; /* BSD: Msb=1, shift -> */ #else /* not __APPLE__ */ bufbyt <<= 1; if(!bufbyt) bufbyt = COMAT; /* SYSV: ctrl, shift <- */ #endif /* __APPLE__ */ if(!isforb(bufbyt)) gc = bufbyt; else { bufbyt = ~bufbyt; /* Zweierkomplement */ if(!bufbyt) bufbyt = SPACE; if(!isforb(bufbyt)) gc = bufbyt; else { bufbyt = swanyb(bufbyt); /* swap hex digits */ if(!isforb(bufbyt)) gc = bufbyt; else { bufbyt = cbitrv(bufbyt); /* reverse bits */ if(!isforb(bufbyt)) gc = bufbyt; else { bufbyt = ~bufbyt; /* Zweierkomplement zurück */ if(!isforb(bufbyt)) gc = bufbyt; else { bufbyt = swanyb(bufbyt); /* swap hex digits back */ if(!isforb(bufbyt)) gc = bufbyt; else { bufbyt >>= 1; /* shift */ if(!bufbyt) #ifdef __APPLE__ bufbyt = COMAT; #else bufbyt = '\200'; #endif if(!isforb(bufbyt)) gc = bufbyt; else gc = '$'; } } } } } } } *(cc++) = gc; goodchar[goodix] = gc; } } *(cc--) = NULLC; #ifndef __NO_TRACING /* wir markieren den transienten Filenamen mit der Nummer des Durchlaufs */ if(very_verbose && namlen > 3) *cc = r64_seq[i_pas]; #endif /* __NO_TRACING */ } return(nam); } ino_t inodeno = 0; /* inode number of current directory */ ino_t gino() { return(inodeno); } void pino(ino_t in_nr) { inodeno = in_nr; return; } /* Alle Pfade gelten vom Ausgangspunkt (= current directory) */ void go_home() { TrEnt("go_home"); if(curd_node && (curd_node->nodid == ZOMBIE) && curd_node->path) { ino_t inon; /* muß es sein ? */ if(!(inon = gino()) || (inon != curd_node->inon)) { /* es muß sein */ if((curd_node->fist && !fchdir(curd_node->fild)) || !chdir(curd_node->path)) { pino(curd_node->inon); } else bleat("chdir",curd_node->path); } } TrExi; return; } char *go_to_the(NODE *node) { NODE *drct; char *psl,*d_nam; TrEnt("go_to_the"); /* Falls wir was über das darüberliegende Verzeichnis wissen, gehts leicht */ if((drct = node->drct)) { restore_path(drct); d_nam = NULL; /* sind wir da schon ? */ if(drct->inon != gino()) { /* Alle Pfade gelten vom Ausgangspunkt */ go_home(); if(drct->path) { /* ins Verzeichnis rein */ if(!chdir(drct->path)) { pino(drct->inon); } else { extern int errno; if(errno != ENOENT) { bleat("chdir",drct->path); } pino(0); } } } } else if((psl = node->psls)) { /* das isn bissl umständlicher */ go_home(); if(psl != node->path) { *psl = NULLC; d_nam = node->path; /* nur vorübergehend */ } else { d_nam = slast; /* eher selten */ } if(!chdir(d_nam)) { Stat dirst; if(!stat(d_nam,&dirst)) { pino(dirst.st_ino); } else { extern int errno; if(errno != ENOENT) bleat("stat",d_nam); pino(0); } } else { extern int errno; if(errno != ENOENT) { bleat("chdir",d_nam); } pino(0); } *psl = SLASH; } TrExi; return(d_nam); } void rename_the(NODE *node, int i_pass) { uint32_t *namsrc; TrEnt("rename_the"); /* was is, wenn amal mehrere Dateien hintereinander 0 Bytes lang sind ? */ if(!(node->fsiz)) { namsrc = (bones)?bones:wrbuf; if(!(*(uint32_t *)namsrc)) { r_vect((u_char *)namsrc,howmany(bon_sz,VEC_BYTS)*VEC_BYTS); } bytrot((u_char *)namsrc,node->blen); /* oder nix im Schreibpuffer steht ? */ } else if((*(uint32_t *)&(wrbuf[0])) || (*(uint32_t *)&(wrbuf[node->blen/sizeof(uint32_t)]))) { namsrc = wrbuf; } else { namsrc = (bones)?bones:wrbuf; r_vect((u_char *)namsrc,howmany(bon_sz,VEC_BYTS)*VEC_BYTS); } /* wir machen den neuen Namen so wie den Inhalt des Schreibpuffers */ if((node->nnam = Bf2Nm(namsrc,node->nnam,node->blen,i_pass))) { Stat nst; go_to_the(node); /* Sichtbar/unsichtbar bleibt erhalten */ if(node->onam[0] == DOT) { node->nnam[0] = DOT; } else if(node->nnam[0] == DOT) { node->nnam[0] = node->onam[0]; } /* gibt's den neuen Namen hoffentlich noch nicht ? Schaun mer mal */ if(!lstat(node->nnam,&nst)) { /* kost' leider Muskeln */ /* wups - da kommt dann kein Gutmann rein, sondern random */ r_vect((u_char *)bones,howmany(bon_sz,VEC_BYTS)*VEC_BYTS); node->nnam = Bf2Nm(bones,node->nnam,node->blen,i_pass); if(node->onam[0] == DOT) { node->nnam[0] = DOT; } else if(node->nnam[0] == DOT) { node->nnam[0] = node->onam[0]; } } else { extern int errno; if(errno != ENOENT) { bleat("lstat",node->nnam); } } /* umtaufen - machen wir sofort, damit man's sehen kann */ if(rename(node->onam,node->nnam) < 0) { extern int errno; if(errno != ENOENT) { bleat2("rename",node->onam,node->nnam); } nCpy(node->nnam,node->onam,(node->blen)+1); /* Namen wiederherstellen */ } else { struct timeval doneat[2]; uint32_t dau,dif; nCpy(node->onam,node->nnam,(node->blen)+1); /* alter Name = neuer Name */ /* und dann verbasteln wir die Zugriffszeitpunkte noch a bissel */ /* - creation date gibt's ja nicht, aber */ /* mod setzen wir bis zu 7 1/2 Jahre zurück */ doneat[1].tv_sec = node->time[1].tv_sec - ONE_HOUR*squirt(namsrc[1]); /* acc bis zu 45 1/2 Tage danach */ doneat[0].tv_sec = doneat[1].tv_sec + ONE_MINUTE*squirt(namsrc[0]); /* die Mikrosekunden sollen nur plausibel sein */ dau = node->time[1].tv_usec; dif = 16*squirt(doneat[0].tv_sec); doneat[1].tv_usec = (dau > dif)?(dau - dif):(dif - dau); dau = (namsrc[0]^namsrc[1])%(MEGAB); dif = 16*squirt(doneat[1].tv_sec); doneat[0].tv_usec = (dau > dif)?(dau - dif):(dif - dau); #ifdef __APPLE__ futimes(node->fild,doneat); #else futimesat(node->fild,NULL,doneat); #endif } } TrExi; return; } void rename_path_above(NODE *node) { TrEnt("rename_path_above"); if((node != curd_node) && node->drct) { NODE *self,*drct; self = node; self->ipas += 1; rename_the(self,self->ipas); go_home(); if(self->path) { nfree(self->path); self->psls = NULL; } /* wir klettern den Baum hinauf, und taufen dabei alle Verzeichnisse */ /* auf unserem Pfad um, die nur noch genau ein Verzeichnis enthalten */ do { /* ist noch ein Verzeichnis darüber ? */ if((drct = self->drct) && (drct != curd_node)) { if(drct->ecnt <= 1) { /* sind wir die einzigen darin ? */ drct->ipas += 1; /* dreimal muß ich rummarschieren */ rename_the(drct,drct->ipas); go_home(); /* umtaufen */ if(drct->drct) { /* da ist noch was drüber */ /* hier dürfen wir path putzen: restore_path kann ihn wieder herstellen */ if(drct->path) { nfree(drct->path); drct->psls = NULL; } } else if(drct->path) { /* wir sind oben - path nicht putzen, sondern berichtigen */ if(drct->psls) { /* path mit Verzeichnisanteil */ nCpy((drct->psls)+1,drct->nnam,(drct->blen)+1); } else { /* ...und ohne */ nCpy(drct->path,drct->nnam,(drct->blen)+1); } } } else break; } self = drct; } while(self); /* node->nnam ist korrekt, aber node->path stimmt jetzt nicht mehr */ /* gehen wir eben noch mal durch die Verzeichnisse hinauf */ self = node; do { restore_path(self); if((drct = self->drct)) { if(drct->ecnt > 1) break; } self = drct; } while(self); } TrExi; return; } /* Augenpulver: wir basteln einen Fortschrittsbalken */ /* ...und den wollen wir auch wieder wegmachen */ char pnix[] = " "; /* Wenn noch leere Verzeichnisse rumhängen... */ /* verkettete Liste von hinten her abarbeiten */ size_t rem_dirs(int once) { static int say = FALSE; size_t numnod; NODE *rmno; TrEnt("rem_dirs"); numnod = 0; if(once) say = FALSE; if((rmno = last_node)) { while(rmno) { ++numnod; /* erstmal alle zählen */ rmno = rmno->prev; } rmno = last_node; while(rmno) { NODE *pvno; /* dann suchen wir mal */ pvno = rmno->prev; if(rmno == curd_node) { break; } else if((rmno->mode & S_IFMT) == S_IFDIR) { /* da wir mitgezählt haben... */ if(!(rmno->ecnt) #ifdef __APPLE__ && !(rmno->isrf) #endif /* __APPLE__ */ ) { /* ...gehen wir nur ans Gerät, wenn wir Aussicht auf Erfolg haben */ /* allerdings löschen wir erst, nachdem ein paarmal umgetauft ist : */ if((rmno->ipas > NDINSH) || /* das wird weiter unten gemacht */ (rmno->prev == curd_node)) { /* top level Verzeichnis */ if(!say) { if(very_verbose) { fprintf(stderr,"\r%s\r",pnix); fflush(stderr); fprintf(stderr,"%c%s...\r",flac, (ger)?"leere Verzeichnisse löschen": "deleting empty directories"); fflush(stderr); } say = TRUE; } /* Das funktioniert auch bei geändertem Namen noch: */ go_to_the(rmno); /* und dann putzen wir das leere Ding gnadenlos weg */ if((rmdir(rmno->nnam) < 0)) { extern int errno; if(errno != #ifdef __APPLE__ ENOTEMPTY #else /* not __APPLE__ */ EEXIST #endif /* __APPLE__ */ ) { if(errno == ENOENT) { /* da is was kaputt */ pvno = rem_node(rmno); --numnod; } else { bleat("rmdir",rmno->path); } } else { /* was, das geht immer noch nicht ? */ break; /* probieren wir's beim nächsten Mal wieder */ } } else { NODE *drct,*rsrc; rmno->mode = 0; /* flag as done */ if((drct = rmno->drct)) drct->ecnt -= 1; /* 'türlich */ pvno = rem_node(rmno); --numnod; /* da warens nur noch n-1 */ } go_home(); } } } else if(!(rmno->mode)) { pvno = rem_node(rmno); --numnod; } rmno = pvno; } /* Namen der Verzeichnisse werden geändert, bevor wir sie löschen dürfen */ rmno = last_node; while(rmno) { if((rmno->mode & S_IFMT) == S_IFDIR) { if(!rmno->ecnt) { rename_path_above(rmno); /* und zwar möglichst weit nach obent */ } } rmno = rmno->prev; } /* das ist das Letzte */ if(numnod < 2) { last_node = rem_node(last_node); --numnod; } /* das wird deshalb so gemacht, damit zwischen Umtaufen und Löschen */ /* ausreichend Gelegenheit ist, die geänderten Namen auf die Platte */ /* zu schreiben. Zur Sicherheit machen wir noch ein */ sync(); } if(verbose) { if(say) { fprintf(stderr,"\r%s\r",pnix); fflush(stderr); if(once) say = FALSE; } } TrExi; return(numnod); } /* beim Panikknopf wollen wir eine wohlüberlegte Reaktion */ sig_t my_int_hd() { NODE *node; TrEnt("my_int_hd"); if(verbose) fprintf(stderr,"\r%s\r",pnix); fflush(stderr); if(!noact) { /* und 'n büschen ttv */ if((node = last_node)) { while(node) { /* Erst mal mit den Files Heile Heile Segen machen */ if( #ifdef __APPLE__ !node->isrf && #endif /* __APPLE__ */ ((node->mode & S_IFMT) == S_IFREG)) { /* falls nötig, schließen */ if(node->fist) { if(fclose(node->fist)) { bleat("fclose",node->path); } node->fist = NULL; node->fild = -1; if(node->ispd) { /* das ist die Polsterdatei */ go_to_the(node); /* die wird immer geputzt */ if(unlink(node->nnam) < 0) bleat("unlink",node->nnam); } else if(delfi) { NODE *drct; char *psl,*b_nam,*d_nam; psl = node->psls; b_nam = (psl)?(psl+1):node->path; d_nam = go_to_the(node); /* d_nam ist nur ein Flag */ /* Filenamen wiederherstellen */ #ifdef __APPLE__ if(!node->isrf) { #endif /* __APPLE__ */ if(strncmp(node->nnam,b_nam,strlen(b_nam))) { if(rename(node->nnam,b_nam)) { extern int errno; if(errno != ENOENT) bleat2("rename",node->nnam,b_nam); } } #ifdef __APPLE__ } #endif /* __APPLE__ */ /* Filestatus wiederherstellen (wurde wegen fopen geändert) */ if(chmod(b_nam,node->mode)) { extern int errno; if(errno != ENOENT) bleat("chmod",b_nam); } else if(utimes(b_nam,node->time)) { extern int errno; if(errno != ENOENT) bleat("utimes",b_nam); } else { node->mchd = FALSE; } /* Verzeichnisstatus wiederherstellen (wurde wegen rename geändert) */ if((drct = node->drct)) { if(drct->mchd) { /* ..aber nur einmal */ if(chmod(dotst,drct->mode)) { /* Ik bün al dor ! */ bleat("chmod",drct->path); } else if (utimes(dotst,drct->time)) { bleat("utimes",drct->path); } else { drct->mchd = FALSE; } } } else if(d_nam) { /* Hier können wir den Verzeichnisstatus zwar nicht exakt */ /* wiederherstellen, aber man tut, was man kann */ if(utimes(dotst,node->time) < 0) { *psl = NULLC; bleat("utimes",d_nam); *psl = SLASH; } } } else if(obfus) { if(node == cuno) { close(tmpfd); tmpfd = -1; if(unlink(tmfnam) < 0) bleat("unlink",tmfnam); } } } } node = node->prev; } } r_reset(0); /* clean out random generator */ /* was jetzt kommt, geht vom Ausgangspunkt aus */ if(delfi) { go_home(); rem_dirs(TRUE); } } if(!silent) { if(obfus) { fprintf(stderr,"\r%s%s", (decrypt)?((ger)?"Ent":"de"):((ger)?"Ver":"en"), (ger)?"schleiern von":"crypting"); } else { fprintf(stderr,"\r%s",(ger)?"Überschreiben von":"shredding"); } fprintf(stderr," %s %s\n",cuno->path,(ger)?"abgebrochen":"aborted"); } TrExi; exit(0); } /* Auf geht's */ /* Würden die deterministischen Bitmuster immer in derselben Reihenfolge */ /* geschrieben, könnte das einem zu allem entschlossenen Ermittler einen */ /* Vorteil verschaffen. Also mischen wir die deterministische Durchläufe */ void do_shuffle(NODE *node) { uint32_t **bpat; static int lend = 0,begd = 0,endd = 0xff; /* wird noch nachgebessert */ TrEnt("do_shuffle"); /* Zuerst suchen wir, wo im Gutmann-Array die deterministischen Werte stehen */ if(!lend) { _rg int ipas; for(ipas=0;ipas begd) { begd = pas0; } lend = endd - begd; } else { begd = endd; lend = 1; } /* nur Zufall ist angesagt */ } } /* Wenn wir Pointer vertauschen wollen, müssen die auch vertauschbar sein: */ bpat = node->bpat; if(!bpat) { int ipas; if((bpat = (uint32_t **)malloc(nxpw2(passes)*sizeof(uint32_t *)))) { uint32_t *gutpat; /* wir kopieren die Gutmann-Bitmuster in das vertauschbare Array */ for(ipas=0;ipasbpat = bpat; } if(bpat) { _rg int ipas; /* after the first instance, we come right here: Gotta roll dem bones ! */ /* r_vect ist hier schon initialisiert, und wrbuf ist allokiert */ r_vect((u_char *)wrbuf, howmany((passes*sizeof(uint32_t)),VEC_BYTS)*VEC_BYTS); /* Und jetzt tauscht Huber mit Meier, Meier mit Schmidt, Schmidt mit... */ for(ipas=begd;ipasnodid = ZOMBIE; name = (pthru)?stinp:nodename; /* für pipe ein dummy node */ nlen = strlen(name); /* Den Schrägstrich am Ende können wir nicht ab. */ if(name[nlen-1] == SLASH) name[--nlen] = NULLC; if(keep) { if((node->path = (char *)malloc(nxpw2(nlen+1)))) { Cpy(node->path,name); } } else { /* wenn wir's nicht behalten wollen, geht das auch */ node->path = name; } if(node->path) { size_t bas_sz; node->plen = nlen; /* das stimmt natürlich später nicht mehr */ node->psls = strrchr(node->path,SLASH); #ifdef __APPLE__ /* wenn's ein Resource Fork ist, */ if(node->psls && !strncmp((node->psls)+1,r_bnm,rbnml)) { _rg char *psl,*pnm; node->isrf = TRUE; /* muß der letzte Slash weg */ pnm = node->path; psl = node->psls; node->psls = NULL; do { if(*(--psl) == SLASH) break; } while(psl >= pnm); node->psls = (psl < pnm)?NULL:psl; } /* verkettete Liste aufbauen */ #endif /* __APPLE__ */ if(node->psls) { bas_sz = (node->path + node->plen) - (node->psls + 1); } else { /* der Pfad hat keinen Verzeichnis-Anteil */ bas_sz = node->plen; node->drct = curd_node; /* damit sind wir komplett */ } node->blen = bas_sz; /* auch aufheben */ bas_sz = nxpw2(bas_sz+1); if((node->onam = (char *)malloc(bas_sz))) { if(!pthru) { /* Base Name alt */ Cpy(node->onam,(node->psls)?(node->psls)+1:node->path); } if((node->nnam = (char *)malloc(bas_sz))) { Stat shst; if(!pthru) { /* Base Name neu */ Cpy(node->nnam,node->onam); /* Wir verwenden lstat statt stat, denn mit */ if(!lstat(node->path,&shst)) { /* symbolischen Links wollen wir nichts zu tun haben! */ node->mode = shst.st_mode; node->inon = shst.st_ino; #ifdef __APPLE__ node->blsz = shst.st_blksize; #endif /* __APPLE__ */ /* Zugriffszeitpunkte aufheben */ node->time[0].tv_sec = shst.st_atime; node->time[0].tv_usec = 0; node->time[1].tv_sec = shst.st_mtime; node->time[1].tv_usec = 0; if((node->mode & S_IFMT) == S_IFREG) ++numfiles; } else { nost = TRUE; bleat("lstat",node->path); } } else { /* pipe */ ++numfiles; node->mode = S_IRUSR|S_IFREG; /* Lug */ Cpy(node->onam,node->path); /* und */ Cpy(node->nnam,node->path); /* Trug */ } } else { nost = TRUE; bleat(manm,"node->nnam"); } } else { nost = TRUE; bleat(manm,"node->onam"); } } else { nost = TRUE; bleat(manm,"node->path"); } } else { nost = TRUE; bleat(canm,"node"); } if(nost) { extern int errno; ifree(node->nnam); ifree(node->onam); if(node->path) { if(keep) free((void *)node->path); /* selber allokiert */ node->path = NULL; } node->psls = NULL; ifree(node); if(where && (where->nodid == ZOMBIE)) { node = where; } else { node = last_node; } } else { size_t bonln,nitems,itemsz; if(!keep) { /* nur geklaut */ node->path = NULL; node->psls = NULL; } /* beim ersten Mal allokieren wir diverse Puffer... */ #ifdef __APPLE__ /* Zwischenpuffer für periodische Bitmuster für Vektor-Kopieren */ if(!pthru) { if(!vec_patt) { nitems = howmany(VEC_PATLB,VEC_BYTS); itemsz = (VEC_BYTS); /* alignment */ if(!(vec_patt = (uint32_t *)malloc(nxpw2(nitems)*itemsz))) { bleat(manm,"vec_patt"); exit(4); } } } #endif /* __APPLE__ */ if(!wrbuf) { int32_t seed; /* hintendran braucht's was Luft */ nitems = howmany(WRBLBPP,VEC_BYTS); itemsz = (VEC_BYTS); /* alignment */ if(!(wrbuf = (uint32_t *)malloc(nxpw2(nitems)*itemsz))) { bleat(manm,"wrbuf"); exit(4); } if(obfus || krypt) { if(!(rdbuf = (uint32_t *)malloc(nxpw2(nitems)*itemsz))) { bleat(manm,"rdbuf"); exit(4); } } else { /* ...und initialisieren den Zufallsgenerator */ /* wir wollen so richtig zufällige Zufallszahlen */ seed = smp_rand(); if(seed > 0) seed = -seed; r_init(seed); } } /* ...den Würfelpuffer passen wir für normale Dateien jeweils an */ if((node->mode & S_IFMT) == S_IFREG) { bonln = (node->plen)?node->plen:MAXPATHLEN; /* nur nicht knausern */ nitems = howmany(bonln,VEC_BYTS); itemsz = (VEC_BYTS); /* alignment */ nitems = nxpw2(nitems); if(!bones) { if(!(bones = (uint32_t *)malloc(nitems*itemsz))) { bleat(manm,"bones"); exit(4); } else { bon_sz = nitems*itemsz; } } else if(bonln > bon_sz) { if(!(bones = (uint32_t *)realloc((void *)bones,nitems*itemsz))) { bleat(ranm,"bones"); exit(4); } else { bon_sz = nitems*itemsz; } } /* Jede Datei kriegt eine eigens verwürfelte Reihenfolge */ /* if(!pthru) do_shuffle(node); */ /* Aber erst bei Bedarf ! */ } node->fild = -1; /* is noch zu */ /* Verkettungszeiger */ if(where && (where->nodid == ZOMBIE)) { NODE *whnx; whnx = where->next; if(whnx) { whnx->prev = node; } else { last_node = node; } where->next = node; node->prev = where; node->next = whnx; } else { node->prev = last_node; if(last_node) last_node->next = node; last_node = node; if(!first_nod) first_nod = node; } } if(!keep) keep = TRUE; TrExi; return(node); } /* fast immer */ NODE *add_name(char *name,int keep) { NODE *node; TrEnt("add_name"); node = add_node(name,NULL,keep); TrExi; return(node); } /* Verzeichnis durchgehen und Einträge auf den Stapel legen */ NODE *do_directory(NODE *drct) { NODE *nxno; TrEnt("do_directory"); if(drct == curd_node) { /* das lösen wir nicht auf */ nxno = drct->next; } else { _rg DIR *directory; static char *panm = NULL; static size_t panmsz = 0; #ifdef __APPLE__ static char *d_entries = NULL; size_t direntsize; direntsize = howmany(drct->blsz,sizeof(Dirent)); /* beim ersten Mal da tuz noch weee */ if(!d_entries) { d_entries = (char *)calloc(direntsize,sizeof(Dirent)); } else { /* keine Altlasten, bitte */ memset(d_entries,0,(direntsize * sizeof(Dirent))); } if(d_entries) { long bap = 0; #endif /* __APPLE__ */ restore_path(drct); /* wenn der Pfad fehlt */ if((directory = opendir(drct->path))) { /* Pfad vom Ausgangspunkt */ Dirent *entry; char *e_nam; size_t p_len,p_sze; /* dann klappern wir alle Einträge durch */ #ifdef __APPLE__ while(getdirentries(directory->dd_fd,d_entries,drct->blsz,&bap)) { uint32_t d_recoff = 0; do { uint32_t r_len; u_char d_typ; entry = (Dirent *)(d_entries + d_recoff); r_len = entry->d_reclen; d_typ = entry->d_type; /* Wir behandeln keine anderen Typen */ if(r_len && (r_len <= drct->blsz) && ((d_typ == DT_REG) || (d_typ == DT_DIR) || (d_typ == DT_LNK))) { d_recoff += r_len; if(entry->d_fileno && entry->d_namlen && (entry->d_namlen < MAXNAMLEN)) { #else /* not __APPLE__ */ while((entry = readdir(directory))) { if(entry->d_ino) { if(entry->d_reclen) { #endif /* __APPLE__ */ e_nam = entry->d_name; /* "." und ".." lassen wir außen vor */ if(!(drct->ecnt)) { _rg char *enp; if(*(enp = e_nam) == DOT) { _rg char enc; if((enc = *(++enp)) == DOT) { if(*(++enp) == NULLC) continue; } else if(enc == NULLC) continue; } else if(!noact && (delfi || obfus)) { /* Das ist dann der erste richtige Eintrag */ if(!drct->mchd) { /* nur 1 mal pro Verzeichnis */ /* sicher ist sicher - wir wollen schließlich schlimme Sachen machen */ if(chmod(drct->path,S_IRWXU|S_IRWXG|S_IRWXO)) { bleat("chmod",drct->path); } else { drct->mchd = TRUE; } } } } ++(drct->ecnt); /* alle anderen Einträge mitzählen */ /* wir gehen vom Ausgangspunkt aus und basteln */ /* den vollständigen Pfad zum Eintrag zusammen */ p_len = drct->plen + 1; /* mit slash */ p_len += strlen(e_nam); p_sze = nxpw2(p_len+1); if(p_sze > panmsz) { /* nur ein paar mal... */ if(!panm) { if((panm = (char *)malloc(p_sze))) { panmsz = p_sze; } else { bleat(manm,"panm"); } } else { if((panm = (char *)realloc((void *)panm,p_sze))) { /* wie's Murphy will, ist vielleicht noch irgendwelcher Dreck drin */ /* memset(panm,0,p_sze); */ panmsz = p_sze; } else { panmsz = 0; bleat(ranm,"panm"); } } } if(panm) { NODE *newn; Cat(Cat(Cpy(panm,drct->path),slast),e_nam); /* alle Einträge hängen wir hinten an die Liste */ if((newn = add_name(panm,FALSE))) { /* jeder _neue_ Eintrag muß sein darüberliegendes Verzeichnis kennen */ if(!newn->drct) newn->drct = drct; } else exit(1); /* Höchst */ } else { bleat((manm+1),"panm"); exit(2); } /* fatal */ #ifndef __APPLE__ } else break; } } #else /* not __APPLE__ */ } } else break; /* kein Eintrag mehr */ } while(d_recoff < drct->blsz); } #endif /* __APPLE__ */ if(closedir(directory) < 0) { bleat("closedir",drct->path); } directory = NULL; nxno = drct->next; if(!noact && delfi) { if(!(drct->ecnt)) { /* Wenn das Verzeichnis leer ist, taufen wir es um und lassen */ /* es bei der nächsten Gelegenheit von rem_dirs wegräumen. */ rename_path_above(drct); /* So. jetzt können wir den vollen Pfadnamen des Verzeichnisses */ /* wieder putzen, falls es kein Wurzelverzeichnis ist */ } else if(drct->drct && drct->path) { nfree(drct->path); drct->psls = NULL; } } } else { nxno = drct->next; bleat("opendir",drct->path); } #ifdef __APPLE__ } else nxno = drct->next; #endif /* __APPLE__ */ } TrExi; return(nxno); } #ifdef __APPLE__ /* Resource Fork ? */ NODE *do_resource(NODE *node) { NODE *nxno; size_t r_len,r_sze; static char *rsnm = NULL; static size_t rsnmsz = 0; char *npth; TrEnt("do_resource"); /* wir gehen vom Ausgangspunkt aus und basteln */ /* den vollständigen Pfad zum Ressourcenzweig zusammen */ /* look for a file name "path/rsrc" - we need a path for this */ npth = restore_path(node); /* BtW: this here appears to be the site of the act of stupidity mentioned */ /* below, because earlier, restore_path was called AFTER the Allocation of */ /* rsnm - which at that time didn't know about the length required for the */ /* full path. Oh dear. */ r_len = node->plen + 1; /* mit slash */ r_len += rbnml; /* Länge von "rsrc" */ r_sze = nxpw2(r_len + 1); /* mit \0 */ if(r_sze > rsnmsz) { /* nur ein paar mal... */ if(!rsnm) { if((rsnm = (char *)malloc(r_sze))) { rsnmsz = r_sze; } else bleat(manm,"rsnm"); } else { if((rsnm = (char *)realloc((void *)rsnm,r_sze))) { rsnmsz = r_sze; } else { rsnmsz = 0; bleat(ranm,"rsnm"); } } } if(rsnm) { NODE *newn,*drct; int keep; Cat(Cat(Cpy(rsnm,node->path),slast),r_bnm); drct = node->drct; /* gibts den ? */ /* Der kommt vor den Datenzweig */ keep = (drct)?FALSE:TRUE; if((newn = add_node(rsnm,(node->prev)?node->prev:first_nod,keep))) { if(newn->mode & S_IFDIR) { newn->mode &= ~S_IFDIR; newn->mode |= S_IFREG; } /* Der Ressourcenzweig ist im gleichen Verzeichnis wie der Datenzweig */ nxno = node->next; if(newn != node) { newn->drct = drct; } } else exit(1); if(!npth && node->path) { nfree(node->path); node->psls = NULL; } } else { bleat((manm+1),"rsnm"); exit(2); } TrExi; return(nxno); } #endif /* __APPLE__ */ /* Wollen wir ver- oder entschleiern ? */ /* Wird ein Geheimschlüssel verlangt ? Den müssen wir finden und benutzen */ void get_fish_key(void) { char *chips,*chaps,*keypath = NULL; int key_stdin = FALSE; size_t wikl = 0; static int32_t sno[] = { SIGHUP, SIGINT, SIGQUIT,SIGILL, SIGTRAP,SIGABI, SIGEMT, SIGFPE, SIGBUS, SIGSEGV,SIGSYS, SIGPIPE,SIGALRM,SIGTERM,SIGURG, SIGTSTP, SIGCONT,SIGCHLD, SIGTTIN,SIGTTOU,SIGIO, SIGXCPU, SIGXFSZ,SIGVTALRM,SIGPROF,SIGWINCH, SIGUSR1,SIGUSR2 }; static hd_t *o_hd[] = { DefHdlr,DefHdlr, DefHdlr,DefHdlr, DefHdlr,DefHdlr,DefHdlr,DefHdlr, DefHdlr, DefHdlr,DefHdlr, DefHdlr,DefHdlr,DefHdlr,DefHdlr, DefHdlr, DefHdlr,DefHdlr, DefHdlr,DefHdlr,DefHdlr,DefHdlr, DefHdlr,DefHdlr, DefHdlr,DefHdlr, DefHdlr,DefHdlr }; static int32_t ns = sizeof(sno)/sizeof(*sno); TrEnt("get_fish_key"); /* Wir brauchen das Home-Verzeichnis des Anwenders */ if(!(chaps = getenv("HOME"))) chaps = dotst; if((chips = getenv(keyenv))) { keyl = strlen(chips) + 1; key_stdin = (!strncmp(chips,stinp,keyl))?TRUE:FALSE; } /* wird Eingabe des Geheimschlüssels von der Tastatur gewünscht? */ if(!key_stdin) { if(info) { fprintf(stderr,"%s",(ger)?"Geheimschlüssel":"secret key"); if(chips) { fprintf(stderr," in %s",keypath); fprintf(stderr," %s %s\n",(ger)?"aus Umgebung":"from environment"); } else fprintf(stderr,"\n"); exit(0); } /* das kann irgendeine Datei sein, wenn nur der Name stimmt */ if((keypath = (void *)malloc(strlen(chaps) + keyl + 2))) { int kyfd; FILE *kyfi; /* aber zuerst sehen wir im aktuellen Verzeichnis nach : */ Cat(Cat(Cpy(keypath,dotst),slast),(chips)?chips:keynam); /* gibt es dort eine Schlüsseldatei ? */ if((kyfd = open(keypath,O_RDONLY,S_IRUSR)) < 0) { /* wenn nicht, dann im Heimverzeichnis : */ Cat(Cat(Cpy(keypath,chaps),slast),keynam); kyfd = open(keypath,O_RDONLY,S_IRUSR); } if((kyfd >= 0) && (kyfi = fdopen(kyfd,"r"))) { off_t kyf_sz; fseeko(kyfi,(off_t)0,SEEK_END); /* Alles hat ein Ende */ if((kyf_sz = ftello(kyfi)) > 0) { fikl = (size_t) kyf_sz; /* so viele Bytes isser lang */ wikl = howmany(fikl,sizeof(uint32_t)); /* Worte */ if((fish_key = (u_char *)calloc(wikl,sizeof(uint32_t)))) { /* Den Inhalt der Datei nehmen wir und machen daraus einen Schlüssel. */ /* Der darf beliebig lang sein, wird allerdings auf volle Worte aufgefüllt ! */ lseek(kyfd,0,SEEK_SET); /* zurückstellen - wir wollen lesen */ if((read(kyfd,fish_key,(size_t)fikl)) != (size_t)fikl) { nfree(fish_key); fikl = 0; wikl = 0; } } else { fikl = 0; wikl = 0; } } else { fikl = 0; } close(kyfd); kyfd = -1; kyfi = NULL; chmod(keypath,S_IRUSR|S_IWUSR); /* Geht nur uns was an */ } else if(info) { fprintf(stderr,"%s\n",(ger)?"kein Geheimschlüssel":"no secret key"); exit(0); } nfree(keypath); } } else { u_char *eok; int is,stof; /* Der Schlüssel wird interaktiv (über die Tastatur) eingegeben */ #ifdef __SVR4 struct termios my_tty; #else /* not __SVR4 */ #ifdef __linux struct termios my_tty; #else struct sgttyb my_tty; #endif /* __linux */ #endif /* __SVR4 */ if(info) { fprintf(stderr,"%s",(ger)?"Geheimschlüssel":"secret key"); fprintf(stderr," %s\n",(ger)?"von stdin":"from stdin"); exit(0); } /* wir lesen den Schlüssel von stdin ein */ if(verbose) { printf("%s!\n",(ger)?"bitte fehlerfrei tippen": "no typing errors, please"); } printf("%s -> ",(ger)?"Geheimschlüssel":"secret key"); fflush(stdout); /* Schaut mir da einer über die Schulter? */ stof = (int)fileno(stdout); ioctl(stof,TIOCGETP,&my_tty); /* während der Eingabe soll der Prozeß nicht abgebrochen werden */ for(is = 0; is < ns; ++is) o_hd[is] = (hd_t *)signal(sno[is],SIG_IGN); #ifdef __SVR4 my_tty.c_lflag &= ~ECHO; #else /* not __SVR4 */ #ifdef __linux my_tty.c_lflag &= ~ECHO; #else my_tty.sg_flags &= ~ECHO; #endif /* __linux */ #endif /* __SVR4 */ ioctl(stof,TIOCSETP,&my_tty); /* und zugucken darf auch keiner */ /* da steht er jetzt erst mal drin */ if((fish_key = (u_char *)getlne(stdin,(char **)&eok))) { u_char *dupl_key; fikl = eok - fish_key; printf("\n%s -> ",(ger)?"noch mal, bitte":"repeat it "); fflush(stdout); /* verifizieren */ if((dupl_key = (u_char *)getlne(stdin,(char **)&eok))) { size_t dukl; dukl = eok - dupl_key; if((dukl == fikl) && !strncmp((char *)fish_key,(char *)dupl_key,fikl)) { printf("\n"); wikl = howmany(fikl,sizeof(uint32_t)); /* Worte */ /* Wenn wir auf Wörter auffüllen, brauchen wir mehr Platz */ if((wikl * sizeof(uint32_t) > fikl)) { if(!(fish_key = (u_char *) realloc((void *)fish_key,wikl * sizeof(uint32_t)))) { fikl = 0; wikl = 0; } } } else { printf("\n%s\n",(ger)?"vertippt - so geht's nicht": "mistyped, bailing out"); exit(0); } nfree(dupl_key); } else { printf("\n"); fikl = 0; wikl = 0; } } else { printf("\n"); fikl = 0; wikl = 0; } /* jetzt kannste wieder gucken */ #ifdef __SVR4 my_tty.c_lflag |= ECHO; #else /* not __SVR4 */ #ifdef __linux my_tty.c_lflag |= ECHO; #else my_tty.sg_flags |= ECHO; #endif /* __linux */ #endif /* __SVR4 */ ioctl(stof,TIOCSETP,&my_tty); /* und jetzt sind wir auch nicht mehr zickig mit den Signalen */ for(is = 0; is < ns; ++is) { signal(sno[is], #ifdef __SVR4 (hd_t(*)()) #endif o_hd[is]); } } /* noch a bissel nacharbeiten */ if(wikl) { int ib,jb; jb = fikl; /* Schwänzchen definiert auffüllen = letzte Bytes (max. 3) hineinspiegeln */ for(ib=fikl;ib<(wikl*sizeof(uint32_t));++ib) { if(!jb) jb = fikl; /* eher unwahrscheinlich, aber... */ fish_key[ib] = cbitrv(fish_key[--jb]); } /* ENDIAN */ /* To big or not to big (=little), this is the question. */ /* we use the same byte sequence on big and little endian machines, because */ /* the key modification works on the byte, not the word sequence. */ if(ntohl(ZOMBIE) != ZOMBIE) { _rg uint32_t *fkw; fkw = (uint32_t *)fish_key; swac((void *)fkw,(void *)fkw,wikl,sizeof(uint32_t)); } /* und damit's nicht zu simpel wird, Schwänzchen einziehen */ wrdrot((uint32_t *)fish_key,wikl); fikl = wikl * sizeof(uint32_t); /* so lang isser */ /* Jetzt ist die Länge des Schlüssels durch 4 teilbar. */ /* Schreibt Euch das auf, das könnte noch mal wichtig werden. */ } TrExi; return; } int /* we do nothing, unless */ there_are_options(int arc, char **arv) { int cnt = 0; TrEnt("there_are_options"); /* I admit this is unnecessarily spaghetti-ish, but there is the advantage */ /* of having this "usage string" that appears in the option error message, */ /* and a very simple parsing engine... */ progpath = *(arv); if(arc > 1) { /* nicht-Option-Argumente: */ int nofnam = FALSE,nocrypt = TRUE; /* Was sollen wir tun ? */ while(--arc > 0) { char *pop,cop; if(*(pop = *(++arv)) == DASH) { if(strlen(pop) == 1) { /* this wants the crypter */ pthru = TRUE; if(!obfus) obfus = TRUE; delfi = FALSE; zero = FALSE; if(!(add_name(pop,TRUE))) exit(1); ++cnt; } while((cop = *(++pop))) { char *puc,cuc; puc = usgchr; while((cuc = *(puc++))) { if(cop == cuc) { *(lop[(int)(puc-usgchr-1)]) = TRUE; /* ist kein Filename */ if(cache || quick || feil || rofl) nofnam = TRUE; if(nocrypt && (obfus || krypt)) nocrypt = FALSE; break; } } if(!cuc) { /* fish is a dangerous command - mustn't use it without knowing the options */ if(!silent) { fprintf(stderr, "un%sption -%c\n",(ger)?"bekannte O":"known o",cop); } help = TRUE; noact = TRUE; } if(help) break; } } else { /* nicht-Option-Argument = Filename */ /* wir können Listen nur korrekt abarbeiten, wenn wir wissen, wo wir sind */ if(!curd_node) { char *curd = NULL; if((curd = getcwd(curd,MAXPATHLEN+1))) { if((curd_node = add_name(curd,TRUE))) { if((curd_node->fist = fopen(dotst,"r"))) { curd_node->fild = fileno(curd_node->fist); } else { bleat("fopen",dotst); exit(9); } } else { exit(9); } free((void *)curd); } } if(nofnam) { /* kludge as kludge can */ if(cache) { if(sscanf(pop,"%ld",&cache_size) != 1) { bleat("sscanf","cache_size"); } /* Is gebongt */ cache = FALSE; /* Flagge wieder streichen */ } else if(quick) { if(sscanf(pop,"%ld",&quick_npas) != 1) { bleat("sscanf","quick_npas"); } /* the last pass (zeroes) has to be counted, too */ ++quick_npas; /* ...so that we get at least 2 passes... */ quick_npas = (quick_npas <= 1)?2: (quick_npas >= passes)?(passes-1):quick_npas; /* ...and at most 34 passes - 35 wouldn't be quick, would it ? */ quick = FALSE; /* Flagge wieder streichen */ } else if(feil) { feil = FALSE; veilname = *(arv); /* Verschleierungsfile & Co */ if(veilname && *veilname) { int ib; for(ib=0;ib MAXCS)?MAXCS:cache_size; cache_size *= MEGAB; /* Wir machen sowieso nur dann etwas, wenn es Dateinamen gibt */ if(silent) { verbose = FALSE; very_verbose = FALSE; } /* shaddap */ if(very_verbose) verbose = TRUE; /* wenn schon, denn schon */ /* fish ist I/O-bestimmt, d.h. braucht nur wenig CPU. Deshalb reduzieren wir */ if(modst) { int prio; extern int errno; /* die Priorität */ /* aber nicht immer auf den gleichen Wert. Existieren mehrere Instanzen des */ /* Programms, behindern sie einander sonst gegenseitig */ prio = smp_rand() % 20; if(prio < 0) prio = -prio; errno = 0; if(setpriority(PRIO_PROCESS,0,prio) < 0) { if(errno) { char prist[4]; sprintf(prist,"%2d",prio); bleat("setpriority",prist); } } } /* strong encryption needs weak encryption */ if(krypt) obfus = TRUE; if(obfus) get_fish_key(); /* Turbo ? */ if(_have_altivec) flac = USCOR; else flac = SPACE; } else if(!help) { print_usage(); } else { print_help(); } } else { print_help(); } /* not needed, but a few free file descriptors */ if(silent) { if(fclose(stderr) < 0) { bleat("fclose",sterr); } } if(!pthru) { if(fclose(stdin) < 0) { bleat("fclose",stinp); } if(fclose(stdout) < 0) { bleat("fclose",stout); } } TrExi; return(cnt); } /* ------------------------------------------*/ /* The next part is the reversible shredder, */ /* or the file crypter. */ /* ------------------------------------------*/ /* I have to be quite clear: ***THIS IS NOT STRONG ENCRYPTION !*** It's more */ /* like rot13 for geeks. The reason is that the seed for the one-time pad is */ /* there in plain sight at the beginning of the obfuscated file. Anybody who */ /* has access to fish or its source code can decipher the file, knowing that */ /* the gibberish is the result of fish "encryption". But if you want to hide */ /* a naughty video from spying eyes, fish it. "Security by obscurity" at its */ /* best ;-) ...Umm, let's think about this for a moment. What if we improved */ /* the method by modifying the random number generator, using a secret key ? */ /* Well, on second thought, it might be f-f-fairly hot... */ /* Here's the dirt: the first degree modifies the initialization of the prng */ /* so that it spews out a sequence that is completely different from the one */ /* produced by an unmodified generator. This makes a correlation attack with */ /* known plaintext harder. The second degree additionally shuffles the bytes */ /* after obfuscation, swapping them by offsets derived from the bytes of the */ /* secret key and the pseudo-random numbers. Recovery of the secret key will */ /* be computationally very expensive, even if the plaintext were known to an */ /* attacker. The drawback is that the second-degree encryption is not really */ /* fast. Nothing's perfect. In any case : Devour this and go down in flames, */ /* Intel-powered, doubleplusungood rotten Micro$oft Mongrel Carnivore scum ! */ /* * RIP Carnivore : in January 2005, the FBI announced that they ditch it in * favour of unspecified off-the shelf (MS-spawned) sniffer software. BWAHAHAHA. */ /* On the other hand, Echelon appears to still be in bussiness... */ /* In der ersten Ausbaustufe modifizieren wir den Zufallsgenerator mit einem */ /* Geheimschlüssel (beliebige Datei). In der verschärften Version verwürfeln */ /* wir zusätzlich noch die Positionen der Bytes in der Datei. Damit wird ein */ /* Korrelationsangriff (mit bekanntem Klartext) beträchtlich erschwert. */ /* here is a simple byte swapping algorithm that uses our secret key to swap */ /* buffer bytes by up to 65535 positions */ void shuffle_bytes(int nbyt) /* der is allerdings schnarchlangsam */ { _rg u_char *pfa; /* haben wir einen Geheimschlüssel ? */ if((pfa = (u_char *)fish_key)) { _rg u_char *pfe,*wra,*wre,*rda; pfe = pfa + fikl; wra = (u_char *)wrbuf; wre = wra + nbyt; rda = (u_char *)rdbuf; if(!decrypt) { _rg u_char *pfk,*wrb,*rdb; /* straightforward */ pfk = pfa; wrb = wra; rdb = rda; while(wrb < wre) { _rg u_char swb; _rg int swi; /* multiply random vector byte with secret key byte */ /* and use result as swap index offset */ swi = ((int)(*(wrb++)) * (int)(*(pfk++))) % nbyt; if(pfk >= pfe) pfk = pfa; /* key exhausted ? wrap around */ swb = rda[swi]; rda[swi] = *rdb; *(rdb++) = swb; /* swap positions */ } } else { _rg u_char *pfk,*wrb,*rdb,*rde; /* - but to undo, it must be done backwards: */ pfk = pfa + (nbyt % fikl); /* tricky... */ wrb = wre; rde = rda + nbyt; rdb = rde; while(wrb > wra) { _rg u_char swb; _rg int swi; swi = ((int)(*(--wrb)) * (int)(*(--pfk))) % nbyt; if(pfk <= pfa) pfk = pfe; swb = rda[swi]; rda[swi] = *(--rdb); *rdb = swb; } } } /* without a secret key, do nothing */ return; } /* This is the not-so-grim, i.e. reversible shredder */ /* Ver- und Entschleierung von Dateien */ void crypt_the(NODE *node) { TrEnt("crypt_the"); if(node && (node->nodid == ZOMBIE)) { uint32_t *prd; /* nur wenn's was bringt */ if(node->fsiz > 0) { _rg off_t length; int32_t hd[2]; size_t hdsz; uint32_t h0,h1,hul; hdsz = sizeof(hd); if(!pthru) { go_to_the(node); /* in das Verzeichnis mit der Datei */ lseek(node->fild,(off_t)0,SEEK_SET); /* zurückstellen */ } length = node->fsiz; /* Ganze Datei, aber nicht mehr */ prd = rdbuf; /* den brauchen wir für den Anfang */ decrypt = TRUE; /* bis ein anderer Befehl kommt */ /* wir lesen die ersten 4 Bytes */ if(read(node->fild,(void *)&h0,sizeof(h0)) == sizeof(h0)) { char *px; *(prd++) = h0; length -= sizeof(*prd); hd[0] = ntohl(h0); /* ENDIAN */ /* Falls das bitreversierte XOR der nächsten 4 Bytes mit den ersten 4 Bytes */ /* unseren Namen ergibt, dann ist es ein gefishte File (kein gefilte Fish)! */ if(read(node->fild,(void *)&h1,sizeof(h1)) == sizeof(h1)) { int32_t hbx,ipn; *(prd++) = h1; length -= sizeof(*prd); hd[1] = ntohl(h1); /* ENDIAN */ hbx = bitrv(h1^h0); /* h1 und h0 sind nicht geswapt! */ hd[1] = 0; ipn = *(int32_t *)progname; if(hbx == ipn) { /* beim Entschleiern stellen wir den Puffer auf den Anfang zurück */ krypt = FALSE; prd = rdbuf; } else { ipn = *(int32_t *)PROGNAME; if(hbx == ipn) { /* die ist ja verschärft verschlüsselt - */ krypt = TRUE; prd = rdbuf; } else { /* beim Verschleiern machen wir da weiter, wo wir sind */ /* und holen uns eine Zufallszahl für r_init */ decrypt = FALSE; hd[1] = smp_rand(); length += hdsz; /* beim Verschleiern kommt der Header dazu ! */ } } } else { /* Datei hat weniger als 8 Bytes - nix gefishte File */ decrypt = FALSE; hd[1] = smp_rand(); length += hdsz; /* ...muß trotzdem dazu */ } /* das ist jetzt für r_init */ h0 = hd[0]; h1 = hd[1]; h1 ^= h0; /* wir brauchen eine Zwischendatei */ if(!pthru) { if((px = strchr(tmfnam,'_'))) sprintf(px+1,"%08x",h1); tmpfd = open(tmfnam,O_RDWR|O_CREAT|O_TRUNC,S_IRWXU); } else { /* not in a pipe we don't */ strcpy(tmfnam,stout); tmpfd = fileno(stdout); } if(tmpfd >= 0) { _rg int byr,byw; r_kimp(fish_key,fikl); /* Geheimschlüssel importieren */ r_reset(h1); /* Zufallsgenerator neu initialisieren */ if(decrypt) { hdsz = 0; /* wird als Flag für 1. Puffer mißbraucht */ } else { int32_t ipn; /* zuerst kommt die Initialisierung für den Zufallsgenerator in die Datei */ h0 = h1; /* und dahinter schreiben wir noch ein eindeutiges Wapperl, allerdings nicht */ /* einfach so, sondern ein bißchen verschleiert : */ ipn = *(int32_t *)((krypt)?PROGNAME:progname); ipn = htonl(ipn); h1 ^= bitrv(ipn); /* tag : gefishte File */ /* ENDIAN */ hd[0] = htonl(h0); /* network */ hd[1] = htonl(h1); /* order */ if((write(tmpfd,(void *)hd,hdsz) != hdsz)) { bleat("hd - write",tmfnam); } } do { /* so viele Bytes wollen wir lesen */ byr = (length < (WRBLB - hdsz))?length:(WRBLB - hdsz); if(byr > 0) { /* schon fertig ? */ /* und so viele kriegen wir */ if((byw = /* read(node->fild,(void *)prd,byr) */ /* I don't believe this: read() fails when reading from stdin, whereas */ /* fread() does not. I think my brain hurts. */ fread((void *)prd,sizeof(char),byr,node->fist)) > 0) { _rg int nlg,nby; _rg uint32_t *lr,*lw,*lend; #ifdef __APPLE__ _rg int nvec; vector unsigned int *vr,*vw,*vend; #endif /* __APPLE__ */ /* Nur beim 1. Puffer */ if(hdsz) { byw += hdsz; hdsz = 0; } #ifdef __APPLE__ if(_have_altivec) { nvec = byw / VEC_BYTS; nlg = (byw % VEC_BYTS) / sizeof(int32_t); } else { nlg = byw / sizeof(int32_t); } #else /* not __APPLE__ */ nlg = byw / sizeof(int32_t); #endif /* __APPLE__ */ nby = byw % sizeof(int32_t); /* Schreibpuffer mit Zufallszahlen füllen */ /* Wir nehmen die nächstgößere Anzahl ganzer Vektoren */ r_vect((u_char *)wrbuf,howmany(byw,VEC_BYTS)*VEC_BYTS); /* ENDIAN */ /* Die Byte-Ordnung der Zufallszahlen ist bei little verkehrt */ if(ntohl(ZOMBIE) != ZOMBIE) { size_t nw; _rg uint32_t wrd; nw = howmany(byw,VEC_BYTS)*VEC_INTS; swac((void *)wrbuf,(void *)wrbuf,nw,sizeof(uint32_t)); } /* Bei einer verschärft verschlüsselten Datei : */ /* vor der XOR-Operation entwürfeln */ if(krypt && decrypt) shuffle_bytes(byw); #ifdef __APPLE__ /* erst mal die Vektoren wegmachen */ if(_have_altivec && nvec) { vector unsigned int rV,wV; vw = (vector unsigned int *)wrbuf; vr = (vector unsigned int *)rdbuf; vend = vr + nvec; switch(nvec % 8) { /* Duff AND AltiVec - */ case 0: do { /* - how efficient can you get ? */ rV = vec_ld(0,vr); wV = vec_ld(0,vw++); rV = vec_xor(wV,rV); vec_st(rV,0,vr++); case 7: rV = vec_ld(0,vr); wV = vec_ld(0,vw++); rV = vec_xor(wV,rV); vec_st(rV,0,vr++); case 6: rV = vec_ld(0,vr); wV = vec_ld(0,vw++); rV = vec_xor(wV,rV); vec_st(rV,0,vr++); case 5: rV = vec_ld(0,vr); wV = vec_ld(0,vw++); rV = vec_xor(wV,rV); vec_st(rV,0,vr++); case 4: rV = vec_ld(0,vr); wV = vec_ld(0,vw++); rV = vec_xor(wV,rV); vec_st(rV,0,vr++); case 3: rV = vec_ld(0,vr); wV = vec_ld(0,vw++); rV = vec_xor(wV,rV); vec_st(rV,0,vr++); case 2: rV = vec_ld(0,vr); wV = vec_ld(0,vw++); rV = vec_xor(wV,rV); vec_st(rV,0,vr++); case 1: rV = vec_ld(0,vr); wV = vec_ld(0,vw++); rV = vec_xor(wV,rV); vec_st(rV,0,vr++); } while(vr < vend); } lw = (uint32_t *)vw; lr = (uint32_t *)vr; lend = lr + nlg; } else { #else /* not __APPLE__ */ lw = wrbuf; lr = rdbuf; lend = lr + nlg; #endif /* __APPLE__ */ #ifdef __APPLE__ } #endif /* die 32-bit ints wegmachen */ if(nlg) { switch(nlg % 8) { /* Hi Mr. Duff */ case 0: do { *(lr++) ^= *(lw++); case 7: *(lr++) ^= *(lw++); case 6: *(lr++) ^= *(lw++); case 5: *(lr++) ^= *(lw++); case 4: *(lr++) ^= *(lw++); case 3: *(lr++) ^= *(lw++); case 2: *(lr++) ^= *(lw++); case 1: *(lr++) ^= *(lw++); } while(lr < lend); } } /* und dann den krummen Rest byte-weise erledigen */ if(nby) { _rg u_char *br,*bw; bw = (u_char *)lw; br = (u_char *)lr; switch(nby % sizeof(int32_t)) { case 3: *(br++) ^= *(bw++); case 2: *(br++) ^= *(bw++); case 1: *(br++) ^= *(bw++); } } /* beim verschärften Verschlüsseln werden die Bytes hier verwürfelt */ if(krypt && !decrypt) shuffle_bytes(byw); /* wegschreiben */ if((write(tmpfd,(void *)rdbuf,byw) != byw)) { bleat("write",tmfnam); } } else if(byw < 0) { bleat("read",node->nnam); } if(very_verbose) progress_bar(so_far,all_files); so_far -= byw; length -= byw; /* Pufferzähler dekrementieren */ prd = rdbuf; } else byw = byr; } while(byw > 0); close(tmpfd); tmpfd = -1; } else bleat("open",tmfnam); } else { bleat("hd[0] - read",node->nnam); } /* Jetzt ist der Inhalt der Datei verschlüsselt - Klappe zu */ if(node->fist) { if(fclose(node->fist)) { restore_path(node); bleat("fclose",node->path); } node->fist = NULL; node->fild = -1; if(!noact && !pthru) { #ifdef __APPLE__ if(!node->isrf) { #endif /* __APPLE__ */ if(krypt) { pid_t chpid = -1; if(decrypt) { /* no need to shred source ;-) */ if(unlink(node->nnam) < 0) { /* unlink must do */ bleat("unlink",node->nnam); } } else { char *px,*scp = ".scr_",scfnam[TMFNLEN]; /* real encryption ? to be thorough, shred the source file */ Cpy(scfnam,tmfnam); if((px = strchr(scfnam,'_'))) nCpy(scfnam,scp,px-scfnam); else nCpy(scfnam,scp,strlen(scp)); /* rename as a scratch file */ if(rename(node->nnam,scfnam) < 0) { /* umtaufen geht nicht ? */ bleat2("rename",node->nnam,scfnam); if(unlink(node->nnam) < 0) { /* dann nur löschen. */ bleat("unlink",node->nnam); } } else if(!(chpid = fork())) { /* child: schreddern */ char exop[] = "-smd"; /* Das macht unser alter ego */ if((execlp(progpath,progname,exop,scfnam,NULL))) { if(!silent && ErB) { char exnm[] = "execlp("; size_t arsl; arsl = strlen(exnm) + strlen(progpath) + strlen(progname) + strlen(exop) + strlen(scfnam) + 3; /* 2 Kommas, eine Klammer */ ErBln += arsl; if((ErN = adjust_ErB())) { Cat(Cat(Cat(Cat(Cat(Cat(Cat(Cpy(ErN,exnm),progpath), ","),progname),","),exop),scfnam),")"); perror(ErB); } else { perror(progname); } ErBln -= arsl; } } } else if(chpid < 0) { /* error: fork failed */ bleat("fork",""); if(unlink(scfnam) < 0) { /* unlink must do */ bleat("unlink",scfnam); } } /* for the parent: business as usual */ } } else if(unlink(node->nnam) < 0) { /* obfuscating or decrypting ? unlink's enough */ bleat("unlink",node->nnam); } /* now the original file is gone, move the temp file in the former's place */ if(rename(tmfnam,node->nnam) < 0) { /* move dest -> source */ bleat2("rename",tmfnam,node->nnam); } #ifdef __APPLE__ } else { int tmfd; /* temp file has result of resource fork - */ /* apparently you can't move anything into a resource fork. Alright, then we */ /* copy the contents of the temporary file into the target's resource fork. */ if((tmfd = open(tmfnam,O_RDONLY,S_IRUSR)) >= 0) { int ndfd; if((ndfd = open(node->nnam,O_WRONLY,S_IWUSR)) >= 0) { ssize_t fs; while((fs = read(tmfd,(void *)wrbuf,WRBLB)) > 0) { if((write(ndfd,(void *)wrbuf,fs)) < fs) break; } close(ndfd); ndfd = -1; } if(close(tmfd) < 0) { bleat("close",tmfnam); } tmfd = -1; } /* When encrypting, the temp file is gibberish. Decrypting, we don't care. */ /* So there's no need to shred the temp. */ if(unlink(tmfnam) < 0) bleat("unlink",tmfnam); } #endif /* __APPLE__ */ /* restore file mode and access times */ if(chmod(node->nnam,node->mode) < 0) { bleat("chmod",node->nnam); } else if(utimes(node->nnam,node->time) < 0) { bleat("utimes",node->nnam); } else { node->mchd = FALSE; } } } --numfiles; go_home(); /* zum Ausgangspunkt zurück */ } } TrExi; return; } NODE /* we set up a list, if */ *there_are_files() { NODE *node; TrEnt("there_are_files"); if((node = first_nod)) { /* current node */ /* da das Traversieren eines Filesystem-Baums brontal */ /* flott geht, wird zuerst der ganze Baum aufgedröselt */ while(node) { NODE *neno; neno = node->next; if(node->mode && node->nnam) { mode_t shfmt; shfmt = node->mode & S_IFMT; if((shfmt != S_IFREG) && (shfmt != S_IFDIR)) { /* könnte ein Link sein. Unheil! Unheil!!! */ node->mode = 0; /* in der Liste deaktivieren, */ if(delfi) { /* aber echt löschen nur wenn's ernst ist */ restore_path(node); if(unlink(node->path) < 0) { bleat("unlink",node->path); } else { NODE *drct; if((drct = node->drct)) drct->ecnt -= 1; /* mitzählen */ } } /* so, und jetzt wollen wir mal sehen, ob's ein Verzeichnis ist */ } else if(shfmt == S_IFDIR) { /* jawoll. Wird aufgelöst. */ neno = do_directory(node); } #ifdef __APPLE__ else if(!pthru && (shfmt == S_IFREG)) { /* look for resource fork */ neno = do_resource(node); } #endif /* __APPLE__ */ } node = neno; } } TrExi; return(last_node); } /* shredder engine */ int p_vect(uint32_t *buffer,NODE *myno) { _rg int vbi; uint32_t *bitpap; int bufind,bufend,patind; TrEnt("p_vect"); bitpap = myno->bpat[myno->ipas]; #ifdef __APPLE__ if(_have_altivec) { _rg int bpi; _rg vector unsigned int *v_wrb,*v_wre,*v_pat,*v_pap,*v_pep; vbi = 0; bpi = 0; /* Zwischenpuffer mit periodischem Bitmuster füllen */ while(vbi < VEC_PATLL) { vec_patt[vbi++] = bitpap[bpi++]; if(bpi >= PATLL) { if(vbi > VEC_PATLL) break; bpi = 0; } /* das machen wir einmal pro Durchlauf - */ } v_wrb = (vector unsigned int *)wrbuf; if(myno->fsiz < WRBLB) vbi = howmany(myno->fsiz,VEC_BYTS); else vbi = VEC_WRBLL; v_wre = v_wrb + vbi; ++v_wre; /* Wir brauchen hintendran etwas Platz */ v_pat = (vector unsigned int *)vec_patt; v_pap = v_pat; v_pep = v_pap + PATLL; /* Weil im Bundle Modus die Files so oft wechseln, soll's flott gehen */ while(v_wrb < v_wre) { vector unsigned int v_scr; v_scr = vec_ld(0,(u_int *)v_pap++); vec_st(v_scr,0,(u_int *)v_wrb++); if(v_pap >= v_pep) v_pap = v_pat; } } else { /* müssen wir's skalar machen */ #endif /* __APPLE__ */ bufind = 0; patind = 0; if(myno->fsiz < WRBLB) { bufend = howmany(myno->fsiz,sizeof(uint32_t)); } else { bufend = WRBLL; } bufend += (PATLL + PATLL); while(bufind < bufend) { wrbuf[bufind++] = bitpap[patind++]; if(patind >= PATLL) { if(bufind > (bufend - PATLL)) break; patind = 0; } } vbi = bufind - 1; #ifdef __APPLE__ } #endif TrExi; return(vbi); } void shred_the(NODE *node,int i_pass) { TrEnt("shred_the"); if(node != curd_node) { if(!noact) { node->ipas = i_pass; /* braucht p_vect */ if(delfi) { /* Wenn's ernst ist, Dateinamen auch schreddern */ if( #ifdef __APPLE__ /* Resource Fork wird nicht umgetauft*/ !node->isrf && #endif /* __APPLE__ */ /* und "." und ".." wollen wir auch nicht */ (node->blen > 2)) { rename_the(node,i_pass); } } } /* nur wenn's was bringt */ if(node->bpat && (node->fsiz > 0)) { _rg off_t length; uint32_t *bitpap; if((length = lseek(node->fild,(off_t)0,SEEK_SET)) != 0) { bleat("lseek",node->path); } length = node->fsiz; /* Ganze Datei, aber nicht mehr */ if(veilname && (i_pass == (passes - 1))) { /* Schleierdatei */ /* den Schreibpuffer füllen wir einmal pro Gruppe mit der Schleierdatei */ if(!veillen) { _rg int wbl,vlv,vlo; _rg char *wbp,*wbz; if(!banks && !jack && !spoof) { FILE *veilfile; if((veilfile = fopen(veilname,"r"))) { veillen = read(fileno(veilfile),wrbuf,WRBLB); if(veillen <= 0) banks = TRUE; fclose(veilfile); } else banks = TRUE; /* Schleierdatei funzt nicht */ } if(banks) { /* Destructive Recall */ veillen = wamolen-1; if(!Rot13) rot13(watermoon,(char *)wrbuf); else memcpy((void *)wrbuf,(void *)watermoon,veillen); } else if(spoof) { /* Destructive Recall */ veillen = ectrlen-1; if(!Rot13) rot13(echeltrig,(char *)wrbuf); else memcpy((void *)wrbuf,(void *)echeltrig,veillen); } else if(jack) { u_char *wrp,*wre; /* something else */ /* the string "notthekey" gets its bytes bit-reversed and two's-complemented */ veillen = ntkylen-1; memcpy((void *)wrbuf,(void *)notthekey,veillen); wrp = (u_char *)wrbuf; wre = wrp + veillen; while(wrp < wre) { u_char uc; uc = cbitrv(*wrp); *(wrp++) = ~uc; } /* ...lo and behold! It was a misnomer after all. */ } /* the write buffer is the limit */ vlo = (veillen < WRBLB)?veillen:WRBLB; /* original length */ vlv = vlo; /* variable length */ wbp = (char *)wrbuf; /* source pointer */ wbz = wbp + vlv; /* target pointer */ wbl = WRBLB - vlv; /* chunk size */ /* now the veil file is replicated until the write buffer is (sort of) full */ do { /* at least once */ memcpy((void *)wbz,(void *)wbp,vlv); veillen += vlv; /* update total length */ wbz += vlv; /* ...target pointer */ wbl -= vlv; /* and space remaining */ if(wbl > 0) { if(vlv > wbl) { /* chunk too big? Slice in half */ do { vlv /= 2; } while(vlv > wbl); /* until it fits */ if(vlv < vlo) break; /* No half things */ } else { vlv += vlv; } /* double the chunk to be copied */ } } while(wbl > 0); } if(veillen > 0) { int bufoff; bufoff = 0; do { _rg int nbytes; nbytes = (length < veillen)?length:veillen; if(bufoff >= veillen) bufoff = 0; /* - weil wir beim Schreiben immer dort weitermachen, */ write(node->fild,(void *)(wrbuf + bufoff),nbytes); if(very_verbose) progress_bar(so_far,all_files); so_far -= nbytes; /* wo wir das letzte Mal aufgehört haben. */ bufoff += nbytes; length -= nbytes; } while(length > 0); } } else { bitpap = node->bpat[i_pass]; /* zufällig oder festgelegtes Muster ? */ if(*bitpap == RBYT) { /* zufällig */ do { _rg int nbytes; size_t wrbbyt; wrbbyt = (WRBLL * sizeof(int32_t)); nbytes = (length < wrbbyt)?length:wrbbyt; /* Puffer mit Zufallszahlen füllen */ r_vect((u_char *)wrbuf,howmany(nbytes,VEC_BYTS)*VEC_BYTS); write(node->fild,(void *)wrbuf,nbytes); /* in Datei schreiben */ if(very_verbose) progress_bar(so_far,all_files); so_far -= nbytes; length -= nbytes; /* Pufferzähler dekrementieren */ } while(length > 0); } else { int bufoff; /* Muster */ p_vect(wrbuf,node); /* den Schreibpuffer füllen wir nur einmal */ bufoff = 0; do { _rg int nbytes; nbytes = (length < (WRBLL * sizeof(int32_t)))?length: (WRBLL * sizeof(int32_t)); /* - weil wir beim Schreiben immer dort weitermachen, */ write(node->fild,(void *)(wrbuf + bufoff),nbytes); if(very_verbose) progress_bar(so_far,all_files); so_far -= nbytes; /* wo wir das letzte Mal aufgehört haben. */ bufoff += BUFRM; bufoff %= PATLL; length -= nbytes; } while(length > 0); } } } /* Jetzt ist der Inhalt der Datei gründlich versaut - Klappe zu */ if((((zero || veilname) && (i_pass == (passes - 1))) || (!(zero || veilname) && (i_pass == (passes - 2))))) { if(delfi && node->fist) { int err = 0; off_t trunc; extern int errno; /* irgendwo abhacken; die Länge gewinnen wir aus der Filegröße, */ /* dem File-Deskriptor, der inode-Nummer und dem Würfelpuffer : */ trunc = trunc_len(node->inon,node->fsiz,node->fild); if(trunc > (GIGAB / 7)) trunc %= cache_size; /* uh oh */ if(empty) { /* Da ist gar nix frei - */ if(trunc < node->fsiz) { /* nur verkürzen */ err = ftruncate(node->fild,trunc); } } else { /* sonst ist normalerweise was frei, */ do { /* aber man weiß nie, wieviel */ if(!(err = ftruncate(node->fild,trunc))) break; trunc /= 13; /* things appear to be somewhat cramped */ } while((errno == ENOSPC) && trunc); } if((err < 0) && (errno != ENOSPC)) { bleat2("ftruncate",node->nnam,filsze((off_t)trunc)); fprintf(stderr,"\n"); } } if(node->fist) { /* Affe gleich tot... */ if(fclose(node->fist) < 0) { restore_path(node); bleat("fclose",node->path); } node->fist = NULL; node->fild = -1; } if(delfi) { NODE *drct; #ifdef __APPLE__ if(!node->isrf) { #endif /* __APPLE__ */ if(unlink(node->nnam) < 0) { extern int errno; if(errno != ENOENT) { restore_path(node); bleat("unlink",node->path); } else { /* well if I can't find it, it's obviously not there.*/ node->mode = 0; if((drct = node->drct)) { drct->ecnt -= 1; } --numfiles; } } else { node->mode = 0; if((drct = node->drct)) { drct->ecnt -= 1; } --numfiles; } #ifdef __APPLE__ } else { /* Ressourcenzweige brauchen wir nicht zu löschen. */ /* Die verschwinden mit dem Datenzweig. */ node->mode = 0; --numfiles; } #endif /* __APPLE__ */ } } } TrExi; return; } /* this is all very well, but them @$§%! disk drives have giant caches */ /* these days, and we can write clever bit patterns into files until we */ /* are green in the face, while the disk drive smugly updates its cache */ /* with each pass, and only writes the stuff to the actual disk platter */ /* when the system is shut down. Ahem. Sooo - we are more cleverer than */ /* a friggen hard disk controller, aren't we ? Now here's my plan: Open */ /* LOTS of files that want shredding AT ONCE, and shred them all at the */ /* same time. This fills up the drive's cache, and eventually the thing */ /* will HAVE TO write the data to the physical storage device! Well, at */ /* least that's what occurs if everything is as we assume, i.e. the 250 */ /* files the system lets us have open at a time are big enough to flood */ /* the drive's cache. It all depends on the amount of bytes we shred in */ /* one go being larger than the cache, yeah that's all there is to say. */ /* * And if we only have just one pathetic teensy weensy little file ? * Well that's taken care of, too (a little further down). */ int filcnt = 0; NODE *lano = NULL; NODE *do_node(NODE *node) { NODE *lsnd; TrEnt("do_node"); restore_path(node); if(node->path && node->mode) { mode_t shfmt; shfmt = node->mode & S_IFMT; if(shfmt == S_IFREG) { lsnd = node; /* den letzten merken wir uns */ /* is ne richtije Datei. Uffmachen. Gnadenlos. */ if(pthru) { node->fist = stdin; } else { mode_t nomo; #ifdef __APPLE__ if(!(node->isrf)) { #endif /* __APPLE__ */ nomo = node->mode; nomo |= (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); if(chmod(node->path,nomo) < 0) { extern int errno; if(errno != ENOENT) { bleat("chmod",node->path); } } else { node->mchd = TRUE; } #ifdef __APPLE__ } #endif /* __APPLE__ */ if(!(node->fist = fopen(node->path,"r+"))) { extern int errno; if(errno != ENOENT) { bleat("fopen",node->path); } } } if(node->fist) { ++filcnt; node->fild = fileno(node->fist); /* Soo mein Bübchen, wie groß sind wir denn ? */ if(pthru) { /* from stdin to stdout - no way to know how much is */ /* going to come. Make it just a little bit bigger than it could possibly be */ node->fsiz = LUDICROUS; /* this ought to do */ } else { fseeko(node->fist,(off_t)0,SEEK_END); node->fsiz = ftello(node->fist); /* so lang isser */ } if(!pthru) { if((node->fsiz < 0) /* Datei ist krank */ #ifdef __APPLE__ || (node->isrf && (node->fsiz == 0)) /* empty resrc fork */ #endif /* __APPLE__ */ ) { if(node->fsiz < 0) { bleat("ftello",node->path); } if(fclose(node->fist)) { bleat("fclose",node->path); } node->fist = NULL; node->fild = -1; node->mode = 0; --filcnt; /* Der zählt nicht */ --numfiles; } else { /* Der aber schon */ /* Jede Datei kriegt eine eigens verwürfelte Reihenfolge */ do_shuffle(node); if(!silent) { if(node->fsiz > 0) { fprintf(stderr,"%s%s", #ifdef __APPLE__ (node->isrf)?" --- ": #endif /* __APPLE__ */ "",node->nnam); if(very_verbose) { fprintf(stderr,", %s",filsze(node->fsiz)); } fprintf(stderr,"\n"); } } all_files += node->fsiz; } } /* sollen wir richtig oder nur aus Spaß ? */ if(noact) { if(node->fist) { if(fclose(node->fist)) { bleat("fclose",node->path); } if((node->fist = fopen("/dev/null","r+"))) { node->fild = fileno(node->fist); } else { node->fild = -1; } } } } } else lsnd = lano; } TrExi; return(lsnd); } NODE /* if there are some files, we have to deal with them, so that */ *only_dirs_are_left() { NODE *node,*beno; static char fan[] = "/-\\|"; static size_t fansz = sizeof(fan)-1; TrEnt("only_dirs_are_left"); beno = last_node; while((node = beno)) { NODE *enno,*prno = NULL; int ipas,pas0; size_t maxnnod; /* Erstmal machen wir ein Bündel Files auf */ maxnnod = MAXNNOD; if(veilname) --maxnnod; lano = NULL; filcnt = 0; all_files = 0; while(node) { prno = node->prev; lano = do_node(node); if(filcnt > maxnnod) { break; } node = prno; } enno = prno; if(!pthru) { #ifdef __APPLE__ /* here is a nice one. If the last node in the bunch holds a data fork, the */ /* corresponding resource fork gets orphaned when the data fork is deleted, */ /* and is no longer accessible; rem_node will fail, and the node can't be */ /* deleted from the list. In the end, wipe_them_too will fail as well. */ /* So we strike the last node from the list if it's a data fork */ if(enno && enno->isrf) { NODE *lsnd; if((lsnd = do_node(enno))) { lano = lsnd; enno = enno->prev; } } #endif /* __APPLE__ */ if(very_verbose) { if(filcnt == 1) { fprintf(stderr,"%d %s, %s ",filcnt, (ger)?"Datei":"file",filsze(all_files)); } else if(filcnt > 1){ fprintf(stderr,"%d %s %ld %s, %s",filcnt, (ger)?"von":"of",numfiles,(ger)?"Dateien":"files",filsze(all_files)); } } /* wenn es lauter kleines Geschisse ist, legen wir noch eine */ /* zusätzliche Datei an, damit der Laufwerks-Cache überläuft */ if(!(obfus)) { if(!noact && (empty || (all_files < cache_size))) { if(lano) { char *d_nam,*b_nam; size_t bnl; FILE *pdst = NULL; d_nam = go_to_the(lano); /* d_nam : flag für Verzeichnisanteil */ /* Platzhalter machen */ if(ger) { b_nam = podnam; bnl = podl; } else { b_nam = pafnam; bnl = pafl; } if(!(pdst = fopen(b_nam,"w"))) { bleat("fopen",b_nam); } else { NODE *drct; off_t fsiz; size_t pan_sz; char *p_nam,*psl; Stat pst; int pdfd; pdfd = fileno(pdst); fsiz = (cache_size - all_files + 1); fsiz = howmany(fsiz,VEC_BYTS)*VEC_BYTS; /* das würde den Cache _exakt_ füllen. Wir würfeln noch was dazu */ /* - mehr schadet nicht, zumal es eine große Datei ist. */ if(fstat(pdfd,&pst) < 0) { bleat("fstat",b_nam); } else { /* Wenn wir den Auftrag haben, den leeren Speicherplatz zu überschreiben, */ /* machen wir die Polsterdatei so groß wie geht (kann sehr groß werden) : */ if(empty) { FsInfo fs_info; /* siehe oben typedef FsInfo */ if(!(fs_stat(pdfd,fs_info))) { fsiz = #ifdef __APPLE__ fs_info.f_bsize #else /* not __APPLE__ */ fs_info.f_frsize #endif /* __APPLE__ */ * fs_info.f_bavail; } else { fsiz += trunc_len(pst.st_ino,fsiz,pdfd); } } else { fsiz += trunc_len(pst.st_ino,fsiz,pdfd); } } fsiz = (fsiz < sizeof(fsiz))?sizeof(fsiz):fsiz; /* Basta. */ /* wir setzen schon mal die Größe richtig */ /* - d.h. wir schreiben am Ende was rein. Das hält den Betrieb nicht auf. */ if(lseek(pdfd,(off_t)(fsiz-sizeof(fsiz)),SEEK_END) < 0) { bleat2("lseek",b_nam,filsze(fsiz)); } else { if(write(pdfd,(void *)&fsiz,sizeof(fsiz)) != sizeof(fsiz)) { bleat2("write",b_nam,"fsiz"); } } if(close(pdfd) < 0) { bleat("close",b_nam); } pdfd = -1; /* Verzeichnis */ psl = lano->psls; if((drct = lano->drct)) { restore_path(drct); d_nam = drct->path; drct->ecnt += 1; /* immer schön Buch führen */ } else if(d_nam) { if(psl) *psl = NULLC; d_nam = lano->path; } /* kompletten Pfad zusammenbasteln */ pan_sz = strlen(d_nam) + bnl + 3; p_nam = (char *)malloc(nxpw2(pan_sz)); if(!p_nam) { bleat(manm,"p_nam"); } else { NODE *pdno; Cat(Cat(Cpy(p_nam,d_nam),slast),b_nam); go_home(); /* neuen Node nach dem letzten geöffneten einfügen */ if((pdno = add_node(p_nam,lano,TRUE))) { if(very_verbose) { fprintf(stderr,", %s %s ",filsze(fsiz),b_nam); } /* Auch die Polsterdatei kriegt eine eigens verwürfelte Reihenfolge */ do_shuffle(pdno); pdno->fsiz = fsiz; pdno->drct = drct; pdno->ispd = TRUE; if((pdno->fist = fopen(pdno->path,"r+"))) { pdno->fild = fileno(pdno->fist); } else { bleat("fopen",pdno->path); } /* Wird natürlich mitgezählt ! */ all_files += pdno->fsiz; } free((void *)p_nam); } if(psl) *psl = SLASH; /* reparieren */ } } } if(very_verbose) { fprintf(stderr,"\n"); } } } if(veillen) veillen = 0; /* force reading of veil file for each bunch */ if(!quick_npas) { pas0 = 0; } else { pas0 = passes - quick_npas; } for(ipas=pas0;ipasbpat) break; /* that has the bit patterns */ node = node->prev; } if(node && node->bpat) { bitpap = node->bpat[ipas]; } else { bitpap = (uint32_t *)(Gutmann[ipas]); } /* zufällig oder festgelegtes Muster ? */ if(*bitpap == RBYT) { /* zufällig */ fprintf(stderr,"%c%s %2d: %s %s",flac, (ger)?"Lauf":"pass",(ipas-pas0)+1, (ger)?"zufällig":"random", (very_verbose)?"":" \r"); } else { if(veilname && (ipas == (passes-1))) { /* Schleierdatei */ fprintf(stderr,"%c%s %2d: %s %s",flac, (ger)?"Lauf":"pass",(ipas-pas0)+1, (banks)?imblabel: ((jack)?jjvlabel: ((spoof)?fbilabel: ((ger)?"Schleierdatei":"veil file"))), (very_verbose)?"":"\r"); } else { u_char *bpp; /* Bitmuster */ bpp = (u_char *)bitpap; fprintf(stderr,"%c%s %2d: 0x%02x 0x%02x 0x%02x %s",flac, (ger)?"Lauf":"pass",(ipas-pas0)+1, bpp[0],bpp[1],bpp[2], (very_verbose)?"":"\r"); } } fflush(stderr); } } node = last_node; while(node != enno) { NODE *prno; prno = node->prev; if(node->fist) { cuno = node; /* Allons enfants */ if(obfus) { crypt_the(node); } else { shred_the(node,ipas); } } node = prno; } /* We've written the stuff to the disk buffers - but will it ever end */ /* on the disk platters themselves? We'll have to make sure it does : */ if(!noact) { node = last_node; if(verbose) { if(very_verbose) { progress_bar(so_far,all_files); putc('\b',stderr); } else { fprintf(stderr,"\r%c%s\r",flac,pnix); fflush(stderr); fprintf(stderr,"\r%cfsync...\r",flac); fflush(stderr); } } while(node != enno) { NODE *prno; prno = node->prev; if((node != curd_node) && (node->fild >= 0)) { if(very_verbose) { putc(fan[(node->fild)%fansz],stderr); fflush(stderr); putc('\b',stderr); } /* NB.: I can't say for sure, but since, at the time of writing, there's a */ /* Darwin kernel bug that panics when a thread is started incorrectly, and */ /* fish dies horribly (with a kernel panic) on the other machine, methinks */ /* here's a safer place for fsync to be called than amidst lots of writes. */ fsync(node->fild); /* An entirely unexpected - but not unwelcome - side effect of this way of */ /* doing things is that the whole procedure now runs noticeably faster :-) */ } /* Uhm there are still occasional panics.... A big file may now fill a system */ /* buffer, and I'm not sure what happens when we sync a giant buffer which in */ /* turn may be subject to being swapped ?! - bugs me */ node = prno; } } if(verbose) { fprintf(stderr,"\r%c%s\r",flac,pnix); fflush(stderr); } /* unsere Signal-Handler werden bis auf Weiteres nicht mehr benötigt */ signal(SIGINT,SIG_DFL); signal(SIGTERM,SIG_DFL); if(obfus) break; /* one pass-encryption */ } } beno = node; /* aber vorher entfernen wir leere Verzeichnisse und abgearbeitete Dateien */ if(!noact && delfi) { go_home(); rem_dirs(TRUE); } /* und die dicke Polsterdatei machen wir nur einmal */ if(empty) empty = FALSE; } TrExi; return(last_node); } void /* if there are directories left after all this shredding and deleting, */ wipe_them_too() { size_t orest,nrest,natt; TrEnt("wipe_them_too"); if(!noact) { if(delfi) { natt = 0; if((orest = rem_dirs(FALSE))) { /* prime the... */ while((nrest = rem_dirs(FALSE))) { /* ...bilge pump */ natt = (nrest == orest)?(natt+1):0; if(natt > NDINSH) { break; } /* Kannz ma sehen, was für'n zähes Leben so ein dummer Käfer hat. */ /* Seit fast einem Jahr war diese Zeile falsch rum, und ich hab's */ /* nicht gemerkt. There is no such thing as a bug-free programme. */ else orest = nrest; } } } } r_reset(0); /* ...and paranoidly wipe the random generator */ /* What happens in the following section is an act of sheer desperation. Some */ /* might call it "hacking the Gordian Knot", I call it a kludge. Shame on me. */ /* Due to at least one (1) vile and unspeakable act of stupidity (to be ident-*/ /* and rect-ified at a later time), the path names of the remaining nodes are */ /* corrupt, and the associated files can no longer be found. Thus, we have to */ /* resort to letting the shell perform the mopping-up operation. */ /* 21.07.2005----------------------------------------------------------------*/ /* Said vile act has been identified ! (see do_resource) And rectified, too. */ /* --------------------------------------------------------------------------*/ if(!noact && delfi) { /* for some incomprehensible reason(s), */ if(nrest) { /* we could not get rid of the empty directories. */ /* But! We kept the list of file arguments to shred - */ if(filarp) { int arc; char **arp; go_home(); arp = filarp - 1; arc = filarc + 1; /* - and we deal with them the regular way. */ while(--arc) { pid_t cid; FILE *cmp; size_t cml; char *cms,rms[] = "/bin/rm -rf "; ++arp; /* assemble a command string */ cml = strlen(rms); cml += strlen(*arp) + 2; cml = nxpw2(cml + 1); if((cms = (char *)calloc(cml,sizeof(char)))) { /* This is a shell command, so any shell metacharacters in a file name will */ /* likely have side effects, the most common one being prevention of command */ /* execution. So we escape the metacharacters by quoting the file name. */ Cat(Cat(Cat(Cpy(cms,rms),"\""),*arp),"\""); /* remove unconditionally */ if((cmp = popen(cms,"r"))) { char *cmr; char cmb[KILOB]; while((cmr = fgets(cmb,KILOB,cmp))) { extern int errno; errno = 0; bleat(rms,*arp); /* tell us when we fail */ } pclose(cmp); } else bleat("popen",cms); free((void *)cms); cml = 0; } else bleat(canm,rms); } } } } TrExi; return; } int /* wrap it all up */ main(int ac,char **av) { get_lang(); if(there_are_options(ac,av)) { if(there_are_files()) { if(only_dirs_are_left()) { wipe_them_too(); } } } bleat(NULL,(ger)?" fertig":" done"); exit(0); } /*+*/ /*-*/