Home | History | Annotate | Line # | Download | only in mDNSPosix
      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