miniroon

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

miniroon-verify.c (6030B)


      1 #include <errno.h>
      2 #include <assert.h>
      3 #include <stdbool.h>
      4 #include <unistd.h>
      5 #include <sys/select.h>
      6 
      7 // for debug prints
      8 #include <stdio.h>
      9 
     10 #include <skalibs/types.h>
     11 #include <skalibs/strerr.h>
     12 #include <skalibs/djbunix.h>
     13 #include <skalibs/exec.h>
     14 #include <skalibs/netstring.h>
     15 #include <skalibs/uint64.h>
     16 #include <skalibs/stralloc.h>
     17 #include <skalibs/env.h>
     18 
     19 #include "verify_common.h"
     20 #include "bytebuffer.h"
     21 #include "netstring.h"
     22 #include "hmac_sha2_256.h"
     23 #include "miniroon-header.h"
     24 #include "caveats.h"
     25 
     26 typedef struct miniroon_data_s {
     27   miniroon_header hdr;
     28   bytebuffer caveats[MAX_CAVEATS];
     29   size_t caveat_count;
     30 } miniroon_data;
     31 
     32 /* declarations */
     33 void miniroon_data_init(miniroon_data *data);
     34 void process_payload(const bytebuffer payload);
     35 void validate_and_exec(miniroon_data *data);
     36 void read_secret(const bytebuffer secret);
     37 
     38 #define MINIROON_HMAC_SIZE 32
     39 //#define MINIROON_HMAC_FUNC(key, msg, out) hmac_b2s_256(key, msg, out)
     40 #define MINIROON_HMAC_FUNC(key, msg, out) hmac_sha2_256(key, msg, out)
     41 
     42 /* definitions */
     43 
     44 
     45 void miniroon_data_init(miniroon_data *data) {
     46   memset(data, 0, sizeof(miniroon_data));
     47   // data->env_modif = STRALLOC_ZERO ;
     48 }
     49 
     50 void read_secret(const bytebuffer secret){
     51   assert(secret.len == MINIROON_HMAC_SIZE);
     52   size_t bytes_read = 0;
     53   int secret_fd = openc_readb("secret");
     54   if (secret_fd < 0) {
     55     strerr_dief1sys(111, "open(secret)");
     56   }
     57   while(bytes_read < secret.len) {
     58     ssize_t r = read(secret_fd, &secret.data[bytes_read], secret.len - bytes_read);
     59     switch(r) {
     60       case 0:
     61         strerr_dief1x(111, "EOF before full secret was read");
     62         break;
     63       case -1:
     64         if(errno != EINTR) {
     65           strerr_dief1sys(111, "read() length");
     66         }
     67         break;
     68     }
     69     bytes_read += r;
     70   }
     71   if(close(secret_fd) != 0) {
     72     strerr_dief1sys(111, "close(secret_fd)");
     73   }
     74 }
     75 
     76 
     77 void validate_and_exec(miniroon_data *md) {
     78   miniroon_caveats_state state;
     79   miniroon_caveats_state_init(&state);
     80   // stralloc env_modif;
     81 
     82   for(size_t i=0; i < md->caveat_count; i++) {
     83     dbg_print_bb1("Validate caveat", md->caveats[i]);
     84     miniroon_caveat_prepare(md->caveats[i], &state);
     85   }
     86   for(size_t i=0; i < md->caveat_count; i++) {
     87     dbg_print_bb1("Validate caveat", md->caveats[i]);
     88     miniroon_caveat_validate(md->caveats[i], &state);
     89   }
     90 
     91   /* iff everything validated correctly */
     92   // TODO: pass unused argv from main() ?
     93   char cmd[] = "./run";
     94   const char *cmd_argv[2] = {cmd, 0};
     95   miniroon_caveats_state_exec(&state, cmd_argv);
     96 }
     97 
     98 void process_payload(const bytebuffer payload) {
     99   miniroon_data md;
    100   miniroon_data_init(&md);
    101   netstring_chunk c;
    102   netstring_chunk_init(&c, payload);
    103 
    104   if(!netstring_chunk_next(&c)) {
    105     strerr_dief1x(111, "Mising miniroon header");
    106   }
    107   parse_header(&md.hdr, c.inner);
    108   // header should be verified by now, we can start hashing
    109   uint8_t hmac_data[MINIROON_HMAC_SIZE];
    110   bytebuffer hmac_bb = {hmac_data, MINIROON_HMAC_SIZE};
    111   read_secret(hmac_bb);
    112   // dbg_print_bb1("Secret", hmac_bb);
    113   MINIROON_HMAC_FUNC(hmac_bb, c.inner, hmac_bb);
    114   // dbg_print_bb1("Signature update", hmac_bb);
    115 
    116   if(!netstring_chunk_next(&c)) {
    117     strerr_dief1x(111, "Mising miniroon body");
    118   }
    119   netstring_chunk body;
    120   netstring_chunk_init(&body, c.inner);
    121 
    122   while(netstring_chunk_next(&body)) {
    123     dbg_print_bb1("Got caveat", body.inner);
    124     if(md.caveat_count >= MAX_CAVEATS) {
    125       strerr_dief1x(111, "Too many caveats");
    126     }
    127     md.caveats[md.caveat_count++] = body.inner;
    128     MINIROON_HMAC_FUNC(hmac_bb, body.inner, hmac_bb);
    129     // dbg_print_bb1("Signature update", hmac_bb);
    130   }
    131 
    132   if(!netstring_chunk_next(&c)) {
    133     strerr_dief1x(111, "Mising miniroon signature");
    134   }
    135   dbg_print_bb1("Got signature", c.inner);
    136   if(c.inner.len != MINIROON_HMAC_SIZE) {
    137     strerr_dief1x(111, "Invalid miniroon signature length");
    138   }
    139   /* constant time hash compare */
    140   uint8_t bitdiff = 0;
    141   for(size_t i=0; i<MINIROON_HMAC_SIZE; i++) {
    142     bitdiff |= hmac_data[i] ^ c.inner.data[i];
    143   }
    144   if(netstring_chunk_next(&c)) {
    145     strerr_dief1x(111, "Extraneous data in miniroon");
    146   }
    147   if(bitdiff) {
    148     strerr_dief1x(111, "Invalid miniroon signature");
    149   }
    150 
    151   validate_and_exec(&md);
    152   strerr_dief1x(110, "Internal logic error, should not get here");
    153 }
    154 
    155 void read_payload(int payload_fd, const bytebuffer bb) {
    156   int flags = fcntl(payload_fd, F_GETFL);
    157   if(flags == -1) {
    158     strerr_dief1sys(111, "fcntl(payload_fd) getfd");
    159   }
    160   if(fcntl(payload_fd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
    161     strerr_dief1sys(111, "fcntl(payload_fd) setfd");
    162   }
    163 
    164   ssize_t payload_read = fd_read(payload_fd, bb.data, bb.len);
    165   if(payload_read < 0 ) {
    166     strerr_dief1sys(111, "read(payload)");
    167   }
    168   if(payload_read != bb.len) {
    169     strerr_dief1x(111, "could not read whole payload");
    170   }
    171 }
    172 
    173 bool parse_fd(char const *arg, int *fd) {
    174   size_t arg_size = strlen(arg);
    175   return int_scan(arg, fd) == arg_size;
    176 }
    177 
    178 bool parse_size(char const *arg, size_t *size) {
    179   size_t arg_size = strlen(arg);
    180   return size_scan(arg, size) == arg_size;
    181 }
    182 
    183 int main (int argc, char const *const *argv)
    184 {
    185   if (argc != 3) {
    186     strerr_dieusage(100, USAGE);
    187   }
    188 
    189   int payload_fd;
    190   size_t payload_size;
    191 
    192   if(!parse_fd(argv[1], &payload_fd)) {
    193     strerr_dief1x(100, "could not parse payload fd");
    194   }
    195 
    196   if(!parse_size(argv[2], &payload_size)) {
    197     strerr_dief1x(100, "could not parse payload length");
    198   }
    199 
    200   char payload_data[payload_size];
    201   bytebuffer payload = {payload_data, payload_size};
    202   read_payload(payload_fd, payload);
    203   fd_close(payload_fd);
    204 
    205   dbg_print_bb1("Got payload", payload);
    206   process_payload(payload);
    207   strerr_dief1x(110, "Internal logic error, should not get here");
    208   return 110;
    209 }
    210 /*
    211 capability ```
    212 container/bzr.ccx/123456
    213 login/tty1/7890
    214 ```
    215 - secret
    216 - execline command
    217 - env allowlist (re?)
    218 - max execution count/id (uuidv7?)
    219 
    220 ```
    221 caphdr = [capv0;name;invoke-once]
    222 c1 = [caphdr;;h1=hmac(secret, caphdr)]
    223 c2 = [caphdr;[att1];h2=hmac(h1, att1)]
    224 c3 = [caphdr;[att1;att2];h3=hmac(h2, att2)]
    225 ```
    226 */
    227 
    228 /*  vim: sts=2 sw=2 et
    229 */