miniroon

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

miniroon-read.c (5115B)


      1 #include <errno.h>
      2 #include <unistd.h>
      3 #include <sys/select.h>
      4 #include <stdlib.h>
      5 #include <assert.h>
      6 
      7 #include <skalibs/types.h>
      8 #include <skalibs/strerr.h>
      9 #include <skalibs/djbunix.h>
     10 #include <skalibs/exec.h>
     11 #include <skalibs/netstring.h>
     12 #include <skalibs/stralloc.h>
     13 #include <skalibs/env.h>
     14 
     15 #include "netstring.h"
     16 #include "miniroon-header.h"
     17 
     18 #define USAGE "miniroon-read directory"
     19 #define PROG "miniroon-read"
     20 
     21 #define input_fd 0
     22 #define payload_size_max 1024*1024
     23 #define MAX_CAVEATS 256
     24 #define MAX_ENV_ALLOW 256
     25 
     26 
     27 void fd_block(int fd) {
     28     int flags = fcntl(fd, F_GETFL);
     29     if(flags == -1) {
     30         strerr_dief1sys(111, "fcntl() getfd");
     31     }
     32     if(fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
     33         strerr_dief1sys(111, "fcntl() setfd");
     34     }
     35 }
     36 
     37 size_t read_payload_size(int fd) {
     38   char read_char;
     39   size_t payload_size = 0;
     40 
     41   fd_block(fd);
     42 
     43   while(payload_size < payload_size_max) {
     44     switch(read(fd, &read_char, 1)) {
     45       case 0:
     46         strerr_dief1x(111, "EOF before netstring size was read");
     47         break;
     48       case 1:
     49         if(read_char == ':') {
     50           return payload_size;
     51         } else if(read_char >= '0' && read_char <= '9') {
     52           payload_size *= 10;
     53           payload_size += read_char - '0';
     54         } else {
     55           strerr_dief1x(111, "Malformed netstring on input");
     56         }
     57         break;
     58       case -1:
     59         if(errno != EINTR) {
     60           strerr_dief1sys(111, "read() length");
     61         }
     62         break;
     63       default:
     64         strerr_dief1x(110, "Unexpected return value from read()");
     65         break;
     66     }
     67   }
     68 
     69   strerr_dief1x(111, "Input netstring too big");
     70 }
     71 
     72 void read_payload(const bytebuffer bb) {
     73   char *read_next = bb.data;
     74   ssize_t read_size;
     75   while(read_next - bb.data < bb.len) {
     76     read_size = read(input_fd, read_next, bb.len - (read_next - bb.data));
     77     if(read_size == 0) {
     78       strerr_dief1x(111, "EOF before full netstring payload was read");
     79     }
     80     if(read_size == -1) {
     81       if(errno != EINTR) {
     82         strerr_dief1sys(111, "read() payload");
     83       }
     84       continue;
     85     }
     86     read_next += read_size;
     87   }
     88   if(bb.data[bb.len - 1] != ',') {
     89       strerr_dief1x(111, "Invalid netstring terminator");
     90   }
     91 }
     92 
     93 void process_header(miniroon_header *header, const bytebuffer source) {
     94   parse_header(header, source);
     95 
     96   char id[header->id.len + 1];
     97   for(size_t i=0; i<header->id.len; i++) {
     98     id[i] = header->id.data[i];
     99     if(id[i] == '-') { continue; }
    100     if(id[i] >= '0' && id[i] <= '9') { continue; }
    101     if(id[i] >= 'a' && id[i] <= 'z') { continue; }
    102     strerr_dief1x(111, "Invalid character in miniroon ID");
    103   }
    104   id[header->id.len] = 0;
    105 
    106   if (chdir(id) != 0) {
    107     strerr_dief1sys(111, "chdir(id)");
    108   }
    109 }
    110 
    111 void exec_verify_and_write_payload (const bytebuffer payload);
    112 void process_payload(const bytebuffer payload) {
    113   miniroon_header header;
    114   netstring_chunk c;
    115   netstring_chunk_init(&c, payload);
    116 
    117   if(!netstring_chunk_next(&c)) {
    118     strerr_dief1x(111, "Mising miniroon header");
    119   }
    120   process_header(&header, c.inner);
    121   exec_verify_and_write_payload(payload);
    122 }
    123 
    124 void exec_verify_and_write_payload (const bytebuffer payload) {
    125   pid_t pid1=0, pid2=0;
    126   int wstat ;
    127   int p[2] ;
    128 
    129   if(pipe(p) == -1) {
    130     strerr_diefu1sys(111, "create pipe");
    131   }
    132 
    133   pid1 = fork();
    134   switch(pid1) {
    135     case -1:
    136       strerr_diefu1sys(111, "first fork()");
    137       break;
    138     case 0:  /* child */
    139       fd_close(p[0]);
    140       pid2 = fork();
    141       switch(pid2) {
    142         case -1:
    143           strerr_diefu1sys(111, "second fork()");
    144           break;
    145         case 0:  /* child */
    146           ssize_t payload_write = fd_write(p[1], payload.data, payload.len);
    147           if(payload_write < 0 ) {
    148             strerr_dief1sys(111, "write(payload)");
    149           }
    150           if(payload_write != payload.len) {
    151             strerr_dief1x(111, "could not write whole payload");
    152           }
    153           exit(0);
    154           break;
    155         default:  /* parent */
    156           exit(0);
    157           break;
    158       }
    159       break;
    160     default:  /* parent */
    161       waitpid_nointr(pid1, &wstat, 0);
    162       if(!WIFEXITED(wstat) || WEXITSTATUS(wstat) != 0) {
    163         strerr_dief1x(111, "child terminated abnormally");
    164       }
    165 
    166       char cmd[] = "./verify";
    167       char fmt_fd[UINT64_FMT], fmt_len[UINT64_FMT];
    168       fmt_fd[uint64_fmt(fmt_fd, (uint64_t)p[0])] = 0;
    169       fmt_len[uint64_fmt(fmt_len, (uint64_t)payload.len)] = 0;
    170       const char *cmd_argv[4] = {cmd, fmt_fd, fmt_len, 0};
    171 
    172       fd_close(p[1]);
    173       xexec(cmd_argv);
    174       break;
    175   }
    176 }
    177 
    178 int main (int argc, char const *const *argv)
    179 {
    180 
    181   if (argc != 2) {
    182     strerr_dieusage(100, USAGE);
    183   }
    184 
    185   if (chdir(argv[1]) != 0) {
    186     strerr_dief1sys(111, "chdir()");
    187   }
    188   size_t payload_size = read_payload_size(input_fd);
    189   char payload_data[payload_size + 1];
    190   bytebuffer payload = {payload_data, payload_size + 1};
    191   read_payload(payload);
    192   payload.len -= 1; /* strip final netstring terminator */
    193   dbg_print_bb1("Got payload", payload);
    194   process_payload(payload);
    195   assert(0);
    196   strerr_dief1x(110, "Internal logic error, should not get here");
    197   return 110;
    198 }
    199 
    200 /*  vim: sts=2 sw=2 et
    201 */