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