Coverage Report

Created: 2025-07-18 06:43

/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
}