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