1 /* 2 * Copyright (c) 2019-2021 Apple Inc. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * https://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * This file contains a TLS Shim that allows mDNSPosix to use mbedtls to do TLS session 17 * establishment and also to accept TLS connections. 18 */ 19 20 #include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above 21 #include "DNSCommon.h" 22 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform 23 #include "PlatformCommon.h" 24 25 #include <stdio.h> 26 #include <arpa/inet.h> 27 #include <string.h> 28 #include <stdlib.h> 29 #include <errno.h> 30 #include <unistd.h> 31 32 #include <mbedtls/error.h> 33 #include <mbedtls/pk.h> 34 #include <mbedtls/ecp.h> 35 #include <mbedtls/ecdsa.h> 36 #include <mbedtls/entropy.h> 37 #include <mbedtls/ctr_drbg.h> 38 #include <mbedtls/sha256.h> 39 #include <mbedtls/base64.h> 40 41 #include <mbedtls/certs.h> 42 #include <mbedtls/x509.h> 43 #include <mbedtls/ssl.h> 44 #include <mbedtls/config.h> 45 46 // Posix TLS server context 47 struct TLSContext_struct { 48 mbedtls_ssl_context context; 49 }; 50 51 struct TLSServerContext_struct { 52 mbedtls_x509_crt cert; 53 mbedtls_pk_context key; 54 mbedtls_x509_crt cacert; 55 mbedtls_ssl_config config; 56 }; 57 58 // Context that is shared amongs all TLS connections, regardless of which server cert/key is in use. 59 static mbedtls_entropy_context entropy; 60 static mbedtls_ctr_drbg_context ctr_drbg; 61 62 mDNSBool 63 mDNSPosixTLSInit(void) 64 { 65 int status; 66 67 mbedtls_entropy_init(&entropy); 68 mbedtls_ctr_drbg_init(&ctr_drbg); 69 70 status = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0); 71 if (status != 0) { 72 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, "Unable to seed RNG: %x", -status); 73 return mDNSfalse; 74 } 75 return mDNStrue; 76 } 77 78 void 79 mDNSPosixTLSContextFree(TLSContext *tls) 80 { 81 mbedtls_ssl_free(&tls->context); 82 mDNSPlatformMemFree(tls); 83 } 84 85 TLSContext * 86 mDNSPosixTLSClientStateCreate(TCPSocket *sock) 87 { 88 int status; 89 TLSContext *tls; 90 mbedtls_ssl_config config; 91 92 status = mbedtls_ssl_config_defaults(&config, MBEDTLS_SSL_IS_CLIENT, 93 MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); 94 if (status != 0) { 95 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, "Unable to set ssl config defaults: %d", -status); 96 return NULL; 97 } 98 mbedtls_ssl_conf_authmode(&config, (sock->flags & kTCPSocketFlags_TLSValidationNotRequired 99 ? MBEDTLS_SSL_VERIFY_NONE 100 : MBEDTLS_SSL_VERIFY_REQUIRED)); 101 102 tls = mDNSPlatformMemAllocateClear(sizeof(*tls)); 103 if (tls == mDNSNULL) { 104 return tls; 105 } 106 107 status = mbedtls_ssl_setup(&tls->context, &config); 108 if (status == 0) { 109 if (sock->hostname) { 110 char serverName[MAX_ESCAPED_DOMAIN_NAME]; 111 ConvertDomainNameToCString_withescape(sock->hostname, serverName, '\\'); 112 status = mbedtls_ssl_set_hostname(&tls->context, serverName); 113 } 114 } 115 if (status != 0) { 116 LogInfo("Unable to set up TLS listener state: %x", -status); 117 mDNSPosixTLSContextFree(tls); 118 return NULL; 119 } 120 return tls; 121 } 122 123 static int 124 tls_io_send(void *ctx, const unsigned char *buf, size_t len) 125 { 126 ssize_t ret; 127 TCPSocket *sock = ctx; 128 ret = mDNSPosixWriteTCP(sock->events.fd, (const char *)buf, len); 129 if (ret < 0) { 130 if (errno == EAGAIN) { 131 return MBEDTLS_ERR_SSL_WANT_WRITE; 132 } else { 133 return MBEDTLS_ERR_SSL_INTERNAL_ERROR; 134 } 135 } else { 136 return (int)ret; 137 } 138 } 139 140 static int 141 tls_io_recv(void *ctx, unsigned char *buf, size_t max) 142 { 143 ssize_t ret; 144 TCPSocket *sock = ctx; 145 mDNSBool closed = mDNSfalse; 146 ret = mDNSPosixReadTCP(sock->events.fd, buf, max, &closed); 147 if (ret < 0) { 148 if (errno == EWOULDBLOCK || errno == EAGAIN) { 149 return MBEDTLS_ERR_SSL_WANT_READ; 150 } else { 151 return MBEDTLS_ERR_SSL_INTERNAL_ERROR; 152 } 153 } else if (closed) { 154 return MBEDTLS_ERR_SSL_CONN_EOF; 155 } else { 156 return (int)ret; 157 } 158 } 159 160 mDNSBool 161 mDNSPosixTLSStart(TCPSocket *sock) 162 { 163 int status; 164 165 // Set up the I/O functions. 166 mbedtls_ssl_set_bio(&sock->tls->context, sock, tls_io_send, tls_io_recv, NULL); 167 168 // Start the TLS handshake 169 status = mbedtls_ssl_handshake(&sock->tls->context); 170 if (status != 0 && status != MBEDTLS_ERR_SSL_WANT_READ && status != MBEDTLS_ERR_SSL_WANT_WRITE) { 171 LogInfo("TLS handshake failed: %x", -status); 172 return mDNSfalse; 173 } 174 return mDNStrue; 175 } 176 177 TLSContext * 178 PosixTLSAccept(TCPListener *listenContext) 179 { 180 int status; 181 TLSContext *tls = mDNSPlatformMemAllocateClear(sizeof(*tls)); 182 183 if (tls == mDNSNULL) { 184 return tls; 185 } 186 187 status = mbedtls_ssl_setup(&tls->context, &listenContext->tls->config); 188 if (status != 0) { 189 LogInfo("Unable to set up TLS listener state: %x", -status); 190 mDNSPlatformMemFree(tls); 191 return NULL; 192 } 193 return tls; 194 } 195 196 int 197 mDNSPosixTLSRead(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed) 198 { 199 int ret; 200 201 // Shouldn't ever happen. 202 if (!sock->tls) { 203 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, "mDNSPosixTLSRead: called without TLS context!"); 204 *closed = mDNStrue; 205 return 0; 206 } 207 208 ret = mbedtls_ssl_read(&sock->tls->context, buf, buflen); 209 if (ret < 0) { 210 switch (ret) { 211 case MBEDTLS_ERR_SSL_WANT_READ: 212 return 0; 213 case MBEDTLS_ERR_SSL_WANT_WRITE: 214 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, "Got SSL want write in TLS read!"); 215 // This means we got EWOULDBLOCK on a write operation. 216 // Not implemented yet, but the right way to handle this is probably to 217 // deselect read events until the socket is ready to write, then write, 218 // and then re-enable read events. What we don't want is to keep calling 219 // read, because that will spin. 220 return 0; 221 case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS: 222 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, "Got async in progress in TLS read!"); 223 // No idea how to handle this yet. 224 return 0; 225 #ifdef MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS 226 case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS: 227 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, "Got crypto in progress in TLS read!"); 228 // No idea how to handle this. 229 return 0; 230 #endif 231 default: 232 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, "Unexpected response from SSL read: %x", -ret); 233 return -1; 234 } 235 } else { 236 // mbedtls returns 0 for EOF, just like read(), but we need a different signal, 237 // so we treat 0 as an error (for now). In principle, we should get a notification 238 // when the remote end is done writing, so a clean close should be different than 239 // an abrupt close. 240 if (ret == 0) { 241 if (closed) { 242 *closed = mDNStrue; 243 } 244 return -1; 245 } 246 return ret; 247 } 248 } 249 250 int 251 mDNSPosixTLSWrite(TCPSocket *sock, const void *buf, unsigned long buflen) 252 { 253 int ret; 254 ret = mbedtls_ssl_write(&sock->tls->context, buf, buflen); 255 if (ret < 0) { 256 switch (ret) { 257 case MBEDTLS_ERR_SSL_WANT_READ: 258 return 0; 259 case MBEDTLS_ERR_SSL_WANT_WRITE: 260 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, "Got SSL want write in TLS read!"); 261 return 0; 262 case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS: 263 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, "Got async in progress in TLS read!"); 264 return 0; 265 #ifdef MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS 266 case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS: 267 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, "Got crypto in progress in TLS read!"); 268 return 0; 269 #endif 270 default: 271 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, "Unexpected response from SSL read: %x", -ret); 272 return -1; 273 } 274 } 275 return ret; 276 } 277 278 // Local Variables: 279 // mode: C 280 // tab-width: 4 281 // c-file-style: "bsd" 282 // c-basic-offset: 4 283 // fill-column: 108 284 // indent-tabs-mode: nil 285 // End: 286