s6-accessrules-fs-from-cdb.c (4263B)
1 /* ISC license. */ 2 3 #include <string.h> 4 #include <stdint.h> 5 #include <sys/stat.h> 6 #include <errno.h> 7 #include <unistd.h> 8 9 #include <skalibs/bytestr.h> 10 #include <skalibs/types.h> 11 #include <skalibs/cdb.h> 12 #include <skalibs/strerr.h> 13 #include <skalibs/djbunix.h> 14 15 #define USAGE "s6-accessrules-fs-from-cdb dir cdbfile" 16 17 static char const *basedir ; 18 size_t basedirlen ; 19 20 static void cleanup () 21 { 22 int e = errno ; 23 rm_rf(basedir) ; 24 errno = e ; 25 } 26 27 static int domkdir (char const *s) 28 { 29 return mkdir(s, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH | S_ISGID) < 0 ? errno == EEXIST : 1 ; 30 } 31 32 static void mkdirp (char *s) 33 { 34 mode_t m = umask(0) ; 35 size_t len = strlen(s) ; 36 size_t i = basedirlen + 1 ; 37 for (; i < len ; i++) if (s[i] == '/') 38 { 39 s[i] = 0 ; 40 if (!domkdir(s)) goto err ; 41 s[i] = '/' ; 42 } 43 if (!domkdir(s)) goto err ; 44 umask(m) ; 45 return ; 46 47 err: 48 cleanup() ; 49 strerr_diefu2sys(111, "mkdir ", s) ; 50 } 51 52 static void touchtrunc (char const *file) 53 { 54 int fd = open_trunc(file) ; 55 if (fd < 0) 56 { 57 cleanup() ; 58 strerr_diefu2sys(111, "open_trunc ", file) ; 59 } 60 fd_close(fd) ; 61 } 62 63 static int doenv (char const *dir, size_t dirlen, char const *env, uint32_t envlen) 64 { 65 mode_t m = umask(0) ; 66 size_t i = 0 ; 67 if (!domkdir(dir)) 68 { 69 cleanup() ; 70 strerr_diefu2sys(111, "mkdir ", dir) ; 71 } 72 umask(m) ; 73 while (i < envlen) 74 { 75 size_t n = byte_chr(env + i, envlen - i, 0) ; 76 if (i + n >= envlen) return 0 ; 77 { 78 size_t p = byte_chr(env + i, n, '=') ; 79 char tmp[dirlen + p + 2] ; 80 memcpy(tmp, dir, dirlen) ; 81 tmp[dirlen] = '/' ; 82 memcpy(tmp + dirlen + 1, env + i, p) ; 83 tmp[dirlen + p + 1] = 0 ; 84 if (p < n) 85 { 86 struct iovec v[2] = { { .iov_base = (char *)env + i + p + 1, .iov_len = n - p - 1 }, { .iov_base = "\n", .iov_len = 1 } } ; 87 if (!openwritevnclose_unsafe(tmp, v, 2)) 88 { 89 cleanup() ; 90 strerr_diefu2sys(111, "openwritenclose_unsafe ", tmp) ; 91 } 92 } 93 else touchtrunc(tmp) ; 94 } 95 i += n + 1 ; 96 } 97 return 1 ; 98 } 99 100 static int doit (char const *key, uint32_t klen, char const *data, uint32_t dlen) 101 { 102 uint16_t envlen, execlen ; 103 char name[basedirlen + klen + 8] ; 104 if (!dlen || (dlen > 8201)) return 0 ; 105 memcpy(name, basedir, basedirlen) ; 106 name[basedirlen] = '/' ; 107 memcpy(name + basedirlen + 1, key, klen) ; 108 name[basedirlen + 1 + klen] = 0 ; 109 mkdirp(name) ; 110 name[basedirlen + 1 + klen] = '/' ; 111 if (data[0] == 'A') 112 { 113 memcpy(name + basedirlen + klen + 2, "allow", 6) ; 114 touchtrunc(name) ; 115 } 116 else if (data[0] == 'D') 117 { 118 memcpy(name + basedirlen + klen + 2, "deny", 5) ; 119 touchtrunc(name) ; 120 } 121 if (dlen < 3) return 1 ; 122 uint16_unpack_big(data + 1, &envlen) ; 123 if ((envlen > 4096U) || (3U + envlen > dlen)) return 0 ; 124 uint16_unpack_big(data + 3 + envlen, &execlen) ; 125 if ((execlen > 4096U) || (5U + envlen + execlen != dlen)) return 0 ; 126 if (envlen) 127 { 128 memcpy(name + basedirlen + klen + 2, "env", 4) ; 129 if (!doenv(name, basedirlen + klen + 5, data + 3, envlen)) return 0 ; 130 } 131 if (execlen) 132 { 133 memcpy(name + basedirlen + klen + 2, "exec", 5) ; 134 if (!openwritenclose_unsafe(name, data + 5 + envlen, execlen)) 135 { 136 cleanup() ; 137 strerr_diefu2sys(111, "openwritenclose_unsafe ", name) ; 138 } 139 } 140 return 1 ; 141 } 142 143 int main (int argc, char const *const *argv) 144 { 145 cdb c = CDB_ZERO ; 146 uint32_t pos = CDB_TRAVERSE_INIT() ; 147 PROG = "s6-accessrules-fs-from-cdb" ; 148 if (argc < 3) strerr_dieusage(100, USAGE) ; 149 if (!cdb_init(&c, argv[2])) strerr_diefu1sys(111, "cdb_init") ; 150 basedir = argv[1] ; 151 basedirlen = strlen(argv[1]) ; 152 { 153 mode_t m = umask(0) ; 154 if (mkdir(basedir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH | S_ISGID) < 0) 155 strerr_diefu2sys(111, "mkdir ", basedir) ; 156 umask(m) ; 157 } 158 for (;;) 159 { 160 cdb_data key, data ; 161 int r = cdb_traverse_next(&c, &key, &data, &pos) ; 162 if (r < 0) 163 { 164 cleanup() ; 165 strerr_diefu1x(111, "cdb_traverse_next: invalid cdb") ; 166 } 167 else if (!r) break ; 168 else if (!doit(key.s, key.len, data.s, data.len)) 169 { 170 cleanup() ; 171 strerr_diefu3x(111, "handle cdb record: ", argv[2], " does not contain valid accessrules data") ; 172 } 173 } 174 return 0 ; 175 }