1 /* tls-mbedtls.c 2 * 3 * Copyright (c) 2019-2021 Apple Computer, Inc. All rights reserved. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 * DNS SIG(0) signature verification for DNSSD SRP using mbedtls. 18 * 19 * Provides functions for generating a public key validating context based on SIG(0) KEY RR data, and 20 * validating a signature using a context generated with that public key. Currently only ECDSASHA256 is 21 * supported. 22 */ 23 24 #include <stdio.h> 25 #include <arpa/inet.h> 26 #include <string.h> 27 #include <stdlib.h> 28 #include <errno.h> 29 #include <unistd.h> 30 31 #include "srp.h" 32 #define SRP_CRYPTO_MBEDTLS_INTERNAL 33 #include "dns-msg.h" 34 #include "srp-crypto.h" 35 #include "ioloop.h" 36 #include "srp-tls.h" 37 38 // Context that is shared amongs all TLS connections, regardless of which server cert/key is in use. 39 mbedtls_entropy_context entropy; 40 mbedtls_ctr_drbg_context ctr_drbg; 41 42 // For now, assume that we are using just one key and one server cert, plus the ca cert. Consequently, we 43 // can treat this as global state. If wanted later, we could make this its own structure. 44 mbedtls_x509_crt cacert_struct, *cacert = NULL; 45 mbedtls_x509_crt srvcert_struct, *srvcert = NULL; 46 mbedtls_pk_context srvkey; 47 mbedtls_ssl_config tls_server_config; 48 mbedtls_ssl_config tls_client_config; 49 mbedtls_ssl_config tls_opportunistic_config; 50 51 bool 52 srp_tls_init(void) 53 { 54 int status; 55 56 // Initialize the shared data structures. 57 mbedtls_x509_crt_init(&srvcert_struct); 58 mbedtls_pk_init(&srvkey); 59 mbedtls_entropy_init(&entropy); 60 mbedtls_ctr_drbg_init(&ctr_drbg); 61 62 status = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0); 63 if (status != 0) { 64 ERROR("Unable to seed RNG: %x", -status); 65 return false; 66 } 67 return true; 68 } 69 70 static bool 71 mbedtls_config_init(mbedtls_ssl_config *config, int flags) 72 { 73 int status = mbedtls_ssl_config_defaults(config, flags, 74 MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); 75 if (status != 0) { 76 ERROR("Unable to set up default TLS config state: %x", -status); 77 return false; 78 } 79 80 mbedtls_ssl_conf_rng(config, mbedtls_ctr_drbg_random, &ctr_drbg); 81 return true; 82 } 83 84 void 85 srp_tls_configure(void *const NULLABLE context) 86 { 87 } 88 89 bool 90 srp_tls_client_init(void) 91 { 92 if (!mbedtls_config_init(&tls_client_config, MBEDTLS_SSL_IS_CLIENT)) { 93 return false; 94 } 95 if (!mbedtls_config_init(&tls_opportunistic_config, MBEDTLS_SSL_IS_CLIENT)) { 96 return false; 97 } 98 mbedtls_ssl_conf_authmode(&tls_opportunistic_config, MBEDTLS_SSL_VERIFY_OPTIONAL); 99 return true; 100 } 101 102 bool 103 srp_tls_server_init(const char *cacert_file, const char *srvcert_file, const char *server_key_file) 104 { 105 int status; 106 107 // Load the public key and cert 108 if (cacert_file != NULL) { 109 status = mbedtls_x509_crt_parse_file(&cacert_struct, cacert_file); 110 if (status != 0) { 111 ERROR("Unable to parse ca cert file: %x", -status); 112 return false; 113 } 114 cacert = &cacert_struct; 115 } 116 117 if (srvcert_file != NULL) { 118 status = mbedtls_x509_crt_parse_file(&srvcert_struct, srvcert_file); 119 if (status != 0) { 120 ERROR("Unable to parse server cert file: %x", -status); 121 return false; 122 } 123 srvcert = &srvcert_struct; 124 if (srvcert_struct.next && cacert != NULL) { 125 cacert = srvcert_struct.next; 126 } 127 } 128 129 if (server_key_file != NULL) { 130 status = mbedtls_pk_parse_keyfile(&srvkey, server_key_file, NULL); 131 if (status != 0) { 132 ERROR("Unable to parse server cert file: %x", -status); 133 return false; 134 } 135 } 136 137 if (!mbedtls_config_init(&tls_server_config, MBEDTLS_SSL_IS_SERVER)) { 138 return false; 139 } 140 141 if (cacert != NULL) { 142 mbedtls_ssl_conf_ca_chain(&tls_server_config, cacert, NULL); 143 } 144 145 status = mbedtls_ssl_conf_own_cert(&tls_server_config, srvcert, &srvkey); 146 if (status != 0) { 147 ERROR("Unable to configure own cert: %x", -status); 148 return false; 149 } 150 return true; 151 } 152 153 static int 154 srp_tls_io_send(void *ctx, const unsigned char *buf, size_t len) 155 { 156 ssize_t ret; 157 comm_t *comm = ctx; 158 ret = write(comm->io.fd, buf, len); 159 if (ret < 0) { 160 if (errno == EAGAIN) { 161 return MBEDTLS_ERR_SSL_WANT_WRITE; 162 } else { 163 return MBEDTLS_ERR_SSL_INTERNAL_ERROR; 164 } 165 } else { 166 return (int)ret; 167 } 168 } 169 170 static int 171 srp_tls_io_recv(void *ctx, unsigned char *buf, size_t max) 172 { 173 ssize_t ret; 174 comm_t *comm = ctx; 175 ret = read(comm->io.fd, buf, max); 176 if (ret < 0) { 177 if (errno == EWOULDBLOCK || errno == EAGAIN) { 178 return MBEDTLS_ERR_SSL_WANT_READ; 179 } else { 180 return MBEDTLS_ERR_SSL_INTERNAL_ERROR; 181 } 182 } else if (ret == 0) { 183 return MBEDTLS_ERR_SSL_CONN_EOF; 184 } else { 185 return (int)ret; 186 } 187 } 188 189 bool 190 srp_tls_listen_callback(comm_t *comm) 191 { 192 int status; 193 194 // Allocate the TLS config and state structures. 195 comm->tls_context = calloc(1, sizeof *comm->tls_context); 196 if (comm->tls_context == NULL) { 197 return false; 198 } 199 status = mbedtls_ssl_setup(&comm->tls_context->context, &tls_server_config); 200 if (status != 0) { 201 ERROR("Unable to set up TLS listener state: %x", -status); 202 return false; 203 } 204 205 // Set up the I/O functions. 206 mbedtls_ssl_set_bio(&comm->tls_context->context, comm, srp_tls_io_send, srp_tls_io_recv, NULL); 207 208 // Start the TLS handshake. 209 status = mbedtls_ssl_handshake(&comm->tls_context->context); 210 if (status != 0 && status != MBEDTLS_ERR_SSL_WANT_READ && status != MBEDTLS_ERR_SSL_WANT_WRITE) { 211 ERROR("TLS handshake failed: %x", -status); 212 srp_tls_context_free(comm); 213 ioloop_close(&comm->io); 214 } 215 return true; 216 } 217 218 bool 219 srp_tls_connect_callback(comm_t *comm) 220 { 221 int status; 222 mbedtls_ssl_config *config = comm->opportunistic ? &tls_opportunistic_config : &tls_client_config; 223 // Allocate the TLS config and state structures. 224 comm->tls_context = calloc(1, sizeof *comm->tls_context); 225 if (comm->tls_context == NULL) { 226 return false; 227 } 228 status = mbedtls_ssl_setup(&comm->tls_context->context, config); 229 if (status != 0) { 230 ERROR("Unable to set up TLS connect state: %x", -status); 231 return false; 232 } 233 234 // Set up the I/O functions. 235 mbedtls_ssl_set_bio(&comm->tls_context->context, comm, srp_tls_io_send, srp_tls_io_recv, NULL); 236 237 // Start the TLS handshake. 238 status = mbedtls_ssl_handshake(&comm->tls_context->context); 239 if (status != 0 && status != MBEDTLS_ERR_SSL_WANT_READ && status != MBEDTLS_ERR_SSL_WANT_WRITE) { 240 ERROR("TLS handshake failed: %x", -status); 241 srp_tls_context_free(comm); 242 return false; 243 } 244 if (status == MBEDTLS_ERR_SSL_WANT_READ) { 245 comm->tls_handshake_incomplete = true; 246 } 247 INFO(PRI_S_SRP ": TLS handshake progress %d", comm->name, -status); 248 return true; 249 } 250 251 ssize_t 252 srp_tls_read(comm_t *comm, unsigned char *buf, size_t max) 253 { 254 // If we aren't done with the TLS handshake, continue it. 255 if (comm->tls_handshake_incomplete) { 256 int status = mbedtls_ssl_handshake(&comm->tls_context->context); 257 if (status != 0 && status != MBEDTLS_ERR_SSL_WANT_READ && status != MBEDTLS_ERR_SSL_WANT_WRITE) { 258 ERROR("TLS handshake failed: %x", -status); 259 srp_tls_context_free(comm); 260 return -1; 261 } 262 if (status == 0) { 263 comm->tls_handshake_incomplete = false; 264 comm->connected(comm, comm->context); 265 } 266 INFO(PRI_S_SRP ": TLS handshake progress %d", comm->name, -status); 267 return 0; 268 } 269 270 // Otherwise, read application data. 271 int ret = mbedtls_ssl_read(&comm->tls_context->context, buf, max); 272 if (ret < 0) { 273 switch (ret) { 274 case MBEDTLS_ERR_SSL_WANT_READ: 275 return 0; 276 case MBEDTLS_ERR_SSL_WANT_WRITE: 277 ERROR("Got SSL want write in TLS read!"); 278 // This means we got EWOULDBLOCK on a write operation. 279 // Not implemented yet, but the right way to handle this is probably to 280 // deselect read events until the socket is ready to write, then write, 281 // and then re-enable read events. What we don't want is to keep calling 282 // read, because that will spin. 283 return 0; 284 case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS: 285 ERROR("Got async in progress in TLS read!"); 286 // No idea how to handle this yet. 287 return 0; 288 #ifdef MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS 289 case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS: 290 ERROR("Got crypto in progress in TLS read!"); 291 // No idea how to handle this. 292 return 0; 293 #endif 294 default: 295 ERROR("Unexpected response from SSL read: %x", -ret); 296 return -1; 297 } 298 } else { 299 // mbedtls returns 0 for EOF, just like read(), but we need a different signal, 300 // so we treat 0 as an error (for now). In principle, we should get a notification 301 // when the remote end is done writing, so a clean close should be different than 302 // an abrupt close. 303 if (ret == 0) { 304 ERROR("mbedtls_ssl_read returned zero."); 305 return -1; 306 } 307 return ret; 308 } 309 } 310 311 void 312 srp_tls_context_free(comm_t *comm) 313 { 314 // Free any state that the TLS library allocated 315 mbedtls_ssl_free(&comm->tls_context->context); 316 // Free and forget the context data structure 317 free(comm->tls_context); 318 comm->tls_context = 0; 319 } 320 321 ssize_t 322 srp_tls_write(comm_t *comm, struct iovec *iov, int iov_len) 323 { 324 int ret; 325 int i; 326 int bytes_written = 0; 327 for (i = 0; i < iov_len; i++) { 328 ret = mbedtls_ssl_write(&comm->tls_context->context, iov[i].iov_base, iov[i].iov_len); 329 if (ret < 0) { 330 switch (ret) { 331 case MBEDTLS_ERR_SSL_WANT_READ: 332 return bytes_written; 333 case MBEDTLS_ERR_SSL_WANT_WRITE: 334 ERROR("Got SSL want write in TLS read!"); 335 return bytes_written; 336 case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS: 337 ERROR("Got async in progress in TLS read!"); 338 return bytes_written; 339 #ifdef MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS 340 case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS: 341 ERROR("Got crypto in progress in TLS read!"); 342 return bytes_written; 343 #endif 344 default: 345 ERROR("Unexpected response from SSL read: %x", -ret); 346 return -1; 347 } 348 } else if (ret != iov[i].iov_len) { 349 return bytes_written + ret; 350 } else { 351 bytes_written += ret; 352 } 353 } 354 return bytes_written; 355 } 356 357 // Dummy function for now; should eventually fetch the TLS context to use for validating 358 // a cert presented by a remote connection. 359 void 360 configure_tls(void *const NULLABLE UNUSED context) 361 { 362 } 363 364 void 365 schedule_tls_certificate_rotation(wakeup_t **const UNUSED tls_listener_wakeup, 366 comm_t *const UNUSED tls_listener_to_rotate) 367 { 368 ; 369 } 370 371 // Local Variables: 372 // mode: C 373 // tab-width: 4 374 // c-file-style: "bsd" 375 // c-basic-offset: 4 376 // fill-column: 108 377 // indent-tabs-mode: nil 378 // End: 379