xref: /openbsd-src/lib/libtls/tls_util.c (revision e6d77be9938e1d9838cdb3161f248f5ed49909a7)
1*e6d77be9Sop /* $OpenBSD: tls_util.c,v 1.16 2023/05/14 07:26:25 op Exp $ */
2b600beedSjsing /*
3b600beedSjsing  * Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
415ba8e50Sjsing  * Copyright (c) 2014 Ted Unangst <tedu@openbsd.org>
5cbcdaa48Sreyk  * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
6b600beedSjsing  *
7b600beedSjsing  * Permission to use, copy, modify, and distribute this software for any
8b600beedSjsing  * purpose with or without fee is hereby granted, provided that the above
9b600beedSjsing  * copyright notice and this permission notice appear in all copies.
10b600beedSjsing  *
11b600beedSjsing  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12b600beedSjsing  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13b600beedSjsing  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14b600beedSjsing  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15b600beedSjsing  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16b600beedSjsing  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17b600beedSjsing  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18b600beedSjsing  */
19b600beedSjsing 
20cbcdaa48Sreyk #include <sys/stat.h>
21b600beedSjsing 
22cbcdaa48Sreyk #include <stdlib.h>
23*e6d77be9Sop #include <string.h>
24cbcdaa48Sreyk #include <unistd.h>
25cbcdaa48Sreyk #include <fcntl.h>
26cbcdaa48Sreyk 
27cbcdaa48Sreyk #include "tls.h"
28b600beedSjsing #include "tls_internal.h"
29b600beedSjsing 
30bb4cb1b0Sjsing static void *
memdup(const void * in,size_t len)31bb4cb1b0Sjsing memdup(const void *in, size_t len)
32bb4cb1b0Sjsing {
33bb4cb1b0Sjsing 	void *out;
34bb4cb1b0Sjsing 
35bb4cb1b0Sjsing 	if ((out = malloc(len)) == NULL)
36bb4cb1b0Sjsing 		return NULL;
37bb4cb1b0Sjsing 	memcpy(out, in, len);
38bb4cb1b0Sjsing 	return out;
39bb4cb1b0Sjsing }
40bb4cb1b0Sjsing 
41bb4cb1b0Sjsing int
tls_set_mem(char ** dest,size_t * destlen,const void * src,size_t srclen)42bb4cb1b0Sjsing tls_set_mem(char **dest, size_t *destlen, const void *src, size_t srclen)
43bb4cb1b0Sjsing {
44bb4cb1b0Sjsing 	free(*dest);
45bb4cb1b0Sjsing 	*dest = NULL;
46bb4cb1b0Sjsing 	*destlen = 0;
47a2b2c537Sjsing 	if (src != NULL) {
48bb4cb1b0Sjsing 		if ((*dest = memdup(src, srclen)) == NULL)
49bb4cb1b0Sjsing 			return -1;
50bb4cb1b0Sjsing 		*destlen = srclen;
51a2b2c537Sjsing 	}
52bb4cb1b0Sjsing 	return 0;
53bb4cb1b0Sjsing }
54bb4cb1b0Sjsing 
55bb4cb1b0Sjsing int
tls_set_string(const char ** dest,const char * src)56bb4cb1b0Sjsing tls_set_string(const char **dest, const char *src)
57bb4cb1b0Sjsing {
58bb4cb1b0Sjsing 	free((char *)*dest);
59bb4cb1b0Sjsing 	*dest = NULL;
60bb4cb1b0Sjsing 	if (src != NULL)
61bb4cb1b0Sjsing 		if ((*dest = strdup(src)) == NULL)
62bb4cb1b0Sjsing 			return -1;
63bb4cb1b0Sjsing 	return 0;
64bb4cb1b0Sjsing }
65bb4cb1b0Sjsing 
66b600beedSjsing /*
67b600beedSjsing  * Extract the host and port from a colon separated value. For a literal IPv6
68b600beedSjsing  * address the address must be contained with square braces. If a host and
69b600beedSjsing  * port are successfully extracted, the function will return 0 and the
70b600beedSjsing  * caller is responsible for freeing the host and port. If no port is found
71b600beedSjsing  * then the function will return 1, with both host and port being NULL.
72b600beedSjsing  * On memory allocation failure -1 will be returned.
73b600beedSjsing  */
74b600beedSjsing int
tls_host_port(const char * hostport,char ** host,char ** port)75b600beedSjsing tls_host_port(const char *hostport, char **host, char **port)
76b600beedSjsing {
77b600beedSjsing 	char *h, *p, *s;
78b600beedSjsing 	int rv = 1;
79b600beedSjsing 
80b600beedSjsing 	*host = NULL;
81b600beedSjsing 	*port = NULL;
82b600beedSjsing 
83b600beedSjsing 	if ((s = strdup(hostport)) == NULL)
847add217bSjsing 		goto err;
85b600beedSjsing 
86b600beedSjsing 	h = p = s;
87b600beedSjsing 
88b600beedSjsing 	/* See if this is an IPv6 literal with square braces. */
89b600beedSjsing 	if (p[0] == '[') {
90b600beedSjsing 		h++;
91b600beedSjsing 		if ((p = strchr(s, ']')) == NULL)
92b600beedSjsing 			goto done;
93b600beedSjsing 		*p++ = '\0';
94b600beedSjsing 	}
95b600beedSjsing 
96718d8d98Stb 	/* Find the port separator. */
97b600beedSjsing 	if ((p = strchr(p, ':')) == NULL)
98b600beedSjsing 		goto done;
99b600beedSjsing 
100b600beedSjsing 	/* If there is another separator then we have issues. */
101b600beedSjsing 	if (strchr(p + 1, ':') != NULL)
102b600beedSjsing 		goto done;
103b600beedSjsing 
104b600beedSjsing 	*p++ = '\0';
105b600beedSjsing 
1060f235647Stb 	if (asprintf(host, "%s", h) == -1) {
1070f235647Stb 		*host = NULL;
1087add217bSjsing 		goto err;
1090f235647Stb 	}
1100f235647Stb 	if (asprintf(port, "%s", p) == -1) {
1110f235647Stb 		*port = NULL;
1127add217bSjsing 		goto err;
1130f235647Stb 	}
114b600beedSjsing 
115b600beedSjsing 	rv = 0;
116b600beedSjsing 	goto done;
117b600beedSjsing 
1187add217bSjsing  err:
119b600beedSjsing 	free(*host);
120b600beedSjsing 	*host = NULL;
121b600beedSjsing 	free(*port);
122b600beedSjsing 	*port = NULL;
123b600beedSjsing 	rv = -1;
124b600beedSjsing 
125b600beedSjsing  done:
126b600beedSjsing 	free(s);
127b600beedSjsing 
128b600beedSjsing 	return (rv);
129b600beedSjsing }
130cbcdaa48Sreyk 
131a192468aSjsing int
tls_password_cb(char * buf,int size,int rwflag,void * u)132cbcdaa48Sreyk tls_password_cb(char *buf, int size, int rwflag, void *u)
133cbcdaa48Sreyk {
134cbcdaa48Sreyk 	size_t len;
135dad7bfe2Sjsing 
136dad7bfe2Sjsing 	if (size < 0)
137dad7bfe2Sjsing 		return (0);
138dad7bfe2Sjsing 
139cbcdaa48Sreyk 	if (u == NULL) {
140cbcdaa48Sreyk 		memset(buf, 0, size);
141cbcdaa48Sreyk 		return (0);
142cbcdaa48Sreyk 	}
143dad7bfe2Sjsing 
144cbcdaa48Sreyk 	if ((len = strlcpy(buf, u, size)) >= (size_t)size)
145cbcdaa48Sreyk 		return (0);
146dad7bfe2Sjsing 
147cbcdaa48Sreyk 	return (len);
148cbcdaa48Sreyk }
149cbcdaa48Sreyk 
150cbcdaa48Sreyk uint8_t *
tls_load_file(const char * name,size_t * len,char * password)151cbcdaa48Sreyk tls_load_file(const char *name, size_t *len, char *password)
152cbcdaa48Sreyk {
153cbcdaa48Sreyk 	FILE *fp;
154cbcdaa48Sreyk 	EVP_PKEY *key = NULL;
155cbcdaa48Sreyk 	BIO *bio = NULL;
156b97593a4Sbcook 	char *data;
157b97593a4Sbcook 	uint8_t *buf = NULL;
158cbcdaa48Sreyk 	struct stat st;
15954356a5dSjsing 	size_t size = 0;
160cbcdaa48Sreyk 	int fd = -1;
161dad7bfe2Sjsing 	ssize_t n;
162cbcdaa48Sreyk 
163cbcdaa48Sreyk 	*len = 0;
164cbcdaa48Sreyk 
165cbcdaa48Sreyk 	if ((fd = open(name, O_RDONLY)) == -1)
166cbcdaa48Sreyk 		return (NULL);
167cbcdaa48Sreyk 
168cbcdaa48Sreyk 	/* Just load the file into memory without decryption */
169cbcdaa48Sreyk 	if (password == NULL) {
170cbcdaa48Sreyk 		if (fstat(fd, &st) != 0)
1717add217bSjsing 			goto err;
172dad7bfe2Sjsing 		if (st.st_size < 0)
1737add217bSjsing 			goto err;
174dad7bfe2Sjsing 		size = (size_t)st.st_size;
175dad7bfe2Sjsing 		if ((buf = malloc(size)) == NULL)
1767add217bSjsing 			goto err;
177dad7bfe2Sjsing 		n = read(fd, buf, size);
178dad7bfe2Sjsing 		if (n < 0 || (size_t)n != size)
1797add217bSjsing 			goto err;
180cbcdaa48Sreyk 		close(fd);
181cbcdaa48Sreyk 		goto done;
182cbcdaa48Sreyk 	}
183cbcdaa48Sreyk 
184cbcdaa48Sreyk 	/* Or read the (possibly) encrypted key from file */
185cbcdaa48Sreyk 	if ((fp = fdopen(fd, "r")) == NULL)
1867add217bSjsing 		goto err;
187cbcdaa48Sreyk 	fd = -1;
188cbcdaa48Sreyk 
189cbcdaa48Sreyk 	key = PEM_read_PrivateKey(fp, NULL, tls_password_cb, password);
190cbcdaa48Sreyk 	fclose(fp);
191cbcdaa48Sreyk 	if (key == NULL)
1927add217bSjsing 		goto err;
193cbcdaa48Sreyk 
194cbcdaa48Sreyk 	/* Write unencrypted key to memory buffer */
195cbcdaa48Sreyk 	if ((bio = BIO_new(BIO_s_mem())) == NULL)
1967add217bSjsing 		goto err;
197cbcdaa48Sreyk 	if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL))
1987add217bSjsing 		goto err;
199cbcdaa48Sreyk 	if ((size = BIO_get_mem_data(bio, &data)) <= 0)
2007add217bSjsing 		goto err;
20154356a5dSjsing 	if ((buf = malloc(size)) == NULL)
2027add217bSjsing 		goto err;
203cbcdaa48Sreyk 	memcpy(buf, data, size);
204cbcdaa48Sreyk 
205cbcdaa48Sreyk 	BIO_free_all(bio);
206cbcdaa48Sreyk 	EVP_PKEY_free(key);
207cbcdaa48Sreyk 
208cbcdaa48Sreyk  done:
209cbcdaa48Sreyk 	*len = size;
210cbcdaa48Sreyk 	return (buf);
211cbcdaa48Sreyk 
2127add217bSjsing  err:
213cbcdaa48Sreyk 	if (fd != -1)
214cbcdaa48Sreyk 		close(fd);
21554356a5dSjsing 	freezero(buf, size);
216cbcdaa48Sreyk 	BIO_free_all(bio);
217cbcdaa48Sreyk 	EVP_PKEY_free(key);
218cbcdaa48Sreyk 
219cbcdaa48Sreyk 	return (NULL);
220cbcdaa48Sreyk }
221028ca023Sjsing 
222028ca023Sjsing void
tls_unload_file(uint8_t * buf,size_t len)223028ca023Sjsing tls_unload_file(uint8_t *buf, size_t len)
224028ca023Sjsing {
225028ca023Sjsing 	freezero(buf, len);
226028ca023Sjsing }
227