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