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
27static int
28lws_ssl_client_connect1(struct lws *wsi, char *errbuf, size_t len)
29{
30	int n;
31
32	n = lws_tls_client_connect(wsi, errbuf, len);
33	switch (n) {
34	case LWS_SSL_CAPABLE_ERROR:
35		lws_tls_restrict_return_handshake(wsi);
36		return -1;
37	case LWS_SSL_CAPABLE_DONE:
38		lws_tls_restrict_return_handshake(wsi);
39		lws_metrics_caliper_report(wsi->cal_conn, METRES_GO);
40#if defined(LWS_WITH_CONMON)
41	wsi->conmon.ciu_tls = (lws_conmon_interval_us_t)
42					(lws_now_usecs() - wsi->conmon_datum);
43#endif
44		return 1; /* connected */
45	case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
46		lws_callback_on_writable(wsi);
47		/* fallthru */
48	case LWS_SSL_CAPABLE_MORE_SERVICE:
49	case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
50		lwsi_set_state(wsi, LRS_WAITING_SSL);
51		break;
52	}
53
54	return 0; /* retry */
55}
56
57int
58lws_ssl_client_connect2(struct lws *wsi, char *errbuf, size_t len)
59{
60	int n;
61
62	if (lwsi_state(wsi) == LRS_WAITING_SSL) {
63		n = lws_tls_client_connect(wsi, errbuf, len);
64		lwsl_debug("%s: SSL_connect says %d\n", __func__, n);
65
66		switch (n) {
67		case LWS_SSL_CAPABLE_ERROR:
68			lws_tls_restrict_return_handshake(wsi);
69
70			if (lws_tls_client_confirm_peer_cert(wsi, errbuf, len)) {
71				lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
72				return -1;
73			}
74
75			// lws_snprintf(errbuf, len, "client connect failed");
76			return -1;
77		case LWS_SSL_CAPABLE_DONE:
78			break; /* connected */
79		case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
80			lws_callback_on_writable(wsi);
81			/* fallthru */
82		case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
83			lwsi_set_state(wsi, LRS_WAITING_SSL);
84			/* fallthru */
85		case LWS_SSL_CAPABLE_MORE_SERVICE:
86			return 0; /* retry */
87		}
88	}
89
90	lws_tls_restrict_return_handshake(wsi);
91
92	if (lws_tls_client_confirm_peer_cert(wsi, errbuf, len)) {
93		lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
94		return -1;
95	}
96
97	lws_metrics_caliper_report(wsi->cal_conn, METRES_GO);
98#if defined(LWS_WITH_CONMON)
99	wsi->conmon.ciu_tls = (lws_conmon_interval_us_t)
100					(lws_now_usecs() - wsi->conmon_datum);
101#endif
102
103	return 1; /* connected */
104}
105
106
107int lws_context_init_client_ssl(const struct lws_context_creation_info *info,
108				struct lws_vhost *vhost)
109{
110	const char *private_key_filepath = info->ssl_private_key_filepath;
111	const char *cert_filepath = info->ssl_cert_filepath;
112	const char *ca_filepath = info->ssl_ca_filepath;
113	const char *cipher_list = info->ssl_cipher_list;
114	lws_fakewsi_def_plwsa(&vhost->context->pt[0]);
115
116	lws_fakewsi_prep_plwsa_ctx(vhost->context);
117
118	if (vhost->options & LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG)
119		return 0;
120
121	if (vhost->tls.ssl_ctx) {
122		cert_filepath = NULL;
123		private_key_filepath = NULL;
124		ca_filepath = NULL;
125	}
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	if (info->client_ssl_cipher_list)
132		cipher_list = info->client_ssl_cipher_list;
133	if (info->client_ssl_cert_filepath)
134		cert_filepath = info->client_ssl_cert_filepath;
135	if (info->client_ssl_private_key_filepath)
136		private_key_filepath = info->client_ssl_private_key_filepath;
137
138	if (info->client_ssl_ca_filepath)
139		ca_filepath = info->client_ssl_ca_filepath;
140
141	if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
142		return 0;
143
144	if (vhost->tls.ssl_client_ctx)
145		return 0;
146
147#if !defined(LWS_WITH_MBEDTLS)
148	if (info->provided_client_ssl_ctx) {
149		/* use the provided OpenSSL context if given one */
150		vhost->tls.ssl_client_ctx = info->provided_client_ssl_ctx;
151		/* nothing for lib to delete */
152		vhost->tls.user_supplied_ssl_ctx = 1;
153
154		return 0;
155	}
156#endif
157
158	if (lws_tls_client_create_vhost_context(vhost, info, cipher_list,
159						ca_filepath,
160						info->client_ssl_ca_mem,
161						info->client_ssl_ca_mem_len,
162						cert_filepath,
163						info->client_ssl_cert_mem,
164						info->client_ssl_cert_mem_len,
165						private_key_filepath,
166						info->client_ssl_key_mem,
167						info->client_ssl_key_mem_len
168						))
169		return 1;
170
171	lwsl_info("created client ssl context for %s\n", vhost->name);
172
173	/*
174	 * give him a fake wsi with context set, so he can use
175	 * lws_get_context() in the callback
176	 */
177
178	plwsa->vhost = vhost; /* not a real bound wsi */
179
180	vhost->protocols[0].callback((struct lws *)plwsa,
181			LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS,
182				     vhost->tls.ssl_client_ctx, NULL, 0);
183
184	return 0;
185}
186
187int
188lws_client_create_tls(struct lws *wsi, const char **pcce, int do_c1)
189{
190	/* we can retry this... just cook the SSL BIO the first time */
191
192	if (wsi->tls.use_ssl & LCCSCF_USE_SSL) {
193		int n;
194
195		if (!wsi->tls.ssl) {
196
197#if defined(LWS_WITH_TLS)
198			if (!wsi->transaction_from_pipeline_queue &&
199			    lws_tls_restrict_borrow(wsi)) {
200				*pcce = "tls restriction limit";
201				return CCTLS_RETURN_ERROR;
202			}
203#endif
204			if (lws_ssl_client_bio_create(wsi) < 0) {
205				*pcce = "bio_create failed";
206				return CCTLS_RETURN_ERROR;
207			}
208		}
209
210		if (!do_c1)
211			return CCTLS_RETURN_DONE;
212
213		lws_metrics_caliper_report(wsi->cal_conn, METRES_GO);
214		lws_metrics_caliper_bind(wsi->cal_conn, wsi->a.context->mt_conn_tls);
215#if defined(LWS_WITH_CONMON)
216		wsi->conmon_datum = lws_now_usecs();
217#endif
218
219		n = lws_ssl_client_connect1(wsi, (char *)wsi->a.context->pt[(int)wsi->tsi].serv_buf,
220					    wsi->a.context->pt_serv_buf_size);
221		lwsl_debug("%s: lws_ssl_client_connect1: %d\n", __func__, n);
222		if (!n)
223			return CCTLS_RETURN_RETRY; /* caller should return 0 */
224
225		if (n < 0) {
226			*pcce = (const char *)wsi->a.context->pt[(int)wsi->tsi].serv_buf;
227			lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO);
228			return CCTLS_RETURN_ERROR;
229		}
230		/* ...connect1 already handled caliper if SSL_accept done */
231
232		lws_tls_server_conn_alpn(wsi);
233
234	} else
235		wsi->tls.ssl = NULL;
236
237	return CCTLS_RETURN_DONE; /* OK */
238}
239