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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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 version [8c92429cd1].







































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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 version [1b97491783].











































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
/* [ʞ] nkvd.c - XDG directory enforcer
 *  ~ lexi hale  <lexi@hale.su>
 *  © AGPLv3
 *  $ cc -fPIC -pie nkvd.c -Wl,-E -onkvd -ldl
 *       [-D_NO_GNU [-D_LIBC=…]] [-D_CONF_HOME=…]
 *  $ export LD_PRELOAD=nkvd.so <dissident>
 *
 *  ! 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 <stdarg.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <pwd.h>
#ifdef _NO_GNU
	/* the POSIX version of basename is a goddamn mess and
	 * it's better to use the glibc version where possible. */
#	include <libgen.h>
#	define SYMSRC hnd
#else
	/* glibc's basename() is imported from <string.h> 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 <string.h>
#include <dlfcn.h>

#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<PATH_MAX; i << 1) {
		char cwd[i + plen + 1];
		if (getcwd(cwd, i) == NULL) continue;
		size_t cwdlen = strlen(cwd);
		cwd[cwdlen] = '/';
		strncpy(cwd + cwdlen + 1, path, plen + 1);
		return checkpath(cwd,cwdlen + plen, gulag);
	}
	return checkpath(path, plen, gulag);
}

bool shitlist(const char* name) {
#	ifdef _NO_GNU /* avoid POSIX weirdness */
		{ char* name = strdup(name); /* yay shadowing! */
#	endif

	const char* base = basename(name);

	const char* list = getenv("nkvd_subversives");
	if (list == NULL) return true;

	if (list[0] != '+' && list[0] != '-') 
		fail(-4, "the variable $nkvd_subversives needs to "
				"begin with either a + to indicate blacklist "
				"mode, or a - to indicate whitelist mode.");

	const enum { blacklist, whitelist } mode =
		(list[0] == '+' ? blacklist : whitelist);

	const char* p = list + 1;
	for(;;) {
#		ifndef _NO_GNU
			const char* end = strchrnul(p, ':');
#		else
			const char* end = strchr(p, ':');
			if (end == NULL) end = p + strlen(p);
#		endif

		if(strncmp(base,p,end-p) == 0) return (mode == blacklist);

		if (*end == 0) break;
			else p = end + 1;
	}

#	ifdef _NO_GNU
		free(name);
#	endif
	return (mode == whitelist);
#	ifdef _NO_GNU
		}
#	endif
}

#define STAT_PARAMS  const char* volatile path, struct stat* statbuf
#define XSTAT_PARAMS int ver, const char* volatile path, struct stat* statbuf
#define decl_stat_ptr(nm)  static int (*libc_##nm) (STAT_PARAMS)
#define decl_xstat_ptr(nm) static int (*libc_##nm) (XSTAT_PARAMS)

static int (*libc_open)  (const char*,int,...);
static int (*libc_unlink)(const char*);

decl_stat_ptr(stat);
decl_stat_ptr(stat64);
decl_stat_ptr(lstat);
decl_stat_ptr(lstat64);

#ifndef _NO_GNU
	decl_xstat_ptr(xstat);
	decl_xstat_ptr(xstat64);
	decl_xstat_ptr(lxstat);
	decl_xstat_ptr(lxstat64);
#endif

int nkvd_stat(int (*fn)(STAT_PARAMS), STAT_PARAMS) {
	if (intercept) {
		size_t plen = strlen(path);
		char gulag[redir_len + plen];
		if (interrogate(path, plen, gulag)) {
			return (*fn)(gulag,statbuf);
		}
	}
	return (*fn)(path,statbuf);
}

int nkvd_xstat(int (*fn)(XSTAT_PARAMS), XSTAT_PARAMS) {
	if (intercept) {
		size_t plen = strlen(path);
		char gulag[redir_len + plen];
		if (interrogate(path, plen, gulag)) {
			return (*fn)(ver,gulag,statbuf);
		}
	}
	return (*fn)(ver,path,statbuf);
}

#define def_stat_fn(nm,fn) int nm(STAT_PARAMS) { return nkvd_stat(libc_##fn,path,statbuf); }
#define def_xstat_fn(nm,fn) int nm(XSTAT_PARAMS) { return nkvd_xstat(libc_##fn,ver,path,statbuf); }

def_stat_fn(stat,stat);
def_stat_fn(stat64,stat64);
def_stat_fn(lstat,lstat);
def_stat_fn(lstat64,lstat64);

#ifndef _NO_GNU
	def_xstat_fn(__xstat,xstat);
	def_xstat_fn(__xstat64,xstat64);
	def_xstat_fn(__lxstat,lxstat);
	def_xstat_fn(__lxstat64,lxstat64);
#endif

int unlink(const char* volatile path) {
	if (intercept) {
		size_t plen = strlen(path);
		char gulag[redir_len + plen];
		if (interrogate(path, plen, gulag)) {
			return (*libc_unlink)(gulag);
		}
	}
	return (*libc_unlink)(path);
};

int open(const char* volatile path, int flags, ...) {
	/* fuck you for using varargs, asshole */
	va_list v; va_start(v,flags);
	mode_t m;
	if(flags & O_CREAT) m = va_arg(v, mode_t);
	va_end(v);

	if (intercept) {
		size_t plen = strlen(path);
		char gulag[redir_len + plen];
		if (interrogate(path, plen, gulag)) {
			return (*libc_open)(gulag, flags, m);
		}
	}
	return (*libc_open)(path, flags, m);
}

int nkvd_init(int argc, const char** argv) {
	wiretap_argc = argc;
	wiretap_argv = argv;

#	ifdef _NO_GNU
		/* RTLD_NEXT is unfortunately a glibc extension, so in
		* the _NO_GNU case we need to load open() via other
		* means. */
		void* SYMSRC = dlopen(_LIBC,RTLD_LAZY);
		if (SYMSRC == NULL)
			fail(-2,"compiled in non-glibc mode and cannot load "
				"libc from " bold(_LIBC) "; please recompile "
				" and specify your libc with -D_LIBC= on the"
				"compile command line.");
#	endif

#	define load_fn(fn,pfx) libc_##fn = dlsym(SYMSRC, #pfx #fn)
		load_fn(open,);      load_fn(unlink,);
		load_fn(stat,);      load_fn(lstat,);
		load_fn(stat64,);    load_fn(lstat64,);
		
#		ifndef _NO_GNU
			load_fn(xstat,__);   load_fn(lxstat,__);
			load_fn(xstat64,__); load_fn(lxstat64,__);
#		endif
#	undef load_fn
	
	/* have enough stat fns been found for us to proceed? */
	bool statfns = (  (libc_stat    && libc_lstat)
	               || (libc_stat64  && libc_lstat64)
#ifndef _NO_GNU
	               || (libc_xstat   && libc_lxstat)
	               || (libc_xstat64 && libc_lxstat64)
#endif
	               );

	if (! (libc_open && statfns && libc_unlink))
		fail(-3, "your libc is defective. bailing.");

	intercept = shitlist(argv[0]);
	if (intercept) {
		configdirs();
	}

	return 0; /* ?? why is this int, not void */
}

__attribute__((section(".init_array"))) static void* nkvd_constructor = &nkvd_init;

int main() { return 0; } /* stub for -pie */

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

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







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