initial commit

This commit is contained in:
kento2 2026-09-08 18:36:12 +02:00
commit 584aad56b8
19 changed files with 390 additions and 0 deletions

1
.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
example.input -text

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build/

24
README Normal file
View file

@ -0,0 +1,24 @@
nobotgate
=========
When you run a private site (that still uses a
super-serve), you can use nobotgate to require users to
provide an http bearer token. This way, bots won't flood
your site with spam.
usage
-----
use nobotgate(1) together with a super-server such as inetd(8),
tcpserver(1) or s6-tcpserver:
# tcpserver 0.0.0.0 80 nobotgate /etc/wwwtokens ./on-valid ./on-invalid
where /etc/wwwtokens contains:
and ./on-valid contains:
#!/bin/sh
exec busybox httpd -i -h /var/www
and ./on-invalid contains:
#!/bin/sh
exec nobotgate-access cat usage
and usage contains:
No bearer token specified!

39
check_token.c Normal file
View file

@ -0,0 +1,39 @@
#include <libowfat/stralloc.h>
#include <libowfat/errmsg.h>
#include <libowfat/open.h>
#include <libowfat/str.h>
#include <errno.h>
#include "nobotgate.h"
int check_token(char *tokenfile, stralloc *token)
{
int fd;
char buf[4096];
buffer b;
int r,found;
stralloc line;
fd = open_read(tokenfile);
if (fd < 0)
diesys(errno == ENOENT ? 100 : 111, "open_read ", tokenfile);
buffer_init_read(&b, fd, buf, sizeof(buf));
stralloc_init(&line);
found = 0;
for (;;) {
r = buffer_getnewline_sa(&b, &line);
if (r == 0) break;
if (r < 0) diesys(111, "failed reading tokenfile ", tokenfile);
stralloc_chomp(&line);
stralloc_0(&line);
line.s[str_chr(line.s, ' ')] = 0;
if (stralloc_equals(token, line.s)) {
found = 1;
break;
}
}
buffer_close(&b);
stralloc_free(&line);
return found;
}

2
example.input Normal file
View file

@ -0,0 +1,2 @@
Authorization: Bearer 1234

4
example.service/on-invalid Executable file
View file

@ -0,0 +1,4 @@
#!/bin/sh
exec \
nobotgate-access \
cat usage

3
example.service/on-valid Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
exec \
busybox httpd -i -h .

10
example.service/run Executable file
View file

@ -0,0 +1,10 @@
#!/bin/sh
# service that runs a web server protected with nobotgate.
# if the user provided a valid token, it will exec into
# ./on-valid, if not, into ./on-invalid.
exec \
envuidgid www \
tcpserver -U 127.0.0.1 80 \
../build/nobotgate ./tokens \
./on-valid \
./on-invalid

2
example.service/tokens Normal file
View file

@ -0,0 +1,2 @@
1234 a pretty insecure token
11aa this token is a lot more secure

91
example.service/trace Normal file
View file

@ -0,0 +1,91 @@
2783 execve("/usr/bin/envuidgid", ["envuidgid", "www", "tcpserver", "-U", "127.0.0.1", "80"], 0x7ffed7055678 /* 11 vars */) = 0
2783 brk(NULL) = 0x558000b69000
2783 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
2783 openat(AT_FDCWD</home/a/src/kento2/nobotgate/example.service>, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3</etc/ld.so.cache>
2783 fstat(3</etc/ld.so.cache>, {st_mode=S_IFREG|0644, st_size=129031, ...}) = 0
2783 mmap(NULL, 129031, PROT_READ, MAP_PRIVATE, 3</etc/ld.so.cache>, 0) = 0x7f13d3dfa000
2783 close(3</etc/ld.so.cache>) = 0
2783 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f13d3df8000
2783 openat(AT_FDCWD</home/a/src/kento2/nobotgate/example.service>, "/usr/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3</usr/lib/libc.so.6>
2783 read(3</usr/lib/libc.so.6>, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\255\2\0\0\0\0\0@\0\0\0\0\0\0\08\264\301\0\0\0\0\0\0\0\0\0@\08\0\16\0@\0G\0F\0\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0\20\3\0\0\0\0\0\0\20\3\0\0\0\0\0\0\10\0\0\0\0\0\0\0\3\0\0\0\4\0\0\0\240q\32\0\0\0\0\0\240q\32\0\0\0\0\0\240q\32\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\1\0\0\0\4\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\210t\2\0\0\0\0\0\210t\2\0\0\0\0\0\0\20\0\0\0\0\0\0\1\0\0\0\5\0\0\0\0\200\2\0\0\0\0\0\0\200\2\0\0\0\0\0"..., 832) = 832
2783 pread64(3</usr/lib/libc.so.6>, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0\20\3\0\0\0\0\0\0\20\3\0\0\0\0\0\0\10\0\0\0\0\0\0\0\3\0\0\0\4\0\0\0\240q\32\0\0\0\0\0\240q\32\0\0\0\0\0\240q\32\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\1\0\0\0\4\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\210t\2\0\0\0\0\0\210t\2\0\0\0\0\0\0\20\0\0\0\0\0\0\1\0\0\0\5\0\0\0\0\200\2\0\0\0\0\0\0\200\2\0\0\0\0\0\0\200\2\0\0\0\0\0\235\216\25\0\0\0\0\0\235\216\25\0\0\0\0\0\0\20\0\0\0\0\0\0\1\0\0\0\4\0\0\0\0\20\30\0\0\0\0\0\0\20\30\0\0\0\0\0\0\20\30\0\0\0\0\0"..., 784, 64) = 784
2783 fstat(3</usr/lib/libc.so.6>, {st_mode=S_IFREG|0755, st_size=12699128, ...}) = 0
2783 pread64(3</usr/lib/libc.so.6>, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0\20\3\0\0\0\0\0\0\20\3\0\0\0\0\0\0\10\0\0\0\0\0\0\0\3\0\0\0\4\0\0\0\240q\32\0\0\0\0\0\240q\32\0\0\0\0\0\240q\32\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\1\0\0\0\4\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\210t\2\0\0\0\0\0\210t\2\0\0\0\0\0\0\20\0\0\0\0\0\0\1\0\0\0\5\0\0\0\0\200\2\0\0\0\0\0\0\200\2\0\0\0\0\0\0\200\2\0\0\0\0\0\235\216\25\0\0\0\0\0\235\216\25\0\0\0\0\0\0\20\0\0\0\0\0\0\1\0\0\0\4\0\0\0\0\20\30\0\0\0\0\0\0\20\30\0\0\0\0\0\0\20\30\0\0\0\0\0"..., 784, 64) = 784
2783 mmap(NULL, 2006680, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3</usr/lib/libc.so.6>, 0) = 0x7f13d3c0e000
2783 mmap(0x7f13d3c36000, 1413120, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3</usr/lib/libc.so.6>, 0x28000) = 0x7f13d3c36000
2783 mmap(0x7f13d3d8f000, 352256, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3</usr/lib/libc.so.6>, 0x181000) = 0x7f13d3d8f000
2783 mmap(0x7f13d3de5000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3</usr/lib/libc.so.6>, 0x1d6000) = 0x7f13d3de5000
2783 mmap(0x7f13d3deb000, 52888, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f13d3deb000
2783 close(3</usr/lib/libc.so.6>) = 0
2783 mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f13d3c0b000
2783 arch_prctl(ARCH_SET_FS, 0x7f13d3c0b740) = 0
2783 set_tid_address(0x7f13d3c0ba10) = 2783
2783 set_robust_list(0x7f13d3c0ba20, 24) = 0
2783 rseq({cpu_id_start=0, cpu_id=RSEQ_CPU_ID_UNINITIALIZED, rseq_cs=NULL, flags=0, node_id=0, mm_cid=0, slice_ctrl={request=0, granted=0, __reserved=0}, __reserved=0}, 32, 0, 0x53053053) = 0
2783 mprotect(0x7f13d3de5000, 16384, PROT_READ) = 0
2783 mprotect(0x557fcb203000, 4096, PROT_READ) = 0
2783 mprotect(0x7f13d3e55000, 8192, PROT_READ) = 0
2783 prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
2783 munmap(0x7f13d3dfa000, 129031) = 0
2783 getrandom("\x50\xa4\x5b\x1a\x33\xa9\x65\xde", 8, GRND_NONBLOCK) = 8
2783 brk(NULL) = 0x558000b69000
2783 brk(0x558000b8a000) = 0x558000b8a000
2783 socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3<UNIX-STREAM:[57002340]>
2783 connect(3<UNIX-STREAM:[57002340]>, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
2783 close(3<UNIX-STREAM:[57002340]>) = 0
2783 socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3<UNIX-STREAM:[57002341]>
2783 connect(3<UNIX-STREAM:[57002341]>, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
2783 close(3<UNIX-STREAM:[57002341]>) = 0
2783 newfstatat(AT_FDCWD</home/a/src/kento2/nobotgate/example.service>, "/etc/nsswitch.conf", {st_mode=S_IFREG|0644, st_size=216, ...}, 0) = 0
2783 newfstatat(AT_FDCWD</home/a/src/kento2/nobotgate/example.service>, "/", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
2783 openat(AT_FDCWD</home/a/src/kento2/nobotgate/example.service>, "/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 3</etc/nsswitch.conf>
2783 fstat(3</etc/nsswitch.conf>, {st_mode=S_IFREG|0644, st_size=216, ...}) = 0
2783 read(3</etc/nsswitch.conf>, "# /etc/nsswitch.conf\n#\n# See nsswitch.conf(5) for information.\n#\npasswd: \tfiles\ngroup: \t\tfiles\nshadow: \tfiles\n\nhosts: \t\tfiles mdns dns\nnetworks: \tfiles\n\nprotocols: \tfiles\nservices: \tfiles\nethers: \tfiles\nrpc: \t\tfiles\n", 4096) = 216
2783 read(3</etc/nsswitch.conf>, "", 4096) = 0
2783 fstat(3</etc/nsswitch.conf>, {st_mode=S_IFREG|0644, st_size=216, ...}) = 0
2783 close(3</etc/nsswitch.conf>) = 0
2783 openat(AT_FDCWD</home/a/src/kento2/nobotgate/example.service>, "/etc/passwd", O_RDONLY|O_CLOEXEC) = 3</etc/passwd>
2783 fstat(3</etc/passwd>, {st_mode=S_IFREG|0644, st_size=5894, ...}) = 0
2783 lseek(3</etc/passwd>, 0, SEEK_SET) = 0
2783 read(3</etc/passwd>, "root:x:0:0:root:/root:/bedrock/cross/bin/sh\nnobody:x:99:99:Unprivileged User:/dev/null:/bedrock/cross/bin/false\nchrony:x:999:999:chrony unprivileged user:/var/lib/chrony:/bedrock/cross/bin/nologin\n_dhcpcd:x:998:998:_dhcpcd unprivileged user:/var/db/dhcpcd:"..., 4096) = 4096
2783 read(3</etc/passwd>, "6 unprivileged user:/var/empty:/bedrock/cross/bin/nologin\nnixbld7:x:979:952:nixbld7 unprivileged user:/var/empty:/bedrock/cross/bin/nologin\nnixbld8:x:978:952:nixbld8 unprivileged user:/var/empty:/bedrock/cross/bin/nologin\nnixbld9:x:977:952:nixbld9 unprivil"..., 4096) = 1798
2783 close(3</etc/passwd>) = 0
2783 execve("/home/a/.local/bin/tcpserver", ["tcpserver", "-U", "127.0.0.1", "80"], 0x557fcb2047f0 /* 13 vars */) = -1 ENOENT (No such file or directory)
2783 execve("/bedrock/cross/pin/bin/tcpserver", ["tcpserver", "-U", "127.0.0.1", "80"], 0x557fcb2047f0 /* 13 vars */) = -1 ENOENT (No such file or directory)
2783 execve("/bedrock/bin/tcpserver", ["tcpserver", "-U", "127.0.0.1", "80"], 0x557fcb2047f0 /* 13 vars */) = -1 ENOENT (No such file or directory)
2783 execve("/home/a/.nix-profile/bin/tcpserver", ["tcpserver", "-U", "127.0.0.1", "80"], 0x557fcb2047f0 /* 13 vars */) = -1 ENOENT (No such file or directory)
2783 execve("/nix/var/nix/profiles/default/bin/tcpserver", ["tcpserver", "-U", "127.0.0.1", "80"], 0x557fcb2047f0 /* 13 vars */) = -1 ENOENT (No such file or directory)
2783 execve("/usr/local/bin/tcpserver", ["tcpserver", "-U", "127.0.0.1", "80"], 0x557fcb2047f0 /* 13 vars */) = -1 ENOENT (No such file or directory)
2783 execve("/usr/local/sbin/tcpserver", ["tcpserver", "-U", "127.0.0.1", "80"], 0x557fcb2047f0 /* 13 vars */) = -1 ENOENT (No such file or directory)
2783 execve("/usr/bin/tcpserver", ["tcpserver", "-U", "127.0.0.1", "80"], 0x557fcb2047f0 /* 13 vars */) = 0
2783 brk(NULL) = 0x5605f057b000
2783 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
2783 openat(AT_FDCWD</home/a/src/kento2/nobotgate/example.service>, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3</etc/ld.so.cache>
2783 fstat(3</etc/ld.so.cache>, {st_mode=S_IFREG|0644, st_size=129031, ...}) = 0
2783 mmap(NULL, 129031, PROT_READ, MAP_PRIVATE, 3</etc/ld.so.cache>, 0) = 0x7f9752158000
2783 close(3</etc/ld.so.cache>) = 0
2783 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9752156000
2783 openat(AT_FDCWD</home/a/src/kento2/nobotgate/example.service>, "/usr/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3</usr/lib/libc.so.6>
2783 read(3</usr/lib/libc.so.6>, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\255\2\0\0\0\0\0@\0\0\0\0\0\0\08\264\301\0\0\0\0\0\0\0\0\0@\08\0\16\0@\0G\0F\0\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0\20\3\0\0\0\0\0\0\20\3\0\0\0\0\0\0\10\0\0\0\0\0\0\0\3\0\0\0\4\0\0\0\240q\32\0\0\0\0\0\240q\32\0\0\0\0\0\240q\32\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\1\0\0\0\4\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\210t\2\0\0\0\0\0\210t\2\0\0\0\0\0\0\20\0\0\0\0\0\0\1\0\0\0\5\0\0\0\0\200\2\0\0\0\0\0\0\200\2\0\0\0\0\0"..., 832) = 832
2783 pread64(3</usr/lib/libc.so.6>, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0\20\3\0\0\0\0\0\0\20\3\0\0\0\0\0\0\10\0\0\0\0\0\0\0\3\0\0\0\4\0\0\0\240q\32\0\0\0\0\0\240q\32\0\0\0\0\0\240q\32\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\1\0\0\0\4\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\210t\2\0\0\0\0\0\210t\2\0\0\0\0\0\0\20\0\0\0\0\0\0\1\0\0\0\5\0\0\0\0\200\2\0\0\0\0\0\0\200\2\0\0\0\0\0\0\200\2\0\0\0\0\0\235\216\25\0\0\0\0\0\235\216\25\0\0\0\0\0\0\20\0\0\0\0\0\0\1\0\0\0\4\0\0\0\0\20\30\0\0\0\0\0\0\20\30\0\0\0\0\0\0\20\30\0\0\0\0\0"..., 784, 64) = 784
2783 fstat(3</usr/lib/libc.so.6>, {st_mode=S_IFREG|0755, st_size=12699128, ...}) = 0
2783 pread64(3</usr/lib/libc.so.6>, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0\20\3\0\0\0\0\0\0\20\3\0\0\0\0\0\0\10\0\0\0\0\0\0\0\3\0\0\0\4\0\0\0\240q\32\0\0\0\0\0\240q\32\0\0\0\0\0\240q\32\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\1\0\0\0\4\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\210t\2\0\0\0\0\0\210t\2\0\0\0\0\0\0\20\0\0\0\0\0\0\1\0\0\0\5\0\0\0\0\200\2\0\0\0\0\0\0\200\2\0\0\0\0\0\0\200\2\0\0\0\0\0\235\216\25\0\0\0\0\0\235\216\25\0\0\0\0\0\0\20\0\0\0\0\0\0\1\0\0\0\4\0\0\0\0\20\30\0\0\0\0\0\0\20\30\0\0\0\0\0\0\20\30\0\0\0\0\0"..., 784, 64) = 784
2783 mmap(NULL, 2006680, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3</usr/lib/libc.so.6>, 0) = 0x7f9751f6c000
2783 mmap(0x7f9751f94000, 1413120, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3</usr/lib/libc.so.6>, 0x28000) = 0x7f9751f94000
2783 mmap(0x7f97520ed000, 352256, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3</usr/lib/libc.so.6>, 0x181000) = 0x7f97520ed000
2783 mmap(0x7f9752143000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3</usr/lib/libc.so.6>, 0x1d6000) = 0x7f9752143000
2783 mmap(0x7f9752149000, 52888, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f9752149000
2783 close(3</usr/lib/libc.so.6>) = 0
2783 mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9751f69000
2783 arch_prctl(ARCH_SET_FS, 0x7f9751f69740) = 0
2783 set_tid_address(0x7f9751f69a10) = 2783
2783 set_robust_list(0x7f9751f69a20, 24) = 0
2783 rseq({cpu_id_start=0, cpu_id=RSEQ_CPU_ID_UNINITIALIZED, rseq_cs=NULL, flags=0, node_id=0, mm_cid=0, slice_ctrl={request=0, granted=0, __reserved=0}, __reserved=0}, 32, 0, 0x53053053) = 0
2783 mprotect(0x7f9752143000, 16384, PROT_READ) = 0
2783 mprotect(0x5605de88f000, 4096, PROT_READ) = 0
2783 mprotect(0x7f97521b3000, 8192, PROT_READ) = 0
2783 prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
2783 munmap(0x7f9752158000, 129031) = 0
2783 write(2</dev/pts/1<char 136:1>>, "tcpserver: usage: tcpserver [ -461UXpPhHrRoOdDqQv ] [ -c limit ] [ -x rules.cdb ] [ -B banner ] [ -g gid ] [ -u uid ] [ -b backlog ] [ -l localname ] [ -t timeout ] [ -I interface ] host port program\n", 200) = 200
2783 exit_group(100) = ?
2783 +++ exited with 100 +++

1
example.service/usage Normal file
View file

@ -0,0 +1 @@
curl -H 'Authorization: Bearer <your token>' http://localhost:80

28
meson.build Normal file
View file

@ -0,0 +1,28 @@
project(
'nobotgate', 'c',
default_options: [
'c_std=c99',
'warning_level=2',
],
)
cc = meson.get_compiler('c')
deps = [
cc.find_library('libowfat'),
]
add_project_arguments(
'-DPATH_TMP="'+get_option('path_tmp')+'"',
language: 'c',
)
executable('nobotgate', files(
'./nobotgate.c',
'./parse_token.c',
'./check_token.c',
'./send_headers.c',
), dependencies:deps, install:true)
executable('nobotgate-access', files(
'./nobotgate-access.c',
), dependencies:deps, install:true)

6
meson.options Normal file
View file

@ -0,0 +1,6 @@
option(
'path_tmp',
type : 'string',
value : '/tmp',
description : 'path to tmpfs',
)

36
nobotgate-access.c Normal file
View file

@ -0,0 +1,36 @@
#include <libowfat/buffer.h>
#include <libowfat/errmsg.h>
#include <libowfat/str.h>
#include <unistd.h>
#define dieusage() die(100, argv0, " PROG ARGS");
static void consume_headers(void) {
char buf[2048];
int r;
for (;;) {
r=buffer_getline(buffer_0, buf, sizeof buf);
if (r==0) die(100,"EOF before header end");
if (buf[0]=='\r')
break;
}
}
int main(int argc, char **argv)
{
char *header;
(void)argc;
errmsg_iam(*argv);
if (argc<2) dieusage();
consume_headers();
header = "HTTP/1.0 200 OK\r\n"
"Connection: close\r\n"
"\r\n";
if (write(1, header, str_len(header)) < 0)
diesys(111, "write to stdout");
execvp(argv[1], argv+1);
diesys(111, "exec ", argv[1]);
}

57
nobotgate.c Normal file
View file

@ -0,0 +1,57 @@
#define _XOPEN_SOURCE 700
#include <libowfat/buffer.h>
#include <libowfat/errmsg.h>
#include <unistd.h>
#include <stdlib.h>
#include "nobotgate.h"
#define dieusage() die(100,"usage: ",argv0," tokenfile allowed-handler denied-handler")
static stralloc token;
static char *tokenfile, *cmd_valid, *cmd_invalid;
static void run(void) {
char tmp[] = PATH_TMP"/nobotgate.XXXXXX";
int tmpfd;
char buf[8192];
buffer b;
char *cmd;
tmpfd = mkstemp(tmp);
buffer_init_write(&b,tmpfd,buf,sizeof buf);
if (!send_headers(buffer_0, &b))
die(111,"copy stdin to temporary file");
lseek(tmpfd,0,SEEK_SET);
buffer_init_read(&b,tmpfd,buf,sizeof buf);
if (!parse_token(&b,&token)) {
carp("failed to parse token");
goto err;
}
cmd=check_token(tokenfile,&token)
? cmd_valid
: cmd_invalid;
lseek(tmpfd,0,SEEK_SET);
dup2(tmpfd,0);
unlink(tmp);
buffer_close(&b);
stralloc_free(&token);
carp("execing ",cmd);
execlp(cmd,cmd,NULL);
err:
unlink(tmp);
exit(111);
}
int main(int argc,char **argv)
{
errmsg_iam(*argv);
argv++; argc--;
if (argc != 3) dieusage();
tokenfile=argv[0];
cmd_valid=argv[1];
cmd_invalid=argv[2];
run();
}

19
nobotgate.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef NOBOTGATE
#define NOBOTGATE
#include <libowfat/buffer.h>
#include <libowfat/stralloc.h>
/* buffer shall stream an http request.
* it stores the found bearer token to result or
* returns 0 if none is found.
* always succeeds. */
int parse_token(buffer *b, stralloc *result);
/* return: 1 if the token is valid(i.e. in tokenfile),
* 0 else */
int check_token(char *tokenfile, stralloc *token);
int send_headers(buffer *from, buffer *to);
#endif

34
parse_token.c Normal file
View file

@ -0,0 +1,34 @@
#include <libowfat/stralloc.h>
#include <libowfat/buffer.h>
#include <libowfat/errmsg.h>
#include <libowfat/str.h>
#define AUTH_HEADER "Authorization: Bearer "
#define AUTH_HEADER_LEN str_len(AUTH_HEADER)
// TODO make case insensitive according to https://www.rfc-editor.org/info/rfc7230/#section-3.2
int parse_token(buffer *b, stralloc *result)
{
stralloc line;
int r;
stralloc_init(result);
for (;;) {
r=buffer_getnewline_sa(b,&line);
if (r==0) break;
if (r<0) { carpsys("read line"); goto err; }
if (stralloc_starts(&line,AUTH_HEADER)) {
stralloc_chomp(&line);
if (!stralloc_copyb(result, line.s+AUTH_HEADER_LEN,
line.len-AUTH_HEADER_LEN)
) { carpsys("out of memory"); goto err; }
break;
}
}
stralloc_free(&line);
return 1;
err:
stralloc_free(&line);
return 0;
}

27
send_headers.c Normal file
View file

@ -0,0 +1,27 @@
#include <unistd.h>
#include <libowfat/errmsg.h>
#include "nobotgate.h"
int send_headers(buffer *from, buffer *to)
{
char buf[8192];
int nread;
for (;;) {
nread=buffer_getline(from,buf,sizeof buf);
if (nread==0) { carp("EOF before header end"); return 0; }
if (nread<0) { carpsys("failed reading"); return 0; }
/* include \n */
nread++;
if (buffer_put(to,buf,nread)<0) return 0;
if (buf[0]=='\r')
break;
}
if (buffer_flush(to)<0) {
carpsys("buffer flush");
return 0;
}
return 1;
}

5
tool/debug Executable file
View file

@ -0,0 +1,5 @@
#!/bin/sh
set -eux
gdb \
-ex 'set args ./example.service/tokens ./example.service/on-valid ./example.service/on-invalid < ./example.input' \
./build/nobotgate