1/*	$OpenBSD: ssl.c,v 1.37 2023/06/25 08:07:39 op 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
31#include "relayd.h"
32
33int	ssl_password_cb(char *, int, int, void *);
34
35int
36ssl_password_cb(char *buf, int size, int rwflag, void *u)
37{
38	size_t	len;
39	if (u == NULL) {
40		bzero(buf, size);
41		return (0);
42	}
43	if ((len = strlcpy(buf, u, size)) >= (size_t)size)
44		return (0);
45	return (len);
46}
47
48char *
49ssl_load_key(struct relayd *env, const char *name, off_t *len, char *pass)
50{
51	FILE		*fp;
52	EVP_PKEY	*key = NULL;
53	BIO		*bio = NULL;
54	long		 size;
55	char		*data, *buf = NULL;
56
57	/*
58	 * Read (possibly) encrypted key from file
59	 */
60	if ((fp = fopen(name, "r")) == NULL)
61		return (NULL);
62
63	key = PEM_read_PrivateKey(fp, NULL, ssl_password_cb, pass);
64	fclose(fp);
65	if (key == NULL)
66		goto fail;
67
68	/*
69	 * Write unencrypted key to memory buffer
70	 */
71	if ((bio = BIO_new(BIO_s_mem())) == NULL)
72		goto fail;
73	if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL))
74		goto fail;
75	if ((size = BIO_get_mem_data(bio, &data)) <= 0)
76		goto fail;
77	if ((buf = calloc(1, size)) == NULL)
78		goto fail;
79	memcpy(buf, data, size);
80
81	BIO_free_all(bio);
82	EVP_PKEY_free(key);
83
84	*len = (off_t)size;
85	return (buf);
86
87 fail:
88	free(buf);
89	if (bio != NULL)
90		BIO_free_all(bio);
91	if (key != NULL)
92		EVP_PKEY_free(key);
93	return (NULL);
94}
95
96uint8_t *
97ssl_update_certificate(const uint8_t *oldcert, size_t oldlen, EVP_PKEY *pkey,
98    EVP_PKEY *capkey, X509 *cacert, size_t *newlen)
99{
100	char		 name[2][TLS_NAME_SIZE];
101	BIO		*in, *out = NULL;
102	BUF_MEM		*bptr = NULL;
103	X509		*cert = NULL;
104	uint8_t		*newcert = NULL;
105
106	if ((in = BIO_new_mem_buf(oldcert, oldlen)) == NULL) {
107		log_warnx("%s: BIO_new_mem_buf failed", __func__);
108		goto done;
109	}
110
111	if ((cert = PEM_read_bio_X509(in, NULL,
112	    ssl_password_cb, NULL)) == NULL) {
113		log_warnx("%s: PEM_read_bio_X509 failed", __func__);
114		goto done;
115	}
116
117	BIO_free(in);
118	in = NULL;
119
120	name[0][0] = name[1][0] = '\0';
121	if (!X509_NAME_oneline(X509_get_subject_name(cert),
122	    name[0], sizeof(name[0])) ||
123	    !X509_NAME_oneline(X509_get_issuer_name(cert),
124	    name[1], sizeof(name[1])))
125		goto done;
126
127	if ((cert = X509_dup(cert)) == NULL)
128		goto done;
129
130	/* Update certificate key and use our CA as the issuer */
131	X509_set_pubkey(cert, pkey);
132	X509_set_issuer_name(cert, X509_get_subject_name(cacert));
133
134	/* Sign with our CA */
135	if (!X509_sign(cert, capkey, EVP_sha256())) {
136		log_warnx("%s: X509_sign failed", __func__);
137		goto done;
138	}
139
140#if DEBUG_CERT
141	log_debug("%s: subject %s", __func__, name[0]);
142	log_debug("%s: issuer %s", __func__, name[1]);
143#if DEBUG > 2
144	X509_print_fp(stdout, cert);
145#endif
146#endif
147
148	/* write cert as PEM file */
149	out = BIO_new(BIO_s_mem());
150	if (out == NULL) {
151		log_warnx("%s: BIO_new failed", __func__);
152		goto done;
153	}
154	if (!PEM_write_bio_X509(out, cert)) {
155		log_warnx("%s: PEM_write_bio_X509 failed", __func__);
156		goto done;
157	}
158	BIO_get_mem_ptr(out, &bptr);
159	if ((newcert = malloc(bptr->length)) == NULL) {
160		log_warn("%s: malloc", __func__);
161		goto done;
162	}
163	memcpy(newcert, bptr->data, bptr->length);
164	*newlen = bptr->length;
165
166done:
167	if (in)
168		BIO_free(in);
169	if (out)
170		BIO_free(out);
171	if (cert)
172		X509_free(cert);
173	return (newcert);
174}
175
176int
177ssl_load_pkey(char *buf, off_t len, X509 **x509ptr, EVP_PKEY **pkeyptr)
178{
179	BIO		*in;
180	X509		*x509 = NULL;
181	EVP_PKEY	*pkey = NULL;
182	RSA		*rsa = NULL;
183	char		*hash = NULL;
184
185	if ((in = BIO_new_mem_buf(buf, len)) == NULL) {
186		log_warnx("%s: BIO_new_mem_buf failed", __func__);
187		return (0);
188	}
189	if ((x509 = PEM_read_bio_X509(in, NULL,
190	    ssl_password_cb, NULL)) == NULL) {
191		log_warnx("%s: PEM_read_bio_X509 failed", __func__);
192		goto fail;
193	}
194	if ((pkey = X509_get_pubkey(x509)) == NULL) {
195		log_warnx("%s: X509_get_pubkey failed", __func__);
196		goto fail;
197	}
198	if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) {
199		log_warnx("%s: failed to extract RSA", __func__);
200		goto fail;
201	}
202	if ((hash = malloc(TLS_CERT_HASH_SIZE)) == NULL) {
203		log_warn("%s: allocate hash failed", __func__);
204		goto fail;
205	}
206	hash_x509(x509, hash, TLS_CERT_HASH_SIZE);
207	if (RSA_set_ex_data(rsa, 0, hash) != 1) {
208		log_warnx("%s: failed to set hash as exdata", __func__);
209		goto fail;
210	}
211
212	RSA_free(rsa); /* dereference, will be cleaned up with pkey */
213	*pkeyptr = pkey;
214	if (x509ptr != NULL)
215		*x509ptr = x509;
216	else
217		X509_free(x509);
218	BIO_free(in);
219
220	return (1);
221
222 fail:
223	free(hash);
224	if (rsa != NULL)
225		RSA_free(rsa);
226	if (pkey != NULL)
227		EVP_PKEY_free(pkey);
228	if (x509 != NULL)
229		X509_free(x509);
230	BIO_free(in);
231
232	return (0);
233}
234