| /* |
| * LWA auth support for Secure Streams |
| * |
| * libwebsockets - small server side websockets and web server implementation |
| * |
| * Copyright (C) 2019 - 2020 Andy Green <[email protected]> |
| * |
| * 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; |
| lws_sorted_usec_list_t sul; |
| 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); |
| |
| /* if nothing is there to intercept anything, go all the way */ |
| lws_state_transition_steps(&context->mgr_system, |
| LWS_SYSTATE_OPERATIONAL); |
| } |
| |
| 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; |
| |
| 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; |
| if (lws_system_blob_heap_append(lws_system_get_blob(context, |
| LWS_SYSBLOB_TYPE_AUTH, |
| AUTH_IDX_LWA), |
| (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, |
| (uint64_t)m->expires_secs * LWS_US_PER_SEC); |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /* secure streams payload interface */ |
| |
| static int |
| 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; |
| size_t total; |
| int n; |
| |
| ab = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, AUTH_IDX_LWA); |
| |
| 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 = (int)(signed char)lejp_parse(&m->jctx, buf, 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 -1; |
| } |
| } |
| if (!(flags & LWSSS_FLAG_EOM)) |
| return 0; |
| |
| /* we should have the auth token now */ |
| |
| 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); |
| |
| lejp_destruct(&m->jctx); |
| |
| /* we move the system state at auth connection close */ |
| |
| return 0; |
| } |
| |
| static int |
| 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); |
| total = lws_system_blob_get_size(ab); |
| |
| n = lws_system_blob_get(ab, buf, len, m->pos); |
| if (n < 0) |
| return 1; |
| |
| 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 0; |
| } |
| |
| static int |
| 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; |
| size_t s; |
| |
| lwsl_info("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state), |
| (unsigned int)ack); |
| |
| switch (state) { |
| case LWSSSCS_CREATING: |
| case LWSSSCS_CONNECTING: |
| s = lws_system_blob_get_size( |
| lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, |
| AUTH_IDX_ROOT)); |
| lws_ss_request_tx_len(m->ss, s); |
| m->pos = 0; |
| break; |
| |
| 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( |
| lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, |
| AUTH_IDX_LWA)); |
| |
| if (s) |
| lws_sul_schedule(context, 0, |
| &context->sul_api_amazon_com_kick, |
| lws_ss_sys_auth_api_amazon_com_kick, 1); |
| break; |
| |
| case LWSSSCS_DESTROYING: |
| lws_sul_schedule(context, 0, &context->sul_api_amazon_com, |
| NULL, LWS_SET_TIMER_USEC_CANCEL); |
| lws_system_blob_destroy( |
| lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, |
| AUTH_IDX_LWA)); |
| break; |
| |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| 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; |
| } |