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 static int
lws_ssl_handshake_serialize(struct lws_context * ctx,struct lws * wsi)28 lws_ssl_handshake_serialize(struct lws_context *ctx, struct lws *wsi)
29 {
30 struct lws_vhost *vh = ctx->vhost_list;
31 #if LWS_MAX_SMP > 1
32 int tsi = lws_pthread_self_to_tsi(ctx);
33 #else
34 int tsi = 0;
35 #endif
36 struct lws_context_per_thread *pt = &ctx->pt[tsi];
37 unsigned int n;
38
39 while (vh) {
40 for (n = 0; n < pt->fds_count; n++) {
41 struct lws *w = wsi_from_fd(ctx, pt->fds[n].fd);
42
43 if (!w || w->tsi != tsi || w->a.vhost != vh || wsi == w)
44 continue;
45
46 /* Now we found other vhost's wsi in process */
47 if (lwsi_role_mqtt(w)) {
48 /* MQTT TLS connection not established yet.
49 * Let it finish.
50 */
51 if (lwsi_state(w) != LRS_ESTABLISHED)
52 return 1;
53 } else {
54 /* H1/H2 not finished yet. Let it finish. */
55 if (lwsi_state(w) != LRS_DEAD_SOCKET)
56 return 1;
57 }
58 }
59 vh = vh->vhost_next;
60 }
61 return 0;
62 }
63
64 static int
lws_ssl_client_connect1(struct lws * wsi,char * errbuf,size_t len)65 lws_ssl_client_connect1(struct lws *wsi, char *errbuf, size_t len)
66 {
67 int n;
68
69 n = lws_tls_client_connect(wsi, errbuf, len);
70 switch (n) {
71 case LWS_SSL_CAPABLE_ERROR:
72 return -1;
73 case LWS_SSL_CAPABLE_DONE:
74 lws_metrics_caliper_report(wsi->cal_conn, METRES_GO);
75 #if defined(LWS_WITH_CONMON)
76 wsi->conmon.ciu_tls = (lws_conmon_interval_us_t)
77 (lws_now_usecs() - wsi->conmon_datum);
78 #endif
79 return 1; /* connected */
80 case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
81 lws_callback_on_writable(wsi);
82 /* fallthru */
83 case LWS_SSL_CAPABLE_MORE_SERVICE:
84 case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
85 lwsi_set_state(wsi, LRS_WAITING_SSL);
86 break;
87 }
88
89 return 0; /* retry */
90 }
91
92 int
lws_ssl_client_connect2(struct lws * wsi,char * errbuf,size_t len)93 lws_ssl_client_connect2(struct lws *wsi, char *errbuf, size_t len)
94 {
95 int n;
96
97 if (lwsi_state(wsi) == LRS_WAITING_SSL) {
98 n = lws_tls_client_connect(wsi, errbuf, len);
99 lwsl_debug("%s: SSL_connect says %d\n", __func__, n);
100
101 switch (n) {
102 case LWS_SSL_CAPABLE_ERROR:
103 // lws_snprintf(errbuf, len, "client connect failed");
104 return -1;
105 case LWS_SSL_CAPABLE_DONE:
106 break; /* connected */
107 case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
108 lws_callback_on_writable(wsi);
109 /* fallthru */
110 case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
111 lwsi_set_state(wsi, LRS_WAITING_SSL);
112 /* fallthru */
113 case LWS_SSL_CAPABLE_MORE_SERVICE:
114 return 0;
115 }
116 }
117
118 if (lws_tls_client_confirm_peer_cert(wsi, errbuf, len)) {
119 lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
120 return -1;
121 }
122
123 lws_metrics_caliper_report(wsi->cal_conn, METRES_GO);
124 #if defined(LWS_WITH_CONMON)
125 wsi->conmon.ciu_tls = (lws_conmon_interval_us_t)
126 (lws_now_usecs() - wsi->conmon_datum);
127 #endif
128
129 return 1;
130 }
131
132
lws_context_init_client_ssl(const struct lws_context_creation_info * info,struct lws_vhost * vhost)133 int lws_context_init_client_ssl(const struct lws_context_creation_info *info,
134 struct lws_vhost *vhost)
135 {
136 const char *private_key_filepath = info->ssl_private_key_filepath;
137 const char *cert_filepath = info->ssl_cert_filepath;
138 const char *ca_filepath = info->ssl_ca_filepath;
139 const char *cipher_list = info->ssl_cipher_list;
140 lws_fakewsi_def_plwsa(&vhost->context->pt[0]);
141
142 lws_fakewsi_prep_plwsa_ctx(vhost->context);
143
144 if (vhost->options & LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG)
145 return 0;
146
147 if (vhost->tls.ssl_ctx) {
148 cert_filepath = NULL;
149 private_key_filepath = NULL;
150 ca_filepath = NULL;
151 }
152
153 /*
154 * for backwards-compatibility default to using ssl_... members, but
155 * if the newer client-specific ones are given, use those
156 */
157 if (info->client_ssl_cipher_list)
158 cipher_list = info->client_ssl_cipher_list;
159 if (info->client_ssl_cert_filepath)
160 cert_filepath = info->client_ssl_cert_filepath;
161 if (info->client_ssl_private_key_filepath)
162 private_key_filepath = info->client_ssl_private_key_filepath;
163
164 if (info->client_ssl_ca_filepath)
165 ca_filepath = info->client_ssl_ca_filepath;
166
167 if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
168 return 0;
169
170 if (vhost->tls.ssl_client_ctx)
171 return 0;
172
173 #if !defined(LWS_WITH_MBEDTLS)
174 if (info->provided_client_ssl_ctx) {
175 /* use the provided OpenSSL context if given one */
176 vhost->tls.ssl_client_ctx = info->provided_client_ssl_ctx;
177 /* nothing for lib to delete */
178 vhost->tls.user_supplied_ssl_ctx = 1;
179
180 return 0;
181 }
182 #endif
183
184 if (lws_tls_client_create_vhost_context(vhost, info, cipher_list,
185 ca_filepath,
186 info->client_ssl_ca_mem,
187 info->client_ssl_ca_mem_len,
188 cert_filepath,
189 info->client_ssl_cert_mem,
190 info->client_ssl_cert_mem_len,
191 private_key_filepath,
192 info->client_ssl_key_mem,
193 info->client_ssl_key_mem_len
194 ))
195 return 1;
196
197 lwsl_info("created client ssl context for %s\n", vhost->name);
198
199 /*
200 * give him a fake wsi with context set, so he can use
201 * lws_get_context() in the callback
202 */
203
204 plwsa->vhost = vhost; /* not a real bound wsi */
205
206 vhost->protocols[0].callback((struct lws *)plwsa,
207 LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS,
208 vhost->tls.ssl_client_ctx, NULL, 0);
209
210 return 0;
211 }
212
213 int
lws_client_create_tls(struct lws * wsi,const char ** pcce,int do_c1)214 lws_client_create_tls(struct lws *wsi, const char **pcce, int do_c1)
215 {
216
217 /* we can retry this... just cook the SSL BIO the first time */
218
219 if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
220 int n;
221
222 if (!wsi->tls.ssl) {
223 if (lws_ssl_client_bio_create(wsi) < 0) {
224 *pcce = "bio_create failed";
225 return CCTLS_RETURN_ERROR;
226 }
227
228 #if defined(LWS_WITH_TLS)
229 if (!wsi->transaction_from_pipeline_queue) {
230 if (lws_tls_restrict_borrow(wsi->a.context)) {
231 *pcce = "tls restriction limit";
232 return CCTLS_RETURN_ERROR;
233 }
234 wsi->tls_borrowed = 1;
235 if (wsi->a.context->ssl_handshake_serialize) {
236 if (lws_ssl_handshake_serialize(wsi->a.context, wsi)) {
237 lws_tls_restrict_return(wsi->a.context);
238 wsi->tls_borrowed = 0;
239 *pcce = "ssl handshake serialization";
240 return CCTLS_RETURN_ERROR;
241 }
242 }
243 }
244 #endif
245 }
246
247 if (!do_c1)
248 return 0;
249
250 lws_metrics_caliper_report(wsi->cal_conn, METRES_GO);
251 lws_metrics_caliper_bind(wsi->cal_conn, wsi->a.context->mt_conn_tls);
252 #if defined(LWS_WITH_CONMON)
253 wsi->conmon_datum = lws_now_usecs();
254 #endif
255
256 n = lws_ssl_client_connect1(wsi, (char *)wsi->a.context->pt[(int)wsi->tsi].serv_buf,
257 wsi->a.context->pt_serv_buf_size);
258 lwsl_debug("%s: lws_ssl_client_connect1: %d\n", __func__, n);
259 if (!n)
260 return CCTLS_RETURN_RETRY; /* caller should return 0 */
261 if (n < 0) {
262 *pcce = (const char *)wsi->a.context->pt[(int)wsi->tsi].serv_buf;
263 lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
264 return CCTLS_RETURN_ERROR;
265 }
266 /* ...connect1 already handled caliper if SSL_accept done */
267 } else
268 wsi->tls.ssl = NULL;
269
270 #if 0
271 #if defined (LWS_WITH_HTTP2)
272 if (wsi->client_h2_alpn) {
273 /*
274 * We connected to the server and set up tls, and
275 * negotiated "h2".
276 *
277 * So this is it, we are an h2 nwsi client connection
278 * now, not an h1 client connection.
279 */
280 #if defined(LWS_WITH_TLS)
281 lws_tls_server_conn_alpn(wsi);
282 #endif
283
284 /* send the H2 preface to legitimize the connection */
285 if (lws_h2_issue_preface(wsi)) {
286 *pcce = "error sending h2 preface";
287 return CCTLS_RETURN_ERROR;
288 }
289
290 lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
291 }
292 #endif
293 #endif
294
295 return CCTLS_RETURN_DONE; /* OK */
296 }
297