1 /* $OpenBSD: apitest.c,v 1.1 2022/01/05 09:59:39 jsing Exp $ */ 2 /* 3 * Copyright (c) 2020, 2021 Joel Sing <jsing@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 #include <err.h> 19 20 #include <openssl/bio.h> 21 #include <openssl/err.h> 22 #include <openssl/ssl.h> 23 24 const char *certs_path; 25 26 int debug = 0; 27 28 static int 29 ssl_ctx_use_ca_file(SSL_CTX *ssl_ctx, const char *ca_file) 30 { 31 char *ca_path = NULL; 32 int ret = 0; 33 34 if (asprintf(&ca_path, "%s/%s", certs_path, ca_file) == -1) 35 goto err; 36 if (!SSL_CTX_load_verify_locations(ssl_ctx, ca_path, NULL)) { 37 fprintf(stderr, "load_verify_locations(%s) failed\n", ca_path); 38 goto err; 39 } 40 41 ret = 1; 42 43 err: 44 free(ca_path); 45 46 return ret; 47 } 48 49 static int 50 ssl_ctx_use_keypair(SSL_CTX *ssl_ctx, const char *chain_file, 51 const char *key_file) 52 { 53 char *chain_path = NULL, *key_path = NULL; 54 int ret = 0; 55 56 if (asprintf(&chain_path, "%s/%s", certs_path, chain_file) == -1) 57 goto err; 58 if (SSL_CTX_use_certificate_chain_file(ssl_ctx, chain_path) != 1) { 59 fprintf(stderr, "FAIL: Failed to load certificates\n"); 60 goto err; 61 } 62 if (asprintf(&key_path, "%s/%s", certs_path, key_file) == -1) 63 goto err; 64 if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_path, 65 SSL_FILETYPE_PEM) != 1) { 66 fprintf(stderr, "FAIL: Failed to load private key\n"); 67 goto err; 68 } 69 70 ret = 1; 71 72 err: 73 free(chain_path); 74 free(key_path); 75 76 return ret; 77 } 78 79 static SSL * 80 tls_client(BIO *rbio, BIO *wbio) 81 { 82 SSL_CTX *ssl_ctx = NULL; 83 SSL *ssl = NULL; 84 85 if ((ssl_ctx = SSL_CTX_new(TLS_method())) == NULL) 86 errx(1, "client context"); 87 88 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); 89 90 if (!ssl_ctx_use_ca_file(ssl_ctx, "ca-root-rsa.pem")) 91 goto failure; 92 if (!ssl_ctx_use_keypair(ssl_ctx, "client1-rsa-chain.pem", 93 "client1-rsa.pem")) 94 goto failure; 95 96 if ((ssl = SSL_new(ssl_ctx)) == NULL) 97 errx(1, "client ssl"); 98 99 BIO_up_ref(rbio); 100 BIO_up_ref(wbio); 101 102 SSL_set_bio(ssl, rbio, wbio); 103 104 failure: 105 SSL_CTX_free(ssl_ctx); 106 107 return ssl; 108 } 109 110 static SSL * 111 tls_server(BIO *rbio, BIO *wbio) 112 { 113 SSL_CTX *ssl_ctx = NULL; 114 SSL *ssl = NULL; 115 116 if ((ssl_ctx = SSL_CTX_new(TLS_method())) == NULL) 117 errx(1, "server context"); 118 119 SSL_CTX_set_dh_auto(ssl_ctx, 2); 120 121 SSL_CTX_set_verify(ssl_ctx, 122 SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); 123 124 if (!ssl_ctx_use_ca_file(ssl_ctx, "ca-root-rsa.pem")) 125 goto failure; 126 if (!ssl_ctx_use_keypair(ssl_ctx, "server1-rsa-chain.pem", 127 "server1-rsa.pem")) 128 goto failure; 129 130 if ((ssl = SSL_new(ssl_ctx)) == NULL) 131 errx(1, "server ssl"); 132 133 BIO_up_ref(rbio); 134 BIO_up_ref(wbio); 135 136 SSL_set_bio(ssl, rbio, wbio); 137 138 failure: 139 SSL_CTX_free(ssl_ctx); 140 141 return ssl; 142 } 143 144 static int 145 ssl_error(SSL *ssl, const char *name, const char *desc, int ssl_ret) 146 { 147 int ssl_err; 148 149 ssl_err = SSL_get_error(ssl, ssl_ret); 150 151 if (ssl_err == SSL_ERROR_WANT_READ) { 152 return 1; 153 } else if (ssl_err == SSL_ERROR_WANT_WRITE) { 154 return 1; 155 } else if (ssl_err == SSL_ERROR_SYSCALL && errno == 0) { 156 /* Yup, this is apparently a thing... */ 157 } else { 158 fprintf(stderr, "FAIL: %s %s failed - ssl err = %d, errno = %d\n", 159 name, desc, ssl_err, errno); 160 ERR_print_errors_fp(stderr); 161 return 0; 162 } 163 164 return 1; 165 } 166 167 static int 168 do_connect(SSL *ssl, const char *name, int *done) 169 { 170 int ssl_ret; 171 172 if ((ssl_ret = SSL_connect(ssl)) == 1) { 173 fprintf(stderr, "INFO: %s connect done\n", name); 174 *done = 1; 175 return 1; 176 } 177 178 return ssl_error(ssl, name, "connect", ssl_ret); 179 } 180 181 static int 182 do_accept(SSL *ssl, const char *name, int *done) 183 { 184 int ssl_ret; 185 186 if ((ssl_ret = SSL_accept(ssl)) == 1) { 187 fprintf(stderr, "INFO: %s accept done\n", name); 188 *done = 1; 189 return 1; 190 } 191 192 return ssl_error(ssl, name, "accept", ssl_ret); 193 } 194 195 typedef int (*ssl_func)(SSL *ssl, const char *name, int *done); 196 197 static int 198 do_client_server_loop(SSL *client, ssl_func client_func, SSL *server, 199 ssl_func server_func) 200 { 201 int client_done = 0, server_done = 0; 202 int i = 0; 203 204 do { 205 if (!client_done) { 206 if (debug) 207 fprintf(stderr, "DEBUG: client loop\n"); 208 if (!client_func(client, "client", &client_done)) 209 return 0; 210 } 211 if (!server_done) { 212 if (debug) 213 fprintf(stderr, "DEBUG: server loop\n"); 214 if (!server_func(server, "server", &server_done)) 215 return 0; 216 } 217 } while (i++ < 100 && (!client_done || !server_done)); 218 219 if (!client_done || !server_done) 220 fprintf(stderr, "FAIL: gave up\n"); 221 222 return client_done && server_done; 223 } 224 225 static int 226 ssl_get_peer_cert_chain_test(uint16_t tls_version) 227 { 228 STACK_OF(X509) *peer_chain; 229 X509 *peer_cert; 230 BIO *client_wbio = NULL, *server_wbio = NULL; 231 SSL *client = NULL, *server = NULL; 232 int failed = 1; 233 234 if ((client_wbio = BIO_new(BIO_s_mem())) == NULL) 235 goto failure; 236 if (BIO_set_mem_eof_return(client_wbio, -1) <= 0) 237 goto failure; 238 239 if ((server_wbio = BIO_new(BIO_s_mem())) == NULL) 240 goto failure; 241 if (BIO_set_mem_eof_return(server_wbio, -1) <= 0) 242 goto failure; 243 244 if ((client = tls_client(server_wbio, client_wbio)) == NULL) 245 goto failure; 246 if (tls_version != 0) { 247 if (!SSL_set_min_proto_version(client, tls_version)) 248 goto failure; 249 if (!SSL_set_max_proto_version(client, tls_version)) 250 goto failure; 251 } 252 253 if ((server = tls_server(client_wbio, server_wbio)) == NULL) 254 goto failure; 255 if (tls_version != 0) { 256 if (!SSL_set_min_proto_version(server, tls_version)) 257 goto failure; 258 if (!SSL_set_max_proto_version(server, tls_version)) 259 goto failure; 260 } 261 262 if (!do_client_server_loop(client, do_connect, server, do_accept)) { 263 fprintf(stderr, "FAIL: client and server handshake failed\n"); 264 goto failure; 265 } 266 267 if (tls_version != 0) { 268 if (SSL_version(client) != tls_version) { 269 fprintf(stderr, "FAIL: client got TLS version %x, " 270 "want %x\n", SSL_version(client), tls_version); 271 goto failure; 272 } 273 if (SSL_version(server) != tls_version) { 274 fprintf(stderr, "FAIL: server got TLS version %x, " 275 "want %x\n", SSL_version(server), tls_version); 276 goto failure; 277 } 278 } 279 280 /* 281 * Due to the wonders of API inconsistency, SSL_get_peer_cert_chain() 282 * includes the peer's leaf certificate when called by the client, 283 * however it does not when called by the server. Futhermore, the 284 * certificate returned by SSL_get_peer_certificate() has already 285 * had its reference count incremented and must be freed, where as 286 * the certificates returned from SSL_get_peer_cert_chain() must 287 * not be freed... *sigh* 288 */ 289 peer_cert = SSL_get_peer_certificate(client); 290 peer_chain = SSL_get_peer_cert_chain(client); 291 X509_free(peer_cert); 292 293 if (peer_cert == NULL) { 294 fprintf(stderr, "FAIL: client got no peer cert\n"); 295 goto failure; 296 } 297 if (sk_X509_num(peer_chain) != 2) { 298 fprintf(stderr, "FAIL: client got peer cert chain with %d " 299 "certificates, want 2\n", sk_X509_num(peer_chain)); 300 goto failure; 301 } 302 if (X509_cmp(peer_cert, sk_X509_value(peer_chain, 0)) != 0) { 303 fprintf(stderr, "FAIL: client got peer cert chain without peer " 304 "certificate\n"); 305 goto failure; 306 } 307 308 peer_cert = SSL_get_peer_certificate(server); 309 peer_chain = SSL_get_peer_cert_chain(server); 310 X509_free(peer_cert); 311 312 if (peer_cert == NULL) { 313 fprintf(stderr, "FAIL: server got no peer cert\n"); 314 goto failure; 315 } 316 if (sk_X509_num(peer_chain) != 1) { 317 fprintf(stderr, "FAIL: server got peer cert chain with %d " 318 "certificates, want 1\n", sk_X509_num(peer_chain)); 319 goto failure; 320 } 321 if (X509_cmp(peer_cert, sk_X509_value(peer_chain, 0)) == 0) { 322 fprintf(stderr, "FAIL: server got peer cert chain with peer " 323 "certificate\n"); 324 goto failure; 325 } 326 327 fprintf(stderr, "INFO: Done!\n"); 328 329 failed = 0; 330 331 failure: 332 BIO_free(client_wbio); 333 BIO_free(server_wbio); 334 335 SSL_free(client); 336 SSL_free(server); 337 338 return failed; 339 } 340 341 static int 342 ssl_get_peer_cert_chain_tests(void) 343 { 344 int failed = 0; 345 346 fprintf(stderr, "\n== Testing SSL_get_peer_cert_chain()... ==\n"); 347 348 failed |= ssl_get_peer_cert_chain_test(0); 349 failed |= ssl_get_peer_cert_chain_test(TLS1_3_VERSION); 350 failed |= ssl_get_peer_cert_chain_test(TLS1_2_VERSION); 351 352 return failed; 353 } 354 355 int 356 main(int argc, char **argv) 357 { 358 int failed = 0; 359 360 if (argc != 2) { 361 fprintf(stderr, "usage: %s certspath\n", argv[0]); 362 exit(1); 363 } 364 certs_path = argv[1]; 365 366 failed |= ssl_get_peer_cert_chain_tests(); 367 368 return failed; 369 } 370