1/* 2 * lws-minimal-secure-streams-policy2c 3 * 4 * Written in 2010-2021 by Andy Green <andy@warmcat.com> 5 * 6 * This file is made available under the Creative Commons CC0 1.0 7 * Universal Public Domain Dedication. 8 * 9 * 10 * This reads policy JSON on stdin and emits it as compileable 11 * C structs. 12 * 13 * It's useful if your platform is too space-constrained for a 14 * JSON policy and needs to build a static policy in C via 15 * LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY... this way you can 16 * still create and maintain the JSON policy but implement it directly 17 * as C structs in your code. 18 */ 19 20#include <libwebsockets.h> 21#include <string.h> 22#include <signal.h> 23#include <stdio.h> 24#include <assert.h> 25 26static int interrupted, bad = 1; 27 28 29static void 30sigint_handler(int sig) 31{ 32 interrupted = 1; 33} 34 35struct aggstr { 36 struct aggstr *next; 37 38 const char *orig; 39 size_t offset; 40}; 41 42static struct aggstr *rbomap, /* retry / backoff object map */ 43 *trustmap, /* trust store map */ 44 *certmap; /* x.509 cert map */ 45static size_t last_offset; 46 47 48 49static const char * 50purify_csymbol(const char *in, char *temp, size_t templen) 51{ 52 const char *otemp = temp; 53 54 assert (strlen(in) < templen); 55 56 while (*in) { 57 if ((*in >= 'a' && *in <= 'z') || (*in >= 'A' && *in <= 'Z') || 58 (*in >= '0' && *in <= '9')) 59 *temp++ = *in; 60 else 61 *temp++ = '_'; 62 63 in++; 64 } 65 66 *temp = '\0'; 67 68 return otemp; 69} 70 71int main(int argc, const char **argv) 72{ 73 const lws_ss_policy_t *pol, *lastpol = NULL; 74 struct lws_context_creation_info info; 75 size_t json_size = 0, est = 0; 76 struct lws_context *context; 77 const lws_ss_auth_t *auth; 78 char prev[128], curr[128]; 79 int unique_rbo = 0, m, n; 80 char buf[64], buf1[64]; 81 lws_ss_metadata_t *md; 82 struct aggstr *a, *a1; 83 84 signal(SIGINT, sigint_handler); 85 86 memset(&info, 0, sizeof info); 87 lws_cmdline_option_handle_builtin(argc, argv, &info); 88 89 lwsl_user("LWS secure streams policy2c [-d<verb>]\n"); 90 91 info.fd_limit_per_thread = 1 + 6 + 1; 92 info.port = CONTEXT_PORT_NO_LISTEN; 93 94 info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | 95 LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; 96 97 /* create the context */ 98 99 context = lws_create_context(&info); 100 if (!context) { 101 lwsl_err("lws init failed\n"); 102 return 1; 103 } 104 105 lws_ss_policy_parse_begin(context, 0); 106 107 printf("/*\n * Autogenerated from the following JSON policy\n */\n\n#if 0\n"); 108 109 do { 110 int m, n = (int)read(0, buf, sizeof(buf)); 111 112 if (n < 1) 113 break; 114 115 m = lws_ss_policy_parse(context, (uint8_t *)buf, (size_t)n); 116 117 printf("%.*s", n, buf); 118 json_size += (unsigned int)n; 119 120 if (m < 0 && m != LEJP_CONTINUE) { 121 lwsl_err("%s: policy parse failed... lws has WITH_ROLEs" 122 "for what's in the JSON?\n", __func__); 123 goto bail; 124 } 125 } while (1); 126 127 printf("\n\n Original JSON size: %zu\n#endif\n\n", json_size); 128 129 lwsl_notice("%s: parsed JSON\n", __func__); 130 131 /* 132 * Well, this is fun, isn't it... we have parsed the JSON into in-memory 133 * policy objects, and it has set the context policy pointer to the head 134 * of those but has not set the new policy (which would free the x.509). 135 * 136 * We want to walk the streamtype list first discovering unique objects 137 * and strings referenced there and emitting them compactly as C data, 138 * and then second to emit the streamtype linked-list referring to those 139 * objects. 140 * 141 * For const strings, we aggregate them and avoid generating extra 142 * pointers by encoding the reference as &_lws_ss_staticpol_str[xxx] 143 * where xxx is the fixed offset in the aggregated monster-string. When 144 * doing that, we keep a map of original pointers to offsets. 145 * 146 * Although we want to minimize memory used by the emitted C, we don't 147 * have to sweat memory during this conversion since it's happening on a 148 * PC 149 */ 150 151 pol = lws_ss_policy_get(context); 152 153 while (pol) { 154 155 /* 156 * Walk the metadata list gathering strings and issuing the 157 * C struct 158 */ 159 160 md = pol->metadata; 161 162 if (md) { 163 int idx = 0; 164 165 printf("\nstatic const lws_ss_metadata_t "); 166 167 prev[0] = '\0'; 168 md = pol->metadata; 169 while (md) { 170 171 est += sizeof(lws_ss_metadata_t); 172 173 lws_snprintf(curr, sizeof(curr), "_md_%s_%s", 174 purify_csymbol(pol->streamtype, buf, 175 sizeof(buf)), 176 purify_csymbol(md->name, buf1, 177 sizeof(buf1))); 178 179 printf("%s = {\n", curr); 180 if (prev[0]) 181 printf("\t.next = (void *)&%s, \n", prev); 182 183 printf("\t.name = \"%s\",\n", (const char *)md->name); 184 if (md->value__may_own_heap) { 185 printf("\t.value__may_own_heap = (void *)\"%s\",\n", 186 (const char *)md->value__may_own_heap); 187 printf("\t.value_length = 0x%x,\n", 188 (unsigned int)strlen( 189 (const char *)md->value__may_own_heap)); 190 } 191 192 printf("\t.length = %d,\n", idx++); // md->length); 193 printf("\t.value_is_http_token = 0x%x,\n", 194 (unsigned int)md->value_is_http_token); 195 printf("}"); 196 if (md->next) 197 printf(",\n"); 198 199 lws_strncpy(prev, curr, sizeof(prev)); 200 201 md = md->next; 202 } 203 204 printf(";\n\n"); 205 } 206 207 /* 208 * Create unique retry policies... have we seen this guy? 209 */ 210 211 if (pol->retry_bo) { 212 a = rbomap; 213 while (a) { 214 if (a->orig == (const char *)pol->retry_bo) 215 break; 216 217 a = a->next; 218 } 219 220 if (!a) { 221 222 /* We haven't seen it before and need to create it */ 223 224 a = malloc(sizeof(*a)); 225 if (!a) 226 goto bail; 227 a->next = rbomap; 228 a->offset = (unsigned int)unique_rbo++; 229 a->orig = (const char *)pol->retry_bo; 230 rbomap = a; 231 232 printf("static const uint32_t _rbo_bo_%zu[] = {\n", 233 a->offset); 234 for (n = 0; n < pol->retry_bo->retry_ms_table_count; n++) 235 printf(" %u, ", (unsigned int) 236 pol->retry_bo->retry_ms_table[n]); 237 238 est += sizeof(uint32_t) * 239 pol->retry_bo->retry_ms_table_count; 240 241 printf("\n};\nstatic const " 242 "lws_retry_bo_t _rbo_%zu = {\n", a->offset); 243 244 printf("\t.retry_ms_table = _rbo_bo_%zu,\n", 245 a->offset); 246 printf("\t.retry_ms_table_count = %u,\n", 247 pol->retry_bo->retry_ms_table_count); 248 printf("\t.conceal_count = %u,\n", 249 pol->retry_bo->conceal_count); 250 printf("\t.secs_since_valid_ping = %u,\n", 251 pol->retry_bo->secs_since_valid_ping); 252 printf("\t.secs_since_valid_hangup = %u,\n", 253 pol->retry_bo->secs_since_valid_hangup); 254 printf("\t.jitter_percent = %u,\n", 255 pol->retry_bo->jitter_percent); 256 printf("};\n"); 257 258 est += sizeof(lws_retry_bo_t); 259 } 260 } 261 262 /* 263 * How about his trust store, it's new to us? 264 */ 265 266 if (pol->trust.store) { 267 a = trustmap; 268 while (a) { 269 if (a->orig == (const char *)pol->trust.store) 270 break; 271 272 a = a->next; 273 } 274 275 if (!a) { 276 277 /* it's new to us... */ 278 279 a = malloc(sizeof(*a)); 280 if (!a) 281 goto bail; 282 a->next = trustmap; 283 a->offset = 0; /* don't care, just track seen */ 284 a->orig = (const char *)pol->trust.store; 285 trustmap = a; 286 287 /* 288 * Have a look through his x.509 stack... 289 * any that're new to us? 290 */ 291 292 for (n = 0; n < pol->trust.store->count; n++) { 293 if (!pol->trust.store->ssx509[n]) 294 continue; 295 a1 = certmap; 296 while (a1) { 297 if (a1->orig == (const char *)pol->trust.store->ssx509[n]) 298 break; 299 a1 = a1->next; 300 } 301 302 if (!a1) { 303 /* 304 * This x.509 cert is new to us... 305 * let's capture the DER 306 */ 307 308 a1 = malloc(sizeof(*a1)); 309 if (!a1) 310 goto bail; 311 a1->next = certmap; 312 a1->offset = 0; /* don't care, just track seen */ 313 a1->orig = (const char *)pol->trust.store->ssx509[n]; 314 certmap = a1; 315 316 printf("static const uint8_t _ss_der_%s[] = {\n", 317 purify_csymbol(pol->trust.store->ssx509[n]->vhost_name, 318 buf, sizeof(buf))); 319 320 for (m = 0; m < (int)pol->trust.store->ssx509[n]->ca_der_len; m++) { 321 if ((m & 7) == 0) 322 printf("\t/* 0x%3x */ ", m); 323 324 printf("0x%02X, ", pol->trust.store->ssx509[n]->ca_der[m]); 325 if ((m & 7) == 7) 326 printf("\n"); 327 } 328 329 printf("\n};\nstatic const lws_ss_x509_t _ss_x509_%s = {\n", 330 purify_csymbol(pol->trust.store->ssx509[n]->vhost_name, 331 buf, sizeof(buf))); 332 printf("\t.vhost_name = \"%s\",\n", pol->trust.store->ssx509[n]->vhost_name); 333 printf("\t.ca_der = _ss_der_%s,\n", 334 purify_csymbol(pol->trust.store->ssx509[n]->vhost_name, 335 buf, sizeof(buf))); 336 printf("\t.ca_der_len = %zu,\n", pol->trust.store->ssx509[n]->ca_der_len); 337 printf("};\n"); 338 339 est += sizeof(lws_ss_x509_t) + pol->trust.store->ssx509[n]->ca_der_len; 340 } 341 342 } 343 344 345 printf("static const lws_ss_trust_store_t _ss_ts_%s = {\n", 346 purify_csymbol(pol->trust.store->name, 347 buf, sizeof(buf))); 348 349 printf("\t.name = \"%s\",\n", pol->trust.store->name); 350 printf("\t.count = %d,\n", pol->trust.store->count); 351 printf("\t.ssx509 = {\n"); 352 353 for (n = pol->trust.store->count - 1; n >= 0 ; n--) 354 printf("\t\t&_ss_x509_%s,\n", 355 pol->trust.store->ssx509[n]->vhost_name); 356 357 printf("\t}\n};\n"); 358 359 est += sizeof(lws_ss_trust_store_t); 360 361 } 362 } 363 364 pol = pol->next; 365 } 366 367 368 /* dump any streamtype's http resp map */ 369 370 pol = lws_ss_policy_get(context); 371 m = 0; 372 373 while (pol) { 374 375 lws_snprintf(curr, sizeof(curr), "_ssp_%s", 376 purify_csymbol(pol->streamtype, buf, sizeof(buf))); 377 378 /* if relevant, dump http resp map */ 379 380 switch (pol->protocol) { 381 case LWSSSP_H1: 382 case LWSSSP_H2: 383 case LWSSSP_WS: 384 385 if (!pol->u.http.count_respmap) 386 break; 387 388 if (!m) 389 printf("\nstatic const lws_ss_http_respmap_t "); 390 else 391 printf(",\n"); 392 m++; 393 394 printf("%s_http_respmap[] = {\n", curr); 395 for (n = 0; n < pol->u.http.count_respmap; n++) { 396 printf("\t{ %d, 0x%x },\n", 397 pol->u.http.respmap[n].resp, 398 pol->u.http.respmap[n].state); 399 400 est += sizeof(lws_ss_http_respmap_t); 401 } 402 printf("}"); 403 break; 404 } 405 406 pol = pol->next; 407 } 408 409 if (m) 410 printf(";\n"); 411 412 /* 413 * The auth map 414 */ 415 416 auth = lws_ss_auth_get(context); 417 if (auth) 418 printf("\nstatic const lws_ss_auth_t "); 419 prev[0] = '\0'; 420 421 while (auth) { 422 lws_snprintf(curr, sizeof(curr), "_ssau_%s", 423 purify_csymbol(auth->name, buf, sizeof(buf))); 424 425 printf("%s = {\n", curr); 426 if (prev[0]) 427 printf("\t.next = (void *)&%s,\n", prev); 428 429 printf("\t.name = \"%s\",\n", auth->name); 430 printf("\t.type= \"%s\",\n", auth->type); 431 printf("\t.streamtype = \"%s\",\n", auth->streamtype); 432 printf("\t.blob_index = %d,\n", auth->blob_index); 433 printf("}"); 434 if (auth->next) 435 printf(","); 436 else 437 printf(";"); 438 printf("\n"); 439 440 lws_strncpy(prev, curr, sizeof(prev)); 441 442 auth = auth->next; 443 } 444 445 if (lws_ss_auth_get(context)) 446 printf("\n"); 447 448 /* 449 * The streamtypes 450 */ 451 452 pol = lws_ss_policy_get(context); 453 454 printf("\nstatic const lws_ss_policy_t "); 455 prev[0] = '\0'; 456 457 while (pol) { 458 459 est += sizeof(*pol); 460 461 lws_snprintf(curr, sizeof(curr), "_ssp_%s", 462 purify_csymbol(pol->streamtype, buf, sizeof(buf))); 463 464 printf("%s = {\n", curr); 465 466 if (prev[0]) 467 printf("\t.next = (void *)&%s,\n", prev); 468 469 printf("\t.streamtype = \"%s\",\n", pol->streamtype); 470 if (pol->endpoint) 471 printf("\t.endpoint = \"%s\",\n", pol->endpoint); 472 if (pol->rideshare_streamtype) 473 printf("\t.rideshare_streamtype = \"%s\",\n", 474 pol->rideshare_streamtype); 475 if (pol->payload_fmt) 476 printf("\t.payload_fmt = \"%s\",\n", 477 pol->payload_fmt); 478 if (pol->socks5_proxy) 479 printf("\t.socks5_proxy = \"%s\",\n", 480 pol->socks5_proxy); 481 482 if (pol->auth) 483 printf("\t.auth = &_ssau_%s,\n", 484 purify_csymbol(pol->auth->name, buf, sizeof(buf))); 485 486 { 487 lws_ss_metadata_t *nv = pol->metadata, *last = NULL; 488 489 while (nv) { 490 last = nv; 491 nv = nv->next; 492 } 493 if (pol->metadata) 494 printf("\t.metadata = (void *)&_md_%s_%s,\n", 495 purify_csymbol(pol->streamtype, buf, sizeof(buf)), 496 purify_csymbol(last->name, buf1, sizeof(buf1))); 497 } 498 499 500 switch (pol->protocol) { 501 case LWSSSP_H1: 502 case LWSSSP_H2: 503 case LWSSSP_WS: 504 505 printf("\t.u = {\n\t\t.http = {\n"); 506 507 if (pol->u.http.method) 508 printf("\t\t\t.method = \"%s\",\n", 509 pol->u.http.method); 510 if (pol->u.http.url) 511 printf("\t\t\t.url = \"%s\",\n", 512 pol->u.http.url); 513 if (pol->u.http.multipart_name) 514 printf("\t\t\t.multipart_name = \"%s\",\n", 515 pol->u.http.multipart_name); 516 if (pol->u.http.multipart_filename) 517 printf("\t\t\t.multipart_filename = \"%s\",\n", 518 pol->u.http.multipart_filename); 519 if (pol->u.http.multipart_content_type) 520 printf("\t\t\t.multipart_content_type = \"%s\",\n", 521 pol->u.http.multipart_content_type); 522 if (pol->u.http.auth_preamble) 523 printf("\t\t\t.auth_preamble = \"%s\",\n", 524 pol->u.http.auth_preamble); 525 526 if (pol->u.http.respmap) { 527 printf("\t\t\t.respmap = (void *)&%s_http_respmap,\n", 528 curr); 529 printf("\t\t\t.count_respmap = %d,\n", 530 pol->u.http.count_respmap); 531 } 532 533 if (pol->u.http.blob_header[0]) { 534 printf("\t\t\t.blob_header = {\n"); 535 for (n = 0; n < (int)LWS_ARRAY_SIZE(pol->u.http.blob_header); n++) 536 if (pol->u.http.blob_header[n]) 537 printf("\t\t\t\t\"%s\",\n", 538 pol->u.http.blob_header[n]); 539 540 printf("\t\t\t},\n"); 541 } 542 543 if (pol->protocol == LWSSSP_WS) { 544 printf("\t\t\t.u = {\n\t\t\t\t.ws = {\n"); 545 if (pol->u.http.u.ws.subprotocol) 546 printf("\t\t\t\t\t.subprotocol = \"%s\",\n", 547 pol->u.http.u.ws.subprotocol); 548 printf("\t\t\t\t\t.binary = %u\n", pol->u.http.u.ws.binary); 549 printf("\t\t\t\t}\n\t\t\t},\n"); 550 } 551 552 if (pol->u.http.resp_expect) 553 printf("\t\t\t.resp_expect = %u,\n", pol->u.http.resp_expect); 554 if (pol->u.http.fail_redirect) 555 printf("\t\t\t.fail_redirect = %u,\n", pol->u.http.fail_redirect); 556 557 printf("\t\t}\n\t},\n"); 558 559 break; 560 case LWSSSP_MQTT: 561 562 printf("\t.u = {\n\t\t.mqtt = {\n"); 563 564 if (pol->u.mqtt.topic) 565 printf("\t\t\t.topic = \"%s\",\n", 566 pol->u.mqtt.topic); 567 if (pol->u.mqtt.subscribe) 568 printf("\t\t\t.subscribe = \"%s\",\n", 569 pol->u.mqtt.subscribe); 570 if (pol->u.mqtt.will_topic) 571 printf("\t\t\t.will_topic = \"%s\",\n", 572 pol->u.mqtt.will_topic); 573 if (pol->u.mqtt.will_message) 574 printf("\t\t\t.will_message = \"%s\",\n", 575 pol->u.mqtt.will_message); 576 if (pol->u.mqtt.will_qos) 577 printf("\t\t\t.will_qos = %u,\n", 578 pol->u.mqtt.will_qos); 579 if (pol->u.mqtt.will_retain) 580 printf("\t\t\t.will_retain = %u,\n", 581 pol->u.mqtt.will_retain); 582 if (pol->u.mqtt.birth_topic) 583 printf("\t\t\t.birth_topic = \"%s\",\n", 584 pol->u.mqtt.birth_topic); 585 if (pol->u.mqtt.birth_message) 586 printf("\t\t\t.birth_message = \"%s\",\n", 587 pol->u.mqtt.birth_message); 588 if (pol->u.mqtt.birth_qos) 589 printf("\t\t\t.birth_qos = %u,\n", 590 pol->u.mqtt.birth_qos); 591 if (pol->u.mqtt.birth_retain) 592 printf("\t\t\t.birth_retain = %u,\n", 593 pol->u.mqtt.birth_retain); 594 if (pol->u.mqtt.keep_alive) 595 printf("\t\t\t.keep_alive = %u,\n", 596 pol->u.mqtt.keep_alive); 597 if (pol->u.mqtt.qos) 598 printf("\t\t\t.qos = %u,\n", 599 pol->u.mqtt.qos); 600 if (pol->u.mqtt.clean_start) 601 printf("\t\t\t.clean_start = %u,\n", 602 pol->u.mqtt.clean_start); 603 if (pol->u.mqtt.aws_iot) 604 printf("\t\t\t.aws_iot = %u,\n", 605 pol->u.mqtt.aws_iot); 606 if (pol->u.mqtt.retain) 607 printf("\t\t\t.retain = %u,\n", 608 pol->u.mqtt.retain); 609 printf("\t\t}\n\t},\n"); 610 611 break; 612 default: 613 lwsl_err("%s: unknown ss protocol index %d\n", __func__, 614 pol->protocol); 615 goto bail; 616 } 617 618#if 0 619 const lws_ss_trust_store_t *trust_store; /**< CA certs needed for conn 620 validation, only set between policy parsing and vhost creation */ 621#endif 622 623 if (pol->retry_bo) { 624 a = rbomap; 625 while (a) { 626 if (a->orig == (const char *)pol->retry_bo) 627 break; 628 629 a = a->next; 630 } 631 if (!a) 632 goto bail; 633 634 printf("\t.retry_bo = &_rbo_%zu,\n", a->offset); 635 } 636 637 if (pol->timeout_ms) 638 printf("\t.timeout_ms = %u,\n", pol->timeout_ms); 639 if (pol->flags) 640 printf("\t.flags = 0x%x,\n", pol->flags); 641 if (pol->flags) 642 printf("\t.priority = 0x%x,\n", (unsigned int)pol->priority); 643 if (pol->port) 644 printf("\t.port = %u,\n", pol->port); 645 if (pol->metadata_count) 646 printf("\t.metadata_count = %u,\n", pol->metadata_count); 647 printf("\t.protocol = %u,\n", pol->protocol); 648 if (pol->client_cert) 649 printf("\t.client_cert = %u,\n", pol->client_cert); 650 651 if (pol->trust.store) 652 printf("\t.trust = {.store = &_ss_ts_%s},\n", 653 purify_csymbol(pol->trust.store->name, 654 buf, sizeof(buf))); 655#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4) 656 if (pol->aws_region) 657 printf("\t.aws_region= \"%s\",\n", pol->aws_region); 658 if (pol->aws_service) 659 printf("\t.aws_service= \"%s\",\n", pol->aws_service); 660 661#endif 662 663 664 printf("}"); 665 if (pol->next) 666 printf(",\n"); 667 668 lws_strncpy(prev, curr, sizeof(prev)); 669 670 lastpol = pol; 671 672 pol = pol->next; 673 } 674 675 printf(";\n"); 676 if (lastpol) 677 printf("#define _ss_static_policy_entry _ssp_%s\n", 678 purify_csymbol(lastpol->streamtype, buf, sizeof(buf))); 679 680 est += last_offset; 681 682 printf("/* estimated footprint %zu (when sizeof void * = %zu) */\n", 683 est, sizeof(void *)); 684 685 lws_ss_policy_parse_abandon(context); 686 bad = 0; 687 688bail: 689 690 691 lws_context_destroy(context); 692 693 lwsl_user("Completed: %s\n", bad ? "failed" : "OK"); 694 695 return bad; 696} 697