int swClient_enable_ssl_encrypt(swClient *cli) { cli->ssl_context = swSSL_get_context(&cli->ssl_option); if (cli->ssl_context == NULL) { return SW_ERR; } if (cli->ssl_option.verify_peer) { if (swSSL_set_capath(&cli->ssl_option, cli->ssl_context) < 0) { return SW_ERR; } } cli->socket->ssl_send = 1; #if defined(SW_USE_HTTP2) && defined(SW_USE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10002000L if (cli->http2) { if (SSL_CTX_set_alpn_protos(cli->ssl_context, (const unsigned char *) "\x02h2", 3) < 0) { return SW_ERR; } } #endif return SW_OK; }
bool SSLContext::setRandomizedAdvertisedNextProtocols( const std::list<NextProtocolsItem>& items, NextProtocolType protocolType) { unsetNextProtocols(); if (items.size() == 0) { return false; } int total_weight = 0; for (const auto &item : items) { if (item.protocols.size() == 0) { continue; } AdvertisedNextProtocolsItem advertised_item; advertised_item.length = 0; for (const auto& proto : item.protocols) { ++advertised_item.length; unsigned protoLength = proto.length(); if (protoLength >= 256) { deleteNextProtocolsStrings(); return false; } advertised_item.length += protoLength; } advertised_item.protocols = new unsigned char[advertised_item.length]; if (!advertised_item.protocols) { throw std::runtime_error("alloc failure"); } unsigned char* dst = advertised_item.protocols; for (auto& proto : item.protocols) { unsigned protoLength = proto.length(); *dst++ = (unsigned char)protoLength; memcpy(dst, proto.data(), protoLength); dst += protoLength; } total_weight += item.weight; advertisedNextProtocols_.push_back(advertised_item); advertisedNextProtocolWeights_.push_back(item.weight); } if (total_weight == 0) { deleteNextProtocolsStrings(); return false; } nextProtocolDistribution_ = std::discrete_distribution<>(advertisedNextProtocolWeights_.begin(), advertisedNextProtocolWeights_.end()); if ((uint8_t)protocolType & (uint8_t)NextProtocolType::NPN) { SSL_CTX_set_next_protos_advertised_cb( ctx_, advertisedNextProtocolCallback, this); SSL_CTX_set_next_proto_select_cb(ctx_, selectNextProtocolCallback, this); } #if OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined(OPENSSL_NO_TLSEXT) if ((uint8_t)protocolType & (uint8_t)NextProtocolType::ALPN) { SSL_CTX_set_alpn_select_cb(ctx_, alpnSelectCallback, this); // Client cannot really use randomized alpn SSL_CTX_set_alpn_protos(ctx_, advertisedNextProtocols_[0].protocols, advertisedNextProtocols_[0].length); } #endif return true; }
void SSLContext::unsetNextProtocols() { deleteNextProtocolsStrings(); SSL_CTX_set_next_protos_advertised_cb(ctx_, nullptr, nullptr); SSL_CTX_set_next_proto_select_cb(ctx_, nullptr, nullptr); #if OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined(OPENSSL_NO_TLSEXT) SSL_CTX_set_alpn_select_cb(ctx_, nullptr, nullptr); SSL_CTX_set_alpn_protos(ctx_, nullptr, 0); #endif }
extern "C" int32_t CryptoNative_SslCtxSetAlpnProtos(SSL_CTX* ctx, const uint8_t* protos, uint32_t protos_len) { #ifdef HAVE_OPENSSL_ALPN if (API_EXISTS(SSL_CTX_set_alpn_protos)) { return SSL_CTX_set_alpn_protos(ctx, protos, protos_len); } else #endif { return 0; } }
int tls_configure_ssl(struct tls *ctx, SSL_CTX *ssl_ctx) { SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv3); SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1); SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1_1); SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1_2); if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_0) == 0) SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1); if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_1) == 0) SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_1); if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_2) == 0) SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_2); if (ctx->config->alpn != NULL) { if (SSL_CTX_set_alpn_protos(ssl_ctx, ctx->config->alpn, ctx->config->alpn_len) != 0) { tls_set_errorx(ctx, "failed to set alpn"); goto err; } } if (ctx->config->ciphers != NULL) { if (SSL_CTX_set_cipher_list(ssl_ctx, ctx->config->ciphers) != 1) { tls_set_errorx(ctx, "failed to set ciphers"); goto err; } } if (ctx->config->verify_time == 0) { X509_VERIFY_PARAM_set_flags(ssl_ctx->param, X509_V_FLAG_NO_CHECK_TIME); } /* Disable any form of session caching by default */ SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_OFF); SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET); return (0); err: return (-1); }
int swClient_enable_ssl_encrypt(swClient *cli) { cli->ssl_context = swSSL_get_context(cli->ssl_method, cli->ssl_cert_file, cli->ssl_key_file); if (cli->ssl_context == NULL) { return SW_ERR; } cli->socket->ssl_send = 1; #if defined(SW_USE_HTTP2) && defined(SW_USE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10002000L if (cli->http2) { if (SSL_CTX_set_alpn_protos(cli->ssl_context, (const unsigned char *) "\x02h2", 3) < 0) { return SW_ERR; } } #endif return SW_OK; }
/* Create SSL_CTX. */ static SSL_CTX *create_ssl_ctx(void) { SSL_CTX *ssl_ctx; ssl_ctx = SSL_CTX_new(SSLv23_client_method()); if (!ssl_ctx) { errx(1, "Could not create SSL/TLS context: %s", ERR_error_string(ERR_get_error(), NULL)); } SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); #ifndef OPENSSL_NO_NEXTPROTONEG SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL); #endif /* !OPENSSL_NO_NEXTPROTONEG */ #if OPENSSL_VERSION_NUMBER >= 0x10002000L SSL_CTX_set_alpn_protos(ssl_ctx, (const unsigned char *)"\x02h2", 3); #endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ return ssl_ctx; }
/* * Configure callbacks and other properties that can't be set directly * in the server/client CONF. */ static void configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx, SSL_CTX *client_ctx, const SSL_TEST_CTX *test_ctx, CTX_DATA *server_ctx_data, CTX_DATA *server2_ctx_data, CTX_DATA *client_ctx_data) { unsigned char *ticket_keys; size_t ticket_key_len; switch (test_ctx->client_verify_callback) { case SSL_TEST_VERIFY_ACCEPT_ALL: SSL_CTX_set_cert_verify_callback(client_ctx, &verify_accept_cb, NULL); break; case SSL_TEST_VERIFY_REJECT_ALL: SSL_CTX_set_cert_verify_callback(client_ctx, &verify_reject_cb, NULL); break; default: break; } /* link the two contexts for SNI purposes */ switch (test_ctx->servername_callback) { case SSL_TEST_SERVERNAME_IGNORE_MISMATCH: SSL_CTX_set_tlsext_servername_callback(server_ctx, servername_ignore_cb); SSL_CTX_set_tlsext_servername_arg(server_ctx, server2_ctx); break; case SSL_TEST_SERVERNAME_REJECT_MISMATCH: SSL_CTX_set_tlsext_servername_callback(server_ctx, servername_reject_cb); SSL_CTX_set_tlsext_servername_arg(server_ctx, server2_ctx); break; default: break; } /* * The initial_ctx/session_ctx always handles the encrypt/decrypt of the * session ticket. This ticket_key callback is assigned to the second * session (assigned via SNI), and should never be invoked */ if (server2_ctx != NULL) SSL_CTX_set_tlsext_ticket_key_cb(server2_ctx, do_not_call_session_ticket_cb); if (test_ctx->session_ticket_expected == SSL_TEST_SESSION_TICKET_BROKEN) { SSL_CTX_set_tlsext_ticket_key_cb(server_ctx, broken_session_ticket_cb); } if (test_ctx->server_npn_protocols != NULL) { parse_protos(test_ctx->server_npn_protocols, &server_ctx_data->npn_protocols, &server_ctx_data->npn_protocols_len); SSL_CTX_set_next_protos_advertised_cb(server_ctx, server_npn_cb, server_ctx_data); } if (test_ctx->server2_npn_protocols != NULL) { parse_protos(test_ctx->server2_npn_protocols, &server2_ctx_data->npn_protocols, &server2_ctx_data->npn_protocols_len); OPENSSL_assert(server2_ctx != NULL); SSL_CTX_set_next_protos_advertised_cb(server2_ctx, server_npn_cb, server2_ctx_data); } if (test_ctx->client_npn_protocols != NULL) { parse_protos(test_ctx->client_npn_protocols, &client_ctx_data->npn_protocols, &client_ctx_data->npn_protocols_len); SSL_CTX_set_next_proto_select_cb(client_ctx, client_npn_cb, client_ctx_data); } if (test_ctx->server_alpn_protocols != NULL) { parse_protos(test_ctx->server_alpn_protocols, &server_ctx_data->alpn_protocols, &server_ctx_data->alpn_protocols_len); SSL_CTX_set_alpn_select_cb(server_ctx, server_alpn_cb, server_ctx_data); } if (test_ctx->server2_alpn_protocols != NULL) { OPENSSL_assert(server2_ctx != NULL); parse_protos(test_ctx->server2_alpn_protocols, &server2_ctx_data->alpn_protocols, &server2_ctx_data->alpn_protocols_len); SSL_CTX_set_alpn_select_cb(server2_ctx, server_alpn_cb, server2_ctx_data); } if (test_ctx->client_alpn_protocols != NULL) { unsigned char *alpn_protos = NULL; size_t alpn_protos_len; parse_protos(test_ctx->client_alpn_protocols, &alpn_protos, &alpn_protos_len); /* Reversed return value convention... */ OPENSSL_assert(SSL_CTX_set_alpn_protos(client_ctx, alpn_protos, alpn_protos_len) == 0); OPENSSL_free(alpn_protos); } /* * Use fixed session ticket keys so that we can decrypt a ticket created with * one CTX in another CTX. Don't address server2 for the moment. */ ticket_key_len = SSL_CTX_set_tlsext_ticket_keys(server_ctx, NULL, 0); ticket_keys = OPENSSL_zalloc(ticket_key_len); OPENSSL_assert(ticket_keys != NULL); OPENSSL_assert(SSL_CTX_set_tlsext_ticket_keys(server_ctx, ticket_keys, ticket_key_len) == 1); OPENSSL_free(ticket_keys); }
int s_client_main(int argc, char **argv) { unsigned int off = 0, clr = 0; SSL *con = NULL; int s, k, state = 0, af = AF_UNSPEC; char *cbuf = NULL, *sbuf = NULL, *mbuf = NULL; int cbuf_len, cbuf_off; int sbuf_len, sbuf_off; char *port = PORT_STR; int full_log = 1; char *host = SSL_HOST_NAME; char *xmpphost = NULL; char *proxy = NULL, *connect = NULL; char *cert_file = NULL, *key_file = NULL; int cert_format = FORMAT_PEM, key_format = FORMAT_PEM; char *passarg = NULL, *pass = NULL; X509 *cert = NULL; EVP_PKEY *key = NULL; char *CApath = NULL, *CAfile = NULL, *cipher = NULL; int reconnect = 0, badop = 0, verify = SSL_VERIFY_NONE, bugs = 0; int crlf = 0; int write_tty, read_tty, write_ssl, read_ssl, tty_on, ssl_pending; SSL_CTX *ctx = NULL; int ret = 1, in_init = 1, i, nbio_test = 0; int starttls_proto = PROTO_OFF; int prexit = 0; X509_VERIFY_PARAM *vpm = NULL; int badarg = 0; const SSL_METHOD *meth = NULL; int socket_type = SOCK_STREAM; BIO *sbio; int mbuf_len = 0; struct timeval timeout; const char *errstr = NULL; char *servername = NULL; tlsextctx tlsextcbp = {NULL, 0}; const char *next_proto_neg_in = NULL; const char *alpn_in = NULL; char *sess_in = NULL; char *sess_out = NULL; struct sockaddr peer; int peerlen = sizeof(peer); int enable_timeouts = 0; long socket_mtu = 0; meth = SSLv23_client_method(); c_Pause = 0; c_quiet = 0; c_ign_eof = 0; c_debug = 0; c_msg = 0; c_showcerts = 0; if (((cbuf = malloc(BUFSIZZ)) == NULL) || ((sbuf = malloc(BUFSIZZ)) == NULL) || ((mbuf = malloc(BUFSIZZ + 1)) == NULL)) { /* NUL byte */ BIO_printf(bio_err, "out of memory\n"); goto end; } verify_depth = 0; c_nbio = 0; argc--; argv++; while (argc >= 1) { if (strcmp(*argv, "-host") == 0) { if (--argc < 1) goto bad; host = *(++argv); } else if (strcmp(*argv, "-port") == 0) { if (--argc < 1) goto bad; port = *(++argv); if (port == NULL || *port == '\0') goto bad; } else if (strcmp(*argv, "-connect") == 0) { if (--argc < 1) goto bad; connect = *(++argv); } else if (strcmp(*argv, "-proxy") == 0) { if (--argc < 1) goto bad; proxy = *(++argv); } else if (strcmp(*argv,"-xmpphost") == 0) { if (--argc < 1) goto bad; xmpphost= *(++argv); } else if (strcmp(*argv, "-verify") == 0) { verify = SSL_VERIFY_PEER; if (--argc < 1) goto bad; verify_depth = strtonum(*(++argv), 0, INT_MAX, &errstr); if (errstr) goto bad; BIO_printf(bio_err, "verify depth is %d\n", verify_depth); } else if (strcmp(*argv, "-cert") == 0) { if (--argc < 1) goto bad; cert_file = *(++argv); } else if (strcmp(*argv, "-sess_out") == 0) { if (--argc < 1) goto bad; sess_out = *(++argv); } else if (strcmp(*argv, "-sess_in") == 0) { if (--argc < 1) goto bad; sess_in = *(++argv); } else if (strcmp(*argv, "-certform") == 0) { if (--argc < 1) goto bad; cert_format = str2fmt(*(++argv)); } else if (args_verify(&argv, &argc, &badarg, bio_err, &vpm)) { if (badarg) goto bad; continue; } else if (strcmp(*argv, "-verify_return_error") == 0) verify_return_error = 1; else if (strcmp(*argv, "-prexit") == 0) prexit = 1; else if (strcmp(*argv, "-crlf") == 0) crlf = 1; else if (strcmp(*argv, "-quiet") == 0) { c_quiet = 1; c_ign_eof = 1; } else if (strcmp(*argv, "-ign_eof") == 0) c_ign_eof = 1; else if (strcmp(*argv, "-no_ign_eof") == 0) c_ign_eof = 0; else if (strcmp(*argv, "-pause") == 0) c_Pause = 1; else if (strcmp(*argv, "-debug") == 0) c_debug = 1; else if (strcmp(*argv, "-tlsextdebug") == 0) c_tlsextdebug = 1; else if (strcmp(*argv, "-status") == 0) c_status_req = 1; else if (strcmp(*argv, "-msg") == 0) c_msg = 1; else if (strcmp(*argv, "-showcerts") == 0) c_showcerts = 1; else if (strcmp(*argv, "-nbio_test") == 0) nbio_test = 1; else if (strcmp(*argv, "-state") == 0) state = 1; else if (strcmp(*argv, "-tls1_2") == 0) meth = TLSv1_2_client_method(); else if (strcmp(*argv, "-tls1_1") == 0) meth = TLSv1_1_client_method(); else if (strcmp(*argv, "-tls1") == 0) meth = TLSv1_client_method(); #ifndef OPENSSL_NO_DTLS1 else if (strcmp(*argv, "-dtls1") == 0) { meth = DTLSv1_client_method(); socket_type = SOCK_DGRAM; } else if (strcmp(*argv, "-timeout") == 0) enable_timeouts = 1; else if (strcmp(*argv, "-mtu") == 0) { if (--argc < 1) goto bad; socket_mtu = strtonum(*(++argv), 0, LONG_MAX, &errstr); if (errstr) goto bad; } #endif else if (strcmp(*argv, "-bugs") == 0) bugs = 1; else if (strcmp(*argv, "-keyform") == 0) { if (--argc < 1) goto bad; key_format = str2fmt(*(++argv)); } else if (strcmp(*argv, "-pass") == 0) { if (--argc < 1) goto bad; passarg = *(++argv); } else if (strcmp(*argv, "-key") == 0) { if (--argc < 1) goto bad; key_file = *(++argv); } else if (strcmp(*argv, "-reconnect") == 0) { reconnect = 5; } else if (strcmp(*argv, "-CApath") == 0) { if (--argc < 1) goto bad; CApath = *(++argv); } else if (strcmp(*argv, "-CAfile") == 0) { if (--argc < 1) goto bad; CAfile = *(++argv); } else if (strcmp(*argv, "-no_tls1_2") == 0) off |= SSL_OP_NO_TLSv1_2; else if (strcmp(*argv, "-no_tls1_1") == 0) off |= SSL_OP_NO_TLSv1_1; else if (strcmp(*argv, "-no_tls1") == 0) off |= SSL_OP_NO_TLSv1; else if (strcmp(*argv, "-no_ssl3") == 0) off |= SSL_OP_NO_SSLv3; else if (strcmp(*argv, "-no_ssl2") == 0) off |= SSL_OP_NO_SSLv2; else if (strcmp(*argv, "-no_comp") == 0) { off |= SSL_OP_NO_COMPRESSION; } else if (strcmp(*argv, "-no_ticket") == 0) { off |= SSL_OP_NO_TICKET; } else if (strcmp(*argv, "-nextprotoneg") == 0) { if (--argc < 1) goto bad; next_proto_neg_in = *(++argv); } else if (strcmp(*argv, "-alpn") == 0) { if (--argc < 1) goto bad; alpn_in = *(++argv); } else if (strcmp(*argv, "-serverpref") == 0) off |= SSL_OP_CIPHER_SERVER_PREFERENCE; else if (strcmp(*argv, "-legacy_renegotiation") == 0) ; /* no-op */ else if (strcmp(*argv, "-legacy_server_connect") == 0) { off |= SSL_OP_LEGACY_SERVER_CONNECT; } else if (strcmp(*argv, "-no_legacy_server_connect") == 0) { clr |= SSL_OP_LEGACY_SERVER_CONNECT; } else if (strcmp(*argv, "-cipher") == 0) { if (--argc < 1) goto bad; cipher = *(++argv); } else if (strcmp(*argv, "-nbio") == 0) { c_nbio = 1; } else if (strcmp(*argv, "-starttls") == 0) { if (--argc < 1) goto bad; ++argv; if (strcmp(*argv, "smtp") == 0) starttls_proto = PROTO_SMTP; else if (strcmp(*argv, "lmtp") == 0) starttls_proto = PROTO_LMTP; else if (strcmp(*argv, "pop3") == 0) starttls_proto = PROTO_POP3; else if (strcmp(*argv, "imap") == 0) starttls_proto = PROTO_IMAP; else if (strcmp(*argv, "ftp") == 0) starttls_proto = PROTO_FTP; else if (strcmp(*argv, "xmpp") == 0) starttls_proto = PROTO_XMPP; else goto bad; } else if (strcmp(*argv, "-4") == 0) { af = AF_INET; } else if (strcmp(*argv, "-6") == 0) { af = AF_INET6; } else if (strcmp(*argv, "-servername") == 0) { if (--argc < 1) goto bad; servername = *(++argv); /* meth=TLSv1_client_method(); */ } #ifndef OPENSSL_NO_SRTP else if (strcmp(*argv, "-use_srtp") == 0) { if (--argc < 1) goto bad; srtp_profiles = *(++argv); } #endif else if (strcmp(*argv, "-keymatexport") == 0) { if (--argc < 1) goto bad; keymatexportlabel = *(++argv); } else if (strcmp(*argv, "-keymatexportlen") == 0) { if (--argc < 1) goto bad; keymatexportlen = strtonum(*(++argv), 1, INT_MAX, &errstr); if (errstr) goto bad; } else { BIO_printf(bio_err, "unknown option %s\n", *argv); badop = 1; break; } argc--; argv++; } if (proxy != NULL) { if (!extract_host_port(proxy, &host, NULL, &port)) goto bad; if (connect == NULL) connect = SSL_HOST_NAME; } else if (connect != NULL) { if (!extract_host_port(connect, &host, NULL, &port)) goto bad; } if (badop) { bad: if (errstr) BIO_printf(bio_err, "invalid argument %s: %s\n", *argv, errstr); else sc_usage(); goto end; } next_proto.status = -1; if (next_proto_neg_in) { next_proto.data = next_protos_parse(&next_proto.len, next_proto_neg_in); if (next_proto.data == NULL) { BIO_printf(bio_err, "Error parsing -nextprotoneg argument\n"); goto end; } } else next_proto.data = NULL; if (!app_passwd(bio_err, passarg, NULL, &pass, NULL)) { BIO_printf(bio_err, "Error getting password\n"); goto end; } if (key_file == NULL) key_file = cert_file; if (key_file) { key = load_key(bio_err, key_file, key_format, 0, pass, "client certificate private key file"); if (!key) { ERR_print_errors(bio_err); goto end; } } if (cert_file) { cert = load_cert(bio_err, cert_file, cert_format, NULL, "client certificate file"); if (!cert) { ERR_print_errors(bio_err); goto end; } } if (bio_c_out == NULL) { if (c_quiet && !c_debug && !c_msg) { bio_c_out = BIO_new(BIO_s_null()); } else { if (bio_c_out == NULL) bio_c_out = BIO_new_fp(stdout, BIO_NOCLOSE); } } ctx = SSL_CTX_new(meth); if (ctx == NULL) { ERR_print_errors(bio_err); goto end; } if (vpm) SSL_CTX_set1_param(ctx, vpm); #ifndef OPENSSL_NO_SRTP if (srtp_profiles != NULL) SSL_CTX_set_tlsext_use_srtp(ctx, srtp_profiles); #endif if (bugs) SSL_CTX_set_options(ctx, SSL_OP_ALL | off); else SSL_CTX_set_options(ctx, off); if (clr) SSL_CTX_clear_options(ctx, clr); /* * DTLS: partial reads end up discarding unread UDP bytes :-( Setting * read ahead solves this problem. */ if (socket_type == SOCK_DGRAM) SSL_CTX_set_read_ahead(ctx, 1); if (next_proto.data) SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &next_proto); if (alpn_in) { unsigned short alpn_len; unsigned char *alpn = next_protos_parse(&alpn_len, alpn_in); if (alpn == NULL) { BIO_printf(bio_err, "Error parsing -alpn argument\n"); goto end; } SSL_CTX_set_alpn_protos(ctx, alpn, alpn_len); free(alpn); } if (state) SSL_CTX_set_info_callback(ctx, apps_ssl_info_callback); if (cipher != NULL) if (!SSL_CTX_set_cipher_list(ctx, cipher)) { BIO_printf(bio_err, "error setting cipher list\n"); ERR_print_errors(bio_err); goto end; } SSL_CTX_set_verify(ctx, verify, verify_callback); if (!set_cert_key_stuff(ctx, cert, key)) goto end; if ((!SSL_CTX_load_verify_locations(ctx, CAfile, CApath)) || (!SSL_CTX_set_default_verify_paths(ctx))) { /* * BIO_printf(bio_err,"error setting default verify * locations\n"); */ ERR_print_errors(bio_err); /* goto end; */ } if (servername != NULL) { tlsextcbp.biodebug = bio_err; SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb); SSL_CTX_set_tlsext_servername_arg(ctx, &tlsextcbp); } con = SSL_new(ctx); if (sess_in) { SSL_SESSION *sess; BIO *stmp = BIO_new_file(sess_in, "r"); if (!stmp) { BIO_printf(bio_err, "Can't open session file %s\n", sess_in); ERR_print_errors(bio_err); goto end; } sess = PEM_read_bio_SSL_SESSION(stmp, NULL, 0, NULL); BIO_free(stmp); if (!sess) { BIO_printf(bio_err, "Can't open session file %s\n", sess_in); ERR_print_errors(bio_err); goto end; } SSL_set_session(con, sess); SSL_SESSION_free(sess); } if (servername != NULL) { if (!SSL_set_tlsext_host_name(con, servername)) { BIO_printf(bio_err, "Unable to set TLS servername extension.\n"); ERR_print_errors(bio_err); goto end; } } /* SSL_set_cipher_list(con,"RC4-MD5"); */ re_start: if (init_client(&s, host, port, socket_type, af) == 0) { BIO_printf(bio_err, "connect:errno=%d\n", errno); goto end; } BIO_printf(bio_c_out, "CONNECTED(%08X)\n", s); if (c_nbio) { if (!c_quiet) BIO_printf(bio_c_out, "turning on non blocking io\n"); if (!BIO_socket_nbio(s, 1)) { ERR_print_errors(bio_err); goto end; } } if (c_Pause & 0x01) SSL_set_debug(con, 1); if (SSL_version(con) == DTLS1_VERSION) { sbio = BIO_new_dgram(s, BIO_NOCLOSE); if (getsockname(s, &peer, (void *) &peerlen) < 0) { BIO_printf(bio_err, "getsockname:errno=%d\n", errno); shutdown(s, SHUT_RD); close(s); goto end; } (void) BIO_ctrl_set_connected(sbio, 1, &peer); if (enable_timeouts) { timeout.tv_sec = 0; timeout.tv_usec = DGRAM_RCV_TIMEOUT; BIO_ctrl(sbio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout); timeout.tv_sec = 0; timeout.tv_usec = DGRAM_SND_TIMEOUT; BIO_ctrl(sbio, BIO_CTRL_DGRAM_SET_SEND_TIMEOUT, 0, &timeout); } if (socket_mtu > 28) { SSL_set_options(con, SSL_OP_NO_QUERY_MTU); SSL_set_mtu(con, socket_mtu - 28); } else /* want to do MTU discovery */ BIO_ctrl(sbio, BIO_CTRL_DGRAM_MTU_DISCOVER, 0, NULL); } else sbio = BIO_new_socket(s, BIO_NOCLOSE); if (nbio_test) { BIO *test; test = BIO_new(BIO_f_nbio_test()); sbio = BIO_push(test, sbio); } if (c_debug) { SSL_set_debug(con, 1); BIO_set_callback(sbio, bio_dump_callback); BIO_set_callback_arg(sbio, (char *) bio_c_out); } if (c_msg) { SSL_set_msg_callback(con, msg_cb); SSL_set_msg_callback_arg(con, bio_c_out); } if (c_tlsextdebug) { SSL_set_tlsext_debug_callback(con, tlsext_cb); SSL_set_tlsext_debug_arg(con, bio_c_out); } if (c_status_req) { SSL_set_tlsext_status_type(con, TLSEXT_STATUSTYPE_ocsp); SSL_CTX_set_tlsext_status_cb(ctx, ocsp_resp_cb); SSL_CTX_set_tlsext_status_arg(ctx, bio_c_out); } SSL_set_bio(con, sbio, sbio); SSL_set_connect_state(con); /* ok, lets connect */ read_tty = 1; write_tty = 0; tty_on = 0; read_ssl = 1; write_ssl = 1; cbuf_len = 0; cbuf_off = 0; sbuf_len = 0; sbuf_off = 0; /* This is an ugly hack that does a lot of assumptions */ /* * We do have to handle multi-line responses which may come in a * single packet or not. We therefore have to use BIO_gets() which * does need a buffering BIO. So during the initial chitchat we do * push a buffering BIO into the chain that is removed again later on * to not disturb the rest of the s_client operation. */ if (starttls_proto == PROTO_SMTP || starttls_proto == PROTO_LMTP) { int foundit = 0; BIO *fbio = BIO_new(BIO_f_buffer()); BIO_push(fbio, sbio); /* wait for multi-line response to end from SMTP */ do { mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); } while (mbuf_len > 3 && mbuf[3] == '-'); /* STARTTLS command requires EHLO... */ BIO_printf(fbio, "%cHLO openssl.client.net\r\n", starttls_proto == PROTO_SMTP ? 'E' : 'L'); (void) BIO_flush(fbio); /* wait for multi-line response to end EHLO SMTP response */ do { mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); if (strstr(mbuf, "STARTTLS")) foundit = 1; } while (mbuf_len > 3 && mbuf[3] == '-'); (void) BIO_flush(fbio); BIO_pop(fbio); BIO_free(fbio); if (!foundit) BIO_printf(bio_err, "didn't found starttls in server response," " try anyway...\n"); BIO_printf(sbio, "STARTTLS\r\n"); BIO_read(sbio, sbuf, BUFSIZZ); } else if (starttls_proto == PROTO_POP3) { mbuf_len = BIO_read(sbio, mbuf, BUFSIZZ); if (mbuf_len == -1) { BIO_printf(bio_err, "BIO_read failed\n"); goto end; } BIO_printf(sbio, "STLS\r\n"); BIO_read(sbio, sbuf, BUFSIZZ); } else if (starttls_proto == PROTO_IMAP) { int foundit = 0; BIO *fbio = BIO_new(BIO_f_buffer()); BIO_push(fbio, sbio); BIO_gets(fbio, mbuf, BUFSIZZ); /* STARTTLS command requires CAPABILITY... */ BIO_printf(fbio, ". CAPABILITY\r\n"); (void) BIO_flush(fbio); /* wait for multi-line CAPABILITY response */ do { mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); if (strstr(mbuf, "STARTTLS")) foundit = 1; } while (mbuf_len > 3 && mbuf[0] != '.'); (void) BIO_flush(fbio); BIO_pop(fbio); BIO_free(fbio); if (!foundit) BIO_printf(bio_err, "didn't found STARTTLS in server response," " try anyway...\n"); BIO_printf(sbio, ". STARTTLS\r\n"); BIO_read(sbio, sbuf, BUFSIZZ); } else if (starttls_proto == PROTO_FTP) { BIO *fbio = BIO_new(BIO_f_buffer()); BIO_push(fbio, sbio); /* wait for multi-line response to end from FTP */ do { mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); } while (mbuf_len > 3 && mbuf[3] == '-'); (void) BIO_flush(fbio); BIO_pop(fbio); BIO_free(fbio); BIO_printf(sbio, "AUTH TLS\r\n"); BIO_read(sbio, sbuf, BUFSIZZ); } else if (starttls_proto == PROTO_XMPP) { int seen = 0; BIO_printf(sbio, "<stream:stream " "xmlns:stream='http://etherx.jabber.org/streams' " "xmlns='jabber:client' to='%s' version='1.0'>", xmpphost ? xmpphost : host); seen = BIO_read(sbio, mbuf, BUFSIZZ); if (seen <= 0) goto shut; mbuf[seen] = 0; while (!strstr(mbuf, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'") && !strstr(mbuf, "<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"")) { seen = BIO_read(sbio, mbuf, BUFSIZZ); if (seen <= 0) goto shut; mbuf[seen] = 0; } BIO_printf(sbio, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"); seen = BIO_read(sbio, sbuf, BUFSIZZ); sbuf[seen] = 0; if (!strstr(sbuf, "<proceed")) goto shut; mbuf[0] = 0; } else if (proxy != NULL) { BIO_printf(sbio, "CONNECT %s HTTP/1.0\r\n\r\n", connect); mbuf_len = BIO_read(sbio, mbuf, BUFSIZZ); if (mbuf_len == -1) { BIO_printf(bio_err, "BIO_read failed\n"); goto end; } } for (;;) { struct pollfd pfd[3]; /* stdin, stdout, socket */ int ptimeout = -1; if ((SSL_version(con) == DTLS1_VERSION) && DTLSv1_get_timeout(con, &timeout)) ptimeout = timeout.tv_sec * 1000 + timeout.tv_usec / 1000; if (SSL_in_init(con) && !SSL_total_renegotiations(con)) { in_init = 1; tty_on = 0; } else { tty_on = 1; if (in_init) { in_init = 0; if (sess_out) { BIO *stmp = BIO_new_file(sess_out, "w"); if (stmp) { PEM_write_bio_SSL_SESSION(stmp, SSL_get_session(con)); BIO_free(stmp); } else BIO_printf(bio_err, "Error writing session file %s\n", sess_out); } print_stuff(bio_c_out, con, full_log); if (full_log > 0) full_log--; if (starttls_proto) { BIO_write(bio_err, mbuf, mbuf_len); /* We don't need to know any more */ starttls_proto = PROTO_OFF; } if (reconnect) { reconnect--; BIO_printf(bio_c_out, "drop connection and then reconnect\n"); SSL_shutdown(con); SSL_set_connect_state(con); shutdown(SSL_get_fd(con), SHUT_RD); close(SSL_get_fd(con)); goto re_start; } } } ssl_pending = read_ssl && SSL_pending(con); pfd[0].fd = -1; pfd[1].fd = -1; if (!ssl_pending) { if (tty_on) { if (read_tty) { pfd[0].fd = fileno(stdin); pfd[0].events = POLLIN; } if (write_tty) { pfd[1].fd = fileno(stdout); pfd[1].events = POLLOUT; } } pfd[2].fd = SSL_get_fd(con); pfd[2].events = 0; if (read_ssl) pfd[2].events |= POLLIN; if (write_ssl) pfd[2].events |= POLLOUT; /* printf("mode tty(%d %d%d) ssl(%d%d)\n", tty_on,read_tty,write_tty,read_ssl,write_ssl);*/ i = poll(pfd, 3, ptimeout); if (i < 0) { BIO_printf(bio_err, "bad select %d\n", errno); goto shut; /* goto end; */ } } if ((SSL_version(con) == DTLS1_VERSION) && DTLSv1_handle_timeout(con) > 0) { BIO_printf(bio_err, "TIMEOUT occured\n"); } if (!ssl_pending && (pfd[2].revents & (POLLOUT|POLLERR|POLLNVAL))) { if (pfd[2].revents & (POLLERR|POLLNVAL)) { BIO_printf(bio_err, "poll error"); goto shut; } k = SSL_write(con, &(cbuf[cbuf_off]), (unsigned int) cbuf_len); switch (SSL_get_error(con, k)) { case SSL_ERROR_NONE: cbuf_off += k; cbuf_len -= k; if (k <= 0) goto end; /* we have done a write(con,NULL,0); */ if (cbuf_len <= 0) { read_tty = 1; write_ssl = 0; } else { /* if (cbuf_len > 0) */ read_tty = 0; write_ssl = 1; } break; case SSL_ERROR_WANT_WRITE: BIO_printf(bio_c_out, "write W BLOCK\n"); write_ssl = 1; read_tty = 0; break; case SSL_ERROR_WANT_READ: BIO_printf(bio_c_out, "write R BLOCK\n"); write_tty = 0; read_ssl = 1; write_ssl = 0; break; case SSL_ERROR_WANT_X509_LOOKUP: BIO_printf(bio_c_out, "write X BLOCK\n"); break; case SSL_ERROR_ZERO_RETURN: if (cbuf_len != 0) { BIO_printf(bio_c_out, "shutdown\n"); ret = 0; goto shut; } else { read_tty = 1; write_ssl = 0; break; } case SSL_ERROR_SYSCALL: if ((k != 0) || (cbuf_len != 0)) { BIO_printf(bio_err, "write:errno=%d\n", errno); goto shut; } else { read_tty = 1; write_ssl = 0; } break; case SSL_ERROR_SSL: ERR_print_errors(bio_err); goto shut; } } else if (!ssl_pending && (pfd[1].revents & (POLLOUT|POLLERR|POLLNVAL))) { if (pfd[1].revents & (POLLERR|POLLNVAL)) { BIO_printf(bio_err, "poll error"); goto shut; } i = write(fileno(stdout), &(sbuf[sbuf_off]), sbuf_len); if (i <= 0) { BIO_printf(bio_c_out, "DONE\n"); ret = 0; goto shut; /* goto end; */ } sbuf_len -= i; sbuf_off += i; if (sbuf_len <= 0) { read_ssl = 1; write_tty = 0; } } else if (ssl_pending || (pfd[2].revents & (POLLIN|POLLHUP))) { #ifdef RENEG { static int iiii; if (++iiii == 52) { SSL_renegotiate(con); iiii = 0; } } #endif k = SSL_read(con, sbuf, 1024 /* BUFSIZZ */ ); switch (SSL_get_error(con, k)) { case SSL_ERROR_NONE: if (k <= 0) goto end; sbuf_off = 0; sbuf_len = k; read_ssl = 0; write_tty = 1; break; case SSL_ERROR_WANT_WRITE: BIO_printf(bio_c_out, "read W BLOCK\n"); write_ssl = 1; read_tty = 0; break; case SSL_ERROR_WANT_READ: BIO_printf(bio_c_out, "read R BLOCK\n"); write_tty = 0; read_ssl = 1; if ((read_tty == 0) && (write_ssl == 0)) write_ssl = 1; break; case SSL_ERROR_WANT_X509_LOOKUP: BIO_printf(bio_c_out, "read X BLOCK\n"); break; case SSL_ERROR_SYSCALL: ret = errno; BIO_printf(bio_err, "read:errno=%d\n", ret); goto shut; case SSL_ERROR_ZERO_RETURN: BIO_printf(bio_c_out, "closed\n"); ret = 0; goto shut; case SSL_ERROR_SSL: ERR_print_errors(bio_err); goto shut; /* break; */ } } else if (pfd[0].revents) { if (pfd[0].revents & (POLLERR|POLLNVAL)) { BIO_printf(bio_err, "poll error"); goto shut; } if (crlf) { int j, lf_num; i = read(fileno(stdin), cbuf, BUFSIZZ / 2); lf_num = 0; /* both loops are skipped when i <= 0 */ for (j = 0; j < i; j++) if (cbuf[j] == '\n') lf_num++; for (j = i - 1; j >= 0; j--) { cbuf[j + lf_num] = cbuf[j]; if (cbuf[j] == '\n') { lf_num--; i++; cbuf[j + lf_num] = '\r'; } } assert(lf_num == 0); } else i = read(fileno(stdin), cbuf, BUFSIZZ); if ((!c_ign_eof) && ((i <= 0) || (cbuf[0] == 'Q'))) { BIO_printf(bio_err, "DONE\n"); ret = 0; goto shut; } if ((!c_ign_eof) && (cbuf[0] == 'R')) { BIO_printf(bio_err, "RENEGOTIATING\n"); SSL_renegotiate(con); cbuf_len = 0; } else { cbuf_len = i; cbuf_off = 0; } write_ssl = 1; read_tty = 0; } } ret = 0; shut: if (in_init) print_stuff(bio_c_out, con, full_log); SSL_shutdown(con); shutdown(SSL_get_fd(con), SHUT_RD); close(SSL_get_fd(con)); end: if (con != NULL) { if (prexit != 0) print_stuff(bio_c_out, con, 1); SSL_free(con); } free(next_proto.data); if (ctx != NULL) SSL_CTX_free(ctx); if (cert) X509_free(cert); if (key) EVP_PKEY_free(key); free(pass); if (vpm) X509_VERIFY_PARAM_free(vpm); if (cbuf != NULL) { explicit_bzero(cbuf, BUFSIZZ); free(cbuf); } if (sbuf != NULL) { explicit_bzero(sbuf, BUFSIZZ); free(sbuf); } if (mbuf != NULL) { explicit_bzero(mbuf, BUFSIZZ); free(mbuf); } if (bio_c_out != NULL) { BIO_free(bio_c_out); bio_c_out = NULL; } return (ret); }
static int test_client_hello(int currtest) { SSL_CTX *ctx; SSL *con = NULL; BIO *rbio; BIO *wbio; long len; unsigned char *data; PACKET pkt = {0}, pkt2 = {0}, pkt3 = {0}; char *dummytick = "Hello World!"; unsigned int type = 0; int testresult = 0; size_t msglen; BIO *sessbio = NULL; SSL_SESSION *sess = NULL; #ifdef OPENSSL_NO_TLS1_3 if (currtest == TEST_ADD_PADDING_AND_PSK) return 1; #endif /* * For each test set up an SSL_CTX and SSL and see what ClientHello gets * produced when we try to connect */ ctx = SSL_CTX_new(TLS_method()); if (!TEST_ptr(ctx)) goto end; switch(currtest) { case TEST_SET_SESSION_TICK_DATA_VER_NEG: /* Testing for session tickets <= TLS1.2; not relevant for 1.3 */ if (!TEST_true(SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION))) goto end; break; case TEST_ADD_PADDING_AND_PSK: case TEST_ADD_PADDING: case TEST_PADDING_NOT_NEEDED: SSL_CTX_set_options(ctx, SSL_OP_TLSEXT_PADDING); /* * Add lots of ciphersuites so that the ClientHello is at least * F5_WORKAROUND_MIN_MSG_LEN bytes long - meaning padding will be * needed. Also add some dummy ALPN protocols in case we still don't * have enough. */ if (currtest == TEST_ADD_PADDING && (!TEST_true(SSL_CTX_set_cipher_list(ctx, "ALL")) || !TEST_false(SSL_CTX_set_alpn_protos(ctx, (unsigned char *)alpn_prots, sizeof(alpn_prots) - 1)))) goto end; break; default: goto end; } con = SSL_new(ctx); if (!TEST_ptr(con)) goto end; if (currtest == TEST_ADD_PADDING_AND_PSK) { sessbio = BIO_new_file(sessionfile, "r"); if (!TEST_ptr(sessbio)) { TEST_info("Unable to open session.pem"); goto end; } sess = PEM_read_bio_SSL_SESSION(sessbio, NULL, NULL, NULL); if (!TEST_ptr(sess)) { TEST_info("Unable to load SSL_SESSION"); goto end; } /* * We reset the creation time so that we don't discard the session as * too old. */ if (!TEST_true(SSL_SESSION_set_time(sess, time(NULL))) || !TEST_true(SSL_set_session(con, sess))) goto end; } rbio = BIO_new(BIO_s_mem()); wbio = BIO_new(BIO_s_mem()); if (!TEST_ptr(rbio)|| !TEST_ptr(wbio)) { BIO_free(rbio); BIO_free(wbio); goto end; } SSL_set_bio(con, rbio, wbio); SSL_set_connect_state(con); if (currtest == TEST_SET_SESSION_TICK_DATA_VER_NEG) { if (!TEST_true(SSL_set_session_ticket_ext(con, dummytick, strlen(dummytick)))) goto end; } if (!TEST_int_le(SSL_connect(con), 0)) { /* This shouldn't succeed because we don't have a server! */ goto end; } len = BIO_get_mem_data(wbio, (char **)&data); if (!TEST_true(PACKET_buf_init(&pkt, data, len)) /* Skip the record header */ || !PACKET_forward(&pkt, SSL3_RT_HEADER_LENGTH)) goto end; msglen = PACKET_remaining(&pkt); /* Skip the handshake message header */ if (!TEST_true(PACKET_forward(&pkt, SSL3_HM_HEADER_LENGTH)) /* Skip client version and random */ || !TEST_true(PACKET_forward(&pkt, CLIENT_VERSION_LEN + SSL3_RANDOM_SIZE)) /* Skip session id */ || !TEST_true(PACKET_get_length_prefixed_1(&pkt, &pkt2)) /* Skip ciphers */ || !TEST_true(PACKET_get_length_prefixed_2(&pkt, &pkt2)) /* Skip compression */ || !TEST_true(PACKET_get_length_prefixed_1(&pkt, &pkt2)) /* Extensions len */ || !TEST_true(PACKET_as_length_prefixed_2(&pkt, &pkt2))) goto end; /* Loop through all extensions */ while (PACKET_remaining(&pkt2)) { if (!TEST_true(PACKET_get_net_2(&pkt2, &type)) || !TEST_true(PACKET_get_length_prefixed_2(&pkt2, &pkt3))) goto end; if (type == TLSEXT_TYPE_session_ticket) { if (currtest == TEST_SET_SESSION_TICK_DATA_VER_NEG) { if (TEST_true(PACKET_equal(&pkt3, dummytick, strlen(dummytick)))) { /* Ticket data is as we expected */ testresult = 1; } goto end; } } if (type == TLSEXT_TYPE_padding) { if (!TEST_false(currtest == TEST_PADDING_NOT_NEEDED)) goto end; else if (TEST_true(currtest == TEST_ADD_PADDING || currtest == TEST_ADD_PADDING_AND_PSK)) testresult = TEST_true(msglen == F5_WORKAROUND_MAX_MSG_LEN); } } if (currtest == TEST_PADDING_NOT_NEEDED) testresult = 1; end: SSL_free(con); SSL_CTX_free(ctx); SSL_SESSION_free(sess); BIO_free(sessbio); return testresult; }
/* * Configure callbacks and other properties that can't be set directly * in the server/client CONF. */ static void configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx, SSL_CTX *client_ctx, const SSL_TEST_CTX *test, const SSL_TEST_EXTRA_CONF *extra, CTX_DATA *server_ctx_data, CTX_DATA *server2_ctx_data, CTX_DATA *client_ctx_data) { unsigned char *ticket_keys; size_t ticket_key_len; TEST_check(SSL_CTX_set_max_send_fragment(server_ctx, test->max_fragment_size) == 1); if (server2_ctx != NULL) { TEST_check(SSL_CTX_set_max_send_fragment(server2_ctx, test->max_fragment_size) == 1); } TEST_check(SSL_CTX_set_max_send_fragment(client_ctx, test->max_fragment_size) == 1); switch (extra->client.verify_callback) { case SSL_TEST_VERIFY_ACCEPT_ALL: SSL_CTX_set_cert_verify_callback(client_ctx, &verify_accept_cb, NULL); break; case SSL_TEST_VERIFY_REJECT_ALL: SSL_CTX_set_cert_verify_callback(client_ctx, &verify_reject_cb, NULL); break; default: break; } /* link the two contexts for SNI purposes */ switch (extra->server.servername_callback) { case SSL_TEST_SERVERNAME_IGNORE_MISMATCH: SSL_CTX_set_tlsext_servername_callback(server_ctx, servername_ignore_cb); SSL_CTX_set_tlsext_servername_arg(server_ctx, server2_ctx); break; case SSL_TEST_SERVERNAME_REJECT_MISMATCH: SSL_CTX_set_tlsext_servername_callback(server_ctx, servername_reject_cb); SSL_CTX_set_tlsext_servername_arg(server_ctx, server2_ctx); break; default: break; } /* * The initial_ctx/session_ctx always handles the encrypt/decrypt of the * session ticket. This ticket_key callback is assigned to the second * session (assigned via SNI), and should never be invoked */ if (server2_ctx != NULL) SSL_CTX_set_tlsext_ticket_key_cb(server2_ctx, do_not_call_session_ticket_cb); if (extra->server.broken_session_ticket) { SSL_CTX_set_tlsext_ticket_key_cb(server_ctx, broken_session_ticket_cb); } #ifndef OPENSSL_NO_NEXTPROTONEG if (extra->server.npn_protocols != NULL) { parse_protos(extra->server.npn_protocols, &server_ctx_data->npn_protocols, &server_ctx_data->npn_protocols_len); SSL_CTX_set_next_protos_advertised_cb(server_ctx, server_npn_cb, server_ctx_data); } if (extra->server2.npn_protocols != NULL) { parse_protos(extra->server2.npn_protocols, &server2_ctx_data->npn_protocols, &server2_ctx_data->npn_protocols_len); TEST_check(server2_ctx != NULL); SSL_CTX_set_next_protos_advertised_cb(server2_ctx, server_npn_cb, server2_ctx_data); } if (extra->client.npn_protocols != NULL) { parse_protos(extra->client.npn_protocols, &client_ctx_data->npn_protocols, &client_ctx_data->npn_protocols_len); SSL_CTX_set_next_proto_select_cb(client_ctx, client_npn_cb, client_ctx_data); } #endif if (extra->server.alpn_protocols != NULL) { parse_protos(extra->server.alpn_protocols, &server_ctx_data->alpn_protocols, &server_ctx_data->alpn_protocols_len); SSL_CTX_set_alpn_select_cb(server_ctx, server_alpn_cb, server_ctx_data); } if (extra->server2.alpn_protocols != NULL) { TEST_check(server2_ctx != NULL); parse_protos(extra->server2.alpn_protocols, &server2_ctx_data->alpn_protocols, &server2_ctx_data->alpn_protocols_len); SSL_CTX_set_alpn_select_cb(server2_ctx, server_alpn_cb, server2_ctx_data); } if (extra->client.alpn_protocols != NULL) { unsigned char *alpn_protos = NULL; size_t alpn_protos_len; parse_protos(extra->client.alpn_protocols, &alpn_protos, &alpn_protos_len); /* Reversed return value convention... */ TEST_check(SSL_CTX_set_alpn_protos(client_ctx, alpn_protos, alpn_protos_len) == 0); OPENSSL_free(alpn_protos); } /* * Use fixed session ticket keys so that we can decrypt a ticket created with * one CTX in another CTX. Don't address server2 for the moment. */ ticket_key_len = SSL_CTX_set_tlsext_ticket_keys(server_ctx, NULL, 0); ticket_keys = OPENSSL_zalloc(ticket_key_len); TEST_check(ticket_keys != NULL); TEST_check(SSL_CTX_set_tlsext_ticket_keys(server_ctx, ticket_keys, ticket_key_len) == 1); OPENSSL_free(ticket_keys); /* The default log list includes EC keys, so CT can't work without EC. */ #if !defined(OPENSSL_NO_CT) && !defined(OPENSSL_NO_EC) TEST_check(SSL_CTX_set_default_ctlog_list_file(client_ctx)); switch (extra->client.ct_validation) { case SSL_TEST_CT_VALIDATION_PERMISSIVE: TEST_check(SSL_CTX_enable_ct(client_ctx, SSL_CT_VALIDATION_PERMISSIVE)); break; case SSL_TEST_CT_VALIDATION_STRICT: TEST_check(SSL_CTX_enable_ct(client_ctx, SSL_CT_VALIDATION_STRICT)); break; case SSL_TEST_CT_VALIDATION_NONE: break; } #endif }
// This test launches a gRPC server on a separate thread and then establishes a // TLS handshake via a minimal TLS client. The TLS client has configurable (via // alpn_list) ALPN settings and can probe at the supported ALPN preferences // using this (via alpn_expected). static bool server_ssl_test(const char *alpn_list[], unsigned int alpn_list_len, const char *alpn_expected) { bool success = true; grpc_init(); int port = grpc_pick_unused_port_or_die(); gpr_event_init(&client_handshake_complete); // Launch the gRPC server thread. gpr_thd_options thdopt = gpr_thd_options_default(); gpr_thd_id thdid; gpr_thd_options_set_joinable(&thdopt); GPR_ASSERT(gpr_thd_new(&thdid, server_thread, &port, &thdopt)); SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); const SSL_METHOD *method = TLSv1_2_client_method(); SSL_CTX *ctx = SSL_CTX_new(method); if (!ctx) { perror("Unable to create SSL context"); ERR_print_errors_fp(stderr); abort(); } // Load key pair. if (SSL_CTX_use_certificate_file(ctx, SSL_CERT_PATH, SSL_FILETYPE_PEM) < 0) { ERR_print_errors_fp(stderr); abort(); } if (SSL_CTX_use_PrivateKey_file(ctx, SSL_KEY_PATH, SSL_FILETYPE_PEM) < 0) { ERR_print_errors_fp(stderr); abort(); } // Set the cipher list to match the one expressed in // src/core/lib/tsi/ssl_transport_security.c. const char *cipher_list = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-" "SHA384:ECDHE-RSA-AES256-GCM-SHA384"; if (!SSL_CTX_set_cipher_list(ctx, cipher_list)) { ERR_print_errors_fp(stderr); gpr_log(GPR_ERROR, "Couldn't set server cipher list."); abort(); } // Configure ALPN list the client will send to the server. This must match the // wire format, see documentation for SSL_CTX_set_alpn_protos. unsigned int alpn_protos_len = alpn_list_len; for (unsigned int i = 0; i < alpn_list_len; ++i) { alpn_protos_len += (unsigned int)strlen(alpn_list[i]); } unsigned char *alpn_protos = gpr_malloc(alpn_protos_len); unsigned char *p = alpn_protos; for (unsigned int i = 0; i < alpn_list_len; ++i) { const uint8_t len = (uint8_t)strlen(alpn_list[i]); *p++ = len; memcpy(p, alpn_list[i], len); p += len; } GPR_ASSERT(SSL_CTX_set_alpn_protos(ctx, alpn_protos, alpn_protos_len) == 0); // Try and connect to server. We allow a bounded number of retries as we might // be racing with the server setup on its separate thread. int retries = 10; int sock = -1; while (sock == -1 && retries-- > 0) { sock = create_socket(port); if (sock < 0) { sleep(1); } } GPR_ASSERT(sock > 0); gpr_log(GPR_INFO, "Connected to server on port %d", port); // Establish a SSL* and connect at SSL layer. SSL *ssl = SSL_new(ctx); GPR_ASSERT(ssl); SSL_set_fd(ssl, sock); if (SSL_connect(ssl) <= 0) { ERR_print_errors_fp(stderr); gpr_log(GPR_ERROR, "Handshake failed."); success = false; } else { gpr_log(GPR_INFO, "Handshake successful."); // Validate ALPN preferred by server matches alpn_expected. const unsigned char *alpn_selected; unsigned int alpn_selected_len; SSL_get0_alpn_selected(ssl, &alpn_selected, &alpn_selected_len); if (strlen(alpn_expected) != alpn_selected_len || strncmp((const char *)alpn_selected, alpn_expected, alpn_selected_len) != 0) { gpr_log(GPR_ERROR, "Unexpected ALPN protocol preference"); success = false; } } gpr_event_set(&client_handshake_complete, &client_handshake_complete); SSL_free(ssl); gpr_free(alpn_protos); SSL_CTX_free(ctx); EVP_cleanup(); close(sock); gpr_thd_join(thdid); grpc_shutdown(); return success; }