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

ss: allow streamtype policy overlays

Make the policy load apis public with an extra argument that says if you want the
JSON to overlay on an existing policy rather than replace it.

Teach the stream type parser stuff to realize it already has an entry for the
stream type and to modify that rather than create a second one, allowing overlays
to modify stream types.

Add --force-portal and --force-no-internet flags to minimal-secure-streams and
use the new policy overlay stuff to force the policy for captive portal detection
to feel that there is one or that there's no internet.
This commit is contained in:
Andy Green 2020-03-12 14:43:32 +00:00
parent a60cb84c9e
commit 704eaa5e63
8 changed files with 152 additions and 36 deletions

View file

@ -247,3 +247,15 @@ typedef struct lws_ss_policy {
uint8_t client_cert; /**< which client cert to apply
0 = none, 1+ = cc 0+ */
} lws_ss_policy_t;
LWS_VISIBLE LWS_EXTERN int
lws_ss_policy_parse_begin(struct lws_context *context, int overlay);
LWS_VISIBLE LWS_EXTERN int
lws_ss_policy_parse_abandon(struct lws_context *context);
LWS_VISIBLE LWS_EXTERN int
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);

View file

@ -828,7 +828,7 @@ lws_create_context(const struct lws_context_creation_info *info)
assert(lws_check_opt(info->options,
LWS_SERVER_OPTION_EXPLICIT_VHOSTS));
if (lws_ss_policy_parse_begin(context))
if (lws_ss_policy_parse_begin(context, 0))
goto bail;
n = lws_ss_policy_parse(context,

View file

@ -369,6 +369,45 @@ The secure-streams-proxy minimal example shows how this is done and
fetches its real policy from warmcat.com at startup using the built-in
one.
## Applying streamtype policy overlays
This is intended for modifying policies at runtime for testing, eg, to
force error paths to be taken. After the main policy is processed, you
may parse additional, usually smaller policy fragments on top of it.
Where streamtype names in the new fragment already exist in the current
parsed policy, the settings in the fragment are applied over the parsed
policy, overriding settings. There's a simple api to enable this by
giving it the override JSON in one string
```
int
lws_ss_policy_overlay(struct lws_context *context, const char *overlay);
```
but there are also other apis available that can statefully process
larger overlay fragments if needed.
An example overlay fragment looks like this
```
{ "s": [{ "captive_portal_detect": {
"endpoint": "google.com",
"http_url": "/",
"port": 80
}}]}
```
ie the overlay fragment completely follows the structure of the main policy,
just misses out anything it doesn't override.
Currently ONLY streamtypes may be overridden.
You can see an example of this in use in `minimal-secure-streams` example
where `--force-portal` and `--force-no-internet` options cause the captive
portal detect streamtype to be overridden to force the requested kind of
outcome.
## Captive Portal Detection
If the policy contains a streamtype `captive_portal_detect` then the

View file

@ -277,6 +277,7 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason)
char **pp, dotstar[32], *q;
lws_ss_trust_store_t *ts;
lws_ss_metadata_t *pmd;
lws_ss_policy_t *p2;
lws_retry_bo_t *b;
size_t inl, outl;
lws_ss_x509_t *x;
@ -336,11 +337,39 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason)
}
if (reason == LEJPCB_PAIR_NAME && n != -1 && n != LTY_TRUSTSTORE) {
p2 = NULL;
if (n == LTY_POLICY) {
/*
* We want to allow for the possibility of overlays...
* eg, we come later with a JSON snippet that overrides
* select streamtype members of a streamtype that was
* already defined
*/
p2 = (lws_ss_policy_t *)a->context->pss_policies;
while (p2) {
if (!strncmp(p2->streamtype,
ctx->path + ctx->st[ctx->sp].p,
ctx->path_match_len -
ctx->st[ctx->sp].p)) {
lwsl_info("%s: overriding s[] %s\n",
__func__, p2->streamtype);
break;
}
p2 = p2->next;
}
}
/*
* We do the pointers always as .b, all of the participating
* structs begin with .next and .name
* We do the pointers always as .b union member, all of the
* participating structs begin with .next and .name the same
*/
a->curr[n].b = lwsac_use_zero(&a->ac, sizes[n], POL_AC_GRAIN);
if (p2) /* we may be overriding existing streamtype... */
a->curr[n].b = (backoff_t *)p2;
else
a->curr[n].b = lwsac_use_zero(&a->ac, sizes[n], POL_AC_GRAIN);
if (!a->curr[n].b)
goto oom;
@ -352,11 +381,15 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason)
}
a->count = 0;
a->curr[n].b->next = a->heads[n].b;
a->heads[n].b = a->curr[n].b;
pp = (char **)&a->curr[n].b->name;
if (!p2) {
a->curr[n].b->next = a->heads[n].b;
a->heads[n].b = a->curr[n].b;
pp = (char **)&a->curr[n].b->name;
goto string1;
goto string1;
}
return 0; /* overriding */
}
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
@ -762,7 +795,7 @@ oom:
}
int
lws_ss_policy_parse_begin(struct lws_context *context)
lws_ss_policy_parse_begin(struct lws_context *context, int overlay)
{
struct policy_cb_args *args;
char *p;
@ -773,6 +806,12 @@ lws_ss_policy_parse_begin(struct lws_context *context)
return 1;
}
if (overlay)
/* continue to use the existing lwsac */
args->ac = context->ac_policy;
else
/* we don't want to see any old policy */
context->pss_policies = NULL;
context->pol_args = args;
args->context = context;
@ -818,6 +857,14 @@ lws_ss_policy_parse(struct lws_context *context, const uint8_t *buf, size_t len)
return m;
}
int
lws_ss_policy_overlay(struct lws_context *context, const char *overlay)
{
lws_ss_policy_parse_begin(context, 1);
return lws_ss_policy_parse(context, (const uint8_t *)overlay,
strlen(overlay));
}
int
lws_ss_policy_set(struct lws_context *context, const char *name)
{

View file

@ -272,19 +272,9 @@ lws_ss_destroy_dll(struct lws_dll2 *d, void *user);
int
lws_sspc_destroy_dll(struct lws_dll2 *d, void *user);
int
lws_ss_policy_parse_begin(struct lws_context *context);
int
lws_ss_policy_parse(struct lws_context *context, const uint8_t *buf, size_t len);
int
lws_ss_policy_set(struct lws_context *context, const char *name);
int
lws_ss_policy_parse_abandon(struct lws_context *context);
int
lws_ss_sys_fetch_policy(struct lws_context *context);

View file

@ -45,7 +45,7 @@ ss_fetch_policy_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
struct lws_context *context = (struct lws_context *)m->opaque_data;
if (flags & LWSSS_FLAG_SOM) {
if (lws_ss_policy_parse_begin(context))
if (lws_ss_policy_parse_begin(context, 0))
return 1;
m->partway = 1;
}

View file

@ -23,6 +23,8 @@ Commandline option|Meaning
-d <loglevel>|Debug verbosity in decimal, eg, -d15
-f| Force connecting to the wrong endpoint to check backoff retry flow
-p| Run as proxy server for clients to connect to over unix domain socket
--force-portal|Force the SS Captive Portal Detection to feel it's behind a portal
--force-no-internet|Force the SS Captive Portal Detection to feel it can't reach the internet
```
[2019/08/12 07:16:11:0045] USR: LWS minimal secure streams [-d<verbosity>] [-f]

View file

@ -37,7 +37,8 @@
*/
// #define VIA_LOCALHOST_SOCKS
static int interrupted, bad = 1;
static int interrupted, bad = 1, force_cpd_fail_portal,
force_cpd_fail_no_internet;
static lws_state_notify_link_t nl;
/*
@ -184,24 +185,9 @@ static const char * const default_ss_policy =
* every DHCP acquisition
*/
"\"captive_portal_detect\": {"
#if 1
/* this does the actual test */
"\"endpoint\": \"connectivitycheck.android.com\","
"\"http_url\": \"generate_204\","
"\"port\": 80,"
#endif
#if 0
/* this looks like a captive portal due to redirect */
"\"endpoint\": \"google.com\","
"\"http_url\": \"/\","
"\"port\": 80,"
#endif
#if 0
/* this looks like no internet */
"\"endpoint\": \"warmcat.com\","
"\"http_url\": \"/\","
"\"port\": 999,"
#endif
"\"protocol\": \"h1\","
"\"http_method\": \"GET\","
"\"opportunistic\": true,"
@ -309,6 +295,38 @@ app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
* state wait while we trigger the dependent action.
*/
switch (target) {
case LWS_SYSTATE_INITIALIZED: /* overlay on the hardcoded policy */
case LWS_SYSTATE_POLICY_VALID: /* overlay on the loaded policy */
if (target != current)
break;
if (force_cpd_fail_portal)
/* this makes it look like we're behind a captive portal
* because the overriden address does a redirect */
lws_ss_policy_overlay(context,
"{\"s\": [{\"captive_portal_detect\": {"
"\"endpoint\": \"google.com\","
"\"http_url\": \"/\","
"\"port\": 80"
"}}]}");
if (force_cpd_fail_no_internet)
/* this looks like no internet, because the overridden
* port doesn't have anything that will connect to us */
lws_ss_policy_overlay(context,
"{\"s\": [{\"captive_portal_detect\": {"
"\"endpoint\": \"warmcat.com\","
"\"http_url\": \"/\","
"\"port\": 999"
"}}]}");
break;
case LWS_SYSTATE_REGISTERED:
size = lws_system_blob_get_size(ab);
if (size)
@ -372,6 +390,14 @@ int main(int argc, const char **argv)
lwsl_user("LWS secure streams 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)