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