1*de0e0e4dSAntonio Huete Jimenez /* $OpenBSD: tls_util.c,v 1.15 2021/08/16 13:54:38 tb Exp $ */
2f5b1c8a1SJohn Marino /*
3f5b1c8a1SJohn Marino * Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
472c33676SMaxim Ag * Copyright (c) 2014 Ted Unangst <tedu@openbsd.org>
5f5b1c8a1SJohn Marino * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
6f5b1c8a1SJohn Marino *
7f5b1c8a1SJohn Marino * Permission to use, copy, modify, and distribute this software for any
8f5b1c8a1SJohn Marino * purpose with or without fee is hereby granted, provided that the above
9f5b1c8a1SJohn Marino * copyright notice and this permission notice appear in all copies.
10f5b1c8a1SJohn Marino *
11f5b1c8a1SJohn Marino * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12f5b1c8a1SJohn Marino * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13f5b1c8a1SJohn Marino * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14f5b1c8a1SJohn Marino * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15f5b1c8a1SJohn Marino * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16f5b1c8a1SJohn Marino * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17f5b1c8a1SJohn Marino * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18f5b1c8a1SJohn Marino */
19f5b1c8a1SJohn Marino
20f5b1c8a1SJohn Marino #include <sys/stat.h>
21f5b1c8a1SJohn Marino
22f5b1c8a1SJohn Marino #include <stdlib.h>
23f5b1c8a1SJohn Marino #include <unistd.h>
24f5b1c8a1SJohn Marino #include <fcntl.h>
25f5b1c8a1SJohn Marino
26f5b1c8a1SJohn Marino #include "tls.h"
27f5b1c8a1SJohn Marino #include "tls_internal.h"
28f5b1c8a1SJohn Marino
2972c33676SMaxim Ag static void *
memdup(const void * in,size_t len)3072c33676SMaxim Ag memdup(const void *in, size_t len)
3172c33676SMaxim Ag {
3272c33676SMaxim Ag void *out;
3372c33676SMaxim Ag
3472c33676SMaxim Ag if ((out = malloc(len)) == NULL)
3572c33676SMaxim Ag return NULL;
3672c33676SMaxim Ag memcpy(out, in, len);
3772c33676SMaxim Ag return out;
3872c33676SMaxim Ag }
3972c33676SMaxim Ag
4072c33676SMaxim Ag int
tls_set_mem(char ** dest,size_t * destlen,const void * src,size_t srclen)4172c33676SMaxim Ag tls_set_mem(char **dest, size_t *destlen, const void *src, size_t srclen)
4272c33676SMaxim Ag {
4372c33676SMaxim Ag free(*dest);
4472c33676SMaxim Ag *dest = NULL;
4572c33676SMaxim Ag *destlen = 0;
4672c33676SMaxim Ag if (src != NULL) {
4772c33676SMaxim Ag if ((*dest = memdup(src, srclen)) == NULL)
4872c33676SMaxim Ag return -1;
4972c33676SMaxim Ag *destlen = srclen;
5072c33676SMaxim Ag }
5172c33676SMaxim Ag return 0;
5272c33676SMaxim Ag }
5372c33676SMaxim Ag
5472c33676SMaxim Ag int
tls_set_string(const char ** dest,const char * src)5572c33676SMaxim Ag tls_set_string(const char **dest, const char *src)
5672c33676SMaxim Ag {
5772c33676SMaxim Ag free((char *)*dest);
5872c33676SMaxim Ag *dest = NULL;
5972c33676SMaxim Ag if (src != NULL)
6072c33676SMaxim Ag if ((*dest = strdup(src)) == NULL)
6172c33676SMaxim Ag return -1;
6272c33676SMaxim Ag return 0;
6372c33676SMaxim Ag }
6472c33676SMaxim Ag
65f5b1c8a1SJohn Marino /*
66f5b1c8a1SJohn Marino * Extract the host and port from a colon separated value. For a literal IPv6
67f5b1c8a1SJohn Marino * address the address must be contained with square braces. If a host and
68f5b1c8a1SJohn Marino * port are successfully extracted, the function will return 0 and the
69f5b1c8a1SJohn Marino * caller is responsible for freeing the host and port. If no port is found
70f5b1c8a1SJohn Marino * then the function will return 1, with both host and port being NULL.
71f5b1c8a1SJohn Marino * On memory allocation failure -1 will be returned.
72f5b1c8a1SJohn Marino */
73f5b1c8a1SJohn Marino int
tls_host_port(const char * hostport,char ** host,char ** port)74f5b1c8a1SJohn Marino tls_host_port(const char *hostport, char **host, char **port)
75f5b1c8a1SJohn Marino {
76f5b1c8a1SJohn Marino char *h, *p, *s;
77f5b1c8a1SJohn Marino int rv = 1;
78f5b1c8a1SJohn Marino
79f5b1c8a1SJohn Marino *host = NULL;
80f5b1c8a1SJohn Marino *port = NULL;
81f5b1c8a1SJohn Marino
82f5b1c8a1SJohn Marino if ((s = strdup(hostport)) == NULL)
8372c33676SMaxim Ag goto err;
84f5b1c8a1SJohn Marino
85f5b1c8a1SJohn Marino h = p = s;
86f5b1c8a1SJohn Marino
87f5b1c8a1SJohn Marino /* See if this is an IPv6 literal with square braces. */
88f5b1c8a1SJohn Marino if (p[0] == '[') {
89f5b1c8a1SJohn Marino h++;
90f5b1c8a1SJohn Marino if ((p = strchr(s, ']')) == NULL)
91f5b1c8a1SJohn Marino goto done;
92f5b1c8a1SJohn Marino *p++ = '\0';
93f5b1c8a1SJohn Marino }
94f5b1c8a1SJohn Marino
95*de0e0e4dSAntonio Huete Jimenez /* Find the port separator. */
96f5b1c8a1SJohn Marino if ((p = strchr(p, ':')) == NULL)
97f5b1c8a1SJohn Marino goto done;
98f5b1c8a1SJohn Marino
99f5b1c8a1SJohn Marino /* If there is another separator then we have issues. */
100f5b1c8a1SJohn Marino if (strchr(p + 1, ':') != NULL)
101f5b1c8a1SJohn Marino goto done;
102f5b1c8a1SJohn Marino
103f5b1c8a1SJohn Marino *p++ = '\0';
104f5b1c8a1SJohn Marino
105cca6fc52SDaniel Fojt if (asprintf(host, "%s", h) == -1) {
106cca6fc52SDaniel Fojt *host = NULL;
10772c33676SMaxim Ag goto err;
108cca6fc52SDaniel Fojt }
109cca6fc52SDaniel Fojt if (asprintf(port, "%s", p) == -1) {
110cca6fc52SDaniel Fojt *port = NULL;
11172c33676SMaxim Ag goto err;
112cca6fc52SDaniel Fojt }
113f5b1c8a1SJohn Marino
114f5b1c8a1SJohn Marino rv = 0;
115f5b1c8a1SJohn Marino goto done;
116f5b1c8a1SJohn Marino
11772c33676SMaxim Ag err:
118f5b1c8a1SJohn Marino free(*host);
119f5b1c8a1SJohn Marino *host = NULL;
120f5b1c8a1SJohn Marino free(*port);
121f5b1c8a1SJohn Marino *port = NULL;
122f5b1c8a1SJohn Marino rv = -1;
123f5b1c8a1SJohn Marino
124f5b1c8a1SJohn Marino done:
125f5b1c8a1SJohn Marino free(s);
126f5b1c8a1SJohn Marino
127f5b1c8a1SJohn Marino return (rv);
128f5b1c8a1SJohn Marino }
129f5b1c8a1SJohn Marino
13072c33676SMaxim Ag int
tls_password_cb(char * buf,int size,int rwflag,void * u)131f5b1c8a1SJohn Marino tls_password_cb(char *buf, int size, int rwflag, void *u)
132f5b1c8a1SJohn Marino {
133f5b1c8a1SJohn Marino size_t len;
13472c33676SMaxim Ag
13572c33676SMaxim Ag if (size < 0)
13672c33676SMaxim Ag return (0);
13772c33676SMaxim Ag
138f5b1c8a1SJohn Marino if (u == NULL) {
139f5b1c8a1SJohn Marino memset(buf, 0, size);
140f5b1c8a1SJohn Marino return (0);
141f5b1c8a1SJohn Marino }
14272c33676SMaxim Ag
143f5b1c8a1SJohn Marino if ((len = strlcpy(buf, u, size)) >= (size_t)size)
144f5b1c8a1SJohn Marino return (0);
14572c33676SMaxim Ag
146f5b1c8a1SJohn Marino return (len);
147f5b1c8a1SJohn Marino }
148f5b1c8a1SJohn Marino
149f5b1c8a1SJohn Marino uint8_t *
tls_load_file(const char * name,size_t * len,char * password)150f5b1c8a1SJohn Marino tls_load_file(const char *name, size_t *len, char *password)
151f5b1c8a1SJohn Marino {
152f5b1c8a1SJohn Marino FILE *fp;
153f5b1c8a1SJohn Marino EVP_PKEY *key = NULL;
154f5b1c8a1SJohn Marino BIO *bio = NULL;
15572c33676SMaxim Ag char *data;
15672c33676SMaxim Ag uint8_t *buf = NULL;
157f5b1c8a1SJohn Marino struct stat st;
15872c33676SMaxim Ag size_t size = 0;
159f5b1c8a1SJohn Marino int fd = -1;
16072c33676SMaxim Ag ssize_t n;
161f5b1c8a1SJohn Marino
162f5b1c8a1SJohn Marino *len = 0;
163f5b1c8a1SJohn Marino
164f5b1c8a1SJohn Marino if ((fd = open(name, O_RDONLY)) == -1)
165f5b1c8a1SJohn Marino return (NULL);
166f5b1c8a1SJohn Marino
167f5b1c8a1SJohn Marino /* Just load the file into memory without decryption */
168f5b1c8a1SJohn Marino if (password == NULL) {
169f5b1c8a1SJohn Marino if (fstat(fd, &st) != 0)
17072c33676SMaxim Ag goto err;
17172c33676SMaxim Ag if (st.st_size < 0)
17272c33676SMaxim Ag goto err;
173f5b1c8a1SJohn Marino size = (size_t)st.st_size;
17472c33676SMaxim Ag if ((buf = malloc(size)) == NULL)
17572c33676SMaxim Ag goto err;
17672c33676SMaxim Ag n = read(fd, buf, size);
17772c33676SMaxim Ag if (n < 0 || (size_t)n != size)
17872c33676SMaxim Ag goto err;
179f5b1c8a1SJohn Marino close(fd);
180f5b1c8a1SJohn Marino goto done;
181f5b1c8a1SJohn Marino }
182f5b1c8a1SJohn Marino
183f5b1c8a1SJohn Marino /* Or read the (possibly) encrypted key from file */
184f5b1c8a1SJohn Marino if ((fp = fdopen(fd, "r")) == NULL)
18572c33676SMaxim Ag goto err;
186f5b1c8a1SJohn Marino fd = -1;
187f5b1c8a1SJohn Marino
188f5b1c8a1SJohn Marino key = PEM_read_PrivateKey(fp, NULL, tls_password_cb, password);
189f5b1c8a1SJohn Marino fclose(fp);
190f5b1c8a1SJohn Marino if (key == NULL)
19172c33676SMaxim Ag goto err;
192f5b1c8a1SJohn Marino
193f5b1c8a1SJohn Marino /* Write unencrypted key to memory buffer */
194f5b1c8a1SJohn Marino if ((bio = BIO_new(BIO_s_mem())) == NULL)
19572c33676SMaxim Ag goto err;
196f5b1c8a1SJohn Marino if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL))
19772c33676SMaxim Ag goto err;
198f5b1c8a1SJohn Marino if ((size = BIO_get_mem_data(bio, &data)) <= 0)
19972c33676SMaxim Ag goto err;
20072c33676SMaxim Ag if ((buf = malloc(size)) == NULL)
20172c33676SMaxim Ag goto err;
202f5b1c8a1SJohn Marino memcpy(buf, data, size);
203f5b1c8a1SJohn Marino
204f5b1c8a1SJohn Marino BIO_free_all(bio);
205f5b1c8a1SJohn Marino EVP_PKEY_free(key);
206f5b1c8a1SJohn Marino
207f5b1c8a1SJohn Marino done:
208f5b1c8a1SJohn Marino *len = size;
209f5b1c8a1SJohn Marino return (buf);
210f5b1c8a1SJohn Marino
21172c33676SMaxim Ag err:
212f5b1c8a1SJohn Marino if (fd != -1)
213f5b1c8a1SJohn Marino close(fd);
21472c33676SMaxim Ag freezero(buf, size);
215f5b1c8a1SJohn Marino BIO_free_all(bio);
216f5b1c8a1SJohn Marino EVP_PKEY_free(key);
217f5b1c8a1SJohn Marino
218f5b1c8a1SJohn Marino return (NULL);
219f5b1c8a1SJohn Marino }
22072c33676SMaxim Ag
22172c33676SMaxim Ag void
tls_unload_file(uint8_t * buf,size_t len)22272c33676SMaxim Ag tls_unload_file(uint8_t *buf, size_t len)
22372c33676SMaxim Ag {
22472c33676SMaxim Ag freezero(buf, len);
22572c33676SMaxim Ag }
226