]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/wpa/src/utils/json.c
Update hostapd/wpa_supplicant to 2.8 to fix multiple vulnerabilities.
[FreeBSD/FreeBSD.git] / contrib / wpa / src / utils / json.c
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
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                                                    data[i]);
55                         }
56                         break;
57                 }
58         }
59
60         *txt = '\0';
61 }
62
63
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
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
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
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
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                                         goto fail;
305                                 token->type = JSON_STRING;
306                                 token->string = str;
307                                 token->state = JSON_COMPLETED;
308                         } else if (curr_token->parent &&
309                                    curr_token->parent->type == JSON_ARRAY &&
310                                    curr_token->parent->state == JSON_STARTED &&
311                                    curr_token->state == JSON_EMPTY) {
312                                 curr_token->string = str;
313                                 curr_token->state = JSON_COMPLETED;
314                                 curr_token->type = JSON_STRING;
315                                 wpa_printf(MSG_MSGDUMP,
316                                            "JSON: String value: '%s'",
317                                            curr_token->string);
318                         } else if (curr_token->state == JSON_EMPTY) {
319                                 curr_token->type = JSON_VALUE;
320                                 curr_token->name = str;
321                                 curr_token->state = JSON_STARTED;
322                         } else if (curr_token->state == JSON_WAITING_VALUE) {
323                                 curr_token->string = str;
324                                 curr_token->state = JSON_COMPLETED;
325                                 curr_token->type = JSON_STRING;
326                                 wpa_printf(MSG_MSGDUMP,
327                                            "JSON: String value: '%s' = '%s'",
328                                            curr_token->name,
329                                            curr_token->string);
330                         } else {
331                                 wpa_printf(MSG_DEBUG,
332                                            "JSON: Invalid state for a string");
333                                 os_free(str);
334                                 goto fail;
335                         }
336                         break;
337                 case ' ':
338                 case '\t':
339                 case '\r':
340                 case '\n':
341                         /* ignore whitespace */
342                         break;
343                 case ':': /* name/value separator */
344                         if (!curr_token || curr_token->state != JSON_STARTED)
345                                 goto fail;
346                         curr_token->state = JSON_WAITING_VALUE;
347                         break;
348                 case ',': /* member separator */
349                         if (!curr_token)
350                                 goto fail;
351                         curr_token->sibling = json_alloc_token(&tokens);
352                         if (!curr_token->sibling)
353                                 goto fail;
354                         curr_token->sibling->parent = curr_token->parent;
355                         curr_token = curr_token->sibling;
356                         curr_token->state = JSON_EMPTY;
357                         break;
358                 case 't': /* true */
359                 case 'f': /* false */
360                 case 'n': /* null */
361                         if (!((end - pos >= 4 &&
362                                os_strncmp(pos, "true", 4) == 0) ||
363                               (end - pos >= 5 &&
364                                os_strncmp(pos, "false", 5) == 0) ||
365                               (end - pos >= 4 &&
366                                os_strncmp(pos, "null", 4) == 0))) {
367                                 wpa_printf(MSG_DEBUG,
368                                            "JSON: Invalid literal name");
369                                 goto fail;
370                         }
371                         if (!curr_token) {
372                                 token = json_alloc_token(&tokens);
373                                 if (!token)
374                                         goto fail;
375                                 curr_token = token;
376                         } else if (curr_token->state == JSON_WAITING_VALUE) {
377                                 wpa_printf(MSG_MSGDUMP,
378                                            "JSON: Literal name: '%s' = %c",
379                                            curr_token->name, *pos);
380                         } else if (curr_token->parent &&
381                                    curr_token->parent->type == JSON_ARRAY &&
382                                    curr_token->parent->state == JSON_STARTED &&
383                                    curr_token->state == JSON_EMPTY) {
384                                 wpa_printf(MSG_MSGDUMP,
385                                            "JSON: Literal name: %c", *pos);
386                         } else {
387                                 wpa_printf(MSG_DEBUG,
388                                            "JSON: Invalid state for a literal name");
389                                 goto fail;
390                         }
391                         switch (*pos) {
392                         case 't':
393                                 curr_token->type = JSON_BOOLEAN;
394                                 curr_token->number = 1;
395                                 pos += 3;
396                                 break;
397                         case 'f':
398                                 curr_token->type = JSON_BOOLEAN;
399                                 curr_token->number = 0;
400                                 pos += 4;
401                                 break;
402                         case 'n':
403                                 curr_token->type = JSON_NULL;
404                                 pos += 3;
405                                 break;
406                         }
407                         curr_token->state = JSON_COMPLETED;
408                         break;
409                 case '-':
410                 case '0':
411                 case '1':
412                 case '2':
413                 case '3':
414                 case '4':
415                 case '5':
416                 case '6':
417                 case '7':
418                 case '8':
419                 case '9':
420                         /* number */
421                         if (json_parse_number(&pos, end, &num) < 0)
422                                 goto fail;
423                         if (!curr_token) {
424                                 token = json_alloc_token(&tokens);
425                                 if (!token)
426                                         goto fail;
427                                 token->type = JSON_NUMBER;
428                                 token->number = num;
429                                 token->state = JSON_COMPLETED;
430                         } else if (curr_token->state == JSON_WAITING_VALUE) {
431                                 curr_token->number = num;
432                                 curr_token->state = JSON_COMPLETED;
433                                 curr_token->type = JSON_NUMBER;
434                                 wpa_printf(MSG_MSGDUMP,
435                                            "JSON: Number value: '%s' = '%d'",
436                                            curr_token->name,
437                                            curr_token->number);
438                         } else if (curr_token->parent &&
439                                    curr_token->parent->type == JSON_ARRAY &&
440                                    curr_token->parent->state == JSON_STARTED &&
441                                    curr_token->state == JSON_EMPTY) {
442                                 curr_token->number = num;
443                                 curr_token->state = JSON_COMPLETED;
444                                 curr_token->type = JSON_NUMBER;
445                                 wpa_printf(MSG_MSGDUMP,
446                                            "JSON: Number value: %d",
447                                            curr_token->number);
448                         } else {
449                                 wpa_printf(MSG_DEBUG,
450                                            "JSON: Invalid state for a number");
451                                 goto fail;
452                         }
453                         break;
454                 default:
455                         wpa_printf(MSG_DEBUG,
456                                    "JSON: Unexpected JSON character: %c", *pos);
457                         goto fail;
458                 }
459
460                 if (!root)
461                         root = token;
462                 if (!curr_token)
463                         curr_token = token;
464         }
465
466         if (json_check_tree_state(root) < 0) {
467                 wpa_printf(MSG_DEBUG, "JSON: Incomplete token in the tree");
468                 goto fail;
469         }
470
471         return root;
472 fail:
473         wpa_printf(MSG_DEBUG, "JSON: Parsing failed");
474         json_free(root);
475         return NULL;
476 }
477
478
479 void json_free(struct json_token *json)
480 {
481         if (!json)
482                 return;
483         json_free(json->child);
484         json_free(json->sibling);
485         os_free(json->name);
486         os_free(json->string);
487         os_free(json);
488 }
489
490
491 struct json_token * json_get_member(struct json_token *json, const char *name)
492 {
493         struct json_token *token, *ret = NULL;
494
495         if (!json || json->type != JSON_OBJECT)
496                 return NULL;
497         /* Return last matching entry */
498         for (token = json->child; token; token = token->sibling) {
499                 if (token->name && os_strcmp(token->name, name) == 0)
500                         ret = token;
501         }
502         return ret;
503 }
504
505
506 struct wpabuf * json_get_member_base64url(struct json_token *json,
507                                           const char *name)
508 {
509         struct json_token *token;
510         unsigned char *buf;
511         size_t buflen;
512         struct wpabuf *ret;
513
514         token = json_get_member(json, name);
515         if (!token || token->type != JSON_STRING)
516                 return NULL;
517         buf = base64_url_decode((const unsigned char *) token->string,
518                                 os_strlen(token->string), &buflen);
519         if (!buf)
520                 return NULL;
521         ret = wpabuf_alloc_ext_data(buf, buflen);
522         if (!ret)
523                 os_free(buf);
524
525         return ret;
526 }
527
528
529 static const char * json_type_str(enum json_type type)
530 {
531         switch (type) {
532         case JSON_VALUE:
533                 return "VALUE";
534         case JSON_OBJECT:
535                 return "OBJECT";
536         case JSON_ARRAY:
537                 return "ARRAY";
538         case JSON_STRING:
539                 return "STRING";
540         case JSON_NUMBER:
541                 return "NUMBER";
542         case JSON_BOOLEAN:
543                 return "BOOLEAN";
544         case JSON_NULL:
545                 return "NULL";
546         }
547         return "??";
548 }
549
550
551 static void json_print_token(struct json_token *token, int depth,
552                              char *buf, size_t buflen)
553 {
554         size_t len;
555         int ret;
556
557         if (!token)
558                 return;
559         len = os_strlen(buf);
560         ret = os_snprintf(buf + len, buflen - len, "[%d:%s:%s]",
561                           depth, json_type_str(token->type),
562                           token->name ? token->name : "");
563         if (os_snprintf_error(buflen - len, ret)) {
564                 buf[len] = '\0';
565                 return;
566         }
567         json_print_token(token->child, depth + 1, buf, buflen);
568         json_print_token(token->sibling, depth, buf, buflen);
569 }
570
571
572 void json_print_tree(struct json_token *root, char *buf, size_t buflen)
573 {
574         buf[0] = '\0';
575         json_print_token(root, 1, buf, buflen);
576 }