util  Check-in [7ba40af07e]

Overview
Comment:add nkvd.c and make infra
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 7ba40af07e719881adf5a5f139094d8504c180f60511c08b68abf52bf9cbb3c4
User & Date: lexi on 2019-07-25 11:06:57
Other Links: manifest | tags
Context
2019-07-25
11:08
fix typo check-in: 10946f3ca5 user: lexi tags: trunk
11:06
add nkvd.c and make infra check-in: 7ba40af07e user: lexi tags: trunk
2019-07-24
22:36
add support for conditionals, array indexing, and reference-taking check-in: 844294c3a4 user: lexi tags: trunk
Changes

Added makefile version [6d462c849e].

            1  +include makerules
            2  +
            3  +# pass debug=yes for debugging symbols, and to disable
            4  +# stripping and optimization of all binaries
            5  +
            6  +# to build project contained in their own directory, run 
            7  +# $ make (dir).proj
            8  +
            9  +all: ctl conv xutil gen
           10  +
           11  +xutil: xpriv safekill
           12  +  # X11 tools
           13  +gen: mkpw rosshil
           14  +  # procedural generators
           15  +conv: ord
           16  +  # converters
           17  +ctl: nkvd.so bgrd
           18  +  # manipulating disobedient software
           19  +
           20  +nkvd.so: nkvd.c
           21  +	$(CC) -fPIC -pie $< -o$@ -Wl,-E -ldl $(nkvd-flags)
           22  +
           23  +bgrd: bgrd.c
           24  +	$(cc) $< -lutil -o$@ $(cc-post)
           25  +
           26  +safekill: safekill.c
           27  +	$(cc) $< -lX11 -o$@ $(cc-post)
           28  +
           29  +xpriv: xpriv.c
           30  +	$(cc) $< -lrt -lutil -lX11 -o $@ $(cc-post)

Added makerules version [8c92429cd1].

            1  +# vim: ft=make
            2  +
            3  +MC = ocamlopt.opt
            4  +mc-opt = 3
            5  +mc-flags = $(if $(debug),-g,-O$(mc-opt))
            6  +mc = $(MC) $(mc-flags)
            7  +
            8  +SC = chicken-csc
            9  +sc-opt = 5
           10  +sc-flags = $(if $(debug),-d3,-O$(sc-opt))
           11  +sc = $(SC) $(sc-flags)
           12  +
           13  +cc-opt = fast
           14  +cc-flags = $(if $(debug),-g,-O$(cc-opt))
           15  +cc = $(CC) $(cc-flags)
           16  +
           17  +post = $(if $(debug),, && strip $@)
           18  +cc-post = $(post)
           19  +sc-post = $(post)
           20  +mc-post = $(post)
           21  +
           22  +%: %.c
           23  +	$(cc) $< -o$@ $(cc-post)
           24  +
           25  +%: %.ml
           26  +	$(mc) $< -o $@ $(mc-post)
           27  +
           28  +%: %.scm
           29  +	$(sc) $< -o $@ $(sc-post)
           30  +
           31  +%.proj: %/makefile
           32  +	cd $* && make $*
           33  +
           34  +%.proj: %/make.sh
           35  +	cd $* && ./make.sh

Added nkvd.c version [1b97491783].

            1  +/* [ʞ] nkvd.c - XDG directory enforcer
            2  + *  ~ lexi hale  <lexi@hale.su>
            3  + *  © AGPLv3
            4  + *  $ cc -fPIC -pie nkvd.c -Wl,-E -onkvd -ldl
            5  + *       [-D_NO_GNU [-D_LIBC=…]] [-D_CONF_HOME=…]
            6  + *  $ export LD_PRELOAD=nkvd.so <dissident>
            7  + *
            8  + *  ! NOTE:  for unknown reasons,  nkvd currently only works
            9  + *    when  built without optimizations.  it's probably that
           10  + *    wrecker Trotsky's fault. i'm working on it.
           11  + *
           12  + *  ! WARNING: test this program thoroughly before you start
           13  + *    exporting it from your shellrcs; if things get fucked,
           14  + *    you  may not be able to execute any binaries with  the
           15  + *    variable set,  and will need to log in as root or boot
           16  + *    off  a rescue disk  to fix the problem.  LD_PRELOAD is
           17  + *    not a toy. you have been warned.
           18  + *
           19  + *  ! WARNING:  while the  code *should*  theoretically work
           20  + *    with  non-GNU  libcs,  i  don't  have  access  to  any
           21  + *    computers running under such a configuration, so i was
           22  + *    only able to test that the compilation process doesn't
           23  + *    explode. there are likely to be bugs.  reports & merge
           24  + *    requests especially are, as always, welcome.
           25  + *
           26  + *  ? tired of programs disrespecting your XDG configuration
           27  + *    and shitting  all over  home sweet ~ ?  fear  no more!
           28  + *    nkvd  is your  very  own digital  thug  to whip  those
           29  + *    dissidents  into  line like  the  counterrevolutionary
           30  + *    scum they  are.  simply command LD to preload nkvd.so,
           31  + *    and it will wiretap startup,  then intercept any calls
           32  + *    to  open(2)  (not  just   fopen)  to  make  sure  they
           33  + *    point where  they're supposed  to. nkvd's  behavior is
           34  + *    controlled by a number of environment variables:
           35  + *
           36  + *      - XDG_CONFIG_HOME  specifies the redirection target,
           37  + *        to,  but can  be  overridden  with nkvd_gulag.  if
           38  + *        neither  are  set,   $HOME/.config  will  be  used
           39  + *        instead.
           40  + *
           41  + *      - nkvd_hq  specifies which directory to protect from
           42  + *        cyberkulaks. it  defaults to your  home directory.
           43  + *        attempts  to access  dotfiles  or dotfolders  with
           44  + *        appropriate  names  in   this  directory  will  be
           45  + *        redirected.
           46  + *
           47  + *      - nkvd_subversives  tells  nkvd  which  programs  to
           48  + *        interdict. it is a  colon-separated list of names.
           49  + *        to determine  if a  program is  on the  List, nkvd
           50  + *        will check the  value of basename(argv[0]) against
           51  + *        its  members. if  nkvd_subversives is  unset, nkvd
           52  + *        will  interdict  all  programs except  for  system
           53  + *        utilities  like rm and ls.  the first character of
           54  + *        nkvd_subversives must be either "+", in which case
           55  + *        it functions as a blacklist,  or a  "-",  in which
           56  + *        case it functions as a whitelist.
           57  + *
           58  + *      - nkvd_interdict_all   tells  nkvd   which   dotfile
           59  + *        accesses   to  redirect.   by  default,   it  will
           60  + *        redirect access  to files  or folders  whose paths
           61  + *        begin with  the string $HOME/.(basename(argv[0])),
           62  + *        instead  returning fds to $XDG_CONFIG_HOME/$1.  if
           63  + *        nkvd_interdict_all  is set,  it will  redirect ALL
           64  + *        accesses   to  files   whose   paths  begin   with
           65  + *        $HOME/.  this  is   a  particularly  extreme  mode
           66  + *        to  operate nkvd  in  and it  is  liable to  cause
           67  + *        serious problems,  in particular for  any programs
           68  + *        attempting   to   access    files   in   ~/.config
           69  + *        or   ~/.local,  another   XDG  directory.   it  is
           70  + *        recommended that nkvd_interdict_all should only be
           71  + *        used with a carefully selected blacklist!
           72  + *
           73  + * NOTE: if you are compiling nkvd.c for a libc other than
           74  + *       glibc, be sure to pass the flag -D_NO_GNU to the
           75  + *       compiler to ensure appropriate behavior at runtime.
           76  + *
           77  + * TODO: extend nkvd to enforce other XDG directories?
           78  + *
           79  + * TODO: build labor  power, arm the workers,  and smash the
           80  + *       bourgeoisie  to   impose  a  dictatorship   of  the
           81  + *       proletariat.
           82  + *
           83  + * TODO: figure out a way to deal with {open,fstat,unlink}at(2)
           84  + *
           85  + * TODO: do a better job of canonicalizing paths, in case
           86  + *       anyone tries to be tricky. (alas, realpath() et al
           87  + *       are not viable for these purposes as they only work
           88  + *       for files already in existence.)
           89  + *
           90  + * TODO: -pie allows the  program to be run  as a standalone
           91  + *       binary.  exploit this;  allow  it  to launch  other
           92  + *       binaries with itself  as LD_PRELOAD? detecting this
           93  + *       state  of  affairs  will likely  only  be  possible
           94  + *       through checking if LD_PRELOAD is empty. due to the
           95  + *       difficulty of determining a binary's path on linux,
           96  + *       this may  not be  feasible. switch back  to -shared
           97  + *       and delete -Wl,-E if so.
           98  + *
           99  + * TODO: support a mappings file/variable for those programs
          100  + *       too special to simply name their config file
          101  + *       "~/.(argv[0])". 
          102  + *
          103  + * TODO: exempt xdg dirs beginning with ~/. from proscription
          104  + *       in nkvd_interdict_all=1 mode
          105  + */
          106  +
          107  +#include <stdarg.h>
          108  +#include <unistd.h>
          109  +#include <sys/stat.h>
          110  +#include <sys/types.h>
          111  +#include <fcntl.h>
          112  +#include <limits.h>
          113  +#include <stdlib.h>
          114  +#include <pwd.h>
          115  +#ifdef _NO_GNU
          116  +	/* the POSIX version of basename is a goddamn mess and
          117  +	 * it's better to use the glibc version where possible. */
          118  +#	include <libgen.h>
          119  +#	define SYMSRC hnd
          120  +#else
          121  +	/* glibc's basename() is imported from <string.h> via
          122  +	 * the following directive. i have no idea how this
          123  +	 * magic works unless they're abusing asm declarations */
          124  +#	define _GNU_SOURCE /* for basename() */
          125  +#	define __USE_GNU   /* for RTLD_NEXT  */
          126  +#	define SYMSRC RTLD_NEXT
          127  +#endif
          128  +#include <string.h>
          129  +#include <dlfcn.h>
          130  +
          131  +#ifndef _LIBC
          132  +#	define _LIBC "libc.so"
          133  +#endif
          134  +
          135  +#ifndef _CONF_HOME
          136  +#	define _CONF_HOME ".config"
          137  +#endif
          138  +
          139  +
          140  +#define pstr(x) x,sizeof x
          141  +#define hl(txt) "\x1b[1;31m" txt "\x1b[m"
          142  +#define bold(txt) "\x1b[1m" txt "\x1b[21m"
          143  +#define e_fatal hl("(nkvd fatal)") " "
          144  +#define fail(code, err) { write(2,pstr(e_fatal " " err)); _exit(code); }
          145  +#define dbg(x) write(2,pstr(bold("(nkvd)") " " x))
          146  +
          147  +typedef _Bool bool;
          148  +enum { false = 0, true = 1 };
          149  +
          150  +static bool intercept;
          151  +static int wiretap_argc = 0;
          152  +static const char** wiretap_argv = NULL;
          153  +static const char* redir_prefix;
          154  +static const char* redir_to;
          155  +static size_t redir_len;
          156  +static const char* hq;
          157  +static size_t hq_len;
          158  +
          159  +void configdirs() {
          160  +	const char* nkvd = getenv("nkvd_gulag");
          161  +	if (nkvd != NULL) redir_to = nkvd, redir_len = strlen(nkvd);
          162  +
          163  +	const char* xdg = getenv("XDG_CONFIG_HOME");
          164  +	if (xdg != NULL) redir_to = xdg, redir_len = strlen(xdg);
          165  +
          166  +	const char* home = getenv("HOME");
          167  +	if (home == NULL) {
          168  +		const char* user = getenv("USER");
          169  +		struct passwd* p;
          170  +		if (user == NULL) {
          171  +			uid_t u = geteuid();
          172  +			p = getpwuid(u);
          173  +		} else 
          174  +			p = getpwnam(user);
          175  +		if(p == NULL) fail(-5, "XDG_CONFIG_HOME, HOME, and USER "
          176  +				"environment variables are all unset, and no home "
          177  +				"directory can be identified for your user ID. "
          178  +				"something is seriously wrong with your system, "
          179  +				"comrade. bailing.");
          180  +		home = p -> pw_dir;
          181  +	}
          182  +	size_t homelen = strlen(home);
          183  +
          184  +	const char* h = getenv("nkvd_hq");
          185  +	if (h != NULL) hq = h, hq_len = strlen(h);
          186  +		else hq = strdup(home), hq_len = homelen;
          187  +
          188  +#	define CONFDIR "/" _CONF_HOME "/"
          189  +	char* buf = malloc(homelen + sizeof CONFDIR);
          190  +	/* since this will be used throughout the lifetime of the
          191  +	 * program, we don't need to free it, and it would actually
          192  +	 * just slow down the exit process to do so. */
          193  +	strcpy(buf, home);
          194  +	strcpy(buf + homelen, CONFDIR);
          195  +	redir_len = homelen + sizeof CONFDIR;
          196  +#	undef CONFDIR
          197  +
          198  +	redir_to = buf;
          199  +}
          200  +
          201  +bool checkpath(const char* path, size_t len, char* gulag) {
          202  +	char c[hq_len + len + 4];
          203  +	strncpy(c, hq, hq_len);
          204  +	c[hq_len] = '/';
          205  +	c[hq_len+1] = '.';
          206  +	c[hq_len+2] = 0;
          207  +	
          208  +	const char* all = getenv("nkvd_interdict_all");
          209  +	size_t clen;
          210  +	if (all == NULL || !(all[0] == '1' && all[1] == 0)) {
          211  +		strcpy(c + hq_len + 2, wiretap_argv[0]);
          212  +		clen = strlen(c);
          213  +		c[clen] = '/'; c[clen+1] = 0;
          214  +		if (len > clen) ++clen;
          215  +	} else {
          216  +		clen = strlen(c);
          217  +	}
          218  +		
          219  +	if (strncmp(path, c, clen) == 0) {
          220  +		/* guilty as sin, your honor */
          221  +		strncpy(gulag,redir_to,redir_len);
          222  +		strncpy(gulag + redir_len - 1,
          223  +				path + hq_len + 2,
          224  +				len - (hq_len + 1));
          225  +		return true;
          226  +	} else {
          227  +		/* no further questions, citizen */
          228  +		return false;
          229  +	}
          230  +}
          231  +
          232  +bool interrogate(const char* path, size_t plen, char* gulag) {
          233  +	/* papers, please */
          234  +	if (path[0] != '/') for (size_t i = 64; i<PATH_MAX; i << 1) {
          235  +		char cwd[i + plen + 1];
          236  +		if (getcwd(cwd, i) == NULL) continue;
          237  +		size_t cwdlen = strlen(cwd);
          238  +		cwd[cwdlen] = '/';
          239  +		strncpy(cwd + cwdlen + 1, path, plen + 1);
          240  +		return checkpath(cwd,cwdlen + plen, gulag);
          241  +	}
          242  +	return checkpath(path, plen, gulag);
          243  +}
          244  +
          245  +bool shitlist(const char* name) {
          246  +#	ifdef _NO_GNU /* avoid POSIX weirdness */
          247  +		{ char* name = strdup(name); /* yay shadowing! */
          248  +#	endif
          249  +
          250  +	const char* base = basename(name);
          251  +
          252  +	const char* list = getenv("nkvd_subversives");
          253  +	if (list == NULL) return true;
          254  +
          255  +	if (list[0] != '+' && list[0] != '-') 
          256  +		fail(-4, "the variable $nkvd_subversives needs to "
          257  +				"begin with either a + to indicate blacklist "
          258  +				"mode, or a - to indicate whitelist mode.");
          259  +
          260  +	const enum { blacklist, whitelist } mode =
          261  +		(list[0] == '+' ? blacklist : whitelist);
          262  +
          263  +	const char* p = list + 1;
          264  +	for(;;) {
          265  +#		ifndef _NO_GNU
          266  +			const char* end = strchrnul(p, ':');
          267  +#		else
          268  +			const char* end = strchr(p, ':');
          269  +			if (end == NULL) end = p + strlen(p);
          270  +#		endif
          271  +
          272  +		if(strncmp(base,p,end-p) == 0) return (mode == blacklist);
          273  +
          274  +		if (*end == 0) break;
          275  +			else p = end + 1;
          276  +	}
          277  +
          278  +#	ifdef _NO_GNU
          279  +		free(name);
          280  +#	endif
          281  +	return (mode == whitelist);
          282  +#	ifdef _NO_GNU
          283  +		}
          284  +#	endif
          285  +}
          286  +
          287  +#define STAT_PARAMS  const char* volatile path, struct stat* statbuf
          288  +#define XSTAT_PARAMS int ver, const char* volatile path, struct stat* statbuf
          289  +#define decl_stat_ptr(nm)  static int (*libc_##nm) (STAT_PARAMS)
          290  +#define decl_xstat_ptr(nm) static int (*libc_##nm) (XSTAT_PARAMS)
          291  +
          292  +static int (*libc_open)  (const char*,int,...);
          293  +static int (*libc_unlink)(const char*);
          294  +
          295  +decl_stat_ptr(stat);
          296  +decl_stat_ptr(stat64);
          297  +decl_stat_ptr(lstat);
          298  +decl_stat_ptr(lstat64);
          299  +
          300  +#ifndef _NO_GNU
          301  +	decl_xstat_ptr(xstat);
          302  +	decl_xstat_ptr(xstat64);
          303  +	decl_xstat_ptr(lxstat);
          304  +	decl_xstat_ptr(lxstat64);
          305  +#endif
          306  +
          307  +int nkvd_stat(int (*fn)(STAT_PARAMS), STAT_PARAMS) {
          308  +	if (intercept) {
          309  +		size_t plen = strlen(path);
          310  +		char gulag[redir_len + plen];
          311  +		if (interrogate(path, plen, gulag)) {
          312  +			return (*fn)(gulag,statbuf);
          313  +		}
          314  +	}
          315  +	return (*fn)(path,statbuf);
          316  +}
          317  +
          318  +int nkvd_xstat(int (*fn)(XSTAT_PARAMS), XSTAT_PARAMS) {
          319  +	if (intercept) {
          320  +		size_t plen = strlen(path);
          321  +		char gulag[redir_len + plen];
          322  +		if (interrogate(path, plen, gulag)) {
          323  +			return (*fn)(ver,gulag,statbuf);
          324  +		}
          325  +	}
          326  +	return (*fn)(ver,path,statbuf);
          327  +}
          328  +
          329  +#define def_stat_fn(nm,fn) int nm(STAT_PARAMS) { return nkvd_stat(libc_##fn,path,statbuf); }
          330  +#define def_xstat_fn(nm,fn) int nm(XSTAT_PARAMS) { return nkvd_xstat(libc_##fn,ver,path,statbuf); }
          331  +
          332  +def_stat_fn(stat,stat);
          333  +def_stat_fn(stat64,stat64);
          334  +def_stat_fn(lstat,lstat);
          335  +def_stat_fn(lstat64,lstat64);
          336  +
          337  +#ifndef _NO_GNU
          338  +	def_xstat_fn(__xstat,xstat);
          339  +	def_xstat_fn(__xstat64,xstat64);
          340  +	def_xstat_fn(__lxstat,lxstat);
          341  +	def_xstat_fn(__lxstat64,lxstat64);
          342  +#endif
          343  +
          344  +int unlink(const char* volatile path) {
          345  +	if (intercept) {
          346  +		size_t plen = strlen(path);
          347  +		char gulag[redir_len + plen];
          348  +		if (interrogate(path, plen, gulag)) {
          349  +			return (*libc_unlink)(gulag);
          350  +		}
          351  +	}
          352  +	return (*libc_unlink)(path);
          353  +};
          354  +
          355  +int open(const char* volatile path, int flags, ...) {
          356  +	/* fuck you for using varargs, asshole */
          357  +	va_list v; va_start(v,flags);
          358  +	mode_t m;
          359  +	if(flags & O_CREAT) m = va_arg(v, mode_t);
          360  +	va_end(v);
          361  +
          362  +	if (intercept) {
          363  +		size_t plen = strlen(path);
          364  +		char gulag[redir_len + plen];
          365  +		if (interrogate(path, plen, gulag)) {
          366  +			return (*libc_open)(gulag, flags, m);
          367  +		}
          368  +	}
          369  +	return (*libc_open)(path, flags, m);
          370  +}
          371  +
          372  +int nkvd_init(int argc, const char** argv) {
          373  +	wiretap_argc = argc;
          374  +	wiretap_argv = argv;
          375  +
          376  +#	ifdef _NO_GNU
          377  +		/* RTLD_NEXT is unfortunately a glibc extension, so in
          378  +		* the _NO_GNU case we need to load open() via other
          379  +		* means. */
          380  +		void* SYMSRC = dlopen(_LIBC,RTLD_LAZY);
          381  +		if (SYMSRC == NULL)
          382  +			fail(-2,"compiled in non-glibc mode and cannot load "
          383  +				"libc from " bold(_LIBC) "; please recompile "
          384  +				" and specify your libc with -D_LIBC= on the"
          385  +				"compile command line.");
          386  +#	endif
          387  +
          388  +#	define load_fn(fn,pfx) libc_##fn = dlsym(SYMSRC, #pfx #fn)
          389  +		load_fn(open,);      load_fn(unlink,);
          390  +		load_fn(stat,);      load_fn(lstat,);
          391  +		load_fn(stat64,);    load_fn(lstat64,);
          392  +		
          393  +#		ifndef _NO_GNU
          394  +			load_fn(xstat,__);   load_fn(lxstat,__);
          395  +			load_fn(xstat64,__); load_fn(lxstat64,__);
          396  +#		endif
          397  +#	undef load_fn
          398  +	
          399  +	/* have enough stat fns been found for us to proceed? */
          400  +	bool statfns = (  (libc_stat    && libc_lstat)
          401  +	               || (libc_stat64  && libc_lstat64)
          402  +#ifndef _NO_GNU
          403  +	               || (libc_xstat   && libc_lxstat)
          404  +	               || (libc_xstat64 && libc_lxstat64)
          405  +#endif
          406  +	               );
          407  +
          408  +	if (! (libc_open && statfns && libc_unlink))
          409  +		fail(-3, "your libc is defective. bailing.");
          410  +
          411  +	intercept = shitlist(argv[0]);
          412  +	if (intercept) {
          413  +		configdirs();
          414  +	}
          415  +
          416  +	return 0; /* ?? why is this int, not void */
          417  +}
          418  +
          419  +__attribute__((section(".init_array"))) static void* nkvd_constructor = &nkvd_init;
          420  +
          421  +int main() { return 0; } /* stub for -pie */

Modified sexpc/sexpc.scm from [46b6c9a575] to [8d07b14bd7].

     1         -; [ʞ] sexpc.scm - sexp→c converter
            1  +[ʞ] sexpc.scm - sexp→c converter
     2      2   ;  ~ lexi hale <lexi@hale>
     3      3   ;  © GNU Affero GPL v3
     4      4   ;  $ chicken-csc -O5 sexpc.scm; strip sexpc
     5      5   ;  > ./sexpc <file>
     6      6   ;  ? a tool to convert sheets of s-expressions into
     7      7   ;    valid C, enabling the use of Scheme as a
     8      8   ;    preprocessor language. it also generates C type