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