1 /* $Id: json.c,v 1.2 2016/08/31 23:19:50 benno 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 #ifdef HAVE_CONFIG_H 18 # include "config.h" 19 #endif 20 21 #include <assert.h> 22 #include <err.h> 23 #include <stdarg.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <unistd.h> 28 29 #include "jsmn.h" 30 #include "extern.h" 31 32 struct jsmnp; 33 34 /* 35 * A node in the JSMN parse tree. 36 * Each of this corresponds to an object in the original JSMN token 37 * list, although the contents have been extracted properly. 38 */ 39 struct jsmnn { 40 struct parse *p; /* parser object */ 41 union { 42 char *str; /* JSMN_PRIMITIVE, JSMN_STRING */ 43 struct jsmnp *obj; /* JSMN_OBJECT */ 44 struct jsmnn **array; /* JSMN_ARRAY */ 45 } d; 46 size_t fields; /* entries in "d" */ 47 jsmntype_t type; /* type of node */ 48 }; 49 50 /* 51 * Objects consist of node pairs: the left-hand side (before the colon) 52 * and the right-hand side---the data. 53 */ 54 struct jsmnp { 55 struct jsmnn *lhs; /* left of colon */ 56 struct jsmnn *rhs; /* right of colon */ 57 }; 58 59 /* 60 * Object for converting the JSMN token array into a tree. 61 */ 62 struct parse { 63 struct jsmnn *nodes; /* all nodes */ 64 size_t cur; /* current number */ 65 size_t max; /* nodes in "nodes" */ 66 }; 67 68 /* 69 * Recursive part for convertin a JSMN token array into a tree. 70 * See "example/jsondump.c" for its construction (it's the same except 71 * for how it handles allocation errors). 72 */ 73 static ssize_t 74 build(struct parse *parse, struct jsmnn **np, 75 jsmntok_t *t, const char *js, size_t sz) 76 { 77 size_t i, j; 78 struct jsmnn *n; 79 ssize_t tmp; 80 81 if (0 == sz) 82 return (0); 83 84 assert(parse->cur < parse->max); 85 n = *np = &parse->nodes[parse->cur++]; 86 n->p = parse; 87 n->type = t->type; 88 89 switch (t->type) { 90 case (JSMN_STRING): 91 /* FALLTHROUGH */ 92 case (JSMN_PRIMITIVE): 93 n->fields = 1; 94 n->d.str = strndup 95 (js + t->start, 96 t->end - t->start); 97 if (NULL == n->d.str) 98 break; 99 return (1); 100 case (JSMN_OBJECT): 101 n->fields = t->size; 102 n->d.obj = calloc(n->fields, 103 sizeof(struct jsmnp)); 104 if (NULL == n->d.obj) 105 break; 106 for (i = j = 0; i < (size_t)t->size; i++) { 107 tmp = build(parse, 108 &n->d.obj[i].lhs, 109 t + 1 + j, js, sz - j); 110 if (tmp < 0) 111 break; 112 j += tmp; 113 tmp = build(parse, 114 &n->d.obj[i].rhs, 115 t + 1 + j, js, sz - j); 116 if (tmp < 0) 117 break; 118 j += tmp; 119 } 120 if (i < (size_t)t->size) 121 break; 122 return (j + 1); 123 case (JSMN_ARRAY): 124 n->fields = t->size; 125 n->d.array = calloc(n->fields, 126 sizeof(struct jsmnn *)); 127 if (NULL == n->d.array) 128 break; 129 for (i = j = 0; i < (size_t)t->size; i++) { 130 tmp = build(parse, 131 &n->d.array[i], 132 t + 1 + j, js, sz - j); 133 if (tmp < 0) 134 break; 135 j += tmp; 136 } 137 if (i < (size_t)t->size) 138 break; 139 return (j + 1); 140 default: 141 break; 142 } 143 144 return (-1); 145 } 146 147 /* 148 * Fully free up a parse sequence. 149 * This handles all nodes sequentially, not recursively. 150 */ 151 static void 152 jsmnparse_free(struct parse *p) 153 { 154 size_t i; 155 156 if (NULL == p) 157 return; 158 for (i = 0; i < p->max; i++) 159 if (JSMN_ARRAY == p->nodes[i].type) 160 free(p->nodes[i].d.array); 161 else if (JSMN_OBJECT == p->nodes[i].type) 162 free(p->nodes[i].d.obj); 163 else if (JSMN_PRIMITIVE == p->nodes[i].type) 164 free(p->nodes[i].d.str); 165 else if (JSMN_STRING == p->nodes[i].type) 166 free(p->nodes[i].d.str); 167 free(p->nodes); 168 free(p); 169 } 170 171 /* 172 * Allocate a tree representation of "t". 173 * This returns NULL on allocation failure or when sz is zero, in which 174 * case all resources allocated along the way are freed already. 175 */ 176 static struct jsmnn * 177 jsmntree_alloc(jsmntok_t *t, const char *js, size_t sz) 178 { 179 struct jsmnn *first; 180 struct parse *p; 181 182 if (0 == sz) 183 return (NULL); 184 185 p = calloc(1, sizeof(struct parse)); 186 if (NULL == p) 187 return (NULL); 188 189 p->max = sz; 190 p->nodes = calloc(p->max, sizeof(struct jsmnn)); 191 if (NULL == p->nodes) { 192 free(p); 193 return (NULL); 194 } 195 196 if (build(p, &first, t, js, sz) < 0) { 197 jsmnparse_free(p); 198 first = NULL; 199 } 200 201 return (first); 202 } 203 204 /* 205 * Call through to free parse contents. 206 */ 207 void 208 json_free(struct jsmnn *first) 209 { 210 211 if (NULL != first) 212 jsmnparse_free(first->p); 213 } 214 215 /* 216 * Just check that the array object is in fact an object. 217 */ 218 static struct jsmnn * 219 json_getarrayobj(struct jsmnn *n) 220 { 221 222 return (JSMN_OBJECT != n->type ? NULL : n); 223 } 224 225 /* 226 * Extract an array from the returned JSON object, making sure that it's 227 * the correct type. 228 * Returns NULL on failure. 229 */ 230 static struct jsmnn * 231 json_getarray(struct jsmnn *n, const char *name) 232 { 233 size_t i; 234 235 if (JSMN_OBJECT != n->type) 236 return (NULL); 237 for (i = 0; i < n->fields; i++) { 238 if (JSMN_STRING != n->d.obj[i].lhs->type && 239 JSMN_PRIMITIVE != n->d.obj[i].lhs->type) 240 continue; 241 else if (strcmp(name, n->d.obj[i].lhs->d.str)) 242 continue; 243 break; 244 } 245 if (i == n->fields) 246 return (NULL); 247 if (JSMN_ARRAY != n->d.obj[i].rhs->type) 248 return (NULL); 249 return (n->d.obj[i].rhs); 250 } 251 252 /* 253 * Extract a single string from the returned JSON object, making sure 254 * that it's the correct type. 255 * Returns NULL on failure. 256 */ 257 static char * 258 json_getstr(struct jsmnn *n, const char *name) 259 { 260 size_t i; 261 char *cp; 262 263 if (JSMN_OBJECT != n->type) 264 return (NULL); 265 for (i = 0; i < n->fields; i++) { 266 if (JSMN_STRING != n->d.obj[i].lhs->type && 267 JSMN_PRIMITIVE != n->d.obj[i].lhs->type) 268 continue; 269 else if (strcmp(name, n->d.obj[i].lhs->d.str)) 270 continue; 271 break; 272 } 273 if (i == n->fields) 274 return (NULL); 275 if (JSMN_STRING != n->d.obj[i].rhs->type && 276 JSMN_PRIMITIVE != n->d.obj[i].rhs->type) 277 return (NULL); 278 279 cp = strdup(n->d.obj[i].rhs->d.str); 280 if (NULL == cp) 281 warn("strdup"); 282 return (cp); 283 } 284 285 /* 286 * Completely free the challenge response body. 287 */ 288 void 289 json_free_challenge(struct chng *p) 290 { 291 292 free(p->uri); 293 free(p->token); 294 p->uri = p->token = NULL; 295 } 296 297 /* 298 * Parse the response from the ACME server when we're waiting to see 299 * whether the challenge has been ok. 300 */ 301 int 302 json_parse_response(struct jsmnn *n) 303 { 304 char *resp; 305 int rc; 306 307 if (NULL == n) 308 return (-1); 309 if (NULL == (resp = json_getstr(n, "status"))) 310 return (-1); 311 312 if (0 == strcmp(resp, "valid")) 313 rc = 1; 314 else if (0 == strcmp(resp, "pending")) 315 rc = 0; 316 else 317 rc = -1; 318 319 free(resp); 320 return (rc); 321 } 322 323 /* 324 * Parse the response from a new-authz, which consists of challenge 325 * information, into a structure. 326 * We only care about the HTTP-01 response. 327 */ 328 int 329 json_parse_challenge(struct jsmnn *n, struct chng *p) 330 { 331 struct jsmnn *array, *obj; 332 size_t i; 333 int rc; 334 char *type; 335 336 if (NULL == n) 337 return (0); 338 339 array = json_getarray(n, "challenges"); 340 if (NULL == array) 341 return (0); 342 343 for (i = 0; i < array->fields; i++) { 344 obj = json_getarrayobj(array->d.array[i]); 345 if (NULL == obj) 346 continue; 347 type = json_getstr(obj, "type"); 348 if (NULL == type) 349 continue; 350 rc = strcmp(type, "http-01"); 351 free(type); 352 if (rc) 353 continue; 354 p->uri = json_getstr(obj, "uri"); 355 p->token = json_getstr(obj, "token"); 356 return (NULL != p->uri && 357 NULL != p->token); 358 } 359 360 return (0); 361 } 362 363 /* 364 * Extract the CA paths from the JSON response object. 365 * Return zero on failure, non-zero on success. 366 */ 367 int 368 json_parse_capaths(struct jsmnn *n, struct capaths *p) 369 { 370 371 if (NULL == n) 372 return (0); 373 374 p->newauthz = json_getstr(n, "new-authz"); 375 p->newcert = json_getstr(n, "new-cert"); 376 p->newreg = json_getstr(n, "new-reg"); 377 p->revokecert = json_getstr(n, "revoke-cert"); 378 379 return (NULL != p->newauthz && 380 NULL != p->newcert && 381 NULL != p->newreg && 382 NULL != p->revokecert); 383 } 384 385 /* 386 * Free up all of our CA-noted paths (which may all be NULL). 387 */ 388 void 389 json_free_capaths(struct capaths *p) 390 { 391 392 free(p->newauthz); 393 free(p->newcert); 394 free(p->newreg); 395 free(p->revokecert); 396 memset(p, 0, sizeof(struct capaths)); 397 } 398 399 /* 400 * Parse an HTTP response body from a buffer of size "sz". 401 * Returns an opaque pointer on success, otherwise NULL on error. 402 */ 403 struct jsmnn * 404 json_parse(const char *buf, size_t sz) 405 { 406 struct jsmnn *n; 407 jsmn_parser p; 408 jsmntok_t *tok; 409 int r; 410 size_t tokcount; 411 412 jsmn_init(&p); 413 tokcount = 128; 414 415 /* Do this until we don't need any more tokens. */ 416 again: 417 tok = calloc(tokcount, sizeof(jsmntok_t)); 418 if (NULL == tok) { 419 warn("calloc"); 420 return (NULL); 421 } 422 423 /* Actually try to parse the JSON into the tokens. */ 424 425 r = jsmn_parse(&p, buf, sz, tok, tokcount); 426 if (r < 0 && JSMN_ERROR_NOMEM == r) { 427 tokcount *= 2; 428 free(tok); 429 goto again; 430 } else if (r < 0) { 431 warnx("jsmn_parse: %d", r); 432 free(tok); 433 return (NULL); 434 } 435 436 /* Now parse the tokens into a tree. */ 437 438 n = jsmntree_alloc(tok, buf, r); 439 free(tok); 440 return (n); 441 } 442 443 /* 444 * Format the "new-reg" resource request. 445 */ 446 char * 447 json_fmt_newreg(const char *license) 448 { 449 int c; 450 char *p; 451 452 c = asprintf(&p, "{" 453 "\"resource\": \"new-reg\", " 454 "\"agreement\": \"%s\"" 455 "}", license); 456 if (-1 == c) { 457 warn("asprintf"); 458 p = NULL; 459 } 460 return (p); 461 } 462 463 /* 464 * Format the "new-authz" resource request. 465 */ 466 char * 467 json_fmt_newauthz(const char *domain) 468 { 469 int c; 470 char *p; 471 472 c = asprintf(&p, "{" 473 "\"resource\": \"new-authz\", " 474 "\"identifier\": " 475 "{\"type\": \"dns\", \"value\": \"%s\"}" 476 "}", domain); 477 if (-1 == c) { 478 warn("asprintf"); 479 p = NULL; 480 } 481 return (p); 482 } 483 484 /* 485 * Format the "challenge" resource request. 486 */ 487 char * 488 json_fmt_challenge(const char *token, const char *thumb) 489 { 490 int c; 491 char *p; 492 493 c = asprintf(&p, "{" 494 "\"resource\": \"challenge\", " 495 "\"keyAuthorization\": \"%s.%s\"" 496 "}", token, thumb); 497 if (-1 == c) { 498 warn("asprintf"); 499 p = NULL; 500 } 501 return (p); 502 } 503 504 /* 505 * Format the "new-cert" resource request. 506 */ 507 char * 508 json_fmt_revokecert(const char *cert) 509 { 510 int c; 511 char *p; 512 513 c = asprintf(&p, "{" 514 "\"resource\": \"revoke-cert\", " 515 "\"certificate\": \"%s\"" 516 "}", cert); 517 if (-1 == c) { 518 warn("asprintf"); 519 p = NULL; 520 } 521 return (p); 522 } 523 524 /* 525 * Format the "new-cert" resource request. 526 */ 527 char * 528 json_fmt_newcert(const char *cert) 529 { 530 int c; 531 char *p; 532 533 c = asprintf(&p, "{" 534 "\"resource\": \"new-cert\", " 535 "\"csr\": \"%s\"" 536 "}", cert); 537 if (-1 == c) { 538 warn("asprintf"); 539 p = NULL; 540 } 541 return (p); 542 } 543 544 /* 545 * Header component of json_fmt_signed(). 546 */ 547 char * 548 json_fmt_header_rsa(const char *exp, const char *mod) 549 { 550 int c; 551 char *p; 552 553 c = asprintf(&p, "{" 554 "\"alg\": \"RS256\", " 555 "\"jwk\": " 556 "{\"e\": \"%s\", \"kty\": \"RSA\", \"n\": \"%s\"}" 557 "}", exp, mod); 558 if (-1 == c) { 559 warn("asprintf"); 560 p = NULL; 561 } 562 return (p); 563 } 564 565 /* 566 * Protected component of json_fmt_signed(). 567 */ 568 char * 569 json_fmt_protected_rsa(const char *exp, const char *mod, const char *nce) 570 { 571 int c; 572 char *p; 573 574 c = asprintf(&p, "{" 575 "\"alg\": \"RS256\", " 576 "\"jwk\": " 577 "{\"e\": \"%s\", \"kty\": \"RSA\", \"n\": \"%s\"}, " 578 "\"nonce\": \"%s\"" 579 "}", exp, mod, nce); 580 if (-1 == c) { 581 warn("asprintf"); 582 p = NULL; 583 } 584 return (p); 585 } 586 587 /* 588 * Signed message contents for the CA server. 589 */ 590 char * 591 json_fmt_signed(const char *header, const char *protected, 592 const char *payload, const char *digest) 593 { 594 int c; 595 char *p; 596 597 c = asprintf(&p, "{" 598 "\"header\": %s, " 599 "\"protected\": \"%s\", " 600 "\"payload\": \"%s\", " 601 "\"signature\": \"%s\"" 602 "}", header, protected, payload, digest); 603 if (-1 == c) { 604 warn("asprintf"); 605 p = NULL; 606 } 607 return (p); 608 } 609 610 /* 611 * Produce thumbprint input. 612 * This isn't technically a JSON string--it's the input we'll use for 613 * hashing and digesting. 614 * However, it's in the form of a JSON string, so do it here. 615 */ 616 char * 617 json_fmt_thumb_rsa(const char *exp, const char *mod) 618 { 619 int c; 620 char *p; 621 622 /*NOTE: WHITESPACE IS IMPORTANT. */ 623 624 c = asprintf(&p, 625 "{\"e\":\"%s\",\"kty\":\"RSA\",\"n\":\"%s\"}", 626 exp, mod); 627 if (-1 == c) { 628 warn("asprintf"); 629 p = NULL; 630 } 631 return (p); 632 } 633