/src/libwebsockets/lib/secure-streams/system/auth-api.amazon.com/auth.c
Line | Count | Source (jump to first uncovered line) |
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 |
54 | | lws_ss_sys_auth_api_amazon_com_kick(lws_sorted_usec_list_t *sul) |
55 | 0 | { |
56 | 0 | struct lws_context *context = lws_container_of(sul, struct lws_context, |
57 | 0 | sul_api_amazon_com_kick); |
58 | |
|
59 | 0 | lws_state_transition_steps(&context->mgr_system, |
60 | 0 | LWS_SYSTATE_OPERATIONAL); |
61 | 0 | } |
62 | | |
63 | | static void |
64 | | lws_ss_sys_auth_api_amazon_com_renew(lws_sorted_usec_list_t *sul) |
65 | 0 | { |
66 | 0 | struct lws_context *context = lws_container_of(sul, struct lws_context, |
67 | 0 | sul_api_amazon_com); |
68 | |
|
69 | 0 | lws_ss_sys_auth_api_amazon_com(context); |
70 | 0 | } |
71 | | |
72 | | static signed char |
73 | | auth_api_amazon_com_parser_cb(struct lejp_ctx *ctx, char reason) |
74 | 0 | { |
75 | 0 | ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)ctx->user; |
76 | 0 | struct lws_context *context = (struct lws_context *)m->opaque_data; |
77 | 0 | lws_system_blob_t *blob; |
78 | |
|
79 | 0 | if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) |
80 | 0 | return 0; |
81 | | |
82 | 0 | switch (ctx->path_match - 1) { |
83 | 0 | case LSSPPT_ACCESS_TOKEN: |
84 | 0 | if (!ctx->npos) |
85 | 0 | break; |
86 | | |
87 | 0 | blob = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, |
88 | 0 | AUTH_IDX_LWA); |
89 | 0 | if (!blob) |
90 | 0 | return -1; |
91 | | |
92 | 0 | if (lws_system_blob_heap_append(blob, |
93 | 0 | (const uint8_t *)ctx->buf, |
94 | 0 | ctx->npos)) { |
95 | 0 | lwsl_err("%s: unable to store auth token\n", __func__); |
96 | |
|
97 | 0 | return -1; |
98 | 0 | } |
99 | 0 | break; |
100 | 0 | case LSSPPT_EXPIRES_IN: |
101 | 0 | m->expires_secs = atoi(ctx->buf); |
102 | 0 | lws_sul_schedule(context, 0, &context->sul_api_amazon_com, |
103 | 0 | lws_ss_sys_auth_api_amazon_com_renew, |
104 | 0 | (lws_usec_t)m->expires_secs * LWS_US_PER_SEC); |
105 | 0 | break; |
106 | 0 | } |
107 | | |
108 | 0 | return 0; |
109 | 0 | } |
110 | | |
111 | | /* secure streams payload interface */ |
112 | | |
113 | | static lws_ss_state_return_t |
114 | | ss_api_amazon_auth_rx(void *userobj, const uint8_t *buf, size_t len, int flags) |
115 | 0 | { |
116 | 0 | ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)userobj; |
117 | 0 | struct lws_context *context = (struct lws_context *)m->opaque_data; |
118 | 0 | lws_system_blob_t *ab; |
119 | 0 | #if !defined(LWS_WITH_NO_LOGS) |
120 | 0 | size_t total; |
121 | 0 | #endif |
122 | 0 | int n; |
123 | |
|
124 | 0 | ab = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, AUTH_IDX_LWA); |
125 | | /* coverity */ |
126 | 0 | if (!ab) |
127 | 0 | return LWSSSSRET_DISCONNECT_ME; |
128 | | |
129 | 0 | if (buf) { |
130 | 0 | if (flags & LWSSS_FLAG_SOM) { |
131 | 0 | lejp_construct(&m->jctx, auth_api_amazon_com_parser_cb, |
132 | 0 | m, lejp_tokens_lwa, |
133 | 0 | LWS_ARRAY_SIZE(lejp_tokens_lwa)); |
134 | 0 | lws_system_blob_heap_empty(ab); |
135 | 0 | } |
136 | |
|
137 | 0 | n = lejp_parse(&m->jctx, buf, (int)len); |
138 | 0 | if (n < 0) { |
139 | 0 | lejp_destruct(&m->jctx); |
140 | 0 | lws_system_blob_destroy( |
141 | 0 | lws_system_get_blob(context, |
142 | 0 | LWS_SYSBLOB_TYPE_AUTH, |
143 | 0 | AUTH_IDX_LWA)); |
144 | |
|
145 | 0 | return LWSSSSRET_DISCONNECT_ME; |
146 | 0 | } |
147 | 0 | } |
148 | 0 | if (!(flags & LWSSS_FLAG_EOM)) |
149 | 0 | return LWSSSSRET_OK; |
150 | | |
151 | | /* we should have the auth token now */ |
152 | | |
153 | 0 | #if !defined(LWS_WITH_NO_LOGS) |
154 | 0 | total = lws_system_blob_get_size(ab); |
155 | 0 | lwsl_notice("%s: acquired %u-byte api.amazon.com auth token, exp %ds\n", |
156 | 0 | __func__, (unsigned int)total, m->expires_secs); |
157 | 0 | #endif |
158 | |
|
159 | 0 | lejp_destruct(&m->jctx); |
160 | | |
161 | | /* we move the system state at auth connection close */ |
162 | |
|
163 | 0 | return LWSSSSRET_DISCONNECT_ME; |
164 | 0 | } |
165 | | |
166 | | static lws_ss_state_return_t |
167 | | ss_api_amazon_auth_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, |
168 | | size_t *len, int *flags) |
169 | 0 | { |
170 | 0 | ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)userobj; |
171 | 0 | struct lws_context *context = (struct lws_context *)m->opaque_data; |
172 | 0 | lws_system_blob_t *ab; |
173 | 0 | size_t total; |
174 | 0 | 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 | 0 | ab = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, AUTH_IDX_ROOT); |
182 | 0 | if (!ab) |
183 | 0 | return LWSSSSRET_DESTROY_ME; |
184 | | |
185 | 0 | total = lws_system_blob_get_size(ab); |
186 | |
|
187 | 0 | n = lws_system_blob_get(ab, buf, len, m->pos); |
188 | 0 | if (n < 0) |
189 | 0 | return LWSSSSRET_TX_DONT_SEND; |
190 | | |
191 | 0 | if (!m->pos) |
192 | 0 | *flags |= LWSSS_FLAG_SOM; |
193 | |
|
194 | 0 | m->pos += *len; |
195 | |
|
196 | 0 | if (m->pos == total) { |
197 | 0 | *flags |= LWSSS_FLAG_EOM; |
198 | 0 | m->pos = 0; /* for next time */ |
199 | 0 | } |
200 | |
|
201 | 0 | return LWSSSSRET_OK; |
202 | 0 | } |
203 | | |
204 | | static lws_ss_state_return_t |
205 | | ss_api_amazon_auth_state(void *userobj, void *sh, lws_ss_constate_t state, |
206 | | lws_ss_tx_ordinal_t ack) |
207 | 0 | { |
208 | 0 | ss_api_amazon_auth_t *m = (ss_api_amazon_auth_t *)userobj; |
209 | 0 | struct lws_context *context = (struct lws_context *)m->opaque_data; |
210 | 0 | lws_system_blob_t *ab; |
211 | 0 | size_t s; |
212 | |
|
213 | 0 | lwsl_info("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name(state), |
214 | 0 | (unsigned int)ack); |
215 | |
|
216 | 0 | ab = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, AUTH_IDX_ROOT); |
217 | | /* coverity */ |
218 | 0 | if (!ab) |
219 | 0 | return LWSSSSRET_DESTROY_ME; |
220 | | |
221 | 0 | switch (state) { |
222 | 0 | case LWSSSCS_CREATING: |
223 | | //if (lws_ss_set_metadata(m->ss, "ctype", "application/json", 16)) |
224 | | // return LWSSSSRET_DESTROY_ME; |
225 | | /* fallthru */ |
226 | 0 | case LWSSSCS_CONNECTING: |
227 | 0 | s = lws_system_blob_get_size(ab); |
228 | 0 | if (!s) |
229 | 0 | lwsl_debug("%s: no auth blob\n", __func__); |
230 | 0 | m->pos = 0; |
231 | 0 | return lws_ss_request_tx_len(m->ss, (unsigned long)s); |
232 | | |
233 | 0 | 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 | 0 | s = lws_system_blob_get_size(ab); |
247 | |
|
248 | 0 | if (s && context->mgr_system.state != LWS_SYSTATE_OPERATIONAL) |
249 | 0 | lws_sul_schedule(context, 0, |
250 | 0 | &context->sul_api_amazon_com_kick, |
251 | 0 | lws_ss_sys_auth_api_amazon_com_kick, 1); |
252 | |
|
253 | 0 | context->hss_auth = NULL; |
254 | 0 | return LWSSSSRET_DESTROY_ME; |
255 | | |
256 | 0 | default: |
257 | 0 | break; |
258 | 0 | } |
259 | | |
260 | 0 | return LWSSSSRET_OK; |
261 | 0 | } |
262 | | |
263 | | int |
264 | | lws_ss_sys_auth_api_amazon_com(struct lws_context *context) |
265 | 0 | { |
266 | 0 | lws_ss_info_t ssi; |
267 | |
|
268 | 0 | if (context->hss_auth) /* already exists */ |
269 | 0 | return 0; |
270 | | |
271 | | /* We're making an outgoing secure stream ourselves */ |
272 | | |
273 | 0 | memset(&ssi, 0, sizeof(ssi)); |
274 | 0 | ssi.handle_offset = offsetof(ss_api_amazon_auth_t, ss); |
275 | 0 | ssi.opaque_user_data_offset = offsetof(ss_api_amazon_auth_t, opaque_data); |
276 | 0 | ssi.rx = ss_api_amazon_auth_rx; |
277 | 0 | ssi.tx = ss_api_amazon_auth_tx; |
278 | 0 | ssi.state = ss_api_amazon_auth_state; |
279 | 0 | ssi.user_alloc = sizeof(ss_api_amazon_auth_t); |
280 | 0 | ssi.streamtype = "api_amazon_com_auth"; |
281 | |
|
282 | 0 | if (lws_ss_create(context, 0, &ssi, context, &context->hss_auth, |
283 | 0 | NULL, NULL)) { |
284 | 0 | lwsl_info("%s: Create LWA auth ss failed (policy?)\n", __func__); |
285 | 0 | return 1; |
286 | 0 | } |
287 | | |
288 | 0 | return 0; |
289 | 0 | } |