From a204886eff0ac791b8c9cbfc34eeb36efcb97696 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Wed, 19 Aug 2020 11:21:58 +0100 Subject: [PATCH] ss: policy: add auth mapping section --- .../libwebsockets/lws-secure-streams-policy.h | 22 ++++- lib/core/private-lib-core.h | 1 + lib/secure-streams/README.md | 33 +++++++ lib/secure-streams/policy-json.c | 85 ++++++++++++++++++- .../private-lib-secure-streams.h | 10 ++- .../minimal-secure-streams-alexa/main.c | 16 ++-- .../minimal-secure-streams-avs/main.c | 18 ++-- .../minimal-secure-streams.c | 39 +++++++++ 8 files changed, 204 insertions(+), 20 deletions(-) diff --git a/include/libwebsockets/lws-secure-streams-policy.h b/include/libwebsockets/lws-secure-streams-policy.h index 0337d72ff..2583cd1f3 100644 --- a/include/libwebsockets/lws-secure-streams-policy.h +++ b/include/libwebsockets/lws-secure-streams-policy.h @@ -163,6 +163,19 @@ typedef struct lws_ss_metadata { uint8_t value_on_lws_heap; /* proxy does this */ } lws_ss_metadata_t; +/* + * This is a mapping between an auth streamtype and a name and other information + * that can be independently instantiated. Other streamtypes can indicate they + * require this authentication on their connection. + */ + +typedef struct lws_ss_auth { + struct lws_ss_auth *next; + const char *name; + + const char *streamtype; + uint8_t blob_index; +} lws_ss_auth_t; /** * lws_ss_policy_t: policy database entry for a stream type @@ -188,6 +201,7 @@ typedef struct lws_ss_policy { const char *payload_fmt; const char *socks5_proxy; lws_ss_metadata_t *metadata; /* linked-list of metadata */ + const lws_ss_auth_t *auth; /* NULL or auth object we bind to */ /* protocol-specific connection policy details */ @@ -308,10 +322,14 @@ 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 + * You almost certainly don't want these, they return the first policy or auth + * object in a linked-list of objects created by lws_ss_policy_parse above, + * they are exported to generate static policy with */ LWS_VISIBLE LWS_EXTERN const lws_ss_policy_t * lws_ss_policy_get(struct lws_context *context); +LWS_VISIBLE LWS_EXTERN const lws_ss_auth_t * +lws_ss_auth_get(struct lws_context *context); + #endif diff --git a/lib/core/private-lib-core.h b/lib/core/private-lib-core.h index e5e11f4bc..fcf0514a0 100644 --- a/lib/core/private-lib-core.h +++ b/lib/core/private-lib-core.h @@ -497,6 +497,7 @@ struct lws_context { void *pol_args; #endif const lws_ss_policy_t *pss_policies; + const lws_ss_auth_t *pss_auths; #if defined(LWS_WITH_SSPLUGINS) const lws_ss_plugin_t **pss_plugins; #endif diff --git a/lib/secure-streams/README.md b/lib/secure-streams/README.md index d4050b8fa..92a9b96e6 100644 --- a/lib/secure-streams/README.md +++ b/lib/secure-streams/README.md @@ -195,6 +195,34 @@ with one of these to enforce validity checking of the remote server. Entries should be named using "name" and the stack array defined using "stack" +### `auth` + +Optional section describing a map of available authentication streamtypes to +auth token blob indexes. + +``` +... + "auth": [{"name":"lwa","streamtype":"api_amazon_com_lwa","blob":0}] +... +``` + +Streams can indicate they depend on a valid auth token from one of these schemes +by using the `"use_auth": "name"` member in the streamtype definition, where name +is, eg, "lwa" in the example above. + +### `auth[].name` + +This is the name of the authentication scheme used by other streamtypes + +### `auth[].streamtype` + +This is the auth streamtype to be used to refresh the authentication token + +### `auth[].blob` + +This is the auth blob index the authentication token is stored into and retreived +from + ### `s` These are an array of policies for the supported stream type names. @@ -293,6 +321,11 @@ policy is applied. The name of the trust store described in the `trust_stores` section to apply to validate the remote server cert. +### `use_auth` + +Indicate that the streamtype should use the named auth type from the `auth` +array in the policy + ### `server_cert` **SERVER ONLY**: subject to change... the name of the x.509 cert that is the diff --git a/lib/secure-streams/policy-json.c b/lib/secure-streams/policy-json.c index 718e6c4d9..4d61db774 100644 --- a/lib/secure-streams/policy-json.c +++ b/lib/secure-streams/policy-json.c @@ -97,7 +97,12 @@ static const char * const lejp_tokens_policy[] = { "s[].*.mqtt_will_qos", "s[].*.mqtt_will_retain", "s[].*.swake_validity", + "s[].*.use_auth", "s[].*", + "auth[].name", + "auth[].streamtype", + "auth[].blob", + "auth[]", }; typedef enum { @@ -169,7 +174,12 @@ typedef enum { LSSPPT_MQTT_WILL_QOS, LSSPPT_MQTT_WILL_RETAIN, LSSPPT_SWAKE_VALIDITY, + LSSPPT_USE_AUTH, LSSPPT_STREAMTYPES, + LSSPPT_AUTH_NAME, + LSSPPT_AUTH_STREAMTYPE, + LSSPPT_AUTH_BLOB, + LSSPPT_AUTH, } policy_token_t; @@ -182,9 +192,10 @@ static uint8_t sizes[] = { sizeof(lws_ss_x509_t), sizeof(lws_ss_trust_store_t), sizeof(lws_ss_policy_t), + sizeof(lws_ss_auth_t), }; -static const char *protonames[] = { +static const char * const protonames[] = { "h1", /* LWSSSP_H1 */ "h2", /* LWSSSP_H2 */ "ws", /* LWSSSP_WS */ @@ -192,6 +203,24 @@ static const char *protonames[] = { "raw", /* LWSSSP_RAW */ }; +static const lws_ss_auth_t * +lws_ss_policy_find_auth_by_name(struct policy_cb_args *a, + const char *name, size_t len) +{ + const lws_ss_auth_t *auth = a->heads[LTY_AUTH].a; + + while (auth) { + if (auth->name && + len == strlen(auth->name) && + !strncmp(auth->name, name, len)) + return auth; + + auth = auth->next; + } + + return NULL; +} + static signed char lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason) { @@ -210,8 +239,8 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason) backoff_t *bot; int n = -1; - lwsl_debug("%s: %d %d %s\n", __func__, reason, ctx->path_match - 1, - ctx->path); +// lwsl_debug("%s: %d %d %s\n", __func__, reason, ctx->path_match - 1, +// ctx->path); switch (ctx->path_match - 1) { case LSSPPT_RETRY: @@ -227,6 +256,9 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason) case LSSPPT_STREAMTYPES: n = LTY_POLICY; break; + case LSSPPT_AUTH: + n = LTY_AUTH; + break; } if (reason == LEJPCB_ARRAY_START && @@ -234,6 +266,17 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason) ctx->path_match - 1 == LSSPPT_METADATA)) a->count = 0; + if (reason == LEJPCB_OBJECT_START && n == LTY_AUTH) { + a->curr[n].b = lwsac_use_zero(&a->ac, sizes[n], POL_AC_GRAIN); + if (!a->curr[n].b) + goto oom; + + a->curr[n].b->next = a->heads[n].b; + a->heads[n].b = a->curr[n].b; + + return 0; + } + if (reason == LEJPCB_ARRAY_END && ctx->path_match - 1 == LSSPPT_TRUST_STORES_STACK && !a->count) { lwsl_err("%s: at least one cert required in trust store\n", @@ -264,7 +307,8 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason) return 0; } - if (reason == LEJPCB_PAIR_NAME && n != -1 && n != LTY_TRUSTSTORE) { + if (reason == LEJPCB_PAIR_NAME && n != -1 && + (n != LTY_TRUSTSTORE && n != LTY_AUTH)) { p2 = NULL; if (n == LTY_POLICY) { @@ -543,6 +587,10 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason) a->curr[LTY_POLICY].p->client_cert = atoi(ctx->buf) + 1; break; + case LSSPPT_AUTH_BLOB: + a->curr[LTY_AUTH].a->blob_index = atoi(ctx->buf); + break; + case LSSPPT_HTTP_EXPECT: a->curr[LTY_POLICY].p->u.http.resp_expect = atoi(ctx->buf); break; @@ -624,6 +672,16 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason) case LSSPPT_METADATA: break; + case LSSPPT_USE_AUTH: + a->curr[LTY_POLICY].p->auth = + lws_ss_policy_find_auth_by_name(a, ctx->buf, ctx->npos); + if (!a->curr[LTY_POLICY].p->auth) { + lws_strnncpy(dotstar, ctx->buf, ctx->npos, sizeof(dotstar)); + lwsl_err("%s: unknown auth '%s'\n", __func__, dotstar); + return -1; + } + break; + case LSSPPT_METADATA_ITEM: pmd = a->curr[LTY_POLICY].p->metadata; a->curr[LTY_POLICY].p->metadata = lwsac_use_zero(&a->ac, @@ -688,6 +746,14 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason) pp = (char **)&a->curr[LTY_POLICY].p->u.http.multipart_content_type; goto string2; + case LSSPPT_AUTH_NAME: + pp = (char **)&a->curr[LTY_AUTH].a->name; + goto string2; + + case LSSPPT_AUTH_STREAMTYPE: + pp = (char **)&a->curr[LTY_AUTH].a->streamtype; + goto string2; + case LSSPPT_HTTP_FAIL_REDIRECT: a->curr[LTY_POLICY].p->u.http.fail_redirect = reason == LEJPCB_VAL_TRUE; @@ -894,3 +960,14 @@ lws_ss_policy_get(struct lws_context *context) return args->heads[LTY_POLICY].p; } + +const lws_ss_auth_t * +lws_ss_auth_get(struct lws_context *context) +{ + struct policy_cb_args *args = (struct policy_cb_args *)context->pol_args; + + if (!args) + return NULL; + + return args->heads[LTY_AUTH].a; +} diff --git a/lib/secure-streams/private-lib-secure-streams.h b/lib/secure-streams/private-lib-secure-streams.h index f338fc6b8..8f396a715 100644 --- a/lib/secure-streams/private-lib-secure-streams.h +++ b/lib/secure-streams/private-lib-secure-streams.h @@ -296,10 +296,11 @@ typedef struct backoffs { } backoff_t; union u { - backoff_t *b; - lws_ss_x509_t *x; - lws_ss_trust_store_t *t; - lws_ss_policy_t *p; + backoff_t *b; + lws_ss_x509_t *x; + lws_ss_trust_store_t *t; + lws_ss_policy_t *p; + lws_ss_auth_t *a; }; enum { @@ -307,6 +308,7 @@ enum { LTY_X509, LTY_TRUSTSTORE, LTY_POLICY, + LTY_AUTH, _LTY_COUNT /* always last */ }; diff --git a/minimal-examples/secure-streams/minimal-secure-streams-alexa/main.c b/minimal-examples/secure-streams/minimal-secure-streams-alexa/main.c index 02f7ba03d..d07c4c723 100644 --- a/minimal-examples/secure-streams/minimal-secure-streams-alexa/main.c +++ b/minimal-examples/secure-streams/minimal-secure-streams-alexa/main.c @@ -175,14 +175,20 @@ static const char * const default_ss_policy = "]" "}" "]," + "\"auth\": [" /* available auth type bindings */ + "{" + "\"name\":" "\"lwa\"," + "\"streamtype\":" "\"api_amazon_com_lwa\"," + "\"blob\":" "0" + "}" + "]," "\"s\": [" /* the supported stream types */ - "{\"api_amazon_com_auth\": {" + "{\"api_amazon_com_lwa\": {" "\"endpoint\":" "\"api.amazon.com\"," "\"port\":" "443," "\"protocol\":" "\"h1\"," "\"http_method\":" "\"POST\"," "\"http_url\":" "\"auth/o2/token\"," - "\"plugins\":" "[]," "\"opportunistic\":" "true," "\"tls\":" "true," "\"h2q_oflow_txcr\":" "true," @@ -200,6 +206,7 @@ static const char * const default_ss_policy = "\"protocol\":" "\"h2\"," "\"http_method\":" "\"GET\"," "\"http_url\":" "\"v20160207/directives\"," + "\"use_auth\":" "\"lwa\"," "\"h2q_oflow_txcr\":" "true," "\"http_auth_header\":" "\"authorization:\"," "\"http_auth_preamble\":" "\"Bearer \"," @@ -207,7 +214,6 @@ static const char * const default_ss_policy = "\"nailed_up\":" "true," "\"long_poll\":" "true," "\"retry\":" "\"default\"," - "\"plugins\":" "[]," "\"tls\":" "true," "\"tls_trust_store\":" "\"avs_via_starfield\"" "}}," @@ -223,6 +229,7 @@ static const char * const default_ss_policy = "\"protocol\":" "\"h2\"," "\"http_method\":" "\"POST\"," "\"http_url\":" "\"v20160207/events\"," + "\"use_auth\":" "\"lwa\"," "\"opportunistic\":" "true," "\"h2q_oflow_txcr\":" "true," "\"http_auth_header\":" "\"authorization:\"," @@ -233,7 +240,6 @@ static const char * const default_ss_policy = "\"http_multipart_ss_in\":" "true," "\"rideshare\":" "\"avs_audio\"," "\"retry\":" "\"default\"," - "\"plugins\":" "[]," "\"tls\":" "true," "\"tls_trust_store\":" "\"avs_via_starfield\"" "}}," @@ -243,7 +249,7 @@ static const char * const default_ss_policy = "\"protocol\":" "\"h2\"," "\"http_method\":" "\"POST\"," "\"http_url\":" "\"v20160207/events\"," - "\"plugins\":" "[]," + "\"use_auth\":" "\"lwa\"," "\"tls\":" "true," "\"h2q_oflow_txcr\":" "true," "\"http_auth_header\":" "\"authorization:\"," diff --git a/minimal-examples/secure-streams/minimal-secure-streams-avs/main.c b/minimal-examples/secure-streams/minimal-secure-streams-avs/main.c index 9bae7d54f..b3b18f6f5 100644 --- a/minimal-examples/secure-streams/minimal-secure-streams-avs/main.c +++ b/minimal-examples/secure-streams/minimal-secure-streams-avs/main.c @@ -151,14 +151,20 @@ static const char * const default_ss_policy = "]" "}" "]," + "\"auth\": [" /* available auth type bindings */ + "{" + "\"name\":" "\"lwa\"," + "\"streamtype\":" "\"api_amazon_com_lwa\"," + "\"blob\":" "0" + "}" + "]," "\"s\": [" /* the supported stream types */ - "{\"api_amazon_com_auth\": {" + "{\"api_amazon_com_lwa\": {" "\"endpoint\":" "\"api.amazon.com\"," "\"port\":" "443," "\"protocol\":" "\"h1\"," "\"http_method\":" "\"POST\"," "\"http_url\":" "\"auth/o2/token\"," - "\"plugins\":" "[]," "\"opportunistic\":" "true," "\"tls\":" "true," "\"h2q_oflow_txcr\":" "true," @@ -176,10 +182,10 @@ static const char * const default_ss_policy = "\"h2q_oflow_txcr\":" "true," "\"http_auth_header\":" "\"authorization:\"," "\"http_auth_preamble\":" "\"Bearer \"," + "\"use_auth\":" "\"lwa\"," "\"nailed_up\":" "true," "\"long_poll\":" "true," "\"retry\":" "\"default\"," - "\"plugins\":" "[]," "\"tls\":" "true," "\"tls_trust_store\":" "\"avs_via_starfield\"" "}}," @@ -191,6 +197,7 @@ static const char * const default_ss_policy = "\"http_url\":" "\"v20160207/events\"," "\"http_no_content_length\":" "true," "\"h2q_oflow_txcr\":" "true," + "\"use_auth\":" "\"lwa\"," "\"http_auth_header\":" "\"authorization:\"," "\"http_auth_preamble\":" "\"Bearer \"," "\"http_multipart_name\":" "\"metadata\"," @@ -200,7 +207,6 @@ static const char * const default_ss_policy = #endif "\"rideshare\":" "\"avs_audio\"," "\"retry\":" "\"default\"," - "\"plugins\":" "[]," "\"tls\":" "true," "\"tls_trust_store\":" "\"avs_via_starfield\"" "}}," @@ -211,12 +217,12 @@ static const char * const default_ss_policy = "\"http_method\":" "\"POST\"," "\"http_url\":" "\"v20160207/events\"," "\"http_no_content_length\":" "true," - "\"plugins\":" "[]," "\"tls\":" "true," "\"h2q_oflow_txcr\":" "true," #if 1 "\"http_multipart_ss_in\":" "true," #endif + "\"use_auth\":" "\"lwa\"," "\"http_auth_header\":" "\"authorization:\"," "\"http_auth_preamble\":" "\"Bearer \"," "\"http_multipart_name\":" "\"audio\"," @@ -345,6 +351,8 @@ int main(int argc, const char **argv) nl.notify_cb = app_system_state_nf; info.register_notifier_list = app_notifier_list; + puts(default_ss_policy); + context = lws_create_context(&info); if (!context) { lwsl_err("lws init failed\n"); diff --git a/minimal-examples/secure-streams/minimal-secure-streams-policy2c/minimal-secure-streams.c b/minimal-examples/secure-streams/minimal-secure-streams-policy2c/minimal-secure-streams.c index f45f4a95a..70b6d89fd 100644 --- a/minimal-examples/secure-streams/minimal-secure-streams-policy2c/minimal-secure-streams.c +++ b/minimal-examples/secure-streams/minimal-secure-streams-policy2c/minimal-secure-streams.c @@ -74,6 +74,7 @@ int main(int argc, const char **argv) struct lws_context_creation_info info; size_t json_size = 0, est = 0; struct lws_context *context; + const lws_ss_auth_t *auth; char prev[128], curr[128]; int unique_rbo = 0, m, n; char buf[64], buf1[64]; @@ -356,6 +357,40 @@ int main(int argc, const char **argv) pol = pol->next; } + /* + * The auth map + */ + + auth = lws_ss_auth_get(context); + if (auth) + printf("\nstatic const lws_ss_auth_t "); + prev[0] = '\0'; + + while (auth) { + lws_snprintf(curr, sizeof(curr), "_ssau_%s", + purify_csymbol(auth->name, buf, sizeof(buf))); + + printf("%s = {\n", curr); + if (prev[0]) + printf("\t.next = (void *)&%s,\n", prev); + + printf("\t.name = \"%s\",\n", auth->name); + printf("\t.streamtype = \"%s\",\n", auth->streamtype); + printf("\t.blob = %d,\n", auth->blob_index); + printf("}"); + if (auth->next) + printf(","); + else + printf(";"); + printf("\n"); + + lws_strncpy(prev, curr, sizeof(prev)); + + auth = auth->next; + } + + if (lws_ss_auth_get(context)) + printf("\n"); /* @@ -392,6 +427,10 @@ int main(int argc, const char **argv) printf("\t.socks5_proxy = \"%s\",\n", pol->socks5_proxy); + if (pol->auth) + printf("\t.auth = &_ssau_%s,\n", + purify_csymbol(pol->auth->name, buf, sizeof(buf))); + { lws_ss_metadata_t *nv = pol->metadata, *last = NULL;