GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/acme-client/json.c Lines: 0 216 0.0 %
Date: 2017-11-07 Branches: 0 134 0.0 %

Line Branch Exec Source
1
/*	$Id: json.c,v 1.9 2017/01/24 13:32:55 jsing 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
 * Extract an array from the returned JSON object, making sure that it's
234
 * the correct type.
235
 * Returns NULL on failure.
236
 */
237
static struct jsmnn *
238
json_getarray(struct jsmnn *n, const char *name)
239
{
240
	size_t		 i;
241
242
	if (n->type != JSMN_OBJECT)
243
		return NULL;
244
	for (i = 0; i < n->fields; i++) {
245
		if (n->d.obj[i].lhs->type != JSMN_STRING &&
246
		    n->d.obj[i].lhs->type != JSMN_PRIMITIVE)
247
			continue;
248
		else if (strcmp(name, n->d.obj[i].lhs->d.str))
249
			continue;
250
		break;
251
	}
252
	if (i == n->fields)
253
		return NULL;
254
	if (n->d.obj[i].rhs->type != JSMN_ARRAY)
255
		return NULL;
256
	return n->d.obj[i].rhs;
257
}
258
259
/*
260
 * Extract a single string from the returned JSON object, making sure
261
 * that it's the correct type.
262
 * Returns NULL on failure.
263
 */
264
static char *
265
json_getstr(struct jsmnn *n, const char *name)
266
{
267
	size_t		 i;
268
	char		*cp;
269
270
	if (n->type != JSMN_OBJECT)
271
		return NULL;
272
	for (i = 0; i < n->fields; i++) {
273
		if (n->d.obj[i].lhs->type != JSMN_STRING &&
274
		    n->d.obj[i].lhs->type != JSMN_PRIMITIVE)
275
			continue;
276
		else if (strcmp(name, n->d.obj[i].lhs->d.str))
277
			continue;
278
		break;
279
	}
280
	if (i == n->fields)
281
		return NULL;
282
	if (n->d.obj[i].rhs->type != JSMN_STRING &&
283
	    n->d.obj[i].rhs->type != JSMN_PRIMITIVE)
284
		return NULL;
285
286
	cp = strdup(n->d.obj[i].rhs->d.str);
287
	if (cp == NULL)
288
		warn("strdup");
289
	return cp;
290
}
291
292
/*
293
 * Completely free the challenge response body.
294
 */
295
void
296
json_free_challenge(struct chng *p)
297
{
298
299
	free(p->uri);
300
	free(p->token);
301
	p->uri = p->token = NULL;
302
}
303
304
/*
305
 * Parse the response from the ACME server when we're waiting to see
306
 * whether the challenge has been ok.
307
 */
308
int
309
json_parse_response(struct jsmnn *n)
310
{
311
	char		*resp;
312
	int		 rc;
313
314
	if (n == NULL)
315
		return -1;
316
	if ((resp = json_getstr(n, "status")) == NULL)
317
		return -1;
318
319
	if (strcmp(resp, "valid") == 0)
320
		rc = 1;
321
	else if (strcmp(resp, "pending") == 0)
322
		rc = 0;
323
	else
324
		rc = -1;
325
326
	free(resp);
327
	return rc;
328
}
329
330
/*
331
 * Parse the response from a new-authz, which consists of challenge
332
 * information, into a structure.
333
 * We only care about the HTTP-01 response.
334
 */
335
int
336
json_parse_challenge(struct jsmnn *n, struct chng *p)
337
{
338
	struct jsmnn	*array, *obj;
339
	size_t		 i;
340
	int		 rc;
341
	char		*type;
342
343
	if (n == NULL)
344
		return 0;
345
346
	array = json_getarray(n, "challenges");
347
	if (array == NULL)
348
		return 0;
349
350
	for (i = 0; i < array->fields; i++) {
351
		obj = json_getarrayobj(array->d.array[i]);
352
		if (obj == NULL)
353
			continue;
354
		type = json_getstr(obj, "type");
355
		if (type == NULL)
356
			continue;
357
		rc = strcmp(type, "http-01");
358
		free(type);
359
		if (rc)
360
			continue;
361
		p->uri = json_getstr(obj, "uri");
362
		p->token = json_getstr(obj, "token");
363
		return p->uri != NULL && p->token != NULL;
364
	}
365
366
	return 0;
367
}
368
369
/*
370
 * Extract the CA paths from the JSON response object.
371
 * Return zero on failure, non-zero on success.
372
 */
373
int
374
json_parse_capaths(struct jsmnn *n, struct capaths *p)
375
{
376
377
	if (n == NULL)
378
		return 0;
379
380
	p->newauthz = json_getstr(n, "new-authz");
381
	p->newcert = json_getstr(n, "new-cert");
382
	p->newreg = json_getstr(n, "new-reg");
383
	p->revokecert = json_getstr(n, "revoke-cert");
384
385
	return p->newauthz != NULL && p->newcert != NULL &&
386
	    p->newreg != NULL && p->revokecert != NULL;
387
}
388
389
/*
390
 * Free up all of our CA-noted paths (which may all be NULL).
391
 */
392
void
393
json_free_capaths(struct capaths *p)
394
{
395
396
	free(p->newauthz);
397
	free(p->newcert);
398
	free(p->newreg);
399
	free(p->revokecert);
400
	memset(p, 0, sizeof(struct capaths));
401
}
402
403
/*
404
 * Parse an HTTP response body from a buffer of size "sz".
405
 * Returns an opaque pointer on success, otherwise NULL on error.
406
 */
407
struct jsmnn *
408
json_parse(const char *buf, size_t sz)
409
{
410
	struct jsmnn	*n;
411
	jsmn_parser	 p;
412
	jsmntok_t	*tok;
413
	int		 r;
414
	size_t		 tokcount;
415
416
	jsmn_init(&p);
417
	tokcount = 128;
418
419
	/* Do this until we don't need any more tokens. */
420
again:
421
	tok = calloc(tokcount, sizeof(jsmntok_t));
422
	if (tok == NULL) {
423
		warn("calloc");
424
		return NULL;
425
	}
426
427
	/* Actually try to parse the JSON into the tokens. */
428
429
	r = jsmn_parse(&p, buf, sz, tok, tokcount);
430
	if (r < 0 && r == JSMN_ERROR_NOMEM) {
431
		tokcount *= 2;
432
		free(tok);
433
		goto again;
434
	} else if (r < 0) {
435
		warnx("jsmn_parse: %d", r);
436
		free(tok);
437
		return NULL;
438
	}
439
440
	/* Now parse the tokens into a tree. */
441
442
	n = jsmntree_alloc(tok, buf, r);
443
	free(tok);
444
	return n;
445
}
446
447
/*
448
 * Format the "new-reg" resource request.
449
 */
450
char *
451
json_fmt_newreg(const char *license)
452
{
453
	int	 c;
454
	char	*p;
455
456
	c = asprintf(&p, "{"
457
	    "\"resource\": \"new-reg\", "
458
	    "\"agreement\": \"%s\""
459
	    "}",
460
	    license);
461
	if (c == -1) {
462
		warn("asprintf");
463
		p = NULL;
464
	}
465
	return p;
466
}
467
468
/*
469
 * Format the "new-authz" resource request.
470
 */
471
char *
472
json_fmt_newauthz(const char *domain)
473
{
474
	int	 c;
475
	char	*p;
476
477
	c = asprintf(&p, "{"
478
	    "\"resource\": \"new-authz\", "
479
	    "\"identifier\": "
480
	    "{\"type\": \"dns\", \"value\": \"%s\"}"
481
	    "}",
482
	    domain);
483
	if (c == -1) {
484
		warn("asprintf");
485
		p = NULL;
486
	}
487
	return p;
488
}
489
490
/*
491
 * Format the "challenge" resource request.
492
 */
493
char *
494
json_fmt_challenge(const char *token, const char *thumb)
495
{
496
	int	 c;
497
	char	*p;
498
499
	c = asprintf(&p, "{"
500
	    "\"resource\": \"challenge\", "
501
	    "\"keyAuthorization\": \"%s.%s\""
502
	    "}",
503
	    token, thumb);
504
	if (c == -1) {
505
		warn("asprintf");
506
		p = NULL;
507
	}
508
	return p;
509
}
510
511
/*
512
 * Format the "new-cert" resource request.
513
 */
514
char *
515
json_fmt_revokecert(const char *cert)
516
{
517
	int	 c;
518
	char	*p;
519
520
	c = asprintf(&p, "{"
521
	    "\"resource\": \"revoke-cert\", "
522
	    "\"certificate\": \"%s\""
523
	    "}",
524
	    cert);
525
	if (c == -1) {
526
		warn("asprintf");
527
		p = NULL;
528
	}
529
	return p;
530
}
531
532
/*
533
 * Format the "new-cert" resource request.
534
 */
535
char *
536
json_fmt_newcert(const char *cert)
537
{
538
	int	 c;
539
	char	*p;
540
541
	c = asprintf(&p, "{"
542
	    "\"resource\": \"new-cert\", "
543
	    "\"csr\": \"%s\""
544
	    "}",
545
	    cert);
546
	if (c == -1) {
547
		warn("asprintf");
548
		p = NULL;
549
	}
550
	return p;
551
}
552
553
/*
554
 * Header component of json_fmt_signed().
555
 */
556
char *
557
json_fmt_header_rsa(const char *exp, const char *mod)
558
{
559
	int	 c;
560
	char	*p;
561
562
	c = asprintf(&p, "{"
563
	    "\"alg\": \"RS256\", "
564
	    "\"jwk\": "
565
	    "{\"e\": \"%s\", \"kty\": \"RSA\", \"n\": \"%s\"}"
566
	    "}",
567
	    exp, mod);
568
	if (c == -1) {
569
		warn("asprintf");
570
		p = NULL;
571
	}
572
	return p;
573
}
574
575
/*
576
 * Protected component of json_fmt_signed().
577
 */
578
char *
579
json_fmt_protected_rsa(const char *exp, const char *mod, const char *nce)
580
{
581
	int	 c;
582
	char	*p;
583
584
	c = asprintf(&p, "{"
585
	    "\"alg\": \"RS256\", "
586
	    "\"jwk\": "
587
	    "{\"e\": \"%s\", \"kty\": \"RSA\", \"n\": \"%s\"}, "
588
	    "\"nonce\": \"%s\""
589
	    "}",
590
	    exp, mod, nce);
591
	if (c == -1) {
592
		warn("asprintf");
593
		p = NULL;
594
	}
595
	return p;
596
}
597
598
/*
599
 * Signed message contents for the CA server.
600
 */
601
char *
602
json_fmt_signed(const char *header, const char *protected,
603
    const char *payload, const char *digest)
604
{
605
	int	 c;
606
	char	*p;
607
608
	c = asprintf(&p, "{"
609
	    "\"header\": %s, "
610
	    "\"protected\": \"%s\", "
611
	    "\"payload\": \"%s\", "
612
	    "\"signature\": \"%s\""
613
	    "}",
614
	    header, protected, payload, digest);
615
	if (c == -1) {
616
		warn("asprintf");
617
		p = NULL;
618
	}
619
	return p;
620
}
621
622
/*
623
 * Produce thumbprint input.
624
 * This isn't technically a JSON string--it's the input we'll use for
625
 * hashing and digesting.
626
 * However, it's in the form of a JSON string, so do it here.
627
 */
628
char *
629
json_fmt_thumb_rsa(const char *exp, const char *mod)
630
{
631
	int	 c;
632
	char	*p;
633
634
	/*NOTE: WHITESPACE IS IMPORTANT. */
635
636
	c = asprintf(&p, "{\"e\":\"%s\",\"kty\":\"RSA\",\"n\":\"%s\"}",
637
	    exp, mod);
638
	if (c == -1) {
639
		warn("asprintf");
640
		p = NULL;
641
	}
642
	return p;
643
}