• 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 	lws_sorted_usec_list_t	sul;
35 	size_t			pos;
36 	int			expires_secs;
37 } ss_api_amazon_auth_t;
38 
39 static const char * const lejp_tokens_lwa[] = {
40 	"access_token",
41 	"expires_in",
42 };
43 
44 typedef enum {
45 	LSSPPT_ACCESS_TOKEN,
46 	LSSPPT_EXPIRES_IN,
47 } lejp_tokens_t;
48 
49 enum {
50 	AUTH_IDX_LWA,
51 	AUTH_IDX_ROOT,
52 };
53 
54 static void
lws_ss_sys_auth_api_amazon_com_kick(lws_sorted_usec_list_t * sul)55 lws_ss_sys_auth_api_amazon_com_kick(lws_sorted_usec_list_t *sul)
56 {
57 	struct lws_context *context = lws_container_of(sul, struct lws_context,
58 							sul_api_amazon_com_kick);
59 
60 	lws_state_transition_steps(&context->mgr_system,
61 				   LWS_SYSTATE_OPERATIONAL);
62 }
63 
64 static void
lws_ss_sys_auth_api_amazon_com_renew(lws_sorted_usec_list_t * sul)65 lws_ss_sys_auth_api_amazon_com_renew(lws_sorted_usec_list_t *sul)
66 {
67 	struct lws_context *context = lws_container_of(sul, struct lws_context,
68 							sul_api_amazon_com);
69 
70 	/* if nothing is there to intercept anything, go all the way */
71 	lws_state_transition_steps(&context->mgr_system,
72 				   LWS_SYSTATE_OPERATIONAL);
73 }
74 
75 static signed char
auth_api_amazon_com_parser_cb(struct lejp_ctx * ctx,char reason)76 auth_api_amazon_com_parser_cb(struct lejp_ctx *ctx, char reason)
77 {
78 	ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)ctx->user;
79 	struct lws_context *context = (struct lws_context *)m->opaque_data;
80 
81 	if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
82 		return 0;
83 
84 	switch (ctx->path_match - 1) {
85 	case LSSPPT_ACCESS_TOKEN:
86 		if (!ctx->npos)
87 			break;
88 		if (lws_system_blob_heap_append(lws_system_get_blob(context,
89 						LWS_SYSBLOB_TYPE_AUTH,
90 						AUTH_IDX_LWA),
91 						(const uint8_t *)ctx->buf,
92 						ctx->npos)) {
93 			lwsl_err("%s: unable to store auth token\n", __func__);
94 			return -1;
95 		}
96 		break;
97 	case LSSPPT_EXPIRES_IN:
98 		m->expires_secs = atoi(ctx->buf);
99 		lws_sul_schedule(context, 0, &context->sul_api_amazon_com,
100 				 lws_ss_sys_auth_api_amazon_com_renew,
101 				 (uint64_t)m->expires_secs * LWS_US_PER_SEC);
102 		break;
103 	}
104 
105 	return 0;
106 }
107 
108 /* secure streams payload interface */
109 
110 static int
ss_api_amazon_auth_rx(void * userobj,const uint8_t * buf,size_t len,int flags)111 ss_api_amazon_auth_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
112 {
113 	ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)userobj;
114 	struct lws_context *context = (struct lws_context *)m->opaque_data;
115 	lws_system_blob_t *ab;
116 	size_t total;
117 	int n;
118 
119 	ab = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, AUTH_IDX_LWA);
120 
121 	if (buf) {
122 		if (flags & LWSSS_FLAG_SOM) {
123 			lejp_construct(&m->jctx, auth_api_amazon_com_parser_cb,
124 				       m, lejp_tokens_lwa,
125 				       LWS_ARRAY_SIZE(lejp_tokens_lwa));
126 			lws_system_blob_heap_empty(ab);
127 		}
128 
129 		n = (int)(signed char)lejp_parse(&m->jctx, buf, len);
130 		if (n < 0) {
131 			lejp_destruct(&m->jctx);
132 			lws_system_blob_destroy(
133 				lws_system_get_blob(context,
134 						    LWS_SYSBLOB_TYPE_AUTH,
135 						    AUTH_IDX_LWA));
136 
137 			return -1;
138 		}
139 	}
140 	if (!(flags & LWSSS_FLAG_EOM))
141 		return 0;
142 
143 	/* we should have the auth token now */
144 
145 	total = lws_system_blob_get_size(ab);
146 	lwsl_notice("%s: acquired %u-byte api.amazon.com auth token, exp %ds\n",
147 			__func__, (unsigned int)total, m->expires_secs);
148 
149 	lejp_destruct(&m->jctx);
150 
151 	/* we move the system state at auth connection close */
152 
153 	return 0;
154 }
155 
156 static int
ss_api_amazon_auth_tx(void * userobj,lws_ss_tx_ordinal_t ord,uint8_t * buf,size_t * len,int * flags)157 ss_api_amazon_auth_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf,
158 		      size_t *len, int *flags)
159 {
160 	ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)userobj;
161 	struct lws_context *context = (struct lws_context *)m->opaque_data;
162 	lws_system_blob_t *ab;
163 	size_t total;
164 	int n;
165 
166 	/*
167 	 * We send out auth slot AUTH_IDX_ROOT, it's the LWA user / device
168 	 * identity token
169 	 */
170 
171 	ab = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, AUTH_IDX_ROOT);
172 	total = lws_system_blob_get_size(ab);
173 
174 	n = lws_system_blob_get(ab, buf, len, m->pos);
175 	if (n < 0)
176 		return 1;
177 
178 	if (!m->pos)
179 		*flags |= LWSSS_FLAG_SOM;
180 
181 	m->pos += *len;
182 
183 	if (m->pos == total) {
184 		*flags |= LWSSS_FLAG_EOM;
185 		m->pos = 0; /* for next time */
186 	}
187 
188 	return 0;
189 }
190 
191 static int
ss_api_amazon_auth_state(void * userobj,void * sh,lws_ss_constate_t state,lws_ss_tx_ordinal_t ack)192 ss_api_amazon_auth_state(void *userobj, void *sh, lws_ss_constate_t state,
193 			 lws_ss_tx_ordinal_t ack)
194 {
195 	ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)userobj;
196 	struct lws_context *context = (struct lws_context *)m->opaque_data;
197 	size_t s;
198 
199 	lwsl_info("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state),
200 		  (unsigned int)ack);
201 
202 	switch (state) {
203 	case LWSSSCS_CREATING:
204 	case LWSSSCS_CONNECTING:
205 		s = lws_system_blob_get_size(
206 			lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH,
207 					    AUTH_IDX_ROOT));
208 		lws_ss_request_tx_len(m->ss, s);
209 		m->pos = 0;
210 		break;
211 
212 	case LWSSSCS_DISCONNECTED:
213 		/*
214 		 * We defer moving the system state forward until we have
215 		 * closed our connection + tls for the auth action... this is
216 		 * because on small systems, we need that memory recovered
217 		 * before we can make another connection subsequently.
218 		 *
219 		 * At this point, we're ultimately being called from within
220 		 * the wsi close process, the tls tunnel is not freed yet.
221 		 * Use a sul to actually do it next time around the event loop
222 		 * when the close process for the auth wsi has completed and
223 		 * the related tls is already freed.
224 		 */
225 		s = lws_system_blob_get_size(
226 			lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH,
227 					    AUTH_IDX_LWA));
228 
229 		if (s)
230 			lws_sul_schedule(context, 0,
231 					 &context->sul_api_amazon_com_kick,
232 					 lws_ss_sys_auth_api_amazon_com_kick, 1);
233 		break;
234 
235 	case LWSSSCS_DESTROYING:
236 		lws_sul_schedule(context, 0, &context->sul_api_amazon_com,
237 				 NULL, LWS_SET_TIMER_USEC_CANCEL);
238 		lws_system_blob_destroy(
239 			lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH,
240 					    AUTH_IDX_LWA));
241 		break;
242 
243 	default:
244 		break;
245 	}
246 
247 	return 0;
248 }
249 
250 int
lws_ss_sys_auth_api_amazon_com(struct lws_context * context)251 lws_ss_sys_auth_api_amazon_com(struct lws_context *context)
252 {
253 	lws_ss_info_t ssi;
254 
255 	if (context->hss_auth) /* already exists */
256 		return 0;
257 
258 	/* We're making an outgoing secure stream ourselves */
259 
260 	memset(&ssi, 0, sizeof(ssi));
261 	ssi.handle_offset	    = offsetof(ss_api_amazon_auth_t, ss);
262 	ssi.opaque_user_data_offset = offsetof(ss_api_amazon_auth_t, opaque_data);
263 	ssi.rx			    = ss_api_amazon_auth_rx;
264 	ssi.tx			    = ss_api_amazon_auth_tx;
265 	ssi.state		    = ss_api_amazon_auth_state;
266 	ssi.user_alloc		    = sizeof(ss_api_amazon_auth_t);
267 	ssi.streamtype		    = "api_amazon_com_auth";
268 
269 	if (lws_ss_create(context, 0, &ssi, context, &context->hss_auth,
270 			  NULL, NULL)) {
271 		lwsl_info("%s: Create LWA auth ss failed (policy?)\n", __func__);
272 		return 1;
273 	}
274 
275 	return 0;
276 }
277