miniroon

Simplistic macaroon-based authorization for Unix systems
git clone https://ccx.te2000.cz/git/miniroon
Log | Files | Refs

commit c9239415b7aa969f1ac5bcd8e46636718d997118
parent d24673d64ec2257dc8362ab75ccd9b0c9f0f6353
Author: Jan Pobrislo <ccx@te2000.cz>
Date:   Fri,  6 Dec 2024 00:36:04 +0000

Make source filenames more consistent

Diffstat:
Msrc/Makefile | 4++--
Asrc/cmd_read.c | 201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/cmd_verify.c | 229+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/header.c | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/miniroon-header.h -> src/header.h | 0
Dsrc/miniroon-header.c | 56--------------------------------------------------------
Dsrc/miniroon-read.c | 201-------------------------------------------------------------------------------
Dsrc/miniroon-verify.c | 229-------------------------------------------------------------------------------
8 files changed, 488 insertions(+), 488 deletions(-)

diff --git a/src/Makefile b/src/Makefile @@ -7,10 +7,10 @@ build/miniroon-$(1): $$(patsubst %,build/%,$$(obj_$(1))) ../link ../link -o '$$@' $$(patsubst %,build/%,$$(obj_$(1))) endef -obj_read:=miniroon-read.o bytebuffer.o netstring.o miniroon-header.o +obj_read:=cmd_read.o bytebuffer.o netstring.o header.o $(eval $(call miniroon_link,read)) -obj_verify:=miniroon-verify.o bytebuffer.o netstring.o hmac_sha2_256.o miniroon-header.o envmap.o caveats.o miniroon_caveat_name_perfhash.o caveat_env_is.o caveat_env_absent.o caveat_env_fnmatch.o +obj_verify:=cmd_verify.o bytebuffer.o netstring.o hmac_sha2_256.o header.o envmap.o caveats.o miniroon_caveat_name_perfhash.o caveat_env_is.o caveat_env_absent.o caveat_env_fnmatch.o $(eval $(call miniroon_link,verify)) tools:=$(patsubst %,build/miniroon-%,$(miniroon_tool_names)) diff --git a/src/cmd_read.c b/src/cmd_read.c @@ -0,0 +1,201 @@ +#include <errno.h> +#include <unistd.h> +#include <sys/select.h> +#include <stdlib.h> +#include <assert.h> + +#include <skalibs/types.h> +#include <skalibs/strerr.h> +#include <skalibs/djbunix.h> +#include <skalibs/exec.h> +#include <skalibs/netstring.h> +#include <skalibs/stralloc.h> +#include <skalibs/env.h> + +#include "netstring.h" +#include "header.h" + +#define USAGE "miniroon-read directory" +#define PROG "miniroon-read" + +#define input_fd 0 +#define payload_size_max 1024*1024 +#define MAX_CAVEATS 256 +#define MAX_ENV_ALLOW 256 + + +void fd_block(int fd) { + int flags = fcntl(fd, F_GETFL); + if(flags == -1) { + strerr_dief1sys(111, "fcntl() getfd"); + } + if(fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) { + strerr_dief1sys(111, "fcntl() setfd"); + } +} + +size_t read_payload_size(int fd) { + char read_char; + size_t payload_size = 0; + + fd_block(fd); + + while(payload_size < payload_size_max) { + switch(read(fd, &read_char, 1)) { + case 0: + strerr_dief1x(111, "EOF before netstring size was read"); + break; + case 1: + if(read_char == ':') { + return payload_size; + } else if(read_char >= '0' && read_char <= '9') { + payload_size *= 10; + payload_size += read_char - '0'; + } else { + strerr_dief1x(111, "Malformed netstring on input"); + } + break; + case -1: + if(errno != EINTR) { + strerr_dief1sys(111, "read() length"); + } + break; + default: + strerr_dief1x(110, "Unexpected return value from read()"); + break; + } + } + + strerr_dief1x(111, "Input netstring too big"); +} + +void read_payload(const bytebuffer bb) { + char *read_next = bb.data; + ssize_t read_size; + while(read_next - bb.data < bb.len) { + read_size = read(input_fd, read_next, bb.len - (read_next - bb.data)); + if(read_size == 0) { + strerr_dief1x(111, "EOF before full netstring payload was read"); + } + if(read_size == -1) { + if(errno != EINTR) { + strerr_dief1sys(111, "read() payload"); + } + continue; + } + read_next += read_size; + } + if(bb.data[bb.len - 1] != ',') { + strerr_dief1x(111, "Invalid netstring terminator"); + } +} + +void process_header(miniroon_header *header, const bytebuffer source) { + parse_header(header, source); + + char id[header->id.len + 1]; + for(size_t i=0; i<header->id.len; i++) { + id[i] = header->id.data[i]; + if(id[i] == '-') { continue; } + if(id[i] >= '0' && id[i] <= '9') { continue; } + if(id[i] >= 'a' && id[i] <= 'z') { continue; } + strerr_dief1x(111, "Invalid character in miniroon ID"); + } + id[header->id.len] = 0; + + if (chdir(id) != 0) { + strerr_dief1sys(111, "chdir(id)"); + } +} + +void exec_verify_and_write_payload (const bytebuffer payload); +void process_payload(const bytebuffer payload) { + miniroon_header header; + netstring_chunk c; + netstring_chunk_init(&c, payload); + + if(!netstring_chunk_next(&c)) { + strerr_dief1x(111, "Mising miniroon header"); + } + process_header(&header, c.inner); + exec_verify_and_write_payload(payload); +} + +void exec_verify_and_write_payload (const bytebuffer payload) { + pid_t pid1=0, pid2=0; + int wstat ; + int p[2] ; + + if(pipe(p) == -1) { + strerr_diefu1sys(111, "create pipe"); + } + + pid1 = fork(); + switch(pid1) { + case -1: + strerr_diefu1sys(111, "first fork()"); + break; + case 0: /* child */ + fd_close(p[0]); + pid2 = fork(); + switch(pid2) { + case -1: + strerr_diefu1sys(111, "second fork()"); + break; + case 0: /* child */ + ssize_t payload_write = fd_write(p[1], payload.data, payload.len); + if(payload_write < 0 ) { + strerr_dief1sys(111, "write(payload)"); + } + if(payload_write != payload.len) { + strerr_dief1x(111, "could not write whole payload"); + } + exit(0); + break; + default: /* parent */ + exit(0); + break; + } + break; + default: /* parent */ + waitpid_nointr(pid1, &wstat, 0); + if(!WIFEXITED(wstat) || WEXITSTATUS(wstat) != 0) { + strerr_dief1x(111, "child terminated abnormally"); + } + + char cmd[] = "./verify"; + char fmt_fd[UINT64_FMT], fmt_len[UINT64_FMT]; + fmt_fd[uint64_fmt(fmt_fd, (uint64_t)p[0])] = 0; + fmt_len[uint64_fmt(fmt_len, (uint64_t)payload.len)] = 0; + const char *cmd_argv[4] = {cmd, fmt_fd, fmt_len, 0}; + + fd_close(p[1]); + xexec(cmd_argv); + break; + } +} + +int main (int argc, char const *const *argv) +{ + + if (argc != 2) { + strerr_dieusage(100, USAGE); + } + + if (chdir(argv[1]) != 0) { + strerr_dief1sys(111, "chdir()"); + } + size_t payload_size = read_payload_size(input_fd); + char payload_data[payload_size + 1]; + bytebuffer payload = {payload_data, payload_size + 1}; + read_payload(payload); + payload.len -= 1; /* strip final netstring terminator */ + dbg_print_bb1("Got payload", payload); + process_payload(payload); + assert(0); + strerr_dief1x(110, "Internal logic error, should not get here"); + return 110; +} + +/* vim: sts=2 sw=2 et +*/ diff --git a/src/cmd_verify.c b/src/cmd_verify.c @@ -0,0 +1,229 @@ +#include <errno.h> +#include <assert.h> +#include <stdbool.h> +#include <unistd.h> +#include <sys/select.h> + +// for debug prints +#include <stdio.h> + +#include <skalibs/types.h> +#include <skalibs/strerr.h> +#include <skalibs/djbunix.h> +#include <skalibs/exec.h> +#include <skalibs/netstring.h> +#include <skalibs/uint64.h> +#include <skalibs/stralloc.h> +#include <skalibs/env.h> + +#include "verify_common.h" +#include "bytebuffer.h" +#include "netstring.h" +#include "hmac_sha2_256.h" +#include "header.h" +#include "caveats.h" + +typedef struct miniroon_data_s { + miniroon_header hdr; + bytebuffer caveats[MAX_CAVEATS]; + size_t caveat_count; +} miniroon_data; + +/* declarations */ +void miniroon_data_init(miniroon_data *data); +void process_payload(const bytebuffer payload); +void validate_and_exec(miniroon_data *data); +void read_secret(const bytebuffer secret); + +#define MINIROON_HMAC_SIZE 32 +//#define MINIROON_HMAC_FUNC(key, msg, out) hmac_b2s_256(key, msg, out) +#define MINIROON_HMAC_FUNC(key, msg, out) hmac_sha2_256(key, msg, out) + +/* definitions */ + + +void miniroon_data_init(miniroon_data *data) { + memset(data, 0, sizeof(miniroon_data)); + // data->env_modif = STRALLOC_ZERO ; +} + +void read_secret(const bytebuffer secret){ + assert(secret.len == MINIROON_HMAC_SIZE); + size_t bytes_read = 0; + int secret_fd = openc_readb("secret"); + if (secret_fd < 0) { + strerr_dief1sys(111, "open(secret)"); + } + while(bytes_read < secret.len) { + ssize_t r = read(secret_fd, &secret.data[bytes_read], secret.len - bytes_read); + switch(r) { + case 0: + strerr_dief1x(111, "EOF before full secret was read"); + break; + case -1: + if(errno != EINTR) { + strerr_dief1sys(111, "read() length"); + } + break; + } + bytes_read += r; + } + if(close(secret_fd) != 0) { + strerr_dief1sys(111, "close(secret_fd)"); + } +} + + +void validate_and_exec(miniroon_data *md) { + miniroon_caveats_state state; + miniroon_caveats_state_init(&state); + // stralloc env_modif; + + for(size_t i=0; i < md->caveat_count; i++) { + dbg_print_bb1("Validate caveat", md->caveats[i]); + miniroon_caveat_prepare(md->caveats[i], &state); + } + for(size_t i=0; i < md->caveat_count; i++) { + dbg_print_bb1("Validate caveat", md->caveats[i]); + miniroon_caveat_validate(md->caveats[i], &state); + } + + /* iff everything validated correctly */ + // TODO: pass unused argv from main() ? + char cmd[] = "./run"; + const char *cmd_argv[2] = {cmd, 0}; + miniroon_caveats_state_exec(&state, cmd_argv); +} + +void process_payload(const bytebuffer payload) { + miniroon_data md; + miniroon_data_init(&md); + netstring_chunk c; + netstring_chunk_init(&c, payload); + + if(!netstring_chunk_next(&c)) { + strerr_dief1x(111, "Mising miniroon header"); + } + parse_header(&md.hdr, c.inner); + // header should be verified by now, we can start hashing + uint8_t hmac_data[MINIROON_HMAC_SIZE]; + bytebuffer hmac_bb = {hmac_data, MINIROON_HMAC_SIZE}; + read_secret(hmac_bb); + // dbg_print_bb1("Secret", hmac_bb); + MINIROON_HMAC_FUNC(hmac_bb, c.inner, hmac_bb); + // dbg_print_bb1("Signature update", hmac_bb); + + if(!netstring_chunk_next(&c)) { + strerr_dief1x(111, "Mising miniroon body"); + } + netstring_chunk body; + netstring_chunk_init(&body, c.inner); + + while(netstring_chunk_next(&body)) { + dbg_print_bb1("Got caveat", body.inner); + if(md.caveat_count >= MAX_CAVEATS) { + strerr_dief1x(111, "Too many caveats"); + } + md.caveats[md.caveat_count++] = body.inner; + MINIROON_HMAC_FUNC(hmac_bb, body.inner, hmac_bb); + // dbg_print_bb1("Signature update", hmac_bb); + } + + if(!netstring_chunk_next(&c)) { + strerr_dief1x(111, "Mising miniroon signature"); + } + dbg_print_bb1("Got signature", c.inner); + if(c.inner.len != MINIROON_HMAC_SIZE) { + strerr_dief1x(111, "Invalid miniroon signature length"); + } + /* constant time hash compare */ + uint8_t bitdiff = 0; + for(size_t i=0; i<MINIROON_HMAC_SIZE; i++) { + bitdiff |= hmac_data[i] ^ c.inner.data[i]; + } + if(netstring_chunk_next(&c)) { + strerr_dief1x(111, "Extraneous data in miniroon"); + } + if(bitdiff) { + strerr_dief1x(111, "Invalid miniroon signature"); + } + + validate_and_exec(&md); + strerr_dief1x(110, "Internal logic error, should not get here"); +} + +void read_payload(int payload_fd, const bytebuffer bb) { + int flags = fcntl(payload_fd, F_GETFL); + if(flags == -1) { + strerr_dief1sys(111, "fcntl(payload_fd) getfd"); + } + if(fcntl(payload_fd, F_SETFL, flags & ~O_NONBLOCK) < 0) { + strerr_dief1sys(111, "fcntl(payload_fd) setfd"); + } + + ssize_t payload_read = fd_read(payload_fd, bb.data, bb.len); + if(payload_read < 0 ) { + strerr_dief1sys(111, "read(payload)"); + } + if(payload_read != bb.len) { + strerr_dief1x(111, "could not read whole payload"); + } +} + +bool parse_fd(char const *arg, int *fd) { + size_t arg_size = strlen(arg); + return int_scan(arg, fd) == arg_size; +} + +bool parse_size(char const *arg, size_t *size) { + size_t arg_size = strlen(arg); + return size_scan(arg, size) == arg_size; +} + +int main (int argc, char const *const *argv) +{ + if (argc != 3) { + strerr_dieusage(100, USAGE); + } + + int payload_fd; + size_t payload_size; + + if(!parse_fd(argv[1], &payload_fd)) { + strerr_dief1x(100, "could not parse payload fd"); + } + + if(!parse_size(argv[2], &payload_size)) { + strerr_dief1x(100, "could not parse payload length"); + } + + char payload_data[payload_size]; + bytebuffer payload = {payload_data, payload_size}; + read_payload(payload_fd, payload); + fd_close(payload_fd); + + dbg_print_bb1("Got payload", payload); + process_payload(payload); + strerr_dief1x(110, "Internal logic error, should not get here"); + return 110; +} +/* +capability ``` +container/bzr.ccx/123456 +login/tty1/7890 +``` +- secret +- execline command +- env allowlist (re?) +- max execution count/id (uuidv7?) + +``` +caphdr = [capv0;name;invoke-once] +c1 = [caphdr;;h1=hmac(secret, caphdr)] +c2 = [caphdr;[att1];h2=hmac(h1, att1)] +c3 = [caphdr;[att1;att2];h3=hmac(h2, att2)] +``` +*/ + +/* vim: sts=2 sw=2 et +*/ diff --git a/src/header.c b/src/header.c @@ -0,0 +1,56 @@ +#include <skalibs/strerr.h> + +#include "netstring.h" +#include "header.h" + +void set_header_version(miniroon_header *header, const bytebuffer source) { + if(strbbcmp(source, "capv0") == 0) { + header->version = V0; + } else { + strerr_dief1x(111, "Unhandled miniroon version"); + } +} + +void set_header_action(miniroon_header *header, const bytebuffer source) { + if(strbbcmp(source, "revoke") == 0) { + header->action = REVOKE; + } else if(strbbcmp(source, "invoke") == 0) { + header->action = INVOKE; + } else if(strbbcmp(source, "invoke-once") == 0) { + header->action = INVOKE_ONCE; + } else { + strerr_dief1x(111, "Unhandled miniroon action"); + } +} + +void parse_header(miniroon_header *header, const bytebuffer source) { + dbg_print_bb1("Got header", source); + netstring_chunk c; + netstring_chunk_init(&c, source); + + if(!netstring_chunk_next(&c)) { + strerr_dief1x(111, "Mising version in miniroon header"); + } + dbg_print_bb1("Header > Version", c.inner); + set_header_version(header, c.inner); + + if(!netstring_chunk_next(&c)) { + strerr_dief1x(111, "Mising ID in miniroon header"); + } + dbg_print_bb1("Header > ID", c.inner); + header->id = c.inner; + + if(!netstring_chunk_next(&c)) { + strerr_dief1x(111, "Mising action in miniroon header"); + } + dbg_print_bb1("Header > Action", c.inner); + set_header_action(header, c.inner); + + if(netstring_chunk_next(&c)) { + strerr_dief1x(111, "Extraneous data in miniroon header"); + } + +} + +/* vim: sts=2 sw=2 et +*/ diff --git a/src/miniroon-header.h b/src/header.h diff --git a/src/miniroon-header.c b/src/miniroon-header.c @@ -1,56 +0,0 @@ -#include <skalibs/strerr.h> - -#include "netstring.h" -#include "miniroon-header.h" - -void set_header_version(miniroon_header *header, const bytebuffer source) { - if(strbbcmp(source, "capv0") == 0) { - header->version = V0; - } else { - strerr_dief1x(111, "Unhandled miniroon version"); - } -} - -void set_header_action(miniroon_header *header, const bytebuffer source) { - if(strbbcmp(source, "revoke") == 0) { - header->action = REVOKE; - } else if(strbbcmp(source, "invoke") == 0) { - header->action = INVOKE; - } else if(strbbcmp(source, "invoke-once") == 0) { - header->action = INVOKE_ONCE; - } else { - strerr_dief1x(111, "Unhandled miniroon action"); - } -} - -void parse_header(miniroon_header *header, const bytebuffer source) { - dbg_print_bb1("Got header", source); - netstring_chunk c; - netstring_chunk_init(&c, source); - - if(!netstring_chunk_next(&c)) { - strerr_dief1x(111, "Mising version in miniroon header"); - } - dbg_print_bb1("Header > Version", c.inner); - set_header_version(header, c.inner); - - if(!netstring_chunk_next(&c)) { - strerr_dief1x(111, "Mising ID in miniroon header"); - } - dbg_print_bb1("Header > ID", c.inner); - header->id = c.inner; - - if(!netstring_chunk_next(&c)) { - strerr_dief1x(111, "Mising action in miniroon header"); - } - dbg_print_bb1("Header > Action", c.inner); - set_header_action(header, c.inner); - - if(netstring_chunk_next(&c)) { - strerr_dief1x(111, "Extraneous data in miniroon header"); - } - -} - -/* vim: sts=2 sw=2 et -*/ diff --git a/src/miniroon-read.c b/src/miniroon-read.c @@ -1,201 +0,0 @@ -#include <errno.h> -#include <unistd.h> -#include <sys/select.h> -#include <stdlib.h> -#include <assert.h> - -#include <skalibs/types.h> -#include <skalibs/strerr.h> -#include <skalibs/djbunix.h> -#include <skalibs/exec.h> -#include <skalibs/netstring.h> -#include <skalibs/stralloc.h> -#include <skalibs/env.h> - -#include "netstring.h" -#include "miniroon-header.h" - -#define USAGE "miniroon-read directory" -#define PROG "miniroon-read" - -#define input_fd 0 -#define payload_size_max 1024*1024 -#define MAX_CAVEATS 256 -#define MAX_ENV_ALLOW 256 - - -void fd_block(int fd) { - int flags = fcntl(fd, F_GETFL); - if(flags == -1) { - strerr_dief1sys(111, "fcntl() getfd"); - } - if(fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) { - strerr_dief1sys(111, "fcntl() setfd"); - } -} - -size_t read_payload_size(int fd) { - char read_char; - size_t payload_size = 0; - - fd_block(fd); - - while(payload_size < payload_size_max) { - switch(read(fd, &read_char, 1)) { - case 0: - strerr_dief1x(111, "EOF before netstring size was read"); - break; - case 1: - if(read_char == ':') { - return payload_size; - } else if(read_char >= '0' && read_char <= '9') { - payload_size *= 10; - payload_size += read_char - '0'; - } else { - strerr_dief1x(111, "Malformed netstring on input"); - } - break; - case -1: - if(errno != EINTR) { - strerr_dief1sys(111, "read() length"); - } - break; - default: - strerr_dief1x(110, "Unexpected return value from read()"); - break; - } - } - - strerr_dief1x(111, "Input netstring too big"); -} - -void read_payload(const bytebuffer bb) { - char *read_next = bb.data; - ssize_t read_size; - while(read_next - bb.data < bb.len) { - read_size = read(input_fd, read_next, bb.len - (read_next - bb.data)); - if(read_size == 0) { - strerr_dief1x(111, "EOF before full netstring payload was read"); - } - if(read_size == -1) { - if(errno != EINTR) { - strerr_dief1sys(111, "read() payload"); - } - continue; - } - read_next += read_size; - } - if(bb.data[bb.len - 1] != ',') { - strerr_dief1x(111, "Invalid netstring terminator"); - } -} - -void process_header(miniroon_header *header, const bytebuffer source) { - parse_header(header, source); - - char id[header->id.len + 1]; - for(size_t i=0; i<header->id.len; i++) { - id[i] = header->id.data[i]; - if(id[i] == '-') { continue; } - if(id[i] >= '0' && id[i] <= '9') { continue; } - if(id[i] >= 'a' && id[i] <= 'z') { continue; } - strerr_dief1x(111, "Invalid character in miniroon ID"); - } - id[header->id.len] = 0; - - if (chdir(id) != 0) { - strerr_dief1sys(111, "chdir(id)"); - } -} - -void exec_verify_and_write_payload (const bytebuffer payload); -void process_payload(const bytebuffer payload) { - miniroon_header header; - netstring_chunk c; - netstring_chunk_init(&c, payload); - - if(!netstring_chunk_next(&c)) { - strerr_dief1x(111, "Mising miniroon header"); - } - process_header(&header, c.inner); - exec_verify_and_write_payload(payload); -} - -void exec_verify_and_write_payload (const bytebuffer payload) { - pid_t pid1=0, pid2=0; - int wstat ; - int p[2] ; - - if(pipe(p) == -1) { - strerr_diefu1sys(111, "create pipe"); - } - - pid1 = fork(); - switch(pid1) { - case -1: - strerr_diefu1sys(111, "first fork()"); - break; - case 0: /* child */ - fd_close(p[0]); - pid2 = fork(); - switch(pid2) { - case -1: - strerr_diefu1sys(111, "second fork()"); - break; - case 0: /* child */ - ssize_t payload_write = fd_write(p[1], payload.data, payload.len); - if(payload_write < 0 ) { - strerr_dief1sys(111, "write(payload)"); - } - if(payload_write != payload.len) { - strerr_dief1x(111, "could not write whole payload"); - } - exit(0); - break; - default: /* parent */ - exit(0); - break; - } - break; - default: /* parent */ - waitpid_nointr(pid1, &wstat, 0); - if(!WIFEXITED(wstat) || WEXITSTATUS(wstat) != 0) { - strerr_dief1x(111, "child terminated abnormally"); - } - - char cmd[] = "./verify"; - char fmt_fd[UINT64_FMT], fmt_len[UINT64_FMT]; - fmt_fd[uint64_fmt(fmt_fd, (uint64_t)p[0])] = 0; - fmt_len[uint64_fmt(fmt_len, (uint64_t)payload.len)] = 0; - const char *cmd_argv[4] = {cmd, fmt_fd, fmt_len, 0}; - - fd_close(p[1]); - xexec(cmd_argv); - break; - } -} - -int main (int argc, char const *const *argv) -{ - - if (argc != 2) { - strerr_dieusage(100, USAGE); - } - - if (chdir(argv[1]) != 0) { - strerr_dief1sys(111, "chdir()"); - } - size_t payload_size = read_payload_size(input_fd); - char payload_data[payload_size + 1]; - bytebuffer payload = {payload_data, payload_size + 1}; - read_payload(payload); - payload.len -= 1; /* strip final netstring terminator */ - dbg_print_bb1("Got payload", payload); - process_payload(payload); - assert(0); - strerr_dief1x(110, "Internal logic error, should not get here"); - return 110; -} - -/* vim: sts=2 sw=2 et -*/ diff --git a/src/miniroon-verify.c b/src/miniroon-verify.c @@ -1,229 +0,0 @@ -#include <errno.h> -#include <assert.h> -#include <stdbool.h> -#include <unistd.h> -#include <sys/select.h> - -// for debug prints -#include <stdio.h> - -#include <skalibs/types.h> -#include <skalibs/strerr.h> -#include <skalibs/djbunix.h> -#include <skalibs/exec.h> -#include <skalibs/netstring.h> -#include <skalibs/uint64.h> -#include <skalibs/stralloc.h> -#include <skalibs/env.h> - -#include "verify_common.h" -#include "bytebuffer.h" -#include "netstring.h" -#include "hmac_sha2_256.h" -#include "miniroon-header.h" -#include "caveats.h" - -typedef struct miniroon_data_s { - miniroon_header hdr; - bytebuffer caveats[MAX_CAVEATS]; - size_t caveat_count; -} miniroon_data; - -/* declarations */ -void miniroon_data_init(miniroon_data *data); -void process_payload(const bytebuffer payload); -void validate_and_exec(miniroon_data *data); -void read_secret(const bytebuffer secret); - -#define MINIROON_HMAC_SIZE 32 -//#define MINIROON_HMAC_FUNC(key, msg, out) hmac_b2s_256(key, msg, out) -#define MINIROON_HMAC_FUNC(key, msg, out) hmac_sha2_256(key, msg, out) - -/* definitions */ - - -void miniroon_data_init(miniroon_data *data) { - memset(data, 0, sizeof(miniroon_data)); - // data->env_modif = STRALLOC_ZERO ; -} - -void read_secret(const bytebuffer secret){ - assert(secret.len == MINIROON_HMAC_SIZE); - size_t bytes_read = 0; - int secret_fd = openc_readb("secret"); - if (secret_fd < 0) { - strerr_dief1sys(111, "open(secret)"); - } - while(bytes_read < secret.len) { - ssize_t r = read(secret_fd, &secret.data[bytes_read], secret.len - bytes_read); - switch(r) { - case 0: - strerr_dief1x(111, "EOF before full secret was read"); - break; - case -1: - if(errno != EINTR) { - strerr_dief1sys(111, "read() length"); - } - break; - } - bytes_read += r; - } - if(close(secret_fd) != 0) { - strerr_dief1sys(111, "close(secret_fd)"); - } -} - - -void validate_and_exec(miniroon_data *md) { - miniroon_caveats_state state; - miniroon_caveats_state_init(&state); - // stralloc env_modif; - - for(size_t i=0; i < md->caveat_count; i++) { - dbg_print_bb1("Validate caveat", md->caveats[i]); - miniroon_caveat_prepare(md->caveats[i], &state); - } - for(size_t i=0; i < md->caveat_count; i++) { - dbg_print_bb1("Validate caveat", md->caveats[i]); - miniroon_caveat_validate(md->caveats[i], &state); - } - - /* iff everything validated correctly */ - // TODO: pass unused argv from main() ? - char cmd[] = "./run"; - const char *cmd_argv[2] = {cmd, 0}; - miniroon_caveats_state_exec(&state, cmd_argv); -} - -void process_payload(const bytebuffer payload) { - miniroon_data md; - miniroon_data_init(&md); - netstring_chunk c; - netstring_chunk_init(&c, payload); - - if(!netstring_chunk_next(&c)) { - strerr_dief1x(111, "Mising miniroon header"); - } - parse_header(&md.hdr, c.inner); - // header should be verified by now, we can start hashing - uint8_t hmac_data[MINIROON_HMAC_SIZE]; - bytebuffer hmac_bb = {hmac_data, MINIROON_HMAC_SIZE}; - read_secret(hmac_bb); - // dbg_print_bb1("Secret", hmac_bb); - MINIROON_HMAC_FUNC(hmac_bb, c.inner, hmac_bb); - // dbg_print_bb1("Signature update", hmac_bb); - - if(!netstring_chunk_next(&c)) { - strerr_dief1x(111, "Mising miniroon body"); - } - netstring_chunk body; - netstring_chunk_init(&body, c.inner); - - while(netstring_chunk_next(&body)) { - dbg_print_bb1("Got caveat", body.inner); - if(md.caveat_count >= MAX_CAVEATS) { - strerr_dief1x(111, "Too many caveats"); - } - md.caveats[md.caveat_count++] = body.inner; - MINIROON_HMAC_FUNC(hmac_bb, body.inner, hmac_bb); - // dbg_print_bb1("Signature update", hmac_bb); - } - - if(!netstring_chunk_next(&c)) { - strerr_dief1x(111, "Mising miniroon signature"); - } - dbg_print_bb1("Got signature", c.inner); - if(c.inner.len != MINIROON_HMAC_SIZE) { - strerr_dief1x(111, "Invalid miniroon signature length"); - } - /* constant time hash compare */ - uint8_t bitdiff = 0; - for(size_t i=0; i<MINIROON_HMAC_SIZE; i++) { - bitdiff |= hmac_data[i] ^ c.inner.data[i]; - } - if(netstring_chunk_next(&c)) { - strerr_dief1x(111, "Extraneous data in miniroon"); - } - if(bitdiff) { - strerr_dief1x(111, "Invalid miniroon signature"); - } - - validate_and_exec(&md); - strerr_dief1x(110, "Internal logic error, should not get here"); -} - -void read_payload(int payload_fd, const bytebuffer bb) { - int flags = fcntl(payload_fd, F_GETFL); - if(flags == -1) { - strerr_dief1sys(111, "fcntl(payload_fd) getfd"); - } - if(fcntl(payload_fd, F_SETFL, flags & ~O_NONBLOCK) < 0) { - strerr_dief1sys(111, "fcntl(payload_fd) setfd"); - } - - ssize_t payload_read = fd_read(payload_fd, bb.data, bb.len); - if(payload_read < 0 ) { - strerr_dief1sys(111, "read(payload)"); - } - if(payload_read != bb.len) { - strerr_dief1x(111, "could not read whole payload"); - } -} - -bool parse_fd(char const *arg, int *fd) { - size_t arg_size = strlen(arg); - return int_scan(arg, fd) == arg_size; -} - -bool parse_size(char const *arg, size_t *size) { - size_t arg_size = strlen(arg); - return size_scan(arg, size) == arg_size; -} - -int main (int argc, char const *const *argv) -{ - if (argc != 3) { - strerr_dieusage(100, USAGE); - } - - int payload_fd; - size_t payload_size; - - if(!parse_fd(argv[1], &payload_fd)) { - strerr_dief1x(100, "could not parse payload fd"); - } - - if(!parse_size(argv[2], &payload_size)) { - strerr_dief1x(100, "could not parse payload length"); - } - - char payload_data[payload_size]; - bytebuffer payload = {payload_data, payload_size}; - read_payload(payload_fd, payload); - fd_close(payload_fd); - - dbg_print_bb1("Got payload", payload); - process_payload(payload); - strerr_dief1x(110, "Internal logic error, should not get here"); - return 110; -} -/* -capability ``` -container/bzr.ccx/123456 -login/tty1/7890 -``` -- secret -- execline command -- env allowlist (re?) -- max execution count/id (uuidv7?) - -``` -caphdr = [capv0;name;invoke-once] -c1 = [caphdr;;h1=hmac(secret, caphdr)] -c2 = [caphdr;[att1];h2=hmac(h1, att1)] -c3 = [caphdr;[att1;att2];h3=hmac(h2, att2)] -``` -*/ - -/* vim: sts=2 sw=2 et -*/