xref: /openbsd-src/usr.sbin/acme-client/json.c (revision 7b00f4e9025e62f943a7d71b1da3c1269c5d956f)
1 /*	$Id: json.c,v 1.12 2019/06/07 08:07:52 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 #ifdef notyet
269 /*
270  * Extract subtree from the returned JSON object, making sure that it's
271  * the correct type.
272  * Returns NULL on failure.
273  */
274 static struct jsmnn *
275 json_getobj(struct jsmnn *n, const char *name)
276 {
277 	size_t		 i;
278 
279 	if (n->type != JSMN_OBJECT)
280 		return NULL;
281 	for (i = 0; i < n->fields; i++) {
282 		if (n->d.obj[i].lhs->type != JSMN_STRING &&
283 		    n->d.obj[i].lhs->type != JSMN_PRIMITIVE)
284 			continue;
285 		else if (strcmp(name, n->d.obj[i].lhs->d.str))
286 			continue;
287 		break;
288 	}
289 	if (i == n->fields)
290 		return NULL;
291 	if (n->d.obj[i].rhs->type != JSMN_OBJECT)
292 		return NULL;
293 	return n->d.obj[i].rhs;
294 }
295 #endif /* notyet */
296 
297 /*
298  * Extract a single string from the returned JSON object, making sure
299  * that it's the correct type.
300  * Returns NULL on failure.
301  */
302 static char *
303 json_getstr(struct jsmnn *n, const char *name)
304 {
305 	size_t		 i;
306 	char		*cp;
307 
308 	if (n->type != JSMN_OBJECT)
309 		return NULL;
310 	for (i = 0; i < n->fields; i++) {
311 		if (n->d.obj[i].lhs->type != JSMN_STRING &&
312 		    n->d.obj[i].lhs->type != JSMN_PRIMITIVE)
313 			continue;
314 		else if (strcmp(name, n->d.obj[i].lhs->d.str))
315 			continue;
316 		break;
317 	}
318 	if (i == n->fields)
319 		return NULL;
320 	if (n->d.obj[i].rhs->type != JSMN_STRING &&
321 	    n->d.obj[i].rhs->type != JSMN_PRIMITIVE)
322 		return NULL;
323 
324 	cp = strdup(n->d.obj[i].rhs->d.str);
325 	if (cp == NULL)
326 		warn("strdup");
327 	return cp;
328 }
329 
330 /*
331  * Completely free the challenge response body.
332  */
333 void
334 json_free_challenge(struct chng *p)
335 {
336 
337 	free(p->uri);
338 	free(p->token);
339 	p->uri = p->token = NULL;
340 }
341 
342 /*
343  * Parse the response from the ACME server when we're waiting to see
344  * whether the challenge has been ok.
345  */
346 enum chngstatus
347 json_parse_response(struct jsmnn *n)
348 {
349 	char		*resp;
350 	enum chngstatus	 rc;
351 
352 	if (n == NULL)
353 		return CHNG_INVALID;
354 	if ((resp = json_getstr(n, "status")) == NULL)
355 		return CHNG_INVALID;
356 
357 	if (strcmp(resp, "valid") == 0)
358 		rc = CHNG_VALID;
359 	else if (strcmp(resp, "pending") == 0)
360 		rc = CHNG_PENDING;
361 	else if (strcmp(resp, "processing") == 0)
362 		rc = CHNG_PROCESSING;
363 	else
364 		rc = CHNG_INVALID;
365 
366 	free(resp);
367 	return rc;
368 }
369 
370 /*
371  * Parse the response from a new-authz, which consists of challenge
372  * information, into a structure.
373  * We only care about the HTTP-01 response.
374  */
375 int
376 json_parse_challenge(struct jsmnn *n, struct chng *p)
377 {
378 	struct jsmnn	*array, *obj;
379 	size_t		 i;
380 	int		 rc;
381 	char		*type;
382 
383 	if (n == NULL)
384 		return 0;
385 
386 	array = json_getarray(n, "challenges");
387 	if (array == NULL)
388 		return 0;
389 
390 	for (i = 0; i < array->fields; i++) {
391 		obj = json_getarrayobj(array->d.array[i]);
392 		if (obj == NULL)
393 			continue;
394 		type = json_getstr(obj, "type");
395 		if (type == NULL)
396 			continue;
397 		rc = strcmp(type, "http-01");
398 		free(type);
399 		if (rc)
400 			continue;
401 		p->uri = json_getstr(obj, "url");
402 		p->token = json_getstr(obj, "token");
403 		p->status = json_parse_response(obj);
404 		return p->uri != NULL && p->token != NULL;
405 	}
406 
407 	return 0;
408 }
409 
410 static enum orderstatus
411 json_parse_order_status(struct jsmnn *n)
412 {
413 	char	*status;
414 
415 	if (n == NULL)
416 		return ORDER_INVALID;
417 
418 	if ((status = json_getstr(n, "status")) == NULL)
419 		return ORDER_INVALID;
420 
421 	if (strcmp(status, "pending") == 0)
422 		return ORDER_PENDING;
423 	else if (strcmp(status, "ready") == 0)
424 		return ORDER_READY;
425 	else if (strcmp(status, "processing") == 0)
426 		return ORDER_PROCESSING;
427 	else if (strcmp(status, "valid") == 0)
428 		return ORDER_VALID;
429 	else if (strcmp(status, "invalid") == 0)
430 		return ORDER_INVALID;
431 	else
432 		return ORDER_INVALID;
433 }
434 
435 /*
436  * Parse the response from a newOrder, which consists of a status
437  * a list of authorization urls and a finalize url into a struct.
438  */
439 int
440 json_parse_order(struct jsmnn *n, struct order *order)
441 {
442 	struct jsmnn	*array;
443 	size_t		 i;
444 	char		*finalize, *str;
445 
446 	order->status = json_parse_order_status(n);
447 
448 	if (n == NULL)
449 		return 0;
450 
451 	if ((finalize = json_getstr(n, "finalize")) == NULL) {
452 		warnx("no finalize field in order response");
453 		return 0;
454 	}
455 
456 	if ((order->finalize = strdup(finalize)) == NULL)
457 		goto err;
458 
459 	if ((array = json_getarray(n, "authorizations")) == NULL)
460 		goto err;
461 
462 	if ((order->authsz = array->fields) > 0) {
463 		order->auths = calloc(sizeof(*order->auths), order->authsz);
464 		if (order->auths == NULL) {
465 			warn("malloc");
466 			goto err;
467 		}
468 	}
469 
470 	for (i = 0; i < array->fields; i++) {
471 		str = json_getarraystr(array->d.array[i]);
472 		if (str == NULL)
473 			continue;
474 		if ((order->auths[i] = strdup(str)) == NULL) {
475 			warn("strdup");
476 			goto err;
477 		}
478 	}
479 	return 1;
480 err:
481 	json_free_order(order);
482 	return 0;
483 }
484 
485 int
486 json_parse_upd_order(struct jsmnn *n, struct order *order)
487 {
488 	char	*certificate;
489 	order->status = json_parse_order_status(n);
490 	if ((certificate = json_getstr(n, "certificate")) != NULL) {
491 		if ((order->certificate = strdup(certificate)) == NULL)
492 			return 0;
493 	}
494 	return 1;
495 }
496 
497 void
498 json_free_order(struct order *order)
499 {
500 	size_t i;
501 
502 	free(order->finalize);
503 	order->finalize = NULL;
504 	for(i = 0; i < order->authsz; i++)
505 		free(order->auths[i]);
506 	free(order->auths);
507 
508 	order->finalize = NULL;
509 	order->auths = NULL;
510 	order->authsz = 0;
511 }
512 
513 /*
514  * Extract the CA paths from the JSON response object.
515  * Return zero on failure, non-zero on success.
516  */
517 int
518 json_parse_capaths(struct jsmnn *n, struct capaths *p)
519 {
520 	if (n == NULL)
521 		return 0;
522 
523 	p->newaccount = json_getstr(n, "newAccount");
524 	p->newnonce = json_getstr(n, "newNonce");
525 	p->neworder = json_getstr(n, "newOrder");
526 	p->revokecert = json_getstr(n, "revokeCert");
527 
528 	return p->newaccount != NULL && p->newnonce != NULL &&
529 	    p->neworder != NULL && p->revokecert != NULL;
530 }
531 
532 /*
533  * Free up all of our CA-noted paths (which may all be NULL).
534  */
535 void
536 json_free_capaths(struct capaths *p)
537 {
538 
539 	free(p->newaccount);
540 	free(p->newnonce);
541 	free(p->neworder);
542 	free(p->revokecert);
543 	memset(p, 0, sizeof(struct capaths));
544 }
545 
546 /*
547  * Parse an HTTP response body from a buffer of size "sz".
548  * Returns an opaque pointer on success, otherwise NULL on error.
549  */
550 struct jsmnn *
551 json_parse(const char *buf, size_t sz)
552 {
553 	struct jsmnn	*n;
554 	jsmn_parser	 p;
555 	jsmntok_t	*tok;
556 	int		 r;
557 	size_t		 tokcount;
558 
559 	jsmn_init(&p);
560 	tokcount = 128;
561 
562 	/* Do this until we don't need any more tokens. */
563 again:
564 	tok = calloc(tokcount, sizeof(jsmntok_t));
565 	if (tok == NULL) {
566 		warn("calloc");
567 		return NULL;
568 	}
569 
570 	/* Actually try to parse the JSON into the tokens. */
571 
572 	r = jsmn_parse(&p, buf, sz, tok, tokcount);
573 	if (r < 0 && r == JSMN_ERROR_NOMEM) {
574 		tokcount *= 2;
575 		free(tok);
576 		goto again;
577 	} else if (r < 0) {
578 		warnx("jsmn_parse: %d", r);
579 		free(tok);
580 		return NULL;
581 	}
582 
583 	/* Now parse the tokens into a tree. */
584 
585 	n = jsmntree_alloc(tok, buf, r);
586 	free(tok);
587 	return n;
588 }
589 
590 /*
591  * Format the "newAccount" resource request to check if the account exist.
592  */
593 char *
594 json_fmt_chkacc(void)
595 {
596 	int	 c;
597 	char	*p;
598 
599 	c = asprintf(&p, "{"
600 	    "\"termsOfServiceAgreed\": true, "
601 	    "\"onlyReturnExisting\": true"
602 	    "}");
603 	if (c == -1) {
604 		warn("asprintf");
605 		p = NULL;
606 	}
607 	return p;
608 }
609 
610 /*
611  * Format the "newAccount" resource request.
612  */
613 char *
614 json_fmt_newacc(void)
615 {
616 	int	 c;
617 	char	*p;
618 
619 	c = asprintf(&p, "{"
620 	    "\"termsOfServiceAgreed\": true"
621 	    "}");
622 	if (c == -1) {
623 		warn("asprintf");
624 		p = NULL;
625 	}
626 	return p;
627 }
628 
629 /*
630  * Format the "newOrder" resource request
631  */
632 char *
633 json_fmt_neworder(const char *const *alts, size_t altsz)
634 {
635 	size_t	 i;
636 	int	 c;
637 	char	*p, *t;
638 
639 	if ((p = strdup("{ \"identifiers\": [")) == NULL)
640 		goto err;
641 
642 	t = p;
643 	for (i = 0; i < altsz; i++) {
644 		c = asprintf(&p,
645 		    "%s { \"type\": \"dns\", \"value\": \"%s\" }%s",
646 		    t, alts[i], i + 1 == altsz ? "" : ",");
647 		free(t);
648 		if (c == -1) {
649 			warn("asprintf");
650 			p = NULL;
651 			goto err;
652 		}
653 		t = p;
654 	}
655 	c = asprintf(&p, "%s ] }", t);
656 	free(t);
657 	if (c == -1) {
658 		warn("asprintf");
659 		p = NULL;
660 	}
661 	return p;
662 err:
663 	free(p);
664 	return NULL;
665 }
666 
667 /*
668  * Format the revoke resource request.
669  */
670 char *
671 json_fmt_revokecert(const char *cert)
672 {
673 	int	 c;
674 	char	*p;
675 
676 	c = asprintf(&p, "{"
677 	    "\"certificate\": \"%s\""
678 	    "}",
679 	    cert);
680 	if (c == -1) {
681 		warn("asprintf");
682 		p = NULL;
683 	}
684 	return p;
685 }
686 
687 /*
688  * Format the "new-cert" resource request.
689  */
690 char *
691 json_fmt_newcert(const char *cert)
692 {
693 	int	 c;
694 	char	*p;
695 
696 	c = asprintf(&p, "{"
697 	    "\"csr\": \"%s\""
698 	    "}",
699 	    cert);
700 	if (c == -1) {
701 		warn("asprintf");
702 		p = NULL;
703 	}
704 	return p;
705 }
706 
707 /*
708  * Protected component of json_fmt_signed().
709  */
710 char *
711 json_fmt_protected_rsa(const char *exp, const char *mod, const char *nce,
712     const char *url)
713 {
714 	int	 c;
715 	char	*p;
716 
717 	c = asprintf(&p, "{"
718 	    "\"alg\": \"RS256\", "
719 	    "\"jwk\": "
720 	    "{\"e\": \"%s\", \"kty\": \"RSA\", \"n\": \"%s\"}, "
721 	    "\"nonce\": \"%s\", "
722 	    "\"url\": \"%s\""
723 	    "}",
724 	    exp, mod, nce, url);
725 	if (c == -1) {
726 		warn("asprintf");
727 		p = NULL;
728 	}
729 	return p;
730 }
731 
732 /*
733  * Protected component of json_fmt_signed().
734  */
735 char *
736 json_fmt_protected_kid(const char *kid, const char *nce, const char *url)
737 {
738 	int	 c;
739 	char	*p;
740 
741 	c = asprintf(&p, "{"
742 	    "\"alg\": \"RS256\", "
743 	    "\"kid\": \"%s\", "
744 	    "\"nonce\": \"%s\", "
745 	    "\"url\": \"%s\""
746 	    "}",
747 	    kid, nce, url);
748 	if (c == -1) {
749 		warn("asprintf");
750 		p = NULL;
751 	}
752 	return p;
753 }
754 
755 /*
756  * Signed message contents for the CA server.
757  */
758 char *
759 json_fmt_signed(const char *protected, const char *payload, const char *digest)
760 {
761 	int	 c;
762 	char	*p;
763 
764 	c = asprintf(&p, "{"
765 	    "\"protected\": \"%s\", "
766 	    "\"payload\": \"%s\", "
767 	    "\"signature\": \"%s\""
768 	    "}",
769 	    protected, payload, digest);
770 	if (c == -1) {
771 		warn("asprintf");
772 		p = NULL;
773 	}
774 	return p;
775 }
776 
777 /*
778  * Produce thumbprint input.
779  * This isn't technically a JSON string--it's the input we'll use for
780  * hashing and digesting.
781  * However, it's in the form of a JSON string, so do it here.
782  */
783 char *
784 json_fmt_thumb_rsa(const char *exp, const char *mod)
785 {
786 	int	 c;
787 	char	*p;
788 
789 	/*NOTE: WHITESPACE IS IMPORTANT. */
790 
791 	c = asprintf(&p, "{\"e\":\"%s\",\"kty\":\"RSA\",\"n\":\"%s\"}",
792 	    exp, mod);
793 	if (c == -1) {
794 		warn("asprintf");
795 		p = NULL;
796 	}
797 	return p;
798 }
799