xref: /openbsd/usr.sbin/relayd/ssl.c (revision 4958adbc)
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 
33 int	ssl_password_cb(char *, int, int, void *);
34 
35 int
ssl_password_cb(char * buf,int size,int rwflag,void * u)36 ssl_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 
48 char *
ssl_load_key(struct relayd * env,const char * name,off_t * len,char * pass)49 ssl_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 
96 uint8_t *
ssl_update_certificate(const uint8_t * oldcert,size_t oldlen,EVP_PKEY * pkey,EVP_PKEY * capkey,X509 * cacert,size_t * newlen)97 ssl_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 
166 done:
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 
176 int
ssl_load_pkey(char * buf,off_t len,X509 ** x509ptr,EVP_PKEY ** pkeyptr)177 ssl_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