xref: /openbsd-src/usr.sbin/relayd/ssl.c (revision 24bb5fcea3ed904bc467217bdaadb5dfc618d5bf)
1 /*	$OpenBSD: ssl.c,v 1.35 2021/01/27 20:33:05 eric Exp $	*/
2 
3 /*
4  * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
5  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@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/types.h>
21 #include <sys/queue.h>
22 #include <sys/uio.h>
23 
24 #include <unistd.h>
25 #include <string.h>
26 #include <imsg.h>
27 
28 #include <openssl/ssl.h>
29 #include <openssl/err.h>
30 #include <openssl/engine.h>
31 
32 #include "relayd.h"
33 
34 int	ssl_password_cb(char *, int, int, void *);
35 
36 void
37 ssl_init(struct relayd *env)
38 {
39 	static int	 initialized = 0;
40 
41 	if (initialized)
42 		return;
43 
44 	SSL_library_init();
45 	SSL_load_error_strings();
46 
47 	/* Init hardware crypto engines. */
48 	ENGINE_load_builtin_engines();
49 	ENGINE_register_all_complete();
50 
51 	initialized = 1;
52 }
53 
54 int
55 ssl_password_cb(char *buf, int size, int rwflag, void *u)
56 {
57 	size_t	len;
58 	if (u == NULL) {
59 		bzero(buf, size);
60 		return (0);
61 	}
62 	if ((len = strlcpy(buf, u, size)) >= (size_t)size)
63 		return (0);
64 	return (len);
65 }
66 
67 char *
68 ssl_load_key(struct relayd *env, const char *name, off_t *len, char *pass)
69 {
70 	FILE		*fp;
71 	EVP_PKEY	*key = NULL;
72 	BIO		*bio = NULL;
73 	long		 size;
74 	char		*data, *buf = NULL;
75 
76 	/* Initialize SSL library once */
77 	ssl_init(env);
78 
79 	/*
80 	 * Read (possibly) encrypted key from file
81 	 */
82 	if ((fp = fopen(name, "r")) == NULL)
83 		return (NULL);
84 
85 	key = PEM_read_PrivateKey(fp, NULL, ssl_password_cb, pass);
86 	fclose(fp);
87 	if (key == NULL)
88 		goto fail;
89 
90 	/*
91 	 * Write unencrypted key to memory buffer
92 	 */
93 	if ((bio = BIO_new(BIO_s_mem())) == NULL)
94 		goto fail;
95 	if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL))
96 		goto fail;
97 	if ((size = BIO_get_mem_data(bio, &data)) <= 0)
98 		goto fail;
99 	if ((buf = calloc(1, size)) == NULL)
100 		goto fail;
101 	memcpy(buf, data, size);
102 
103 	BIO_free_all(bio);
104 	EVP_PKEY_free(key);
105 
106 	*len = (off_t)size;
107 	return (buf);
108 
109  fail:
110 	free(buf);
111 	if (bio != NULL)
112 		BIO_free_all(bio);
113 	if (key != NULL)
114 		EVP_PKEY_free(key);
115 	return (NULL);
116 }
117 
118 uint8_t *
119 ssl_update_certificate(const uint8_t *oldcert, size_t oldlen, EVP_PKEY *pkey,
120     EVP_PKEY *capkey, X509 *cacert, size_t *newlen)
121 {
122 	char		 name[2][TLS_NAME_SIZE];
123 	BIO		*in, *out = NULL;
124 	BUF_MEM		*bptr = NULL;
125 	X509		*cert = NULL;
126 	uint8_t		*newcert = NULL, *foo = NULL;
127 
128 	/* XXX BIO_new_mem_buf is not using const so work around this */
129 	if ((foo = malloc(oldlen)) == NULL) {
130 		log_warn("%s: malloc", __func__);
131 		return (NULL);
132 	}
133 	memcpy(foo, oldcert, oldlen);
134 
135 	if ((in = BIO_new_mem_buf(foo, oldlen)) == NULL) {
136 		log_warnx("%s: BIO_new_mem_buf failed", __func__);
137 		goto done;
138 	}
139 
140 	if ((cert = PEM_read_bio_X509(in, NULL,
141 	    ssl_password_cb, NULL)) == NULL) {
142 		log_warnx("%s: PEM_read_bio_X509 failed", __func__);
143 		goto done;
144 	}
145 
146 	BIO_free(in);
147 	in = NULL;
148 
149 	name[0][0] = name[1][0] = '\0';
150 	if (!X509_NAME_oneline(X509_get_subject_name(cert),
151 	    name[0], sizeof(name[0])) ||
152 	    !X509_NAME_oneline(X509_get_issuer_name(cert),
153 	    name[1], sizeof(name[1])))
154 		goto done;
155 
156 	if ((cert = X509_dup(cert)) == NULL)
157 		goto done;
158 
159 	/* Update certificate key and use our CA as the issuer */
160 	X509_set_pubkey(cert, pkey);
161 	X509_set_issuer_name(cert, X509_get_subject_name(cacert));
162 
163 	/* Sign with our CA */
164 	if (!X509_sign(cert, capkey, EVP_sha256())) {
165 		log_warnx("%s: X509_sign failed", __func__);
166 		goto done;
167 	}
168 
169 #if DEBUG_CERT
170 	log_debug("%s: subject %s", __func__, name[0]);
171 	log_debug("%s: issuer %s", __func__, name[1]);
172 #if DEBUG > 2
173 	X509_print_fp(stdout, cert);
174 #endif
175 #endif
176 
177 	/* write cert as PEM file */
178 	out = BIO_new(BIO_s_mem());
179 	if (out == NULL) {
180 		log_warnx("%s: BIO_new failed", __func__);
181 		goto done;
182 	}
183 	if (!PEM_write_bio_X509(out, cert)) {
184 		log_warnx("%s: PEM_write_bio_X509 failed", __func__);
185 		goto done;
186 	}
187 	BIO_get_mem_ptr(out, &bptr);
188 	if ((newcert = malloc(bptr->length)) == NULL) {
189 		log_warn("%s: malloc", __func__);
190 		goto done;
191 	}
192 	memcpy(newcert, bptr->data, bptr->length);
193 	*newlen = bptr->length;
194 
195 done:
196 	free(foo);
197 	if (in)
198 		BIO_free(in);
199 	if (out)
200 		BIO_free(out);
201 	if (cert)
202 		X509_free(cert);
203 	return (newcert);
204 }
205 
206 int
207 ssl_load_pkey(char *buf, off_t len, X509 **x509ptr, EVP_PKEY **pkeyptr)
208 {
209 	BIO		*in;
210 	X509		*x509 = NULL;
211 	EVP_PKEY	*pkey = NULL;
212 	RSA		*rsa = NULL;
213 	char		*hash = NULL;
214 
215 	if ((in = BIO_new_mem_buf(buf, len)) == NULL) {
216 		log_warnx("%s: BIO_new_mem_buf failed", __func__);
217 		return (0);
218 	}
219 	if ((x509 = PEM_read_bio_X509(in, NULL,
220 	    ssl_password_cb, NULL)) == NULL) {
221 		log_warnx("%s: PEM_read_bio_X509 failed", __func__);
222 		goto fail;
223 	}
224 	if ((pkey = X509_get_pubkey(x509)) == NULL) {
225 		log_warnx("%s: X509_get_pubkey failed", __func__);
226 		goto fail;
227 	}
228 	if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) {
229 		log_warnx("%s: failed to extract RSA", __func__);
230 		goto fail;
231 	}
232 	if ((hash = malloc(TLS_CERT_HASH_SIZE)) == NULL) {
233 		log_warn("%s: allocate hash failed", __func__);
234 		goto fail;
235 	}
236 	hash_x509(x509, hash, TLS_CERT_HASH_SIZE);
237 	if (RSA_set_ex_data(rsa, 0, hash) != 1) {
238 		log_warnx("%s: failed to set hash as exdata", __func__);
239 		goto fail;
240 	}
241 
242 	RSA_free(rsa); /* dereference, will be cleaned up with pkey */
243 	*pkeyptr = pkey;
244 	if (x509ptr != NULL)
245 		*x509ptr = x509;
246 	else
247 		X509_free(x509);
248 	BIO_free(in);
249 
250 	return (1);
251 
252  fail:
253 	free(hash);
254 	if (rsa != NULL)
255 		RSA_free(rsa);
256 	if (pkey != NULL)
257 		EVP_PKEY_free(pkey);
258 	if (x509 != NULL)
259 		X509_free(x509);
260 	BIO_free(in);
261 
262 	return (0);
263 }
264