ADDED makefile Index: makefile ================================================================== --- makefile +++ makefile @@ -0,0 +1,30 @@ +include makerules + +# pass debug=yes for debugging symbols, and to disable +# stripping and optimization of all binaries + +# to build project contained in their own directory, run +# $ make (dir).proj + +all: ctl conv xutil gen + +xutil: xpriv safekill + # X11 tools +gen: mkpw rosshil + # procedural generators +conv: ord + # converters +ctl: nkvd.so bgrd + # manipulating disobedient software + +nkvd.so: nkvd.c + $(CC) -fPIC -pie $< -o$@ -Wl,-E -ldl $(nkvd-flags) + +bgrd: bgrd.c + $(cc) $< -lutil -o$@ $(cc-post) + +safekill: safekill.c + $(cc) $< -lX11 -o$@ $(cc-post) + +xpriv: xpriv.c + $(cc) $< -lrt -lutil -lX11 -o $@ $(cc-post) ADDED makerules Index: makerules ================================================================== --- makerules +++ makerules @@ -0,0 +1,35 @@ +# vim: ft=make + +MC = ocamlopt.opt +mc-opt = 3 +mc-flags = $(if $(debug),-g,-O$(mc-opt)) +mc = $(MC) $(mc-flags) + +SC = chicken-csc +sc-opt = 5 +sc-flags = $(if $(debug),-d3,-O$(sc-opt)) +sc = $(SC) $(sc-flags) + +cc-opt = fast +cc-flags = $(if $(debug),-g,-O$(cc-opt)) +cc = $(CC) $(cc-flags) + +post = $(if $(debug),, && strip $@) +cc-post = $(post) +sc-post = $(post) +mc-post = $(post) + +%: %.c + $(cc) $< -o$@ $(cc-post) + +%: %.ml + $(mc) $< -o $@ $(mc-post) + +%: %.scm + $(sc) $< -o $@ $(sc-post) + +%.proj: %/makefile + cd $* && make $* + +%.proj: %/make.sh + cd $* && ./make.sh ADDED nkvd.c Index: nkvd.c ================================================================== --- nkvd.c +++ nkvd.c @@ -0,0 +1,421 @@ +/* [ʞ] nkvd.c - XDG directory enforcer + * ~ lexi hale + * © AGPLv3 + * $ cc -fPIC -pie nkvd.c -Wl,-E -onkvd -ldl + * [-D_NO_GNU [-D_LIBC=…]] [-D_CONF_HOME=…] + * $ export LD_PRELOAD=nkvd.so + * + * ! NOTE: for unknown reasons, nkvd currently only works + * when built without optimizations. it's probably that + * wrecker Trotsky's fault. i'm working on it. + * + * ! WARNING: test this program thoroughly before you start + * exporting it from your shellrcs; if things get fucked, + * you may not be able to execute any binaries with the + * variable set, and will need to log in as root or boot + * off a rescue disk to fix the problem. LD_PRELOAD is + * not a toy. you have been warned. + * + * ! WARNING: while the code *should* theoretically work + * with non-GNU libcs, i don't have access to any + * computers running under such a configuration, so i was + * only able to test that the compilation process doesn't + * explode. there are likely to be bugs. reports & merge + * requests especially are, as always, welcome. + * + * ? tired of programs disrespecting your XDG configuration + * and shitting all over home sweet ~ ? fear no more! + * nkvd is your very own digital thug to whip those + * dissidents into line like the counterrevolutionary + * scum they are. simply command LD to preload nkvd.so, + * and it will wiretap startup, then intercept any calls + * to open(2) (not just fopen) to make sure they + * point where they're supposed to. nkvd's behavior is + * controlled by a number of environment variables: + * + * - XDG_CONFIG_HOME specifies the redirection target, + * to, but can be overridden with nkvd_gulag. if + * neither are set, $HOME/.config will be used + * instead. + * + * - nkvd_hq specifies which directory to protect from + * cyberkulaks. it defaults to your home directory. + * attempts to access dotfiles or dotfolders with + * appropriate names in this directory will be + * redirected. + * + * - nkvd_subversives tells nkvd which programs to + * interdict. it is a colon-separated list of names. + * to determine if a program is on the List, nkvd + * will check the value of basename(argv[0]) against + * its members. if nkvd_subversives is unset, nkvd + * will interdict all programs except for system + * utilities like rm and ls. the first character of + * nkvd_subversives must be either "+", in which case + * it functions as a blacklist, or a "-", in which + * case it functions as a whitelist. + * + * - nkvd_interdict_all tells nkvd which dotfile + * accesses to redirect. by default, it will + * redirect access to files or folders whose paths + * begin with the string $HOME/.(basename(argv[0])), + * instead returning fds to $XDG_CONFIG_HOME/$1. if + * nkvd_interdict_all is set, it will redirect ALL + * accesses to files whose paths begin with + * $HOME/. this is a particularly extreme mode + * to operate nkvd in and it is liable to cause + * serious problems, in particular for any programs + * attempting to access files in ~/.config + * or ~/.local, another XDG directory. it is + * recommended that nkvd_interdict_all should only be + * used with a carefully selected blacklist! + * + * NOTE: if you are compiling nkvd.c for a libc other than + * glibc, be sure to pass the flag -D_NO_GNU to the + * compiler to ensure appropriate behavior at runtime. + * + * TODO: extend nkvd to enforce other XDG directories? + * + * TODO: build labor power, arm the workers, and smash the + * bourgeoisie to impose a dictatorship of the + * proletariat. + * + * TODO: figure out a way to deal with {open,fstat,unlink}at(2) + * + * TODO: do a better job of canonicalizing paths, in case + * anyone tries to be tricky. (alas, realpath() et al + * are not viable for these purposes as they only work + * for files already in existence.) + * + * TODO: -pie allows the program to be run as a standalone + * binary. exploit this; allow it to launch other + * binaries with itself as LD_PRELOAD? detecting this + * state of affairs will likely only be possible + * through checking if LD_PRELOAD is empty. due to the + * difficulty of determining a binary's path on linux, + * this may not be feasible. switch back to -shared + * and delete -Wl,-E if so. + * + * TODO: support a mappings file/variable for those programs + * too special to simply name their config file + * "~/.(argv[0])". + * + * TODO: exempt xdg dirs beginning with ~/. from proscription + * in nkvd_interdict_all=1 mode + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef _NO_GNU + /* the POSIX version of basename is a goddamn mess and + * it's better to use the glibc version where possible. */ +# include +# define SYMSRC hnd +#else + /* glibc's basename() is imported from via + * the following directive. i have no idea how this + * magic works unless they're abusing asm declarations */ +# define _GNU_SOURCE /* for basename() */ +# define __USE_GNU /* for RTLD_NEXT */ +# define SYMSRC RTLD_NEXT +#endif +#include +#include + +#ifndef _LIBC +# define _LIBC "libc.so" +#endif + +#ifndef _CONF_HOME +# define _CONF_HOME ".config" +#endif + + +#define pstr(x) x,sizeof x +#define hl(txt) "\x1b[1;31m" txt "\x1b[m" +#define bold(txt) "\x1b[1m" txt "\x1b[21m" +#define e_fatal hl("(nkvd fatal)") " " +#define fail(code, err) { write(2,pstr(e_fatal " " err)); _exit(code); } +#define dbg(x) write(2,pstr(bold("(nkvd)") " " x)) + +typedef _Bool bool; +enum { false = 0, true = 1 }; + +static bool intercept; +static int wiretap_argc = 0; +static const char** wiretap_argv = NULL; +static const char* redir_prefix; +static const char* redir_to; +static size_t redir_len; +static const char* hq; +static size_t hq_len; + +void configdirs() { + const char* nkvd = getenv("nkvd_gulag"); + if (nkvd != NULL) redir_to = nkvd, redir_len = strlen(nkvd); + + const char* xdg = getenv("XDG_CONFIG_HOME"); + if (xdg != NULL) redir_to = xdg, redir_len = strlen(xdg); + + const char* home = getenv("HOME"); + if (home == NULL) { + const char* user = getenv("USER"); + struct passwd* p; + if (user == NULL) { + uid_t u = geteuid(); + p = getpwuid(u); + } else + p = getpwnam(user); + if(p == NULL) fail(-5, "XDG_CONFIG_HOME, HOME, and USER " + "environment variables are all unset, and no home " + "directory can be identified for your user ID. " + "something is seriously wrong with your system, " + "comrade. bailing."); + home = p -> pw_dir; + } + size_t homelen = strlen(home); + + const char* h = getenv("nkvd_hq"); + if (h != NULL) hq = h, hq_len = strlen(h); + else hq = strdup(home), hq_len = homelen; + +# define CONFDIR "/" _CONF_HOME "/" + char* buf = malloc(homelen + sizeof CONFDIR); + /* since this will be used throughout the lifetime of the + * program, we don't need to free it, and it would actually + * just slow down the exit process to do so. */ + strcpy(buf, home); + strcpy(buf + homelen, CONFDIR); + redir_len = homelen + sizeof CONFDIR; +# undef CONFDIR + + redir_to = buf; +} + +bool checkpath(const char* path, size_t len, char* gulag) { + char c[hq_len + len + 4]; + strncpy(c, hq, hq_len); + c[hq_len] = '/'; + c[hq_len+1] = '.'; + c[hq_len+2] = 0; + + const char* all = getenv("nkvd_interdict_all"); + size_t clen; + if (all == NULL || !(all[0] == '1' && all[1] == 0)) { + strcpy(c + hq_len + 2, wiretap_argv[0]); + clen = strlen(c); + c[clen] = '/'; c[clen+1] = 0; + if (len > clen) ++clen; + } else { + clen = strlen(c); + } + + if (strncmp(path, c, clen) == 0) { + /* guilty as sin, your honor */ + strncpy(gulag,redir_to,redir_len); + strncpy(gulag + redir_len - 1, + path + hq_len + 2, + len - (hq_len + 1)); + return true; + } else { + /* no further questions, citizen */ + return false; + } +} + +bool interrogate(const char* path, size_t plen, char* gulag) { + /* papers, please */ + if (path[0] != '/') for (size_t i = 64; i ; © GNU Affero GPL v3 ; $ chicken-csc -O5 sexpc.scm; strip sexpc ; > ./sexpc ; ? a tool to convert sheets of s-expressions into