1341618Scy/*
2341618Scy * JavaScript Object Notation (JSON) parser (RFC7159)
3341618Scy * Copyright (c) 2017, Qualcomm Atheros, Inc.
4341618Scy *
5341618Scy * This software may be distributed under the terms of the BSD license.
6341618Scy * See README for more details.
7341618Scy */
8341618Scy
9341618Scy#include "includes.h"
10341618Scy
11341618Scy#include "common.h"
12341618Scy#include "base64.h"
13341618Scy#include "json.h"
14341618Scy
15341618Scy#define JSON_MAX_DEPTH 10
16341618Scy#define JSON_MAX_TOKENS 500
17341618Scy
18341618Scy
19341618Scyvoid json_escape_string(char *txt, size_t maxlen, const char *data, size_t len)
20341618Scy{
21341618Scy	char *end = txt + maxlen;
22341618Scy	size_t i;
23341618Scy
24341618Scy	for (i = 0; i < len; i++) {
25341618Scy		if (txt + 4 >= end)
26341618Scy			break;
27341618Scy
28341618Scy		switch (data[i]) {
29341618Scy		case '\"':
30341618Scy			*txt++ = '\\';
31341618Scy			*txt++ = '\"';
32341618Scy			break;
33341618Scy		case '\\':
34341618Scy			*txt++ = '\\';
35341618Scy			*txt++ = '\\';
36341618Scy			break;
37341618Scy		case '\n':
38341618Scy			*txt++ = '\\';
39341618Scy			*txt++ = 'n';
40341618Scy			break;
41341618Scy		case '\r':
42341618Scy			*txt++ = '\\';
43341618Scy			*txt++ = 'r';
44341618Scy			break;
45341618Scy		case '\t':
46341618Scy			*txt++ = '\\';
47341618Scy			*txt++ = 't';
48341618Scy			break;
49341618Scy		default:
50341618Scy			if (data[i] >= 32 && data[i] <= 126) {
51341618Scy				*txt++ = data[i];
52341618Scy			} else {
53341618Scy				txt += os_snprintf(txt, end - txt, "\\u%04x",
54341618Scy						   data[i]);
55341618Scy			}
56341618Scy			break;
57341618Scy		}
58341618Scy	}
59341618Scy
60341618Scy	*txt = '\0';
61341618Scy}
62341618Scy
63341618Scy
64341618Scystatic char * json_parse_string(const char **json_pos, const char *end)
65341618Scy{
66341618Scy	const char *pos = *json_pos;
67341618Scy	char *str, *spos, *s_end;
68341618Scy	size_t max_len, buf_len;
69341618Scy	u8 bin[2];
70341618Scy
71341618Scy	pos++; /* skip starting quote */
72341618Scy
73341618Scy	max_len = end - pos + 1;
74341618Scy	buf_len = max_len > 10 ? 10 : max_len;
75341618Scy	str = os_malloc(buf_len);
76341618Scy	if (!str)
77341618Scy		return NULL;
78341618Scy	spos = str;
79341618Scy	s_end = str + buf_len;
80341618Scy
81341618Scy	for (; pos < end; pos++) {
82341618Scy		if (buf_len < max_len && s_end - spos < 3) {
83341618Scy			char *tmp;
84341618Scy			int idx;
85341618Scy
86341618Scy			idx = spos - str;
87341618Scy			buf_len *= 2;
88341618Scy			if (buf_len > max_len)
89341618Scy				buf_len = max_len;
90341618Scy			tmp = os_realloc(str, buf_len);
91341618Scy			if (!tmp)
92341618Scy				goto fail;
93341618Scy			str = tmp;
94341618Scy			spos = str + idx;
95341618Scy			s_end = str + buf_len;
96341618Scy		}
97341618Scy
98341618Scy		switch (*pos) {
99341618Scy		case '\"': /* end string */
100341618Scy			*spos = '\0';
101341618Scy			/* caller will move to the next position */
102341618Scy			*json_pos = pos;
103341618Scy			return str;
104341618Scy		case '\\':
105341618Scy			pos++;
106346981Scy			if (pos >= end) {
107346981Scy				wpa_printf(MSG_DEBUG,
108346981Scy					   "JSON: Truncated \\ escape");
109346981Scy				goto fail;
110346981Scy			}
111341618Scy			switch (*pos) {
112341618Scy			case '"':
113341618Scy			case '\\':
114341618Scy			case '/':
115341618Scy				*spos++ = *pos;
116341618Scy				break;
117341618Scy			case 'n':
118341618Scy				*spos++ = '\n';
119341618Scy				break;
120341618Scy			case 'r':
121341618Scy				*spos++ = '\r';
122341618Scy				break;
123341618Scy			case 't':
124341618Scy				*spos++ = '\t';
125341618Scy				break;
126341618Scy			case 'u':
127341618Scy				if (end - pos < 5 ||
128341618Scy				    hexstr2bin(pos + 1, bin, 2) < 0 ||
129341618Scy				    bin[1] == 0x00) {
130341618Scy					wpa_printf(MSG_DEBUG,
131341618Scy						   "JSON: Invalid \\u escape");
132341618Scy					goto fail;
133341618Scy				}
134341618Scy				if (bin[0] == 0x00) {
135341618Scy					*spos++ = bin[1];
136341618Scy				} else {
137341618Scy					*spos++ = bin[0];
138341618Scy					*spos++ = bin[1];
139341618Scy				}
140341618Scy				pos += 4;
141341618Scy				break;
142341618Scy			default:
143341618Scy				wpa_printf(MSG_DEBUG,
144341618Scy					   "JSON: Unknown escape '%c'", *pos);
145341618Scy				goto fail;
146341618Scy			}
147341618Scy			break;
148341618Scy		default:
149341618Scy			*spos++ = *pos;
150341618Scy			break;
151341618Scy		}
152341618Scy	}
153341618Scy
154341618Scyfail:
155341618Scy	os_free(str);
156341618Scy	return NULL;
157341618Scy}
158341618Scy
159341618Scy
160341618Scystatic int json_parse_number(const char **json_pos, const char *end,
161341618Scy			     int *ret_val)
162341618Scy{
163341618Scy	const char *pos = *json_pos;
164341618Scy	size_t len;
165341618Scy	char *str;
166341618Scy
167341618Scy	for (; pos < end; pos++) {
168341618Scy		if (*pos != '-' && (*pos < '0' || *pos > '9')) {
169341618Scy			pos--;
170341618Scy			break;
171341618Scy		}
172341618Scy	}
173346981Scy	if (pos == end)
174346981Scy		pos--;
175341618Scy	if (pos < *json_pos)
176341618Scy		return -1;
177341618Scy	len = pos - *json_pos + 1;
178341618Scy	str = os_malloc(len + 1);
179341618Scy	if (!str)
180341618Scy		return -1;
181341618Scy	os_memcpy(str, *json_pos, len);
182341618Scy	str[len] = '\0';
183341618Scy
184341618Scy	*ret_val = atoi(str);
185341618Scy	os_free(str);
186341618Scy	*json_pos = pos;
187341618Scy	return 0;
188341618Scy}
189341618Scy
190341618Scy
191341618Scystatic int json_check_tree_state(struct json_token *token)
192341618Scy{
193341618Scy	if (!token)
194341618Scy		return 0;
195341618Scy	if (json_check_tree_state(token->child) < 0 ||
196341618Scy	    json_check_tree_state(token->sibling) < 0)
197341618Scy		return -1;
198341618Scy	if (token->state != JSON_COMPLETED) {
199341618Scy		wpa_printf(MSG_DEBUG,
200341618Scy			   "JSON: Unexpected token state %d (name=%s type=%d)",
201341618Scy			   token->state, token->name ? token->name : "N/A",
202341618Scy			   token->type);
203341618Scy		return -1;
204341618Scy	}
205341618Scy	return 0;
206341618Scy}
207341618Scy
208341618Scy
209341618Scystatic struct json_token * json_alloc_token(unsigned int *tokens)
210341618Scy{
211341618Scy	(*tokens)++;
212341618Scy	if (*tokens > JSON_MAX_TOKENS) {
213341618Scy		wpa_printf(MSG_DEBUG, "JSON: Maximum token limit exceeded");
214341618Scy		return NULL;
215341618Scy	}
216341618Scy	return os_zalloc(sizeof(struct json_token));
217341618Scy}
218341618Scy
219341618Scy
220341618Scystruct json_token * json_parse(const char *data, size_t data_len)
221341618Scy{
222341618Scy	struct json_token *root = NULL, *curr_token = NULL, *token = NULL;
223341618Scy	const char *pos, *end;
224341618Scy	char *str;
225341618Scy	int num;
226341618Scy	unsigned int depth = 0;
227341618Scy	unsigned int tokens = 0;
228341618Scy
229341618Scy	pos = data;
230341618Scy	end = data + data_len;
231341618Scy
232341618Scy	for (; pos < end; pos++) {
233341618Scy		switch (*pos) {
234341618Scy		case '[': /* start array */
235341618Scy		case '{': /* start object */
236341618Scy			if (!curr_token) {
237341618Scy				token = json_alloc_token(&tokens);
238341618Scy				if (!token)
239341618Scy					goto fail;
240341618Scy				if (!root)
241341618Scy					root = token;
242341618Scy			} else if (curr_token->state == JSON_WAITING_VALUE) {
243341618Scy				token = curr_token;
244341618Scy			} else if (curr_token->parent &&
245341618Scy				   curr_token->parent->type == JSON_ARRAY &&
246341618Scy				   curr_token->parent->state == JSON_STARTED &&
247341618Scy				   curr_token->state == JSON_EMPTY) {
248341618Scy				token = curr_token;
249341618Scy			} else {
250341618Scy				wpa_printf(MSG_DEBUG,
251341618Scy					   "JSON: Invalid state for start array/object");
252341618Scy				goto fail;
253341618Scy			}
254341618Scy			depth++;
255341618Scy			if (depth > JSON_MAX_DEPTH) {
256341618Scy				wpa_printf(MSG_DEBUG,
257341618Scy					   "JSON: Max depth exceeded");
258341618Scy				goto fail;
259341618Scy			}
260341618Scy			token->type = *pos == '[' ? JSON_ARRAY : JSON_OBJECT;
261341618Scy			token->state = JSON_STARTED;
262341618Scy			token->child = json_alloc_token(&tokens);
263341618Scy			if (!token->child)
264341618Scy				goto fail;
265341618Scy			curr_token = token->child;
266341618Scy			curr_token->parent = token;
267341618Scy			curr_token->state = JSON_EMPTY;
268341618Scy			break;
269341618Scy		case ']': /* end array */
270341618Scy		case '}': /* end object */
271341618Scy			if (!curr_token || !curr_token->parent ||
272341618Scy			    curr_token->parent->state != JSON_STARTED) {
273341618Scy				wpa_printf(MSG_DEBUG,
274341618Scy					   "JSON: Invalid state for end array/object");
275341618Scy				goto fail;
276341618Scy			}
277341618Scy			depth--;
278341618Scy			curr_token = curr_token->parent;
279341618Scy			if ((*pos == ']' &&
280341618Scy			     curr_token->type != JSON_ARRAY) ||
281341618Scy			    (*pos == '}' &&
282341618Scy			     curr_token->type != JSON_OBJECT)) {
283341618Scy				wpa_printf(MSG_DEBUG,
284341618Scy					   "JSON: Array/Object mismatch");
285341618Scy				goto fail;
286341618Scy			}
287341618Scy			if (curr_token->child->state == JSON_EMPTY &&
288341618Scy			    !curr_token->child->child &&
289341618Scy			    !curr_token->child->sibling) {
290341618Scy				/* Remove pending child token since the
291341618Scy				 * array/object was empty. */
292341618Scy				json_free(curr_token->child);
293341618Scy				curr_token->child = NULL;
294341618Scy			}
295341618Scy			curr_token->state = JSON_COMPLETED;
296341618Scy			break;
297341618Scy		case '\"': /* string */
298341618Scy			str = json_parse_string(&pos, end);
299341618Scy			if (!str)
300341618Scy				goto fail;
301341618Scy			if (!curr_token) {
302341618Scy				token = json_alloc_token(&tokens);
303341618Scy				if (!token)
304341618Scy					goto fail;
305341618Scy				token->type = JSON_STRING;
306341618Scy				token->string = str;
307341618Scy				token->state = JSON_COMPLETED;
308341618Scy			} else if (curr_token->parent &&
309341618Scy				   curr_token->parent->type == JSON_ARRAY &&
310341618Scy				   curr_token->parent->state == JSON_STARTED &&
311341618Scy				   curr_token->state == JSON_EMPTY) {
312341618Scy				curr_token->string = str;
313341618Scy				curr_token->state = JSON_COMPLETED;
314341618Scy				curr_token->type = JSON_STRING;
315341618Scy				wpa_printf(MSG_MSGDUMP,
316341618Scy					   "JSON: String value: '%s'",
317341618Scy					   curr_token->string);
318341618Scy			} else if (curr_token->state == JSON_EMPTY) {
319341618Scy				curr_token->type = JSON_VALUE;
320341618Scy				curr_token->name = str;
321341618Scy				curr_token->state = JSON_STARTED;
322341618Scy			} else if (curr_token->state == JSON_WAITING_VALUE) {
323341618Scy				curr_token->string = str;
324341618Scy				curr_token->state = JSON_COMPLETED;
325341618Scy				curr_token->type = JSON_STRING;
326341618Scy				wpa_printf(MSG_MSGDUMP,
327341618Scy					   "JSON: String value: '%s' = '%s'",
328341618Scy					   curr_token->name,
329341618Scy					   curr_token->string);
330341618Scy			} else {
331341618Scy				wpa_printf(MSG_DEBUG,
332341618Scy					   "JSON: Invalid state for a string");
333341618Scy				os_free(str);
334341618Scy				goto fail;
335341618Scy			}
336341618Scy			break;
337341618Scy		case ' ':
338341618Scy		case '\t':
339341618Scy		case '\r':
340341618Scy		case '\n':
341341618Scy			/* ignore whitespace */
342341618Scy			break;
343341618Scy		case ':': /* name/value separator */
344341618Scy			if (!curr_token || curr_token->state != JSON_STARTED)
345341618Scy				goto fail;
346341618Scy			curr_token->state = JSON_WAITING_VALUE;
347341618Scy			break;
348341618Scy		case ',': /* member separator */
349341618Scy			if (!curr_token)
350341618Scy				goto fail;
351341618Scy			curr_token->sibling = json_alloc_token(&tokens);
352341618Scy			if (!curr_token->sibling)
353341618Scy				goto fail;
354341618Scy			curr_token->sibling->parent = curr_token->parent;
355341618Scy			curr_token = curr_token->sibling;
356341618Scy			curr_token->state = JSON_EMPTY;
357341618Scy			break;
358341618Scy		case 't': /* true */
359341618Scy		case 'f': /* false */
360341618Scy		case 'n': /* null */
361341618Scy			if (!((end - pos >= 4 &&
362341618Scy			       os_strncmp(pos, "true", 4) == 0) ||
363341618Scy			      (end - pos >= 5 &&
364341618Scy			       os_strncmp(pos, "false", 5) == 0) ||
365341618Scy			      (end - pos >= 4 &&
366341618Scy			       os_strncmp(pos, "null", 4) == 0))) {
367341618Scy				wpa_printf(MSG_DEBUG,
368341618Scy					   "JSON: Invalid literal name");
369341618Scy				goto fail;
370341618Scy			}
371341618Scy			if (!curr_token) {
372341618Scy				token = json_alloc_token(&tokens);
373341618Scy				if (!token)
374341618Scy					goto fail;
375341618Scy				curr_token = token;
376341618Scy			} else if (curr_token->state == JSON_WAITING_VALUE) {
377341618Scy				wpa_printf(MSG_MSGDUMP,
378341618Scy					   "JSON: Literal name: '%s' = %c",
379341618Scy					   curr_token->name, *pos);
380341618Scy			} else if (curr_token->parent &&
381341618Scy				   curr_token->parent->type == JSON_ARRAY &&
382341618Scy				   curr_token->parent->state == JSON_STARTED &&
383341618Scy				   curr_token->state == JSON_EMPTY) {
384341618Scy				wpa_printf(MSG_MSGDUMP,
385341618Scy					   "JSON: Literal name: %c", *pos);
386341618Scy			} else {
387341618Scy				wpa_printf(MSG_DEBUG,
388341618Scy					   "JSON: Invalid state for a literal name");
389341618Scy				goto fail;
390341618Scy			}
391341618Scy			switch (*pos) {
392341618Scy			case 't':
393341618Scy				curr_token->type = JSON_BOOLEAN;
394341618Scy				curr_token->number = 1;
395341618Scy				pos += 3;
396341618Scy				break;
397341618Scy			case 'f':
398341618Scy				curr_token->type = JSON_BOOLEAN;
399341618Scy				curr_token->number = 0;
400341618Scy				pos += 4;
401341618Scy				break;
402341618Scy			case 'n':
403341618Scy				curr_token->type = JSON_NULL;
404341618Scy				pos += 3;
405341618Scy				break;
406341618Scy			}
407341618Scy			curr_token->state = JSON_COMPLETED;
408341618Scy			break;
409341618Scy		case '-':
410341618Scy		case '0':
411341618Scy		case '1':
412341618Scy		case '2':
413341618Scy		case '3':
414341618Scy		case '4':
415341618Scy		case '5':
416341618Scy		case '6':
417341618Scy		case '7':
418341618Scy		case '8':
419341618Scy		case '9':
420341618Scy			/* number */
421341618Scy			if (json_parse_number(&pos, end, &num) < 0)
422341618Scy				goto fail;
423341618Scy			if (!curr_token) {
424341618Scy				token = json_alloc_token(&tokens);
425341618Scy				if (!token)
426341618Scy					goto fail;
427341618Scy				token->type = JSON_NUMBER;
428341618Scy				token->number = num;
429341618Scy				token->state = JSON_COMPLETED;
430341618Scy			} else if (curr_token->state == JSON_WAITING_VALUE) {
431341618Scy				curr_token->number = num;
432341618Scy				curr_token->state = JSON_COMPLETED;
433341618Scy				curr_token->type = JSON_NUMBER;
434341618Scy				wpa_printf(MSG_MSGDUMP,
435341618Scy					   "JSON: Number value: '%s' = '%d'",
436341618Scy					   curr_token->name,
437341618Scy					   curr_token->number);
438341618Scy			} else if (curr_token->parent &&
439341618Scy				   curr_token->parent->type == JSON_ARRAY &&
440341618Scy				   curr_token->parent->state == JSON_STARTED &&
441341618Scy				   curr_token->state == JSON_EMPTY) {
442341618Scy				curr_token->number = num;
443341618Scy				curr_token->state = JSON_COMPLETED;
444341618Scy				curr_token->type = JSON_NUMBER;
445341618Scy				wpa_printf(MSG_MSGDUMP,
446341618Scy					   "JSON: Number value: %d",
447341618Scy					   curr_token->number);
448341618Scy			} else {
449341618Scy				wpa_printf(MSG_DEBUG,
450341618Scy					   "JSON: Invalid state for a number");
451341618Scy				goto fail;
452341618Scy			}
453341618Scy			break;
454341618Scy		default:
455341618Scy			wpa_printf(MSG_DEBUG,
456341618Scy				   "JSON: Unexpected JSON character: %c", *pos);
457341618Scy			goto fail;
458341618Scy		}
459341618Scy
460341618Scy		if (!root)
461341618Scy			root = token;
462341618Scy		if (!curr_token)
463341618Scy			curr_token = token;
464341618Scy	}
465341618Scy
466341618Scy	if (json_check_tree_state(root) < 0) {
467341618Scy		wpa_printf(MSG_DEBUG, "JSON: Incomplete token in the tree");
468341618Scy		goto fail;
469341618Scy	}
470341618Scy
471341618Scy	return root;
472341618Scyfail:
473341618Scy	wpa_printf(MSG_DEBUG, "JSON: Parsing failed");
474341618Scy	json_free(root);
475341618Scy	return NULL;
476341618Scy}
477341618Scy
478341618Scy
479341618Scyvoid json_free(struct json_token *json)
480341618Scy{
481341618Scy	if (!json)
482341618Scy		return;
483341618Scy	json_free(json->child);
484341618Scy	json_free(json->sibling);
485341618Scy	os_free(json->name);
486341618Scy	os_free(json->string);
487341618Scy	os_free(json);
488341618Scy}
489341618Scy
490341618Scy
491341618Scystruct json_token * json_get_member(struct json_token *json, const char *name)
492341618Scy{
493341618Scy	struct json_token *token, *ret = NULL;
494341618Scy
495341618Scy	if (!json || json->type != JSON_OBJECT)
496341618Scy		return NULL;
497341618Scy	/* Return last matching entry */
498341618Scy	for (token = json->child; token; token = token->sibling) {
499341618Scy		if (token->name && os_strcmp(token->name, name) == 0)
500341618Scy			ret = token;
501341618Scy	}
502341618Scy	return ret;
503341618Scy}
504341618Scy
505341618Scy
506341618Scystruct wpabuf * json_get_member_base64url(struct json_token *json,
507341618Scy					  const char *name)
508341618Scy{
509341618Scy	struct json_token *token;
510341618Scy	unsigned char *buf;
511341618Scy	size_t buflen;
512341618Scy	struct wpabuf *ret;
513341618Scy
514341618Scy	token = json_get_member(json, name);
515341618Scy	if (!token || token->type != JSON_STRING)
516341618Scy		return NULL;
517341618Scy	buf = base64_url_decode((const unsigned char *) token->string,
518341618Scy				os_strlen(token->string), &buflen);
519341618Scy	if (!buf)
520341618Scy		return NULL;
521341618Scy	ret = wpabuf_alloc_ext_data(buf, buflen);
522341618Scy	if (!ret)
523341618Scy		os_free(buf);
524341618Scy
525341618Scy	return ret;
526341618Scy}
527341618Scy
528341618Scy
529341618Scystatic const char * json_type_str(enum json_type type)
530341618Scy{
531341618Scy	switch (type) {
532341618Scy	case JSON_VALUE:
533341618Scy		return "VALUE";
534341618Scy	case JSON_OBJECT:
535341618Scy		return "OBJECT";
536341618Scy	case JSON_ARRAY:
537341618Scy		return "ARRAY";
538341618Scy	case JSON_STRING:
539341618Scy		return "STRING";
540341618Scy	case JSON_NUMBER:
541341618Scy		return "NUMBER";
542341618Scy	case JSON_BOOLEAN:
543341618Scy		return "BOOLEAN";
544341618Scy	case JSON_NULL:
545341618Scy		return "NULL";
546341618Scy	}
547341618Scy	return "??";
548341618Scy}
549341618Scy
550341618Scy
551341618Scystatic void json_print_token(struct json_token *token, int depth,
552341618Scy			     char *buf, size_t buflen)
553341618Scy{
554341618Scy	size_t len;
555341618Scy	int ret;
556341618Scy
557341618Scy	if (!token)
558341618Scy		return;
559341618Scy	len = os_strlen(buf);
560341618Scy	ret = os_snprintf(buf + len, buflen - len, "[%d:%s:%s]",
561341618Scy			  depth, json_type_str(token->type),
562341618Scy			  token->name ? token->name : "");
563341618Scy	if (os_snprintf_error(buflen - len, ret)) {
564341618Scy		buf[len] = '\0';
565341618Scy		return;
566341618Scy	}
567341618Scy	json_print_token(token->child, depth + 1, buf, buflen);
568341618Scy	json_print_token(token->sibling, depth, buf, buflen);
569341618Scy}
570341618Scy
571341618Scy
572341618Scyvoid json_print_tree(struct json_token *root, char *buf, size_t buflen)
573341618Scy{
574341618Scy	buf[0] = '\0';
575341618Scy	json_print_token(root, 1, buf, buflen);
576341618Scy}
577