ccx-utils

Miscellaneous utilities written in C
git clone https://ccx.te2000.cz/git/ccx-utils
Log | Files | Refs

commit c5eed37c744c786076a1ec13132ade7ef410ed16
parent 3b759a5229fcfaaf3732f296bdda279507ef1081
Author: Jan Pobrislo <ccx@te2000.cz>
Date:   Mon, 11 Nov 2024 01:50:49 +0000

Split miniroon into a separate repository

Diffstat:
Msrc/Makefile | 2+-
Dsrc/gen-miniroon.py | 43-------------------------------------------
Dsrc/miniroon.c | 537-------------------------------------------------------------------------------
3 files changed, 1 insertion(+), 581 deletions(-)

diff --git a/src/Makefile b/src/Makefile @@ -1,5 +1,5 @@ -tools_simple:=argv0exec nosuid pidns_run safelink spawn-pty fdsend fdrecv fdrecvto socketpair ptsname mtime_to_uuidv7 ucspi-socksserver ucspi-socksserver-connected miniroon +tools_simple:=argv0exec nosuid pidns_run safelink spawn-pty fdsend fdrecv fdrecvto socketpair ptsname mtime_to_uuidv7 ucspi-socksserver ucspi-socksserver-connected tools_libcap:=applyuidgid-caps diff --git a/src/gen-miniroon.py b/src/gen-miniroon.py @@ -1,43 +0,0 @@ -#!/usr/bin/env python3 -import sys -import hmac - - -class NetString(bytes): - @classmethod - def from_any(cls, data): - if isinstance(data, NetString): - return cls(data) - if isinstance(data, bytes): - return cls(to_ns(data)) - if isinstance(data, str): - return cls(to_ns(data.encode('ascii'))) - return cls(to_ns(b''.join(cls.from_any(i) for i in data))) - - -def to_ns(b): - assert isinstance(b, bytes) - return NetString(b'%d:%s,' % (len(b), b)) - - -def miniroon_hmac(key, msg): - print('miniroon_hmac%r' % ((key, msg),), file=sys.stderr) - #return hmac.digest(key, msg, 'blake2s') - return hmac.digest(key, msg, 'sha256') - - -def make_miniroon(name, action='invoke-once', secret=b'\0'*32, caveats=(), version='capv0'): - hdr = b''.join(NetString.from_any(i) for i in (version, name, action)) - sig = miniroon_hmac(secret, hdr) - for c in caveats: - sig = miniroon_hmac(sig, c) - return NetString.from_any([ - hdr, - caveats, - sig, - ]) - - -if __name__ == '__main__': - import os - os.write(1, make_miniroon(name='ccx')) diff --git a/src/miniroon.c b/src/miniroon.c @@ -1,537 +0,0 @@ -#include <errno.h> -#include <assert.h> -#include <unistd.h> -#include <stdbool.h> -#include <string.h> -#include <sys/select.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/blake2s.h> -#include <skalibs/sha256.h> -#include <skalibs/stralloc.h> -#include <skalibs/env.h> - -#define USAGE "miniroon directory" -#define PROG "miniroon" - -#define input_fd 0 -#define payload_size_max 1024*1024 -#define MAX_CAVEATS 256 -#define MAX_ENV_ALLOW 256 - -typedef struct bytebuffer_s { - char *data; - size_t len; -} bytebuffer; - -typedef struct netstring_chunk_b { - bytebuffer source; - bytebuffer outer; - bytebuffer inner; -} netstring_chunk; - -typedef struct miniroon_header_s { - bytebuffer id; - - enum miniroon_version { - V0 = 0 - } version; - - enum miniroon_action { - REVOKE, INVOKE, INVOKE_ONCE - } action; - -} miniroon_header; - -typedef struct miniroon_env_entry_s { - const bytebuffer name; - const bytebuffer value; - enum miniroon_env_state { - ENV_NO_CHANGE = 0, - ENV_SET = 1, - ENV_REMOVE = 2 - } state; -} miniroon_env_entry; - -typedef struct miniroon_env_map_s { - miniroon_env_entry env[MAX_ENV_ALLOW]; - size_t env_count; -} miniroon_env_map; - -typedef struct miniroon_data_s { - miniroon_header hdr; - bytebuffer caveats[MAX_CAVEATS]; - size_t caveat_count; -} miniroon_data; - -/* declarations */ -void dbg_print_bb(const bytebuffer bb); -void dbg_print_bb1(const char *text, const bytebuffer bb); -void miniroon_env_map_init(miniroon_env_map *emap); -void miniroon_env_map_add(miniroon_env_map *emap, const bytebuffer name); -int miniroon_env_map_find(miniroon_env_map *emap, const bytebuffer name); -void miniroon_data_init(miniroon_data *data); -void process_payload(const bytebuffer payload); -void process_header(miniroon_header *header, const bytebuffer source); -void validate_caveats(miniroon_data *data); -void read_secret(const bytebuffer secret); // TODO -void hmac_b2s_256(const bytebuffer key, const bytebuffer msg, const bytebuffer output); -void hmac_sha2_256(const bytebuffer key, const bytebuffer msg, const bytebuffer output); - -#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 dbg_print_bb(const bytebuffer bb) { - static const char digit[] = "0123456789abcdef"; - char ascii[16]; - char hex[32]; - size_t i, j, a_off=0, h_off=0; - for (i = 0; i < bb.len; ++i) { - if (i % 16 == 0) { - if(i) { - write(2, "|", 1); - write(2, hex, h_off); - write(2, "| [", 3); - write(2, ascii, a_off); - write(2, "]\n", 2); - } - a_off=0; - h_off=0; - } - unsigned char c = bb.data[i]; - ascii[a_off++] = (c >= ' ' && c <= '~') ? c : '.'; - hex[h_off++] = digit[0xf & (c >> 4)]; - hex[h_off++] = digit[0xf & c]; - } - while(h_off < 32) { - hex[h_off++] = ' '; - } - write(2, "|", 1); - write(2, hex, h_off); - write(2, "| [", 3); - write(2, ascii, a_off); - write(2, "]\n", 2); -} - -void dbg_print_bb1(const char *text, const bytebuffer bb) { - write(2, "\n", 1); - write(2, text, strlen(text)); - write(2, ":\n", 2); - dbg_print_bb(bb); -} - -void netstring_chunk_init (netstring_chunk *chunk, const bytebuffer source) { - memset(chunk, 0, sizeof(netstring_chunk)); - chunk->source = source; -} - -bool netstring_chunk_next (netstring_chunk *c) { - // bytebuffer dbg_bb = {(void*)c, sizeof(netstring_chunk)}; - // dbg_print_bb1("netstring chunk", dbg_bb); - // dbg_print_bb1("netstring source", c->source); - - if(!c->source.len) { - return false; - } - c->outer.data = c->source.data; - c->outer.len /* size of numerical prefix */ = uint64_scan(c->source.data, &c->inner.len); - if (c->source.data[c->outer.len] != ':') { - strerr_dief1x(111, "Malformed netstring (expected ':')"); - } - if (c->outer.len + c->inner.len + 2 > c->source.len) { - strerr_dief1x(111, "Malformed netstring (truncated)"); - } - if (c->source.data[c->outer.len + c->inner.len + 1] != ',') { - strerr_dief1x(111, "Malformed netstring (expected ',')"); - } - assert(c->source.len >= c->outer.len); - c->inner.data = &c->source.data[c->outer.len + 1]; - c->outer.len += c->inner.len + 2; - c->source.data += c->outer.len; - c->source.len -= c->outer.len; - // dbg_print_bb1("Chunk > Outer", c->outer); - // dbg_print_bb1("Chunk > Inner", c->inner); - return true; -} - -void miniroon_data_init(miniroon_data *data) { - memset(data, 0, sizeof(miniroon_data)); - // data->env_modif = STRALLOC_ZERO ; -} - -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"); - } -} - -int strbbcmp(const bytebuffer bb, const char *s) { - return strncmp(bb.data, s, bb.len); -} - -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 process_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"); - } - - 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 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 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 miniroon_env_map_init(miniroon_env_map *emap) { - memset(emap, 0, sizeof(miniroon_env_map)); -} -void miniroon_env_map_add(miniroon_env_map *emap, const bytebuffer name); -// TODO -int miniroon_env_map_find(miniroon_env_map *emap, const bytebuffer name); -// TODO - -void validate_caveats(miniroon_data *md) { - miniroon_env_map emap; - miniroon_env_map_init(&emap); - // stralloc env_modif; - - int env_allow_fd = openc_readb("env.allow"); - if (env_allow_fd < 0) { - strerr_dief1sys(111, "open(env.allow)"); - } - - if(close(env_allow_fd) != 0) { - strerr_dief1sys(111, "close(env_allow_fd)"); - } -} - -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"); - } - process_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_caveats(&md); - - /* iff everything validated correctly */ - // TODO: pass unused argv from main() ? - char cmd[] = "./run"; - const char *cmd_argv[2] = {cmd, 0}; - xexec(cmd_argv); -} - -void hmac_b2s_256(const bytebuffer key, const bytebuffer msg, const bytebuffer output) { - static const size_t block_size = 64, digest_size = 32; - assert(key.len <= block_size); - assert(output.len == digest_size); - //assert(msg); - - dbg_print_bb1("HMAC key", key); - dbg_print_bb1("HMAC message", msg); - blake2s_ctx hash_ctx; - uint8_t pad[block_size], ihash[digest_size]; - - blake2s_init(&hash_ctx, digest_size); - // i_key_pad := block_sized_key xor [0x36 blockSize] // Inner padded key - for(size_t i=0; i<block_size; i++) { - pad[i] = (i < key.len ? key.data[i] : 0) ^ 0x36; - } - // ihash = hash(i_key_pad || message) - blake2s_update(&hash_ctx, pad, block_size); - blake2s_update(&hash_ctx, msg.data, msg.len); - blake2s_final(&hash_ctx, ihash); - - blake2s_init(&hash_ctx, block_size); - // o_key_pad := block_sized_key xor [0x5c blockSize] // Outer padded key - for(size_t i=0; i<block_size; i++) { - pad[i] = (i < key.len ? key.data[i] : 0) ^ 0x5c; - } - // ohash = hash(o_key_pad || ihash) - blake2s_update(&hash_ctx, pad, block_size); - blake2s_update(&hash_ctx, ihash, digest_size); - blake2s_final(&hash_ctx, output.data); - dbg_print_bb1("HMAC output", output); -} - -/* function doing the HMAC-SHA-256 calculation */ -void hmac_sha256(const uint8_t* key, const uint32_t keysize, const uint8_t* msg, const uint32_t msgsize, uint8_t* output) -{ - static const size_t block_size = 64, digest_size = 32; - SHA256Schedule outer, inner; - uint8_t tmp; - - if (keysize > block_size) // if len(key) > blocksize(sha256) => key = sha256(key) - { - uint8_t new_key[digest_size]; - sha256_init(&outer); - sha256_update(&outer, key, keysize); - sha256_final(&outer, new_key); - return hmac_sha256(new_key, digest_size, msg, msgsize, output); - } - sha256_init(&outer); - sha256_init(&inner); - - uint32_t i; - for (i = 0; i < keysize; ++i) - { - tmp = key[i] ^ 0x5C; - sha256_update(&outer, &tmp, 1); - tmp = key[i] ^ 0x36; - sha256_update(&inner, &tmp, 1); - } - for (; i < block_size; ++i) - { - tmp = 0x5C; - sha256_update(&outer, &tmp, 1); - tmp = 0x36; - sha256_update(&inner, &tmp, 1); - } - - sha256_update(&inner, msg, msgsize); - sha256_final(&inner, output); - - sha256_update(&outer, output, digest_size); - sha256_final(&outer, output); -} - -void hmac_sha2_256(const bytebuffer key, const bytebuffer msg, bytebuffer output) { - static const size_t block_size = 32; - assert(key.len == block_size); - assert(output.len == block_size); - - dbg_print_bb1("HMAC key", key); - dbg_print_bb1("HMAC message", msg); - hmac_sha256(key.data, key.len, msg.data, msg.len, output.data); - dbg_print_bb1("HMAC output", output); -} - -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"); -} - -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); - 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 -*/