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