• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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