manifest.c (19244B)
1 #include "manifest.h" 2 3 #include <stdio.h> 4 #include <string.h> 5 6 #include "dist_parse.h" 7 #include "tree.h" 8 9 static int emit(KitWriter* out, const char* s) { 10 return kit_writer_write(out, s, strlen(s)) == KIT_OK ? DIST_OK : DIST_ERR; 11 } 12 13 static int emit_hex(KitWriter* out, const char* key, const uint8_t* h, 14 size_t n) { 15 char hex[2 * DIST_BLAKE2B_LEN + 1]; 16 dist_hex_encode(hex, h, n); 17 return dist_emit_kv(out, key, hex); 18 } 19 20 static int emit_u64(KitWriter* out, const char* key, uint64_t v) { 21 char num[24]; 22 snprintf(num, sizeof num, "%llu", (unsigned long long)v); 23 return dist_emit_kv(out, key, num); 24 } 25 26 static int kind_valid(const char* k) { 27 return strcmp(k, "exe") == 0 || strcmp(k, "dso") == 0 || 28 strcmp(k, "obj") == 0 || strcmp(k, "wasm") == 0 || 29 strcmp(k, "lib") == 0 || strcmp(k, "data") == 0 || 30 strcmp(k, "source") == 0; 31 } 32 33 int dist_manifest_path_valid(const char* p) { 34 /* Shared with dist_tree_path_valid (the strictest variant, which also 35 * rejects embedded '\n'/'\r'). Manifest path values are already line-scanned 36 * and field_text_valid-checked before reaching here, so delegating only adds 37 * defence in depth: it does not change any reachable accept/reject result. */ 38 return dist_tree_path_valid(p); 39 } 40 41 #define P3_F_NAME 0x00000001u 42 #define P3_F_VERSION 0x00000002u 43 #define P3_F_DESCRIPTION 0x00000004u 44 #define P3_F_HASH 0x00000008u 45 #define P3_F_TREE_FORMAT 0x00000010u 46 #define P3_F_BLOB_FORMAT 0x00000020u 47 #define P3_F_ID 0x00000040u 48 #define P3_F_OUTPUT_NAME 0x00000080u 49 #define P3_F_TREE_ID 0x00000100u 50 #define P3_F_TARGET 0x00000200u 51 #define P3_F_DEFAULT 0x00000400u 52 #define P3_F_OUTPUT 0x00000800u 53 #define P3_F_PATH 0x00001000u 54 #define P3_F_KIND 0x00002000u 55 #define P3_F_ENTRY 0x00004000u 56 #define P3_F_PACKAGE 0x00008000u 57 #define P3_F_KEY 0x00010000u 58 59 typedef enum { 60 P3_SEC_TOP, 61 P3_SEC_OUTPUT, 62 P3_SEC_ARTIFACT, 63 P3_SEC_DEPENDENCY 64 } PackageSection; 65 66 static int field_text_valid(const char* s, int required) { 67 if (required && !s[0]) return 0; 68 for (; *s; ++s) { 69 if (*s == '\n' || *s == '\r') return 0; 70 } 71 return 1; 72 } 73 74 static int parse_bool3(const char* s, int* out) { 75 if (strcmp(s, "true") == 0) { 76 *out = 1; 77 return DIST_OK; 78 } 79 if (strcmp(s, "false") == 0) { 80 *out = 0; 81 return DIST_OK; 82 } 83 return DIST_ERR; 84 } 85 86 static int decode_hash3(uint8_t out[DIST_BLAKE2B_LEN], const char* val, 87 const char* err_msg, char* err, size_t errcap) { 88 if (strlen(val) != 2u * DIST_BLAKE2B_LEN || 89 dist_hex_decode(out, val, DIST_BLAKE2B_LEN) != DIST_OK) 90 return dist_set_err(err, errcap, err_msg); 91 return DIST_OK; 92 } 93 94 static int decode_keyid3(uint8_t out[DIST_KEYID_LEN], const char* val, 95 char* err, size_t errcap) { 96 if (strlen(val) != 2u * DIST_KEYID_LEN || 97 dist_hex_decode(out, val, DIST_KEYID_LEN) != DIST_OK) 98 return dist_set_err(err, errcap, "bad dependency key id"); 99 return DIST_OK; 100 } 101 102 static int seen_once3(uint32_t* seen, uint32_t bit, const char* msg, char* err, 103 size_t errcap) { 104 if (*seen & bit) return dist_set_err(err, errcap, msg); 105 *seen |= bit; 106 return DIST_OK; 107 } 108 109 static int find_output3(const DistPackageManifest* m, uint64_t id) { 110 size_t i; 111 for (i = 0; i < m->n_outputs; ++i) { 112 if (m->outputs[i].id == id) return (int)i; 113 } 114 return -1; 115 } 116 117 static int finalize_package_section3(PackageSection sec, uint32_t seen, 118 char* err, size_t errcap) { 119 if (sec == P3_SEC_TOP) { 120 if ((seen & (P3_F_NAME | P3_F_VERSION | P3_F_HASH | P3_F_TREE_FORMAT | 121 P3_F_BLOB_FORMAT)) != (P3_F_NAME | P3_F_VERSION | P3_F_HASH | 122 P3_F_TREE_FORMAT | P3_F_BLOB_FORMAT)) 123 return dist_set_err(err, errcap, "missing required top-level field"); 124 } else if (sec == P3_SEC_OUTPUT) { 125 if ((seen & (P3_F_ID | P3_F_OUTPUT_NAME | P3_F_TREE_ID)) != 126 (P3_F_ID | P3_F_OUTPUT_NAME | P3_F_TREE_ID)) 127 return dist_set_err(err, errcap, "missing required [output] field"); 128 } else if (sec == P3_SEC_ARTIFACT) { 129 if ((seen & (P3_F_OUTPUT | P3_F_PATH | P3_F_KIND)) != 130 (P3_F_OUTPUT | P3_F_PATH | P3_F_KIND)) 131 return dist_set_err(err, errcap, "missing required [artifact] field"); 132 } else { 133 if ((seen & (P3_F_NAME | P3_F_VERSION)) != (P3_F_NAME | P3_F_VERSION)) 134 return dist_set_err(err, errcap, "missing required [dependency] field"); 135 } 136 return DIST_OK; 137 } 138 139 int dist_package_manifest_validate(const DistPackageManifest* m, char* err, 140 size_t errcap) { 141 size_t i, j; 142 size_t default_outputs = 0; 143 144 if (!field_text_valid(m->name, 1) || !field_text_valid(m->version, 1) || 145 !field_text_valid(m->description, 0)) 146 return dist_set_err(err, errcap, "bad package string field"); 147 if (m->n_outputs == 0) return dist_set_err(err, errcap, "missing [output]"); 148 if (m->n_outputs > DIST_MAX_OUTPUTS) 149 return dist_set_err(err, errcap, "too many outputs"); 150 if (m->n_artifacts > DIST_MAX_ARTIFACTS) 151 return dist_set_err(err, errcap, "too many artifacts"); 152 if (m->n_deps > DIST_MAX_DEPS) 153 return dist_set_err(err, errcap, "too many dependencies"); 154 155 for (i = 0; i < m->n_outputs; ++i) { 156 const DistPackageOutput* out = &m->outputs[i]; 157 if (!field_text_valid(out->name, 1) || !field_text_valid(out->target, 0)) 158 return dist_set_err(err, errcap, "bad output string field"); 159 if (out->is_default) ++default_outputs; 160 for (j = i + 1u; j < m->n_outputs; ++j) { 161 if (out->id == m->outputs[j].id) 162 return dist_set_err(err, errcap, "duplicate output id"); 163 } 164 } 165 if (default_outputs > 1u) 166 return dist_set_err(err, errcap, "duplicate default output"); 167 168 for (i = 0; i < m->n_artifacts; ++i) { 169 const DistPackageArtifact* art = &m->artifacts[i]; 170 if (find_output3(m, art->output_id) < 0) 171 return dist_set_err(err, errcap, "artifact references unknown output"); 172 if (!field_text_valid(art->path, 1) || !dist_manifest_path_valid(art->path)) 173 return dist_set_err(err, errcap, "unsafe artifact path"); 174 if (!kind_valid(art->kind)) 175 return dist_set_err(err, errcap, "unknown artifact kind"); 176 for (j = i + 1u; j < m->n_artifacts; ++j) { 177 const DistPackageArtifact* other = &m->artifacts[j]; 178 if (art->output_id == other->output_id && 179 strcmp(art->path, other->path) == 0) 180 return dist_set_err(err, errcap, "duplicate artifact path"); 181 } 182 } 183 184 for (i = 0; i < m->n_deps; ++i) { 185 const DistPackageDependency* dep = &m->deps[i]; 186 if (!field_text_valid(dep->name, 1) || !field_text_valid(dep->version, 1)) 187 return dist_set_err(err, errcap, "bad dependency string field"); 188 } 189 190 return DIST_OK; 191 } 192 193 int dist_package_manifest_emit(const DistPackageManifest* m, KitWriter* out) { 194 size_t i; 195 char err[128]; 196 197 if (dist_package_manifest_validate(m, err, sizeof err) != DIST_OK) 198 return DIST_ERR; 199 200 if (emit(out, DIST_PACKAGE3_MAGIC "\n") != DIST_OK) return DIST_ERR; 201 if (dist_emit_kv(out, "name", m->name) != DIST_OK) return DIST_ERR; 202 if (dist_emit_kv(out, "version", m->version) != DIST_OK) return DIST_ERR; 203 if (m->description[0] && 204 dist_emit_kv(out, "description", m->description) != DIST_OK) 205 return DIST_ERR; 206 if (dist_emit_kv(out, "hash", DIST_PACKAGE3_HASH) != DIST_OK) return DIST_ERR; 207 if (dist_emit_kv(out, "tree", DIST_PACKAGE3_TREE_FORMAT) != DIST_OK) 208 return DIST_ERR; 209 if (dist_emit_kv(out, "blob", DIST_PACKAGE3_BLOB_FORMAT) != DIST_OK) 210 return DIST_ERR; 211 212 for (i = 0; i < m->n_outputs; ++i) { 213 const DistPackageOutput* pkg_out = &m->outputs[i]; 214 if (emit(out, "\n[output]\n") != DIST_OK) return DIST_ERR; 215 if (emit_u64(out, "id", pkg_out->id) != DIST_OK) return DIST_ERR; 216 if (dist_emit_kv(out, "name", pkg_out->name) != DIST_OK) return DIST_ERR; 217 if (emit_hex(out, "tree", pkg_out->tree, DIST_BLAKE2B_LEN) != DIST_OK) 218 return DIST_ERR; 219 if (pkg_out->target[0] && 220 dist_emit_kv(out, "target", pkg_out->target) != DIST_OK) 221 return DIST_ERR; 222 if (pkg_out->is_default && dist_emit_kv(out, "default", "true") != DIST_OK) 223 return DIST_ERR; 224 } 225 226 for (i = 0; i < m->n_artifacts; ++i) { 227 const DistPackageArtifact* art = &m->artifacts[i]; 228 if (emit(out, "\n[artifact]\n") != DIST_OK) return DIST_ERR; 229 if (emit_u64(out, "output", art->output_id) != DIST_OK) return DIST_ERR; 230 if (dist_emit_kv(out, "path", art->path) != DIST_OK) return DIST_ERR; 231 if (dist_emit_kv(out, "kind", art->kind) != DIST_OK) return DIST_ERR; 232 if (art->entry && dist_emit_kv(out, "entry", "true") != DIST_OK) 233 return DIST_ERR; 234 } 235 236 for (i = 0; i < m->n_deps; ++i) { 237 const DistPackageDependency* dep = &m->deps[i]; 238 if (emit(out, "\n[dependency]\n") != DIST_OK) return DIST_ERR; 239 if (dist_emit_kv(out, "name", dep->name) != DIST_OK) return DIST_ERR; 240 if (dist_emit_kv(out, "version", dep->version) != DIST_OK) return DIST_ERR; 241 if (dep->has_package && 242 emit_hex(out, "package", dep->package, DIST_BLAKE2B_LEN) != DIST_OK) 243 return DIST_ERR; 244 if (dep->has_keyid && 245 emit_hex(out, "key", dep->keyid, DIST_KEYID_LEN) != DIST_OK) 246 return DIST_ERR; 247 } 248 249 return DIST_OK; 250 } 251 252 int dist_package_manifest_parse(const uint8_t* data, size_t len, 253 DistPackageManifest* m, char* err, 254 size_t errcap) { 255 size_t pos = 0; 256 int first = 1; 257 PackageSection sec = P3_SEC_TOP; 258 uint32_t seen = 0; 259 DistPackageOutput* pkg_out = NULL; 260 DistPackageArtifact* art = NULL; 261 DistPackageDependency* dep = NULL; 262 263 memset(m, 0, sizeof *m); 264 265 while (pos < len) { 266 char buf[DIST_KV_LINE_MAX]; 267 size_t end = pos; 268 size_t n, i; 269 char *t, *key, *val, *eq; 270 271 while (end < len && data[end] != '\n') ++end; 272 n = end - pos; 273 if (n >= sizeof buf) return dist_set_err(err, errcap, "line too long"); 274 for (i = pos; i < end; ++i) 275 if (data[i] == 0) 276 return dist_set_err(err, errcap, "NUL byte in package manifest"); 277 memcpy(buf, data + pos, n); 278 buf[n] = '\0'; 279 pos = (end < len) ? end + 1 : end; 280 dist_trim_trail(buf); 281 282 if (first) { 283 first = 0; 284 if (strcmp(buf, DIST_PACKAGE3_MAGIC) != 0) 285 return dist_set_err(err, errcap, "bad package manifest magic/version"); 286 continue; 287 } 288 289 t = dist_trim_lead(buf); 290 if (*t == '\0' || *t == '#') continue; 291 292 if (*t == '[') { 293 if (finalize_package_section3(sec, seen, err, errcap) != DIST_OK) 294 return DIST_ERR; 295 seen = 0; 296 if (strcmp(t, "[output]") == 0) { 297 if (m->n_outputs >= DIST_MAX_OUTPUTS) 298 return dist_set_err(err, errcap, "too many outputs"); 299 sec = P3_SEC_OUTPUT; 300 pkg_out = &m->outputs[m->n_outputs++]; 301 } else if (strcmp(t, "[artifact]") == 0) { 302 if (m->n_artifacts >= DIST_MAX_ARTIFACTS) 303 return dist_set_err(err, errcap, "too many artifacts"); 304 sec = P3_SEC_ARTIFACT; 305 art = &m->artifacts[m->n_artifacts++]; 306 } else if (strcmp(t, "[dependency]") == 0) { 307 if (m->n_deps >= DIST_MAX_DEPS) 308 return dist_set_err(err, errcap, "too many dependencies"); 309 sec = P3_SEC_DEPENDENCY; 310 dep = &m->deps[m->n_deps++]; 311 } else { 312 return dist_set_err(err, errcap, "unknown section"); 313 } 314 continue; 315 } 316 317 eq = strchr(t, '='); 318 if (!eq) return dist_set_err(err, errcap, "expected key = value"); 319 *eq = '\0'; 320 key = t; 321 dist_trim_trail(key); 322 val = dist_trim_lead(eq + 1); 323 324 if (sec == P3_SEC_TOP) { 325 if (strcmp(key, "name") == 0) { 326 if (seen_once3(&seen, P3_F_NAME, "duplicate top-level key", err, 327 errcap) != DIST_OK) 328 return DIST_ERR; 329 if (!field_text_valid(val, 1)) 330 return dist_set_err(err, errcap, "bad package name"); 331 if (dist_copy_field(m->name, sizeof m->name, val, err, errcap)) 332 return DIST_ERR; 333 } else if (strcmp(key, "version") == 0) { 334 if (seen_once3(&seen, P3_F_VERSION, "duplicate top-level key", err, 335 errcap) != DIST_OK) 336 return DIST_ERR; 337 if (!field_text_valid(val, 1)) 338 return dist_set_err(err, errcap, "bad package version"); 339 if (dist_copy_field(m->version, sizeof m->version, val, err, errcap)) 340 return DIST_ERR; 341 } else if (strcmp(key, "description") == 0) { 342 if (seen_once3(&seen, P3_F_DESCRIPTION, "duplicate top-level key", err, 343 errcap) != DIST_OK) 344 return DIST_ERR; 345 if (!field_text_valid(val, 0)) 346 return dist_set_err(err, errcap, "bad package description"); 347 if (dist_copy_field(m->description, sizeof m->description, val, err, 348 errcap)) 349 return DIST_ERR; 350 } else if (strcmp(key, "hash") == 0) { 351 if (seen_once3(&seen, P3_F_HASH, "duplicate top-level key", err, 352 errcap) != DIST_OK) 353 return DIST_ERR; 354 if (strcmp(val, DIST_PACKAGE3_HASH) != 0) 355 return dist_set_err(err, errcap, "unsupported hash algorithm"); 356 } else if (strcmp(key, "tree") == 0) { 357 if (seen_once3(&seen, P3_F_TREE_FORMAT, "duplicate top-level key", err, 358 errcap) != DIST_OK) 359 return DIST_ERR; 360 if (strcmp(val, DIST_PACKAGE3_TREE_FORMAT) != 0) 361 return dist_set_err(err, errcap, "unsupported tree format"); 362 } else if (strcmp(key, "blob") == 0) { 363 if (seen_once3(&seen, P3_F_BLOB_FORMAT, "duplicate top-level key", err, 364 errcap) != DIST_OK) 365 return DIST_ERR; 366 if (strcmp(val, DIST_PACKAGE3_BLOB_FORMAT) != 0) 367 return dist_set_err(err, errcap, "unsupported blob format"); 368 } else { 369 return dist_set_err(err, errcap, "unknown top-level key"); 370 } 371 } else if (sec == P3_SEC_OUTPUT) { 372 if (strcmp(key, "id") == 0) { 373 if (seen_once3(&seen, P3_F_ID, "duplicate [output] key", err, errcap) != 374 DIST_OK) 375 return DIST_ERR; 376 if (dist_parse_u64(val, &pkg_out->id) != DIST_OK) 377 return dist_set_err(err, errcap, "bad output id"); 378 } else if (strcmp(key, "name") == 0) { 379 if (seen_once3(&seen, P3_F_OUTPUT_NAME, "duplicate [output] key", err, 380 errcap) != DIST_OK) 381 return DIST_ERR; 382 if (!field_text_valid(val, 1)) 383 return dist_set_err(err, errcap, "bad output name"); 384 if (dist_copy_field(pkg_out->name, sizeof pkg_out->name, val, err, 385 errcap)) 386 return DIST_ERR; 387 } else if (strcmp(key, "tree") == 0) { 388 if (seen_once3(&seen, P3_F_TREE_ID, "duplicate [output] key", err, 389 errcap) != DIST_OK) 390 return DIST_ERR; 391 if (decode_hash3(pkg_out->tree, val, "bad output tree id", err, 392 errcap) != DIST_OK) 393 return DIST_ERR; 394 } else if (strcmp(key, "target") == 0) { 395 if (seen_once3(&seen, P3_F_TARGET, "duplicate [output] key", err, 396 errcap) != DIST_OK) 397 return DIST_ERR; 398 if (!field_text_valid(val, 0)) 399 return dist_set_err(err, errcap, "bad output target"); 400 if (dist_copy_field(pkg_out->target, sizeof pkg_out->target, val, err, 401 errcap)) 402 return DIST_ERR; 403 } else if (strcmp(key, "default") == 0) { 404 if (seen_once3(&seen, P3_F_DEFAULT, "duplicate [output] key", err, 405 errcap) != DIST_OK) 406 return DIST_ERR; 407 if (parse_bool3(val, &pkg_out->is_default) != DIST_OK) 408 return dist_set_err(err, errcap, "bad default value"); 409 } else { 410 return dist_set_err(err, errcap, "unknown [output] key"); 411 } 412 } else if (sec == P3_SEC_ARTIFACT) { 413 if (strcmp(key, "output") == 0) { 414 if (seen_once3(&seen, P3_F_OUTPUT, "duplicate [artifact] key", err, 415 errcap) != DIST_OK) 416 return DIST_ERR; 417 if (dist_parse_u64(val, &art->output_id) != DIST_OK) 418 return dist_set_err(err, errcap, "bad artifact output id"); 419 } else if (strcmp(key, "path") == 0) { 420 if (seen_once3(&seen, P3_F_PATH, "duplicate [artifact] key", err, 421 errcap) != DIST_OK) 422 return DIST_ERR; 423 if (!dist_manifest_path_valid(val)) 424 return dist_set_err(err, errcap, "unsafe artifact path"); 425 if (dist_copy_field(art->path, sizeof art->path, val, err, errcap)) 426 return DIST_ERR; 427 } else if (strcmp(key, "kind") == 0) { 428 if (seen_once3(&seen, P3_F_KIND, "duplicate [artifact] key", err, 429 errcap) != DIST_OK) 430 return DIST_ERR; 431 if (!kind_valid(val)) 432 return dist_set_err(err, errcap, "unknown artifact kind"); 433 if (dist_copy_field(art->kind, sizeof art->kind, val, err, errcap)) 434 return DIST_ERR; 435 } else if (strcmp(key, "entry") == 0) { 436 if (seen_once3(&seen, P3_F_ENTRY, "duplicate [artifact] key", err, 437 errcap) != DIST_OK) 438 return DIST_ERR; 439 if (parse_bool3(val, &art->entry) != DIST_OK) 440 return dist_set_err(err, errcap, "bad entry value"); 441 } else { 442 return dist_set_err(err, errcap, "unknown [artifact] key"); 443 } 444 } else { 445 if (strcmp(key, "name") == 0) { 446 if (seen_once3(&seen, P3_F_NAME, "duplicate [dependency] key", err, 447 errcap) != DIST_OK) 448 return DIST_ERR; 449 if (!field_text_valid(val, 1)) 450 return dist_set_err(err, errcap, "bad dependency name"); 451 if (dist_copy_field(dep->name, sizeof dep->name, val, err, errcap)) 452 return DIST_ERR; 453 } else if (strcmp(key, "version") == 0) { 454 if (seen_once3(&seen, P3_F_VERSION, "duplicate [dependency] key", err, 455 errcap) != DIST_OK) 456 return DIST_ERR; 457 if (!field_text_valid(val, 1)) 458 return dist_set_err(err, errcap, "bad dependency version"); 459 if (dist_copy_field(dep->version, sizeof dep->version, val, err, 460 errcap)) 461 return DIST_ERR; 462 } else if (strcmp(key, "package") == 0) { 463 if (seen_once3(&seen, P3_F_PACKAGE, "duplicate [dependency] key", err, 464 errcap) != DIST_OK) 465 return DIST_ERR; 466 if (decode_hash3(dep->package, val, "bad dependency package id", err, 467 errcap) != DIST_OK) 468 return DIST_ERR; 469 dep->has_package = 1; 470 } else if (strcmp(key, "key") == 0) { 471 if (seen_once3(&seen, P3_F_KEY, "duplicate [dependency] key", err, 472 errcap) != DIST_OK) 473 return DIST_ERR; 474 if (decode_keyid3(dep->keyid, val, err, errcap) != DIST_OK) 475 return DIST_ERR; 476 dep->has_keyid = 1; 477 } else { 478 return dist_set_err(err, errcap, "unknown [dependency] key"); 479 } 480 } 481 } 482 483 if (first) 484 return dist_set_err(err, errcap, "bad package manifest magic/version"); 485 if (finalize_package_section3(sec, seen, err, errcap) != DIST_OK) 486 return DIST_ERR; 487 return dist_package_manifest_validate(m, err, errcap); 488 }