xref: /openbsd-src/usr.sbin/relayd/ssl.c (revision 8550894424f8a4aa4aafb6cd57229dd6ed7cd9dd)
1 /*	$OpenBSD: ssl.c,v 1.36 2021/12/08 19:25:04 tb 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;
127 
128 	if ((in = BIO_new_mem_buf(oldcert, oldlen)) == NULL) {
129 		log_warnx("%s: BIO_new_mem_buf failed", __func__);
130 		goto done;
131 	}
132 
133 	if ((cert = PEM_read_bio_X509(in, NULL,
134 	    ssl_password_cb, NULL)) == NULL) {
135 		log_warnx("%s: PEM_read_bio_X509 failed", __func__);
136 		goto done;
137 	}
138 
139 	BIO_free(in);
140 	in = NULL;
141 
142 	name[0][0] = name[1][0] = '\0';
143 	if (!X509_NAME_oneline(X509_get_subject_name(cert),
144 	    name[0], sizeof(name[0])) ||
145 	    !X509_NAME_oneline(X509_get_issuer_name(cert),
146 	    name[1], sizeof(name[1])))
147 		goto done;
148 
149 	if ((cert = X509_dup(cert)) == NULL)
150 		goto done;
151 
152 	/* Update certificate key and use our CA as the issuer */
153 	X509_set_pubkey(cert, pkey);
154 	X509_set_issuer_name(cert, X509_get_subject_name(cacert));
155 
156 	/* Sign with our CA */
157 	if (!X509_sign(cert, capkey, EVP_sha256())) {
158 		log_warnx("%s: X509_sign failed", __func__);
159 		goto done;
160 	}
161 
162 #if DEBUG_CERT
163 	log_debug("%s: subject %s", __func__, name[0]);
164 	log_debug("%s: issuer %s", __func__, name[1]);
165 #if DEBUG > 2
166 	X509_print_fp(stdout, cert);
167 #endif
168 #endif
169 
170 	/* write cert as PEM file */
171 	out = BIO_new(BIO_s_mem());
172 	if (out == NULL) {
173 		log_warnx("%s: BIO_new failed", __func__);
174 		goto done;
175 	}
176 	if (!PEM_write_bio_X509(out, cert)) {
177 		log_warnx("%s: PEM_write_bio_X509 failed", __func__);
178 		goto done;
179 	}
180 	BIO_get_mem_ptr(out, &bptr);
181 	if ((newcert = malloc(bptr->length)) == NULL) {
182 		log_warn("%s: malloc", __func__);
183 		goto done;
184 	}
185 	memcpy(newcert, bptr->data, bptr->length);
186 	*newlen = bptr->length;
187 
188 done:
189 	if (in)
190 		BIO_free(in);
191 	if (out)
192 		BIO_free(out);
193 	if (cert)
194 		X509_free(cert);
195 	return (newcert);
196 }
197 
198 int
199 ssl_load_pkey(char *buf, off_t len, X509 **x509ptr, EVP_PKEY **pkeyptr)
200 {
201 	BIO		*in;
202 	X509		*x509 = NULL;
203 	EVP_PKEY	*pkey = NULL;
204 	RSA		*rsa = NULL;
205 	char		*hash = NULL;
206 
207 	if ((in = BIO_new_mem_buf(buf, len)) == NULL) {
208 		log_warnx("%s: BIO_new_mem_buf failed", __func__);
209 		return (0);
210 	}
211 	if ((x509 = PEM_read_bio_X509(in, NULL,
212 	    ssl_password_cb, NULL)) == NULL) {
213 		log_warnx("%s: PEM_read_bio_X509 failed", __func__);
214 		goto fail;
215 	}
216 	if ((pkey = X509_get_pubkey(x509)) == NULL) {
217 		log_warnx("%s: X509_get_pubkey failed", __func__);
218 		goto fail;
219 	}
220 	if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) {
221 		log_warnx("%s: failed to extract RSA", __func__);
222 		goto fail;
223 	}
224 	if ((hash = malloc(TLS_CERT_HASH_SIZE)) == NULL) {
225 		log_warn("%s: allocate hash failed", __func__);
226 		goto fail;
227 	}
228 	hash_x509(x509, hash, TLS_CERT_HASH_SIZE);
229 	if (RSA_set_ex_data(rsa, 0, hash) != 1) {
230 		log_warnx("%s: failed to set hash as exdata", __func__);
231 		goto fail;
232 	}
233 
234 	RSA_free(rsa); /* dereference, will be cleaned up with pkey */
235 	*pkeyptr = pkey;
236 	if (x509ptr != NULL)
237 		*x509ptr = x509;
238 	else
239 		X509_free(x509);
240 	BIO_free(in);
241 
242 	return (1);
243 
244  fail:
245 	free(hash);
246 	if (rsa != NULL)
247 		RSA_free(rsa);
248 	if (pkey != NULL)
249 		EVP_PKEY_free(pkey);
250 	if (x509 != NULL)
251 		X509_free(x509);
252 	BIO_free(in);
253 
254 	return (0);
255 }
256