1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* plugins/tls/k5tls/openssl.c - OpenSSL TLS module implementation */ 3 /* 4 * Copyright 2013,2014 Red Hat, Inc. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in 14 * the documentation and/or other materials provided with the 15 * distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include "k5-int.h" 31 #include "k5-utf8.h" 32 #include "k5-tls.h" 33 34 #ifdef TLS_IMPL_OPENSSL 35 #include <openssl/err.h> 36 #include <openssl/ssl.h> 37 #include <openssl/x509.h> 38 #include <openssl/x509v3.h> 39 #include <dirent.h> 40 41 struct k5_tls_handle_st { 42 SSL *ssl; 43 char *servername; 44 }; 45 46 static int ex_context_id = -1; 47 static int ex_handle_id = -1; 48 49 MAKE_INIT_FUNCTION(init_openssl); 50 51 int 52 init_openssl() 53 { 54 SSL_library_init(); 55 SSL_load_error_strings(); 56 OpenSSL_add_all_algorithms(); 57 ex_context_id = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); 58 ex_handle_id = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); 59 return 0; 60 } 61 62 static void 63 flush_errors(krb5_context context) 64 { 65 unsigned long err; 66 char buf[128]; 67 68 while ((err = ERR_get_error()) != 0) { 69 ERR_error_string_n(err, buf, sizeof(buf)); 70 TRACE_TLS_ERROR(context, buf); 71 } 72 } 73 74 /* Return the passed-in character, lower-cased if it's an ASCII character. */ 75 static inline char 76 ascii_tolower(char p) 77 { 78 if (KRB5_UPPER(p)) 79 return p + ('a' - 'A'); 80 return p; 81 } 82 83 /* 84 * Check a single label. If allow_wildcard is true, and the presented name 85 * includes a wildcard, return true and note that we matched a wildcard. 86 * Otherwise, for both the presented and expected values, do a case-insensitive 87 * comparison of ASCII characters, and a case-sensitive comparison of 88 * everything else. 89 */ 90 static krb5_boolean 91 label_match(const char *presented, size_t plen, const char *expected, 92 size_t elen, krb5_boolean allow_wildcard, krb5_boolean *wildcard) 93 { 94 unsigned int i; 95 96 if (allow_wildcard && plen == 1 && presented[0] == '*') { 97 *wildcard = TRUE; 98 return TRUE; 99 } 100 101 if (plen != elen) 102 return FALSE; 103 104 for (i = 0; i < elen; i++) { 105 if (ascii_tolower(presented[i]) != ascii_tolower(expected[i])) 106 return FALSE; 107 } 108 return TRUE; 109 } 110 111 /* Break up the two names and check them, label by label. */ 112 static krb5_boolean 113 domain_match(const char *presented, size_t plen, const char *expected) 114 { 115 const char *p, *q, *r, *s; 116 int n_label; 117 krb5_boolean used_wildcard = FALSE; 118 119 n_label = 0; 120 p = presented; 121 r = expected; 122 while (p < presented + plen && *r != '\0') { 123 q = memchr(p, '.', plen - (p - presented)); 124 if (q == NULL) 125 q = presented + plen; 126 s = r + strcspn(r, "."); 127 if (!label_match(p, q - p, r, s - r, n_label == 0, &used_wildcard)) 128 return FALSE; 129 p = q < presented + plen ? q + 1 : q; 130 r = *s ? s + 1 : s; 131 n_label++; 132 } 133 if (used_wildcard && n_label <= 2) 134 return FALSE; 135 if (p == presented + plen && *r == '\0') 136 return TRUE; 137 return FALSE; 138 } 139 140 /* Fetch the list of subjectAltNames from a certificate. */ 141 static GENERAL_NAMES * 142 get_cert_sans(X509 *x) 143 { 144 int ext; 145 X509_EXTENSION *san_ext; 146 147 ext = X509_get_ext_by_NID(x, NID_subject_alt_name, -1); 148 if (ext < 0) 149 return NULL; 150 san_ext = X509_get_ext(x, ext); 151 if (san_ext == NULL) 152 return NULL; 153 return X509V3_EXT_d2i(san_ext); 154 } 155 156 /* Fetch a CN value from the subjct name field, returning its length, or -1 if 157 * there is no subject name or it contains no CN value. */ 158 static int 159 get_cert_cn(X509 *x, char *buf, size_t bufsize) 160 { 161 X509_NAME *name; 162 163 name = X509_get_subject_name(x); 164 if (name == NULL) 165 return -1; 166 return X509_NAME_get_text_by_NID(name, NID_commonName, buf, bufsize); 167 } 168 169 /* Return true if text matches any of the addresses we can recover from x. */ 170 static krb5_boolean 171 check_cert_address(X509 *x, const char *text) 172 { 173 char buf[1024]; 174 GENERAL_NAMES *sans; 175 GENERAL_NAME *san = NULL; 176 ASN1_OCTET_STRING *ip; 177 krb5_boolean found_ip_san = FALSE, matched = FALSE; 178 int n_sans, i; 179 int name_length; 180 struct in_addr sin; 181 struct in6_addr sin6; 182 183 /* Parse the IP address into an octet string. */ 184 ip = ASN1_OCTET_STRING_new(); 185 if (ip == NULL) 186 return FALSE; 187 if (inet_pton(AF_INET, text, &sin)) { 188 ASN1_OCTET_STRING_set(ip, (unsigned char *)&sin, sizeof(sin)); 189 } else if (inet_pton(AF_INET6, text, &sin6)) { 190 ASN1_OCTET_STRING_set(ip, (unsigned char *)&sin6, sizeof(sin6)); 191 } else { 192 ASN1_OCTET_STRING_free(ip); 193 return FALSE; 194 } 195 196 /* Check for matches in ipaddress subjectAltName values. */ 197 sans = get_cert_sans(x); 198 if (sans != NULL) { 199 n_sans = sk_GENERAL_NAME_num(sans); 200 for (i = 0; i < n_sans; i++) { 201 san = sk_GENERAL_NAME_value(sans, i); 202 if (san->type != GEN_IPADD) 203 continue; 204 found_ip_san = TRUE; 205 matched = (ASN1_OCTET_STRING_cmp(ip, san->d.iPAddress) == 0); 206 if (matched) 207 break; 208 } 209 sk_GENERAL_NAME_pop_free(sans, GENERAL_NAME_free); 210 } 211 ASN1_OCTET_STRING_free(ip); 212 213 if (found_ip_san) 214 return matched; 215 216 /* Check for a match against the CN value in the peer's subject name. */ 217 name_length = get_cert_cn(x, buf, sizeof(buf)); 218 if (name_length >= 0) { 219 /* Do a string compare to check if it's an acceptable value. */ 220 return strlen(text) == (size_t)name_length && 221 strncmp(text, buf, name_length) == 0; 222 } 223 224 /* We didn't find a match. */ 225 return FALSE; 226 } 227 228 /* Return true if expected matches any of the names we can recover from x. */ 229 static krb5_boolean 230 check_cert_servername(X509 *x, const char *expected) 231 { 232 char buf[1024]; 233 GENERAL_NAMES *sans; 234 GENERAL_NAME *san = NULL; 235 unsigned char *dnsname; 236 krb5_boolean found_dns_san = FALSE, matched = FALSE; 237 int name_length, n_sans, i; 238 239 /* Check for matches in dnsname subjectAltName values. */ 240 sans = get_cert_sans(x); 241 if (sans != NULL) { 242 n_sans = sk_GENERAL_NAME_num(sans); 243 for (i = 0; i < n_sans; i++) { 244 san = sk_GENERAL_NAME_value(sans, i); 245 if (san->type != GEN_DNS) 246 continue; 247 found_dns_san = TRUE; 248 dnsname = NULL; 249 name_length = ASN1_STRING_to_UTF8(&dnsname, san->d.dNSName); 250 if (dnsname == NULL) 251 continue; 252 matched = domain_match((char *)dnsname, name_length, expected); 253 OPENSSL_free(dnsname); 254 if (matched) 255 break; 256 } 257 sk_GENERAL_NAME_pop_free(sans, GENERAL_NAME_free); 258 } 259 260 if (matched) 261 return TRUE; 262 if (found_dns_san) 263 return matched; 264 265 /* Check for a match against the CN value in the peer's subject name. */ 266 name_length = get_cert_cn(x, buf, sizeof(buf)); 267 if (name_length >= 0) 268 return domain_match(buf, name_length, expected); 269 270 /* We didn't find a match. */ 271 return FALSE; 272 } 273 274 static krb5_boolean 275 check_cert_name_or_ip(X509 *x, const char *expected_name) 276 { 277 struct in_addr in; 278 struct in6_addr in6; 279 280 if (inet_pton(AF_INET, expected_name, &in) != 0 || 281 inet_pton(AF_INET6, expected_name, &in6) != 0) { 282 return check_cert_address(x, expected_name); 283 } else { 284 return check_cert_servername(x, expected_name); 285 } 286 } 287 288 static int 289 verify_callback(int preverify_ok, X509_STORE_CTX *store_ctx) 290 { 291 X509 *x; 292 SSL *ssl; 293 BIO *bio; 294 krb5_context context; 295 int err, depth; 296 k5_tls_handle handle; 297 const char *cert = NULL, *errstr, *expected_name; 298 size_t count; 299 300 ssl = X509_STORE_CTX_get_ex_data(store_ctx, 301 SSL_get_ex_data_X509_STORE_CTX_idx()); 302 context = SSL_get_ex_data(ssl, ex_context_id); 303 handle = SSL_get_ex_data(ssl, ex_handle_id); 304 assert(context != NULL && handle != NULL); 305 /* We do have the peer's cert, right? */ 306 x = X509_STORE_CTX_get_current_cert(store_ctx); 307 if (x == NULL) { 308 TRACE_TLS_NO_REMOTE_CERTIFICATE(context); 309 return 0; 310 } 311 /* Figure out where we are. */ 312 depth = X509_STORE_CTX_get_error_depth(store_ctx); 313 if (depth < 0) 314 return 0; 315 /* If there's an error at this level that we're not ignoring, fail. */ 316 err = X509_STORE_CTX_get_error(store_ctx); 317 if (err != X509_V_OK) { 318 bio = BIO_new(BIO_s_mem()); 319 if (bio != NULL) { 320 X509_NAME_print_ex(bio, X509_get_subject_name(x), 0, 0); 321 count = BIO_get_mem_data(bio, &cert); 322 errstr = X509_verify_cert_error_string(err); 323 TRACE_TLS_CERT_ERROR(context, depth, count, cert, err, errstr); 324 BIO_free(bio); 325 } 326 return 0; 327 } 328 /* If we're not looking at the peer, we're done and everything's ok. */ 329 if (depth != 0) 330 return 1; 331 /* Check if the name we expect to find is in the certificate. */ 332 expected_name = handle->servername; 333 if (check_cert_name_or_ip(x, expected_name)) { 334 TRACE_TLS_SERVER_NAME_MATCH(context, expected_name); 335 return 1; 336 } else { 337 TRACE_TLS_SERVER_NAME_MISMATCH(context, expected_name); 338 } 339 /* The name didn't match. */ 340 return 0; 341 } 342 343 static krb5_error_code 344 load_anchor_file(X509_STORE *store, const char *path) 345 { 346 FILE *fp; 347 STACK_OF(X509_INFO) *sk = NULL; 348 X509_INFO *xi; 349 int i; 350 351 fp = fopen(path, "r"); 352 if (fp == NULL) 353 return errno; 354 sk = PEM_X509_INFO_read(fp, NULL, NULL, NULL); 355 fclose(fp); 356 if (sk == NULL) 357 return ENOENT; 358 for (i = 0; i < sk_X509_INFO_num(sk); i++) { 359 xi = sk_X509_INFO_value(sk, i); 360 if (xi->x509 != NULL) 361 X509_STORE_add_cert(store, xi->x509); 362 } 363 sk_X509_INFO_pop_free(sk, X509_INFO_free); 364 return 0; 365 } 366 367 static krb5_error_code 368 load_anchor_dir(X509_STORE *store, const char *path) 369 { 370 DIR *d = NULL; 371 struct dirent *dentry = NULL; 372 char filename[1024]; 373 krb5_boolean found_any = FALSE; 374 375 d = opendir(path); 376 if (d == NULL) 377 return ENOENT; 378 while ((dentry = readdir(d)) != NULL) { 379 if (dentry->d_name[0] != '.') { 380 snprintf(filename, sizeof(filename), "%s/%s", 381 path, dentry->d_name); 382 if (load_anchor_file(store, filename) == 0) 383 found_any = TRUE; 384 } 385 } 386 closedir(d); 387 return found_any ? 0 : ENOENT; 388 } 389 390 static krb5_error_code 391 load_anchor(SSL_CTX *ctx, const char *location) 392 { 393 X509_STORE *store; 394 const char *envloc; 395 396 store = SSL_CTX_get_cert_store(ctx); 397 if (strncmp(location, "FILE:", 5) == 0) { 398 return load_anchor_file(store, location + 5); 399 } else if (strncmp(location, "DIR:", 4) == 0) { 400 return load_anchor_dir(store, location + 4); 401 } else if (strncmp(location, "ENV:", 4) == 0) { 402 envloc = secure_getenv(location + 4); 403 if (envloc == NULL) 404 return ENOENT; 405 return load_anchor(ctx, envloc); 406 } 407 return EINVAL; 408 } 409 410 static krb5_error_code 411 load_anchors(krb5_context context, char **anchors, SSL_CTX *sctx) 412 { 413 unsigned int i; 414 krb5_error_code ret; 415 416 if (anchors != NULL) { 417 for (i = 0; anchors[i] != NULL; i++) { 418 ret = load_anchor(sctx, anchors[i]); 419 if (ret) 420 return ret; 421 } 422 } else { 423 /* Use the library defaults. */ 424 if (SSL_CTX_set_default_verify_paths(sctx) != 1) 425 return ENOENT; 426 } 427 428 return 0; 429 } 430 431 static krb5_error_code 432 setup(krb5_context context, SOCKET fd, const char *servername, 433 char **anchors, k5_tls_handle *handle_out) 434 { 435 int e; 436 long options = SSL_OP_NO_SSLv2; 437 SSL_CTX *ctx = NULL; 438 SSL *ssl = NULL; 439 k5_tls_handle handle = NULL; 440 441 *handle_out = NULL; 442 443 (void)CALL_INIT_FUNCTION(init_openssl); 444 if (ex_context_id == -1 || ex_handle_id == -1) 445 return KRB5_PLUGIN_OP_NOTSUPP; 446 447 /* Do general SSL library setup. */ 448 ctx = SSL_CTX_new(SSLv23_client_method()); 449 if (ctx == NULL) 450 goto error; 451 452 #ifdef SSL_OP_IGNORE_UNEXPECTED_EOF 453 /* 454 * For OpenSSL 3 and later, mark close_notify alerts as optional. We don't 455 * need to worry about truncation attacks because the protocols this module 456 * is used with (Kerberos and change-password) receive a single 457 * length-delimited message from the server. For prior versions of OpenSSL 458 * we check for SSL_ERROR_SYSCALL when reading instead (this error changes 459 * to SSL_ERROR_SSL in OpenSSL 3). 460 */ 461 options |= SSL_OP_IGNORE_UNEXPECTED_EOF; 462 #endif 463 SSL_CTX_set_options(ctx, options); 464 465 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback); 466 X509_STORE_set_flags(SSL_CTX_get_cert_store(ctx), 0); 467 e = load_anchors(context, anchors, ctx); 468 if (e != 0) 469 goto error; 470 471 ssl = SSL_new(ctx); 472 if (ssl == NULL) 473 goto error; 474 475 if (!SSL_set_fd(ssl, fd)) 476 goto error; 477 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME 478 if (!SSL_set_tlsext_host_name(ssl, servername)) 479 goto error; 480 #endif 481 SSL_set_connect_state(ssl); 482 483 /* Create a handle and allow verify_callback to access it. */ 484 handle = malloc(sizeof(*handle)); 485 if (handle == NULL || !SSL_set_ex_data(ssl, ex_handle_id, handle)) 486 goto error; 487 488 handle->ssl = ssl; 489 handle->servername = strdup(servername); 490 if (handle->servername == NULL) 491 goto error; 492 *handle_out = handle; 493 SSL_CTX_free(ctx); 494 return 0; 495 496 error: 497 flush_errors(context); 498 free(handle); 499 SSL_free(ssl); 500 SSL_CTX_free(ctx); 501 return KRB5_PLUGIN_OP_NOTSUPP; 502 } 503 504 static k5_tls_status 505 write_tls(krb5_context context, k5_tls_handle handle, const void *data, 506 size_t len) 507 { 508 int nwritten, e; 509 510 /* Try to transmit our request; allow verify_callback to access context. */ 511 if (!SSL_set_ex_data(handle->ssl, ex_context_id, context)) 512 return ERROR_TLS; 513 nwritten = SSL_write(handle->ssl, data, len); 514 (void)SSL_set_ex_data(handle->ssl, ex_context_id, NULL); 515 if (nwritten > 0) 516 return DONE; 517 518 e = SSL_get_error(handle->ssl, nwritten); 519 if (e == SSL_ERROR_WANT_READ) 520 return WANT_READ; 521 else if (e == SSL_ERROR_WANT_WRITE) 522 return WANT_WRITE; 523 flush_errors(context); 524 return ERROR_TLS; 525 } 526 527 static k5_tls_status 528 read_tls(krb5_context context, k5_tls_handle handle, void *data, 529 size_t data_size, size_t *len_out) 530 { 531 ssize_t nread; 532 int e; 533 534 *len_out = 0; 535 536 /* Try to read response data; allow verify_callback to access context. */ 537 if (!SSL_set_ex_data(handle->ssl, ex_context_id, context)) 538 return ERROR_TLS; 539 nread = SSL_read(handle->ssl, data, data_size); 540 (void)SSL_set_ex_data(handle->ssl, ex_context_id, NULL); 541 if (nread > 0) { 542 *len_out = nread; 543 return DATA_READ; 544 } 545 546 e = SSL_get_error(handle->ssl, nread); 547 if (e == SSL_ERROR_WANT_READ) 548 return WANT_READ; 549 else if (e == SSL_ERROR_WANT_WRITE) 550 return WANT_WRITE; 551 552 if (e == SSL_ERROR_ZERO_RETURN || (e == SSL_ERROR_SYSCALL && nread == 0)) 553 return DONE; 554 555 flush_errors(context); 556 return ERROR_TLS; 557 } 558 559 static void 560 free_handle(krb5_context context, k5_tls_handle handle) 561 { 562 SSL_free(handle->ssl); 563 free(handle->servername); 564 free(handle); 565 } 566 567 krb5_error_code 568 tls_k5tls_initvt(krb5_context context, int maj_ver, int min_ver, 569 krb5_plugin_vtable vtable); 570 571 krb5_error_code 572 tls_k5tls_initvt(krb5_context context, int maj_ver, int min_ver, 573 krb5_plugin_vtable vtable) 574 { 575 k5_tls_vtable vt; 576 577 vt = (k5_tls_vtable)vtable; 578 vt->setup = setup; 579 vt->write = write_tls; 580 vt->read = read_tls; 581 vt->free_handle = free_handle; 582 return 0; 583 } 584 585 #endif /* TLS_IMPL_OPENSSL */ 586