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:
parent
a60cb84c9e
commit
704eaa5e63
8 changed files with 152 additions and 36 deletions
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue