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
26 static int interrupted, bad = 1;
27
28
29 static void
sigint_handler(int sig)30 sigint_handler(int sig)
31 {
32 interrupted = 1;
33 }
34
35 struct aggstr {
36 struct aggstr *next;
37
38 const char *orig;
39 size_t offset;
40 };
41
42 static struct aggstr *rbomap, /* retry / backoff object map */
43 *trustmap, /* trust store map */
44 *certmap; /* x.509 cert map */
45 static size_t last_offset;
46
47
48
49 static const char *
purify_csymbol(const char * in,char * temp,size_t templen)50 purify_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
main(int argc,const char ** argv)71 int 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
577 if (pol->u.mqtt.keep_alive)
578 printf("\t\t\t.keep_alive = %u,\n",
579 pol->u.mqtt.keep_alive);
580 if (pol->u.mqtt.qos)
581 printf("\t\t\t.qos = %u,\n",
582 pol->u.mqtt.qos);
583 if (pol->u.mqtt.clean_start)
584 printf("\t\t\t.clean_start = %u,\n",
585 pol->u.mqtt.clean_start);
586 if (pol->u.mqtt.will_qos)
587 printf("\t\t\t.will_qos = %u,\n",
588 pol->u.mqtt.will_qos);
589 if (pol->u.mqtt.will_retain)
590 printf("\t\t\t.will_retain = %u,\n",
591 pol->u.mqtt.will_retain);
592
593 printf("\t\t}\n\t},\n");
594
595 break;
596 default:
597 lwsl_err("%s: unknown ss protocol index %d\n", __func__,
598 pol->protocol);
599 goto bail;
600 }
601
602 #if 0
603 const lws_ss_trust_store_t *trust_store; /**< CA certs needed for conn
604 validation, only set between policy parsing and vhost creation */
605 #endif
606
607 if (pol->retry_bo) {
608 a = rbomap;
609 while (a) {
610 if (a->orig == (const char *)pol->retry_bo)
611 break;
612
613 a = a->next;
614 }
615 if (!a)
616 goto bail;
617
618 printf("\t.retry_bo = &_rbo_%zu,\n", a->offset);
619 }
620
621 if (pol->timeout_ms)
622 printf("\t.timeout_ms = %u,\n", pol->timeout_ms);
623 if (pol->flags)
624 printf("\t.flags = 0x%x,\n", pol->flags);
625 if (pol->flags)
626 printf("\t.priority = 0x%x,\n", (unsigned int)pol->priority);
627 if (pol->port)
628 printf("\t.port = %u,\n", pol->port);
629 if (pol->metadata_count)
630 printf("\t.metadata_count = %u,\n", pol->metadata_count);
631 printf("\t.protocol = %u,\n", pol->protocol);
632 if (pol->client_cert)
633 printf("\t.client_cert = %u,\n", pol->client_cert);
634
635 if (pol->trust.store)
636 printf("\t.trust = {.store = &_ss_ts_%s},\n",
637 purify_csymbol(pol->trust.store->name,
638 buf, sizeof(buf)));
639 #if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4)
640 if (pol->aws_region)
641 printf("\t.aws_region= \"%s\",\n", pol->aws_region);
642 if (pol->aws_service)
643 printf("\t.aws_service= \"%s\",\n", pol->aws_service);
644
645 #endif
646
647
648 printf("}");
649 if (pol->next)
650 printf(",\n");
651
652 lws_strncpy(prev, curr, sizeof(prev));
653
654 lastpol = pol;
655
656 pol = pol->next;
657 }
658
659 printf(";\n");
660 if (lastpol)
661 printf("#define _ss_static_policy_entry _ssp_%s\n",
662 purify_csymbol(lastpol->streamtype, buf, sizeof(buf)));
663
664 est += last_offset;
665
666 printf("/* estimated footprint %zu (when sizeof void * = %zu) */\n",
667 est, sizeof(void *));
668
669 lws_ss_policy_parse_abandon(context);
670 bad = 0;
671
672 bail:
673
674
675 lws_context_destroy(context);
676
677 lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
678
679 return bad;
680 }
681