1 /* $Id: json.c,v 1.21 2020/09/14 16:00:17 florian Exp $ */ 2 /* 3 * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <assert.h> 19 #include <err.h> 20 #include <stdarg.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 26 #include "jsmn.h" 27 #include "extern.h" 28 29 struct jsmnp; 30 31 /* 32 * A node in the JSMN parse tree. 33 * Each of this corresponds to an object in the original JSMN token 34 * list, although the contents have been extracted properly. 35 */ 36 struct jsmnn { 37 struct parse *p; /* parser object */ 38 union { 39 char *str; /* JSMN_PRIMITIVE, JSMN_STRING */ 40 struct jsmnp *obj; /* JSMN_OBJECT */ 41 struct jsmnn **array; /* JSMN_ARRAY */ 42 } d; 43 size_t fields; /* entries in "d" */ 44 jsmntype_t type; /* type of node */ 45 }; 46 47 /* 48 * Objects consist of node pairs: the left-hand side (before the colon) 49 * and the right-hand side---the data. 50 */ 51 struct jsmnp { 52 struct jsmnn *lhs; /* left of colon */ 53 struct jsmnn *rhs; /* right of colon */ 54 }; 55 56 /* 57 * Object for converting the JSMN token array into a tree. 58 */ 59 struct parse { 60 struct jsmnn *nodes; /* all nodes */ 61 size_t cur; /* current number */ 62 size_t max; /* nodes in "nodes" */ 63 }; 64 65 /* 66 * Recursive part for convertin a JSMN token array into a tree. 67 * See "example/jsondump.c" for its construction (it's the same except 68 * for how it handles allocation errors). 69 */ 70 static ssize_t 71 build(struct parse *parse, struct jsmnn **np, 72 jsmntok_t *t, const char *js, size_t sz) 73 { 74 size_t i, j; 75 struct jsmnn *n; 76 ssize_t tmp; 77 78 if (sz == 0) 79 return 0; 80 81 assert(parse->cur < parse->max); 82 n = *np = &parse->nodes[parse->cur++]; 83 n->p = parse; 84 n->type = t->type; 85 86 switch (t->type) { 87 case JSMN_STRING: 88 /* FALLTHROUGH */ 89 case JSMN_PRIMITIVE: 90 n->fields = 1; 91 n->d.str = strndup 92 (js + t->start, 93 t->end - t->start); 94 if (n->d.str == NULL) 95 break; 96 return 1; 97 case JSMN_OBJECT: 98 n->fields = t->size; 99 n->d.obj = calloc(n->fields, 100 sizeof(struct jsmnp)); 101 if (n->d.obj == NULL) 102 break; 103 for (i = j = 0; i < (size_t)t->size; i++) { 104 tmp = build(parse, 105 &n->d.obj[i].lhs, 106 t + 1 + j, js, sz - j); 107 if (tmp < 0) 108 break; 109 j += tmp; 110 tmp = build(parse, 111 &n->d.obj[i].rhs, 112 t + 1 + j, js, sz - j); 113 if (tmp < 0) 114 break; 115 j += tmp; 116 } 117 if (i < (size_t)t->size) 118 break; 119 return j + 1; 120 case JSMN_ARRAY: 121 n->fields = t->size; 122 n->d.array = calloc(n->fields, 123 sizeof(struct jsmnn *)); 124 if (n->d.array == NULL) 125 break; 126 for (i = j = 0; i < (size_t)t->size; i++) { 127 tmp = build(parse, 128 &n->d.array[i], 129 t + 1 + j, js, sz - j); 130 if (tmp < 0) 131 break; 132 j += tmp; 133 } 134 if (i < (size_t)t->size) 135 break; 136 return j + 1; 137 default: 138 break; 139 } 140 141 return -1; 142 } 143 144 /* 145 * Fully free up a parse sequence. 146 * This handles all nodes sequentially, not recursively. 147 */ 148 static void 149 jsmnparse_free(struct parse *p) 150 { 151 size_t i; 152 153 if (p == NULL) 154 return; 155 for (i = 0; i < p->max; i++) { 156 struct jsmnn *n = &p->nodes[i]; 157 switch (n->type) { 158 case JSMN_ARRAY: 159 free(n->d.array); 160 break; 161 case JSMN_OBJECT: 162 free(n->d.obj); 163 break; 164 case JSMN_PRIMITIVE: 165 free(n->d.str); 166 break; 167 case JSMN_STRING: 168 free(n->d.str); 169 break; 170 case JSMN_UNDEFINED: 171 break; 172 } 173 } 174 free(p->nodes); 175 free(p); 176 } 177 178 /* 179 * Allocate a tree representation of "t". 180 * This returns NULL on allocation failure or when sz is zero, in which 181 * case all resources allocated along the way are freed already. 182 */ 183 static struct jsmnn * 184 jsmntree_alloc(jsmntok_t *t, const char *js, size_t sz) 185 { 186 struct jsmnn *first; 187 struct parse *p; 188 189 if (sz == 0) 190 return NULL; 191 192 p = calloc(1, sizeof(struct parse)); 193 if (p == NULL) 194 return NULL; 195 196 p->max = sz; 197 p->nodes = calloc(p->max, sizeof(struct jsmnn)); 198 if (p->nodes == NULL) { 199 free(p); 200 return NULL; 201 } 202 203 if (build(p, &first, t, js, sz) < 0) { 204 jsmnparse_free(p); 205 first = NULL; 206 } 207 208 return first; 209 } 210 211 /* 212 * Call through to free parse contents. 213 */ 214 void 215 json_free(struct jsmnn *first) 216 { 217 218 if (first != NULL) 219 jsmnparse_free(first->p); 220 } 221 222 /* 223 * Just check that the array object is in fact an object. 224 */ 225 static struct jsmnn * 226 json_getarrayobj(struct jsmnn *n) 227 { 228 229 return n->type != JSMN_OBJECT ? NULL : n; 230 } 231 232 /* 233 * Get a string element from an array 234 */ 235 static char * 236 json_getarraystr(struct jsmnn *n) 237 { 238 return n->type != JSMN_STRING ? NULL : n->d.str; 239 } 240 241 /* 242 * Extract an array from the returned JSON object, making sure that it's 243 * the correct type. 244 * Returns NULL on failure. 245 */ 246 static struct jsmnn * 247 json_getarray(struct jsmnn *n, const char *name) 248 { 249 size_t i; 250 251 if (n->type != JSMN_OBJECT) 252 return NULL; 253 for (i = 0; i < n->fields; i++) { 254 if (n->d.obj[i].lhs->type != JSMN_STRING && 255 n->d.obj[i].lhs->type != JSMN_PRIMITIVE) 256 continue; 257 else if (strcmp(name, n->d.obj[i].lhs->d.str)) 258 continue; 259 break; 260 } 261 if (i == n->fields) 262 return NULL; 263 if (n->d.obj[i].rhs->type != JSMN_ARRAY) 264 return NULL; 265 return n->d.obj[i].rhs; 266 } 267 268 /* 269 * Extract subtree from the returned JSON object, making sure that it's 270 * the correct type. 271 * Returns NULL on failure. 272 */ 273 static struct jsmnn * 274 json_getobj(struct jsmnn *n, const char *name) 275 { 276 size_t i; 277 278 if (n->type != JSMN_OBJECT) 279 return NULL; 280 for (i = 0; i < n->fields; i++) { 281 if (n->d.obj[i].lhs->type != JSMN_STRING && 282 n->d.obj[i].lhs->type != JSMN_PRIMITIVE) 283 continue; 284 else if (strcmp(name, n->d.obj[i].lhs->d.str)) 285 continue; 286 break; 287 } 288 if (i == n->fields) 289 return NULL; 290 if (n->d.obj[i].rhs->type != JSMN_OBJECT) 291 return NULL; 292 return n->d.obj[i].rhs; 293 } 294 295 /* 296 * Extract a single string from the returned JSON object, making sure 297 * that it's the correct type. 298 * Returns NULL on failure. 299 */ 300 char * 301 json_getstr(struct jsmnn *n, const char *name) 302 { 303 size_t i; 304 char *cp; 305 306 if (n->type != JSMN_OBJECT) 307 return NULL; 308 for (i = 0; i < n->fields; i++) { 309 if (n->d.obj[i].lhs->type != JSMN_STRING && 310 n->d.obj[i].lhs->type != JSMN_PRIMITIVE) 311 continue; 312 else if (strcmp(name, n->d.obj[i].lhs->d.str)) 313 continue; 314 break; 315 } 316 if (i == n->fields) 317 return NULL; 318 if (n->d.obj[i].rhs->type != JSMN_STRING && 319 n->d.obj[i].rhs->type != JSMN_PRIMITIVE) 320 return NULL; 321 322 cp = strdup(n->d.obj[i].rhs->d.str); 323 if (cp == NULL) 324 warn("strdup"); 325 return cp; 326 } 327 328 /* 329 * Completely free the challenge response body. 330 */ 331 void 332 json_free_challenge(struct chng *p) 333 { 334 335 free(p->uri); 336 free(p->token); 337 p->uri = p->token = NULL; 338 } 339 340 /* 341 * Parse the response from the ACME server when we're waiting to see 342 * whether the challenge has been ok. 343 */ 344 enum chngstatus 345 json_parse_response(struct jsmnn *n) 346 { 347 char *resp; 348 enum chngstatus rc; 349 350 if (n == NULL) 351 return CHNG_INVALID; 352 if ((resp = json_getstr(n, "status")) == NULL) 353 return CHNG_INVALID; 354 355 if (strcmp(resp, "valid") == 0) 356 rc = CHNG_VALID; 357 else if (strcmp(resp, "pending") == 0) 358 rc = CHNG_PENDING; 359 else if (strcmp(resp, "processing") == 0) 360 rc = CHNG_PROCESSING; 361 else 362 rc = CHNG_INVALID; 363 364 free(resp); 365 return rc; 366 } 367 368 /* 369 * Parse the response from a new-authz, which consists of challenge 370 * information, into a structure. 371 * We only care about the HTTP-01 response. 372 */ 373 int 374 json_parse_challenge(struct jsmnn *n, struct chng *p) 375 { 376 struct jsmnn *array, *obj, *error; 377 size_t i; 378 int rc; 379 char *type; 380 381 if (n == NULL) 382 return 0; 383 384 array = json_getarray(n, "challenges"); 385 if (array == NULL) 386 return 0; 387 388 for (i = 0; i < array->fields; i++) { 389 obj = json_getarrayobj(array->d.array[i]); 390 if (obj == NULL) 391 continue; 392 type = json_getstr(obj, "type"); 393 if (type == NULL) 394 continue; 395 rc = strcmp(type, "http-01"); 396 free(type); 397 if (rc) 398 continue; 399 p->uri = json_getstr(obj, "url"); 400 p->token = json_getstr(obj, "token"); 401 p->status = json_parse_response(obj); 402 if (p->status == CHNG_INVALID) { 403 error = json_getobj(obj, "error"); 404 p->error = json_getstr(error, "detail"); 405 } 406 return p->uri != NULL && p->token != NULL; 407 } 408 409 return 0; 410 } 411 412 static enum orderstatus 413 json_parse_order_status(struct jsmnn *n) 414 { 415 char *status; 416 417 if (n == NULL) 418 return ORDER_INVALID; 419 420 if ((status = json_getstr(n, "status")) == NULL) 421 return ORDER_INVALID; 422 423 if (strcmp(status, "pending") == 0) 424 return ORDER_PENDING; 425 else if (strcmp(status, "ready") == 0) 426 return ORDER_READY; 427 else if (strcmp(status, "processing") == 0) 428 return ORDER_PROCESSING; 429 else if (strcmp(status, "valid") == 0) 430 return ORDER_VALID; 431 else if (strcmp(status, "invalid") == 0) 432 return ORDER_INVALID; 433 else 434 return ORDER_INVALID; 435 } 436 437 /* 438 * Parse the response from a newOrder, which consists of a status 439 * a list of authorization urls and a finalize url into a struct. 440 */ 441 int 442 json_parse_order(struct jsmnn *n, struct order *order) 443 { 444 struct jsmnn *array; 445 size_t i; 446 char *finalize, *str; 447 448 order->status = json_parse_order_status(n); 449 450 if (n == NULL) 451 return 0; 452 453 if ((finalize = json_getstr(n, "finalize")) == NULL) { 454 warnx("no finalize field in order response"); 455 return 0; 456 } 457 458 if ((order->finalize = strdup(finalize)) == NULL) 459 goto err; 460 461 if ((array = json_getarray(n, "authorizations")) == NULL) 462 goto err; 463 464 if (array->fields > 0) { 465 order->auths = calloc(array->fields, sizeof(*order->auths)); 466 if (order->auths == NULL) { 467 warn("malloc"); 468 goto err; 469 } 470 order->authsz = array->fields; 471 } 472 473 for (i = 0; i < array->fields; i++) { 474 str = json_getarraystr(array->d.array[i]); 475 if (str == NULL) 476 continue; 477 if ((order->auths[i] = strdup(str)) == NULL) { 478 warn("strdup"); 479 goto err; 480 } 481 } 482 return 1; 483 err: 484 json_free_order(order); 485 return 0; 486 } 487 488 int 489 json_parse_upd_order(struct jsmnn *n, struct order *order) 490 { 491 char *certificate; 492 order->status = json_parse_order_status(n); 493 if ((certificate = json_getstr(n, "certificate")) != NULL) { 494 if ((order->certificate = strdup(certificate)) == NULL) 495 return 0; 496 } 497 return 1; 498 } 499 500 void 501 json_free_order(struct order *order) 502 { 503 size_t i; 504 505 free(order->finalize); 506 order->finalize = NULL; 507 for(i = 0; i < order->authsz; i++) 508 free(order->auths[i]); 509 free(order->auths); 510 511 order->finalize = NULL; 512 order->auths = NULL; 513 order->authsz = 0; 514 } 515 516 /* 517 * Extract the CA paths from the JSON response object. 518 * Return zero on failure, non-zero on success. 519 */ 520 int 521 json_parse_capaths(struct jsmnn *n, struct capaths *p) 522 { 523 if (n == NULL) 524 return 0; 525 526 p->newaccount = json_getstr(n, "newAccount"); 527 p->newnonce = json_getstr(n, "newNonce"); 528 p->neworder = json_getstr(n, "newOrder"); 529 p->revokecert = json_getstr(n, "revokeCert"); 530 531 return p->newaccount != NULL && p->newnonce != NULL && 532 p->neworder != NULL && p->revokecert != NULL; 533 } 534 535 /* 536 * Free up all of our CA-noted paths (which may all be NULL). 537 */ 538 void 539 json_free_capaths(struct capaths *p) 540 { 541 542 free(p->newaccount); 543 free(p->newnonce); 544 free(p->neworder); 545 free(p->revokecert); 546 memset(p, 0, sizeof(struct capaths)); 547 } 548 549 /* 550 * Parse an HTTP response body from a buffer of size "sz". 551 * Returns an opaque pointer on success, otherwise NULL on error. 552 */ 553 struct jsmnn * 554 json_parse(const char *buf, size_t sz) 555 { 556 struct jsmnn *n; 557 jsmn_parser p; 558 jsmntok_t *tok, *ntok; 559 int r; 560 size_t tokcount; 561 562 jsmn_init(&p); 563 tokcount = 128; 564 565 if ((tok = calloc(tokcount, sizeof(jsmntok_t))) == NULL) { 566 warn("calloc"); 567 return NULL; 568 } 569 570 /* Do this until we don't need any more tokens. */ 571 again: 572 /* Actually try to parse the JSON into the tokens. */ 573 r = jsmn_parse(&p, buf, sz, tok, tokcount); 574 if (r < 0 && r == JSMN_ERROR_NOMEM) { 575 if ((ntok = recallocarray(tok, tokcount, tokcount * 2, 576 sizeof(jsmntok_t))) == NULL) { 577 warn("calloc"); 578 free(tok); 579 return NULL; 580 } 581 tok = ntok; 582 tokcount *= 2; 583 goto again; 584 } else if (r < 0) { 585 warnx("jsmn_parse: %d", r); 586 free(tok); 587 return NULL; 588 } 589 590 /* Now parse the tokens into a tree. */ 591 592 n = jsmntree_alloc(tok, buf, r); 593 free(tok); 594 return n; 595 } 596 597 /* 598 * Format the "newAccount" resource request to check if the account exist. 599 */ 600 char * 601 json_fmt_chkacc(void) 602 { 603 int c; 604 char *p; 605 606 c = asprintf(&p, "{" 607 "\"termsOfServiceAgreed\": true, " 608 "\"onlyReturnExisting\": true" 609 "}"); 610 if (c == -1) { 611 warn("asprintf"); 612 p = NULL; 613 } 614 return p; 615 } 616 617 /* 618 * Format the "newAccount" resource request. 619 */ 620 char * 621 json_fmt_newacc(const char *contact) 622 { 623 int c; 624 char *p, *cnt = NULL; 625 626 if (contact != NULL) { 627 c = asprintf(&cnt, "\"contact\": [ \"%s\" ], ", contact); 628 if (c == -1) { 629 warn("asprintf"); 630 return NULL; 631 } 632 } 633 634 c = asprintf(&p, "{" 635 "%s" 636 "\"termsOfServiceAgreed\": true" 637 "}", cnt == NULL ? "" : cnt); 638 free(cnt); 639 if (c == -1) { 640 warn("asprintf"); 641 p = NULL; 642 } 643 return p; 644 } 645 646 /* 647 * Format the "newOrder" resource request 648 */ 649 char * 650 json_fmt_neworder(const char *const *alts, size_t altsz) 651 { 652 size_t i; 653 int c; 654 char *p, *t; 655 656 if ((p = strdup("{ \"identifiers\": [")) == NULL) 657 goto err; 658 659 t = p; 660 for (i = 0; i < altsz; i++) { 661 c = asprintf(&p, 662 "%s { \"type\": \"dns\", \"value\": \"%s\" }%s", 663 t, alts[i], i + 1 == altsz ? "" : ","); 664 free(t); 665 if (c == -1) { 666 warn("asprintf"); 667 p = NULL; 668 goto err; 669 } 670 t = p; 671 } 672 c = asprintf(&p, "%s ] }", t); 673 free(t); 674 if (c == -1) { 675 warn("asprintf"); 676 p = NULL; 677 } 678 return p; 679 err: 680 free(p); 681 return NULL; 682 } 683 684 /* 685 * Format the revoke resource request. 686 */ 687 char * 688 json_fmt_revokecert(const char *cert) 689 { 690 int c; 691 char *p; 692 693 c = asprintf(&p, "{" 694 "\"certificate\": \"%s\"" 695 "}", 696 cert); 697 if (c == -1) { 698 warn("asprintf"); 699 p = NULL; 700 } 701 return p; 702 } 703 704 /* 705 * Format the "new-cert" resource request. 706 */ 707 char * 708 json_fmt_newcert(const char *cert) 709 { 710 int c; 711 char *p; 712 713 c = asprintf(&p, "{" 714 "\"csr\": \"%s\"" 715 "}", 716 cert); 717 if (c == -1) { 718 warn("asprintf"); 719 p = NULL; 720 } 721 return p; 722 } 723 724 /* 725 * Protected component of json_fmt_signed(). 726 */ 727 char * 728 json_fmt_protected_rsa(const char *exp, const char *mod, const char *nce, 729 const char *url) 730 { 731 int c; 732 char *p; 733 734 c = asprintf(&p, "{" 735 "\"alg\": \"RS256\", " 736 "\"jwk\": " 737 "{\"e\": \"%s\", \"kty\": \"RSA\", \"n\": \"%s\"}, " 738 "\"nonce\": \"%s\", " 739 "\"url\": \"%s\"" 740 "}", 741 exp, mod, nce, url); 742 if (c == -1) { 743 warn("asprintf"); 744 p = NULL; 745 } 746 return p; 747 } 748 749 /* 750 * Protected component of json_fmt_signed(). 751 */ 752 char * 753 json_fmt_protected_ec(const char *x, const char *y, const char *nce, 754 const char *url) 755 { 756 int c; 757 char *p; 758 759 c = asprintf(&p, "{" 760 "\"alg\": \"ES384\", " 761 "\"jwk\": " 762 "{\"crv\": \"P-384\", \"kty\": \"EC\", \"x\": \"%s\", " 763 "\"y\": \"%s\"}, \"nonce\": \"%s\", \"url\": \"%s\"" 764 "}", 765 x, y, nce, url); 766 if (c == -1) { 767 warn("asprintf"); 768 p = NULL; 769 } 770 return p; 771 } 772 773 /* 774 * Protected component of json_fmt_signed(). 775 */ 776 char * 777 json_fmt_protected_kid(const char *alg, const char *kid, const char *nce, 778 const char *url) 779 { 780 int c; 781 char *p; 782 783 c = asprintf(&p, "{" 784 "\"alg\": \"%s\", " 785 "\"kid\": \"%s\", " 786 "\"nonce\": \"%s\", " 787 "\"url\": \"%s\"" 788 "}", 789 alg, kid, nce, url); 790 if (c == -1) { 791 warn("asprintf"); 792 p = NULL; 793 } 794 return p; 795 } 796 797 /* 798 * Signed message contents for the CA server. 799 */ 800 char * 801 json_fmt_signed(const char *protected, const char *payload, const char *digest) 802 { 803 int c; 804 char *p; 805 806 c = asprintf(&p, "{" 807 "\"protected\": \"%s\", " 808 "\"payload\": \"%s\", " 809 "\"signature\": \"%s\"" 810 "}", 811 protected, payload, digest); 812 if (c == -1) { 813 warn("asprintf"); 814 p = NULL; 815 } 816 return p; 817 } 818 819 /* 820 * Produce thumbprint input. 821 * This isn't technically a JSON string--it's the input we'll use for 822 * hashing and digesting. 823 * However, it's in the form of a JSON string, so do it here. 824 */ 825 char * 826 json_fmt_thumb_rsa(const char *exp, const char *mod) 827 { 828 int c; 829 char *p; 830 831 /*NOTE: WHITESPACE IS IMPORTANT. */ 832 833 c = asprintf(&p, "{\"e\":\"%s\",\"kty\":\"RSA\",\"n\":\"%s\"}", 834 exp, mod); 835 if (c == -1) { 836 warn("asprintf"); 837 p = NULL; 838 } 839 return p; 840 } 841 842 /* 843 * Produce thumbprint input. 844 * This isn't technically a JSON string--it's the input we'll use for 845 * hashing and digesting. 846 * However, it's in the form of a JSON string, so do it here. 847 */ 848 char * 849 json_fmt_thumb_ec(const char *x, const char *y) 850 { 851 int c; 852 char *p; 853 854 /*NOTE: WHITESPACE IS IMPORTANT. */ 855 856 c = asprintf(&p, "{\"crv\":\"P-384\",\"kty\":\"EC\",\"x\":\"%s\"," 857 "\"y\":\"%s\"}", 858 x, y); 859 if (c == -1) { 860 warn("asprintf"); 861 p = NULL; 862 } 863 return p; 864 } 865