1 /* $OpenBSD: verify.c,v 1.1.1.1 2021/08/30 17:27:45 tb Exp $ */ 2 /* 3 * Copyright (c) 2021 Theo Buehler <tb@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 /* Based on https://github.com/noxxi/libressl-tests */ 19 20 #include <stdint.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 25 #include <openssl/bio.h> 26 #include <openssl/crypto.h> 27 #include <openssl/err.h> 28 #include <openssl/x509_vfy.h> 29 #include <openssl/ssl.h> 30 31 struct peer_config { 32 const char *name; 33 int server; 34 const char *cert; 35 const char *key; 36 const char *ca_file; 37 }; 38 39 struct ssl_wildcard_test_data { 40 const char *description; 41 struct peer_config client_config; 42 struct peer_config server_config; 43 long verify_result; 44 }; 45 46 static const struct ssl_wildcard_test_data ssl_wildcard_tests[] = { 47 { 48 .description = "unusual wildcard cert, no CA given to client", 49 .client_config = { 50 .name = "client", 51 .server = 0, 52 .cert = NULL, 53 .ca_file = NULL, 54 }, 55 .server_config = { 56 .name = "server", 57 .server = 1, 58 .cert = "server-unusual-wildcard.pem", 59 .key = "server-unusual-wildcard.pem", 60 }, 61 /* OpenSSL returns X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE */ 62 .verify_result = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 63 }, 64 65 { 66 .description = "unusual wildcard cert, CA given to client", 67 .client_config = { 68 .name = "client", 69 .server = 0, 70 .cert = NULL, 71 .ca_file = "caR.pem", 72 }, 73 .server_config = { 74 .name = "server", 75 .server = 1, 76 .cert = "server-unusual-wildcard.pem", 77 .key = "server-unusual-wildcard.pem", 78 }, 79 .verify_result = X509_V_OK, 80 }, 81 82 { 83 .description = "common wildcard cert, no CA given to client", 84 .client_config = { 85 .name = "client", 86 .server = 0, 87 .cert = NULL, 88 .ca_file = NULL, 89 }, 90 .server_config = { 91 .name = "server", 92 .server = 1, 93 .cert = "server-common-wildcard.pem", 94 .key = "server-common-wildcard.pem", 95 }, 96 /* OpenSSL returns X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE */ 97 .verify_result = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 98 }, 99 100 { 101 .description = "common wildcard cert, CA given to client", 102 .client_config = { 103 .name = "client", 104 .server = 0, 105 .cert = NULL, 106 .ca_file = "caR.pem", 107 }, 108 .server_config = { 109 .name = "server", 110 .server = 1, 111 .cert = "server-common-wildcard.pem", 112 .key = "server-common-wildcard.pem", 113 }, 114 .verify_result = X509_V_OK, 115 }, 116 117 { 118 .description = "server sends all chain certificates", 119 .client_config = { 120 .name = "client", 121 .server = 0, 122 .cert = NULL, 123 .ca_file = "caR.pem", 124 }, 125 .server_config = { 126 .name = "server", 127 .server = 1, 128 .cert = "server-subca-chainS.pem", 129 .key = "server-subca-chainS.pem", 130 .ca_file = "subcaR.pem" 131 }, 132 .verify_result = X509_V_OK, 133 }, 134 }; 135 136 static const size_t N_SSL_WILDCARD_TESTS = 137 sizeof(ssl_wildcard_tests) / sizeof(ssl_wildcard_tests[0]); 138 139 static SSL_CTX * 140 peer_config_to_ssl_ctx(const struct peer_config *config) 141 { 142 SSL_CTX *ctx; 143 144 if ((ctx = SSL_CTX_new(TLS_method())) == NULL) { 145 fprintf(stderr, "SSL_CTX_new(%s) failed\n", config->name); 146 goto err; 147 } 148 149 if (config->server) { 150 if (!SSL_CTX_use_certificate_file(ctx, config->cert, 151 SSL_FILETYPE_PEM)) { 152 fprintf(stderr, "use_certificate_file(%s) failed\n", 153 config->name); 154 goto err; 155 } 156 if (config->key != NULL && !SSL_CTX_use_PrivateKey_file(ctx, 157 config->key, SSL_FILETYPE_PEM)) { 158 fprintf(stderr, "use_PrivateKey_file(%s) failed\n", 159 config->name); 160 goto err; 161 } 162 } 163 164 if (config->ca_file != NULL) { 165 if (!SSL_CTX_load_verify_locations(ctx, config->ca_file, NULL)) { 166 fprintf(stderr, "load_verify_locations(%s) failed\n", 167 config->name); 168 goto err; 169 } 170 } 171 172 return ctx; 173 174 err: 175 SSL_CTX_free(ctx); 176 return NULL; 177 } 178 179 /* Connect client and server via a pair of "nonblocking" memory BIOs. */ 180 static int 181 connect_peers(SSL *client_ssl, SSL *server_ssl, const char *description) 182 { 183 BIO *client_wbio = NULL, *server_wbio = NULL; 184 int ret = 0; 185 186 if ((client_wbio = BIO_new(BIO_s_mem())) == NULL) { 187 fprintf(stderr, "%s: failed to create client BIO\n", 188 description); 189 goto err; 190 } 191 if ((server_wbio = BIO_new(BIO_s_mem())) == NULL) { 192 fprintf(stderr, "%s: failed to create server BIO\n", 193 description); 194 goto err; 195 } 196 if (BIO_set_mem_eof_return(client_wbio, -1) <= 0) { 197 fprintf(stderr, "%s: failed to set client eof return\n", 198 description); 199 goto err; 200 } 201 if (BIO_set_mem_eof_return(server_wbio, -1) <= 0) { 202 fprintf(stderr, "%s: failed to set server eof return\n", 203 description); 204 goto err; 205 } 206 207 /* Avoid double free. SSL_set_bio() takes ownership of the BIOs. */ 208 BIO_up_ref(client_wbio); 209 BIO_up_ref(server_wbio); 210 211 SSL_set_bio(client_ssl, server_wbio, client_wbio); 212 SSL_set_bio(server_ssl, client_wbio, server_wbio); 213 client_wbio = NULL; 214 server_wbio = NULL; 215 216 ret = 1; 217 218 err: 219 BIO_free(client_wbio); 220 BIO_free(server_wbio); 221 222 return ret; 223 } 224 225 static int 226 push_data_to_peer(SSL *ssl, int *ret, int (*func)(SSL *), const char *func_name, 227 const char *description) 228 { 229 int ssl_err = 0; 230 231 if (*ret == 1) 232 return 1; 233 234 /* 235 * Do SSL_connect/SSL_accept/SSL_shutdown once and loop while hitting 236 * WANT_WRITE. If done or on WANT_READ hand off to peer. 237 */ 238 239 do { 240 if ((*ret = func(ssl)) <= 0) 241 ssl_err = SSL_get_error(ssl, *ret); 242 } while (*ret <= 0 && ssl_err == SSL_ERROR_WANT_WRITE); 243 244 /* Ignore erroneous error - see SSL_shutdown(3)... */ 245 if (func == SSL_shutdown && ssl_err == SSL_ERROR_SYSCALL) 246 return 1; 247 248 if (*ret <= 0 && ssl_err != SSL_ERROR_WANT_READ) { 249 fprintf(stderr, "%s: %s failed\n", description, func_name); 250 ERR_print_errors_fp(stderr); 251 return 0; 252 } 253 254 return 1; 255 } 256 257 /* 258 * Alternate between loops of SSL_connect() and SSL_accept() as long as only 259 * WANT_READ and WANT_WRITE situations are encountered. A function is repeated 260 * until WANT_READ is returned or it succeeds, then it's the other function's 261 * turn to make progress. Succeeds if SSL_connect() and SSL_accept() return 1. 262 */ 263 static int 264 handshake(SSL *client_ssl, SSL *server_ssl, const char *description) 265 { 266 int loops = 0, client_ret = 0, server_ret = 0; 267 268 while (loops++ < 10 && (client_ret <= 0 || server_ret <= 0)) { 269 if (!push_data_to_peer(client_ssl, &client_ret, SSL_connect, 270 "SSL_connect", description)) 271 return 0; 272 273 if (!push_data_to_peer(server_ssl, &server_ret, SSL_accept, 274 "SSL_accept", description)) 275 return 0; 276 } 277 278 if (client_ret != 1 || server_ret != 1) { 279 fprintf(stderr, "%s: failed\n", __func__); 280 return 0; 281 } 282 283 return 1; 284 } 285 286 static int 287 shutdown_peers(SSL *client_ssl, SSL *server_ssl, const char *description) 288 { 289 int loops = 0, client_ret = 0, server_ret = 0; 290 291 while (loops++ < 10 && (client_ret <= 0 || server_ret <= 0)) { 292 if (!push_data_to_peer(client_ssl, &client_ret, SSL_shutdown, 293 "client shutdown", description)) 294 return 0; 295 296 if (!push_data_to_peer(server_ssl, &server_ret, SSL_shutdown, 297 "server shutdown", description)) 298 return 0; 299 } 300 301 if (client_ret != 1 || server_ret != 1) { 302 fprintf(stderr, "%s: failed\n", __func__); 303 return 0; 304 } 305 306 return 1; 307 } 308 309 static int 310 test_ssl_wildcards(const struct ssl_wildcard_test_data *test) 311 { 312 SSL_CTX *client_ctx = NULL, *server_ctx = NULL; 313 SSL *client_ssl = NULL, *server_ssl = NULL; 314 long verify_result; 315 int failed = 1; 316 317 if ((client_ctx = peer_config_to_ssl_ctx(&test->client_config)) == NULL) 318 goto err; 319 if ((server_ctx = peer_config_to_ssl_ctx(&test->server_config)) == NULL) 320 goto err; 321 322 if ((client_ssl = SSL_new(client_ctx)) == NULL) { 323 fprintf(stderr, "%s: failed to create client SSL\n", 324 test->description); 325 goto err; 326 } 327 if ((server_ssl = SSL_new(server_ctx)) == NULL) { 328 fprintf(stderr, "%s: failed to create server SSL\n", 329 test->description); 330 goto err; 331 } 332 333 if (!connect_peers(client_ssl, server_ssl, test->description)) 334 goto err; 335 336 if (!handshake(client_ssl, server_ssl, test->description)) 337 goto err; 338 339 verify_result = SSL_get_verify_result(client_ssl); 340 341 if (test->verify_result == verify_result) { 342 failed = 0; 343 fprintf(stderr, "%s: ok\n", test->description); 344 } else 345 fprintf(stderr, "%s: verify_result: want %ld, got %ld\n", 346 test->description, test->verify_result, verify_result); 347 348 if (!shutdown_peers(client_ssl, server_ssl, test->description)) 349 goto err; 350 351 err: 352 SSL_CTX_free(client_ctx); 353 SSL_CTX_free(server_ctx); 354 SSL_free(client_ssl); 355 SSL_free(server_ssl); 356 357 return failed; 358 } 359 360 int 361 main(int argc, char **argv) 362 { 363 size_t i; 364 int failed = 0; 365 366 for (i = 0; i < N_SSL_WILDCARD_TESTS; i++) 367 failed |= test_ssl_wildcards(&ssl_wildcard_tests[i]); 368 369 if (failed == 0) 370 printf("PASS %s\n", __FILE__); 371 372 return failed; 373 } 374