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