Update Files
This commit is contained in:
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* LWA auth support for Secure Streams
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
typedef struct ss_api_amazon_auth {
|
||||
struct lws_ss_handle *ss;
|
||||
void *opaque_data;
|
||||
/* ... application specific state ... */
|
||||
struct lejp_ctx jctx;
|
||||
size_t pos;
|
||||
int expires_secs;
|
||||
} ss_api_amazon_auth_t;
|
||||
|
||||
static const char * const lejp_tokens_lwa[] = {
|
||||
"access_token",
|
||||
"expires_in",
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LSSPPT_ACCESS_TOKEN,
|
||||
LSSPPT_EXPIRES_IN,
|
||||
} lejp_tokens_t;
|
||||
|
||||
enum {
|
||||
AUTH_IDX_LWA,
|
||||
AUTH_IDX_ROOT,
|
||||
};
|
||||
|
||||
static void
|
||||
lws_ss_sys_auth_api_amazon_com_kick(lws_sorted_usec_list_t *sul)
|
||||
{
|
||||
struct lws_context *context = lws_container_of(sul, struct lws_context,
|
||||
sul_api_amazon_com_kick);
|
||||
|
||||
lws_state_transition_steps(&context->mgr_system,
|
||||
LWS_SYSTATE_OPERATIONAL);
|
||||
}
|
||||
|
||||
static void
|
||||
lws_ss_sys_auth_api_amazon_com_renew(lws_sorted_usec_list_t *sul)
|
||||
{
|
||||
struct lws_context *context = lws_container_of(sul, struct lws_context,
|
||||
sul_api_amazon_com);
|
||||
|
||||
lws_ss_sys_auth_api_amazon_com(context);
|
||||
}
|
||||
|
||||
static signed char
|
||||
auth_api_amazon_com_parser_cb(struct lejp_ctx *ctx, char reason)
|
||||
{
|
||||
ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)ctx->user;
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
lws_system_blob_t *blob;
|
||||
|
||||
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
|
||||
return 0;
|
||||
|
||||
switch (ctx->path_match - 1) {
|
||||
case LSSPPT_ACCESS_TOKEN:
|
||||
if (!ctx->npos)
|
||||
break;
|
||||
|
||||
blob = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH,
|
||||
AUTH_IDX_LWA);
|
||||
if (!blob)
|
||||
return -1;
|
||||
|
||||
if (lws_system_blob_heap_append(blob,
|
||||
(const uint8_t *)ctx->buf,
|
||||
ctx->npos)) {
|
||||
lwsl_err("%s: unable to store auth token\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case LSSPPT_EXPIRES_IN:
|
||||
m->expires_secs = atoi(ctx->buf);
|
||||
lws_sul_schedule(context, 0, &context->sul_api_amazon_com,
|
||||
lws_ss_sys_auth_api_amazon_com_renew,
|
||||
(lws_usec_t)m->expires_secs * LWS_US_PER_SEC);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* secure streams payload interface */
|
||||
|
||||
static lws_ss_state_return_t
|
||||
ss_api_amazon_auth_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
|
||||
{
|
||||
ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)userobj;
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
lws_system_blob_t *ab;
|
||||
#if !defined(LWS_WITH_NO_LOGS)
|
||||
size_t total;
|
||||
#endif
|
||||
int n;
|
||||
|
||||
ab = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, AUTH_IDX_LWA);
|
||||
/* coverity */
|
||||
if (!ab)
|
||||
return LWSSSSRET_DISCONNECT_ME;
|
||||
|
||||
if (buf) {
|
||||
if (flags & LWSSS_FLAG_SOM) {
|
||||
lejp_construct(&m->jctx, auth_api_amazon_com_parser_cb,
|
||||
m, lejp_tokens_lwa,
|
||||
LWS_ARRAY_SIZE(lejp_tokens_lwa));
|
||||
lws_system_blob_heap_empty(ab);
|
||||
}
|
||||
|
||||
n = lejp_parse(&m->jctx, buf, (int)len);
|
||||
if (n < 0) {
|
||||
lejp_destruct(&m->jctx);
|
||||
lws_system_blob_destroy(
|
||||
lws_system_get_blob(context,
|
||||
LWS_SYSBLOB_TYPE_AUTH,
|
||||
AUTH_IDX_LWA));
|
||||
|
||||
return LWSSSSRET_DISCONNECT_ME;
|
||||
}
|
||||
}
|
||||
if (!(flags & LWSSS_FLAG_EOM))
|
||||
return LWSSSSRET_OK;
|
||||
|
||||
/* we should have the auth token now */
|
||||
|
||||
#if !defined(LWS_WITH_NO_LOGS)
|
||||
total = lws_system_blob_get_size(ab);
|
||||
lwsl_notice("%s: acquired %u-byte api.amazon.com auth token, exp %ds\n",
|
||||
__func__, (unsigned int)total, m->expires_secs);
|
||||
#endif
|
||||
|
||||
lejp_destruct(&m->jctx);
|
||||
|
||||
/* we move the system state at auth connection close */
|
||||
|
||||
return LWSSSSRET_DISCONNECT_ME;
|
||||
}
|
||||
|
||||
static lws_ss_state_return_t
|
||||
ss_api_amazon_auth_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,
|
||||
size_t *len, int *flags)
|
||||
{
|
||||
ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)userobj;
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
lws_system_blob_t *ab;
|
||||
size_t total;
|
||||
int n;
|
||||
|
||||
/*
|
||||
* We send out auth slot AUTH_IDX_ROOT, it's the LWA user / device
|
||||
* identity token
|
||||
*/
|
||||
|
||||
ab = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, AUTH_IDX_ROOT);
|
||||
if (!ab)
|
||||
return LWSSSSRET_DESTROY_ME;
|
||||
|
||||
total = lws_system_blob_get_size(ab);
|
||||
|
||||
n = lws_system_blob_get(ab, buf, len, m->pos);
|
||||
if (n < 0)
|
||||
return LWSSSSRET_TX_DONT_SEND;
|
||||
|
||||
if (!m->pos)
|
||||
*flags |= LWSSS_FLAG_SOM;
|
||||
|
||||
m->pos += *len;
|
||||
|
||||
if (m->pos == total) {
|
||||
*flags |= LWSSS_FLAG_EOM;
|
||||
m->pos = 0; /* for next time */
|
||||
}
|
||||
|
||||
return LWSSSSRET_OK;
|
||||
}
|
||||
|
||||
static lws_ss_state_return_t
|
||||
ss_api_amazon_auth_state(void *userobj, void *sh, lws_ss_constate_t state,
|
||||
lws_ss_tx_ordinal_t ack)
|
||||
{
|
||||
ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)userobj;
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
lws_system_blob_t *ab;
|
||||
size_t s;
|
||||
|
||||
lwsl_info("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name((int)state),
|
||||
(unsigned int)ack);
|
||||
|
||||
ab = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, AUTH_IDX_ROOT);
|
||||
/* coverity */
|
||||
if (!ab)
|
||||
return LWSSSSRET_DESTROY_ME;
|
||||
|
||||
switch (state) {
|
||||
case LWSSSCS_CREATING:
|
||||
//if (lws_ss_set_metadata(m->ss, "ctype", "application/json", 16))
|
||||
// return LWSSSSRET_DESTROY_ME;
|
||||
/* fallthru */
|
||||
case LWSSSCS_CONNECTING:
|
||||
s = lws_system_blob_get_size(ab);
|
||||
if (!s)
|
||||
lwsl_debug("%s: no auth blob\n", __func__);
|
||||
m->pos = 0;
|
||||
return lws_ss_request_tx_len(m->ss, (unsigned long)s);
|
||||
|
||||
case LWSSSCS_DISCONNECTED:
|
||||
/*
|
||||
* We defer moving the system state forward until we have
|
||||
* closed our connection + tls for the auth action... this is
|
||||
* because on small systems, we need that memory recovered
|
||||
* before we can make another connection subsequently.
|
||||
*
|
||||
* At this point, we're ultimately being called from within
|
||||
* the wsi close process, the tls tunnel is not freed yet.
|
||||
* Use a sul to actually do it next time around the event loop
|
||||
* when the close process for the auth wsi has completed and
|
||||
* the related tls is already freed.
|
||||
*/
|
||||
s = lws_system_blob_get_size(ab);
|
||||
|
||||
if (s && context->mgr_system.state != LWS_SYSTATE_OPERATIONAL)
|
||||
lws_sul_schedule(context, 0,
|
||||
&context->sul_api_amazon_com_kick,
|
||||
lws_ss_sys_auth_api_amazon_com_kick, 1);
|
||||
|
||||
context->hss_auth = NULL;
|
||||
return LWSSSSRET_DESTROY_ME;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return LWSSSSRET_OK;
|
||||
}
|
||||
|
||||
int
|
||||
lws_ss_sys_auth_api_amazon_com(struct lws_context *context)
|
||||
{
|
||||
lws_ss_info_t ssi;
|
||||
|
||||
if (context->hss_auth) /* already exists */
|
||||
return 0;
|
||||
|
||||
/* We're making an outgoing secure stream ourselves */
|
||||
|
||||
memset(&ssi, 0, sizeof(ssi));
|
||||
ssi.handle_offset = offsetof(ss_api_amazon_auth_t, ss);
|
||||
ssi.opaque_user_data_offset = offsetof(ss_api_amazon_auth_t, opaque_data);
|
||||
ssi.rx = ss_api_amazon_auth_rx;
|
||||
ssi.tx = ss_api_amazon_auth_tx;
|
||||
ssi.state = ss_api_amazon_auth_state;
|
||||
ssi.user_alloc = sizeof(ss_api_amazon_auth_t);
|
||||
ssi.streamtype = "api_amazon_com_auth";
|
||||
|
||||
if (lws_ss_create(context, 0, &ssi, context, &context->hss_auth,
|
||||
NULL, NULL)) {
|
||||
lwsl_info("%s: Create LWA auth ss failed (policy?)\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
574
Kinc/Sources/kinc/libs/secure-streams/system/auth-sigv4/sign.c
Normal file
574
Kinc/Sources/kinc/libs/secure-streams/system/auth-sigv4/sign.c
Normal file
@ -0,0 +1,574 @@
|
||||
/*
|
||||
* Sigv4 support for Secure Streams
|
||||
*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2020 Andy Green <andy@warmcat.com>
|
||||
* securestreams-dev@amazon.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.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
struct sigv4_header {
|
||||
const char * name;
|
||||
const char * value;
|
||||
};
|
||||
|
||||
#define MAX_HEADER_NUM 8
|
||||
struct sigv4 {
|
||||
struct sigv4_header headers[MAX_HEADER_NUM];
|
||||
uint8_t hnum;
|
||||
char ymd[10]; /*YYYYMMDD*/
|
||||
const char *timestamp;
|
||||
const char *payload_hash;
|
||||
const char *region;
|
||||
const char *service;
|
||||
};
|
||||
|
||||
static const uint8_t blob_idx[] = {
|
||||
LWS_SYSBLOB_TYPE_EXT_AUTH1,
|
||||
LWS_SYSBLOB_TYPE_EXT_AUTH2,
|
||||
LWS_SYSBLOB_TYPE_EXT_AUTH3,
|
||||
LWS_SYSBLOB_TYPE_EXT_AUTH4,
|
||||
};
|
||||
|
||||
enum {
|
||||
LWS_SS_SIGV4_KEYID,
|
||||
LWS_SS_SIGV4_KEY,
|
||||
LWS_SS_SIGV4_BLOB_SLOTS
|
||||
};
|
||||
|
||||
static inline int add_header(struct sigv4 *s, const char *name, const char *value)
|
||||
{
|
||||
if (s->hnum >= MAX_HEADER_NUM) {
|
||||
lwsl_err("%s too many sigv4 headers\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->headers[s->hnum].name = name;
|
||||
s->headers[s->hnum].value = value;
|
||||
s->hnum++;
|
||||
|
||||
if (!strncmp(name, "x-amz-content-sha256", strlen("x-amz-content-sha256")))
|
||||
s->payload_hash = value;
|
||||
|
||||
if (!strncmp(name, "x-amz-date", strlen("x-amz-date"))) {
|
||||
s->timestamp = value;
|
||||
strncpy(s->ymd, value, 8);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
cmp_header(const void * a, const void * b)
|
||||
{
|
||||
return strcmp(((struct sigv4_header *)a)->name,
|
||||
((struct sigv4_header *)b)->name);
|
||||
}
|
||||
|
||||
static int
|
||||
init_sigv4(struct lws *wsi, struct lws_ss_handle *h, struct sigv4 *s)
|
||||
{
|
||||
lws_ss_metadata_t *polmd = h->policy->metadata;
|
||||
int m = 0;
|
||||
|
||||
add_header(s, "host:", lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST));
|
||||
|
||||
while (polmd) {
|
||||
if (polmd->value__may_own_heap &&
|
||||
((uint8_t *)polmd->value__may_own_heap)[0] &&
|
||||
h->metadata[m].value__may_own_heap) {
|
||||
/* consider all headers start with "x-amz-" need to be signed */
|
||||
if (!strncmp(polmd->value__may_own_heap, "x-amz-",
|
||||
strlen("x-amz-"))) {
|
||||
if (add_header(s, polmd->value__may_own_heap,
|
||||
h->metadata[m].value__may_own_heap))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (!strcmp(h->metadata[m].name, h->policy->aws_region) &&
|
||||
h->metadata[m].value__may_own_heap)
|
||||
s->region = h->metadata[m].value__may_own_heap;
|
||||
|
||||
if (!strcmp(h->metadata[m].name, h->policy->aws_service) &&
|
||||
h->metadata[m].value__may_own_heap)
|
||||
s->service = h->metadata[m].value__may_own_heap;
|
||||
|
||||
m++;
|
||||
polmd = polmd->next;
|
||||
}
|
||||
|
||||
qsort(s->headers, s->hnum, sizeof(struct sigv4_header), cmp_header);
|
||||
|
||||
#if 0
|
||||
do {
|
||||
int i;
|
||||
for (i= 0; i<s->hnum; i++)
|
||||
lwsl_debug("%s hdr %s %s\n", __func__,
|
||||
s->headers[i].name, s->headers[i].value);
|
||||
|
||||
lwsl_debug("%s service: %s region: %s\n", __func__,
|
||||
s->service, s->region);
|
||||
} while(0);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
bin2hex(uint8_t *in, size_t len, char *out)
|
||||
{
|
||||
static const char *hex = "0123456789abcdef";
|
||||
size_t n;
|
||||
|
||||
for (n = 0; n < len; n++) {
|
||||
*out++ = hex[(in[n] >> 4) & 0xf];
|
||||
*out++ = hex[in[n] & 15];
|
||||
}
|
||||
*out = '\0';
|
||||
}
|
||||
|
||||
static int
|
||||
hmacsha256(const uint8_t *key, size_t keylen, const uint8_t *txt,
|
||||
size_t txtlen, uint8_t *digest)
|
||||
{
|
||||
struct lws_genhmac_ctx hmacctx;
|
||||
|
||||
if (lws_genhmac_init(&hmacctx, LWS_GENHMAC_TYPE_SHA256,
|
||||
key, keylen))
|
||||
return -1;
|
||||
|
||||
if (lws_genhmac_update(&hmacctx, txt, txtlen)) {
|
||||
lwsl_err("%s: hmac computation failed\n", __func__);
|
||||
lws_genhmac_destroy(&hmacctx, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lws_genhmac_destroy(&hmacctx, digest)) {
|
||||
lwsl_err("%s: problem destroying hmac\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* cut the last byte of the str */
|
||||
static inline int hash_update_bite_str(struct lws_genhash_ctx *ctx, const char * str)
|
||||
{
|
||||
int ret = 0;
|
||||
if ((ret = lws_genhash_update(ctx, (void *)str, strlen(str)-1))) {
|
||||
lws_genhash_destroy(ctx, NULL);
|
||||
lwsl_err("%s err %d line \n", __func__, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int hash_update_str(struct lws_genhash_ctx *ctx, const char * str)
|
||||
{
|
||||
int ret = 0;
|
||||
if ((ret = lws_genhash_update(ctx, (void *)str, strlen(str)))) {
|
||||
lws_genhash_destroy(ctx, NULL);
|
||||
lwsl_err("%s err %d \n", __func__, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
build_sign_string(struct lws *wsi, char *buf, size_t bufsz,
|
||||
struct lws_ss_handle *h, struct sigv4 *s)
|
||||
{
|
||||
char hash[65], *end = &buf[bufsz - 1], *start;
|
||||
struct lws_genhash_ctx hash_ctx;
|
||||
uint8_t hash_bin[32];
|
||||
int i, ret = 0;
|
||||
|
||||
start = buf;
|
||||
|
||||
if ((ret = lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256))) {
|
||||
lws_genhash_destroy(&hash_ctx, NULL);
|
||||
lwsl_err("%s genhash init err %d \n", __func__, ret);
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* hash canonical_request
|
||||
*/
|
||||
|
||||
if (hash_update_str(&hash_ctx, h->policy->u.http.method) ||
|
||||
hash_update_str(&hash_ctx, "\n"))
|
||||
return -1;
|
||||
if (hash_update_str(&hash_ctx, lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI)) ||
|
||||
hash_update_str(&hash_ctx, "\n"))
|
||||
return -1;
|
||||
|
||||
/* TODO, append query string */
|
||||
if (hash_update_str(&hash_ctx, "\n"))
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < s->hnum; i++) {
|
||||
if (hash_update_str(&hash_ctx, s->headers[i].name) ||
|
||||
hash_update_str(&hash_ctx, s->headers[i].value) ||
|
||||
hash_update_str(&hash_ctx, "\n"))
|
||||
return -1;
|
||||
|
||||
}
|
||||
if (hash_update_str(&hash_ctx, "\n"))
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < s->hnum-1; i++) {
|
||||
if (hash_update_bite_str(&hash_ctx, s->headers[i].name) ||
|
||||
hash_update_str(&hash_ctx, ";"))
|
||||
return -1;
|
||||
}
|
||||
if (hash_update_bite_str(&hash_ctx, s->headers[i].name) ||
|
||||
hash_update_str(&hash_ctx, "\n") ||
|
||||
hash_update_str(&hash_ctx, s->payload_hash))
|
||||
return -1;
|
||||
|
||||
if ((ret = lws_genhash_destroy(&hash_ctx, hash_bin))) {
|
||||
lws_genhash_destroy(&hash_ctx, NULL);
|
||||
lwsl_err("%s lws_genhash error \n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bin2hex(hash_bin, sizeof(hash_bin), hash);
|
||||
/*
|
||||
* build sign string like the following
|
||||
*
|
||||
* "AWS4-HMAC-SHA256" + "\n" +
|
||||
* timeStampISO8601Format + "\n" +
|
||||
* date.Format(<YYYYMMDD>) + "/" + <region> + "/" + <service> + "/aws4_request" + "\n" +
|
||||
* Hex(SHA256Hash(<CanonicalRequest>))
|
||||
*/
|
||||
buf = start;
|
||||
|
||||
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "%s\n",
|
||||
"AWS4-HMAC-SHA256");
|
||||
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "%s\n",
|
||||
s->timestamp);
|
||||
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "%s/%s/%s/%s\n",
|
||||
s->ymd, s->region, s->service, "aws4_request");
|
||||
|
||||
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "%s", hash);
|
||||
*buf++ = '\0';
|
||||
|
||||
assert(buf <= start + bufsz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* DateKey = HMAC-SHA256("AWS4"+"<SecretAccessKey>", "<YYYYMMDD>")
|
||||
* DateRegionKey = HMAC-SHA256(<DateKey>, "<aws-region>")
|
||||
* DateRegionServiceKey = HMAC-SHA256(<DateRegionKey>, "<aws-service>")
|
||||
* SigningKey = HMAC-SHA256(<DateRegionServiceKey>, "aws4_request")
|
||||
*/
|
||||
static int
|
||||
calc_signing_key(struct lws *wsi, struct lws_ss_handle *h,
|
||||
struct sigv4 *s, uint8_t *sign_key)
|
||||
{
|
||||
uint8_t key[128], date_key[32], and_region_key[32],
|
||||
and_service_key[32], *kb;
|
||||
lws_system_blob_t *ab;
|
||||
size_t keylen;
|
||||
int n;
|
||||
|
||||
ab = lws_system_get_blob(wsi->a.context,
|
||||
blob_idx[h->policy->auth->blob_index],
|
||||
LWS_SS_SIGV4_KEY);
|
||||
if (!ab)
|
||||
return -1;
|
||||
|
||||
kb = key;
|
||||
|
||||
*kb++ = 'A';
|
||||
*kb++ = 'W';
|
||||
*kb++ = 'S';
|
||||
*kb++ = '4';
|
||||
|
||||
keylen = sizeof(key) - 4;
|
||||
if (lws_system_blob_get_size(ab) > keylen - 1)
|
||||
return -1;
|
||||
|
||||
n = lws_system_blob_get(ab, kb, &keylen, 0);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
||||
kb[keylen] = '\0';
|
||||
|
||||
hmacsha256((const uint8_t *)key, strlen((const char *)key),
|
||||
(const uint8_t *)s->ymd, strlen(s->ymd), date_key);
|
||||
|
||||
hmacsha256(date_key, sizeof(date_key), (const uint8_t *)s->region,
|
||||
strlen(s->region), and_region_key);
|
||||
|
||||
hmacsha256(and_region_key, sizeof(and_region_key),
|
||||
(const uint8_t *)s->service,
|
||||
strlen(s->service), and_service_key);
|
||||
|
||||
hmacsha256(and_service_key, sizeof(and_service_key),
|
||||
(uint8_t *)"aws4_request",
|
||||
strlen("aws4_request"), sign_key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sample auth string:
|
||||
*
|
||||
* 'Authorization: AWS4-HMAC-SHA256 Credential=AKIAVHWASOFE7TJ7ZUQY/20200731/us-west-2/s3/aws4_request,
|
||||
* SignedHeaders=host;x-amz-content-sha256;x-amz-date, \
|
||||
* Signature=ad9fb75ff3b46c7990e3e8f090abfdd6c01fd67761a517111694377e20698377'
|
||||
*/
|
||||
static int
|
||||
build_auth_string(struct lws *wsi, char * buf, size_t bufsz,
|
||||
struct lws_ss_handle *h, struct sigv4 *s,
|
||||
uint8_t *signature_bin)
|
||||
{
|
||||
#if defined(_DEBUG)
|
||||
char *start = buf;
|
||||
#endif
|
||||
char *end = &buf[bufsz - 1];
|
||||
char *c;
|
||||
lws_system_blob_t *ab;
|
||||
size_t keyidlen = 128; // max keyid len is 128
|
||||
int n;
|
||||
|
||||
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "%s",
|
||||
"AWS4-HMAC-SHA256 ");
|
||||
|
||||
ab = lws_system_get_blob(wsi->a.context,
|
||||
blob_idx[h->policy->auth->blob_index],
|
||||
LWS_SS_SIGV4_KEYID);
|
||||
if (!ab)
|
||||
return -1;
|
||||
|
||||
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "%s",
|
||||
"Credential=");
|
||||
n = lws_system_blob_get(ab,(uint8_t *)buf, &keyidlen, 0);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
buf += keyidlen;
|
||||
|
||||
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "/%s/%s/%s/%s, ",
|
||||
s->ymd, s->region, s->service, "aws4_request");
|
||||
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf), "%s",
|
||||
"SignedHeaders=");
|
||||
for (n = 0; n < s->hnum; n++) {
|
||||
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf),
|
||||
"%s",s->headers[n].name);
|
||||
buf--; /* remove ':' */
|
||||
*buf++ = ';';
|
||||
}
|
||||
c = buf - 1;
|
||||
*c = ','; /* overwrite ';' back to ',' */
|
||||
|
||||
buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf),
|
||||
"%s", " Signature=");
|
||||
bin2hex(signature_bin, 32, buf);
|
||||
|
||||
#if defined(_DEBUG)
|
||||
assert(buf + 65 <= start + bufsz);
|
||||
|
||||
lwsl_debug("%s %s\n", __func__, start);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
lws_ss_apply_sigv4(struct lws *wsi, struct lws_ss_handle *h,
|
||||
unsigned char **p, unsigned char *end)
|
||||
{
|
||||
uint8_t buf[512], sign_key[32], signature_bin[32], *bp;
|
||||
struct sigv4 s;
|
||||
|
||||
memset(&s, 0, sizeof(s));
|
||||
|
||||
bp = buf;
|
||||
|
||||
init_sigv4(wsi, h, &s);
|
||||
if (!s.timestamp || !s.payload_hash) {
|
||||
lwsl_err("%s missing headers\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (build_sign_string(wsi, (char *)bp, sizeof(buf), h, &s))
|
||||
return -1;
|
||||
|
||||
if (calc_signing_key(wsi, h, &s, sign_key))
|
||||
return -1;
|
||||
|
||||
hmacsha256(sign_key, sizeof(sign_key), (const uint8_t *)buf,
|
||||
strlen((const char *)buf), signature_bin);
|
||||
|
||||
bp = buf; /* reuse for auth_str */
|
||||
if (build_auth_string(wsi, (char *)bp, sizeof(buf), h, &s,
|
||||
signature_bin))
|
||||
return -1;
|
||||
|
||||
if (lws_add_http_header_by_name(wsi,
|
||||
(const uint8_t *)"Authorization:", buf,
|
||||
(int)strlen((const char*)buf), p, end))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_ss_sigv4_set_aws_key(struct lws_context* context, uint8_t idx,
|
||||
const char * keyid, const char * key)
|
||||
{
|
||||
const char * s[] = { keyid, key };
|
||||
lws_system_blob_t *ab;
|
||||
int i;
|
||||
|
||||
if (idx > LWS_ARRAY_SIZE(blob_idx))
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < LWS_SS_SIGV4_BLOB_SLOTS; i++) {
|
||||
ab = lws_system_get_blob(context, blob_idx[idx], i);
|
||||
if (!ab)
|
||||
return -1;
|
||||
|
||||
lws_system_blob_heap_empty(ab);
|
||||
|
||||
if (lws_system_blob_heap_append(ab, (const uint8_t *)s[i],
|
||||
strlen(s[i]))) {
|
||||
lwsl_err("%s: can't store %d \n", __func__, i);
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(__linux__) || defined(__APPLE__) || defined(WIN32) || \
|
||||
defined(__FreeBSD__) || defined(__NetBSD__) || defined(__ANDROID__) || \
|
||||
defined(__sun) || defined(__OpenBSD__) || defined(__NuttX__)
|
||||
|
||||
/* ie, if we have filesystem ops */
|
||||
|
||||
int
|
||||
lws_aws_filesystem_credentials_helper(const char *path, const char *kid,
|
||||
const char *ak, char **aws_keyid,
|
||||
char **aws_key)
|
||||
{
|
||||
char *str = NULL, *val = NULL, *line = NULL, sth[128];
|
||||
size_t len = sizeof(sth);
|
||||
const char *home = "";
|
||||
int i, poff = 0;
|
||||
ssize_t rd;
|
||||
FILE *fp;
|
||||
|
||||
*aws_keyid = *aws_key = NULL;
|
||||
|
||||
if (path[0] == '~') {
|
||||
home = getenv("HOME");
|
||||
if (home && strlen(home) > sizeof(sth) - 1) /* coverity */
|
||||
return -1;
|
||||
else {
|
||||
if (!home)
|
||||
home = "";
|
||||
|
||||
poff = 1;
|
||||
}
|
||||
}
|
||||
lws_snprintf(sth, sizeof(sth), "%s%s", home, path + poff);
|
||||
|
||||
fp = fopen(sth, "r");
|
||||
if (!fp) {
|
||||
lwsl_err("%s can't open '%s'\n", __func__, sth);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((rd = getline(&line, &len, fp)) != -1) {
|
||||
for (i = 0; i < 2; i++) {
|
||||
size_t slen;
|
||||
|
||||
if (strncmp(line, i ? kid : ak, strlen(i ? kid : ak)))
|
||||
continue;
|
||||
|
||||
str = strchr(line, '=');
|
||||
if (!str)
|
||||
continue;
|
||||
|
||||
str++;
|
||||
|
||||
/* only read the first key for each */
|
||||
if (*(i ? aws_keyid : aws_key))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Trim whitespace from the start and end
|
||||
*/
|
||||
|
||||
slen = (size_t)(rd - lws_ptr_diff(str, line));
|
||||
|
||||
while (slen && *str == ' ') {
|
||||
str++;
|
||||
slen--;
|
||||
}
|
||||
|
||||
while (slen && (str[slen - 1] == '\r' ||
|
||||
str[slen - 1] == '\n' ||
|
||||
str[slen - 1] == ' '))
|
||||
slen--;
|
||||
|
||||
val = malloc(slen + 1);
|
||||
if (!val)
|
||||
goto bail;
|
||||
|
||||
strncpy(val, str, slen);
|
||||
val[slen] = '\0';
|
||||
|
||||
*(i ? aws_keyid : aws_key) = val;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bail:
|
||||
fclose(fp);
|
||||
|
||||
if (line)
|
||||
free(line);
|
||||
|
||||
if (!*aws_keyid || !*aws_key) {
|
||||
if (*aws_keyid) {
|
||||
free(*aws_keyid);
|
||||
*aws_keyid = NULL;
|
||||
}
|
||||
if (*aws_key) {
|
||||
free(*aws_key);
|
||||
*aws_key = NULL;
|
||||
}
|
||||
lwsl_err("%s can't find aws credentials! \
|
||||
please check %s\n", __func__, path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
lwsl_info("%s: '%s' '%s'\n", __func__, *aws_keyid, *aws_key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Captive portal detect for Secure Streams
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
typedef struct ss_cpd {
|
||||
struct lws_ss_handle *ss;
|
||||
void *opaque_data;
|
||||
/* ... application specific state ... */
|
||||
|
||||
lws_sorted_usec_list_t sul;
|
||||
} ss_cpd_t;
|
||||
|
||||
static lws_ss_state_return_t
|
||||
ss_cpd_state(void *userobj, void *sh, lws_ss_constate_t state,
|
||||
lws_ss_tx_ordinal_t ack)
|
||||
{
|
||||
ss_cpd_t *m = (ss_cpd_t *)userobj;
|
||||
struct lws_context *cx = (struct lws_context *)m->opaque_data;
|
||||
|
||||
lwsl_ss_info(m->ss, "%s, ord 0x%x\n", lws_ss_state_name((int)state),
|
||||
(unsigned int)ack);
|
||||
|
||||
switch (state) {
|
||||
case LWSSSCS_CREATING:
|
||||
lws_ss_start_timeout(m->ss, 3 * LWS_US_PER_SEC);
|
||||
return lws_ss_request_tx(m->ss);
|
||||
|
||||
case LWSSSCS_QOS_ACK_REMOTE:
|
||||
lws_system_cpd_set(cx, LWS_CPD_INTERNET_OK);
|
||||
cx->ss_cpd = NULL;
|
||||
return LWSSSSRET_DESTROY_ME;
|
||||
|
||||
case LWSSSCS_TIMEOUT:
|
||||
case LWSSSCS_ALL_RETRIES_FAILED:
|
||||
case LWSSSCS_DISCONNECTED:
|
||||
/*
|
||||
* First result reported sticks... if nothing else, this will
|
||||
* cover the situation we didn't connect to anything
|
||||
*/
|
||||
lws_system_cpd_set(cx, LWS_CPD_NO_INTERNET);
|
||||
cx->ss_cpd = NULL;
|
||||
return LWSSSSRET_DESTROY_ME;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return LWSSSSRET_OK;
|
||||
}
|
||||
|
||||
static const lws_ss_info_t ssi_cpd = {
|
||||
.handle_offset = offsetof(ss_cpd_t, ss),
|
||||
.opaque_user_data_offset = offsetof(ss_cpd_t, opaque_data),
|
||||
.state = ss_cpd_state,
|
||||
.user_alloc = sizeof(ss_cpd_t),
|
||||
.streamtype = "captive_portal_detect",
|
||||
};
|
||||
|
||||
int
|
||||
lws_ss_sys_cpd(struct lws_context *cx)
|
||||
{
|
||||
if (cx->ss_cpd) {
|
||||
lwsl_cx_notice(cx, "CPD already ongoing");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lws_ss_create(cx, 0, &ssi_cpd, cx, &cx->ss_cpd, NULL, NULL)) {
|
||||
lwsl_cx_info(cx, "Create stream failed (policy?)");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Policy fetching for Secure Streams
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <private-lib-core.h>
|
||||
|
||||
typedef struct ss_fetch_policy {
|
||||
struct lws_ss_handle *ss;
|
||||
void *opaque_data;
|
||||
/* ... application specific state ... */
|
||||
|
||||
lws_sorted_usec_list_t sul;
|
||||
|
||||
uint8_t partway;
|
||||
} ss_fetch_policy_t;
|
||||
|
||||
/* secure streams payload interface */
|
||||
|
||||
static lws_ss_state_return_t
|
||||
ss_fetch_policy_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
|
||||
{
|
||||
ss_fetch_policy_t *m = (ss_fetch_policy_t *)userobj;
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
|
||||
if (flags & LWSSS_FLAG_SOM) {
|
||||
if (lws_ss_policy_parse_begin(context, 0))
|
||||
return LWSSSSRET_OK;
|
||||
m->partway = 1;
|
||||
}
|
||||
|
||||
if (len && lws_ss_policy_parse(context, buf, len) < 0)
|
||||
return LWSSSSRET_OK;
|
||||
|
||||
if (flags & LWSSS_FLAG_EOM)
|
||||
m->partway = 2;
|
||||
|
||||
return LWSSSSRET_OK;
|
||||
}
|
||||
|
||||
static lws_ss_state_return_t
|
||||
ss_fetch_policy_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,
|
||||
size_t *len, int *flags)
|
||||
{
|
||||
return LWSSSSRET_TX_DONT_SEND;
|
||||
}
|
||||
|
||||
static void
|
||||
policy_set(lws_sorted_usec_list_t *sul)
|
||||
{
|
||||
ss_fetch_policy_t *m = lws_container_of(sul, ss_fetch_policy_t, sul);
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
|
||||
/*
|
||||
* We get called if the policy parse was successful, just after the
|
||||
* ss connection close that was using the vhost from the old policy
|
||||
*/
|
||||
|
||||
lws_ss_destroy(&m->ss);
|
||||
|
||||
if (lws_ss_policy_set(context, "updated"))
|
||||
lwsl_err("%s: policy set failed\n", __func__);
|
||||
else {
|
||||
context->policy_updated = 1;
|
||||
#if defined(LWS_WITH_SYS_STATE)
|
||||
lws_state_transition_steps(&context->mgr_system,
|
||||
LWS_SYSTATE_OPERATIONAL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static lws_ss_state_return_t
|
||||
ss_fetch_policy_state(void *userobj, void *sh, lws_ss_constate_t state,
|
||||
lws_ss_tx_ordinal_t ack)
|
||||
{
|
||||
ss_fetch_policy_t *m = (ss_fetch_policy_t *)userobj;
|
||||
struct lws_context *context = (struct lws_context *)m->opaque_data;
|
||||
|
||||
lwsl_info("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name((int)state),
|
||||
(unsigned int)ack);
|
||||
|
||||
switch (state) {
|
||||
case LWSSSCS_CREATING:
|
||||
return lws_ss_request_tx(m->ss);
|
||||
|
||||
case LWSSSCS_CONNECTING:
|
||||
break;
|
||||
|
||||
case LWSSSCS_QOS_ACK_REMOTE:
|
||||
switch (m->partway) {
|
||||
case 2:
|
||||
lws_sul_schedule(context, 0, &m->sul, policy_set, 1);
|
||||
m->partway = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWSSSCS_DISCONNECTED:
|
||||
if (m->partway == 1) {
|
||||
lws_ss_policy_parse_abandon(context);
|
||||
break;
|
||||
}
|
||||
m->partway = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return LWSSSSRET_OK;
|
||||
}
|
||||
|
||||
int
|
||||
lws_ss_sys_fetch_policy(struct lws_context *context)
|
||||
{
|
||||
lws_ss_info_t ssi;
|
||||
|
||||
if (context->hss_fetch_policy) /* already exists */
|
||||
return 0;
|
||||
|
||||
/* We're making an outgoing secure stream ourselves */
|
||||
|
||||
memset(&ssi, 0, sizeof(ssi));
|
||||
ssi.handle_offset = offsetof(ss_fetch_policy_t, ss);
|
||||
ssi.opaque_user_data_offset = offsetof(ss_fetch_policy_t, opaque_data);
|
||||
ssi.rx = ss_fetch_policy_rx;
|
||||
ssi.tx = ss_fetch_policy_tx;
|
||||
ssi.state = ss_fetch_policy_state;
|
||||
ssi.user_alloc = sizeof(ss_fetch_policy_t);
|
||||
ssi.streamtype = "fetch_policy";
|
||||
|
||||
if (lws_ss_create(context, 0, &ssi, context, &context->hss_fetch_policy,
|
||||
NULL, NULL)) {
|
||||
/*
|
||||
* If there's no fetch_policy streamtype, it can just be we're
|
||||
* running on a proxied client with no policy of its own,
|
||||
* it's OK.
|
||||
*/
|
||||
lwsl_info("%s: Policy fetch ss failed (stub policy?)\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
lwsl_info("%s: policy fetching ongoing\n", __func__);
|
||||
|
||||
/* fetching it is ongoing */
|
||||
|
||||
return 1;
|
||||
}
|
Reference in New Issue
Block a user