1 /* $OpenBSD: ssl.c,v 1.35 2021/01/27 20:33:05 eric 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, *foo = NULL; 127 128 /* XXX BIO_new_mem_buf is not using const so work around this */ 129 if ((foo = malloc(oldlen)) == NULL) { 130 log_warn("%s: malloc", __func__); 131 return (NULL); 132 } 133 memcpy(foo, oldcert, oldlen); 134 135 if ((in = BIO_new_mem_buf(foo, oldlen)) == NULL) { 136 log_warnx("%s: BIO_new_mem_buf failed", __func__); 137 goto done; 138 } 139 140 if ((cert = PEM_read_bio_X509(in, NULL, 141 ssl_password_cb, NULL)) == NULL) { 142 log_warnx("%s: PEM_read_bio_X509 failed", __func__); 143 goto done; 144 } 145 146 BIO_free(in); 147 in = NULL; 148 149 name[0][0] = name[1][0] = '\0'; 150 if (!X509_NAME_oneline(X509_get_subject_name(cert), 151 name[0], sizeof(name[0])) || 152 !X509_NAME_oneline(X509_get_issuer_name(cert), 153 name[1], sizeof(name[1]))) 154 goto done; 155 156 if ((cert = X509_dup(cert)) == NULL) 157 goto done; 158 159 /* Update certificate key and use our CA as the issuer */ 160 X509_set_pubkey(cert, pkey); 161 X509_set_issuer_name(cert, X509_get_subject_name(cacert)); 162 163 /* Sign with our CA */ 164 if (!X509_sign(cert, capkey, EVP_sha256())) { 165 log_warnx("%s: X509_sign failed", __func__); 166 goto done; 167 } 168 169 #if DEBUG_CERT 170 log_debug("%s: subject %s", __func__, name[0]); 171 log_debug("%s: issuer %s", __func__, name[1]); 172 #if DEBUG > 2 173 X509_print_fp(stdout, cert); 174 #endif 175 #endif 176 177 /* write cert as PEM file */ 178 out = BIO_new(BIO_s_mem()); 179 if (out == NULL) { 180 log_warnx("%s: BIO_new failed", __func__); 181 goto done; 182 } 183 if (!PEM_write_bio_X509(out, cert)) { 184 log_warnx("%s: PEM_write_bio_X509 failed", __func__); 185 goto done; 186 } 187 BIO_get_mem_ptr(out, &bptr); 188 if ((newcert = malloc(bptr->length)) == NULL) { 189 log_warn("%s: malloc", __func__); 190 goto done; 191 } 192 memcpy(newcert, bptr->data, bptr->length); 193 *newlen = bptr->length; 194 195 done: 196 free(foo); 197 if (in) 198 BIO_free(in); 199 if (out) 200 BIO_free(out); 201 if (cert) 202 X509_free(cert); 203 return (newcert); 204 } 205 206 int 207 ssl_load_pkey(char *buf, off_t len, X509 **x509ptr, EVP_PKEY **pkeyptr) 208 { 209 BIO *in; 210 X509 *x509 = NULL; 211 EVP_PKEY *pkey = NULL; 212 RSA *rsa = NULL; 213 char *hash = NULL; 214 215 if ((in = BIO_new_mem_buf(buf, len)) == NULL) { 216 log_warnx("%s: BIO_new_mem_buf failed", __func__); 217 return (0); 218 } 219 if ((x509 = PEM_read_bio_X509(in, NULL, 220 ssl_password_cb, NULL)) == NULL) { 221 log_warnx("%s: PEM_read_bio_X509 failed", __func__); 222 goto fail; 223 } 224 if ((pkey = X509_get_pubkey(x509)) == NULL) { 225 log_warnx("%s: X509_get_pubkey failed", __func__); 226 goto fail; 227 } 228 if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) { 229 log_warnx("%s: failed to extract RSA", __func__); 230 goto fail; 231 } 232 if ((hash = malloc(TLS_CERT_HASH_SIZE)) == NULL) { 233 log_warn("%s: allocate hash failed", __func__); 234 goto fail; 235 } 236 hash_x509(x509, hash, TLS_CERT_HASH_SIZE); 237 if (RSA_set_ex_data(rsa, 0, hash) != 1) { 238 log_warnx("%s: failed to set hash as exdata", __func__); 239 goto fail; 240 } 241 242 RSA_free(rsa); /* dereference, will be cleaned up with pkey */ 243 *pkeyptr = pkey; 244 if (x509ptr != NULL) 245 *x509ptr = x509; 246 else 247 X509_free(x509); 248 BIO_free(in); 249 250 return (1); 251 252 fail: 253 free(hash); 254 if (rsa != NULL) 255 RSA_free(rsa); 256 if (pkey != NULL) 257 EVP_PKEY_free(pkey); 258 if (x509 != NULL) 259 X509_free(x509); 260 BIO_free(in); 261 262 return (0); 263 } 264