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: |
7ba40af07e719881adf5a5f139094d85 |
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