1 /*
2  * JavaScript Object Notation (JSON) parser (RFC7159)
3  * Copyright (c) 2017, Qualcomm Atheros, Inc.
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8 
9 #include "includes.h"
10 
11 #include "common.h"
12 #include "base64.h"
13 #include "json.h"
14 
15 #define JSON_MAX_DEPTH 10
16 #define JSON_MAX_TOKENS 500
17 
18 
json_escape_string(char * txt,size_t maxlen,const char * data,size_t len)19 void json_escape_string(char *txt, size_t maxlen, const char *data, size_t len)
20 {
21           char *end = txt + maxlen;
22           size_t i;
23 
24           for (i = 0; i < len; i++) {
25                     if (txt + 4 >= end)
26                               break;
27 
28                     switch (data[i]) {
29                     case '\"':
30                               *txt++ = '\\';
31                               *txt++ = '\"';
32                               break;
33                     case '\\':
34                               *txt++ = '\\';
35                               *txt++ = '\\';
36                               break;
37                     case '\n':
38                               *txt++ = '\\';
39                               *txt++ = 'n';
40                               break;
41                     case '\r':
42                               *txt++ = '\\';
43                               *txt++ = 'r';
44                               break;
45                     case '\t':
46                               *txt++ = '\\';
47                               *txt++ = 't';
48                               break;
49                     default:
50                               if (data[i] >= 32 && data[i] <= 126) {
51                                         *txt++ = data[i];
52                               } else {
53                                         txt += os_snprintf(txt, end - txt, "\\u%04x",
54                                                                (unsigned char) data[i]);
55                               }
56                               break;
57                     }
58           }
59 
60           *txt = '\0';
61 }
62 
63 
json_parse_string(const char ** json_pos,const char * end)64 static char * json_parse_string(const char **json_pos, const char *end)
65 {
66           const char *pos = *json_pos;
67           char *str, *spos, *s_end;
68           size_t max_len, buf_len;
69           u8 bin[2];
70 
71           pos++; /* skip starting quote */
72 
73           max_len = end - pos + 1;
74           buf_len = max_len > 10 ? 10 : max_len;
75           str = os_malloc(buf_len);
76           if (!str)
77                     return NULL;
78           spos = str;
79           s_end = str + buf_len;
80 
81           for (; pos < end; pos++) {
82                     if (buf_len < max_len && s_end - spos < 3) {
83                               char *tmp;
84                               int idx;
85 
86                               idx = spos - str;
87                               buf_len *= 2;
88                               if (buf_len > max_len)
89                                         buf_len = max_len;
90                               tmp = os_realloc(str, buf_len);
91                               if (!tmp)
92                                         goto fail;
93                               str = tmp;
94                               spos = str + idx;
95                               s_end = str + buf_len;
96                     }
97 
98                     switch (*pos) {
99                     case '\"': /* end string */
100                               *spos = '\0';
101                               /* caller will move to the next position */
102                               *json_pos = pos;
103                               return str;
104                     case '\\':
105                               pos++;
106                               if (pos >= end) {
107                                         wpa_printf(MSG_DEBUG,
108                                                      "JSON: Truncated \\ escape");
109                                         goto fail;
110                               }
111                               switch (*pos) {
112                               case '"':
113                               case '\\':
114                               case '/':
115                                         *spos++ = *pos;
116                                         break;
117                               case 'n':
118                                         *spos++ = '\n';
119                                         break;
120                               case 'r':
121                                         *spos++ = '\r';
122                                         break;
123                               case 't':
124                                         *spos++ = '\t';
125                                         break;
126                               case 'u':
127                                         if (end - pos < 5 ||
128                                             hexstr2bin(pos + 1, bin, 2) < 0 ||
129                                             bin[1] == 0x00) {
130                                                   wpa_printf(MSG_DEBUG,
131                                                                "JSON: Invalid \\u escape");
132                                                   goto fail;
133                                         }
134                                         if (bin[0] == 0x00) {
135                                                   *spos++ = bin[1];
136                                         } else {
137                                                   *spos++ = bin[0];
138                                                   *spos++ = bin[1];
139                                         }
140                                         pos += 4;
141                                         break;
142                               default:
143                                         wpa_printf(MSG_DEBUG,
144                                                      "JSON: Unknown escape '%c'", *pos);
145                                         goto fail;
146                               }
147                               break;
148                     default:
149                               *spos++ = *pos;
150                               break;
151                     }
152           }
153 
154 fail:
155           os_free(str);
156           return NULL;
157 }
158 
159 
json_parse_number(const char ** json_pos,const char * end,int * ret_val)160 static int json_parse_number(const char **json_pos, const char *end,
161                                    int *ret_val)
162 {
163           const char *pos = *json_pos;
164           size_t len;
165           char *str;
166 
167           for (; pos < end; pos++) {
168                     if (*pos != '-' && (*pos < '0' || *pos > '9')) {
169                               pos--;
170                               break;
171                     }
172           }
173           if (pos == end)
174                     pos--;
175           if (pos < *json_pos)
176                     return -1;
177           len = pos - *json_pos + 1;
178           str = os_malloc(len + 1);
179           if (!str)
180                     return -1;
181           os_memcpy(str, *json_pos, len);
182           str[len] = '\0';
183 
184           *ret_val = atoi(str);
185           os_free(str);
186           *json_pos = pos;
187           return 0;
188 }
189 
190 
json_check_tree_state(struct json_token * token)191 static int json_check_tree_state(struct json_token *token)
192 {
193           if (!token)
194                     return 0;
195           if (json_check_tree_state(token->child) < 0 ||
196               json_check_tree_state(token->sibling) < 0)
197                     return -1;
198           if (token->state != JSON_COMPLETED) {
199                     wpa_printf(MSG_DEBUG,
200                                  "JSON: Unexpected token state %d (name=%s type=%d)",
201                                  token->state, token->name ? token->name : "N/A",
202                                  token->type);
203                     return -1;
204           }
205           return 0;
206 }
207 
208 
json_alloc_token(unsigned int * tokens)209 static struct json_token * json_alloc_token(unsigned int *tokens)
210 {
211           (*tokens)++;
212           if (*tokens > JSON_MAX_TOKENS) {
213                     wpa_printf(MSG_DEBUG, "JSON: Maximum token limit exceeded");
214                     return NULL;
215           }
216           return os_zalloc(sizeof(struct json_token));
217 }
218 
219 
json_parse(const char * data,size_t data_len)220 struct json_token * json_parse(const char *data, size_t data_len)
221 {
222           struct json_token *root = NULL, *curr_token = NULL, *token = NULL;
223           const char *pos, *end;
224           char *str;
225           int num;
226           unsigned int depth = 0;
227           unsigned int tokens = 0;
228 
229           pos = data;
230           end = data + data_len;
231 
232           for (; pos < end; pos++) {
233                     switch (*pos) {
234                     case '[': /* start array */
235                     case '{': /* start object */
236                               if (!curr_token) {
237                                         token = json_alloc_token(&tokens);
238                                         if (!token)
239                                                   goto fail;
240                                         if (!root)
241                                                   root = token;
242                               } else if (curr_token->state == JSON_WAITING_VALUE) {
243                                         token = curr_token;
244                               } else if (curr_token->parent &&
245                                            curr_token->parent->type == JSON_ARRAY &&
246                                            curr_token->parent->state == JSON_STARTED &&
247                                            curr_token->state == JSON_EMPTY) {
248                                         token = curr_token;
249                               } else {
250                                         wpa_printf(MSG_DEBUG,
251                                                      "JSON: Invalid state for start array/object");
252                                         goto fail;
253                               }
254                               depth++;
255                               if (depth > JSON_MAX_DEPTH) {
256                                         wpa_printf(MSG_DEBUG,
257                                                      "JSON: Max depth exceeded");
258                                         goto fail;
259                               }
260                               token->type = *pos == '[' ? JSON_ARRAY : JSON_OBJECT;
261                               token->state = JSON_STARTED;
262                               token->child = json_alloc_token(&tokens);
263                               if (!token->child)
264                                         goto fail;
265                               curr_token = token->child;
266                               curr_token->parent = token;
267                               curr_token->state = JSON_EMPTY;
268                               break;
269                     case ']': /* end array */
270                     case '}': /* end object */
271                               if (!curr_token || !curr_token->parent ||
272                                   curr_token->parent->state != JSON_STARTED) {
273                                         wpa_printf(MSG_DEBUG,
274                                                      "JSON: Invalid state for end array/object");
275                                         goto fail;
276                               }
277                               depth--;
278                               curr_token = curr_token->parent;
279                               if ((*pos == ']' &&
280                                    curr_token->type != JSON_ARRAY) ||
281                                   (*pos == '}' &&
282                                    curr_token->type != JSON_OBJECT)) {
283                                         wpa_printf(MSG_DEBUG,
284                                                      "JSON: Array/Object mismatch");
285                                         goto fail;
286                               }
287                               if (curr_token->child->state == JSON_EMPTY &&
288                                   !curr_token->child->child &&
289                                   !curr_token->child->sibling) {
290                                         /* Remove pending child token since the
291                                          * array/object was empty. */
292                                         json_free(curr_token->child);
293                                         curr_token->child = NULL;
294                               }
295                               curr_token->state = JSON_COMPLETED;
296                               break;
297                     case '\"': /* string */
298                               str = json_parse_string(&pos, end);
299                               if (!str)
300                                         goto fail;
301                               if (!curr_token) {
302                                         token = json_alloc_token(&tokens);
303                                         if (!token) {
304                                                   os_free(str);
305                                                   goto fail;
306                                         }
307                                         token->type = JSON_STRING;
308                                         token->string = str;
309                                         token->state = JSON_COMPLETED;
310                               } else if (curr_token->parent &&
311                                            curr_token->parent->type == JSON_ARRAY &&
312                                            curr_token->parent->state == JSON_STARTED &&
313                                            curr_token->state == JSON_EMPTY) {
314                                         curr_token->string = str;
315                                         curr_token->state = JSON_COMPLETED;
316                                         curr_token->type = JSON_STRING;
317                                         wpa_printf(MSG_MSGDUMP,
318                                                      "JSON: String value: '%s'",
319                                                      curr_token->string);
320                               } else if (curr_token->state == JSON_EMPTY) {
321                                         curr_token->type = JSON_VALUE;
322                                         curr_token->name = str;
323                                         curr_token->state = JSON_STARTED;
324                               } else if (curr_token->state == JSON_WAITING_VALUE) {
325                                         curr_token->string = str;
326                                         curr_token->state = JSON_COMPLETED;
327                                         curr_token->type = JSON_STRING;
328                                         wpa_printf(MSG_MSGDUMP,
329                                                      "JSON: String value: '%s' = '%s'",
330                                                      curr_token->name,
331                                                      curr_token->string);
332                               } else {
333                                         wpa_printf(MSG_DEBUG,
334                                                      "JSON: Invalid state for a string");
335                                         os_free(str);
336                                         goto fail;
337                               }
338                               break;
339                     case ' ':
340                     case '\t':
341                     case '\r':
342                     case '\n':
343                               /* ignore whitespace */
344                               break;
345                     case ':': /* name/value separator */
346                               if (!curr_token || curr_token->state != JSON_STARTED)
347                                         goto fail;
348                               curr_token->state = JSON_WAITING_VALUE;
349                               break;
350                     case ',': /* member separator */
351                               if (!curr_token)
352                                         goto fail;
353                               curr_token->sibling = json_alloc_token(&tokens);
354                               if (!curr_token->sibling)
355                                         goto fail;
356                               curr_token->sibling->parent = curr_token->parent;
357                               curr_token = curr_token->sibling;
358                               curr_token->state = JSON_EMPTY;
359                               break;
360                     case 't': /* true */
361                     case 'f': /* false */
362                     case 'n': /* null */
363                               if (!((end - pos >= 4 &&
364                                      os_strncmp(pos, "true", 4) == 0) ||
365                                     (end - pos >= 5 &&
366                                      os_strncmp(pos, "false", 5) == 0) ||
367                                     (end - pos >= 4 &&
368                                      os_strncmp(pos, "null", 4) == 0))) {
369                                         wpa_printf(MSG_DEBUG,
370                                                      "JSON: Invalid literal name");
371                                         goto fail;
372                               }
373                               if (!curr_token) {
374                                         token = json_alloc_token(&tokens);
375                                         if (!token)
376                                                   goto fail;
377                                         curr_token = token;
378                               } else if (curr_token->state == JSON_WAITING_VALUE) {
379                                         wpa_printf(MSG_MSGDUMP,
380                                                      "JSON: Literal name: '%s' = %c",
381                                                      curr_token->name, *pos);
382                               } else if (curr_token->parent &&
383                                            curr_token->parent->type == JSON_ARRAY &&
384                                            curr_token->parent->state == JSON_STARTED &&
385                                            curr_token->state == JSON_EMPTY) {
386                                         wpa_printf(MSG_MSGDUMP,
387                                                      "JSON: Literal name: %c", *pos);
388                               } else {
389                                         wpa_printf(MSG_DEBUG,
390                                                      "JSON: Invalid state for a literal name");
391                                         goto fail;
392                               }
393                               switch (*pos) {
394                               case 't':
395                                         curr_token->type = JSON_BOOLEAN;
396                                         curr_token->number = 1;
397                                         pos += 3;
398                                         break;
399                               case 'f':
400                                         curr_token->type = JSON_BOOLEAN;
401                                         curr_token->number = 0;
402                                         pos += 4;
403                                         break;
404                               case 'n':
405                                         curr_token->type = JSON_NULL;
406                                         pos += 3;
407                                         break;
408                               }
409                               curr_token->state = JSON_COMPLETED;
410                               break;
411                     case '-':
412                     case '0':
413                     case '1':
414                     case '2':
415                     case '3':
416                     case '4':
417                     case '5':
418                     case '6':
419                     case '7':
420                     case '8':
421                     case '9':
422                               /* number */
423                               if (json_parse_number(&pos, end, &num) < 0)
424                                         goto fail;
425                               if (!curr_token) {
426                                         token = json_alloc_token(&tokens);
427                                         if (!token)
428                                                   goto fail;
429                                         token->type = JSON_NUMBER;
430                                         token->number = num;
431                                         token->state = JSON_COMPLETED;
432                               } else if (curr_token->state == JSON_WAITING_VALUE) {
433                                         curr_token->number = num;
434                                         curr_token->state = JSON_COMPLETED;
435                                         curr_token->type = JSON_NUMBER;
436                                         wpa_printf(MSG_MSGDUMP,
437                                                      "JSON: Number value: '%s' = '%d'",
438                                                      curr_token->name,
439                                                      curr_token->number);
440                               } else if (curr_token->parent &&
441                                            curr_token->parent->type == JSON_ARRAY &&
442                                            curr_token->parent->state == JSON_STARTED &&
443                                            curr_token->state == JSON_EMPTY) {
444                                         curr_token->number = num;
445                                         curr_token->state = JSON_COMPLETED;
446                                         curr_token->type = JSON_NUMBER;
447                                         wpa_printf(MSG_MSGDUMP,
448                                                      "JSON: Number value: %d",
449                                                      curr_token->number);
450                               } else {
451                                         wpa_printf(MSG_DEBUG,
452                                                      "JSON: Invalid state for a number");
453                                         goto fail;
454                               }
455                               break;
456                     default:
457                               wpa_printf(MSG_DEBUG,
458                                            "JSON: Unexpected JSON character: %c", *pos);
459                               goto fail;
460                     }
461 
462                     if (!root)
463                               root = token;
464                     if (!curr_token)
465                               curr_token = token;
466           }
467 
468           if (json_check_tree_state(root) < 0) {
469                     wpa_printf(MSG_DEBUG, "JSON: Incomplete token in the tree");
470                     goto fail;
471           }
472 
473           return root;
474 fail:
475           wpa_printf(MSG_DEBUG, "JSON: Parsing failed");
476           json_free(root);
477           return NULL;
478 }
479 
480 
json_free(struct json_token * json)481 void json_free(struct json_token *json)
482 {
483           if (!json)
484                     return;
485           json_free(json->child);
486           json_free(json->sibling);
487           os_free(json->name);
488           os_free(json->string);
489           os_free(json);
490 }
491 
492 
json_get_member(struct json_token * json,const char * name)493 struct json_token * json_get_member(struct json_token *json, const char *name)
494 {
495           struct json_token *token, *ret = NULL;
496 
497           if (!json || json->type != JSON_OBJECT)
498                     return NULL;
499           /* Return last matching entry */
500           for (token = json->child; token; token = token->sibling) {
501                     if (token->name && os_strcmp(token->name, name) == 0)
502                               ret = token;
503           }
504           return ret;
505 }
506 
507 
json_get_member_base64url(struct json_token * json,const char * name)508 struct wpabuf * json_get_member_base64url(struct json_token *json,
509                                                     const char *name)
510 {
511           struct json_token *token;
512           unsigned char *buf;
513           size_t buflen;
514           struct wpabuf *ret;
515 
516           token = json_get_member(json, name);
517           if (!token || token->type != JSON_STRING)
518                     return NULL;
519           buf = base64_url_decode(token->string, os_strlen(token->string),
520                                         &buflen);
521           if (!buf)
522                     return NULL;
523           ret = wpabuf_alloc_ext_data(buf, buflen);
524           if (!ret)
525                     os_free(buf);
526 
527           return ret;
528 }
529 
530 
json_get_member_base64(struct json_token * json,const char * name)531 struct wpabuf * json_get_member_base64(struct json_token *json,
532                                                const char *name)
533 {
534           struct json_token *token;
535           unsigned char *buf;
536           size_t buflen;
537           struct wpabuf *ret;
538 
539           token = json_get_member(json, name);
540           if (!token || token->type != JSON_STRING)
541                     return NULL;
542           buf = base64_decode(token->string, os_strlen(token->string), &buflen);
543           if (!buf)
544                     return NULL;
545           ret = wpabuf_alloc_ext_data(buf, buflen);
546           if (!ret)
547                     os_free(buf);
548 
549           return ret;
550 }
551 
552 
json_type_str(enum json_type type)553 static const char * json_type_str(enum json_type type)
554 {
555           switch (type) {
556           case JSON_VALUE:
557                     return "VALUE";
558           case JSON_OBJECT:
559                     return "OBJECT";
560           case JSON_ARRAY:
561                     return "ARRAY";
562           case JSON_STRING:
563                     return "STRING";
564           case JSON_NUMBER:
565                     return "NUMBER";
566           case JSON_BOOLEAN:
567                     return "BOOLEAN";
568           case JSON_NULL:
569                     return "NULL";
570           }
571           return "??";
572 }
573 
574 
json_print_token(struct json_token * token,int depth,char * buf,size_t buflen)575 static void json_print_token(struct json_token *token, int depth,
576                                    char *buf, size_t buflen)
577 {
578           size_t len;
579           int ret;
580 
581           if (!token)
582                     return;
583           len = os_strlen(buf);
584           ret = os_snprintf(buf + len, buflen - len, "[%d:%s:%s]",
585                                 depth, json_type_str(token->type),
586                                 token->name ? token->name : "");
587           if (os_snprintf_error(buflen - len, ret)) {
588                     buf[len] = '\0';
589                     return;
590           }
591           json_print_token(token->child, depth + 1, buf, buflen);
592           json_print_token(token->sibling, depth, buf, buflen);
593 }
594 
595 
json_print_tree(struct json_token * root,char * buf,size_t buflen)596 void json_print_tree(struct json_token *root, char *buf, size_t buflen)
597 {
598           buf[0] = '\0';
599           json_print_token(root, 1, buf, buflen);
600 }
601 
602 
json_add_int(struct wpabuf * json,const char * name,int val)603 void json_add_int(struct wpabuf *json, const char *name, int val)
604 {
605           wpabuf_printf(json, "\"%s\":%d", name, val);
606 }
607 
608 
json_add_string(struct wpabuf * json,const char * name,const char * val)609 void json_add_string(struct wpabuf *json, const char *name, const char *val)
610 {
611           wpabuf_printf(json, "\"%s\":\"%s\"", name, val);
612 }
613 
614 
json_add_string_escape(struct wpabuf * json,const char * name,const void * val,size_t len)615 int json_add_string_escape(struct wpabuf *json, const char *name,
616                                  const void *val, size_t len)
617 {
618           char *tmp;
619           size_t tmp_len = 6 * len + 1;
620 
621           tmp = os_malloc(tmp_len);
622           if (!tmp)
623                     return -1;
624           json_escape_string(tmp, tmp_len, val, len);
625           json_add_string(json, name, tmp);
626           bin_clear_free(tmp, tmp_len);
627           return 0;
628 }
629 
630 
json_add_base64url(struct wpabuf * json,const char * name,const void * val,size_t len)631 int json_add_base64url(struct wpabuf *json, const char *name, const void *val,
632                            size_t len)
633 {
634           char *b64;
635 
636           b64 = base64_url_encode(val, len, NULL);
637           if (!b64)
638                     return -1;
639           json_add_string(json, name, b64);
640           os_free(b64);
641           return 0;
642 }
643 
644 
json_add_base64(struct wpabuf * json,const char * name,const void * val,size_t len)645 int json_add_base64(struct wpabuf *json, const char *name, const void *val,
646                         size_t len)
647 {
648           char *b64;
649 
650           b64 = base64_encode_no_lf(val, len, NULL);
651           if (!b64)
652                     return -1;
653           json_add_string(json, name, b64);
654           os_free(b64);
655           return 0;
656 }
657 
658 
json_start_object(struct wpabuf * json,const char * name)659 void json_start_object(struct wpabuf *json, const char *name)
660 {
661           if (name)
662                     wpabuf_printf(json, "\"%s\":", name);
663           wpabuf_put_u8(json, '{');
664 }
665 
666 
json_end_object(struct wpabuf * json)667 void json_end_object(struct wpabuf *json)
668 {
669           wpabuf_put_u8(json, '}');
670 }
671 
672 
json_start_array(struct wpabuf * json,const char * name)673 void json_start_array(struct wpabuf *json, const char *name)
674 {
675           if (name)
676                     wpabuf_printf(json, "\"%s\":", name);
677           wpabuf_put_u8(json, '[');
678 }
679 
680 
json_end_array(struct wpabuf * json)681 void json_end_array(struct wpabuf *json)
682 {
683           wpabuf_put_u8(json, ']');
684 }
685 
686 
json_value_sep(struct wpabuf * json)687 void json_value_sep(struct wpabuf *json)
688 {
689           wpabuf_put_u8(json, ',');
690 }
691