1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

ss: add static policy as a build option

In some cases devices may be too constrained to handle JSON policies but still
want to use SS apis and methodology.

This introduces an off-by-default cmake option LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY,
if enabled the JSON parsing part is excluded and it's assumed the user code
provides its policy as hardcoded policy structs.
This commit is contained in:
Andy Green 2020-03-26 06:48:34 +00:00
parent e783a6ca6e
commit 4cc7f4ed02
24 changed files with 3081 additions and 267 deletions

View file

@ -66,6 +66,7 @@ option(LWS_WITH_HTTP_UNCOMMON_HEADERS "Include less common http header support"
option(LWS_WITH_SECURE_STREAMS "Secure Streams protocol-agnostic API" OFF)
option(LWS_WITH_SECURE_STREAMS_PROXY_API "Secure Streams support to work across processes" OFF)
option(LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM "Auth support for api.amazon.com" OFF)
option(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY "Secure Streams Policy is hardcoded only" OFF)
#
# TLS library options... all except mbedTLS are basically OpenSSL variants.
@ -1176,10 +1177,15 @@ if (LWS_WITH_NETWORK)
if (LWS_WITH_SECURE_STREAMS)
list(APPEND SOURCES
lib/secure-streams/secure-streams.c
lib/secure-streams/policy.c
lib/secure-streams/system/fetch-policy/fetch-policy.c
lib/secure-streams/policy-common.c
lib/secure-streams/system/captive-portal-detect/captive-portal-detect.c
)
if (NOT LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY)
list(APPEND SOURCES
lib/secure-streams/policy-json.c
lib/secure-streams/system/fetch-policy/fetch-policy.c
)
endif()
if (LWS_ROLE_H1)
list(APPEND SOURCES
lib/secure-streams/protocols/ss-h1.c

View file

@ -154,6 +154,7 @@
#cmakedefine LWS_WITH_SECURE_STREAMS
#cmakedefine LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM
#cmakedefine LWS_WITH_SECURE_STREAMS_PROXY_API
#cmakedefine LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY
#cmakedefine LWS_WITH_SELFTESTS
#cmakedefine LWS_WITH_SEQUENCER
#cmakedefine LWS_WITH_SERVER_STATUS

View file

@ -712,9 +712,14 @@ struct lws_context_creation_info {
/**< CONTEXT: percentage of udp reads we actually received
* to make disappear, in order to simulate and test udp retry flow */
#if defined(LWS_WITH_SECURE_STREAMS)
#if defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY)
const struct lws_ss_policy *pss_policies; /**< CONTEXT: point to first
* in a linked-list of streamtype policies prepared by user code */
#else
const char *pss_policies_json; /**< CONTEXT: point to a string
* containing a JSON description of the secure streams policies. Set
* to NULL if not using Secure Streams. */
#endif
const struct lws_ss_plugin **pss_plugins; /**< CONTEXT: point to an array
* of pointers to plugin structs here, terminated with a NULL ptr.
* Set to NULL if not using Secure Streams. */

View file

@ -122,7 +122,7 @@ typedef struct lws_ss_trust_store {
struct lws_ss_trust_store *next;
const char *name;
lws_ss_x509_t *ssx509[8];
const lws_ss_x509_t *ssx509[6];
int count;
} lws_ss_trust_store_t;
@ -130,6 +130,7 @@ enum {
LWSSSP_H1,
LWSSSP_H2,
LWSSSP_WS,
LWSSSP_MQTT,
LWSSS_HBI_AUTH = 0,
@ -179,6 +180,8 @@ typedef struct lws_ss_policy {
union {
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) || defined(LWS_ROLE_WS)
/* details for http-related protocols... */
struct {
@ -211,6 +214,10 @@ typedef struct lws_ss_policy {
uint8_t fail_redirect:1;
} http;
#endif
#if defined(LWS_ROLE_MQTT)
struct {
const char *topic; /* stream sends on this topic */
const char *subscribe; /* stream subscribes to this topic */
@ -226,6 +233,8 @@ typedef struct lws_ss_policy {
} mqtt;
#endif
/* details for non-http related protocols... */
} u;
@ -248,6 +257,12 @@ typedef struct lws_ss_policy {
0 = none, 1+ = cc 0+ */
} lws_ss_policy_t;
#if !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY)
/*
* These only exist / have meaning if there's a dynamic JSON policy enabled
*/
LWS_VISIBLE LWS_EXTERN int
lws_ss_policy_parse_begin(struct lws_context *context, int overlay);
@ -259,3 +274,12 @@ lws_ss_policy_parse(struct lws_context *context, const uint8_t *buf, size_t len)
LWS_VISIBLE LWS_EXTERN int
lws_ss_policy_overlay(struct lws_context *context, const char *overlay);
/*
* You almost certainly don't want this, it returns the first policy object
* in a linked-list of objects created by lws_ss_policy_parse above
*/
LWS_VISIBLE LWS_EXTERN const lws_ss_policy_t *
lws_ss_policy_get(struct lws_context *context);
#endif

View file

@ -161,6 +161,7 @@ lws_state_notify_protocol_init(struct lws_state_manager *mgr,
/* it failed, eg, no streamtype for it in the policy */
}
#if !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY)
/*
* Skip this if we are running something without the policy for it
*/
@ -173,6 +174,7 @@ lws_state_notify_protocol_init(struct lws_state_manager *mgr,
if (!lws_ss_sys_fetch_policy(context))
return 1;
}
#endif
#endif
/* protocol part */
@ -295,6 +297,11 @@ lws_create_context(const struct lws_context_creation_info *info)
__func__, context->udp_loss_sim_tx_pc,
context->udp_loss_sim_rx_pc);
#if defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY)
/* directly use the user-provided policy object list */
context->pss_policies = info->pss_policies;
#endif
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
context->ss_proxy_bind = info->ss_proxy_bind;
context->ss_proxy_port = info->ss_proxy_port;
@ -318,7 +325,9 @@ lws_create_context(const struct lws_context_creation_info *info)
#endif
#if defined(LWS_WITH_SECURE_STREAMS)
#if !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY)
context->pss_policies_json = info->pss_policies_json;
#endif
context->pss_plugins = info->pss_plugins;
#endif
@ -822,6 +831,7 @@ lws_create_context(const struct lws_context_creation_info *info)
#if defined(LWS_WITH_SECURE_STREAMS)
#if !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY)
if (context->pss_policies_json) {
/*
* You must create your context with the explicit vhosts flag
@ -844,6 +854,16 @@ lws_create_context(const struct lws_context_creation_info *info)
goto bail;
}
} else
#else
if (context->pss_policies) {
/* user code set the policy objects directly, no parsing step */
if (lws_ss_policy_set(context, "hardcoded")) {
lwsl_err("%s: policy set failed\n", __func__);
goto bail;
}
} else
#endif
lws_create_vhost(context, info);
#endif
@ -1096,9 +1116,11 @@ lws_context_destroy2(struct lws_context *context)
#if defined(LWS_WITH_SECURE_STREAMS)
lws_dll2_foreach_safe(&pt->ss_owner, NULL, lws_ss_destroy_dll);
#if !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY)
if (context->ac_policy)
lwsac_free(&context->ac_policy);
#endif
#endif
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)
lws_dll2_foreach_safe(&pt->ss_client_owner, NULL, lws_sspc_destroy_dll);

View file

@ -358,7 +358,6 @@ struct lws_context {
#endif
#if defined(LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM)
void *pol_args;
struct lws_ss_handle *hss_auth;
struct lws_ss_handle *hss_fetch_policy;
lws_sorted_usec_list_t sul_api_amazon_com;
@ -440,10 +439,13 @@ struct lws_context {
const lws_system_ops_t *system_ops;
#if defined(LWS_WITH_SECURE_STREAMS)
#if !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY)
const char *pss_policies_json;
struct lwsac *ac_policy;
void *pol_args;
#endif
const lws_ss_policy_t *pss_policies;
const lws_ss_plugin_t **pss_plugins;
struct lwsac *ac_policy;
#endif
void *external_baggage_free_on_destroy;

View file

@ -528,3 +528,30 @@ socks_proxy=127.0.0.1:1337 ./bin/lws-minimal-secure-streams-client -p 1234 -i 12
You can confirm this goes through the ssh socks5 proxy to get to the SS proxy
and fulfil the connection.
## Using static policies
If one of your targets is too constrained to make use of dynamic JSON policies, but
using SS and the policies is attractive for wider reasons, you can use a static policy
built into the firmware for the constrained target.
The secure-streams example "policy2c" (which runs on the build machine, not the device)
https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/secure-streams/minimal-secure-streams-policy2c
accepts a normal JSON policy on stdin, and emits a C code representation that can be
included directly in the firmware.
https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/secure-streams/minimal-secure-streams-staticpolicy/static-policy.h
Using this technique it's possible to standardize on maintaining JSON policies across a
range of devices with different contraints, and use the C conversion of the policy on devices
that are too small.
The Cmake option `LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY` should be enabled to use this
mode, it will not build the JSON parser (and the option for LEJP can also be disabled if
you're not otherwise using it, saving an additional couple of KB).
Notice policy2c example tool must be built with `LWS_ROLE_H1`, `LWS_ROLE_H2`, `LWS_ROLE_WS`
and `LWS_ROLE_MQTT` enabled so it can handle any kind of policy.

View file

@ -0,0 +1,279 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This file contains the stuff related to secure streams policy, it's always
* built if LWS_WITH_SECURE_STREAMS enabled.
*/
#include <private-lib-core.h>
const lws_ss_policy_t *
lws_ss_policy_lookup(const struct lws_context *context, const char *streamtype)
{
const lws_ss_policy_t *p = context->pss_policies;
if (!streamtype)
return NULL;
while (p) {
if (!strcmp(p->streamtype, streamtype))
return p;
p = p->next;
}
return NULL;
}
int
lws_ss_set_metadata(struct lws_ss_handle *h, const char *name,
const void *value, size_t len)
{
lws_ss_metadata_t *omd = lws_ss_policy_metadata(h->policy, name);
if (!omd) {
lwsl_err("%s: unknown metadata %s\n", __func__, name);
return 1;
}
h->metadata[omd->length].name = name;
h->metadata[omd->length].value = (void *)value;
h->metadata[omd->length].length = len;
return 0;
}
lws_ss_metadata_t *
lws_ss_get_handle_metadata(struct lws_ss_handle *h, const char *name)
{
lws_ss_metadata_t *omd = lws_ss_policy_metadata(h->policy, name);
if (!omd)
return NULL;
return &h->metadata[omd->length];
}
lws_ss_metadata_t *
lws_ss_policy_metadata(const lws_ss_policy_t *p, const char *name)
{
lws_ss_metadata_t *pmd = p->metadata;
while (pmd) {
if (pmd->name && !strcmp(name, pmd->name))
return pmd;
pmd = pmd->next;
}
return NULL;
}
lws_ss_metadata_t *
lws_ss_policy_metadata_index(const lws_ss_policy_t *p, size_t index)
{
lws_ss_metadata_t *pmd = p->metadata;
while (pmd) {
if (pmd->length == index)
return pmd;
pmd = pmd->next;
}
return NULL;
}
int
lws_ss_policy_set(struct lws_context *context, const char *name)
{
#if !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY)
struct policy_cb_args *args = (struct policy_cb_args *)context->pol_args;
lws_ss_x509_t *x;
char buf[16];
int m;
#endif
const lws_ss_policy_t *pol;
struct lws_vhost *v;
int ret = 0;
#if !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY)
/*
* Parsing seems to have succeeded, and we're going to use the new
* policy that's laid out in args->ac
*/
lejp_destruct(&args->jctx);
if (context->ac_policy) {
/*
* So this is a bit fun-filled, we already had a policy in
* force, perhaps it was the default policy that's just good for
* fetching the real policy, and we're doing that now.
*
* We can destroy all the policy-related direct allocations
* easily because they're cleanly in a single lwsac...
*/
lwsac_free(&context->ac_policy);
/*
* ...but when we did the trust stores, we created vhosts for
* each. We need to destroy those now too, and recreate new
* ones from the new policy, perhaps with different X.509s.
*/
v = context->vhost_list;
while (v) {
if (v->from_ss_policy) {
struct lws_vhost *vh = v->vhost_next;
lwsl_debug("%s: destroying vh %p\n", __func__, v);
lws_vhost_destroy(v);
v = vh;
continue;
}
v = v->vhost_next;
}
lws_check_deferred_free(context, 0, 1);
}
context->pss_policies = args->heads[LTY_POLICY].p;
context->ac_policy = args->ac;
lws_humanize(buf, sizeof(buf), lwsac_total_alloc(args->ac),
humanize_schema_si_bytes);
if (lwsac_total_alloc(args->ac))
m = (int)((lwsac_total_overhead(args->ac) * 100) /
lwsac_total_alloc(args->ac));
else
m = 0;
lwsl_notice("%s: %s, pad %d%c: %s\n", __func__, buf, m, '%', name);
#endif
/* Create vhosts for each type of trust store */
pol = context->pss_policies;
while (pol) {
struct lws_context_creation_info i;
int n;
memset(&i, 0, sizeof(i));
/*
* We get called from context creation... instantiates
* vhosts with client tls contexts set up for each unique CA.
*
* For compatibility with static policy, we create the vhosts
* by walking streamtype list and create vhosts using trust
* store name if it doesn't already exist.
*/
if (!pol->trust_store) {
pol = pol->next;
continue;
}
v = lws_get_vhost_by_name(context, pol->trust_store->name);
if (v) {
/* vhost for this trust store already exists, skip */
pol = pol->next;
continue;
}
i.options = context->options;
i.vhost_name = pol->trust_store->name;
lwsl_debug("%s: %s\n", __func__, i.vhost_name);
i.client_ssl_ca_mem = pol->trust_store->ssx509[0]->ca_der;
i.client_ssl_ca_mem_len = pol->trust_store->ssx509[0]->ca_der_len;
i.port = CONTEXT_PORT_NO_LISTEN;
lwsl_info("%s: %s trust store initial '%s'\n", __func__,
i.vhost_name, pol->trust_store->ssx509[0]->vhost_name);
v = lws_create_vhost(context, &i);
if (!v) {
lwsl_err("%s: failed to create vhost %s\n",
__func__, i.vhost_name);
ret = 1;
} else
v->from_ss_policy = 1;
for (n = 1; v && n < pol->trust_store->count; n++) {
lwsl_info("%s: add '%s' to trust store\n", __func__,
pol->trust_store->ssx509[n]->vhost_name);
if (lws_tls_client_vhost_extra_cert_mem(v,
pol->trust_store->ssx509[n]->ca_der,
pol->trust_store->ssx509[n]->ca_der_len)) {
lwsl_err("%s: add extra cert failed\n",
__func__);
ret = 1;
}
}
pol = pol->next;
}
#if defined(LWS_WITH_SOCKS5)
/*
* ... we need to go through every vhost updating its understanding of
* which socks5 proxy to use...
*/
v = context->vhost_list;
while (v) {
lws_set_socks(v, args->socks5_proxy);
v = v->vhost_next;
}
if (context->vhost_system)
lws_set_socks(context->vhost_system, args->socks5_proxy);
if (args->socks5_proxy)
lwsl_notice("%s: global socks5 proxy: %s\n", __func__,
args->socks5_proxy);
#endif
#if !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY)
/*
* For dynamic policy case, now we processed the x.509 CAs, we can free
* all of our originals. For static policy, they're in .rodata, nothing
* to free.
*/
x = args->heads[LTY_X509].x;
while (x) {
/*
* Free all the DER buffers now they have been parsed into
* tls library X.509 objects
*/
lws_free((void *)x->ca_der);
x->ca_der = NULL;
x = x->next;
}
/* and we can discard the parsing args object now, invalidating args */
lws_free_set_NULL(context->pol_args);
#endif
return ret;
}

View file

@ -20,16 +20,13 @@
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This file contains the stuff related to JSON-provided policy, it's not built
* if LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY enabled.
*/
#include <private-lib-core.h>
typedef struct backoffs {
struct backoffs *next;
const char *name;
lws_retry_bo_t r;
} backoff_t;
static const char * const lejp_tokens_policy[] = {
"release",
"product",
@ -161,39 +158,6 @@ typedef enum {
LSSPPT_STREAMTYPES
} policy_token_t;
union u {
backoff_t *b;
lws_ss_x509_t *x;
lws_ss_trust_store_t *t;
lws_ss_policy_t *p;
};
enum {
LTY_BACKOFF,
LTY_X509,
LTY_TRUSTSTORE,
LTY_POLICY,
_LTY_COUNT /* always last */
};
struct policy_cb_args {
struct lejp_ctx jctx;
struct lws_context *context;
struct lwsac *ac;
const char *socks5_proxy;
struct lws_b64state b64;
union u heads[_LTY_COUNT];
union u curr[_LTY_COUNT];
uint8_t *p;
int count;
};
#define POL_AC_INITIAL 2048
#define POL_AC_GRAIN 800
#define MAX_CERT_TEMP 2048 /* used to discover actual cert size for realloc */
@ -212,63 +176,6 @@ static const char *protonames[] = {
"mqtt", /* LWSSSP_MQTT */
};
lws_ss_metadata_t *
lws_ss_policy_metadata(const lws_ss_policy_t *p, const char *name)
{
lws_ss_metadata_t *pmd = p->metadata;
while (pmd) {
if (pmd->name && !strcmp(name, pmd->name))
return pmd;
pmd = pmd->next;
}
return NULL;
}
lws_ss_metadata_t *
lws_ss_policy_metadata_index(const lws_ss_policy_t *p, size_t index)
{
lws_ss_metadata_t *pmd = p->metadata;
while (pmd) {
if (pmd->length == index)
return pmd;
pmd = pmd->next;
}
return NULL;
}
int
lws_ss_set_metadata(struct lws_ss_handle *h, const char *name,
const void *value, size_t len)
{
lws_ss_metadata_t *omd = lws_ss_policy_metadata(h->policy, name);
if (!omd) {
lwsl_err("%s: unknown metadata %s\n", __func__, name);
return 1;
}
h->metadata[omd->length].name = name;
h->metadata[omd->length].value = (void *)value;
h->metadata[omd->length].length = len;
return 0;
}
lws_ss_metadata_t *
lws_ss_get_handle_metadata(struct lws_ss_handle *h, const char *name)
{
lws_ss_metadata_t *omd = lws_ss_policy_metadata(h->policy, name);
if (!omd)
return NULL;
return &h->metadata[omd->length];
}
static signed char
lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason)
{
@ -645,6 +552,7 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason)
a->curr[LTY_POLICY].p->metadata_count++;
break;
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
case LSSPPT_HTTP_AUTH_HEADER:
case LSSPPT_HTTP_DSN_HEADER:
case LSSPPT_HTTP_FWV_HEADER:
@ -690,6 +598,9 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason)
a->curr[LTY_POLICY].p->u.http.fail_redirect =
reason == LEJPCB_VAL_TRUE;
break;
#endif
#if defined(LWS_ROLE_WS)
case LSSPPT_WS_SUBPROTOCOL:
pp = (char **)&a->curr[LTY_POLICY].p->u.http.u.ws.subprotocol;
@ -699,11 +610,14 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason)
a->curr[LTY_POLICY].p->u.http.u.ws.binary =
reason == LEJPCB_VAL_TRUE;
break;
#endif
case LSSPPT_LOCAL_SINK:
if (reason == LEJPCB_VAL_TRUE)
a->curr[LTY_POLICY].p->flags |= LWSSSPOLF_LOCAL_SINK;
break;
#if defined(LWS_ROLE_MQTT)
case LSSPPT_MQTT_TOPIC:
pp = (char **)&a->curr[LTY_POLICY].p->u.mqtt.topic;
goto string2;
@ -739,6 +653,7 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason)
a->curr[LTY_POLICY].p->u.mqtt.will_retain =
reason == LEJPCB_VAL_TRUE;
break;
#endif
case LSSPPT_PROTOCOL:
a->curr[LTY_POLICY].p->protocol = 0xff;
@ -752,6 +667,9 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason)
lws_strnncpy(dotstar, ctx->buf, ctx->npos, sizeof(dotstar));
lwsl_err("%s: unknown protocol name %s\n", __func__, dotstar);
return -1;
default:
break;
}
return 0;
@ -835,6 +753,7 @@ lws_ss_policy_parse_abandon(struct lws_context *context)
struct policy_cb_args *args = (struct policy_cb_args *)context->pol_args;
lejp_destruct(&args->jctx);
lwsac_free(&args->ac);
lws_free_set_NULL(context->pol_args);
return 0;
@ -865,175 +784,13 @@ lws_ss_policy_overlay(struct lws_context *context, const char *overlay)
strlen(overlay));
}
int
lws_ss_policy_set(struct lws_context *context, const char *name)
const lws_ss_policy_t *
lws_ss_policy_get(struct lws_context *context)
{
struct policy_cb_args *args = (struct policy_cb_args *)context->pol_args;
lws_ss_trust_store_t *ts;
struct lws_vhost *v;
lws_ss_x509_t *x;
char buf[16];
int m, ret = 0;
/*
* Parsing seems to have succeeded, and we're going to use the new
* policy that's laid out in args->ac
*/
lejp_destruct(&args->jctx);
if (context->ac_policy) {
/*
* So this is a bit fun-filled, we already had a policy in
* force, perhaps it was the default policy that's just good for
* fetching the real policy, and we're doing that now.
*
* We can destroy all the policy-related direct allocations
* easily because they're cleanly in a single lwsac...
*/
lwsac_free(&context->ac_policy);
/*
* ...but when we did the trust stores, we created vhosts for
* each. We need to destroy those now too, and recreate new
* ones from the new policy, perhaps with different X.509s.
*/
v = context->vhost_list;
while (v) {
if (v->from_ss_policy) {
struct lws_vhost *vh = v->vhost_next;
lwsl_debug("%s: destroying vh %p\n", __func__, v);
lws_vhost_destroy(v);
v = vh;
continue;
}
v = v->vhost_next;
}
lws_check_deferred_free(context, 0, 1);
}
context->pss_policies = args->heads[LTY_POLICY].p;
context->ac_policy = args->ac;
lws_humanize(buf, sizeof(buf), lwsac_total_alloc(args->ac),
humanize_schema_si_bytes);
if (lwsac_total_alloc(args->ac))
m = (int)((lwsac_total_overhead(args->ac) * 100) /
lwsac_total_alloc(args->ac));
else
m = 0;
lwsl_notice("%s: %s, pad %d%c: %s\n", __func__, buf, m, '%', name);
/* Create vhosts for each type of trust store */
ts = args->heads[LTY_TRUSTSTORE].t;
while (ts) {
struct lws_context_creation_info i;
memset(&i, 0, sizeof(i));
/*
* We get called from context creation... instantiates
* vhosts with client tls contexts set up for each unique CA.
*
* Create the vhost with the first (mandatory) entry in the
* trust store...
*/
v = lws_get_vhost_by_name(context, ts->name);
if (!v) {
int n;
i.options = context->options;
i.vhost_name = ts->name;
lwsl_debug("%s: %s\n", __func__, i.vhost_name);
i.client_ssl_ca_mem = ts->ssx509[0]->ca_der;
i.client_ssl_ca_mem_len = ts->ssx509[0]->ca_der_len;
i.port = CONTEXT_PORT_NO_LISTEN;
lwsl_info("%s: %s trust store initial '%s'\n", __func__,
ts->name, ts->ssx509[0]->vhost_name);
v = lws_create_vhost(context, &i);
if (!v) {
lwsl_err("%s: failed to create vhost %s\n",
__func__, ts->name);
ret = 1;
} else
v->from_ss_policy = 1;
for (n = 1; v && n < ts->count; n++) {
lwsl_info("%s: add '%s' to trust store\n",
__func__, ts->ssx509[n]->vhost_name);
if (lws_tls_client_vhost_extra_cert_mem(v,
ts->ssx509[n]->ca_der,
ts->ssx509[n]->ca_der_len)) {
lwsl_err("%s: add extra cert failed\n",
__func__);
ret = 1;
}
}
}
ts = ts->next;
}
#if defined(LWS_WITH_SOCKS5)
/*
* ... we need to go through every vhost updating its understanding of
* which socks5 proxy to use...
*/
v = context->vhost_list;
while (v) {
lws_set_socks(v, args->socks5_proxy);
v = v->vhost_next;
}
if (context->vhost_system)
lws_set_socks(context->vhost_system, args->socks5_proxy);
if (args->socks5_proxy)
lwsl_notice("%s: global socks5 proxy: %s\n", __func__,
args->socks5_proxy);
#endif
/* now we processed the x.509 CAs, we can free all of our originals */
x = args->heads[LTY_X509].x;
while (x) {
/*
* Free all the DER buffers now they have been parsed into
* tls library X.509 objects
*/
lws_free((void *)x->ca_der);
x->ca_der = NULL;
x = x->next;
}
/* and we can discard the parsing args object now, invalidating args */
lws_free_set_NULL(context->pol_args);
return ret;
}
const lws_ss_policy_t *
lws_ss_policy_lookup(const struct lws_context *context, const char *streamtype)
{
const lws_ss_policy_t *p = context->pss_policies;
if (!streamtype)
if (!args)
return NULL;
while (p) {
if (!strcmp(p->streamtype, streamtype))
return p;
p = p->next;
}
return NULL;
return args->heads[LTY_POLICY].p;
}

View file

@ -242,6 +242,46 @@ typedef struct lws_sspc_handle {
uint8_t destroying:1;
} lws_sspc_handle_t;
typedef struct backoffs {
struct backoffs *next;
const char *name;
lws_retry_bo_t r;
} backoff_t;
union u {
backoff_t *b;
lws_ss_x509_t *x;
lws_ss_trust_store_t *t;
lws_ss_policy_t *p;
};
enum {
LTY_BACKOFF,
LTY_X509,
LTY_TRUSTSTORE,
LTY_POLICY,
_LTY_COUNT /* always last */
};
struct policy_cb_args {
struct lejp_ctx jctx;
struct lws_context *context;
struct lwsac *ac;
const char *socks5_proxy;
struct lws_b64state b64;
union u heads[_LTY_COUNT];
union u curr[_LTY_COUNT];
uint8_t *p;
int count;
};
int
lws_ss_deserialize_parse(struct lws_ss_serialization_parser *par,
struct lws_context *context,

View file

@ -66,6 +66,7 @@ set(requirements 1)
require_lws_config(LWS_ROLE_H1 1 requirements)
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY 0 requirements)
require_lws_config(LWS_WITH_ALSA 1 requirements)
if (requirements)

View file

@ -65,6 +65,7 @@ set(requirements 1)
require_lws_config(LWS_ROLE_H1 1 requirements)
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY 0 requirements)
if (requirements)
add_executable(${SAMP} main.c avs.c)

View file

@ -66,6 +66,7 @@ set(requirements 1)
require_lws_config(LWS_ROLE_H1 1 requirements)
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY 0 requirements)
if (requirements)
add_executable(${SAMP} minimal-secure-streams.c)

View file

@ -0,0 +1,82 @@
cmake_minimum_required(VERSION 2.8)
include(CheckCSourceCompiles)
set(SAMP lws-minimal-secure-streams-policy2c)
# If we are being built as part of lws, confirm current build config supports
# reqconfig, else skip building ourselves.
#
# If we are being built externally, confirm installed lws was configured to
# support reqconfig, else error out with a helpful message about the problem.
#
MACRO(require_lws_config reqconfig _val result)
if (DEFINED ${reqconfig})
if (${reqconfig})
set (rq 1)
else()
set (rq 0)
endif()
else()
set(rq 0)
endif()
if (${_val} EQUAL ${rq})
set(SAME 1)
else()
set(SAME 0)
endif()
if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
if (${_val})
message("${SAMP}: skipping as lws being built without ${reqconfig}")
else()
message("${SAMP}: skipping as lws built with ${reqconfig}")
endif()
set(${result} 0)
else()
if (LWS_WITH_MINIMAL_EXAMPLES)
set(MET ${SAME})
else()
CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
set(HAS_${reqconfig} 0)
else()
set(HAS_${reqconfig} 1)
endif()
if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
set(MET 1)
else()
set(MET 0)
endif()
endif()
if (NOT MET)
if (${_val})
message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
else()
message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
endif()
endif()
endif()
ENDMACRO()
set(requirements 1)
require_lws_config(LWS_ROLE_H1 1 requirements)
require_lws_config(LWS_ROLE_H2 1 requirements)
require_lws_config(LWS_ROLE_MQTT 1 requirements)
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY 0 requirements)
if (requirements)
add_executable(${SAMP} minimal-secure-streams.c)
if (websockets_shared)
target_link_libraries(${SAMP} websockets_shared)
add_dependencies(${SAMP} websockets_shared)
else()
target_link_libraries(${SAMP} websockets)
endif()
endif()

View file

@ -0,0 +1,49 @@
# lws minimal secure streams policy2c
This application parses a JSON policy passed on stdin and emits the
equivalent of it in C structs ready for compilation.
This is useful in the case your platform doesn't use a dynamic JSON
policy and is space-constrained, you can still form and maintain the
policy in JSON, but with this utility convert it into compileable C.
**Notice** this depends on LWS_ROLE_H1, LWS_ROLE_H2, LWS_ROLE_WS and
LWS_ROLE_MQTT build of lws, since it has to be able to work with any kind
of policy content.
## build
```
$ cmake . && make
```
## usage
Commandline option|Meaning
---|---
-d <loglevel>|Debug verbosity in decimal, eg, -d15
```
$ cat mypolicy.json | lws-minimal-secure-streams-policy2c
(on stdout)
static const uint32_t _rbo_bo_0[] = {
1000, 2000, 3000, 5000, 10000,
};
static const lws_retry_bo_t _rbo_0 = {
.retry_ms_table = _rbo_bo_0,
.retry_ms_table_count = 5,
.conceal_count = 5,
.secs_since_valid_ping = 30,
.secs_since_valid_hangup = 35,
.jitter_percent = 20,
};
static const uint8_t _ss_der_amazon_root_ca_1[] = {
/* 0x 0 */ 0x30, 0x82, 0x03, 0x41, 0x30, 0x82, 0x02, 0x29,
/* 0x 8 */ 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x13, 0x06,
/* 0x 10 */ 0x6C, 0x9F, 0xCF, 0x99, 0xBF, 0x8C, 0x0A, 0x39,
/* 0x 18 */ 0xE2, 0xF0, 0x78, 0x8A, 0x43, 0xE6, 0x96, 0x36,
/* 0x 20 */ 0x5B, 0xCA, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86,
...
```

View file

@ -0,0 +1,570 @@
/*
* lws-minimal-secure-streams-policy2c
*
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
*
* This reads policy JSON on stdin and emits it as compileable
* C structs.
*
* It's useful if your platform is too space-constrained for a
* JSON policy and needs to build a static policy in C via
* LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY... this way you can
* still create and maintain the JSON policy but implement it directly
* as C structs in your code.
*/
#include <libwebsockets.h>
#include <string.h>
#include <signal.h>
#include <assert.h>
static int interrupted, bad = 1;
static void
sigint_handler(int sig)
{
interrupted = 1;
}
struct aggstr {
struct aggstr *next;
const char *orig;
size_t offset;
};
static struct aggstr *rbomap, /* retry / backoff object map */
*trustmap, /* trust store map */
*certmap; /* x.509 cert map */
static size_t last_offset;
static const char *
purify_csymbol(const char *in, char *temp, size_t templen)
{
const char *otemp = temp;
assert (strlen(in) < templen);
while (*in) {
if ((*in >= 'a' && *in <= 'z') || (*in >= 'A' && *in <= 'Z') ||
(*in >= '0' && *in <= '9'))
*temp++ = *in;
else
*temp++ = '_';
in++;
}
*temp = '\0';
return otemp;
}
int main(int argc, const char **argv)
{
const lws_ss_policy_t *pol, *lastpol = NULL;
struct lws_context_creation_info info;
size_t json_size = 0, est = 0;
struct lws_context *context;
char prev[128], curr[128];
int unique_rbo = 0, m, n;
char buf[64], buf1[64];
lws_ss_metadata_t *md;
struct aggstr *a, *a1;
signal(SIGINT, sigint_handler);
memset(&info, 0, sizeof info);
lws_cmdline_option_handle_builtin(argc, argv, &info);
lwsl_user("LWS secure streams policy2c [-d<verb>]\n");
info.fd_limit_per_thread = 1 + 6 + 1;
info.port = CONTEXT_PORT_NO_LISTEN;
info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
/* create the context */
context = lws_create_context(&info);
if (!context) {
lwsl_err("lws init failed\n");
return 1;
}
lws_ss_policy_parse_begin(context, 0);
printf("/*\n * Autogenerated from the following JSON policy\n */\n\n#if 0\n");
do {
int m, n = read(0, buf, sizeof(buf));
if (n < 1)
break;
m = lws_ss_policy_parse(context, (uint8_t *)buf, (size_t)n);
printf("%.*s", n, buf);
json_size += n;
if (m < 0 && m != LEJP_CONTINUE) {
lwsl_err("%s: policy parse failed... lws has WITH_ROLEs"
"for what's in the JSON?\n", __func__);
goto bail;
}
} while (1);
printf("\n\n Original JSON size: %zu\n#endif\n\n", json_size);
lwsl_notice("%s: parsed JSON\n", __func__);
/*
* Well, this is fun, isn't it... we have parsed the JSON into in-memory
* policy objects, and it has set the context policy pointer to the head
* of those but has not set the new policy (which would free the x.509).
*
* We want to walk the streamtype list first discovering unique objects
* and strings referenced there and emitting them compactly as C data,
* and then second to emit the streamtype linked-list referring to those
* objects.
*
* For const strings, we aggregate them and avoid generating extra
* pointers by encoding the reference as &_lws_ss_staticpol_str[xxx]
* where xxx is the fixed offset in the aggregated monster-string. When
* doing that, we keep a map of original pointers to offsets.
*
* Although we want to minimize memory used by the emitted C, we don't
* have to sweat memory during this conversion since it's happening on a
* PC
*/
pol = lws_ss_policy_get(context);
lwsl_notice("%s: pol %p\n", __func__, pol);
while (pol) {
/*
* Walk the metadata list gathering strings and issuing the
* C struct
*/
md = pol->metadata;
if (md) {
int idx = 0;
printf("\nstatic const lws_ss_metadata_t ");
prev[0] = '\0';
md = pol->metadata;
while (md) {
est += sizeof(lws_ss_metadata_t);
lws_snprintf(curr, sizeof(curr), "_md_%s_%s",
purify_csymbol(pol->streamtype, buf,
sizeof(buf)),
purify_csymbol(md->name, buf1,
sizeof(buf1)));
printf("%s = {\n", curr);
if (prev[0])
printf("\t.next = (void *)&%s, \n", prev);
printf("\t.name = \"%s\",\n", (const char *)md->name);
if (md->value)
printf("\t.value = (void *)\"%s\",\n", (const char *)md->value);
printf("\t.length = %d,\n", idx++); // md->length);
printf("}");
if (md->next)
printf(",\n");
lws_strncpy(prev, curr, sizeof(prev));
md = md->next;
}
printf(";\n\n");
}
/*
* Create unique retry policies... have we seen this guy?
*/
if (pol->retry_bo) {
a = rbomap;
while (a) {
if (a->orig == (const char *)pol->retry_bo)
break;
a = a->next;
}
if (!a) {
/* We haven't seen it before and need to create it */
a = malloc(sizeof(*a));
if (!a)
goto bail;
a->next = rbomap;
a->offset = unique_rbo++;
a->orig = (const char *)pol->retry_bo;
rbomap = a;
printf("static const uint32_t _rbo_bo_%zu[] = {\n",
a->offset);
for (n = 0; n < pol->retry_bo->retry_ms_table_count; n++)
printf(" %u, ", (unsigned int)
pol->retry_bo->retry_ms_table[n]);
est += sizeof(uint32_t) *
pol->retry_bo->retry_ms_table_count;
printf("\n};\nstatic const "
"lws_retry_bo_t _rbo_%zu = {\n", a->offset);
printf("\t.retry_ms_table = _rbo_bo_%zu,\n",
a->offset);
printf("\t.retry_ms_table_count = %u,\n",
pol->retry_bo->retry_ms_table_count);
printf("\t.conceal_count = %u,\n",
pol->retry_bo->conceal_count);
printf("\t.secs_since_valid_ping = %u,\n",
pol->retry_bo->secs_since_valid_ping);
printf("\t.secs_since_valid_hangup = %u,\n",
pol->retry_bo->secs_since_valid_hangup);
printf("\t.jitter_percent = %u,\n",
pol->retry_bo->jitter_percent);
printf("};\n");
est += sizeof(lws_retry_bo_t);
}
}
/*
* How about his trust store, it's new to us?
*/
if (pol->trust_store) {
a = trustmap;
while (a) {
if (a->orig == (const char *)pol->trust_store)
break;
a = a->next;
}
if (!a) {
/* it's new to us... */
a = malloc(sizeof(*a));
if (!a)
goto bail;
a->next = trustmap;
a->offset = 0; /* don't care, just track seen */
a->orig = (const char *)pol->trust_store;
trustmap = a;
/*
* Have a look through his x.509 stack...
* any that're new to us?
*/
for (n = 0; n < pol->trust_store->count; n++) {
if (!pol->trust_store->ssx509[n])
continue;
a1 = certmap;
while (a1) {
if (a1->orig == (const char *)pol->trust_store->ssx509[n])
break;
a1 = a1->next;
}
if (!a1) {
/*
* This x.509 cert is new to us...
* let's capture the DER
*/
a1 = malloc(sizeof(*a1));
if (!a1)
goto bail;
a1->next = certmap;
a1->offset = 0; /* don't care, just track seen */
a1->orig = (const char *)pol->trust_store->ssx509[n];
certmap = a1;
printf("static const uint8_t _ss_der_%s[] = {\n",
purify_csymbol(pol->trust_store->ssx509[n]->vhost_name,
buf, sizeof(buf)));
for (m = 0; m < (int)pol->trust_store->ssx509[n]->ca_der_len; m++) {
if ((m & 7) == 0)
printf("\t/* 0x%3x */ ", m);
printf("0x%02X, ", pol->trust_store->ssx509[n]->ca_der[m]);
if ((m & 7) == 7)
printf("\n");
}
printf("\n};\nstatic const lws_ss_x509_t _ss_x509_%s = {\n",
purify_csymbol(pol->trust_store->ssx509[n]->vhost_name,
buf, sizeof(buf)));
printf("\t.vhost_name = \"%s\",\n", pol->trust_store->ssx509[n]->vhost_name);
printf("\t.ca_der = _ss_der_%s,\n",
purify_csymbol(pol->trust_store->ssx509[n]->vhost_name,
buf, sizeof(buf)));
printf("\t.ca_der_len = %zu,\n", pol->trust_store->ssx509[n]->ca_der_len);
printf("};\n");
est += sizeof(lws_ss_x509_t) + pol->trust_store->ssx509[n]->ca_der_len;
}
}
printf("static const lws_ss_trust_store_t _ss_ts_%s = {\n",
purify_csymbol(pol->trust_store->name,
buf, sizeof(buf)));
printf("\t.name = \"%s\",\n", pol->trust_store->name);
printf("\t.ssx509 = {\n");
for (n = pol->trust_store->count - 1; n >= 0 ; n--)
printf("\t\t&_ss_x509_%s,\n",
pol->trust_store->ssx509[n]->vhost_name);
printf("\t}\n};\n");
est += sizeof(lws_ss_trust_store_t);
}
}
pol = pol->next;
}
/*
* The streamtypes
*/
pol = lws_ss_policy_get(context);
lwsl_notice("%s: pol %p\n", __func__, pol);
printf("\nstatic const lws_ss_policy_t ");
prev[0] = '\0';
while (pol) {
est += sizeof(*pol);
lws_snprintf(curr, sizeof(curr), "_ssp_%s",
purify_csymbol(pol->streamtype, buf, sizeof(buf)));
printf("%s = {\n", curr);
if (prev[0])
printf("\t.next = (void *)&%s,\n", prev);
printf("\t.streamtype = \"%s\",\n", pol->streamtype);
if (pol->endpoint)
printf("\t.endpoint = \"%s\",\n", pol->endpoint);
if (pol->rideshare_streamtype)
printf("\t.rideshare_streamtype = \"%s\",\n",
pol->rideshare_streamtype);
if (pol->payload_fmt)
printf("\t.payload_fmt = \"%s\",\n",
pol->payload_fmt);
if (pol->socks5_proxy)
printf("\t.socks5_proxy = \"%s\",\n",
pol->socks5_proxy);
{
lws_ss_metadata_t *nv = pol->metadata, *last = NULL;
while (nv) {
last = nv;
nv = nv->next;
}
if (pol->metadata)
printf("\t.metadata = (void *)&_md_%s_%s,\n",
purify_csymbol(pol->streamtype, buf, sizeof(buf)),
purify_csymbol(last->name, buf1, sizeof(buf1)));
}
switch (pol->protocol) {
case LWSSSP_H1:
case LWSSSP_H2:
case LWSSSP_WS:
printf("\t.u = {\n\t\t.http = {\n");
if (pol->u.http.method)
printf("\t\t\t.method = \"%s\",\n",
pol->u.http.method);
if (pol->u.http.url)
printf("\t\t\t.url = \"%s\",\n",
pol->u.http.url);
if (pol->u.http.multipart_name)
printf("\t\t\t.multipart_name = \"%s\",\n",
pol->u.http.multipart_name);
if (pol->u.http.multipart_filename)
printf("\t\t\t.multipart_filename = \"%s\",\n",
pol->u.http.multipart_filename);
if (pol->u.http.multipart_content_type)
printf("\t\t\t.multipart_content_type = \"%s\",\n",
pol->u.http.multipart_content_type);
if (pol->u.http.auth_preamble)
printf("\t\t\t.auth_preamble = \"%s\",\n",
pol->u.http.auth_preamble);
if (pol->u.http.blob_header[0]) {
printf("\t\t\t.blob_header = {\n");
for (n = 0; n < (int)LWS_ARRAY_SIZE(pol->u.http.blob_header); n++)
if (pol->u.http.blob_header[n])
printf("\t\t\t\t\"%s\",\n",
pol->u.http.blob_header[n]);
printf("\t\t\t},\n");
}
if (pol->protocol == LWSSSP_WS) {
printf("\t\t\t.u = {\n\t\t\t\t.ws = {\n");
if (pol->u.http.u.ws.subprotocol)
printf("\t\t\t\t\t.subprotocol = \"%s\",\n",
pol->u.http.u.ws.subprotocol);
printf("\t\t\t\t\t.binary = %u\n", pol->u.http.u.ws.binary);
printf("\t\t\t\t}\n\t\t\t},\n");
}
if (pol->u.http.resp_expect)
printf("\t\t\t.resp_expect = %u,\n", pol->u.http.resp_expect);
if (pol->u.http.fail_redirect)
printf("\t\t\t.fail_redirect = %u,\n", pol->u.http.fail_redirect);
printf("\t\t}\n\t},\n");
break;
case LWSSSP_MQTT:
printf("\t.u = {\n\t\t.mqtt = {\n");
if (pol->u.mqtt.topic)
printf("\t\t\t.topic = \"%s\",\n",
pol->u.mqtt.topic);
if (pol->u.mqtt.subscribe)
printf("\t\t\t.subscribe = \"%s\",\n",
pol->u.mqtt.subscribe);
if (pol->u.mqtt.will_topic)
printf("\t\t\t.will_topic = \"%s\",\n",
pol->u.mqtt.will_topic);
if (pol->u.mqtt.will_message)
printf("\t\t\t.will_message = \"%s\",\n",
pol->u.mqtt.will_message);
if (pol->u.mqtt.keep_alive)
printf("\t\t\t.keep_alive = %u,\n",
pol->u.mqtt.keep_alive);
if (pol->u.mqtt.qos)
printf("\t\t\t.qos = %u,\n",
pol->u.mqtt.qos);
if (pol->u.mqtt.clean_start)
printf("\t\t\t.clean_start = %u,\n",
pol->u.mqtt.clean_start);
if (pol->u.mqtt.will_qos)
printf("\t\t\t.will_qos = %u,\n",
pol->u.mqtt.will_qos);
if (pol->u.mqtt.will_retain)
printf("\t\t\t.will_retain = %u,\n",
pol->u.mqtt.will_retain);
printf("\t\t}\n\t},\n");
break;
default:
lwsl_err("%s: unknown ss protocol index %d\n", __func__,
pol->protocol);
goto bail;
}
#if 0
const lws_ss_trust_store_t *trust_store; /**< CA certs needed for conn
validation, only set between policy parsing and vhost creation */
#endif
if (pol->retry_bo) {
a = rbomap;
while (a) {
if (a->orig == (const char *)pol->retry_bo)
break;
a = a->next;
}
if (!a)
goto bail;
printf("\t.retry_bo = &_rbo_%zu,\n", a->offset);
}
if (pol->flags)
printf("\t.flags = 0x%x,\n", pol->flags);
if (pol->port)
printf("\t.port = %u,\n", pol->port);
if (pol->metadata_count)
printf("\t.metadata_count = %u,\n", pol->metadata_count);
printf("\t.protocol = %u,\n", pol->protocol);
if (pol->client_cert)
printf("\t.client_cert = %u,\n", pol->client_cert);
if (pol->trust_store)
printf("\t.trust_store = &_ss_ts_%s,\n",
purify_csymbol(pol->trust_store->name, buf, sizeof(buf)));
printf("}");
if (pol->next)
printf(",\n");
lws_strncpy(prev, curr, sizeof(prev));
lastpol = pol;
pol = pol->next;
}
printf(";\n");
if (lastpol)
printf("#define _ss_static_policy_entry _ssp_%s\n",
purify_csymbol(lastpol->streamtype, buf, sizeof(buf)));
est += last_offset;
printf("/* estimated footprint %zu (when sizeof void * = %zu) */\n",
est, sizeof(void *));
lws_ss_policy_parse_abandon(context);
bad = 0;
bail:
lws_context_destroy(context);
lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
return bad;
}

View file

@ -67,6 +67,7 @@ require_lws_config(LWS_ROLE_H1 1 requirements)
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS_PROXY_API 1 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY 0 requirements)
if (requirements)
add_executable(${SAMP} ${SRCS})

View file

@ -66,6 +66,7 @@ set(requirements 1)
require_lws_config(LWS_ROLE_H1 1 requirements)
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY 0 requirements)
if (requirements)
add_executable(${SAMP} ${SRCS})

View file

@ -66,6 +66,7 @@ set(requirements 1)
require_lws_config(LWS_ROLE_H1 1 requirements)
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY 0 requirements)
if (requirements)
add_executable(${SAMP} ${SRCS})

View file

@ -0,0 +1,80 @@
cmake_minimum_required(VERSION 2.8)
include(CheckCSourceCompiles)
set(SAMP lws-minimal-secure-streams-staticpolicy)
# If we are being built as part of lws, confirm current build config supports
# reqconfig, else skip building ourselves.
#
# If we are being built externally, confirm installed lws was configured to
# support reqconfig, else error out with a helpful message about the problem.
#
MACRO(require_lws_config reqconfig _val result)
if (DEFINED ${reqconfig})
if (${reqconfig})
set (rq 1)
else()
set (rq 0)
endif()
else()
set(rq 0)
endif()
if (${_val} EQUAL ${rq})
set(SAME 1)
else()
set(SAME 0)
endif()
if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
if (${_val})
message("${SAMP}: skipping as lws being built without ${reqconfig}")
else()
message("${SAMP}: skipping as lws built with ${reqconfig}")
endif()
set(${result} 0)
else()
if (LWS_WITH_MINIMAL_EXAMPLES)
set(MET ${SAME})
else()
CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
set(HAS_${reqconfig} 0)
else()
set(HAS_${reqconfig} 1)
endif()
if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
set(MET 1)
else()
set(MET 0)
endif()
endif()
if (NOT MET)
if (${_val})
message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
else()
message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
endif()
endif()
endif()
ENDMACRO()
set(requirements 1)
require_lws_config(LWS_ROLE_H1 1 requirements)
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY 1 requirements)
if (requirements)
add_executable(${SAMP} minimal-secure-streams.c)
if (websockets_shared)
target_link_libraries(${SAMP} websockets_shared)
add_dependencies(${SAMP} websockets_shared)
else()
target_link_libraries(${SAMP} websockets)
endif()
endif()

View file

@ -0,0 +1,61 @@
# lws minimal secure streams static policy
The application goes to https://warmcat.com and reads index.html there.
It does it using a static Secure Streams policy generated from JSON by
policy2c example.
## build
```
$ cmake . && make
```
## usage
Commandline option|Meaning
---|---
-d <loglevel>|Debug verbosity in decimal, eg, -d15
```
$ ./lws-minimal-secure-streams-staticpolicy
[2020/03/26 15:49:12:6640] U: LWS secure streams static policy test client [-d<verb>]
[2020/03/26 15:49:12:7067] N: lws_create_context: using ss proxy bind '(null)', port 0, ads '(null)'
[2020/03/26 15:49:12:7567] N: lws_tls_client_create_vhost_context: using mem client CA cert 914
[2020/03/26 15:49:12:7597] N: lws_tls_client_create_vhost_context: using mem client CA cert 1011
[2020/03/26 15:49:12:7603] N: lws_tls_client_create_vhost_context: using mem client CA cert 1425
[2020/03/26 15:49:12:7605] N: lws_tls_client_create_vhost_context: using mem client CA cert 1011
[2020/03/26 15:49:12:9713] N: lws_system_cpd_set: setting CPD result OK
[2020/03/26 15:49:13:9625] N: ss_api_amazon_auth_rx: acquired 588-byte api.amazon.com auth token, exp 3600s
[2020/03/26 15:49:13:9747] U: myss_state: LWSSSCS_CREATING, ord 0x0
[2020/03/26 15:49:13:9774] U: myss_state: LWSSSCS_CONNECTING, ord 0x0
[2020/03/26 15:49:14:1897] U: myss_state: LWSSSCS_CONNECTED, ord 0x0
[2020/03/26 15:49:14:1926] U: myss_rx: len 1520, flags: 1
[2020/03/26 15:49:14:1945] U: myss_rx: len 1520, flags: 0
[2020/03/26 15:49:14:1946] U: myss_rx: len 1520, flags: 0
[2020/03/26 15:49:14:1947] U: myss_rx: len 1520, flags: 0
[2020/03/26 15:49:14:1948] U: myss_rx: len 1520, flags: 0
[2020/03/26 15:49:14:1949] U: myss_rx: len 583, flags: 0
[2020/03/26 15:49:14:2087] U: myss_rx: len 1520, flags: 0
[2020/03/26 15:49:14:2089] U: myss_rx: len 1520, flags: 0
[2020/03/26 15:49:14:2090] U: myss_rx: len 1520, flags: 0
[2020/03/26 15:49:14:2091] U: myss_rx: len 1520, flags: 0
[2020/03/26 15:49:14:2092] U: myss_rx: len 1520, flags: 0
[2020/03/26 15:49:14:2093] U: myss_rx: len 583, flags: 0
[2020/03/26 15:49:14:2109] U: myss_rx: len 1520, flags: 0
[2020/03/26 15:49:14:2110] U: myss_rx: len 1520, flags: 0
[2020/03/26 15:49:14:2111] U: myss_rx: len 1520, flags: 0
[2020/03/26 15:49:14:2112] U: myss_rx: len 1520, flags: 0
[2020/03/26 15:49:14:2113] U: myss_rx: len 1520, flags: 0
[2020/03/26 15:49:14:2114] U: myss_rx: len 583, flags: 0
[2020/03/26 15:49:14:2135] U: myss_rx: len 1520, flags: 0
[2020/03/26 15:49:14:2136] U: myss_rx: len 1358, flags: 0
[2020/03/26 15:49:14:2136] U: myss_rx: len 0, flags: 2
[2020/03/26 15:49:14:2138] U: myss_state: LWSSSCS_QOS_ACK_REMOTE, ord 0x0
[2020/03/26 15:49:14:2139] N: myss_state: LWSSSCS_QOS_ACK_REMOTE
[2020/03/26 15:49:14:2170] U: myss_state: LWSSSCS_DISCONNECTED, ord 0x0
[2020/03/26 15:49:14:2192] U: myss_state: LWSSSCS_DESTROYING, ord 0x0
[2020/03/26 15:49:14:2265] E: lws_context_destroy3
[2020/03/26 15:49:14:2282] U: Completed: OK
```

View file

@ -0,0 +1,291 @@
/*
* lws-minimal-secure-streams
*
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
*
* This demonstrates a minimal http client using secure streams api.
*
* It visits https://warmcat.com/ and receives the html page there.
*
* This example is built two different ways from the same source... one includes
* the policy everything needed to fulfil the stream directly. The other -client
* variant has no policy itself and some other minor init changes, and connects
* to the -proxy example to actually get the connection done.
*
* In the -client build case, the example does not even init the tls libraries
* since the proxy part will take care of all that.
*/
#include <libwebsockets.h>
#include <string.h>
#include <signal.h>
static int interrupted, bad = 1, force_cpd_fail_portal,
force_cpd_fail_no_internet;
static lws_state_notify_link_t nl;
/*
* This is example builds with a static policy autogenerated from a JSON
* policy...
*/
#include "static-policy.h"
typedef struct myss {
struct lws_ss_handle *ss;
void *opaque_data;
/* ... application specific state ... */
lws_sorted_usec_list_t sul;
} myss_t;
static const char *canned_root_token_payload =
"grant_type=refresh_token"
"&refresh_token=Atzr|IwEBIJedGXjDqsU_vMxykqOMg"
"SHfYe3CPcedueWEMWSDMaDnEmiW8RlR1Kns7Cb4B-TOSnqp7ifVsY4BMY2B8tpHfO39XP"
"zfu9HapGjTR458IyHX44FE71pWJkGZ79uVBpljP4sazJuk8XS3Oe_yLnm_DIO6fU1nU3Y"
"0flYmsOiOAQE_gRk_pdlmEtHnpMA-9rLw3mkY5L89Ty9kUygBsiFaYatouROhbsTn8-jW"
"k1zZLUDpT6ICtBXSnrCIg0pUbZevPFhTwdXd6eX-u4rq0W-XaDvPWFO7au-iPb4Zk5eZE"
"iX6sissYrtNmuEXc2uHu7MnQO1hHCaTdIO2CANVumf-PHSD8xseamyh04sLV5JgFzY45S"
"KvKMajiUZuLkMokOx86rjC2Hdkx5DO7G-dbG1ufBDG-N79pFMSs7Ck5pc283IdLoJkCQc"
"AGvTX8o8I29QqkcGou-9TKhOJmpX8As94T61ok0UqqEKPJ7RhfQHHYdCtsdwxgvfVr9qI"
"xL_hDCcTho8opCVX-6QhJHl6SQFlTw13"
"&client_id="
"amzn1.application-oa2-client.4823334c434b4190a2b5a42c07938a2d";
/* secure streams payload interface */
static int
myss_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
{
// myss_t *m = (myss_t *)userobj;
lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags);
lwsl_hexdump_info(buf, len);
/*
* If we received the whole message, for our example it means
* we are done.
*/
if (flags & LWSSS_FLAG_EOM) {
bad = 0;
interrupted = 1;
}
return 0;
}
static int
myss_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len,
int *flags)
{
//myss_t *m = (myss_t *)userobj;
return 0;
}
static int
myss_state(void *userobj, void *sh, lws_ss_constate_t state,
lws_ss_tx_ordinal_t ack)
{
myss_t *m = (myss_t *)userobj;
lwsl_user("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state),
(unsigned int)ack);
switch (state) {
case LWSSSCS_CREATING:
lws_ss_set_metadata(m->ss, "uptag", "myuptag123", 10);
lws_ss_set_metadata(m->ss, "ctype", "myctype", 7);
lws_ss_client_connect(m->ss);
break;
case LWSSSCS_ALL_RETRIES_FAILED:
/* if we're out of retries, we want to close the app and FAIL */
interrupted = 1;
break;
case LWSSSCS_QOS_ACK_REMOTE:
lwsl_notice("%s: LWSSSCS_QOS_ACK_REMOTE\n", __func__);
break;
default:
break;
}
return 0;
}
static int
app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
int current, int target)
{
struct lws_context *context = lws_system_context_from_system_mgr(mgr);
lws_system_blob_t *ab = lws_system_get_blob(context,
LWS_SYSBLOB_TYPE_AUTH, 1 /* AUTH_IDX_ROOT */);
size_t size;
/*
* For the things we care about, let's notice if we are trying to get
* past them when we haven't solved them yet, and make the system
* state wait while we trigger the dependent action.
*/
switch (target) {
case LWS_SYSTATE_REGISTERED:
size = lws_system_blob_get_size(ab);
if (size)
break;
/* let's register our canned root token so auth can use it */
lws_system_blob_direct_set(ab,
(const uint8_t *)canned_root_token_payload,
strlen(canned_root_token_payload));
break;
case LWS_SYSTATE_OPERATIONAL:
if (current == LWS_SYSTATE_OPERATIONAL) {
lws_ss_info_t ssi;
/* We're making an outgoing secure stream ourselves */
memset(&ssi, 0, sizeof(ssi));
ssi.handle_offset = offsetof(myss_t, ss);
ssi.opaque_user_data_offset = offsetof(myss_t,
opaque_data);
ssi.rx = myss_rx;
ssi.tx = myss_tx;
ssi.state = myss_state;
ssi.user_alloc = sizeof(myss_t);
ssi.streamtype = "mintest";
if (lws_ss_create(context, 0, &ssi, NULL, NULL,
NULL, NULL)) {
lwsl_err("%s: failed to create secure stream\n",
__func__);
return -1;
}
}
break;
}
return 0;
}
static lws_state_notify_link_t * const app_notifier_list[] = {
&nl, NULL
};
static void
sigint_handler(int sig)
{
interrupted = 1;
}
int main(int argc, const char **argv)
{
struct lws_context_creation_info info;
struct lws_context *context;
int n = 0;
signal(SIGINT, sigint_handler);
memset(&info, 0, sizeof info);
lws_cmdline_option_handle_builtin(argc, argv, &info);
lwsl_user("LWS secure streams static policy test client [-d<verb>]\n");
/* these options are mutually exclusive if given */
if (lws_cmdline_option(argc, argv, "--force-portal"))
force_cpd_fail_portal = 1;
if (lws_cmdline_option(argc, argv, "--force-no-internet"))
force_cpd_fail_no_internet = 1;
info.fd_limit_per_thread = 1 + 6 + 1;
info.port = CONTEXT_PORT_NO_LISTEN;
#if defined(LWS_SS_USE_SSPC)
info.protocols = lws_sspc_protocols;
{
const char *p;
/* connect to ssproxy via UDS by default, else via
* tcp connection to this port */
if ((p = lws_cmdline_option(argc, argv, "-p")))
info.ss_proxy_port = atoi(p);
/* UDS "proxy.ss.lws" in abstract namespace, else this socket
* path; when -p given this can specify the network interface
* to bind to */
if ((p = lws_cmdline_option(argc, argv, "-i")))
info.ss_proxy_bind = p;
/* if -p given, -a specifies the proxy address to connect to */
if ((p = lws_cmdline_option(argc, argv, "-a")))
info.ss_proxy_address = p;
}
#else
info.pss_policies = &_ss_static_policy_entry;
info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
#endif
#if defined(LWS_WITH_DETAILED_LATENCY)
info.detailed_latency_cb = lws_det_lat_plot_cb;
info.detailed_latency_filepath = "/tmp/lws-latency-ssproxy";
#endif
/* integrate us with lws system state management when context created */
nl.name = "app";
nl.notify_cb = app_system_state_nf;
info.register_notifier_list = app_notifier_list;
/* create the context */
context = lws_create_context(&info);
if (!context) {
lwsl_err("lws init failed\n");
return 1;
}
/*
* Set the related lws_system blobs
*
* ...direct_set() sets a pointer, so the thing pointed to has to have
* a suitable lifetime, eg, something that already exists on the heap or
* a const string in .rodata like this
*/
lws_system_blob_direct_set(lws_system_get_blob(context,
LWS_SYSBLOB_TYPE_DEVICE_SERIAL, 0),
(const uint8_t *)"SN12345678", 10);
lws_system_blob_direct_set(lws_system_get_blob(context,
LWS_SYSBLOB_TYPE_DEVICE_FW_VERSION, 0),
(const uint8_t *)"v0.01", 5);
/*
* ..._heap_append() appends to a buflist kind of arrangement on heap,
* just one block is fine, otherwise it will concatenate the fragments
* in the order they were appended (and take care of freeing them at
* context destroy time). ..._heap_empty() is also available to remove
* everything that was already allocated.
*
* Here we use _heap_append() just so it's tested as well as direct set.
*/
lws_system_blob_heap_append(lws_system_get_blob(context,
LWS_SYSBLOB_TYPE_DEVICE_TYPE, 0),
(const uint8_t *)"spacerocket", 11);
/* the event loop */
while (n >= 0 && !interrupted)
n = lws_service(context, 0);
lws_context_destroy(context);
lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
return bad;
}

View file

@ -65,6 +65,7 @@ set(requirements 1)
require_lws_config(LWS_ROLE_H1 1 requirements)
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY 0 requirements)
if (requirements)
add_executable(${SAMP} minimal-secure-streams.c)