1*7c0ec4b8Stb /* $OpenBSD: apitest.c,v 1.3 2024/09/07 16:39:29 tb Exp $ */ 2142fd3e3Sjsing /* 3142fd3e3Sjsing * Copyright (c) 2020, 2021 Joel Sing <jsing@openbsd.org> 4142fd3e3Sjsing * 5142fd3e3Sjsing * Permission to use, copy, modify, and distribute this software for any 6142fd3e3Sjsing * purpose with or without fee is hereby granted, provided that the above 7142fd3e3Sjsing * copyright notice and this permission notice appear in all copies. 8142fd3e3Sjsing * 9142fd3e3Sjsing * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10142fd3e3Sjsing * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11142fd3e3Sjsing * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12142fd3e3Sjsing * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13142fd3e3Sjsing * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14142fd3e3Sjsing * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15142fd3e3Sjsing * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16142fd3e3Sjsing */ 17142fd3e3Sjsing 18142fd3e3Sjsing #include <err.h> 19142fd3e3Sjsing 20142fd3e3Sjsing #include <openssl/bio.h> 21142fd3e3Sjsing #include <openssl/err.h> 22142fd3e3Sjsing #include <openssl/ssl.h> 23142fd3e3Sjsing 24172e4b14Stb #ifndef CERTSDIR 25172e4b14Stb #define CERTSDIR "." 26172e4b14Stb #endif 27172e4b14Stb 28172e4b14Stb const char *certs_path = CERTSDIR; 29142fd3e3Sjsing 30142fd3e3Sjsing int debug = 0; 31142fd3e3Sjsing 32142fd3e3Sjsing static int 33142fd3e3Sjsing ssl_ctx_use_ca_file(SSL_CTX *ssl_ctx, const char *ca_file) 34142fd3e3Sjsing { 35142fd3e3Sjsing char *ca_path = NULL; 36142fd3e3Sjsing int ret = 0; 37142fd3e3Sjsing 38142fd3e3Sjsing if (asprintf(&ca_path, "%s/%s", certs_path, ca_file) == -1) 39142fd3e3Sjsing goto err; 40142fd3e3Sjsing if (!SSL_CTX_load_verify_locations(ssl_ctx, ca_path, NULL)) { 41142fd3e3Sjsing fprintf(stderr, "load_verify_locations(%s) failed\n", ca_path); 42142fd3e3Sjsing goto err; 43142fd3e3Sjsing } 44142fd3e3Sjsing 45142fd3e3Sjsing ret = 1; 46142fd3e3Sjsing 47142fd3e3Sjsing err: 48142fd3e3Sjsing free(ca_path); 49142fd3e3Sjsing 50142fd3e3Sjsing return ret; 51142fd3e3Sjsing } 52142fd3e3Sjsing 53142fd3e3Sjsing static int 54142fd3e3Sjsing ssl_ctx_use_keypair(SSL_CTX *ssl_ctx, const char *chain_file, 55142fd3e3Sjsing const char *key_file) 56142fd3e3Sjsing { 57142fd3e3Sjsing char *chain_path = NULL, *key_path = NULL; 58142fd3e3Sjsing int ret = 0; 59142fd3e3Sjsing 60142fd3e3Sjsing if (asprintf(&chain_path, "%s/%s", certs_path, chain_file) == -1) 61142fd3e3Sjsing goto err; 62142fd3e3Sjsing if (SSL_CTX_use_certificate_chain_file(ssl_ctx, chain_path) != 1) { 63142fd3e3Sjsing fprintf(stderr, "FAIL: Failed to load certificates\n"); 64142fd3e3Sjsing goto err; 65142fd3e3Sjsing } 66142fd3e3Sjsing if (asprintf(&key_path, "%s/%s", certs_path, key_file) == -1) 67142fd3e3Sjsing goto err; 68142fd3e3Sjsing if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_path, 69142fd3e3Sjsing SSL_FILETYPE_PEM) != 1) { 70142fd3e3Sjsing fprintf(stderr, "FAIL: Failed to load private key\n"); 71142fd3e3Sjsing goto err; 72142fd3e3Sjsing } 73142fd3e3Sjsing 74142fd3e3Sjsing ret = 1; 75142fd3e3Sjsing 76142fd3e3Sjsing err: 77142fd3e3Sjsing free(chain_path); 78142fd3e3Sjsing free(key_path); 79142fd3e3Sjsing 80142fd3e3Sjsing return ret; 81142fd3e3Sjsing } 82142fd3e3Sjsing 83142fd3e3Sjsing static SSL * 84142fd3e3Sjsing tls_client(BIO *rbio, BIO *wbio) 85142fd3e3Sjsing { 86142fd3e3Sjsing SSL_CTX *ssl_ctx = NULL; 87142fd3e3Sjsing SSL *ssl = NULL; 88142fd3e3Sjsing 89142fd3e3Sjsing if ((ssl_ctx = SSL_CTX_new(TLS_method())) == NULL) 90142fd3e3Sjsing errx(1, "client context"); 91142fd3e3Sjsing 92142fd3e3Sjsing SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); 93142fd3e3Sjsing 94142fd3e3Sjsing if (!ssl_ctx_use_ca_file(ssl_ctx, "ca-root-rsa.pem")) 95142fd3e3Sjsing goto failure; 96142fd3e3Sjsing if (!ssl_ctx_use_keypair(ssl_ctx, "client1-rsa-chain.pem", 97142fd3e3Sjsing "client1-rsa.pem")) 98142fd3e3Sjsing goto failure; 99142fd3e3Sjsing 100142fd3e3Sjsing if ((ssl = SSL_new(ssl_ctx)) == NULL) 101142fd3e3Sjsing errx(1, "client ssl"); 102142fd3e3Sjsing 103142fd3e3Sjsing BIO_up_ref(rbio); 104142fd3e3Sjsing BIO_up_ref(wbio); 105142fd3e3Sjsing 106142fd3e3Sjsing SSL_set_bio(ssl, rbio, wbio); 107142fd3e3Sjsing 108142fd3e3Sjsing failure: 109142fd3e3Sjsing SSL_CTX_free(ssl_ctx); 110142fd3e3Sjsing 111142fd3e3Sjsing return ssl; 112142fd3e3Sjsing } 113142fd3e3Sjsing 114142fd3e3Sjsing static SSL * 115142fd3e3Sjsing tls_server(BIO *rbio, BIO *wbio) 116142fd3e3Sjsing { 117142fd3e3Sjsing SSL_CTX *ssl_ctx = NULL; 118142fd3e3Sjsing SSL *ssl = NULL; 119142fd3e3Sjsing 120142fd3e3Sjsing if ((ssl_ctx = SSL_CTX_new(TLS_method())) == NULL) 121142fd3e3Sjsing errx(1, "server context"); 122142fd3e3Sjsing 123142fd3e3Sjsing SSL_CTX_set_dh_auto(ssl_ctx, 2); 124142fd3e3Sjsing 125142fd3e3Sjsing SSL_CTX_set_verify(ssl_ctx, 126142fd3e3Sjsing SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); 127142fd3e3Sjsing 128142fd3e3Sjsing if (!ssl_ctx_use_ca_file(ssl_ctx, "ca-root-rsa.pem")) 129142fd3e3Sjsing goto failure; 130142fd3e3Sjsing if (!ssl_ctx_use_keypair(ssl_ctx, "server1-rsa-chain.pem", 131142fd3e3Sjsing "server1-rsa.pem")) 132142fd3e3Sjsing goto failure; 133142fd3e3Sjsing 134142fd3e3Sjsing if ((ssl = SSL_new(ssl_ctx)) == NULL) 135142fd3e3Sjsing errx(1, "server ssl"); 136142fd3e3Sjsing 137142fd3e3Sjsing BIO_up_ref(rbio); 138142fd3e3Sjsing BIO_up_ref(wbio); 139142fd3e3Sjsing 140142fd3e3Sjsing SSL_set_bio(ssl, rbio, wbio); 141142fd3e3Sjsing 142142fd3e3Sjsing failure: 143142fd3e3Sjsing SSL_CTX_free(ssl_ctx); 144142fd3e3Sjsing 145142fd3e3Sjsing return ssl; 146142fd3e3Sjsing } 147142fd3e3Sjsing 148142fd3e3Sjsing static int 149142fd3e3Sjsing ssl_error(SSL *ssl, const char *name, const char *desc, int ssl_ret) 150142fd3e3Sjsing { 151142fd3e3Sjsing int ssl_err; 152142fd3e3Sjsing 153142fd3e3Sjsing ssl_err = SSL_get_error(ssl, ssl_ret); 154142fd3e3Sjsing 155142fd3e3Sjsing if (ssl_err == SSL_ERROR_WANT_READ) { 156142fd3e3Sjsing return 1; 157142fd3e3Sjsing } else if (ssl_err == SSL_ERROR_WANT_WRITE) { 158142fd3e3Sjsing return 1; 159142fd3e3Sjsing } else if (ssl_err == SSL_ERROR_SYSCALL && errno == 0) { 160142fd3e3Sjsing /* Yup, this is apparently a thing... */ 161142fd3e3Sjsing } else { 162142fd3e3Sjsing fprintf(stderr, "FAIL: %s %s failed - ssl err = %d, errno = %d\n", 163142fd3e3Sjsing name, desc, ssl_err, errno); 164142fd3e3Sjsing ERR_print_errors_fp(stderr); 165142fd3e3Sjsing return 0; 166142fd3e3Sjsing } 167142fd3e3Sjsing 168142fd3e3Sjsing return 1; 169142fd3e3Sjsing } 170142fd3e3Sjsing 171142fd3e3Sjsing static int 172142fd3e3Sjsing do_connect(SSL *ssl, const char *name, int *done) 173142fd3e3Sjsing { 174142fd3e3Sjsing int ssl_ret; 175142fd3e3Sjsing 176142fd3e3Sjsing if ((ssl_ret = SSL_connect(ssl)) == 1) { 177142fd3e3Sjsing fprintf(stderr, "INFO: %s connect done\n", name); 178142fd3e3Sjsing *done = 1; 179142fd3e3Sjsing return 1; 180142fd3e3Sjsing } 181142fd3e3Sjsing 182142fd3e3Sjsing return ssl_error(ssl, name, "connect", ssl_ret); 183142fd3e3Sjsing } 184142fd3e3Sjsing 185142fd3e3Sjsing static int 186142fd3e3Sjsing do_accept(SSL *ssl, const char *name, int *done) 187142fd3e3Sjsing { 188142fd3e3Sjsing int ssl_ret; 189142fd3e3Sjsing 190142fd3e3Sjsing if ((ssl_ret = SSL_accept(ssl)) == 1) { 191142fd3e3Sjsing fprintf(stderr, "INFO: %s accept done\n", name); 192142fd3e3Sjsing *done = 1; 193142fd3e3Sjsing return 1; 194142fd3e3Sjsing } 195142fd3e3Sjsing 196142fd3e3Sjsing return ssl_error(ssl, name, "accept", ssl_ret); 197142fd3e3Sjsing } 198142fd3e3Sjsing 199142fd3e3Sjsing typedef int (*ssl_func)(SSL *ssl, const char *name, int *done); 200142fd3e3Sjsing 201142fd3e3Sjsing static int 202142fd3e3Sjsing do_client_server_loop(SSL *client, ssl_func client_func, SSL *server, 203142fd3e3Sjsing ssl_func server_func) 204142fd3e3Sjsing { 205142fd3e3Sjsing int client_done = 0, server_done = 0; 206142fd3e3Sjsing int i = 0; 207142fd3e3Sjsing 208142fd3e3Sjsing do { 209142fd3e3Sjsing if (!client_done) { 210142fd3e3Sjsing if (debug) 211142fd3e3Sjsing fprintf(stderr, "DEBUG: client loop\n"); 212142fd3e3Sjsing if (!client_func(client, "client", &client_done)) 213142fd3e3Sjsing return 0; 214142fd3e3Sjsing } 215142fd3e3Sjsing if (!server_done) { 216142fd3e3Sjsing if (debug) 217142fd3e3Sjsing fprintf(stderr, "DEBUG: server loop\n"); 218142fd3e3Sjsing if (!server_func(server, "server", &server_done)) 219142fd3e3Sjsing return 0; 220142fd3e3Sjsing } 221142fd3e3Sjsing } while (i++ < 100 && (!client_done || !server_done)); 222142fd3e3Sjsing 223142fd3e3Sjsing if (!client_done || !server_done) 224142fd3e3Sjsing fprintf(stderr, "FAIL: gave up\n"); 225142fd3e3Sjsing 226142fd3e3Sjsing return client_done && server_done; 227142fd3e3Sjsing } 228142fd3e3Sjsing 229142fd3e3Sjsing static int 230142fd3e3Sjsing ssl_get_peer_cert_chain_test(uint16_t tls_version) 231142fd3e3Sjsing { 232142fd3e3Sjsing STACK_OF(X509) *peer_chain; 233142fd3e3Sjsing X509 *peer_cert; 234142fd3e3Sjsing BIO *client_wbio = NULL, *server_wbio = NULL; 235142fd3e3Sjsing SSL *client = NULL, *server = NULL; 236142fd3e3Sjsing int failed = 1; 237142fd3e3Sjsing 238142fd3e3Sjsing if ((client_wbio = BIO_new(BIO_s_mem())) == NULL) 239142fd3e3Sjsing goto failure; 240142fd3e3Sjsing if (BIO_set_mem_eof_return(client_wbio, -1) <= 0) 241142fd3e3Sjsing goto failure; 242142fd3e3Sjsing 243142fd3e3Sjsing if ((server_wbio = BIO_new(BIO_s_mem())) == NULL) 244142fd3e3Sjsing goto failure; 245142fd3e3Sjsing if (BIO_set_mem_eof_return(server_wbio, -1) <= 0) 246142fd3e3Sjsing goto failure; 247142fd3e3Sjsing 248142fd3e3Sjsing if ((client = tls_client(server_wbio, client_wbio)) == NULL) 249142fd3e3Sjsing goto failure; 250142fd3e3Sjsing if (tls_version != 0) { 251142fd3e3Sjsing if (!SSL_set_min_proto_version(client, tls_version)) 252142fd3e3Sjsing goto failure; 253142fd3e3Sjsing if (!SSL_set_max_proto_version(client, tls_version)) 254142fd3e3Sjsing goto failure; 255142fd3e3Sjsing } 256142fd3e3Sjsing 257142fd3e3Sjsing if ((server = tls_server(client_wbio, server_wbio)) == NULL) 258142fd3e3Sjsing goto failure; 259142fd3e3Sjsing if (tls_version != 0) { 260142fd3e3Sjsing if (!SSL_set_min_proto_version(server, tls_version)) 261142fd3e3Sjsing goto failure; 262142fd3e3Sjsing if (!SSL_set_max_proto_version(server, tls_version)) 263142fd3e3Sjsing goto failure; 264142fd3e3Sjsing } 265142fd3e3Sjsing 266142fd3e3Sjsing if (!do_client_server_loop(client, do_connect, server, do_accept)) { 267142fd3e3Sjsing fprintf(stderr, "FAIL: client and server handshake failed\n"); 268142fd3e3Sjsing goto failure; 269142fd3e3Sjsing } 270142fd3e3Sjsing 271142fd3e3Sjsing if (tls_version != 0) { 272142fd3e3Sjsing if (SSL_version(client) != tls_version) { 273142fd3e3Sjsing fprintf(stderr, "FAIL: client got TLS version %x, " 274142fd3e3Sjsing "want %x\n", SSL_version(client), tls_version); 275142fd3e3Sjsing goto failure; 276142fd3e3Sjsing } 277142fd3e3Sjsing if (SSL_version(server) != tls_version) { 278142fd3e3Sjsing fprintf(stderr, "FAIL: server got TLS version %x, " 279142fd3e3Sjsing "want %x\n", SSL_version(server), tls_version); 280142fd3e3Sjsing goto failure; 281142fd3e3Sjsing } 282142fd3e3Sjsing } 283142fd3e3Sjsing 284142fd3e3Sjsing /* 285142fd3e3Sjsing * Due to the wonders of API inconsistency, SSL_get_peer_cert_chain() 286142fd3e3Sjsing * includes the peer's leaf certificate when called by the client, 287*7c0ec4b8Stb * however it does not when called by the server. Furthermore, the 288142fd3e3Sjsing * certificate returned by SSL_get_peer_certificate() has already 289142fd3e3Sjsing * had its reference count incremented and must be freed, where as 290142fd3e3Sjsing * the certificates returned from SSL_get_peer_cert_chain() must 291142fd3e3Sjsing * not be freed... *sigh* 292142fd3e3Sjsing */ 293142fd3e3Sjsing peer_cert = SSL_get_peer_certificate(client); 294142fd3e3Sjsing peer_chain = SSL_get_peer_cert_chain(client); 295142fd3e3Sjsing X509_free(peer_cert); 296142fd3e3Sjsing 297142fd3e3Sjsing if (peer_cert == NULL) { 298142fd3e3Sjsing fprintf(stderr, "FAIL: client got no peer cert\n"); 299142fd3e3Sjsing goto failure; 300142fd3e3Sjsing } 301142fd3e3Sjsing if (sk_X509_num(peer_chain) != 2) { 302142fd3e3Sjsing fprintf(stderr, "FAIL: client got peer cert chain with %d " 303142fd3e3Sjsing "certificates, want 2\n", sk_X509_num(peer_chain)); 304142fd3e3Sjsing goto failure; 305142fd3e3Sjsing } 306142fd3e3Sjsing if (X509_cmp(peer_cert, sk_X509_value(peer_chain, 0)) != 0) { 307142fd3e3Sjsing fprintf(stderr, "FAIL: client got peer cert chain without peer " 308142fd3e3Sjsing "certificate\n"); 309142fd3e3Sjsing goto failure; 310142fd3e3Sjsing } 311142fd3e3Sjsing 312142fd3e3Sjsing peer_cert = SSL_get_peer_certificate(server); 313142fd3e3Sjsing peer_chain = SSL_get_peer_cert_chain(server); 314142fd3e3Sjsing X509_free(peer_cert); 315142fd3e3Sjsing 316142fd3e3Sjsing if (peer_cert == NULL) { 317142fd3e3Sjsing fprintf(stderr, "FAIL: server got no peer cert\n"); 318142fd3e3Sjsing goto failure; 319142fd3e3Sjsing } 320142fd3e3Sjsing if (sk_X509_num(peer_chain) != 1) { 321142fd3e3Sjsing fprintf(stderr, "FAIL: server got peer cert chain with %d " 322142fd3e3Sjsing "certificates, want 1\n", sk_X509_num(peer_chain)); 323142fd3e3Sjsing goto failure; 324142fd3e3Sjsing } 325142fd3e3Sjsing if (X509_cmp(peer_cert, sk_X509_value(peer_chain, 0)) == 0) { 326142fd3e3Sjsing fprintf(stderr, "FAIL: server got peer cert chain with peer " 327142fd3e3Sjsing "certificate\n"); 328142fd3e3Sjsing goto failure; 329142fd3e3Sjsing } 330142fd3e3Sjsing 331142fd3e3Sjsing fprintf(stderr, "INFO: Done!\n"); 332142fd3e3Sjsing 333142fd3e3Sjsing failed = 0; 334142fd3e3Sjsing 335142fd3e3Sjsing failure: 336142fd3e3Sjsing BIO_free(client_wbio); 337142fd3e3Sjsing BIO_free(server_wbio); 338142fd3e3Sjsing 339142fd3e3Sjsing SSL_free(client); 340142fd3e3Sjsing SSL_free(server); 341142fd3e3Sjsing 342142fd3e3Sjsing return failed; 343142fd3e3Sjsing } 344142fd3e3Sjsing 345142fd3e3Sjsing static int 346142fd3e3Sjsing ssl_get_peer_cert_chain_tests(void) 347142fd3e3Sjsing { 348142fd3e3Sjsing int failed = 0; 349142fd3e3Sjsing 350142fd3e3Sjsing fprintf(stderr, "\n== Testing SSL_get_peer_cert_chain()... ==\n"); 351142fd3e3Sjsing 352142fd3e3Sjsing failed |= ssl_get_peer_cert_chain_test(0); 353142fd3e3Sjsing failed |= ssl_get_peer_cert_chain_test(TLS1_3_VERSION); 354142fd3e3Sjsing failed |= ssl_get_peer_cert_chain_test(TLS1_2_VERSION); 355142fd3e3Sjsing 356142fd3e3Sjsing return failed; 357142fd3e3Sjsing } 358142fd3e3Sjsing 359142fd3e3Sjsing int 360142fd3e3Sjsing main(int argc, char **argv) 361142fd3e3Sjsing { 362142fd3e3Sjsing int failed = 0; 363142fd3e3Sjsing 364172e4b14Stb if (argc > 2) { 365172e4b14Stb fprintf(stderr, "usage: %s [certspath]\n", argv[0]); 366142fd3e3Sjsing exit(1); 367142fd3e3Sjsing } 368172e4b14Stb if (argc == 2) 369142fd3e3Sjsing certs_path = argv[1]; 370142fd3e3Sjsing 371142fd3e3Sjsing failed |= ssl_get_peer_cert_chain_tests(); 372142fd3e3Sjsing 373142fd3e3Sjsing return failed; 374142fd3e3Sjsing } 375