Blob


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