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:
parent
e783a6ca6e
commit
4cc7f4ed02
24 changed files with 3081 additions and 267 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
279
lib/secure-streams/policy-common.c
Normal file
279
lib/secure-streams/policy-common.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
|
@ -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,
|
||||
...
|
||||
```
|
|
@ -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;
|
||||
}
|
|
@ -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})
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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()
|
|
@ -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
|
||||
|
||||
```
|
|
@ -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;
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue