1d4afb5ceSopenharmony_ci/* 2d4afb5ceSopenharmony_ci * LWA auth support for Secure Streams 3d4afb5ceSopenharmony_ci * 4d4afb5ceSopenharmony_ci * libwebsockets - small server side websockets and web server implementation 5d4afb5ceSopenharmony_ci * 6d4afb5ceSopenharmony_ci * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com> 7d4afb5ceSopenharmony_ci * 8d4afb5ceSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy 9d4afb5ceSopenharmony_ci * of this software and associated documentation files (the "Software"), to 10d4afb5ceSopenharmony_ci * deal in the Software without restriction, including without limitation the 11d4afb5ceSopenharmony_ci * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12d4afb5ceSopenharmony_ci * sell copies of the Software, and to permit persons to whom the Software is 13d4afb5ceSopenharmony_ci * furnished to do so, subject to the following conditions: 14d4afb5ceSopenharmony_ci * 15d4afb5ceSopenharmony_ci * The above copyright notice and this permission notice shall be included in 16d4afb5ceSopenharmony_ci * all copies or substantial portions of the Software. 17d4afb5ceSopenharmony_ci * 18d4afb5ceSopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19d4afb5ceSopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20d4afb5ceSopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21d4afb5ceSopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22d4afb5ceSopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23d4afb5ceSopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24d4afb5ceSopenharmony_ci * IN THE SOFTWARE. 25d4afb5ceSopenharmony_ci */ 26d4afb5ceSopenharmony_ci 27d4afb5ceSopenharmony_ci#include <private-lib-core.h> 28d4afb5ceSopenharmony_ci 29d4afb5ceSopenharmony_citypedef struct ss_api_amazon_auth { 30d4afb5ceSopenharmony_ci struct lws_ss_handle *ss; 31d4afb5ceSopenharmony_ci void *opaque_data; 32d4afb5ceSopenharmony_ci /* ... application specific state ... */ 33d4afb5ceSopenharmony_ci struct lejp_ctx jctx; 34d4afb5ceSopenharmony_ci size_t pos; 35d4afb5ceSopenharmony_ci int expires_secs; 36d4afb5ceSopenharmony_ci} ss_api_amazon_auth_t; 37d4afb5ceSopenharmony_ci 38d4afb5ceSopenharmony_cistatic const char * const lejp_tokens_lwa[] = { 39d4afb5ceSopenharmony_ci "access_token", 40d4afb5ceSopenharmony_ci "expires_in", 41d4afb5ceSopenharmony_ci}; 42d4afb5ceSopenharmony_ci 43d4afb5ceSopenharmony_citypedef enum { 44d4afb5ceSopenharmony_ci LSSPPT_ACCESS_TOKEN, 45d4afb5ceSopenharmony_ci LSSPPT_EXPIRES_IN, 46d4afb5ceSopenharmony_ci} lejp_tokens_t; 47d4afb5ceSopenharmony_ci 48d4afb5ceSopenharmony_cienum { 49d4afb5ceSopenharmony_ci AUTH_IDX_LWA, 50d4afb5ceSopenharmony_ci AUTH_IDX_ROOT, 51d4afb5ceSopenharmony_ci}; 52d4afb5ceSopenharmony_ci 53d4afb5ceSopenharmony_cistatic void 54d4afb5ceSopenharmony_cilws_ss_sys_auth_api_amazon_com_kick(lws_sorted_usec_list_t *sul) 55d4afb5ceSopenharmony_ci{ 56d4afb5ceSopenharmony_ci struct lws_context *context = lws_container_of(sul, struct lws_context, 57d4afb5ceSopenharmony_ci sul_api_amazon_com_kick); 58d4afb5ceSopenharmony_ci 59d4afb5ceSopenharmony_ci lws_state_transition_steps(&context->mgr_system, 60d4afb5ceSopenharmony_ci LWS_SYSTATE_OPERATIONAL); 61d4afb5ceSopenharmony_ci} 62d4afb5ceSopenharmony_ci 63d4afb5ceSopenharmony_cistatic void 64d4afb5ceSopenharmony_cilws_ss_sys_auth_api_amazon_com_renew(lws_sorted_usec_list_t *sul) 65d4afb5ceSopenharmony_ci{ 66d4afb5ceSopenharmony_ci struct lws_context *context = lws_container_of(sul, struct lws_context, 67d4afb5ceSopenharmony_ci sul_api_amazon_com); 68d4afb5ceSopenharmony_ci 69d4afb5ceSopenharmony_ci lws_ss_sys_auth_api_amazon_com(context); 70d4afb5ceSopenharmony_ci} 71d4afb5ceSopenharmony_ci 72d4afb5ceSopenharmony_cistatic signed char 73d4afb5ceSopenharmony_ciauth_api_amazon_com_parser_cb(struct lejp_ctx *ctx, char reason) 74d4afb5ceSopenharmony_ci{ 75d4afb5ceSopenharmony_ci ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)ctx->user; 76d4afb5ceSopenharmony_ci struct lws_context *context = (struct lws_context *)m->opaque_data; 77d4afb5ceSopenharmony_ci lws_system_blob_t *blob; 78d4afb5ceSopenharmony_ci 79d4afb5ceSopenharmony_ci if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) 80d4afb5ceSopenharmony_ci return 0; 81d4afb5ceSopenharmony_ci 82d4afb5ceSopenharmony_ci switch (ctx->path_match - 1) { 83d4afb5ceSopenharmony_ci case LSSPPT_ACCESS_TOKEN: 84d4afb5ceSopenharmony_ci if (!ctx->npos) 85d4afb5ceSopenharmony_ci break; 86d4afb5ceSopenharmony_ci 87d4afb5ceSopenharmony_ci blob = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, 88d4afb5ceSopenharmony_ci AUTH_IDX_LWA); 89d4afb5ceSopenharmony_ci if (!blob) 90d4afb5ceSopenharmony_ci return -1; 91d4afb5ceSopenharmony_ci 92d4afb5ceSopenharmony_ci if (lws_system_blob_heap_append(blob, 93d4afb5ceSopenharmony_ci (const uint8_t *)ctx->buf, 94d4afb5ceSopenharmony_ci ctx->npos)) { 95d4afb5ceSopenharmony_ci lwsl_err("%s: unable to store auth token\n", __func__); 96d4afb5ceSopenharmony_ci 97d4afb5ceSopenharmony_ci return -1; 98d4afb5ceSopenharmony_ci } 99d4afb5ceSopenharmony_ci break; 100d4afb5ceSopenharmony_ci case LSSPPT_EXPIRES_IN: 101d4afb5ceSopenharmony_ci m->expires_secs = atoi(ctx->buf); 102d4afb5ceSopenharmony_ci lws_sul_schedule(context, 0, &context->sul_api_amazon_com, 103d4afb5ceSopenharmony_ci lws_ss_sys_auth_api_amazon_com_renew, 104d4afb5ceSopenharmony_ci (lws_usec_t)m->expires_secs * LWS_US_PER_SEC); 105d4afb5ceSopenharmony_ci break; 106d4afb5ceSopenharmony_ci } 107d4afb5ceSopenharmony_ci 108d4afb5ceSopenharmony_ci return 0; 109d4afb5ceSopenharmony_ci} 110d4afb5ceSopenharmony_ci 111d4afb5ceSopenharmony_ci/* secure streams payload interface */ 112d4afb5ceSopenharmony_ci 113d4afb5ceSopenharmony_cistatic lws_ss_state_return_t 114d4afb5ceSopenharmony_ciss_api_amazon_auth_rx(void *userobj, const uint8_t *buf, size_t len, int flags) 115d4afb5ceSopenharmony_ci{ 116d4afb5ceSopenharmony_ci ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)userobj; 117d4afb5ceSopenharmony_ci struct lws_context *context = (struct lws_context *)m->opaque_data; 118d4afb5ceSopenharmony_ci lws_system_blob_t *ab; 119d4afb5ceSopenharmony_ci#if !defined(LWS_WITH_NO_LOGS) 120d4afb5ceSopenharmony_ci size_t total; 121d4afb5ceSopenharmony_ci#endif 122d4afb5ceSopenharmony_ci int n; 123d4afb5ceSopenharmony_ci 124d4afb5ceSopenharmony_ci ab = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, AUTH_IDX_LWA); 125d4afb5ceSopenharmony_ci /* coverity */ 126d4afb5ceSopenharmony_ci if (!ab) 127d4afb5ceSopenharmony_ci return LWSSSSRET_DISCONNECT_ME; 128d4afb5ceSopenharmony_ci 129d4afb5ceSopenharmony_ci if (buf) { 130d4afb5ceSopenharmony_ci if (flags & LWSSS_FLAG_SOM) { 131d4afb5ceSopenharmony_ci lejp_construct(&m->jctx, auth_api_amazon_com_parser_cb, 132d4afb5ceSopenharmony_ci m, lejp_tokens_lwa, 133d4afb5ceSopenharmony_ci LWS_ARRAY_SIZE(lejp_tokens_lwa)); 134d4afb5ceSopenharmony_ci lws_system_blob_heap_empty(ab); 135d4afb5ceSopenharmony_ci } 136d4afb5ceSopenharmony_ci 137d4afb5ceSopenharmony_ci n = lejp_parse(&m->jctx, buf, (int)len); 138d4afb5ceSopenharmony_ci if (n < 0) { 139d4afb5ceSopenharmony_ci lejp_destruct(&m->jctx); 140d4afb5ceSopenharmony_ci lws_system_blob_destroy( 141d4afb5ceSopenharmony_ci lws_system_get_blob(context, 142d4afb5ceSopenharmony_ci LWS_SYSBLOB_TYPE_AUTH, 143d4afb5ceSopenharmony_ci AUTH_IDX_LWA)); 144d4afb5ceSopenharmony_ci 145d4afb5ceSopenharmony_ci return LWSSSSRET_DISCONNECT_ME; 146d4afb5ceSopenharmony_ci } 147d4afb5ceSopenharmony_ci } 148d4afb5ceSopenharmony_ci if (!(flags & LWSSS_FLAG_EOM)) 149d4afb5ceSopenharmony_ci return LWSSSSRET_OK; 150d4afb5ceSopenharmony_ci 151d4afb5ceSopenharmony_ci /* we should have the auth token now */ 152d4afb5ceSopenharmony_ci 153d4afb5ceSopenharmony_ci#if !defined(LWS_WITH_NO_LOGS) 154d4afb5ceSopenharmony_ci total = lws_system_blob_get_size(ab); 155d4afb5ceSopenharmony_ci lwsl_notice("%s: acquired %u-byte api.amazon.com auth token, exp %ds\n", 156d4afb5ceSopenharmony_ci __func__, (unsigned int)total, m->expires_secs); 157d4afb5ceSopenharmony_ci#endif 158d4afb5ceSopenharmony_ci 159d4afb5ceSopenharmony_ci lejp_destruct(&m->jctx); 160d4afb5ceSopenharmony_ci 161d4afb5ceSopenharmony_ci /* we move the system state at auth connection close */ 162d4afb5ceSopenharmony_ci 163d4afb5ceSopenharmony_ci return LWSSSSRET_DISCONNECT_ME; 164d4afb5ceSopenharmony_ci} 165d4afb5ceSopenharmony_ci 166d4afb5ceSopenharmony_cistatic lws_ss_state_return_t 167d4afb5ceSopenharmony_ciss_api_amazon_auth_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, 168d4afb5ceSopenharmony_ci size_t *len, int *flags) 169d4afb5ceSopenharmony_ci{ 170d4afb5ceSopenharmony_ci ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)userobj; 171d4afb5ceSopenharmony_ci struct lws_context *context = (struct lws_context *)m->opaque_data; 172d4afb5ceSopenharmony_ci lws_system_blob_t *ab; 173d4afb5ceSopenharmony_ci size_t total; 174d4afb5ceSopenharmony_ci int n; 175d4afb5ceSopenharmony_ci 176d4afb5ceSopenharmony_ci /* 177d4afb5ceSopenharmony_ci * We send out auth slot AUTH_IDX_ROOT, it's the LWA user / device 178d4afb5ceSopenharmony_ci * identity token 179d4afb5ceSopenharmony_ci */ 180d4afb5ceSopenharmony_ci 181d4afb5ceSopenharmony_ci ab = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, AUTH_IDX_ROOT); 182d4afb5ceSopenharmony_ci if (!ab) 183d4afb5ceSopenharmony_ci return LWSSSSRET_DESTROY_ME; 184d4afb5ceSopenharmony_ci 185d4afb5ceSopenharmony_ci total = lws_system_blob_get_size(ab); 186d4afb5ceSopenharmony_ci 187d4afb5ceSopenharmony_ci n = lws_system_blob_get(ab, buf, len, m->pos); 188d4afb5ceSopenharmony_ci if (n < 0) 189d4afb5ceSopenharmony_ci return LWSSSSRET_TX_DONT_SEND; 190d4afb5ceSopenharmony_ci 191d4afb5ceSopenharmony_ci if (!m->pos) 192d4afb5ceSopenharmony_ci *flags |= LWSSS_FLAG_SOM; 193d4afb5ceSopenharmony_ci 194d4afb5ceSopenharmony_ci m->pos += *len; 195d4afb5ceSopenharmony_ci 196d4afb5ceSopenharmony_ci if (m->pos == total) { 197d4afb5ceSopenharmony_ci *flags |= LWSSS_FLAG_EOM; 198d4afb5ceSopenharmony_ci m->pos = 0; /* for next time */ 199d4afb5ceSopenharmony_ci } 200d4afb5ceSopenharmony_ci 201d4afb5ceSopenharmony_ci return LWSSSSRET_OK; 202d4afb5ceSopenharmony_ci} 203d4afb5ceSopenharmony_ci 204d4afb5ceSopenharmony_cistatic lws_ss_state_return_t 205d4afb5ceSopenharmony_ciss_api_amazon_auth_state(void *userobj, void *sh, lws_ss_constate_t state, 206d4afb5ceSopenharmony_ci lws_ss_tx_ordinal_t ack) 207d4afb5ceSopenharmony_ci{ 208d4afb5ceSopenharmony_ci ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)userobj; 209d4afb5ceSopenharmony_ci struct lws_context *context = (struct lws_context *)m->opaque_data; 210d4afb5ceSopenharmony_ci lws_system_blob_t *ab; 211d4afb5ceSopenharmony_ci size_t s; 212d4afb5ceSopenharmony_ci 213d4afb5ceSopenharmony_ci lwsl_info("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name((int)state), 214d4afb5ceSopenharmony_ci (unsigned int)ack); 215d4afb5ceSopenharmony_ci 216d4afb5ceSopenharmony_ci ab = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, AUTH_IDX_ROOT); 217d4afb5ceSopenharmony_ci /* coverity */ 218d4afb5ceSopenharmony_ci if (!ab) 219d4afb5ceSopenharmony_ci return LWSSSSRET_DESTROY_ME; 220d4afb5ceSopenharmony_ci 221d4afb5ceSopenharmony_ci switch (state) { 222d4afb5ceSopenharmony_ci case LWSSSCS_CREATING: 223d4afb5ceSopenharmony_ci //if (lws_ss_set_metadata(m->ss, "ctype", "application/json", 16)) 224d4afb5ceSopenharmony_ci // return LWSSSSRET_DESTROY_ME; 225d4afb5ceSopenharmony_ci /* fallthru */ 226d4afb5ceSopenharmony_ci case LWSSSCS_CONNECTING: 227d4afb5ceSopenharmony_ci s = lws_system_blob_get_size(ab); 228d4afb5ceSopenharmony_ci if (!s) 229d4afb5ceSopenharmony_ci lwsl_debug("%s: no auth blob\n", __func__); 230d4afb5ceSopenharmony_ci m->pos = 0; 231d4afb5ceSopenharmony_ci return lws_ss_request_tx_len(m->ss, (unsigned long)s); 232d4afb5ceSopenharmony_ci 233d4afb5ceSopenharmony_ci case LWSSSCS_DISCONNECTED: 234d4afb5ceSopenharmony_ci /* 235d4afb5ceSopenharmony_ci * We defer moving the system state forward until we have 236d4afb5ceSopenharmony_ci * closed our connection + tls for the auth action... this is 237d4afb5ceSopenharmony_ci * because on small systems, we need that memory recovered 238d4afb5ceSopenharmony_ci * before we can make another connection subsequently. 239d4afb5ceSopenharmony_ci * 240d4afb5ceSopenharmony_ci * At this point, we're ultimately being called from within 241d4afb5ceSopenharmony_ci * the wsi close process, the tls tunnel is not freed yet. 242d4afb5ceSopenharmony_ci * Use a sul to actually do it next time around the event loop 243d4afb5ceSopenharmony_ci * when the close process for the auth wsi has completed and 244d4afb5ceSopenharmony_ci * the related tls is already freed. 245d4afb5ceSopenharmony_ci */ 246d4afb5ceSopenharmony_ci s = lws_system_blob_get_size(ab); 247d4afb5ceSopenharmony_ci 248d4afb5ceSopenharmony_ci if (s && context->mgr_system.state != LWS_SYSTATE_OPERATIONAL) 249d4afb5ceSopenharmony_ci lws_sul_schedule(context, 0, 250d4afb5ceSopenharmony_ci &context->sul_api_amazon_com_kick, 251d4afb5ceSopenharmony_ci lws_ss_sys_auth_api_amazon_com_kick, 1); 252d4afb5ceSopenharmony_ci 253d4afb5ceSopenharmony_ci context->hss_auth = NULL; 254d4afb5ceSopenharmony_ci return LWSSSSRET_DESTROY_ME; 255d4afb5ceSopenharmony_ci 256d4afb5ceSopenharmony_ci default: 257d4afb5ceSopenharmony_ci break; 258d4afb5ceSopenharmony_ci } 259d4afb5ceSopenharmony_ci 260d4afb5ceSopenharmony_ci return LWSSSSRET_OK; 261d4afb5ceSopenharmony_ci} 262d4afb5ceSopenharmony_ci 263d4afb5ceSopenharmony_ciint 264d4afb5ceSopenharmony_cilws_ss_sys_auth_api_amazon_com(struct lws_context *context) 265d4afb5ceSopenharmony_ci{ 266d4afb5ceSopenharmony_ci lws_ss_info_t ssi; 267d4afb5ceSopenharmony_ci 268d4afb5ceSopenharmony_ci if (context->hss_auth) /* already exists */ 269d4afb5ceSopenharmony_ci return 0; 270d4afb5ceSopenharmony_ci 271d4afb5ceSopenharmony_ci /* We're making an outgoing secure stream ourselves */ 272d4afb5ceSopenharmony_ci 273d4afb5ceSopenharmony_ci memset(&ssi, 0, sizeof(ssi)); 274d4afb5ceSopenharmony_ci ssi.handle_offset = offsetof(ss_api_amazon_auth_t, ss); 275d4afb5ceSopenharmony_ci ssi.opaque_user_data_offset = offsetof(ss_api_amazon_auth_t, opaque_data); 276d4afb5ceSopenharmony_ci ssi.rx = ss_api_amazon_auth_rx; 277d4afb5ceSopenharmony_ci ssi.tx = ss_api_amazon_auth_tx; 278d4afb5ceSopenharmony_ci ssi.state = ss_api_amazon_auth_state; 279d4afb5ceSopenharmony_ci ssi.user_alloc = sizeof(ss_api_amazon_auth_t); 280d4afb5ceSopenharmony_ci ssi.streamtype = "api_amazon_com_auth"; 281d4afb5ceSopenharmony_ci 282d4afb5ceSopenharmony_ci if (lws_ss_create(context, 0, &ssi, context, &context->hss_auth, 283d4afb5ceSopenharmony_ci NULL, NULL)) { 284d4afb5ceSopenharmony_ci lwsl_info("%s: Create LWA auth ss failed (policy?)\n", __func__); 285d4afb5ceSopenharmony_ci return 1; 286d4afb5ceSopenharmony_ci } 287d4afb5ceSopenharmony_ci 288d4afb5ceSopenharmony_ci return 0; 289d4afb5ceSopenharmony_ci} 290