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