xref: /openbsd-src/usr.sbin/relayd/ssl.c (revision 4958adbcc07dab37012b2df3f33ffe005be875c7)
1*4958adbcSop /*	$OpenBSD: ssl.c,v 1.37 2023/06/25 08:07:39 op Exp $	*/
2e8fb3979Spyr 
3e8fb3979Spyr /*
43d77879fSreyk  * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
536f5dc5eSpyr  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
6e8fb3979Spyr  *
7e8fb3979Spyr  * Permission to use, copy, modify, and distribute this software for any
8e8fb3979Spyr  * purpose with or without fee is hereby granted, provided that the above
9e8fb3979Spyr  * copyright notice and this permission notice appear in all copies.
10e8fb3979Spyr  *
11e8fb3979Spyr  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12e8fb3979Spyr  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13e8fb3979Spyr  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14e8fb3979Spyr  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15e8fb3979Spyr  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16e8fb3979Spyr  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17e8fb3979Spyr  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18e8fb3979Spyr  */
19e8fb3979Spyr 
20f04ff968Sreyk #include <sys/types.h>
2185e5f500Sclaudio #include <sys/queue.h>
2285e5f500Sclaudio #include <sys/uio.h>
230ca734d7Sreyk 
24e8fb3979Spyr #include <unistd.h>
25e8fb3979Spyr #include <string.h>
26f04ff968Sreyk #include <imsg.h>
27e8fb3979Spyr 
28e8fb3979Spyr #include <openssl/ssl.h>
29e8fb3979Spyr #include <openssl/err.h>
30e8fb3979Spyr 
31748ceb64Sreyk #include "relayd.h"
32e8fb3979Spyr 
33cf39ad79Sreyk int	ssl_password_cb(char *, int, int, void *);
34e8fb3979Spyr 
35cf39ad79Sreyk int
ssl_password_cb(char * buf,int size,int rwflag,void * u)36cf39ad79Sreyk ssl_password_cb(char *buf, int size, int rwflag, void *u)
37cf39ad79Sreyk {
38cf39ad79Sreyk 	size_t	len;
39cf39ad79Sreyk 	if (u == NULL) {
40cf39ad79Sreyk 		bzero(buf, size);
41cf39ad79Sreyk 		return (0);
42cf39ad79Sreyk 	}
43cf39ad79Sreyk 	if ((len = strlcpy(buf, u, size)) >= (size_t)size)
44cf39ad79Sreyk 		return (0);
45cf39ad79Sreyk 	return (len);
46cf39ad79Sreyk }
47cf39ad79Sreyk 
48cf39ad79Sreyk char *
ssl_load_key(struct relayd * env,const char * name,off_t * len,char * pass)49cf39ad79Sreyk ssl_load_key(struct relayd *env, const char *name, off_t *len, char *pass)
50cf39ad79Sreyk {
51cf39ad79Sreyk 	FILE		*fp;
52cf39ad79Sreyk 	EVP_PKEY	*key = NULL;
53cf39ad79Sreyk 	BIO		*bio = NULL;
54cf39ad79Sreyk 	long		 size;
55cf39ad79Sreyk 	char		*data, *buf = NULL;
56cf39ad79Sreyk 
57cf39ad79Sreyk 	/*
58cf39ad79Sreyk 	 * Read (possibly) encrypted key from file
59cf39ad79Sreyk 	 */
60cf39ad79Sreyk 	if ((fp = fopen(name, "r")) == NULL)
61cf39ad79Sreyk 		return (NULL);
62cf39ad79Sreyk 
63cf39ad79Sreyk 	key = PEM_read_PrivateKey(fp, NULL, ssl_password_cb, pass);
64cf39ad79Sreyk 	fclose(fp);
65cf39ad79Sreyk 	if (key == NULL)
66cf39ad79Sreyk 		goto fail;
67cf39ad79Sreyk 
68cf39ad79Sreyk 	/*
69cf39ad79Sreyk 	 * Write unencrypted key to memory buffer
70cf39ad79Sreyk 	 */
71cf39ad79Sreyk 	if ((bio = BIO_new(BIO_s_mem())) == NULL)
72cf39ad79Sreyk 		goto fail;
73cf39ad79Sreyk 	if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL))
74cf39ad79Sreyk 		goto fail;
75cf39ad79Sreyk 	if ((size = BIO_get_mem_data(bio, &data)) <= 0)
76cf39ad79Sreyk 		goto fail;
77cf39ad79Sreyk 	if ((buf = calloc(1, size)) == NULL)
78cf39ad79Sreyk 		goto fail;
79cf39ad79Sreyk 	memcpy(buf, data, size);
80cf39ad79Sreyk 
81cf39ad79Sreyk 	BIO_free_all(bio);
825a039c14Sreyk 	EVP_PKEY_free(key);
835a039c14Sreyk 
84cf39ad79Sreyk 	*len = (off_t)size;
85cf39ad79Sreyk 	return (buf);
86cf39ad79Sreyk 
87cf39ad79Sreyk  fail:
88cf39ad79Sreyk 	free(buf);
89cf39ad79Sreyk 	if (bio != NULL)
90cf39ad79Sreyk 		BIO_free_all(bio);
915a039c14Sreyk 	if (key != NULL)
925a039c14Sreyk 		EVP_PKEY_free(key);
93cf39ad79Sreyk 	return (NULL);
94cf39ad79Sreyk }
95cf39ad79Sreyk 
9685e5f500Sclaudio uint8_t *
ssl_update_certificate(const uint8_t * oldcert,size_t oldlen,EVP_PKEY * pkey,EVP_PKEY * capkey,X509 * cacert,size_t * newlen)9785e5f500Sclaudio ssl_update_certificate(const uint8_t *oldcert, size_t oldlen, EVP_PKEY *pkey,
9885e5f500Sclaudio     EVP_PKEY *capkey, X509 *cacert, size_t *newlen)
99cf39ad79Sreyk {
1007bb52228Sreyk 	char		 name[2][TLS_NAME_SIZE];
10185e5f500Sclaudio 	BIO		*in, *out = NULL;
10285e5f500Sclaudio 	BUF_MEM		*bptr = NULL;
10300ae3104Sreyk 	X509		*cert = NULL;
104e4d8cd3cStb 	uint8_t		*newcert = NULL;
10585e5f500Sclaudio 
106e4d8cd3cStb 	if ((in = BIO_new_mem_buf(oldcert, oldlen)) == NULL) {
10785e5f500Sclaudio 		log_warnx("%s: BIO_new_mem_buf failed", __func__);
10885e5f500Sclaudio 		goto done;
10985e5f500Sclaudio 	}
11085e5f500Sclaudio 
11185e5f500Sclaudio 	if ((cert = PEM_read_bio_X509(in, NULL,
11285e5f500Sclaudio 	    ssl_password_cb, NULL)) == NULL) {
11385e5f500Sclaudio 		log_warnx("%s: PEM_read_bio_X509 failed", __func__);
11485e5f500Sclaudio 		goto done;
11585e5f500Sclaudio 	}
11685e5f500Sclaudio 
11785e5f500Sclaudio 	BIO_free(in);
11885e5f500Sclaudio 	in = NULL;
119cf39ad79Sreyk 
120cf39ad79Sreyk 	name[0][0] = name[1][0] = '\0';
12185e5f500Sclaudio 	if (!X509_NAME_oneline(X509_get_subject_name(cert),
122cf39ad79Sreyk 	    name[0], sizeof(name[0])) ||
12385e5f500Sclaudio 	    !X509_NAME_oneline(X509_get_issuer_name(cert),
124cf39ad79Sreyk 	    name[1], sizeof(name[1])))
125cf39ad79Sreyk 		goto done;
126cf39ad79Sreyk 
12785e5f500Sclaudio 	if ((cert = X509_dup(cert)) == NULL)
128cf39ad79Sreyk 		goto done;
129cf39ad79Sreyk 
130cf39ad79Sreyk 	/* Update certificate key and use our CA as the issuer */
1313d77879fSreyk 	X509_set_pubkey(cert, pkey);
132cf39ad79Sreyk 	X509_set_issuer_name(cert, X509_get_subject_name(cacert));
133cf39ad79Sreyk 
134cf39ad79Sreyk 	/* Sign with our CA */
13585e5f500Sclaudio 	if (!X509_sign(cert, capkey, EVP_sha256())) {
13685e5f500Sclaudio 		log_warnx("%s: X509_sign failed", __func__);
13785e5f500Sclaudio 		goto done;
138cf39ad79Sreyk 	}
139cf39ad79Sreyk 
140cf39ad79Sreyk #if DEBUG_CERT
141cf39ad79Sreyk 	log_debug("%s: subject %s", __func__, name[0]);
142cf39ad79Sreyk 	log_debug("%s: issuer %s", __func__, name[1]);
143cf39ad79Sreyk #if DEBUG > 2
144cf39ad79Sreyk 	X509_print_fp(stdout, cert);
145cf39ad79Sreyk #endif
146cf39ad79Sreyk #endif
147cf39ad79Sreyk 
14885e5f500Sclaudio 	/* write cert as PEM file */
14985e5f500Sclaudio 	out = BIO_new(BIO_s_mem());
15085e5f500Sclaudio 	if (out == NULL) {
15185e5f500Sclaudio 		log_warnx("%s: BIO_new failed", __func__);
15285e5f500Sclaudio 		goto done;
15385e5f500Sclaudio 	}
15485e5f500Sclaudio 	if (!PEM_write_bio_X509(out, cert)) {
15585e5f500Sclaudio 		log_warnx("%s: PEM_write_bio_X509 failed", __func__);
15685e5f500Sclaudio 		goto done;
15785e5f500Sclaudio 	}
15885e5f500Sclaudio 	BIO_get_mem_ptr(out, &bptr);
15985e5f500Sclaudio 	if ((newcert = malloc(bptr->length)) == NULL) {
16085e5f500Sclaudio 		log_warn("%s: malloc", __func__);
16185e5f500Sclaudio 		goto done;
16285e5f500Sclaudio 	}
16385e5f500Sclaudio 	memcpy(newcert, bptr->data, bptr->length);
16485e5f500Sclaudio 	*newlen = bptr->length;
165cf39ad79Sreyk 
16685e5f500Sclaudio done:
16785e5f500Sclaudio 	if (in)
16885e5f500Sclaudio 		BIO_free(in);
16985e5f500Sclaudio 	if (out)
17085e5f500Sclaudio 		BIO_free(out);
17185e5f500Sclaudio 	if (cert)
17285e5f500Sclaudio 		X509_free(cert);
17385e5f500Sclaudio 	return (newcert);
174cf39ad79Sreyk }
1753d77879fSreyk 
1763d77879fSreyk int
ssl_load_pkey(char * buf,off_t len,X509 ** x509ptr,EVP_PKEY ** pkeyptr)177f8a1e24fSbluhm ssl_load_pkey(char *buf, off_t len, X509 **x509ptr, EVP_PKEY **pkeyptr)
1783d77879fSreyk {
1793d77879fSreyk 	BIO		*in;
1803d77879fSreyk 	X509		*x509 = NULL;
1813d77879fSreyk 	EVP_PKEY	*pkey = NULL;
1823d77879fSreyk 	RSA		*rsa = NULL;
183f8a1e24fSbluhm 	char		*hash = NULL;
1843d77879fSreyk 
1853d77879fSreyk 	if ((in = BIO_new_mem_buf(buf, len)) == NULL) {
18685e5f500Sclaudio 		log_warnx("%s: BIO_new_mem_buf failed", __func__);
1873d77879fSreyk 		return (0);
1883d77879fSreyk 	}
1893d77879fSreyk 	if ((x509 = PEM_read_bio_X509(in, NULL,
19000ae3104Sreyk 	    ssl_password_cb, NULL)) == NULL) {
19185e5f500Sclaudio 		log_warnx("%s: PEM_read_bio_X509 failed", __func__);
1923d77879fSreyk 		goto fail;
1933d77879fSreyk 	}
1943d77879fSreyk 	if ((pkey = X509_get_pubkey(x509)) == NULL) {
19585e5f500Sclaudio 		log_warnx("%s: X509_get_pubkey failed", __func__);
1963d77879fSreyk 		goto fail;
1973d77879fSreyk 	}
19885e5f500Sclaudio 	if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) {
19985e5f500Sclaudio 		log_warnx("%s: failed to extract RSA", __func__);
2003d77879fSreyk 		goto fail;
2013d77879fSreyk 	}
202f8a1e24fSbluhm 	if ((hash = malloc(TLS_CERT_HASH_SIZE)) == NULL) {
203f8a1e24fSbluhm 		log_warn("%s: allocate hash failed", __func__);
204f8a1e24fSbluhm 		goto fail;
205f8a1e24fSbluhm 	}
206f8a1e24fSbluhm 	hash_x509(x509, hash, TLS_CERT_HASH_SIZE);
207f8a1e24fSbluhm 	if (RSA_set_ex_data(rsa, 0, hash) != 1) {
208f8a1e24fSbluhm 		log_warnx("%s: failed to set hash as exdata", __func__);
209f8a1e24fSbluhm 		goto fail;
210d5cc82f9Sreyk 	}
2113d77879fSreyk 
212f8a1e24fSbluhm 	RSA_free(rsa); /* dereference, will be cleaned up with pkey */
213f8a1e24fSbluhm 	*pkeyptr = pkey;
21485e5f500Sclaudio 	if (x509ptr != NULL)
2153d77879fSreyk 		*x509ptr = x509;
21685e5f500Sclaudio 	else
21785e5f500Sclaudio 		X509_free(x509);
218f8a1e24fSbluhm 	BIO_free(in);
2193d77879fSreyk 
220d5cc82f9Sreyk 	return (1);
2213d77879fSreyk 
2223d77879fSreyk  fail:
223f8a1e24fSbluhm 	free(hash);
224d5cc82f9Sreyk 	if (rsa != NULL)
225d5cc82f9Sreyk 		RSA_free(rsa);
2263d77879fSreyk 	if (pkey != NULL)
2273d77879fSreyk 		EVP_PKEY_free(pkey);
2283d77879fSreyk 	if (x509 != NULL)
2293d77879fSreyk 		X509_free(x509);
230f8a1e24fSbluhm 	BIO_free(in);
2313d77879fSreyk 
232d5cc82f9Sreyk 	return (0);
2333d77879fSreyk }
234