Coverage Report

Created: 2026-07-16 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebsockets/lib/tls/tls-client.c
Line
Count
Source
1
/*
2
 * libwebsockets - small server side websockets and web server implementation
3
 *
4
 * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to
8
 * deal in the Software without restriction, including without limitation the
9
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10
 * sell copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22
 * IN THE SOFTWARE.
23
 */
24
25
#include "private-lib-core.h"
26
27
#if defined(LWS_WITH_TCP_TLS)
28
static int
29
lws_ssl_client_connect1(struct lws *wsi, char *errbuf, size_t len)
30
0
{
31
0
  int n;
32
33
0
  n = lws_tls_client_connect(wsi, errbuf, len);
34
0
  switch (n) {
35
0
  case LWS_SSL_CAPABLE_ERROR:
36
0
    lws_tls_restrict_return_handshake(wsi);
37
0
    return -1;
38
0
  case LWS_SSL_CAPABLE_DONE:
39
0
    lws_tls_restrict_return_handshake(wsi);
40
0
    lws_metrics_caliper_report(wsi->cal_conn, METRES_GO);
41
0
#if defined(LWS_WITH_CONMON)
42
0
  wsi->conmon.ciu_tls = (lws_conmon_interval_us_t)
43
0
          (lws_now_usecs() - wsi->conmon_datum);
44
0
#endif
45
0
    return 1; /* connected */
46
0
  case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
47
0
    lws_callback_on_writable(wsi);
48
    /* fallthru */
49
0
  case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
50
0
    lwsi_set_state(wsi, LRS_WAITING_SSL);
51
0
    break;
52
0
  }
53
54
0
  return 0; /* retry */
55
0
}
56
57
int
58
lws_ssl_client_connect2(struct lws *wsi, char *errbuf, size_t len)
59
0
{
60
0
  int n;
61
62
0
  if (lwsi_state(wsi) == LRS_WAITING_SSL) {
63
0
    n = lws_tls_client_connect(wsi, errbuf, len);
64
0
    lwsl_debug("%s: SSL_connect says %d\n", __func__, n);
65
66
0
    switch (n) {
67
0
    case LWS_SSL_CAPABLE_ERROR:
68
0
      lws_tls_restrict_return_handshake(wsi);
69
70
0
      if (lws_tls_client_confirm_peer_cert(wsi, errbuf, len)) {
71
0
        lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
72
0
        return -1;
73
0
      }
74
75
      // lws_snprintf(errbuf, len, "client connect failed");
76
0
      return -1;
77
0
    case LWS_SSL_CAPABLE_DONE:
78
0
      break; /* connected */
79
0
    case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
80
0
      lws_callback_on_writable(wsi);
81
      /* fallthru */
82
0
    case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
83
0
      lwsi_set_state(wsi, LRS_WAITING_SSL);
84
      /* fallthru */
85
0
      return 0; /* retry */
86
0
    }
87
0
  }
88
89
0
  lws_tls_restrict_return_handshake(wsi);
90
91
0
  if (lws_tls_client_confirm_peer_cert(wsi, errbuf, len)) {
92
0
    lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
93
0
    return -1;
94
0
  }
95
96
0
  lws_metrics_caliper_report(wsi->cal_conn, METRES_GO);
97
0
#if defined(LWS_WITH_CONMON)
98
0
  wsi->conmon.ciu_tls = (lws_conmon_interval_us_t)
99
0
          (lws_now_usecs() - wsi->conmon_datum);
100
0
#endif
101
102
0
  return 1; /* connected */
103
0
}
104
#endif
105
106
107
int lws_context_init_client_ssl(const struct lws_context_creation_info *info,
108
        struct lws_vhost *vhost)
109
0
{
110
0
  const char *private_key_filepath = info->ssl_private_key_filepath;
111
0
  const char *cert_filepath = info->ssl_cert_filepath;
112
0
  const char *ca_filepath = info->ssl_ca_filepath;
113
0
  const char *cipher_list = info->ssl_cipher_list;
114
0
  lws_fakewsi_def_plwsa(&vhost->context->pt[0]);
115
116
0
  lws_fakewsi_prep_plwsa_ctx(vhost->context);
117
118
0
  if (vhost->options & LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG)
119
0
    return 0;
120
121
0
  if (vhost->tls.ssl_ctx) {
122
0
    cert_filepath = NULL;
123
0
    private_key_filepath = NULL;
124
0
    ca_filepath = NULL;
125
0
  }
126
127
  /*
128
   *  for backwards-compatibility default to using ssl_... members, but
129
   * if the newer client-specific ones are given, use those
130
   */
131
0
  if (info->client_ssl_cipher_list)
132
0
    cipher_list = info->client_ssl_cipher_list;
133
0
  if (info->client_ssl_cert_filepath)
134
0
    cert_filepath = info->client_ssl_cert_filepath;
135
0
  if (info->client_ssl_private_key_filepath)
136
0
    private_key_filepath = info->client_ssl_private_key_filepath;
137
138
0
  if (info->client_ssl_ca_filepath)
139
0
    ca_filepath = info->client_ssl_ca_filepath;
140
141
0
  if (vhost->tls.ssl_client_ctx)
142
0
    return 0;
143
144
0
#if !defined(LWS_WITH_MBEDTLS) && !defined(LWS_WITH_BEARSSL) && \
145
0
  !defined(LWS_WITH_OPENHITLS)
146
0
  if (info->provided_client_ssl_ctx) {
147
    /* use the provided OpenSSL context if given one */
148
0
    vhost->tls.ssl_client_ctx = info->provided_client_ssl_ctx;
149
    /* nothing for lib to delete */
150
0
    vhost->tls.user_supplied_ssl_ctx = 1;
151
152
0
    return 0;
153
0
  }
154
0
#endif
155
156
0
  if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
157
0
    return 0;
158
159
0
  if (lws_tls_client_create_vhost_context(vhost, info, cipher_list,
160
0
            ca_filepath,
161
0
            info->client_ssl_ca_mem,
162
0
            info->client_ssl_ca_mem_len,
163
0
            cert_filepath,
164
0
            info->client_ssl_cert_mem,
165
0
            info->client_ssl_cert_mem_len,
166
0
            private_key_filepath,
167
0
            info->client_ssl_key_mem,
168
0
            info->client_ssl_key_mem_len
169
0
            ))
170
0
    return 1;
171
172
0
  lwsl_info("created client ssl context for %s\n", vhost->name);
173
174
  /*
175
   * give him a fake wsi with context set, so he can use
176
   * lws_get_context() in the callback
177
   */
178
179
0
  plwsa->vhost = vhost; /* not a real bound wsi */
180
181
0
  vhost->protocols[0].callback((struct lws *)plwsa,
182
0
      LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS,
183
0
             vhost->tls.ssl_client_ctx, NULL, 0);
184
185
0
  return 0;
186
0
}
187
188
int
189
lws_client_create_tls(struct lws *wsi, const char **pcce, int do_c1)
190
0
{
191
  /* we can retry this... just cook the SSL BIO the first time */
192
193
0
  if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
194
0
#if defined(LWS_WITH_TCP_TLS)
195
0
    int n;
196
0
#endif
197
0
    if (!wsi->tls.ssl) {
198
199
0
#if defined(LWS_WITH_TLS)
200
0
      if (!wsi->transaction_from_pipeline_queue &&
201
0
          lws_tls_restrict_borrow(wsi)) {
202
0
        *pcce = "tls restriction limit";
203
0
        return CCTLS_RETURN_ERROR;
204
0
      }
205
0
#endif
206
0
      if (lws_ssl_client_bio_create(wsi) < 0) {
207
0
        *pcce = "bio_create failed";
208
0
        return CCTLS_RETURN_ERROR;
209
0
      }
210
0
    }
211
212
0
    if (!do_c1)
213
0
      return CCTLS_RETURN_DONE;
214
215
0
#if defined(LWS_WITH_TCP_TLS)
216
0
    lws_metrics_caliper_report(wsi->cal_conn, METRES_GO);
217
0
    lws_metrics_caliper_bind(wsi->cal_conn, wsi->a.context->mt_conn_tls);
218
0
#if defined(LWS_WITH_CONMON)
219
0
    wsi->conmon_datum = lws_now_usecs();
220
0
#endif
221
222
0
    n = lws_ssl_client_connect1(wsi, (char *)wsi->a.context->pt[(int)wsi->tsi].serv_buf,
223
0
              wsi->a.context->pt_serv_buf_size);
224
0
    lwsl_debug("%s: lws_ssl_client_connect1: %d\n", __func__, n);
225
0
    if (!n)
226
0
      return CCTLS_RETURN_RETRY; /* caller should return 0 */
227
228
0
    if (n < 0) {
229
0
      *pcce = (const char *)wsi->a.context->pt[(int)wsi->tsi].serv_buf;
230
0
      lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
231
0
      return CCTLS_RETURN_ERROR;
232
0
    }
233
    /* ...connect1 already handled caliper if SSL_accept done */
234
235
0
    lws_tls_server_conn_alpn(wsi);
236
#else
237
    *pcce = "TCP TLS disabled";
238
    return CCTLS_RETURN_ERROR;
239
#endif
240
0
  } else
241
0
    wsi->tls.ssl = NULL;
242
243
0
  return CCTLS_RETURN_DONE; /* OK */
244
0
}
245
246
int
247
lws_tls_client_upgrade(struct lws *wsi, int ssl_flags)
248
0
{
249
0
  const char *cce = NULL;
250
251
0
  wsi->tls.use_ssl = (unsigned int)ssl_flags;
252
253
0
  if (lws_client_create_tls(wsi, &cce, 1) == CCTLS_RETURN_ERROR)
254
0
    return -1;
255
256
0
  return 0;
257
0
}