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