1 /* $OpenBSD: ssl.c,v 1.34 2017/07/28 13:58:52 bluhm 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 #include "boguskeys.h" 34 35 int ssl_password_cb(char *, int, int, void *); 36 37 void 38 ssl_init(struct relayd *env) 39 { 40 static int initialized = 0; 41 42 if (initialized) 43 return; 44 45 SSL_library_init(); 46 SSL_load_error_strings(); 47 48 /* Init hardware crypto engines. */ 49 ENGINE_load_builtin_engines(); 50 ENGINE_register_all_complete(); 51 52 initialized = 1; 53 } 54 55 int 56 ssl_password_cb(char *buf, int size, int rwflag, void *u) 57 { 58 size_t len; 59 if (u == NULL) { 60 bzero(buf, size); 61 return (0); 62 } 63 if ((len = strlcpy(buf, u, size)) >= (size_t)size) 64 return (0); 65 return (len); 66 } 67 68 char * 69 ssl_load_key(struct relayd *env, const char *name, off_t *len, char *pass) 70 { 71 FILE *fp; 72 EVP_PKEY *key = NULL; 73 BIO *bio = NULL; 74 long size; 75 char *data, *buf = NULL; 76 77 /* Initialize SSL library once */ 78 ssl_init(env); 79 80 /* 81 * Read (possibly) encrypted key from file 82 */ 83 if ((fp = fopen(name, "r")) == NULL) 84 return (NULL); 85 86 key = PEM_read_PrivateKey(fp, NULL, ssl_password_cb, pass); 87 fclose(fp); 88 if (key == NULL) 89 goto fail; 90 91 /* 92 * Write unencrypted key to memory buffer 93 */ 94 if ((bio = BIO_new(BIO_s_mem())) == NULL) 95 goto fail; 96 if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL)) 97 goto fail; 98 if ((size = BIO_get_mem_data(bio, &data)) <= 0) 99 goto fail; 100 if ((buf = calloc(1, size)) == NULL) 101 goto fail; 102 memcpy(buf, data, size); 103 104 BIO_free_all(bio); 105 EVP_PKEY_free(key); 106 107 *len = (off_t)size; 108 return (buf); 109 110 fail: 111 free(buf); 112 if (bio != NULL) 113 BIO_free_all(bio); 114 if (key != NULL) 115 EVP_PKEY_free(key); 116 return (NULL); 117 } 118 119 uint8_t * 120 ssl_update_certificate(const uint8_t *oldcert, size_t oldlen, EVP_PKEY *pkey, 121 EVP_PKEY *capkey, X509 *cacert, size_t *newlen) 122 { 123 char name[2][TLS_NAME_SIZE]; 124 BIO *in, *out = NULL; 125 BUF_MEM *bptr = NULL; 126 X509 *cert = NULL; 127 uint8_t *newcert = NULL, *foo = NULL; 128 129 /* XXX BIO_new_mem_buf is not using const so work around this */ 130 if ((foo = malloc(oldlen)) == NULL) { 131 log_warn("%s: malloc", __func__); 132 return (NULL); 133 } 134 memcpy(foo, oldcert, oldlen); 135 136 if ((in = BIO_new_mem_buf(foo, oldlen)) == NULL) { 137 log_warnx("%s: BIO_new_mem_buf failed", __func__); 138 goto done; 139 } 140 141 if ((cert = PEM_read_bio_X509(in, NULL, 142 ssl_password_cb, NULL)) == NULL) { 143 log_warnx("%s: PEM_read_bio_X509 failed", __func__); 144 goto done; 145 } 146 147 BIO_free(in); 148 in = NULL; 149 150 name[0][0] = name[1][0] = '\0'; 151 if (!X509_NAME_oneline(X509_get_subject_name(cert), 152 name[0], sizeof(name[0])) || 153 !X509_NAME_oneline(X509_get_issuer_name(cert), 154 name[1], sizeof(name[1]))) 155 goto done; 156 157 if ((cert = X509_dup(cert)) == NULL) 158 goto done; 159 160 /* Update certificate key and use our CA as the issuer */ 161 X509_set_pubkey(cert, pkey); 162 X509_set_issuer_name(cert, X509_get_subject_name(cacert)); 163 164 /* Sign with our CA */ 165 if (!X509_sign(cert, capkey, EVP_sha256())) { 166 log_warnx("%s: X509_sign failed", __func__); 167 goto done; 168 } 169 170 #if DEBUG_CERT 171 log_debug("%s: subject %s", __func__, name[0]); 172 log_debug("%s: issuer %s", __func__, name[1]); 173 #if DEBUG > 2 174 X509_print_fp(stdout, cert); 175 #endif 176 #endif 177 178 /* write cert as PEM file */ 179 out = BIO_new(BIO_s_mem()); 180 if (out == NULL) { 181 log_warnx("%s: BIO_new failed", __func__); 182 goto done; 183 } 184 if (!PEM_write_bio_X509(out, cert)) { 185 log_warnx("%s: PEM_write_bio_X509 failed", __func__); 186 goto done; 187 } 188 BIO_get_mem_ptr(out, &bptr); 189 if ((newcert = malloc(bptr->length)) == NULL) { 190 log_warn("%s: malloc", __func__); 191 goto done; 192 } 193 memcpy(newcert, bptr->data, bptr->length); 194 *newlen = bptr->length; 195 196 done: 197 free(foo); 198 if (in) 199 BIO_free(in); 200 if (out) 201 BIO_free(out); 202 if (cert) 203 X509_free(cert); 204 return (newcert); 205 } 206 207 int 208 ssl_load_pkey(char *buf, off_t len, X509 **x509ptr, EVP_PKEY **pkeyptr) 209 { 210 BIO *in; 211 X509 *x509 = NULL; 212 EVP_PKEY *pkey = NULL; 213 RSA *rsa = NULL; 214 char *hash = NULL; 215 216 if ((in = BIO_new_mem_buf(buf, len)) == NULL) { 217 log_warnx("%s: BIO_new_mem_buf failed", __func__); 218 return (0); 219 } 220 if ((x509 = PEM_read_bio_X509(in, NULL, 221 ssl_password_cb, NULL)) == NULL) { 222 log_warnx("%s: PEM_read_bio_X509 failed", __func__); 223 goto fail; 224 } 225 if ((pkey = X509_get_pubkey(x509)) == NULL) { 226 log_warnx("%s: X509_get_pubkey failed", __func__); 227 goto fail; 228 } 229 if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) { 230 log_warnx("%s: failed to extract RSA", __func__); 231 goto fail; 232 } 233 if ((hash = malloc(TLS_CERT_HASH_SIZE)) == NULL) { 234 log_warn("%s: allocate hash failed", __func__); 235 goto fail; 236 } 237 hash_x509(x509, hash, TLS_CERT_HASH_SIZE); 238 if (RSA_set_ex_data(rsa, 0, hash) != 1) { 239 log_warnx("%s: failed to set hash as exdata", __func__); 240 goto fail; 241 } 242 243 RSA_free(rsa); /* dereference, will be cleaned up with pkey */ 244 *pkeyptr = pkey; 245 if (x509ptr != NULL) 246 *x509ptr = x509; 247 else 248 X509_free(x509); 249 BIO_free(in); 250 251 return (1); 252 253 fail: 254 free(hash); 255 if (rsa != NULL) 256 RSA_free(rsa); 257 if (pkey != NULL) 258 EVP_PKEY_free(pkey); 259 if (x509 != NULL) 260 X509_free(x509); 261 BIO_free(in); 262 263 return (0); 264 } 265 266 /* 267 * This function is a horrible hack but for RSA privsep to work a private key 268 * with correct size needs to be loaded into the tls config. 269 */ 270 int 271 ssl_ctx_fake_private_key(char *buf, off_t len, const char **fake_key) 272 { 273 BIO *in; 274 EVP_PKEY *pkey = NULL; 275 X509 *x509 = NULL; 276 int ret = -1, keylen; 277 278 if ((in = BIO_new_mem_buf(buf, len)) == NULL) { 279 log_warnx("%s: BIO_new_mem_buf failed", __func__); 280 return (0); 281 } 282 283 if ((x509 = PEM_read_bio_X509(in, NULL, NULL, NULL)) == NULL) { 284 log_warnx("%s: PEM_read_bio_X509 failed", __func__); 285 goto fail; 286 } 287 288 if ((pkey = X509_get_pubkey(x509)) == NULL) { 289 log_warnx("%s: X509_get_pubkey failed", __func__); 290 goto fail; 291 } 292 293 keylen = EVP_PKEY_size(pkey) * 8; 294 switch(keylen) { 295 case 1024: 296 *fake_key = bogus_1024; 297 ret = sizeof(bogus_1024); 298 break; 299 case 2048: 300 *fake_key = bogus_2048; 301 ret = sizeof(bogus_2048); 302 break; 303 case 4096: 304 *fake_key = bogus_4096; 305 ret = sizeof(bogus_4096); 306 break; 307 case 8192: 308 *fake_key = bogus_8192; 309 ret = sizeof(bogus_8192); 310 break; 311 default: 312 log_warnx("%s: key size %d not support", __func__, keylen); 313 ret = -1; 314 break; 315 } 316 fail: 317 BIO_free(in); 318 319 if (pkey != NULL) 320 EVP_PKEY_free(pkey); 321 if (x509 != NULL) 322 X509_free(x509); 323 324 return (ret); 325 } 326