1 /* tls-mbedtls.c
2  *
3  * Copyright (c) 2019 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 
50 bool
srp_tls_init(void)51 srp_tls_init(void)
52 {
53     int status;
54 
55     // Initialize the shared data structures.
56     mbedtls_x509_crt_init(&srvcert_struct);
57     mbedtls_pk_init(&srvkey);
58     mbedtls_entropy_init(&entropy);
59     mbedtls_ctr_drbg_init(&ctr_drbg);
60 
61     status = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);
62     if (status != 0) {
63         ERROR("Unable to seed RNG: %x", -status);
64         return false;
65     }
66     return true;
67 }
68 
69 static bool
mbedtls_config_init(mbedtls_ssl_config * config,int flags)70 mbedtls_config_init(mbedtls_ssl_config *config, int flags)
71 {
72     int status = mbedtls_ssl_config_defaults(config, flags,
73                                              MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
74     if (status != 0) {
75         ERROR("Unable to set up default TLS config state: %x", -status);
76         return false;
77     }
78 
79     mbedtls_ssl_conf_rng(config, mbedtls_ctr_drbg_random, &ctr_drbg);
80     return true;
81 }
82 
83 bool
srp_tls_client_init(void)84 srp_tls_client_init(void)
85 {
86     if (!mbedtls_config_init(&tls_client_config, MBEDTLS_SSL_IS_CLIENT)) {
87         return false;
88     }
89     return true;
90 }
91 
92 bool
srp_tls_server_init(const char * cacert_file,const char * srvcert_file,const char * server_key_file)93 srp_tls_server_init(const char *cacert_file, const char *srvcert_file, const char *server_key_file)
94 {
95     int status;
96 
97     // Load the public key and cert
98     if (cacert_file != NULL) {
99         status = mbedtls_x509_crt_parse_file(&cacert_struct, cacert_file);
100         if (status != 0) {
101             ERROR("Unable to parse ca cert file: %x", -status);
102             return false;
103         }
104         cacert = &cacert_struct;
105     }
106 
107     if (srvcert_file != NULL) {
108         status = mbedtls_x509_crt_parse_file(&srvcert_struct, srvcert_file);
109         if (status != 0) {
110             ERROR("Unable to parse server cert file: %x", -status);
111             return false;
112         }
113         srvcert = &srvcert_struct;
114         if (srvcert_struct.next && cacert != NULL) {
115             cacert = srvcert_struct.next;
116         }
117     }
118 
119     if (server_key_file != NULL) {
120         status = mbedtls_pk_parse_keyfile(&srvkey, server_key_file, NULL);
121         if (status != 0) {
122             ERROR("Unable to parse server cert file: %x", -status);
123             return false;
124         }
125     }
126 
127     if (!mbedtls_config_init(&tls_server_config, MBEDTLS_SSL_IS_SERVER)) {
128         return false;
129     }
130 
131     if (cacert != NULL) {
132         mbedtls_ssl_conf_ca_chain(&tls_server_config, cacert, NULL);
133     }
134 
135     status = mbedtls_ssl_conf_own_cert(&tls_server_config, srvcert, &srvkey);
136     if (status != 0) {
137         ERROR("Unable to configure own cert: %x", -status);
138         return false;
139     }
140     return true;
141 }
142 
143 static int
srp_tls_io_send(void * ctx,const unsigned char * buf,size_t len)144 srp_tls_io_send(void *ctx, const unsigned char *buf, size_t len)
145 {
146     ssize_t ret;
147     comm_t *comm = ctx;
148     ret = write(comm->io.sock, buf, len);
149     if (ret < 0) {
150         if (errno == EAGAIN) {
151             return MBEDTLS_ERR_SSL_WANT_WRITE;
152         } else {
153             return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
154         }
155     } else {
156         return (int)ret;
157     }
158 }
159 
160 static int
srp_tls_io_recv(void * ctx,unsigned char * buf,size_t max)161 srp_tls_io_recv(void *ctx, unsigned char *buf, size_t max)
162 {
163     ssize_t ret;
164     comm_t *comm = ctx;
165     ret = read(comm->io.sock, buf, max);
166     if (ret < 0) {
167         if (errno == EWOULDBLOCK || errno == EAGAIN) {
168             return MBEDTLS_ERR_SSL_WANT_READ;
169         } else {
170             return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
171         }
172     } else if (ret == 0) {
173         return MBEDTLS_ERR_SSL_CONN_EOF;
174     } else {
175         return (int)ret;
176     }
177 }
178 
179 bool
srp_tls_listen_callback(comm_t * comm)180 srp_tls_listen_callback(comm_t *comm)
181 {
182     int status;
183 
184     // Allocate the TLS config and state structures.
185     comm->tls_context = calloc(1, sizeof *comm->tls_context);
186     if (comm->tls_context == NULL) {
187         return false;
188     }
189     status = mbedtls_ssl_setup(&comm->tls_context->context, &tls_server_config);
190     if (status != 0) {
191         ERROR("Unable to set up TLS listener state: %x", -status);
192         return false;
193     }
194 
195     // Set up the I/O functions.
196     mbedtls_ssl_set_bio(&comm->tls_context->context, comm, srp_tls_io_send, srp_tls_io_recv, NULL);
197 
198     // Start the TLS handshake.
199     status = mbedtls_ssl_handshake(&comm->tls_context->context);
200     if (status != 0 && status != MBEDTLS_ERR_SSL_WANT_READ && status != MBEDTLS_ERR_SSL_WANT_WRITE) {
201         ERROR("TLS handshake failed: %x", -status);
202         srp_tls_context_free(comm);
203         ioloop_close(&comm->io);
204     }
205     return true;
206 }
207 
208 bool
srp_tls_connect_callback(comm_t * comm)209 srp_tls_connect_callback(comm_t *comm)
210 {
211     int status;
212 
213     // Allocate the TLS config and state structures.
214     comm->tls_context = calloc(1, sizeof *comm->tls_context);
215     if (comm->tls_context == NULL) {
216         return false;
217     }
218     status = mbedtls_ssl_setup(&comm->tls_context->context, &tls_client_config);
219     if (status != 0) {
220         ERROR("Unable to set up TLS connect state: %x", -status);
221         return false;
222     }
223 
224     // Set up the I/O functions.
225     mbedtls_ssl_set_bio(&comm->tls_context->context, comm, srp_tls_io_send, srp_tls_io_recv, NULL);
226 
227     // Start the TLS handshake.
228     status = mbedtls_ssl_handshake(&comm->tls_context->context);
229     if (status != 0 && status != MBEDTLS_ERR_SSL_WANT_READ && status != MBEDTLS_ERR_SSL_WANT_WRITE) {
230         ERROR("TLS handshake failed: %x", -status);
231         srp_tls_context_free(comm);
232         ioloop_close(&comm->io);
233     }
234     return true;
235 }
236 
237 ssize_t
srp_tls_read(comm_t * comm,unsigned char * buf,size_t max)238 srp_tls_read(comm_t *comm, unsigned char *buf, size_t max)
239 {
240     int ret = mbedtls_ssl_read(&comm->tls_context->context, buf, max);
241     if (ret < 0) {
242         switch (ret) {
243         case MBEDTLS_ERR_SSL_WANT_READ:
244             return 0;
245         case MBEDTLS_ERR_SSL_WANT_WRITE:
246             ERROR("Got SSL want write in TLS read!");
247             // This means we got EWOULDBLOCK on a write operation.
248             // Not implemented yet, but the right way to handle this is probably to
249             // deselect read events until the socket is ready to write, then write,
250             // and then re-enable read events.   What we don't want is to keep calling
251             // read, because that will spin.
252             return 0;
253         case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS:
254             ERROR("Got async in progress in TLS read!");
255             // No idea how to handle this yet.
256             return 0;
257 #ifdef MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS
258         case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS:
259             ERROR("Got crypto in progress in TLS read!");
260             // No idea how to handle this.
261             return 0;
262 #endif
263         default:
264             ERROR("Unexpected response from SSL read: %x", -ret);
265             return -1;
266         }
267     } else {
268         // mbedtls returns 0 for EOF, just like read(), but we need a different signal,
269         // so we treat 0 as an error (for now).   In principle, we should get a notification
270         // when the remote end is done writing, so a clean close should be different than
271         // an abrupt close.
272         if (ret == 0) {
273             return -1;
274         }
275         return ret;
276     }
277 }
278 
279 void
srp_tls_context_free(comm_t * comm)280 srp_tls_context_free(comm_t *comm)
281 {
282     // Free any state that the TLS library allocated
283     mbedtls_ssl_free(&comm->tls_context->context);
284     // Free and forget the context data structure
285     free(comm->tls_context);
286     comm->tls_context = 0;
287 }
288 
289 ssize_t
srp_tls_write(comm_t * comm,struct iovec * iov,int iov_len)290 srp_tls_write(comm_t *comm, struct iovec *iov, int iov_len)
291 {
292     int ret;
293     int i;
294     int bytes_written = 0;
295     for (i = 0; i < iov_len; i++) {
296         ret = mbedtls_ssl_write(&comm->tls_context->context, iov[i].iov_base, iov[i].iov_len);
297         if (ret < 0) {
298             switch (ret) {
299             case MBEDTLS_ERR_SSL_WANT_READ:
300                 return bytes_written;
301             case MBEDTLS_ERR_SSL_WANT_WRITE:
302                 ERROR("Got SSL want write in TLS read!");
303                 return bytes_written;
304             case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS:
305                 ERROR("Got async in progress in TLS read!");
306                 return bytes_written;
307 #ifdef MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS
308             case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS:
309                 ERROR("Got crypto in progress in TLS read!");
310                 return bytes_written;
311 #endif
312             default:
313                 ERROR("Unexpected response from SSL read: %x", -ret);
314                 return -1;
315             }
316         } else if (ret != iov[i].iov_len) {
317             return bytes_written + ret;
318         } else {
319             bytes_written += ret;
320         }
321     }
322     return bytes_written;
323 }
324 
325 // Local Variables:
326 // mode: C
327 // tab-width: 4
328 // c-file-style: "bsd"
329 // c-basic-offset: 4
330 // fill-column: 108
331 // indent-tabs-mode: nil
332 // End:
333