static void init_ssl(CLI * c) { int i, err; SSL_SESSION *old_session; c->ssl = SSL_new(c->opt->ctx); if (!c->ssl) { sslerror("SSL_new"); longjmp(c->err, 1); } SSL_set_ex_data(c->ssl, cli_index, c); if (c->opt->option.client) { if (c->opt->session) { SSL_set_session(c->ssl, c->opt->session); } SSL_set_fd(c->ssl, c->remote_fd.fd); SSL_set_connect_state(c->ssl); } else { if (c->local_rfd.fd == c->local_wfd.fd) SSL_set_fd(c->ssl, c->local_rfd.fd); else { SSL_set_rfd(c->ssl, c->local_rfd.fd); SSL_set_wfd(c->ssl, c->local_wfd.fd); } SSL_set_accept_state(c->ssl); } if (c->opt->option.client) { c->sock_rfd = &(c->local_rfd); c->sock_wfd = &(c->local_wfd); c->ssl_rfd = c->ssl_wfd = &(c->remote_fd); } else { c->sock_rfd = c->sock_wfd = &(c->remote_fd); c->ssl_rfd = &(c->local_rfd); c->ssl_wfd = &(c->local_wfd); } while (1) { if (c->opt->option.client) i = SSL_connect(c->ssl); else i = SSL_accept(c->ssl); err = SSL_get_error(c->ssl, i); if (err == SSL_ERROR_NONE) break; if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { s_poll_init(c->fds); s_poll_add(c->fds, c->ssl_rfd->fd, err == SSL_ERROR_WANT_READ, err == SSL_ERROR_WANT_WRITE); switch (s_poll_wait(c->fds, c->opt->timeout_busy, 0)) { case -1: sockerror("init_ssl: s_poll_wait"); longjmp(c->err, 1); case 0: s_log(LOG_INFO, "init_ssl: s_poll_wait:" " TIMEOUTbusy exceeded: sending reset"); longjmp(c->err, 1); case 1: break; default: s_log(LOG_ERR, "init_ssl: s_poll_wait: unknown result"); longjmp(c->err, 1); } continue; } if (err == SSL_ERROR_SYSCALL) { switch (get_last_socket_error()) { case S_EINTR: case S_EWOULDBLOCK: #if S_EAGAIN!=S_EWOULDBLOCK case S_EAGAIN: #endif continue; } } if (c->opt->option.client) sslerror("SSL_connect"); else sslerror("SSL_accept"); longjmp(c->err, 1); } if (SSL_session_reused(c->ssl)) { s_log(LOG_INFO, "SSL %s: previous session reused", c->opt->option.client ? "connected" : "accepted"); } else { if (c->opt->option.client) { s_log(LOG_INFO, "SSL connected: new session negotiated"); old_session = c->opt->session; c->opt->session = SSL_get1_session(c->ssl); if (old_session) SSL_SESSION_free(old_session); } else s_log(LOG_INFO, "SSL accepted: new session negotiated"); print_cipher(c); } }
int s_client_main(int argc, char **argv) { unsigned int off = 0, clr = 0; SSL *con = NULL; int s, k, width, state = 0, af = AF_UNSPEC; char *cbuf = NULL, *sbuf = NULL, *mbuf = NULL; int cbuf_len, cbuf_off; int sbuf_len, sbuf_off; fd_set readfds, writefds; char *port = PORT_STR; int full_log = 1; char *host = SSL_HOST_NAME; 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, *timeoutp; const char *errstr = NULL; #ifndef OPENSSL_NO_ENGINE char *engine_id = NULL; char *ssl_client_engine_id = NULL; ENGINE *ssl_client_engine = NULL; #endif ENGINE *e = NULL; #ifndef OPENSSL_NO_TLSEXT char *servername = NULL; tlsextctx tlsextcbp = {NULL, 0}; #ifndef OPENSSL_NO_NEXTPROTONEG const char *next_proto_neg_in = NULL; #endif #endif 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; verify_error = X509_V_OK; 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; if (!extract_host_port(*(++argv), &host, NULL, &port)) goto bad; } 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; #ifndef OPENSSL_NO_TLSEXT else if (strcmp(*argv, "-tlsextdebug") == 0) c_tlsextdebug = 1; else if (strcmp(*argv, "-status") == 0) c_status_req = 1; #endif 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, "-ssl3") == 0) meth = SSLv3_client_method(); 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; } #ifndef OPENSSL_NO_TLSEXT else if (strcmp(*argv, "-no_ticket") == 0) { off |= SSL_OP_NO_TICKET; } #ifndef OPENSSL_NO_NEXTPROTONEG else if (strcmp(*argv, "-nextprotoneg") == 0) { if (--argc < 1) goto bad; next_proto_neg_in = *(++argv); } #endif #endif 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; } #ifndef OPENSSL_NO_ENGINE else if (strcmp(*argv, "-engine") == 0) { if (--argc < 1) goto bad; engine_id = *(++argv); } else if (strcmp(*argv, "-ssl_client_engine") == 0) { if (--argc < 1) goto bad; ssl_client_engine_id = *(++argv); } #endif else if (strcmp(*argv, "-4") == 0) { af = AF_INET; } else if (strcmp(*argv, "-6") == 0) { af = AF_INET6; } #ifndef OPENSSL_NO_TLSEXT else if (strcmp(*argv, "-servername") == 0) { if (--argc < 1) goto bad; servername = *(++argv); /* meth=TLSv1_client_method(); */ } #endif #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) { const char *errstr; 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 (badop) { bad: if (errstr) BIO_printf(bio_err, "invalid argument %s: %s\n", *argv, errstr); else sc_usage(); goto end; } #if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) 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; #endif #ifndef OPENSSL_NO_ENGINE e = setup_engine(bio_err, engine_id, 1); if (ssl_client_engine_id) { ssl_client_engine = ENGINE_by_id(ssl_client_engine_id); if (!ssl_client_engine) { BIO_printf(bio_err, "Error getting client auth engine\n"); goto end; } } #endif 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, e, "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, e, "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_ENGINE if (ssl_client_engine) { if (!SSL_CTX_set_client_cert_engine(ctx, ssl_client_engine)) { BIO_puts(bio_err, "Error setting client auth engine\n"); ERR_print_errors(bio_err); ENGINE_free(ssl_client_engine); goto end; } ENGINE_free(ssl_client_engine); } #endif #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 !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) if (next_proto.data) SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &next_proto); #endif 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; */ } #ifndef OPENSSL_NO_TLSEXT 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); } #endif 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); } #ifndef OPENSSL_NO_TLSEXT 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; } } #endif /* 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); shutdown(s, SHUT_RD); close(s); goto end; } BIO_printf(bio_c_out, "CONNECTED(%08X)\n", s); if (c_nbio) { unsigned long l = 1; BIO_printf(bio_c_out, "turning on non blocking io\n"); if (BIO_socket_ioctl(s, FIONBIO, &l) < 0) { 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); } #ifndef OPENSSL_NO_TLSEXT 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); } #endif SSL_set_bio(con, sbio, sbio); SSL_set_connect_state(con); /* ok, lets connect */ width = SSL_get_fd(con) + 1; 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); } 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'>", host); seen = BIO_read(sbio, mbuf, BUFSIZZ); mbuf[seen] = 0; while (!strstr(mbuf, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'")) { if (strstr(mbuf, "/stream:features>")) goto shut; seen = BIO_read(sbio, mbuf, BUFSIZZ); 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; } for (;;) { FD_ZERO(&readfds); FD_ZERO(&writefds); if ((SSL_version(con) == DTLS1_VERSION) && DTLSv1_get_timeout(con, &timeout)) timeoutp = &timeout; else timeoutp = NULL; 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); /* XXX should add tests for fd_set overflow */ if (!ssl_pending) { if (tty_on) { if (read_tty) FD_SET(fileno(stdin), &readfds); if (write_tty) FD_SET(fileno(stdout), &writefds); } if (read_ssl) FD_SET(SSL_get_fd(con), &readfds); if (write_ssl) FD_SET(SSL_get_fd(con), &writefds); /* printf("mode tty(%d %d%d) ssl(%d%d)\n", tty_on,read_tty,write_tty,read_ssl,write_ssl);*/ i = select(width, &readfds, &writefds, NULL, timeoutp); 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 && FD_ISSET(SSL_get_fd(con), &writefds)) { 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 && FD_ISSET(fileno(stdout), &writefds)) { 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 || FD_ISSET(SSL_get_fd(con), &readfds)) { #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 (FD_ISSET(fileno(stdin), &readfds)) { 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); } #if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) free(next_proto.data); #endif 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) { OPENSSL_cleanse(cbuf, BUFSIZZ); free(cbuf); } if (sbuf != NULL) { OPENSSL_cleanse(sbuf, BUFSIZZ); free(sbuf); } if (mbuf != NULL) { OPENSSL_cleanse(mbuf, BUFSIZZ); free(mbuf); } if (bio_c_out != NULL) { BIO_free(bio_c_out); bio_c_out = NULL; } return (ret); }
/* * This function loads all the client/CA certificates and CRLs. Setup the TLS * layer and do all necessary magic. */ static CURLcode cyassl_connect_step1(struct connectdata *conn, int sockindex) { char error_buffer[CYASSL_MAX_ERROR_SZ]; struct SessionHandle *data = conn->data; struct ssl_connect_data* conssl = &conn->ssl[sockindex]; SSL_METHOD* req_method = NULL; void* ssl_sessionid = NULL; curl_socket_t sockfd = conn->sock[sockindex]; #ifdef HAVE_SNI bool sni = FALSE; #define use_sni(x) sni = (x) #else #define use_sni(x) Curl_nop_stmt #endif if(conssl->state == ssl_connection_complete) return CURLE_OK; /* check to see if we've been told to use an explicit SSL/TLS version */ switch(data->set.ssl.version) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: #if LIBCYASSL_VERSION_HEX >= 0x03003000 /* >= 3.3.0 */ /* minimum protocol version is set later after the CTX object is created */ req_method = SSLv23_client_method(); #else infof(data, "CyaSSL <3.3.0 cannot be configured to use TLS 1.0-1.2, " "TLS 1.0 is used exclusively\n"); req_method = TLSv1_client_method(); #endif use_sni(TRUE); break; case CURL_SSLVERSION_TLSv1_0: req_method = TLSv1_client_method(); use_sni(TRUE); break; case CURL_SSLVERSION_TLSv1_1: req_method = TLSv1_1_client_method(); use_sni(TRUE); break; case CURL_SSLVERSION_TLSv1_2: req_method = TLSv1_2_client_method(); use_sni(TRUE); break; case CURL_SSLVERSION_SSLv3: req_method = SSLv3_client_method(); use_sni(FALSE); break; case CURL_SSLVERSION_SSLv2: failf(data, "CyaSSL does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; default: failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); return CURLE_SSL_CONNECT_ERROR; } if(!req_method) { failf(data, "SSL: couldn't create a method!"); return CURLE_OUT_OF_MEMORY; } if(conssl->ctx) SSL_CTX_free(conssl->ctx); conssl->ctx = SSL_CTX_new(req_method); if(!conssl->ctx) { failf(data, "SSL: couldn't create a context!"); return CURLE_OUT_OF_MEMORY; } switch(data->set.ssl.version) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: #if LIBCYASSL_VERSION_HEX > 0x03004006 /* > 3.4.6 */ /* Versions 3.3.0 to 3.4.6 we know the minimum protocol version is whatever minimum version of TLS was built in and at least TLS 1.0. For later library versions that could change (eg TLS 1.0 built in but defaults to TLS 1.1) so we have this short circuit evaluation to find the minimum supported TLS version. We use wolfSSL_CTX_SetMinVersion and not CyaSSL_SetMinVersion because only the former will work before the user's CTX callback is called. */ if((wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1) != 1) && (wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1_1) != 1) && (wolfSSL_CTX_SetMinVersion(conssl->ctx, WOLFSSL_TLSV1_2) != 1)) { failf(data, "SSL: couldn't set the minimum protocol version"); return CURLE_SSL_CONNECT_ERROR; } #endif break; } #ifndef NO_FILESYSTEM /* load trusted cacert */ if(data->set.str[STRING_SSL_CAFILE]) { if(1 != SSL_CTX_load_verify_locations(conssl->ctx, data->set.str[STRING_SSL_CAFILE], data->set.str[STRING_SSL_CAPATH])) { if(data->set.ssl.verifypeer) { /* Fail if we insist on successfully verifying the server. */ failf(data, "error setting certificate verify locations:\n" " CAfile: %s\n CApath: %s", data->set.str[STRING_SSL_CAFILE]? data->set.str[STRING_SSL_CAFILE]: "none", data->set.str[STRING_SSL_CAPATH]? data->set.str[STRING_SSL_CAPATH] : "none"); return CURLE_SSL_CACERT_BADFILE; } else { /* Just continue with a warning if no strict certificate verification is required. */ infof(data, "error setting certificate verify locations," " continuing anyway:\n"); } } else { /* Everything is fine. */ infof(data, "successfully set certificate verify locations:\n"); } infof(data, " CAfile: %s\n" " CApath: %s\n", data->set.str[STRING_SSL_CAFILE] ? data->set.str[STRING_SSL_CAFILE]: "none", data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]: "none"); } /* Load the client certificate, and private key */ if(data->set.str[STRING_CERT] && data->set.str[STRING_KEY]) { int file_type = do_file_type(data->set.str[STRING_CERT_TYPE]); if(SSL_CTX_use_certificate_file(conssl->ctx, data->set.str[STRING_CERT], file_type) != 1) { failf(data, "unable to use client certificate (no key or wrong pass" " phrase?)"); return CURLE_SSL_CONNECT_ERROR; } file_type = do_file_type(data->set.str[STRING_KEY_TYPE]); if(SSL_CTX_use_PrivateKey_file(conssl->ctx, data->set.str[STRING_KEY], file_type) != 1) { failf(data, "unable to set private key"); return CURLE_SSL_CONNECT_ERROR; } } #else /* NO_FILESYSTEM */ if(CyaSSL_no_filesystem_verify(conssl->ctx)!= SSL_SUCCESS) { return CURLE_SSL_CONNECT_ERROR; } #endif /* NO_FILESYSTEM */ /* SSL always tries to verify the peer, this only says whether it should * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ SSL_CTX_set_verify(conssl->ctx, data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE, NULL); #ifdef HAVE_SNI if(sni) { struct in_addr addr4; #ifdef ENABLE_IPV6 struct in6_addr addr6; #endif size_t hostname_len = strlen(conn->host.name); if((hostname_len < USHRT_MAX) && (0 == Curl_inet_pton(AF_INET, conn->host.name, &addr4)) && #ifdef ENABLE_IPV6 (0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr6)) && #endif (CyaSSL_CTX_UseSNI(conssl->ctx, CYASSL_SNI_HOST_NAME, conn->host.name, (unsigned short)hostname_len) != 1)) { infof(data, "WARNING: failed to configure server name indication (SNI) " "TLS extension\n"); } } #endif /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { CURLcode result = CURLE_OK; result = (*data->set.ssl.fsslctx)(data, conssl->ctx, data->set.ssl.fsslctxp); if(result) { failf(data, "error signaled by ssl ctx callback"); return result; } } #ifdef NO_FILESYSTEM else if(data->set.ssl.verifypeer) { failf(data, "SSL: Certificates couldn't be loaded because CyaSSL was built" " with \"no filesystem\". Either disable peer verification" " (insecure) or if you are building an application with libcurl you" " can load certificates via CURLOPT_SSL_CTX_FUNCTION."); return CURLE_SSL_CONNECT_ERROR; } #endif /* Let's make an SSL structure */ if(conssl->handle) SSL_free(conssl->handle); conssl->handle = SSL_new(conssl->ctx); if(!conssl->handle) { failf(data, "SSL: couldn't create a context (handle)!"); return CURLE_OUT_OF_MEMORY; } /* Check if there's a cached ID we can/should use here! */ if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) { /* we got a session id, use it! */ if(!SSL_set_session(conssl->handle, ssl_sessionid)) { failf(data, "SSL: SSL_set_session failed: %s", ERR_error_string(SSL_get_error(conssl->handle, 0), error_buffer)); return CURLE_SSL_CONNECT_ERROR; } /* Informational message */ infof (data, "SSL re-using session ID\n"); } /* pass the raw socket into the SSL layer */ if(!SSL_set_fd(conssl->handle, (int)sockfd)) { failf(data, "SSL: SSL_set_fd failed"); return CURLE_SSL_CONNECT_ERROR; } conssl->connecting_state = ssl_connect_2; return CURLE_OK; }
/* handle_connect_results assumes that select or poll have already shown the * descriptor to be active */ void handle_connect_result(mspool *ms, msevent *nse, enum nse_status status) { int optval; socklen_t optlen = sizeof(int); msiod *iod = nse->iod; #if HAVE_OPENSSL int sslerr; int rc = 0; int sslconnect_inprogress = nse->type == NSE_TYPE_CONNECT_SSL && nse->iod && (nse->sslinfo.ssl_desire == SSL_ERROR_WANT_READ || nse->sslinfo.ssl_desire == SSL_ERROR_WANT_WRITE); #else int sslconnect_inprogress = 0; #endif if (status == NSE_STATUS_TIMEOUT || status == NSE_STATUS_CANCELLED) { nse->status = status; nse->event_done = 1; } else if (sslconnect_inprogress) { /* Do nothing */ } else if (status == NSE_STATUS_SUCCESS) { /* First we want to determine whether the socket really is connected */ if (getsockopt(iod->sd, SOL_SOCKET, SO_ERROR, (char *)&optval, &optlen) != 0) optval = socket_errno(); /* Stupid Solaris */ switch(optval) { case 0: nse->status = NSE_STATUS_SUCCESS; break; /* EACCES can be caused by ICMPv6 dest-unreach-admin, or when a port is blocked by Windows Firewall (WSAEACCES). */ case EACCES: case ECONNREFUSED: case EHOSTUNREACH: case ENETDOWN: case ENETUNREACH: case ENETRESET: case ECONNABORTED: case ETIMEDOUT: case EHOSTDOWN: case ECONNRESET: #ifdef WIN32 case WSAEADDRINUSE: case WSAEADDRNOTAVAIL: #endif #ifndef WIN32 case EPIPE: /* Has been seen after connect on Linux. */ case ENOPROTOOPT: /* Also seen on Linux, perhaps in response to protocol unreachable. */ #endif nse->status = NSE_STATUS_ERROR; nse->errnum = optval; break; default: fprintf(stderr, "Strange connect error from %s (%d): %s", inet_ntop_ez(&iod->peer, iod->peerlen), optval, socket_strerror(optval)); assert(0); /* I'd like for someone to report it */ break; } /* Now special code for the SSL case where the TCP connection was successful. */ if (nse->type == NSE_TYPE_CONNECT_SSL && nse->status == NSE_STATUS_SUCCESS) { #if HAVE_OPENSSL assert(ms->sslctx != NULL); /* Reuse iod->ssl if present. If set, this is the second try at connection without the SSL_OP_NO_SSLv2 option set. */ if (iod->ssl == NULL) { iod->ssl = SSL_new(ms->sslctx); if (!iod->ssl) fatal("SSL_new failed: %s", ERR_error_string(ERR_get_error(), NULL)); } if (iod->hostname != NULL) { #if HAVE_SSL_SET_TLSEXT_HOST_NAME if (SSL_set_tlsext_host_name(iod->ssl, iod->hostname) != 1) fatal("SSL_set_tlsext_host_name failed: %s", ERR_error_string(ERR_get_error(), NULL)); #endif } /* Associate our new SSL with the connected socket. It will inherit the * non-blocking nature of the sd */ if (SSL_set_fd(iod->ssl, iod->sd) != 1) { fatal("SSL_set_fd failed: %s", ERR_error_string(ERR_get_error(), NULL)); } /* Event not done -- need to do SSL connect below */ nse->sslinfo.ssl_desire = SSL_ERROR_WANT_CONNECT; #endif } else { /* This is not an SSL connect (in which case we are always done), or the * TCP connect() underlying the SSL failed (in which case we are also done */ nse->event_done = 1; } } else { assert(0); /* Currently we only know about TIMEOUT and SUCCESS callbacks */ } /* At this point the TCP connection is done, whether successful or not. * Therefore decrease the read/write listen counts that were incremented in * nsp_add_event. In the SSL case, we may increase one of the counts depending * on whether SSL_connect returns an error of SSL_ERROR_WANT_READ or * SSL_ERROR_WANT_WRITE. In that case we will re-enter this function, but we * don't want to execute this block again. */ if (iod->sd != -1 && !sslconnect_inprogress) { int ev = EV_NONE; ev |= socket_count_read_dec(iod); ev |= socket_count_write_dec(iod); ev |= EV_EXCEPT; update_events(iod, ms, EV_NONE, ev); } #if HAVE_OPENSSL if (nse->type == NSE_TYPE_CONNECT_SSL && !nse->event_done) { /* Lets now start/continue/finish the connect! */ if (iod->ssl_session) { rc = SSL_set_session(iod->ssl, iod->ssl_session); if (rc == 0) fprintf(stderr, "Uh-oh: SSL_set_session() failed - please tell Fyodor\n"); iod->ssl_session = NULL; /* No need for this any more */ } /* If this is a reinvocation of handle_connect_result, clear out the listen * bits that caused it, based on the previous SSL desire. */ if (sslconnect_inprogress) { int ev; ev = socket_count_dec_ssl_desire(nse); update_events(iod, ms, EV_NONE, ev); } rc = SSL_connect(iod->ssl); if (rc == 1) { /* Woop! Connect is done! */ nse->event_done = 1; /* Check that certificate verification was okay, if requested. */ if (nsi_ssl_post_connect_verify(iod)) { nse->status = NSE_STATUS_SUCCESS; } else { if (ms->tracelevel > 0) nsock_trace(ms, "certificate verification error for EID %li: %s", nse->id, ERR_error_string(ERR_get_error(), NULL)); nse->status = NSE_STATUS_ERROR; } } else { long options = SSL_get_options(iod->ssl); sslerr = SSL_get_error(iod->ssl, rc); if (rc == -1 && sslerr == SSL_ERROR_WANT_READ) { nse->sslinfo.ssl_desire = sslerr; socket_count_read_inc(iod); update_events(iod, ms, EV_READ, EV_NONE); } else if (rc == -1 && sslerr == SSL_ERROR_WANT_WRITE) { nse->sslinfo.ssl_desire = sslerr; socket_count_write_inc(iod); update_events(iod, ms, EV_WRITE, EV_NONE); } else if (!(options & SSL_OP_NO_SSLv2)) { int saved_ev; /* SSLv3-only and TLSv1-only servers can't be connected to when the * SSL_OP_NO_SSLv2 option is not set, which is the case when the pool * was initialized with nsp_ssl_init_max_speed. Try reconnecting with * SSL_OP_NO_SSLv2. Never downgrade a NO_SSLv2 connection to one that * might use SSLv2. */ if (ms->tracelevel > 0) nsock_trace(ms, "EID %li reconnecting with SSL_OP_NO_SSLv2", nse->id); saved_ev = iod->watched_events; ms->engine->iod_unregister(ms, iod); close(iod->sd); nsock_connect_internal(ms, nse, SOCK_STREAM, iod->lastproto, &iod->peer, iod->peerlen, nsi_peerport(iod)); ms->engine->iod_register(ms, iod, saved_ev); SSL_clear(iod->ssl); if(!SSL_clear(iod->ssl)) fatal("SSL_clear failed: %s", ERR_error_string(ERR_get_error(), NULL)); SSL_set_options(iod->ssl, options | SSL_OP_NO_SSLv2); socket_count_read_inc(nse->iod); socket_count_write_inc(nse->iod); update_events(iod, ms, EV_READ|EV_WRITE, EV_NONE); nse->sslinfo.ssl_desire = SSL_ERROR_WANT_CONNECT; } else { if (ms->tracelevel > 0) nsock_trace(ms, "EID %li %s", nse->id, ERR_error_string(ERR_get_error(), NULL)); nse->event_done = 1; nse->status = NSE_STATUS_ERROR; nse->errnum = EIO; } } } #endif return; }
static void init_local(CLI *c) { SOCKADDR_UNION addr; socklen_t addrlen; addrlen=sizeof addr; if(getpeername(c->local_rfd.fd, &addr.sa, &addrlen)<0) { strcpy(c->accepted_address, "NOT A SOCKET"); c->local_rfd.is_socket=0; c->local_wfd.is_socket=0; /* TODO: It's not always true */ #ifdef USE_WIN32 if(get_last_socket_error()!=ENOTSOCK) { #else if(c->opt->option.transparent_src || get_last_socket_error()!=ENOTSOCK) { #endif sockerror("getpeerbyname"); longjmp(c->err, 1); } /* ignore ENOTSOCK error so 'local' doesn't have to be a socket */ } else { /* success */ /* copy addr to c->peer_addr */ memcpy(&c->peer_addr.addr[0], &addr, sizeof addr); c->peer_addr.num=1; s_ntop(c->accepted_address, &c->peer_addr.addr[0]); c->local_rfd.is_socket=1; c->local_wfd.is_socket=1; /* TODO: It's not always true */ /* it's a socket: lets setup options */ if(set_socket_options(c->local_rfd.fd, 1)<0) longjmp(c->err, 1); #ifdef USE_LIBWRAP libwrap_auth(c); #endif /* USE_LIBWRAP */ auth_user(c); s_log(LOG_NOTICE, "Service %s accepted connection from %s", c->opt->servname, c->accepted_address); } } static void init_remote(CLI *c) { /* create connection to host/service */ if(c->opt->source_addr.num) memcpy(&c->bind_addr, &c->opt->source_addr, sizeof(SOCKADDR_LIST)); #ifndef USE_WIN32 else if(c->opt->option.transparent_src) memcpy(&c->bind_addr, &c->peer_addr, sizeof(SOCKADDR_LIST)); #endif else { c->bind_addr.num=0; /* don't bind connecting socket */ } /* setup c->remote_fd, now */ if(c->opt->option.remote) c->remote_fd.fd=connect_remote(c); #ifdef SO_ORIGINAL_DST else if(c->opt->option.transparent_dst) c->remote_fd.fd=connect_transparent(c); #endif /* SO_ORIGINAL_DST */ else /* NOT in remote mode */ c->remote_fd.fd=connect_local(c); c->remote_fd.is_socket=1; /* always! */ s_log(LOG_DEBUG, "Remote FD=%d initialized", c->remote_fd.fd); if(set_socket_options(c->remote_fd.fd, 2)<0) longjmp(c->err, 1); } static void init_ssl(CLI *c) { int i, err; SSL_SESSION *old_session; if(!(c->ssl=SSL_new(c->opt->ctx))) { sslerror("SSL_new"); longjmp(c->err, 1); } SSL_set_ex_data(c->ssl, cli_index, c); /* for callbacks */ SSL_set_session_id_context(c->ssl, (unsigned char *)sid_ctx, strlen(sid_ctx)); if(c->opt->option.client) { #ifndef OPENSSL_NO_TLSEXT if(c->opt->host_name) { s_log(LOG_DEBUG, "SNI: host name: %s", c->opt->host_name); if(!SSL_set_tlsext_host_name(c->ssl, c->opt->host_name)) { sslerror("SSL_set_tlsext_host_name"); longjmp(c->err, 1); } } #endif if(c->opt->session) { enter_critical_section(CRIT_SESSION); SSL_set_session(c->ssl, c->opt->session); leave_critical_section(CRIT_SESSION); } SSL_set_fd(c->ssl, c->remote_fd.fd); SSL_set_connect_state(c->ssl); } else { if(c->local_rfd.fd==c->local_wfd.fd) SSL_set_fd(c->ssl, c->local_rfd.fd); else { /* does it make sence to have SSL on STDIN/STDOUT? */ SSL_set_rfd(c->ssl, c->local_rfd.fd); SSL_set_wfd(c->ssl, c->local_wfd.fd); } SSL_set_accept_state(c->ssl); } /* setup some values for transfer() function */ if(c->opt->option.client) { c->sock_rfd=&(c->local_rfd); c->sock_wfd=&(c->local_wfd); c->ssl_rfd=c->ssl_wfd=&(c->remote_fd); } else { c->sock_rfd=c->sock_wfd=&(c->remote_fd); c->ssl_rfd=&(c->local_rfd); c->ssl_wfd=&(c->local_wfd); } while(1) { #if OPENSSL_VERSION_NUMBER<0x1000002f /* this critical section is a crude workaround for CVE-2010-3864 * * see http://www.securityfocus.com/bid/44884 for details * * NOTE: this critical section also covers callbacks (e.g. OCSP) */ enter_critical_section(CRIT_SSL); #endif /* OpenSSL version < 1.0.0b */ if(c->opt->option.client) i=SSL_connect(c->ssl); else i=SSL_accept(c->ssl); #if OPENSSL_VERSION_NUMBER<0x1000002f leave_critical_section(CRIT_SSL); #endif /* OpenSSL version < 1.0.0b */ err=SSL_get_error(c->ssl, i); if(err==SSL_ERROR_NONE) break; /* ok -> done */ if(err==SSL_ERROR_WANT_READ || err==SSL_ERROR_WANT_WRITE) { s_poll_init(&c->fds); s_poll_add(&c->fds, c->ssl_rfd->fd, err==SSL_ERROR_WANT_READ, err==SSL_ERROR_WANT_WRITE); switch(s_poll_wait(&c->fds, c->opt->timeout_busy, 0)) { case -1: sockerror("init_ssl: s_poll_wait"); longjmp(c->err, 1); case 0: s_log(LOG_INFO, "init_ssl: s_poll_wait:" " TIMEOUTbusy exceeded: sending reset"); longjmp(c->err, 1); case 1: break; /* OK */ default: s_log(LOG_ERR, "init_ssl: s_poll_wait: unknown result"); longjmp(c->err, 1); } continue; /* ok -> retry */ } if(err==SSL_ERROR_SYSCALL) { switch(get_last_socket_error()) { case EINTR: case EAGAIN: continue; } } if(c->opt->option.client) sslerror("SSL_connect"); else sslerror("SSL_accept"); longjmp(c->err, 1); } if(SSL_session_reused(c->ssl)) { s_log(LOG_INFO, "SSL %s: previous session reused", c->opt->option.client ? "connected" : "accepted"); } else { /* a new session was negotiated */ if(c->opt->option.client) { s_log(LOG_INFO, "SSL connected: new session negotiated"); enter_critical_section(CRIT_SESSION); old_session=c->opt->session; c->opt->session=SSL_get1_session(c->ssl); /* store it */ if(old_session) SSL_SESSION_free(old_session); /* release the old one */ leave_critical_section(CRIT_SESSION); } else s_log(LOG_INFO, "SSL accepted: new session negotiated"); print_cipher(c); } }
static int init_ssl_socket(pn_transport_t* transport, pni_ssl_t *ssl) { if (ssl->ssl) return 0; if (!ssl->domain) return -1; ssl->ssl = SSL_new(ssl->domain->ctx); if (!ssl->ssl) { pn_transport_logf(transport, "SSL socket setup failure." ); return -1; } // store backpointer to pn_transport_t in SSL object: SSL_set_ex_data(ssl->ssl, ssl_ex_data_index, transport); #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME if (ssl->peer_hostname && ssl->domain->mode == PN_SSL_MODE_CLIENT) { SSL_set_tlsext_host_name(ssl->ssl, ssl->peer_hostname); } #endif // restore session, if available if (ssl->session_id) { pn_ssl_session_t *ssn = ssn_cache_find( ssl->domain, ssl->session_id ); if (ssn) { ssl_log( transport, "Restoring previous session id=%s", ssn->id ); int rc = SSL_set_session( ssl->ssl, ssn->session ); if (rc != 1) { ssl_log( transport, "Session restore failed, id=%s", ssn->id ); } LL_REMOVE( ssl->domain, ssn_cache, ssn ); ssl_session_free( ssn ); } } // now layer a BIO over the SSL socket ssl->bio_ssl = BIO_new(BIO_f_ssl()); if (!ssl->bio_ssl) { pn_transport_log(transport, "BIO setup failure." ); return -1; } (void)BIO_set_ssl(ssl->bio_ssl, ssl->ssl, BIO_NOCLOSE); // create the "lower" BIO "pipe", and attach it below the SSL layer if (!BIO_new_bio_pair(&ssl->bio_ssl_io, 0, &ssl->bio_net_io, 0)) { pn_transport_log(transport, "BIO setup failure." ); return -1; } SSL_set_bio(ssl->ssl, ssl->bio_ssl_io, ssl->bio_ssl_io); if (ssl->domain->mode == PN_SSL_MODE_SERVER) { SSL_set_accept_state(ssl->ssl); BIO_set_ssl_mode(ssl->bio_ssl, 0); // server mode ssl_log( transport, "Server SSL socket created." ); } else { // client mode SSL_set_connect_state(ssl->ssl); BIO_set_ssl_mode(ssl->bio_ssl, 1); // client mode ssl_log( transport, "Client SSL socket created." ); } ssl->subject = NULL; ssl->peer_certificate = NULL; return 0; }
// Starts an SSL handshake on this connection. void SSLSocket::startHandshake() throw(IOException) { Synchronized synchronized(_sync); // TODO: if (_session != NULL) SSL_set_session(_ssl, _session->_session); // TODO: add some checks... if (!_use_client_mode) return; int ret = SSL_connect(_ssl); if (ret != 1) { int ssl_error = SSL_get_error(_ssl, ret); int error = ERR_get_error(); OSTRINGSTREAM reason; reason << "SSLSocket::startHandshake connect error: "; switch (ssl_error) { reason << "SSL_ERROR_NONE reason: " << ERR_reason_error_string(error) << ends; throw IOException(reason.str().c_str()); break; case SSL_ERROR_WANT_READ: reason << "SSL_ERROR_WANT_READ reason: " << ERR_reason_error_string(error) << ends; throw IOException(reason.str().c_str()); break; case SSL_ERROR_WANT_WRITE: reason << "SSL_ERROR_WANT_WRITE reason: " << ERR_reason_error_string(error) << ends; throw IOException(reason.str().c_str()); break; case SSL_ERROR_ZERO_RETURN: reason << "SSL_ERROR_ZERO_RETURN reason: " << ERR_reason_error_string(error) << ends; throw IOException(reason.str().c_str()); break; case SSL_ERROR_WANT_CONNECT: reason << "SSL_ERROR_WANT_CONNECT reason: " << ERR_reason_error_string(error) << ends; throw IOException(reason.str().c_str()); break; case SSL_ERROR_WANT_X509_LOOKUP: reason << "SSL_ERROR_WANT_X509_LOOKUP reason: " << ERR_reason_error_string(error) << ends; throw IOException(reason.str().c_str()); break; case SSL_ERROR_SYSCALL: reason << "SSL_ERROR_SYSCALL reason: " << ERR_reason_error_string(error) << ends; throw IOException(reason.str().c_str()); break; case SSL_ERROR_SSL: reason << "SSL_ERROR_SSL reason: " << ERR_reason_error_string(error) << ends; throw IOException(reason.str().c_str()); break; default: reason << "unknown: " << ends; throw IOException(reason.str().c_str()); } } // if (!SSL_session_reused(_ssl)) // cerr << " new session negotiated" << endl; //check_cert(ssl,host); }
BOOL CHTTPClient::SendSSLRequest(char *szHost, char *szHostName, char *szPath, char *szUserID, char *szPassword, long lPort, char *szProxy, long lProxyPort, char *szProxyUser, char *szProxyPswd, char *szUserAgent, char *szPost, long dwTimeout, SSL **pssl, SSL_CTX **pssl_ctx, char *szTransaction) { char *szConnectHost; char *szConnectionHeader = "Connection: close\r\n"; char *szContentLength = "Content-Length: %d\r\n"; char *szContentType = "Content-Type: application/x-www-form-urlencoded\r\n"; char *szProxyConn = "Proxy-Connection: Keep-Alive\r\n"; char *szPragma = "Pragma: no-cache\r\n"; long lConnectPort; char *szBuffer = szSendBuffer; char szWork[256]; char *lpWork; char *szGetMethod = "GET"; char *szPostMethod = "POST"; char *szMethod; BOOL RC = TRUE; BOOL bBIOset = FALSE; Base64Coder cBase64Coder; BIO *bio = 0; int done = 0, i, n; char buf[2048]; // Got to have a host if (!szHost) return(FALSE); // Initialize strings used for cookies cDomain = szHostName; cPath = szPath; if (szTransaction) cTransaction = szTransaction; else cTransaction = ""; // Clean up ssl storage try { if (ssl) { SSL_free(ssl); ssl = 0; } } catch(...) { ssl = 0; } try { // Initialise the ssl library now in rtm startup // Create the context structure. Operate in the normal default SSLv3 // in a SSLv2 header mode to maximize the number of servers // we can connect to. if (!ssl_ctx) { if (!pssl_ctx || !*pssl_ctx) { if ((ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) { bConnectFailed = TRUE; throw FALSE; } } else ssl_ctx = *pssl_ctx; } // Reset variables for response bHeader = FALSE; bReadComplete = FALSE; bHeadRequest = FALSE; lReceivedLength = 0; lContentLength = 0; cHeader = ""; cContent = ""; // turn on all vendor bug compatibility options SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL); // Create the SSL structure - defaults are inherited from the SSL_CTX if ((ssl = SSL_new(ssl_ctx)) == NULL) { bConnectFailed = TRUE; throw FALSE; }; // Set as client side SSL_set_connect_state(ssl); if (!szProxy) { // No proxy szConnectHost = szHost; lConnectPort = lPort; } else { // Proxy szConnectHost = szProxy; lConnectPort = lProxyPort; }; sprintf(szWork, "%s:%u", szConnectHost, (unsigned int) lConnectPort); // Create a BIO to handle the connect and SSL handling if ((bio = BIO_new_connect(szWork)) == NULL) { bConnectFailed = TRUE; throw FALSE; }; // If using proxy then have to tunnel through to the actual ssl server if (szProxy) { sprintf(szBuffer, "CONNECT %s:%u HTTP/1.0\r\nUser-Agent: %s\r\n", szHostName, (unsigned int) lPort, szUserAgent); if (szProxyUser && szProxyPswd && strlen(szProxyUser) > 0 && strlen(szProxyPswd) > 0) { sprintf(szWork, "%s:%s", szProxyUser, szProxyPswd); cBase64Coder.Encode(szWork); sprintf(szWork, "Proxy-Authorization: Basic %s\r\n", (LPCTSTR) cBase64Coder.EncodedMessage()); strcat(szBuffer, szWork); } strcat(szBuffer, szProxyConn); strcat(szBuffer, szPragma); strcat(szBuffer, "\r\n"); // Send connect request n = strlen(szBuffer); i = 0; do { i = BIO_write(bio, &(szBuffer[i]), n); if (i <= 0) { bConnectFailed = TRUE; BIO_free(bio); throw FALSE; } n -= i; } while(n > 0); // Read response from proxy i = BIO_read(bio, buf, sizeof(buf)); if (i <= 0) { bConnectFailed = TRUE; BIO_free(bio); throw FALSE; }; buf[i] = '\0'; lpWork = strstr(buf, "200"); if (!lpWork) { bConnectFailed = TRUE; BIO_free(bio); throw FALSE; }; }; // Use the newly created connect BIO SSL_set_bio(ssl, bio, bio); bBIOset = TRUE; if (pSession && pssl) { SSL_set_session(ssl, pSession); } else { while(!done) { int i; struct tm *ptr; time_t lt; lt = time(NULL); PROTECT_CALL ptr = localtime(<); char *pszTime = asctime(ptr); RAND_seed((unsigned char *) pszTime, strlen(pszTime)); UNPROTECT_CALL // The all important handshake i = SSL_do_handshake(ssl); switch(SSL_get_error(ssl, i)) { case SSL_ERROR_NONE: // Handshake has finished, so proceed done = 1; break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_CONNECT: // Perform the handshake again. sleep(1); break; default: bConnectFailed = TRUE; throw FALSE; break; }; }; pSession = SSL_get_session(ssl); }; if (szPost) szMethod = szPostMethod; else szMethod = szGetMethod; // Build the request sprintf(szBuffer, "%s %s HTTP/1.0\r\nUser-Agent: %s\r\nHost: %s\r\n", szMethod, szPath, szUserAgent, szHostName); if (szUserID) { sprintf(szWork, "%s:%s", szUserID, szPassword); cBase64Coder.Encode(szWork); sprintf(szWork, "Authorization: Basic %s\r\n", (LPCTSTR) cBase64Coder.EncodedMessage()); strcat(szBuffer, szWork); } if (szPost) { strcat(szBuffer, szContentType); sprintf(szWork, szContentLength, strlen(szPost)); strcat(szBuffer, szWork); strcat(szBuffer, szProxyConn); strcat(szBuffer, szPragma); } else { strcat(szBuffer, szConnectionHeader); } if (!cTransaction.empty()) SendCookie(szBuffer); // Indicate end of header strcat(szBuffer, "\r\n"); sDebugSendHeader = szBuffer; if (szPost) strcat(szBuffer, szPost); dwStartTime = ::XPlatGetMilliseconds(); // Send request n = strlen(szBuffer); i = 0; do { i = SSL_write(ssl, &(szBuffer[i]), n); if (i <= 0) { bConnectFailed = FALSE; throw FALSE; } n -= i; } while(n > 0); // Read from the other side of the protocol while(1) { i = SSL_read(ssl, buf, sizeof(buf)); if (i <= 0) break; ProcessData((unsigned char *) buf, i); if (!GetTimeLeft(dwTimeout, dwStartTime)) { // Timed out bConnectFailed = FALSE; throw FALSE; }; }; if (szPost && !strcmp(szStatus, "100")) { printf("status 100"); strcat(szBuffer, szPost); // Reset variables for response bHeader = FALSE; bReadComplete = FALSE; bHeadRequest = FALSE; lReceivedLength = 0; lContentLength = 0; cHeader = ""; cContent = ""; // Send request n = strlen(szBuffer); i = 0; do { i = SSL_write(ssl, &(szBuffer[i]), n); if (i <= 0) { bConnectFailed = FALSE; throw FALSE; } n -= i; } while(n > 0); // Read from the other side of the protocol while(1) { i = SSL_read(ssl, buf, sizeof(buf)); if (i <= 0) break; ProcessData((unsigned char *) buf, i); if (!GetTimeLeft(dwTimeout, dwStartTime)) { // Timed out bConnectFailed = FALSE; throw FALSE; }; }; }; Disconnected(); throw TRUE; } catch(BOOL rc) { RC = rc; } catch(...) { RC = FALSE; if (!bBIOset && bio) { try { BIO_free(bio); } catch(...) { } } } if (pssl && pssl_ctx) { if (*pssl == 0) { *pssl = ssl; *pssl_ctx = ssl_ctx; ssl = 0; } ssl_ctx = 0; } return RC; };
int connect_dtls_socket(struct openconnect_info *vpninfo) { STACK_OF(SSL_CIPHER) *ciphers; method_const SSL_METHOD *dtls_method; SSL_CIPHER *dtls_cipher; SSL *dtls_ssl; BIO *dtls_bio; int dtls_fd; if (!vpninfo->dtls_addr) { vpn_progress(vpninfo, PRG_ERR, _("No DTLS address\n")); vpninfo->dtls_attempt_period = 0; return -EINVAL; } if (!vpninfo->dtls_cipher) { /* We probably didn't offer it any ciphers it liked */ vpn_progress(vpninfo, PRG_ERR, _("Server offered no DTLS cipher option\n")); vpninfo->dtls_attempt_period = 0; return -EINVAL; } if (vpninfo->proxy) { /* XXX: Theoretically, SOCKS5 proxies can do UDP too */ vpn_progress(vpninfo, PRG_ERR, _("No DTLS when connected via proxy\n")); vpninfo->dtls_attempt_period = 0; return -EINVAL; } dtls_fd = socket(vpninfo->peer_addr->sa_family, SOCK_DGRAM, IPPROTO_UDP); if (dtls_fd < 0) { perror(_("Open UDP socket for DTLS:")); return -EINVAL; } if (connect(dtls_fd, vpninfo->dtls_addr, vpninfo->peer_addrlen)) { perror(_("UDP (DTLS) connect:\n")); close(dtls_fd); return -EINVAL; } fcntl(dtls_fd, F_SETFD, FD_CLOEXEC); if (!vpninfo->dtls_ctx) { dtls_method = DTLSv1_client_method(); vpninfo->dtls_ctx = SSL_CTX_new(dtls_method); if (!vpninfo->dtls_ctx) { vpn_progress(vpninfo, PRG_ERR, _("Initialise DTLSv1 CTX failed\n")); vpninfo->dtls_attempt_period = 0; return -EINVAL; } /* If we don't readahead, then we do short reads and throw away the tail of data packets. */ SSL_CTX_set_read_ahead(vpninfo->dtls_ctx, 1); if (!SSL_CTX_set_cipher_list(vpninfo->dtls_ctx, vpninfo->dtls_cipher)) { vpn_progress(vpninfo, PRG_ERR, _("Set DTLS cipher list failed\n")); SSL_CTX_free(vpninfo->dtls_ctx); vpninfo->dtls_ctx = NULL; vpninfo->dtls_attempt_period = 0; return -EINVAL; } } if (!vpninfo->dtls_session) { /* We're going to "resume" a session which never existed. Fake it... */ vpninfo->dtls_session = SSL_SESSION_new(); if (!vpninfo->dtls_session) { vpn_progress(vpninfo, PRG_ERR, _("Initialise DTLSv1 session failed\n")); vpninfo->dtls_attempt_period = 0; return -EINVAL; } vpninfo->dtls_session->ssl_version = 0x0100; /* DTLS1_BAD_VER */ } /* Do this every time; it may have changed due to a rekey */ vpninfo->dtls_session->master_key_length = sizeof(vpninfo->dtls_secret); memcpy(vpninfo->dtls_session->master_key, vpninfo->dtls_secret, sizeof(vpninfo->dtls_secret)); vpninfo->dtls_session->session_id_length = sizeof(vpninfo->dtls_session_id); memcpy(vpninfo->dtls_session->session_id, vpninfo->dtls_session_id, sizeof(vpninfo->dtls_session_id)); dtls_ssl = SSL_new(vpninfo->dtls_ctx); SSL_set_connect_state(dtls_ssl); ciphers = SSL_get_ciphers(dtls_ssl); if (sk_SSL_CIPHER_num(ciphers) != 1) { vpn_progress(vpninfo, PRG_ERR, _("Not precisely one DTLS cipher\n")); SSL_CTX_free(vpninfo->dtls_ctx); SSL_free(dtls_ssl); SSL_SESSION_free(vpninfo->dtls_session); vpninfo->dtls_ctx = NULL; vpninfo->dtls_session = NULL; vpninfo->dtls_attempt_period = 0; return -EINVAL; } dtls_cipher = sk_SSL_CIPHER_value(ciphers, 0); /* Set the appropriate cipher on our session to be resumed */ vpninfo->dtls_session->cipher = dtls_cipher; vpninfo->dtls_session->cipher_id = dtls_cipher->id; /* Add the generated session to the SSL */ if (!SSL_set_session(dtls_ssl, vpninfo->dtls_session)) { vpn_progress(vpninfo, PRG_ERR, _("SSL_set_session() failed with old protocol version 0x%x\n" "Are you using a version of OpenSSL older than 0.9.8m?\n" "See http://rt.openssl.org/Ticket/Display.html?id=1751\n" "Use the --no-dtls command line option to avoid this message\n"), vpninfo->dtls_session->ssl_version); vpninfo->dtls_attempt_period = 0; return -EINVAL; } /* Go Go Go! */ dtls_bio = BIO_new_socket(dtls_fd, BIO_NOCLOSE); SSL_set_bio(dtls_ssl, dtls_bio, dtls_bio); SSL_set_options(dtls_ssl, SSL_OP_CISCO_ANYCONNECT); /* Set non-blocking */ BIO_set_nbio(SSL_get_rbio(dtls_ssl), 1); BIO_set_nbio(SSL_get_wbio(dtls_ssl), 1); fcntl(dtls_fd, F_SETFL, fcntl(dtls_fd, F_GETFL) | O_NONBLOCK); vpninfo->new_dtls_fd = dtls_fd; vpninfo->new_dtls_ssl = dtls_ssl; if (vpninfo->select_nfds <= dtls_fd) vpninfo->select_nfds = dtls_fd + 1; FD_SET(dtls_fd, &vpninfo->select_rfds); FD_SET(dtls_fd, &vpninfo->select_efds); time(&vpninfo->new_dtls_started); return dtls_try_handshake(vpninfo); }
int connect_ssl(char *host, char *port, int reconnect, int use_sessionid, int use_ticket, int delay, const char *client_cert, const char *client_key) { SSL_CTX* ctx; SSL* ssl; SSL_SESSION* ssl_session = NULL; int s, n; char buffer[256]; struct addrinfo* addr; start("Initialize OpenSSL library"); SSL_load_error_strings(); SSL_library_init(); if ((ctx = SSL_CTX_new(TLSv1_client_method())) == NULL) fail("Unable to initialize SSL context:\n%s", ERR_error_string(ERR_get_error(), NULL)); if (client_cert || client_key) { if (SSL_CTX_use_certificate_chain_file(ctx,client_cert)==0) { fail("failed to read X509 certificate from file %s into PEM format",client_key); } } if (client_key) { if (SSL_CTX_use_PrivateKey_file(ctx,client_key,SSL_FILETYPE_PEM)==0) { fail("failed to read private key from file %s into PEM format",client_key); } } if (!use_ticket) { start("Disable use of session tickets (RFC 5077)"); SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET); } addr = solve(host, port); do { s = connect_socket(addr, host, port); start("Start TLS renegotiation"); if ((ssl = SSL_new(ctx)) == NULL) fail("Unable to create new SSL struct:\n%s", ERR_error_string(ERR_get_error(), NULL)); SSL_set_fd(ssl, s); if (ssl_session) { if (!SSL_set_session(ssl, ssl_session)) { fail("Unable to set session to previous one:\n%s", ERR_error_string(ERR_get_error(), NULL)); } } if (SSL_connect(ssl) != 1) fail("Unable to start TLS renegotiation:\n%s", ERR_error_string(ERR_get_error(), NULL)); start("Check if session was reused"); if (!SSL_session_reused(ssl) && ssl_session) warn("No session was reused."); else if (SSL_session_reused(ssl) && !ssl_session) warn("Session was reused."); else if (SSL_session_reused(ssl)) end("SSL session correctly reused"); else end("SSL session was not used"); start("Get current session"); if (ssl_session) SSL_SESSION_free(ssl_session); ssl_session = NULL; if (!(ssl_session = SSL_get1_session(ssl))) warn("No session available"); else { BIO *mem = BIO_new(BIO_s_mem()); char *buf; if (SSL_SESSION_print(mem, ssl_session) != 1) fail("Unable to print session:\n%s", ERR_error_string(ERR_get_error(), NULL)); n = BIO_get_mem_data(mem, &buf); buf[n-1] = '\0'; end("Session content:\n%s", buf); BIO_free(mem); } if ((!use_sessionid && !use_ticket) || (!use_sessionid && !ssl_session->tlsext_tick)) { SSL_SESSION_free(ssl_session); ssl_session = NULL; } start("Send HTTP GET"); n = snprintf(buffer, sizeof(buffer), "GET / HTTP/1.0\r\n" "Host: %s\r\n" "\r\n", host); if (n == -1 || n >= sizeof(buffer)) fail("Unable to build request to send"); if (SSL_write(ssl, buffer, strlen(buffer)) != strlen(buffer)) fail("SSL write request failed:\n%s", ERR_error_string(ERR_get_error(), NULL)); start("Get HTTP answer"); if ((n = SSL_read(ssl, buffer, sizeof(buffer) - 1)) <= 0) fail("SSL read request failed:\n%s", ERR_error_string(ERR_get_error(), NULL)); buffer[n] = '\0'; if (strchr(buffer, '\r')) *strchr(buffer, '\r') = '\0'; end("%s", buffer); start("End TLS connection"); SSL_shutdown(ssl); close(s); SSL_free(ssl); --reconnect; if (reconnect < 0) break; else { start("waiting %d seconds",delay); sleep(delay); } } while (1); SSL_CTX_free(ctx); return 0; }
void client_test(void* args) { SOCKET_T sockfd = 0; SSL_METHOD* method = 0; SSL_CTX* ctx = 0; SSL* ssl = 0; #ifdef TEST_RESUME SSL* sslResume = 0; SSL_SESSION* session = 0; char resumeMsg[] = "resuming cyassl!"; int resumeSz = sizeof(resumeMsg); #endif char msg[64] = "hello cyassl!"; char reply[1024]; int input; int msgSz = strlen(msg); int argc = ((func_args*)args)->argc; char** argv = ((func_args*)args)->argv; ((func_args*)args)->return_code = -1; /* error state */ #if defined(CYASSL_DTLS) method = DTLSv1_client_method(); #elif !defined(NO_TLS) method = SSLv23_client_method(); #else method = SSLv3_client_method(); #endif ctx = SSL_CTX_new(method); #ifndef NO_PSK SSL_CTX_set_psk_client_callback(ctx, my_psk_client_cb); #endif #ifdef OPENSSL_EXTRA SSL_CTX_set_default_passwd_cb(ctx, PasswordCallBack); #endif #ifdef CYASSL_SNIFFER /* don't use EDH, can't sniff tmp keys */ SSL_CTX_set_cipher_list(ctx, "AES256-SHA"); #endif #ifndef NO_FILESYSTEM if (SSL_CTX_load_verify_locations(ctx, caCert, 0) != SSL_SUCCESS) err_sys("can't load ca file"); #ifdef HAVE_ECC if (SSL_CTX_load_verify_locations(ctx, eccCert, 0) != SSL_SUCCESS) err_sys("can't load ca file"); #endif #else load_buffer(ctx, caCert, CYASSL_CA); #endif #ifdef VERIFY_CALLBACK SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, myVerify); #endif if (argc == 3) { /* ./client server securePort */ SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, 0); /* TODO: add ca cert */ /* this is just to allow easy testing of other servers */ tcp_connect(&sockfd, argv[1], (short)atoi(argv[2])); } else if (argc == 1) { /* ./client // plain mode */ /* for client cert authentication if server requests */ #ifndef NO_FILESYSTEM if (SSL_CTX_use_certificate_file(ctx, cliCert, SSL_FILETYPE_PEM) != SSL_SUCCESS) err_sys("can't load client cert file"); if (SSL_CTX_use_PrivateKey_file(ctx, cliKey, SSL_FILETYPE_PEM) != SSL_SUCCESS) err_sys("can't load client key file"); #else load_buffer(ctx, cliCert, CYASSL_CERT); load_buffer(ctx, cliKey, CYASSL_KEY); #endif tcp_connect(&sockfd, yasslIP, yasslPort); } else if (argc == 2) { /* time passed in number of connects give average */ int times = atoi(argv[1]); int i = 0; double start = current_time(), avg; for (i = 0; i < times; i++) { tcp_connect(&sockfd, yasslIP, yasslPort); ssl = SSL_new(ctx); SSL_set_fd(ssl, sockfd); if (SSL_connect(ssl) != SSL_SUCCESS) err_sys("SSL_connect failed"); SSL_shutdown(ssl); SSL_free(ssl); CloseSocket(sockfd); } avg = current_time() - start; avg /= times; avg *= 1000; /* milliseconds */ printf("SSL_connect avg took:%6.3f milliseconds\n", avg); SSL_CTX_free(ctx); ((func_args*)args)->return_code = 0; return; } else err_sys("usage: ./client server securePort"); ssl = SSL_new(ctx); SSL_set_fd(ssl, sockfd); if (argc != 3) CyaSSL_check_domain_name(ssl, "www.yassl.com"); #ifdef NON_BLOCKING tcp_set_nonblocking(&sockfd); NonBlockingSSL_Connect(ssl); #else #ifndef CYASSL_CALLBACKS if (SSL_connect(ssl) != SSL_SUCCESS) { /* see note at top of README */ int err = SSL_get_error(ssl, 0); char buffer[80]; printf("err = %d, %s\n", err, ERR_error_string(err, buffer)); err_sys("SSL_connect failed");/* if you're getting an error here */ } #else timeout.tv_sec = 2; timeout.tv_usec = 0; NonBlockingSSL_Connect(ssl); /* will keep retrying on timeout */ #endif #endif showPeer(ssl); if (argc == 3) { printf("SSL connect ok, sending GET...\n"); msgSz = 28; strncpy(msg, "GET /index.html HTTP/1.0\r\n\r\n", msgSz); } if (SSL_write(ssl, msg, msgSz) != msgSz) err_sys("SSL_write failed"); input = SSL_read(ssl, reply, sizeof(reply)); if (input > 0) { reply[input] = 0; printf("Server response: %s\n", reply); if (argc == 3) { /* get html */ while (1) { input = SSL_read(ssl, reply, sizeof(reply)); if (input > 0) { reply[input] = 0; printf("%s\n", reply); } else break; } } } #ifdef TEST_RESUME #ifdef CYASSL_DTLS strncpy(msg, "break", 6); msgSz = (int)strlen(msg); /* try to send session close */ SSL_write(ssl, msg, msgSz); #endif session = SSL_get_session(ssl); sslResume = SSL_new(ctx); #endif SSL_shutdown(ssl); SSL_free(ssl); CloseSocket(sockfd); #ifdef TEST_RESUME #ifdef CYASSL_DTLS #ifdef USE_WINDOWS_API Sleep(500); #else sleep(1); #endif #endif if (argc == 3) tcp_connect(&sockfd, argv[1], (short)atoi(argv[2])); else tcp_connect(&sockfd, yasslIP, yasslPort); SSL_set_fd(sslResume, sockfd); SSL_set_session(sslResume, session); showPeer(sslResume); if (SSL_connect(sslResume) != SSL_SUCCESS) err_sys("SSL resume failed"); #ifdef OPENSSL_EXTRA if (SSL_session_reused(sslResume)) printf("reused session id\n"); else printf("didn't reuse session id!!!\n"); #endif if (SSL_write(sslResume, resumeMsg, resumeSz) != resumeSz) err_sys("SSL_write failed"); input = SSL_read(sslResume, reply, sizeof(reply)); if (input > 0) { reply[input] = 0; printf("Server resume response: %s\n", reply); } /* try to send session break */ SSL_write(sslResume, msg, msgSz); SSL_shutdown(sslResume); SSL_free(sslResume); #endif /* TEST_RESUME */ SSL_CTX_free(ctx); CloseSocket(sockfd); ((func_args*)args)->return_code = 0; }
/* {{{ data_accept */ databuf_t* data_accept(databuf_t *data, ftpbuf_t *ftp) { php_sockaddr_storage addr; socklen_t size; #ifdef HAVE_FTP_SSL SSL_CTX *ctx; SSL_SESSION *session; int err, res; zend_bool retry; #endif if (data->fd != -1) { goto data_accepted; } size = sizeof(addr); data->fd = my_accept(ftp, data->listener, (struct sockaddr*) &addr, &size); closesocket(data->listener); data->listener = -1; if (data->fd == -1) { efree(data); return NULL; } data_accepted: #ifdef HAVE_FTP_SSL /* now enable ssl if we need to */ if (ftp->use_ssl && ftp->use_ssl_for_data) { ctx = SSL_get_SSL_CTX(ftp->ssl_handle); if (ctx == NULL) { php_error_docref(NULL, E_WARNING, "data_accept: failed to retreive the existing SSL context"); return 0; } data->ssl_handle = SSL_new(ctx); if (data->ssl_handle == NULL) { php_error_docref(NULL, E_WARNING, "data_accept: failed to create the SSL handle"); return 0; } SSL_set_fd(data->ssl_handle, data->fd); if (ftp->old_ssl) { SSL_copy_session_id(data->ssl_handle, ftp->ssl_handle); } /* get the session from the control connection so we can re-use it */ session = SSL_get_session(ftp->ssl_handle); if (session == NULL) { php_error_docref(NULL, E_WARNING, "data_accept: failed to retreive the existing SSL session"); SSL_free(data->ssl_handle); return 0; } /* and set it on the data connection */ res = SSL_set_session(data->ssl_handle, session); if (res == 0) { php_error_docref(NULL, E_WARNING, "data_accept: failed to set the existing SSL session"); SSL_free(data->ssl_handle); return 0; } do { res = SSL_connect(data->ssl_handle); err = SSL_get_error(data->ssl_handle, res); switch (err) { case SSL_ERROR_NONE: retry = 0; break; case SSL_ERROR_ZERO_RETURN: retry = 0; SSL_shutdown(data->ssl_handle); break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: { php_pollfd p; int i; p.fd = ftp->fd; p.events = (err == SSL_ERROR_WANT_READ) ? (POLLIN|POLLPRI) : POLLOUT; p.revents = 0; i = php_poll2(&p, 1, 300); retry = i > 0; } break; default: php_error_docref(NULL, E_WARNING, "data_accept: SSL/TLS handshake failed"); SSL_shutdown(data->ssl_handle); SSL_free(data->ssl_handle); return 0; } } while (retry); data->ssl_active = 1; } #endif return data; }
static int init_ssl(CLI *c) { int i, err; if(!(c->ssl=SSL_new(ctx))) { sslerror("SSL_new"); return -1; } #if SSLEAY_VERSION_NUMBER >= 0x0922 SSL_set_session_id_context(c->ssl, sid_ctx, strlen(sid_ctx)); #endif if(options.option.client) { /* Attempt to use the most recent id in the session cache */ #ifndef HAVE_YASSL /* yassl add, ctx members available */ if(ctx->session_cache_head) if(!SSL_set_session(c->ssl, ctx->session_cache_head)) log(LOG_WARNING, "Cannot set SSL session id to most recent used"); #endif /* yassl end add */ SSL_set_fd(c->ssl, c->remote_fd.fd); SSL_set_connect_state(c->ssl); } else { if(c->local_rfd.fd==c->local_wfd.fd) SSL_set_fd(c->ssl, c->local_rfd.fd); else { /* Does it make sence to have SSL on STDIN/STDOUT? */ SSL_set_rfd(c->ssl, c->local_rfd.fd); SSL_set_wfd(c->ssl, c->local_wfd.fd); } SSL_set_accept_state(c->ssl); } /* Setup some values for transfer() function */ if(options.option.client) { c->sock_rfd=&(c->local_rfd); c->sock_wfd=&(c->local_wfd); c->ssl_rfd=c->ssl_wfd=&(c->remote_fd); } else { c->sock_rfd=c->sock_wfd=&(c->remote_fd); c->ssl_rfd=&(c->local_rfd); c->ssl_wfd=&(c->local_wfd); } while(1) { if(options.option.client) i=SSL_connect(c->ssl); else i=SSL_accept(c->ssl); err=SSL_get_error(c->ssl, i); if(err==SSL_ERROR_NONE) break; /* ok -> done */ if(err==SSL_ERROR_WANT_READ) { if(waitforsocket(c->ssl_rfd->fd, 0, c->opt->timeout_busy)==1) continue; /* ok -> retry */ return -1; /* timeout or error */ } if(err==SSL_ERROR_WANT_WRITE) { if(waitforsocket(c->ssl_wfd->fd, 1, c->opt->timeout_busy)==1) continue; /* ok -> retry */ return -1; /* timeout or error */ } if(err==SSL_ERROR_SYSCALL) { switch(get_last_socket_error()) { case EINTR: case EAGAIN: continue; } } if(options.option.client) sslerror("SSL_connect"); else sslerror("SSL_accept"); return -1; } print_cipher(c); return 0; /* OK */ }
bool TLS_SOCKET_CLASS::connect() // DESCRIPTION : Set up connection to the remote host. // PRECONDITIONS : // POSTCONDITIONS : // EXCEPTIONS : // NOTES : //<<=========================================================================== { BIO* bio_ptr; long error; ostringstream remoteHost; // make sure the socket is not already in use if (terminatingM) { if (loggerM_ptr) { loggerM_ptr->text(LOG_ERROR, 1, "Secure Socket - In process of terminating. Cannot connect."); } // return - in process of termintating return false; } if (connectedM) { if (loggerM_ptr) { loggerM_ptr->text(LOG_ERROR, 1, "Secure Socket - Already connected to \"%s\". Cannot connect again.", remoteHostnameM.c_str()); } // return - already connected return false; } else if (listeningM) { if (loggerM_ptr) { loggerM_ptr->text(LOG_ERROR, 1, "Secure Socket - Socket is already listening to port %d. Cannot connect to \"%s\".", localListenPortM, remoteHostnameM.c_str()); } // return - socket is already being used to listen return false; } connectedM = false; if (remoteHostnameM.length() == 0) { if (loggerM_ptr) { loggerM_ptr->text(LOG_ERROR, 1, "Secure Socket - No Remote Hostname defined. Can't make connection."); } // return - don't know what to connect to return false; } // create the transport object remoteHost << remoteHostnameM << ':' << remoteConnectPortM; if (loggerM_ptr) { loggerM_ptr->text(LOG_DEBUG, 1, "Secure Socket - tls::connect(%s)", remoteHost.str().c_str()); } bio_ptr = BIO_new_connect(const_cast<char *>(remoteHost.str().c_str())); if (!bio_ptr) { openSslError("creating connection"); return false; } if (BIO_do_connect(bio_ptr) <= 0) { openSslError("connecting to remote machine"); BIO_free(bio_ptr); return false; } // create the secure socket object if (sslM_ptr != NULL) { SSL_free(sslM_ptr); } sslM_ptr = SSL_new(ctxM_ptr); if (sslM_ptr == NULL) { openSslError("creating secure socket object"); BIO_free(bio_ptr); return false; } // set the 'this' pointer for the callbacks openSslMsgCallback and openSslVerifyCallback SSL_set_msg_callback_arg(sslM_ptr, static_cast<void*>(this)); // restore the session if (cacheTlsSessionsM && (savedClientSessionM_ptr != NULL)) { SSL_set_session(sslM_ptr, savedClientSessionM_ptr); SSL_SESSION_free(savedClientSessionM_ptr); // we're done with the saved session now, we'll // get a new one when the session is closed. savedClientSessionM_ptr = NULL; } // make the SSL connection SSL_set_bio(sslM_ptr, bio_ptr, bio_ptr); // the ssl takes over the mangement of the bio memory if (SSL_connect(sslM_ptr) <= 0) { openSslError("connecting to %s", remoteHost.str().c_str()); SSL_free(sslM_ptr); sslM_ptr = NULL; return false; } // make sure everything is OK error = postConnectionCheck(sslM_ptr); if (error != X509_V_OK) { openSslError("checking connection parameters after connecting to %s", remoteHost.str().c_str()); SSL_free(sslM_ptr); sslM_ptr = NULL; return false; } // socket connected to peer connectedM = true; if (loggerM_ptr && (loggerM_ptr->getLogMask() & LOG_DEBUG)) { char buffer[128]; SSL_CIPHER_description(SSL_get_current_cipher(sslM_ptr), buffer, 128); loggerM_ptr->text(LOG_DEBUG, 1, "Secure Socket - %s connection opened using cipher %s", SSL_get_version(sslM_ptr), buffer); } // return whether connected or not return connectedM; }
static int test_dtls_drop_records(int idx) { SSL_CTX *sctx = NULL, *cctx = NULL; SSL *serverssl = NULL, *clientssl = NULL; BIO *c_to_s_fbio, *mempackbio; int testresult = 0; int epoch = 0; SSL_SESSION *sess = NULL; int cli_to_srv_epoch0, cli_to_srv_epoch1, srv_to_cli_epoch0; if (!TEST_true(create_ssl_ctx_pair(DTLS_server_method(), DTLS_client_method(), DTLS1_VERSION, DTLS_MAX_VERSION, &sctx, &cctx, cert, privkey))) return 0; if (idx >= TOTAL_FULL_HAND_RECORDS) { /* We're going to do a resumption handshake. Get a session first. */ if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, NULL, NULL)) || !TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)) || !TEST_ptr(sess = SSL_get1_session(clientssl))) goto end; SSL_shutdown(clientssl); SSL_shutdown(serverssl); SSL_free(serverssl); SSL_free(clientssl); serverssl = clientssl = NULL; cli_to_srv_epoch0 = CLI_TO_SRV_RESUME_EPOCH_0_RECS; cli_to_srv_epoch1 = CLI_TO_SRV_RESUME_EPOCH_1_RECS; srv_to_cli_epoch0 = SRV_TO_CLI_RESUME_EPOCH_0_RECS; idx -= TOTAL_FULL_HAND_RECORDS; } else { cli_to_srv_epoch0 = CLI_TO_SRV_EPOCH_0_RECS; cli_to_srv_epoch1 = CLI_TO_SRV_EPOCH_1_RECS; srv_to_cli_epoch0 = SRV_TO_CLI_EPOCH_0_RECS; } c_to_s_fbio = BIO_new(bio_f_tls_dump_filter()); if (!TEST_ptr(c_to_s_fbio)) goto end; /* BIO is freed by create_ssl_connection on error */ if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, NULL, c_to_s_fbio))) goto end; if (sess != NULL) { if (!TEST_true(SSL_set_session(clientssl, sess))) goto end; } DTLS_set_timer_cb(clientssl, timer_cb); DTLS_set_timer_cb(serverssl, timer_cb); /* Work out which record to drop based on the test number */ if (idx >= cli_to_srv_epoch0 + cli_to_srv_epoch1) { mempackbio = SSL_get_wbio(serverssl); idx -= cli_to_srv_epoch0 + cli_to_srv_epoch1; if (idx >= srv_to_cli_epoch0) { epoch = 1; idx -= srv_to_cli_epoch0; } } else { mempackbio = SSL_get_wbio(clientssl); if (idx >= cli_to_srv_epoch0) { epoch = 1; idx -= cli_to_srv_epoch0; } mempackbio = BIO_next(mempackbio); } BIO_ctrl(mempackbio, MEMPACKET_CTRL_SET_DROP_EPOCH, epoch, NULL); BIO_ctrl(mempackbio, MEMPACKET_CTRL_SET_DROP_REC, idx, NULL); if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE))) goto end; if (sess != NULL && !TEST_true(SSL_session_reused(clientssl))) goto end; /* If the test did what we planned then it should have dropped a record */ if (!TEST_int_eq((int)BIO_ctrl(mempackbio, MEMPACKET_CTRL_GET_DROP_REC, 0, NULL), -1)) goto end; testresult = 1; end: SSL_SESSION_free(sess); SSL_free(serverssl); SSL_free(clientssl); SSL_CTX_free(sctx); SSL_CTX_free(cctx); return testresult; }
/* * Note that |extra| points to the correct client/server configuration * within |test_ctx|. When configuring the handshake, general mode settings * are taken from |test_ctx|, and client/server-specific settings should be * taken from |extra|. * * The configuration code should never reach into |test_ctx->extra| or * |test_ctx->resume_extra| directly. * * (We could refactor test mode settings into a substructure. This would result * in cleaner argument passing but would complicate the test configuration * parsing.) */ static HANDSHAKE_RESULT *do_handshake_internal( SSL_CTX *server_ctx, SSL_CTX *server2_ctx, SSL_CTX *client_ctx, const SSL_TEST_CTX *test_ctx, const SSL_TEST_EXTRA_CONF *extra, SSL_SESSION *session_in, SSL_SESSION **session_out) { PEER server, client; BIO *client_to_server, *server_to_client; HANDSHAKE_EX_DATA server_ex_data, client_ex_data; CTX_DATA client_ctx_data, server_ctx_data, server2_ctx_data; HANDSHAKE_RESULT *ret = HANDSHAKE_RESULT_new(); int client_turn = 1; connect_phase_t phase = HANDSHAKE; handshake_status_t status = HANDSHAKE_RETRY; const unsigned char* tick = NULL; size_t tick_len = 0; SSL_SESSION* sess = NULL; const unsigned char *proto = NULL; /* API dictates unsigned int rather than size_t. */ unsigned int proto_len = 0; memset(&server_ctx_data, 0, sizeof(server_ctx_data)); memset(&server2_ctx_data, 0, sizeof(server2_ctx_data)); memset(&client_ctx_data, 0, sizeof(client_ctx_data)); memset(&server, 0, sizeof(server)); memset(&client, 0, sizeof(client)); configure_handshake_ctx(server_ctx, server2_ctx, client_ctx, test_ctx, extra, &server_ctx_data, &server2_ctx_data, &client_ctx_data); /* Setup SSL and buffers; additional configuration happens below. */ create_peer(&server, server_ctx); create_peer(&client, client_ctx); server.bytes_to_write = client.bytes_to_read = test_ctx->app_data_size; client.bytes_to_write = server.bytes_to_read = test_ctx->app_data_size; configure_handshake_ssl(server.ssl, client.ssl, extra); if (session_in != NULL) { /* In case we're testing resumption without tickets. */ TEST_check(SSL_CTX_add_session(server_ctx, session_in)); TEST_check(SSL_set_session(client.ssl, session_in)); } memset(&server_ex_data, 0, sizeof(server_ex_data)); memset(&client_ex_data, 0, sizeof(client_ex_data)); ret->result = SSL_TEST_INTERNAL_ERROR; client_to_server = BIO_new(BIO_s_mem()); server_to_client = BIO_new(BIO_s_mem()); TEST_check(client_to_server != NULL); TEST_check(server_to_client != NULL); /* Non-blocking bio. */ BIO_set_nbio(client_to_server, 1); BIO_set_nbio(server_to_client, 1); SSL_set_connect_state(client.ssl); SSL_set_accept_state(server.ssl); /* The bios are now owned by the SSL object. */ SSL_set_bio(client.ssl, server_to_client, client_to_server); TEST_check(BIO_up_ref(server_to_client) > 0); TEST_check(BIO_up_ref(client_to_server) > 0); SSL_set_bio(server.ssl, client_to_server, server_to_client); ex_data_idx = SSL_get_ex_new_index(0, "ex data", NULL, NULL, NULL); TEST_check(ex_data_idx >= 0); TEST_check(SSL_set_ex_data(server.ssl, ex_data_idx, &server_ex_data) == 1); TEST_check(SSL_set_ex_data(client.ssl, ex_data_idx, &client_ex_data) == 1); SSL_set_info_callback(server.ssl, &info_cb); SSL_set_info_callback(client.ssl, &info_cb); client.status = server.status = PEER_RETRY; /* * Half-duplex handshake loop. * Client and server speak to each other synchronously in the same process. * We use non-blocking BIOs, so whenever one peer blocks for read, it * returns PEER_RETRY to indicate that it's the other peer's turn to write. * The handshake succeeds once both peers have succeeded. If one peer * errors out, we also let the other peer retry (and presumably fail). */ for(;;) { if (client_turn) { do_connect_step(&client, phase); status = handshake_status(client.status, server.status, 1 /* client went last */); } else { do_connect_step(&server, phase); status = handshake_status(server.status, client.status, 0 /* server went last */); } switch (status) { case HANDSHAKE_SUCCESS: phase = next_phase(phase); if (phase == CONNECTION_DONE) { ret->result = SSL_TEST_SUCCESS; goto err; } else { client.status = server.status = PEER_RETRY; /* * For now, client starts each phase. Since each phase is * started separately, we can later control this more * precisely, for example, to test client-initiated and * server-initiated shutdown. */ client_turn = 1; break; } case CLIENT_ERROR: ret->result = SSL_TEST_CLIENT_FAIL; goto err; case SERVER_ERROR: ret->result = SSL_TEST_SERVER_FAIL; goto err; case INTERNAL_ERROR: ret->result = SSL_TEST_INTERNAL_ERROR; goto err; case HANDSHAKE_RETRY: /* Continue. */ client_turn ^= 1; break; } } err: ret->server_alert_sent = server_ex_data.alert_sent; ret->server_num_fatal_alerts_sent = server_ex_data.num_fatal_alerts_sent; ret->server_alert_received = client_ex_data.alert_received; ret->client_alert_sent = client_ex_data.alert_sent; ret->client_num_fatal_alerts_sent = client_ex_data.num_fatal_alerts_sent; ret->client_alert_received = server_ex_data.alert_received; ret->server_protocol = SSL_version(server.ssl); ret->client_protocol = SSL_version(client.ssl); ret->servername = server_ex_data.servername; if ((sess = SSL_get0_session(client.ssl)) != NULL) SSL_SESSION_get0_ticket(sess, &tick, &tick_len); if (tick == NULL || tick_len == 0) ret->session_ticket = SSL_TEST_SESSION_TICKET_NO; else ret->session_ticket = SSL_TEST_SESSION_TICKET_YES; ret->session_ticket_do_not_call = server_ex_data.session_ticket_do_not_call; #ifndef OPENSSL_NO_NEXTPROTONEG SSL_get0_next_proto_negotiated(client.ssl, &proto, &proto_len); ret->client_npn_negotiated = dup_str(proto, proto_len); SSL_get0_next_proto_negotiated(server.ssl, &proto, &proto_len); ret->server_npn_negotiated = dup_str(proto, proto_len); #endif SSL_get0_alpn_selected(client.ssl, &proto, &proto_len); ret->client_alpn_negotiated = dup_str(proto, proto_len); SSL_get0_alpn_selected(server.ssl, &proto, &proto_len); ret->server_alpn_negotiated = dup_str(proto, proto_len); ret->client_resumed = SSL_session_reused(client.ssl); ret->server_resumed = SSL_session_reused(server.ssl); if (session_out != NULL) *session_out = SSL_get1_session(client.ssl); ctx_data_free_data(&server_ctx_data); ctx_data_free_data(&server2_ctx_data); ctx_data_free_data(&client_ctx_data); peer_free_data(&server); peer_free_data(&client); return ret; }
int MQTTClient_connectURIVersion(MQTTClient handle, MQTTClient_connectOptions* options, const char* serverURI, int MQTTVersion, START_TIME_TYPE start, long millisecsTimeout) { MQTTClients* m = handle; int rc = SOCKET_ERROR; int sessionPresent = 0; FUNC_ENTRY; if (m->ma && !running) { Thread_start(MQTTClient_run, handle); if (MQTTClient_elapsed(start) >= millisecsTimeout) { rc = SOCKET_ERROR; goto exit; } MQTTClient_sleep(100L); } Log(TRACE_MIN, -1, "Connecting to serverURI %s with MQTT version %d", serverURI, MQTTVersion); #if defined(OPENSSL) rc = MQTTProtocol_connect(serverURI, m->c, m->ssl, MQTTVersion); #else rc = MQTTProtocol_connect(serverURI, m->c, MQTTVersion); #endif if (rc == SOCKET_ERROR) goto exit; if (m->c->connect_state == 0) { rc = SOCKET_ERROR; goto exit; } if (m->c->connect_state == 1) /* TCP connect started - wait for completion */ { Thread_unlock_mutex(mqttclient_mutex); MQTTClient_waitfor(handle, CONNECT, &rc, millisecsTimeout - MQTTClient_elapsed(start)); Thread_lock_mutex(mqttclient_mutex); if (rc != 0) { rc = SOCKET_ERROR; goto exit; } #if defined(OPENSSL) if (m->ssl) { if (SSLSocket_setSocketForSSL(&m->c->net, m->c->sslopts) != MQTTCLIENT_SUCCESS) { if (m->c->session != NULL) if ((rc = SSL_set_session(m->c->net.ssl, m->c->session)) != 1) Log(TRACE_MIN, -1, "Failed to set SSL session with stored data, non critical"); rc = SSLSocket_connect(m->c->net.ssl, m->c->net.socket); if (rc == TCPSOCKET_INTERRUPTED) m->c->connect_state = 2; /* the connect is still in progress */ else if (rc == SSL_FATAL) { rc = SOCKET_ERROR; goto exit; } else if (rc == 1) { rc = MQTTCLIENT_SUCCESS; m->c->connect_state = 3; if (MQTTPacket_send_connect(m->c, MQTTVersion) == SOCKET_ERROR) { rc = SOCKET_ERROR; goto exit; } if (!m->c->cleansession && m->c->session == NULL) m->c->session = SSL_get1_session(m->c->net.ssl); } } else { rc = SOCKET_ERROR; goto exit; } } else { #endif m->c->connect_state = 3; /* TCP connect completed, in which case send the MQTT connect packet */ if (MQTTPacket_send_connect(m->c, MQTTVersion) == SOCKET_ERROR) { rc = SOCKET_ERROR; goto exit; } #if defined(OPENSSL) } #endif } #if defined(OPENSSL) if (m->c->connect_state == 2) /* SSL connect sent - wait for completion */ { Thread_unlock_mutex(mqttclient_mutex); MQTTClient_waitfor(handle, CONNECT, &rc, millisecsTimeout - MQTTClient_elapsed(start)); Thread_lock_mutex(mqttclient_mutex); if (rc != 1) { rc = SOCKET_ERROR; goto exit; } if(!m->c->cleansession && m->c->session == NULL) m->c->session = SSL_get1_session(m->c->net.ssl); m->c->connect_state = 3; /* TCP connect completed, in which case send the MQTT connect packet */ if (MQTTPacket_send_connect(m->c, MQTTVersion) == SOCKET_ERROR) { rc = SOCKET_ERROR; goto exit; } } #endif if (m->c->connect_state == 3) /* MQTT connect sent - wait for CONNACK */ { MQTTPacket* pack = NULL; Thread_unlock_mutex(mqttclient_mutex); pack = MQTTClient_waitfor(handle, CONNACK, &rc, millisecsTimeout - MQTTClient_elapsed(start)); Thread_lock_mutex(mqttclient_mutex); if (pack == NULL) rc = SOCKET_ERROR; else { Connack* connack = (Connack*)pack; Log(TRACE_PROTOCOL, 1, NULL, m->c->net.socket, m->c->clientID, connack->rc); if ((rc = connack->rc) == MQTTCLIENT_SUCCESS) { m->c->connected = 1; m->c->good = 1; m->c->connect_state = 0; if (MQTTVersion == 4) sessionPresent = connack->flags.bits.sessionPresent; if (m->c->cleansession) rc = MQTTClient_cleanSession(m->c); if (m->c->outboundMsgs->count > 0) { ListElement* outcurrent = NULL; while (ListNextElement(m->c->outboundMsgs, &outcurrent)) { Messages* m = (Messages*)(outcurrent->content); m->lastTouch = 0; } MQTTProtocol_retry((time_t)0, 1, 1); if (m->c->connected != 1) rc = MQTTCLIENT_DISCONNECTED; } } free(connack); m->pack = NULL; } } exit: if (rc == MQTTCLIENT_SUCCESS) { if (options->struct_version == 4) /* means we have to fill out return values */ { options->returned.serverURI = serverURI; options->returned.MQTTVersion = MQTTVersion; options->returned.sessionPresent = sessionPresent; } } else { Thread_unlock_mutex(mqttclient_mutex); MQTTClient_disconnect1(handle, 0, 0, (MQTTVersion == 3)); /* not "internal" because we don't want to call connection lost */ Thread_lock_mutex(mqttclient_mutex); } FUNC_EXIT_RC(rc); return rc; }
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; }
static void init_ssl(CLI *c) { int i, err; SSL_SESSION *old_session; int unsafe_openssl; c->ssl=SSL_new(c->opt->ctx); if(!c->ssl) { sslerror("SSL_new"); longjmp(c->err, 1); } SSL_set_ex_data(c->ssl, cli_index, c); /* for callbacks */ if(c->opt->option.client) { #ifndef OPENSSL_NO_TLSEXT if(c->opt->sni) { s_log(LOG_DEBUG, "SNI: host name: %s", c->opt->sni); if(!SSL_set_tlsext_host_name(c->ssl, c->opt->sni)) { sslerror("SSL_set_tlsext_host_name"); longjmp(c->err, 1); } } #endif if(c->opt->session) { enter_critical_section(CRIT_SESSION); SSL_set_session(c->ssl, c->opt->session); leave_critical_section(CRIT_SESSION); } SSL_set_fd(c->ssl, c->remote_fd.fd); SSL_set_connect_state(c->ssl); } else { if(c->local_rfd.fd==c->local_wfd.fd) SSL_set_fd(c->ssl, c->local_rfd.fd); else { /* does it make sense to have SSL on STDIN/STDOUT? */ SSL_set_rfd(c->ssl, c->local_rfd.fd); SSL_set_wfd(c->ssl, c->local_wfd.fd); } SSL_set_accept_state(c->ssl); } /* setup some values for transfer() function */ if(c->opt->option.client) { c->sock_rfd=&(c->local_rfd); c->sock_wfd=&(c->local_wfd); c->ssl_rfd=c->ssl_wfd=&(c->remote_fd); } else { c->sock_rfd=c->sock_wfd=&(c->remote_fd); c->ssl_rfd=&(c->local_rfd); c->ssl_wfd=&(c->local_wfd); } unsafe_openssl=SSLeay()<0x0090810fL || (SSLeay()>=0x10000000L && SSLeay()<0x1000002fL); while(1) { /* critical section for OpenSSL version < 0.9.8p or 1.x.x < 1.0.0b * * this critical section is a crude workaround for CVE-2010-3864 * * see http://www.securityfocus.com/bid/44884 for details * * alternative solution is to disable internal session caching * * NOTE: this critical section also covers callbacks (e.g. OCSP) */ if(unsafe_openssl) enter_critical_section(CRIT_SSL); if(c->opt->option.client) i=SSL_connect(c->ssl); else i=SSL_accept(c->ssl); if(unsafe_openssl) leave_critical_section(CRIT_SSL); err=SSL_get_error(c->ssl, i); if(err==SSL_ERROR_NONE) break; /* ok -> done */ if(err==SSL_ERROR_WANT_READ || err==SSL_ERROR_WANT_WRITE) { s_poll_init(c->fds); s_poll_add(c->fds, c->ssl_rfd->fd, err==SSL_ERROR_WANT_READ, err==SSL_ERROR_WANT_WRITE); switch(s_poll_wait(c->fds, c->opt->timeout_busy, 0)) { case -1: sockerror("init_ssl: s_poll_wait"); longjmp(c->err, 1); case 0: s_log(LOG_INFO, "init_ssl: s_poll_wait:" " TIMEOUTbusy exceeded: sending reset"); longjmp(c->err, 1); case 1: break; /* OK */ default: s_log(LOG_ERR, "init_ssl: s_poll_wait: unknown result"); longjmp(c->err, 1); } continue; /* ok -> retry */ } if(err==SSL_ERROR_SYSCALL) { switch(get_last_socket_error()) { case S_EINTR: case S_EWOULDBLOCK: #if S_EAGAIN!=S_EWOULDBLOCK case S_EAGAIN: #endif continue; } } if(c->opt->option.client) sslerror("SSL_connect"); else sslerror("SSL_accept"); longjmp(c->err, 1); } if(SSL_session_reused(c->ssl)) { s_log(LOG_INFO, "SSL %s: previous session reused", c->opt->option.client ? "connected" : "accepted"); } else { /* a new session was negotiated */ #ifdef USE_WIN32 win_new_chain(c); #endif if(c->opt->option.client) { s_log(LOG_INFO, "SSL connected: new session negotiated"); enter_critical_section(CRIT_SESSION); old_session=c->opt->session; c->opt->session=SSL_get1_session(c->ssl); /* store it */ if(old_session) SSL_SESSION_free(old_session); /* release the old one */ leave_critical_section(CRIT_SESSION); } else s_log(LOG_INFO, "SSL accepted: new session negotiated"); print_cipher(c); } }
/* * This function loads all the client/CA certificates and CRLs. Setup the TLS * layer and do all necessary magic. */ static CURLcode cyassl_connect_step1(struct connectdata *conn, int sockindex) { struct SessionHandle *data = conn->data; struct ssl_connect_data* conssl = &conn->ssl[sockindex]; SSL_METHOD* req_method = NULL; void* ssl_sessionid = NULL; curl_socket_t sockfd = conn->sock[sockindex]; if(conssl->state == ssl_connection_complete) return CURLE_OK; /* check to see if we've been told to use an explicit SSL/TLS version */ switch(data->set.ssl.version) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: #if LIBCYASSL_VERSION_HEX >= 0x03003000 /* 3.3.0 */ /* the minimum version is set later after the SSL object is created */ req_method = SSLv23_client_method(); #else infof(data, "CyaSSL <3.3.0 cannot be configured to use TLS 1.0-1.2, " "TLS 1.0 is used exclusively\n"); req_method = TLSv1_client_method(); #endif break; case CURL_SSLVERSION_TLSv1_0: req_method = TLSv1_client_method(); break; case CURL_SSLVERSION_TLSv1_1: req_method = TLSv1_1_client_method(); break; case CURL_SSLVERSION_TLSv1_2: req_method = TLSv1_2_client_method(); break; case CURL_SSLVERSION_SSLv3: req_method = SSLv3_client_method(); break; case CURL_SSLVERSION_SSLv2: failf(data, "CyaSSL does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; default: failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); return CURLE_SSL_CONNECT_ERROR; } if(!req_method) { failf(data, "SSL: couldn't create a method!"); return CURLE_OUT_OF_MEMORY; } if(conssl->ctx) SSL_CTX_free(conssl->ctx); conssl->ctx = SSL_CTX_new(req_method); if(!conssl->ctx) { failf(data, "SSL: couldn't create a context!"); return CURLE_OUT_OF_MEMORY; } #ifndef NO_FILESYSTEM /* load trusted cacert */ if(data->set.str[STRING_SSL_CAFILE]) { if(!SSL_CTX_load_verify_locations(conssl->ctx, data->set.str[STRING_SSL_CAFILE], data->set.str[STRING_SSL_CAPATH])) { if(data->set.ssl.verifypeer) { /* Fail if we insist on successfully verifying the server. */ failf(data, "error setting certificate verify locations:\n" " CAfile: %s\n CApath: %s", data->set.str[STRING_SSL_CAFILE]? data->set.str[STRING_SSL_CAFILE]: "none", data->set.str[STRING_SSL_CAPATH]? data->set.str[STRING_SSL_CAPATH] : "none"); return CURLE_SSL_CACERT_BADFILE; } else { /* Just continue with a warning if no strict certificate verification is required. */ infof(data, "error setting certificate verify locations," " continuing anyway:\n"); } } else { /* Everything is fine. */ infof(data, "successfully set certificate verify locations:\n"); } infof(data, " CAfile: %s\n" " CApath: %s\n", data->set.str[STRING_SSL_CAFILE] ? data->set.str[STRING_SSL_CAFILE]: "none", data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]: "none"); } /* Load the client certificate, and private key */ if(data->set.str[STRING_CERT] && data->set.str[STRING_KEY]) { int file_type = do_file_type(data->set.str[STRING_CERT_TYPE]); if(SSL_CTX_use_certificate_file(conssl->ctx, data->set.str[STRING_CERT], file_type) != 1) { failf(data, "unable to use client certificate (no key or wrong pass" " phrase?)"); return CURLE_SSL_CONNECT_ERROR; } file_type = do_file_type(data->set.str[STRING_KEY_TYPE]); if(SSL_CTX_use_PrivateKey_file(conssl->ctx, data->set.str[STRING_KEY], file_type) != 1) { failf(data, "unable to set private key"); return CURLE_SSL_CONNECT_ERROR; } } #endif /* !NO_FILESYSTEM */ /* SSL always tries to verify the peer, this only says whether it should * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ SSL_CTX_set_verify(conssl->ctx, data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE, NULL); /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { CURLcode result = CURLE_OK; result = (*data->set.ssl.fsslctx)(data, conssl->ctx, data->set.ssl.fsslctxp); if(result) { failf(data, "error signaled by ssl ctx callback"); return result; } } #ifdef NO_FILESYSTEM else if(data->set.ssl.verifypeer) { failf(data, "SSL: Certificates couldn't be loaded because CyaSSL was built" " with \"no filesystem\". Either disable peer verification" " (insecure) or if you are building an application with libcurl you" " can load certificates via CURLOPT_SSL_CTX_FUNCTION."); return CURLE_SSL_CONNECT_ERROR; } #endif /* Let's make an SSL structure */ if(conssl->handle) SSL_free(conssl->handle); conssl->handle = SSL_new(conssl->ctx); if(!conssl->handle) { failf(data, "SSL: couldn't create a context (handle)!"); return CURLE_OUT_OF_MEMORY; } switch(data->set.ssl.version) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: #if LIBCYASSL_VERSION_HEX >= 0x03003000 /* >= 3.3.0 */ /* short circuit evaluation to find minimum supported TLS version */ if((CyaSSL_SetMinVersion(conssl->handle, CYASSL_TLSV1) != SSL_SUCCESS) && (CyaSSL_SetMinVersion(conssl->handle, CYASSL_TLSV1_1) != SSL_SUCCESS) && (CyaSSL_SetMinVersion(conssl->handle, CYASSL_TLSV1_2) != SSL_SUCCESS)) { failf(data, "SSL: couldn't set the minimum protocol version"); return CURLE_SSL_CONNECT_ERROR; } #endif break; } /* Check if there's a cached ID we can/should use here! */ if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) { /* we got a session id, use it! */ if(!SSL_set_session(conssl->handle, ssl_sessionid)) { failf(data, "SSL: SSL_set_session failed: %s", ERR_error_string(SSL_get_error(conssl->handle, 0), NULL)); return CURLE_SSL_CONNECT_ERROR; } /* Informational message */ infof (data, "SSL re-using session ID\n"); } /* pass the raw socket into the SSL layer */ if(!SSL_set_fd(conssl->handle, (int)sockfd)) { failf(data, "SSL: SSL_set_fd failed"); return CURLE_SSL_CONNECT_ERROR; } conssl->connecting_state = ssl_connect_2; return CURLE_OK; }
/* * This is the actual startup routine for the connection. We expect that the * buffers are flushed and the "220 Ready to start TLS" was received by us, * so that we can immediately start the TLS handshake process. */ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) { int sts; int protomask; const char *cipher_list; SSL_SESSION *session = 0; SSL_CIPHER_const SSL_CIPHER *cipher; X509 *peercert; TLS_SESS_STATE *TLScontext; TLS_APPL_STATE *app_ctx = props->ctx; char *myserverid; int log_mask = app_ctx->log_mask; /* * When certificate verification is required, log trust chain validation * errors even when disabled by default for opportunistic sessions. For * DANE this only applies when using trust-anchor associations. */ if (TLS_MUST_TRUST(props->tls_level) && (!TLS_DANE_BASED(props->tls_level) || TLS_DANE_HASTA(props->dane))) log_mask |= TLS_LOG_UNTRUSTED; if (log_mask & TLS_LOG_VERBOSE) msg_info("setting up TLS connection to %s", props->namaddr); /* * First make sure we have valid protocol and cipher parameters * * Per-session protocol restrictions must be applied to the SSL connection, * as restrictions in the global context cannot be cleared. */ protomask = tls_protocol_mask(props->protocols); if (protomask == TLS_PROTOCOL_INVALID) { /* tls_protocol_mask() logs no warning. */ msg_warn("%s: Invalid TLS protocol list \"%s\": aborting TLS session", props->namaddr, props->protocols); return (0); } /* DANE requires SSLv3 or later, not SSLv2. */ if (TLS_DANE_BASED(props->tls_level)) protomask |= TLS_PROTOCOL_SSLv2; /* * Per session cipher selection for sessions with mandatory encryption * * The cipherlist is applied to the global SSL context, since it is likely * to stay the same between connections, so we make use of a 1-element * cache to return the same result for identical inputs. */ cipher_list = tls_set_ciphers(app_ctx, "TLS", props->cipher_grade, props->cipher_exclusions); if (cipher_list == 0) { msg_warn("%s: %s: aborting TLS session", props->namaddr, vstring_str(app_ctx->why)); return (0); } if (log_mask & TLS_LOG_VERBOSE) msg_info("%s: TLS cipher list \"%s\"", props->namaddr, cipher_list); /* * OpenSSL will ignore cached sessions that use the wrong protocol. So we * do not need to filter out cached sessions with the "wrong" protocol, * rather OpenSSL will simply negotiate a new session. * * We salt the session lookup key with the protocol list, so that sessions * found in the cache are plausibly acceptable. * * By the time a TLS client is negotiating ciphers it has already offered to * re-use a session, it is too late to renege on the offer. So we must * not attempt to re-use sessions whose ciphers are too weak. We salt the * session lookup key with the cipher list, so that sessions found in the * cache are always acceptable. * * With DANE, (more generally any TLScontext where we specified explicit * trust-anchor or end-entity certificates) the verification status of * the SSL session depends on the specified list. Since we verify the * certificate only during the initial handshake, we must segregate * sessions with different TA lists. Note, that TA re-verification is * not possible with cached sessions, since these don't hold the complete * peer trust chain. Therefore, we compute a digest of the sorted TA * parameters and append it to the serverid. */ myserverid = tls_serverid_digest(props, protomask, cipher_list); /* * Allocate a new TLScontext for the new connection and get an SSL * structure. Add the location of TLScontext to the SSL to later retrieve * the information inside the tls_verify_certificate_callback(). * * If session caching was enabled when TLS was initialized, the cache type * is stored in the client SSL context. */ TLScontext = tls_alloc_sess_context(log_mask, props->namaddr); TLScontext->cache_type = app_ctx->cache_type; TLScontext->serverid = myserverid; TLScontext->stream = props->stream; TLScontext->mdalg = props->mdalg; /* Alias DANE digest info from props */ TLScontext->dane = props->dane; if ((TLScontext->con = SSL_new(app_ctx->ssl_ctx)) == NULL) { msg_warn("Could not allocate 'TLScontext->con' with SSL_new()"); tls_print_errors(); tls_free_context(TLScontext); return (0); } if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) { msg_warn("Could not set application data for 'TLScontext->con'"); tls_print_errors(); tls_free_context(TLScontext); return (0); } /* * Apply session protocol restrictions. */ if (protomask != 0) SSL_set_options(TLScontext->con, TLS_SSL_OP_PROTOMASK(protomask)); /* * XXX To avoid memory leaks we must always call SSL_SESSION_free() after * calling SSL_set_session(), regardless of whether or not the session * will be reused. */ if (TLScontext->cache_type) { session = load_clnt_session(TLScontext); if (session) { SSL_set_session(TLScontext->con, session); SSL_SESSION_free(session); /* 200411 */ } } #ifdef TLSEXT_MAXLEN_host_name if (TLS_DANE_BASED(props->tls_level) && strlen(props->host) <= TLSEXT_MAXLEN_host_name) { /* * With DANE sessions, send an SNI hint. We don't care whether the * server reports finding a matching certificate or not, so no * callback is required to process the server response. Our use of * SNI is limited to giving servers that are (mis)configured to use * SNI the best opportunity to find the certificate they promised via * the associated TLSA RRs. (Generally, server administrators should * avoid SNI, and there are no plans to support SNI in the Postfix * SMTP server). * * Since the hostname is DNSSEC-validated, it must be a DNS FQDN and * thererefore valid for use with SNI. Failure to set a valid SNI * hostname is a memory allocation error, and thus transient. Since * we must not cache the session if we failed to send the SNI name, * we have little choice but to abort. */ if (!SSL_set_tlsext_host_name(TLScontext->con, props->host)) { msg_warn("%s: error setting SNI hostname to: %s", props->namaddr, props->host); tls_free_context(TLScontext); return (0); } if (log_mask & TLS_LOG_DEBUG) msg_info("%s: SNI hostname: %s", props->namaddr, props->host); } #endif /* * Before really starting anything, try to seed the PRNG a little bit * more. */ tls_int_seed(); (void) tls_ext_seed(var_tls_daemon_rand_bytes); /* * Initialize the SSL connection to connect state. This should not be * necessary anymore since 0.9.3, but the call is still in the library * and maintaining compatibility never hurts. */ SSL_set_connect_state(TLScontext->con); /* * Connect the SSL connection with the network socket. */ if (SSL_set_fd(TLScontext->con, vstream_fileno(props->stream)) != 1) { msg_info("SSL_set_fd error to %s", props->namaddr); tls_print_errors(); uncache_session(app_ctx->ssl_ctx, TLScontext); tls_free_context(TLScontext); return (0); } /* * Turn on non-blocking I/O so that we can enforce timeouts on network * I/O. */ non_blocking(vstream_fileno(props->stream), NON_BLOCKING); /* * If the debug level selected is high enough, all of the data is dumped: * TLS_LOG_TLSPKTS will dump the SSL negotiation, TLS_LOG_ALLPKTS will * dump everything. * * We do have an SSL_set_fd() and now suddenly a BIO_ routine is called? * Well there is a BIO below the SSL routines that is automatically * created for us, so we can use it for debugging purposes. */ if (log_mask & TLS_LOG_TLSPKTS) BIO_set_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb); tls_dane_set_callback(app_ctx->ssl_ctx, TLScontext); /* * Start TLS negotiations. This process is a black box that invokes our * call-backs for certificate verification. * * Error handling: If the SSL handhake fails, we print out an error message * and remove all TLS state concerning this session. */ sts = tls_bio_connect(vstream_fileno(props->stream), props->timeout, TLScontext); if (sts <= 0) { if (ERR_peek_error() != 0) { msg_info("SSL_connect error to %s: %d", props->namaddr, sts); tls_print_errors(); } else if (errno != 0) { msg_info("SSL_connect error to %s: %m", props->namaddr); } else { msg_info("SSL_connect error to %s: lost connection", props->namaddr); } uncache_session(app_ctx->ssl_ctx, TLScontext); tls_free_context(TLScontext); return (0); } /* Turn off packet dump if only dumping the handshake */ if ((log_mask & TLS_LOG_ALLPKTS) == 0) BIO_set_callback(SSL_get_rbio(TLScontext->con), 0); /* * The caller may want to know if this session was reused or if a new * session was negotiated. */ TLScontext->session_reused = SSL_session_reused(TLScontext->con); if ((log_mask & TLS_LOG_CACHE) && TLScontext->session_reused) msg_info("%s: Reusing old session", TLScontext->namaddr); /* * Do peername verification if requested and extract useful information * from the certificate for later use. */ if ((peercert = SSL_get_peer_certificate(TLScontext->con)) != 0) { TLScontext->peer_status |= TLS_CERT_FLAG_PRESENT; /* * Peer name or fingerprint verification as requested. * Unconditionally set peer_CN, issuer_CN and peer_cert_fprint. Check * fingerprint first, and avoid logging verified as untrusted in the * call to verify_extract_name(). */ verify_extract_print(TLScontext, peercert, props); verify_extract_name(TLScontext, peercert, props); if (TLScontext->log_mask & (TLS_LOG_CERTMATCH | TLS_LOG_VERBOSE | TLS_LOG_PEERCERT)) msg_info("%s: subject_CN=%s, issuer_CN=%s, " "fingerprint=%s, pkey_fingerprint=%s", props->namaddr, TLScontext->peer_CN, TLScontext->issuer_CN, TLScontext->peer_cert_fprint, TLScontext->peer_pkey_fprint); X509_free(peercert); } else { TLScontext->issuer_CN = mystrdup(""); TLScontext->peer_CN = mystrdup(""); TLScontext->peer_cert_fprint = mystrdup(""); TLScontext->peer_pkey_fprint = mystrdup(""); } /* * Finally, collect information about protocol and cipher for logging */ TLScontext->protocol = SSL_get_version(TLScontext->con); cipher = SSL_get_current_cipher(TLScontext->con); TLScontext->cipher_name = SSL_CIPHER_get_name(cipher); TLScontext->cipher_usebits = SSL_CIPHER_get_bits(cipher, &(TLScontext->cipher_algbits)); /* * The TLS engine is active. Switch to the tls_timed_read/write() * functions and make the TLScontext available to those functions. */ tls_stream_start(props->stream, TLScontext); /* * Fully secured only if trusted, matched and not insecure like halfdane. * Should perhaps also exclude "verify" (as opposed to "secure") here, * because that can be subject to insecure MX indirection, but that's * rather incompatible. Users have been warned. */ if (TLS_CERT_IS_PRESENT(TLScontext) && TLS_CERT_IS_TRUSTED(TLScontext) && TLS_CERT_IS_MATCHED(TLScontext) && !TLS_NEVER_SECURED(props->tls_level)) TLScontext->peer_status |= TLS_CERT_FLAG_SECURED; /* * All the key facts in a single log entry. */ if (log_mask & TLS_LOG_SUMMARY) msg_info("%s TLS connection established to %s: %s with cipher %s " "(%d/%d bits)", !TLS_CERT_IS_PRESENT(TLScontext) ? "Anonymous" : TLS_CERT_IS_SECURED(TLScontext) ? "Verified" : TLS_CERT_IS_TRUSTED(TLScontext) ? "Trusted" : "Untrusted", props->namaddr, TLScontext->protocol, TLScontext->cipher_name, TLScontext->cipher_usebits, TLScontext->cipher_algbits); tls_int_seed(); return (TLScontext); }
int ne_sock_connect_ssl(ne_socket *sock, ne_ssl_context *ctx, void *userdata) { int ret; #if defined(HAVE_OPENSSL) SSL *ssl; if (seed_ssl_prng()) { set_error(sock, _("SSL disabled due to lack of entropy")); return NE_SOCK_ERROR; } /* If runtime library version differs from compile-time version * number in major/minor/fix level, abort soon. */ if ((SSLeay() ^ OPENSSL_VERSION_NUMBER) & 0xFFFFF000) { set_error(sock, _("SSL disabled due to library version mismatch")); return NE_SOCK_ERROR; } sock->ssl = ssl = SSL_new(ctx->ctx); if (!ssl) { set_error(sock, _("Could not create SSL structure")); return NE_SOCK_ERROR; } SSL_set_app_data(ssl, userdata); SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); SSL_set_fd(ssl, sock->fd); sock->ops = &iofns_ssl; if (ctx->sess) SSL_set_session(ssl, ctx->sess); ret = SSL_connect(ssl); if (ret != 1) { error_ossl(sock, ret); SSL_free(ssl); sock->ssl = NULL; return NE_SOCK_ERROR; } #elif defined(HAVE_GNUTLS) /* DH and RSA params are set in ne_ssl_context_create */ gnutls_init(&sock->ssl, GNUTLS_CLIENT); gnutls_set_default_priority(sock->ssl); gnutls_session_set_ptr(sock->ssl, userdata); gnutls_credentials_set(sock->ssl, GNUTLS_CRD_CERTIFICATE, ctx->cred); gnutls_transport_set_ptr(sock->ssl, (gnutls_transport_ptr) sock->fd); if (ctx->cache.client.data) { #if defined(HAVE_GNUTLS_SESSION_GET_DATA2) gnutls_session_set_data(sock->ssl, ctx->cache.client.data, ctx->cache.client.size); #else gnutls_session_set_data(sock->ssl, ctx->cache.client.data, ctx->cache.client.len); #endif } sock->ops = &iofns_ssl; ret = gnutls_handshake(sock->ssl); if (ret < 0) { error_gnutls(sock, ret); return NE_SOCK_ERROR; } if (!gnutls_session_is_resumed(sock->ssl)) { /* New session. The old method of using the _get_data * function seems to be broken with 1.3.0 and later*/ #if defined(HAVE_GNUTLS_SESSION_GET_DATA2) gnutls_session_get_data2(sock->ssl, &ctx->cache.client); #else ctx->cache.client.len = 0; if (gnutls_session_get_data(sock->ssl, NULL, &ctx->cache.client.len) == 0) { ctx->cache.client.data = ne_malloc(ctx->cache.client.len); gnutls_session_get_data(sock->ssl, ctx->cache.client.data, &ctx->cache.client.len); } #endif } #endif return 0; }
/* * This is the actual startup routine for the connection. We expect that the * buffers are flushed and the "220 Ready to start TLS" was received by us, * so that we can immediately start the TLS handshake process. */ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) { const char *myname = "tls_client_start"; int sts; int protomask; const char *cipher_list; SSL_SESSION *session; SSL_CIPHER *cipher; X509 *peercert; TLS_SESS_STATE *TLScontext; TLS_APPL_STATE *app_ctx = props->ctx; ACL_VSTRING *myserverid; if (props->log_level >= 1) acl_msg_info("%s(%d): setting up TLS connection to %s", myname, __LINE__, props->namaddr); /* * First make sure we have valid protocol and cipher parameters * * The cipherlist will be applied to the global SSL context, where it can be * repeatedly reset if necessary, but the protocol restrictions will be * is applied to the SSL connection, because protocol restrictions in the * global context cannot be cleared. */ /* * OpenSSL will ignore cached sessions that use the wrong protocol. So we * do not need to filter out cached sessions with the "wrong" protocol, * rather OpenSSL will simply negotiate a new session. * * Still, we salt the session lookup key with the protocol list, so that * sessions found in the cache are always acceptable. */ protomask = tls_protocol_mask(props->protocols); if (protomask == TLS_PROTOCOL_INVALID) { /* tls_protocol_mask() logs no warning. */ acl_msg_warn("%s(%d): nameaddr: %s: Invalid TLS protocol list \"%s\": aborting TLS session", myname, __LINE__, props->namaddr, props->protocols); return (0); } myserverid = acl_vstring_alloc(100); acl_vstring_sprintf_append(myserverid, "%s&p=%d", props->serverid, protomask); /* * Per session cipher selection for sessions with mandatory encryption * * By the time a TLS client is negotiating ciphers it has already offered to * re-use a session, it is too late to renege on the offer. So we must * not attempt to re-use sessions whose ciphers are too weak. We salt the * session lookup key with the cipher list, so that sessions found in the * cache are always acceptable. */ cipher_list = tls_set_ciphers(app_ctx, "TLS", props->cipher_grade, props->cipher_exclusions); if (cipher_list == 0) { acl_msg_warn("%s(%d): %s: %s: aborting TLS session", myname, __LINE__, props->namaddr, acl_vstring_str(app_ctx->why)); acl_vstring_free(myserverid); return (0); } if (props->log_level >= 2) acl_msg_info("%s(%d): %s: TLS cipher list \"%s\"", myname, __LINE__, props->namaddr, cipher_list); acl_vstring_sprintf_append(myserverid, "&c=%s", cipher_list); /* * Allocate a new TLScontext for the new connection and get an SSL * structure. Add the location of TLScontext to the SSL to later retrieve * the information inside the tls_verify_certificate_callback(). * * If session caching was enabled when TLS was initialized, the cache type * is stored in the client SSL context. */ TLScontext = tls_alloc_sess_context(props->log_level, props->namaddr); TLScontext->cache_type = app_ctx->cache_type; TLScontext->serverid = acl_vstring_export(myserverid); if ((TLScontext->con = SSL_new(app_ctx->ssl_ctx)) == NULL) { acl_msg_warn("%s(%d): Could not allocate 'TLScontext->con' with SSL_new()", myname, __LINE__); tls_print_errors(); tls_free_context(TLScontext); return (0); } if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) { acl_msg_warn("%s(%d): Could not set application data for 'TLScontext->con'", myname, __LINE__); tls_print_errors(); tls_free_context(TLScontext); return (0); } /* * Apply session protocol restrictions. */ if (protomask != 0) SSL_set_options(TLScontext->con, ((protomask & TLS_PROTOCOL_TLSv1) ? SSL_OP_NO_TLSv1 : 0L) | ((protomask & TLS_PROTOCOL_SSLv3) ? SSL_OP_NO_SSLv3 : 0L) | ((protomask & TLS_PROTOCOL_SSLv2) ? SSL_OP_NO_SSLv2 : 0L)); /* * The TLS connection is realized by a BIO_pair, so obtain the pair. * * XXX There is no need to make internal_bio a member of the TLScontext * structure. It will be attached to TLScontext->con, and destroyed along * with it. The network_bio, however, needs to be freed explicitly. */ if (!BIO_new_bio_pair(&TLScontext->internal_bio, TLS_BIO_BUFSIZE, &TLScontext->network_bio, TLS_BIO_BUFSIZE)) { acl_msg_warn("%s(%d): Could not obtain BIO_pair", myname, __LINE__); tls_print_errors(); tls_free_context(TLScontext); return (0); } /* * XXX To avoid memory leaks we must always call SSL_SESSION_free() after * calling SSL_set_session(), regardless of whether or not the session * will be reused. */ if (TLScontext->cache_type) { session = load_clnt_session(TLScontext); if (session) { SSL_set_session(TLScontext->con, session); SSL_SESSION_free(session); /* 200411 */ #if (OPENSSL_VERSION_NUMBER < 0x00906011L) || (OPENSSL_VERSION_NUMBER == 0x00907000L) /* * Ugly Hack: OpenSSL before 0.9.6a does not store the verify * result in sessions for the client side. We modify the session * directly which is version specific, but this bug is version * specific, too. * * READ: 0-09-06-01-1 = 0-9-6-a-beta1: all versions before beta1 * have this bug, it has been fixed during development of 0.9.6a. * The development version of 0.9.7 can have this bug, too. It * has been fixed on 2000/11/29. */ SSL_set_verify_result(TLScontext->con, session->verify_result); #endif } } /* * Before really starting anything, try to seed the PRNG a little bit * more. */ tls_int_seed(); if (var_tls_daemon_rand_bytes > 0) (void) tls_ext_seed(var_tls_daemon_rand_bytes); /* * Initialize the SSL connection to connect state. This should not be * necessary anymore since 0.9.3, but the call is still in the library * and maintaining compatibility never hurts. */ SSL_set_connect_state(TLScontext->con); /* * Connect the SSL connection with the Postfix side of the BIO-pair for * reading and writing. */ SSL_set_bio(TLScontext->con, TLScontext->internal_bio, TLScontext->internal_bio); /* * If the debug level selected is high enough, all of the data is dumped: * 3 will dump the SSL negotiation, 4 will dump everything. * * We do have an SSL_set_fd() and now suddenly a BIO_ routine is called? * Well there is a BIO below the SSL routines that is automatically * created for us, so we can use it for debugging purposes. */ if (props->log_level >= 3) BIO_set_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb); /* * Start TLS negotiations. This process is a black box that invokes our * call-backs for certificate verification. * * Error handling: If the SSL handhake fails, we print out an error message * and remove all TLS state concerning this session. */ sts = tls_bio_connect(ACL_VSTREAM_SOCK(props->stream), props->timeout, TLScontext); if (sts <= 0) { acl_msg_info("%s(%d): SSL_connect error to %s: %d", myname, __LINE__, props->namaddr, sts); tls_print_errors(); uncache_session(app_ctx->ssl_ctx, TLScontext); tls_free_context(TLScontext); return (0); } /* Only log_level==4 dumps everything */ if (props->log_level < 4) BIO_set_callback(SSL_get_rbio(TLScontext->con), 0); /* * The caller may want to know if this session was reused or if a new * session was negotiated. */ TLScontext->session_reused = SSL_session_reused(TLScontext->con); if (props->log_level >= 2 && TLScontext->session_reused) acl_msg_info("%s(%d): %s: Reusing old session", myname, __LINE__, TLScontext->namaddr); /* * Do peername verification if requested and extract useful information * from the certificate for later use. */ if ((peercert = SSL_get_peer_certificate(TLScontext->con)) != 0) { TLScontext->peer_status |= TLS_CERT_FLAG_PRESENT; /* * Peer name or fingerprint verification as requested. * Unconditionally set peer_CN, issuer_CN and peer_fingerprint. */ verify_extract_name(TLScontext, peercert, props); verify_extract_print(TLScontext, peercert, props); X509_free(peercert); } else { TLScontext->issuer_CN = acl_mystrdup(""); TLScontext->peer_CN = acl_mystrdup(""); TLScontext->peer_fingerprint = acl_mystrdup(""); } /* * Finally, collect information about protocol and cipher for logging */ TLScontext->protocol = SSL_get_version(TLScontext->con); cipher = SSL_get_current_cipher(TLScontext->con); TLScontext->cipher_name = SSL_CIPHER_get_name(cipher); TLScontext->cipher_usebits = SSL_CIPHER_get_bits(cipher, &(TLScontext->cipher_algbits)); /* * The TLS engine is active. Switch to the tls_timed_read/write() * functions and make the TLScontext available to those functions. */ tls_stream_start(props->stream, TLScontext); /* * All the key facts in a single log entry. */ if (props->log_level >= 1) acl_msg_info("%s(%d): %s TLS connection established to %s: %s with cipher %s " "(%d/%d bits)", myname, __LINE__, TLS_CERT_IS_MATCHED(TLScontext) ? "Verified" : TLS_CERT_IS_TRUSTED(TLScontext) ? "Trusted" : "Untrusted", props->namaddr, TLScontext->protocol, TLScontext->cipher_name, TLScontext->cipher_usebits, TLScontext->cipher_algbits); tls_int_seed(); return (TLScontext); }
int tb_connectSSL( Socket_t S ) { BIO* sbio=NULL,* dbio; int l; sock_ssl_t m = XSsl(S); tb_info("tb_connectSSL in\n"); /* Ordinarily, just decorate the SSL connection with the socket file * descriptor. But for super-cool shazaam debugging, build your * own socket BIO, decorate it with a BIO debugging callback, and * presto, see a dump of the bytes as they fly. See ssl/ssl_lib.c * for details. */ if (tb_errorlevel <= TB_NOTICE) { SSL_set_fd(m->cx, tb_getSockFD(S)); } else { if (!(sbio=BIO_new_socket(tb_getSockFD(S),BIO_NOCLOSE))) { tb_warn("tb_connectSSL: Cannot create new socket BIO\n"); ERR_print_errors_fp(stderr); goto err; } SSL_set_bio(m->cx,sbio,sbio); m->cx->debug=1; dbio=BIO_new_fp(stdout,BIO_NOCLOSE); BIO_set_callback(sbio,(void *)bio_dump_cb); BIO_set_callback_arg(sbio,dbio); } tb_info("ssl cx linked to socket\n"); /* Initialize the state of the connection so the first i/o operation * knows to do the SSL connect. Strictly speaking, this not necessary, * as this code goes ahead and calls SSL_connect() anyway (so I can * put the connect tracing stuff in one handy spot). But look closely * in ssl/s_client.c for a call to SSL_connect. You won't find one. */ SSL_set_connect_state(m->cx); // SSL_CTX_set_session_cache_mode(XSSL(S)->ctx, SSL_SESS_CACHE_CLIENT); if(m->session != NULL ) { if(! SSL_set_session(m->cx, m->session)) { ERR_print_errors_fp(stderr); } tb_notice("tb_ConnectSSL: got a session to reuse ! (%X)\n", m->session); } /* Now that we've finally finished customizing the context and the * connection, go ahead and see if it works. This function call * invokes all the SSL connection handshake and key exchange logic, * (which is why there's so much to report on after it completes). */ retry_connect: l = SSL_connect(m->cx); tb_info("ssl connect returns: %d\n", l); switch (SSL_get_error(m->cx,l)) { case SSL_ERROR_NONE: break; case SSL_ERROR_SYSCALL: if ((l != 0) && errno) tb_warn("tb_connectSSL: Write errno=%d\n", errno); goto err; break; /* fall through */ case SSL_ERROR_WANT_WRITE: tb_info("SSL_ERROR_WANT_WRITE\n"); goto retry_connect; case SSL_ERROR_WANT_READ: tb_info("SSL_ERROR_WANT_READ\n"); goto retry_connect; case SSL_ERROR_WANT_X509_LOOKUP: tb_info("SSL_ERROR_WANT_X509_LOOKUP\n"); goto retry_connect; case SSL_ERROR_ZERO_RETURN: tb_info("SSL_ERROR_ZERO_RETURN\n"); case SSL_ERROR_SSL: tb_info("SSL_ERROR_SSL\n"); default: ERR_print_errors_fp(stderr); goto err; break; } tb_info("connected\n"); if(m->session == NULL ) { m->session = SSL_get_session(m->cx); //fixme: will leak ! tb_notice("save session for later reuse (%X)\n", m->session); } /* Report on what happened now that we've successfully connected. */ if (tb_errorlevel >= TB_NOTICE) ssl_barf_out(S); tb_info("tb_connectSSL out\n"); return TB_OK; err: tb_info("tb_connectSSL err out\n"); tb_Clear(S); return TB_ERR; }
void client_test(void* args) { #ifdef _WIN32 WSADATA wsd; WSAStartup(0x0002, &wsd); #endif SOCKET_T sockfd = 0; int argc = 0; char** argv = 0; set_args(argc, argv, *static_cast<func_args*>(args)); tcp_connect(sockfd); #ifdef NON_BLOCKING tcp_set_nonblocking(sockfd); #endif SSL_METHOD* method = TLSv1_client_method(); SSL_CTX* ctx = SSL_CTX_new(method); set_certs(ctx); if (argc >= 2) { printf("setting cipher list to %s\n", argv[1]); if (SSL_CTX_set_cipher_list(ctx, argv[1]) != SSL_SUCCESS) { ClientError(ctx, NULL, sockfd, "set_cipher_list error\n"); } } SSL* ssl = SSL_new(ctx); SSL_set_fd(ssl, sockfd); #ifdef NON_BLOCKING NonBlockingSSL_Connect(ssl, ctx, sockfd); #else // if you get an error here see note at top of README if (SSL_connect(ssl) != SSL_SUCCESS) ClientError(ctx, ssl, sockfd, "SSL_connect failed"); #endif showPeer(ssl); const char* cipher = 0; int index = 0; char list[1024]; strncpy(list, "cipherlist", 11); while ( (cipher = SSL_get_cipher_list(ssl, index++)) ) { strncat(list, ":", 2); strncat(list, cipher, strlen(cipher) + 1); } printf("%s\n", list); printf("Using Cipher Suite: %s\n", SSL_get_cipher(ssl)); char msg[] = "hello yassl!"; if (SSL_write(ssl, msg, sizeof(msg)) != sizeof(msg)) ClientError(ctx, ssl, sockfd, "SSL_write failed"); char reply[1024]; int input = SSL_read(ssl, reply, sizeof(reply)); if (input > 0) { reply[input] = 0; printf("Server response: %s\n", reply); } #ifdef TEST_RESUME SSL_SESSION* session = SSL_get_session(ssl); SSL* sslResume = SSL_new(ctx); #endif SSL_shutdown(ssl); SSL_free(ssl); tcp_close(sockfd); #ifdef TEST_RESUME tcp_connect(sockfd); SSL_set_fd(sslResume, sockfd); SSL_set_session(sslResume, session); if (SSL_connect(sslResume) != SSL_SUCCESS) ClientError(ctx, sslResume, sockfd, "SSL_resume failed"); showPeer(sslResume); if (SSL_write(sslResume, msg, sizeof(msg)) != sizeof(msg)) ClientError(ctx, sslResume, sockfd, "SSL_write failed"); input = SSL_read(sslResume, reply, sizeof(reply)); if (input > 0) { reply[input] = 0; printf("Server response: %s\n", reply); } SSL_shutdown(sslResume); SSL_free(sslResume); tcp_close(sockfd); #endif // TEST_RESUME SSL_CTX_free(ctx); ((func_args*)args)->return_code = 0; }
/* * This function loads all the client/CA certificates and CRLs. Setup the TLS * layer and do all necessary magic. */ static CURLcode cyassl_connect_step1(struct connectdata *conn, int sockindex) { struct SessionHandle *data = conn->data; struct ssl_connect_data* conssl = &conn->ssl[sockindex]; SSL_METHOD* req_method = NULL; void* ssl_sessionid = NULL; curl_socket_t sockfd = conn->sock[sockindex]; if(conssl->state == ssl_connection_complete) return CURLE_OK; /* CyaSSL doesn't support SSLv2 */ if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) { failf(data, "CyaSSL does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; } /* check to see if we've been told to use an explicit SSL/TLS version */ switch(data->set.ssl.version) { case CURL_SSLVERSION_DEFAULT: /* we try to figure out version */ req_method = SSLv23_client_method(); break; case CURL_SSLVERSION_TLSv1: req_method = TLSv1_client_method(); break; case CURL_SSLVERSION_SSLv3: req_method = SSLv3_client_method(); break; default: req_method = TLSv1_client_method(); } if(!req_method) { failf(data, "SSL: couldn't create a method!"); return CURLE_OUT_OF_MEMORY; } if(conssl->ctx) SSL_CTX_free(conssl->ctx); conssl->ctx = SSL_CTX_new(req_method); if(!conssl->ctx) { failf(data, "SSL: couldn't create a context!"); return CURLE_OUT_OF_MEMORY; } #ifndef NO_FILESYSTEM /* load trusted cacert */ if(data->set.str[STRING_SSL_CAFILE]) { if(!SSL_CTX_load_verify_locations(conssl->ctx, data->set.str[STRING_SSL_CAFILE], data->set.str[STRING_SSL_CAPATH])) { if(data->set.ssl.verifypeer) { /* Fail if we insiste on successfully verifying the server. */ failf(data,"error setting certificate verify locations:\n" " CAfile: %s\n CApath: %s\n", data->set.str[STRING_SSL_CAFILE]? data->set.str[STRING_SSL_CAFILE]: "none", data->set.str[STRING_SSL_CAPATH]? data->set.str[STRING_SSL_CAPATH] : "none"); return CURLE_SSL_CACERT_BADFILE; } else { /* Just continue with a warning if no strict certificate verification is required. */ infof(data, "error setting certificate verify locations," " continuing anyway:\n"); } } else { /* Everything is fine. */ infof(data, "successfully set certificate verify locations:\n"); } infof(data, " CAfile: %s\n" " CApath: %s\n", data->set.str[STRING_SSL_CAFILE] ? data->set.str[STRING_SSL_CAFILE]: "none", data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]: "none"); } /* Load the client certificate, and private key */ if(data->set.str[STRING_CERT] && data->set.str[STRING_KEY]) { int file_type = do_file_type(data->set.str[STRING_CERT_TYPE]); if(SSL_CTX_use_certificate_file(conssl->ctx, data->set.str[STRING_CERT], file_type) != 1) { failf(data, "unable to use client certificate (no key or wrong pass" " phrase?)"); return CURLE_SSL_CONNECT_ERROR; } file_type = do_file_type(data->set.str[STRING_KEY_TYPE]); if(SSL_CTX_use_PrivateKey_file(conssl->ctx, data->set.str[STRING_KEY], file_type) != 1) { failf(data, "unable to set private key"); return CURLE_SSL_CONNECT_ERROR; } } #else if(CyaSSL_no_filesystem_verify(conssl->ctx)!= SSL_SUCCESS) { return CURLE_SSL_CONNECT_ERROR; } #endif /* NO_FILESYSTEM */ /* SSL always tries to verify the peer, this only says whether it should * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ SSL_CTX_set_verify(conssl->ctx, data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE, NULL); /* Let's make an SSL structure */ if(conssl->handle) SSL_free(conssl->handle); conssl->handle = SSL_new(conssl->ctx); if(!conssl->handle) { failf(data, "SSL: couldn't create a context (handle)!"); return CURLE_OUT_OF_MEMORY; } /* Check if there's a cached ID we can/should use here! */ if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) { /* we got a session id, use it! */ if(!SSL_set_session(conssl->handle, ssl_sessionid)) { failf(data, "SSL: SSL_set_session failed: %s", ERR_error_string(SSL_get_error(conssl->handle, 0),NULL)); return CURLE_SSL_CONNECT_ERROR; } /* Informational message */ infof (data, "SSL re-using session ID\n"); } /* pass the raw socket into the SSL layer */ if(!SSL_set_fd(conssl->handle, (int)sockfd)) { failf(data, "SSL: SSL_set_fd failed"); return CURLE_SSL_CONNECT_ERROR; } conssl->connecting_state = ssl_connect_2; return CURLE_OK; }
/* ====================================================== */ CURLcode Curl_ossl_connect(struct connectdata *conn, int sockindex) { CURLcode retcode = CURLE_OK; struct SessionHandle *data = conn->data; int err; long lerr; int what; char * str; SSL_METHOD *req_method; void *ssl_sessionid=NULL; ASN1_TIME *certdate; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; if(!ssl_seeded || data->set.ssl.random_file || data->set.ssl.egdsocket) { /* Make funny stuff to get random input */ random_the_seed(data); ssl_seeded = TRUE; } /* check to see if we've been told to use an explicit SSL/TLS version */ switch(data->set.ssl.version) { default: case CURL_SSLVERSION_DEFAULT: /* we try to figure out version */ req_method = SSLv23_client_method(); break; case CURL_SSLVERSION_TLSv1: req_method = TLSv1_client_method(); break; case CURL_SSLVERSION_SSLv2: req_method = SSLv2_client_method(); break; case CURL_SSLVERSION_SSLv3: req_method = SSLv3_client_method(); break; } connssl->ctx = SSL_CTX_new(req_method); if(!connssl->ctx) { failf(data, "SSL: couldn't create a context!"); return CURLE_OUT_OF_MEMORY; } #ifdef SSL_CTRL_SET_MSG_CALLBACK if (data->set.fdebug) { if (!SSL_CTX_callback_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK, ssl_tls_trace)) { failf(data, "SSL: couldn't set callback!"); return CURLE_SSL_CONNECT_ERROR; } if (!SSL_CTX_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK_ARG, 0, conn)) { failf(data, "SSL: couldn't set callback argument!"); return CURLE_SSL_CONNECT_ERROR; } } #endif /* OpenSSL contains code to work-around lots of bugs and flaws in various SSL-implementations. SSL_CTX_set_options() is used to enabled those work-arounds. The man page for this option states that SSL_OP_ALL enables ll the work-arounds and that "It is usually safe to use SSL_OP_ALL to enable the bug workaround options if compatibility with somewhat broken implementations is desired." */ SSL_CTX_set_options(connssl->ctx, SSL_OP_ALL); #if 0 /* * Not sure it's needed to tell SSL_connect() that socket is * non-blocking. It doesn't seem to care, but just return with * SSL_ERROR_WANT_x. */ if (data->state.used_interface == Curl_if_multi) SSL_CTX_ctrl(connssl->ctx, BIO_C_SET_NBIO, 1, NULL); #endif if(data->set.cert) { if(!cert_stuff(conn, connssl->ctx, data->set.cert, data->set.cert_type, data->set.key, data->set.key_type)) { /* failf() is already done in cert_stuff() */ return CURLE_SSL_CERTPROBLEM; } } if(data->set.ssl.cipher_list) { if(!SSL_CTX_set_cipher_list(connssl->ctx, data->set.ssl.cipher_list)) { failf(data, "failed setting cipher list"); return CURLE_SSL_CIPHER; } } if (data->set.ssl.CAfile || data->set.ssl.CApath) { /* tell SSL where to find CA certificates that are used to verify the servers certificate. */ if (!SSL_CTX_load_verify_locations(connssl->ctx, data->set.ssl.CAfile, data->set.ssl.CApath)) { if (data->set.ssl.verifypeer) { /* Fail if we insist on successfully verifying the server. */ failf(data,"error setting certificate verify locations:\n" " CAfile: %s\n CApath: %s\n", data->set.ssl.CAfile ? data->set.ssl.CAfile : "none", data->set.ssl.CApath ? data->set.ssl.CApath : "none"); return CURLE_SSL_CACERT; } else { /* Just continue with a warning if no strict certificate verification is required. */ infof(data, "error setting certificate verify locations," " continuing anyway:\n"); } } else { /* Everything is fine. */ infof(data, "successfully set certificate verify locations:\n"); } infof(data, " CAfile: %s\n" " CApath: %s\n", data->set.ssl.CAfile ? data->set.ssl.CAfile : "none", data->set.ssl.CApath ? data->set.ssl.CApath : "none"); } /* SSL always tries to verify the peer, this only says whether it should * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ SSL_CTX_set_verify(connssl->ctx, data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE, cert_verify_callback); /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { retcode = (*data->set.ssl.fsslctx)(data, connssl->ctx, data->set.ssl.fsslctxp); if(retcode) { failf(data,"error signaled by ssl ctx callback"); return retcode; } } /* Lets make an SSL structure */ connssl->handle = SSL_new(connssl->ctx); if (!connssl->handle) { failf(data, "SSL: couldn't create a context (handle)!"); return CURLE_OUT_OF_MEMORY; } SSL_set_connect_state(connssl->handle); connssl->server_cert = 0x0; /* Check if there's a cached ID we can/should use here! */ if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) { /* we got a session id, use it! */ if (!SSL_set_session(connssl->handle, ssl_sessionid)) { failf(data, "SSL: SSL_set_session failed: %s", ERR_error_string(ERR_get_error(),NULL)); return CURLE_SSL_CONNECT_ERROR; } /* Informational message */ infof (data, "SSL re-using session ID\n"); } /* pass the raw socket into the SSL layers */ if (!SSL_set_fd(connssl->handle, sockfd)) { failf(data, "SSL: SSL_set_fd failed: %s", ERR_error_string(ERR_get_error(),NULL)); return CURLE_SSL_CONNECT_ERROR; } while(1) { int writefd; int readfd; long timeout_ms; long has_passed; /* Find out if any timeout is set. If not, use 300 seconds. Otherwise, figure out the most strict timeout of the two possible one and then how much time that has elapsed to know how much time we allow for the connect call */ if(data->set.timeout || data->set.connecttimeout) { /* get the most strict timeout of the ones converted to milliseconds */ if(data->set.timeout && (data->set.timeout>data->set.connecttimeout)) timeout_ms = data->set.timeout*1000; else timeout_ms = data->set.connecttimeout*1000; } else /* no particular time-out has been set */ timeout_ms= DEFAULT_CONNECT_TIMEOUT; /* Evaluate in milliseconds how much time that has passed */ has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); /* subtract the passed time */ timeout_ms -= has_passed; if(timeout_ms < 0) { /* a precaution, no need to continue if time already is up */ failf(data, "SSL connection timeout"); return CURLE_OPERATION_TIMEOUTED; } readfd = CURL_SOCKET_BAD; writefd = CURL_SOCKET_BAD; err = SSL_connect(connssl->handle); /* 1 is fine 0 is "not successful but was shut down controlled" <0 is "handshake was not successful, because a fatal error occurred" */ if(1 != err) { int detail = SSL_get_error(connssl->handle, err); if(SSL_ERROR_WANT_READ == detail) readfd = sockfd; else if(SSL_ERROR_WANT_WRITE == detail) writefd = sockfd; else { /* untreated error */ unsigned long errdetail; char error_buffer[120]; /* OpenSSL documents that this must be at least 120 bytes long. */ CURLcode rc; const char *cert_problem = NULL; errdetail = ERR_get_error(); /* Gets the earliest error code from the thread's error queue and removes the entry. */ switch(errdetail) { case 0x1407E086: /* 1407E086: SSL routines: SSL2_SET_CERTIFICATE: certificate verify failed */ /* fall-through */ case 0x14090086: /* 14090086: SSL routines: SSL3_GET_SERVER_CERTIFICATE: certificate verify failed */ cert_problem = "SSL certificate problem, verify that the CA cert is" " OK. Details:\n"; rc = CURLE_SSL_CACERT; break; default: rc = CURLE_SSL_CONNECT_ERROR; break; } /* detail is already set to the SSL error above */ /* If we e.g. use SSLv2 request-method and the server doesn't like us * (RST connection etc.), OpenSSL gives no explanation whatsoever and * the SO_ERROR is also lost. */ if (CURLE_SSL_CONNECT_ERROR == rc && errdetail == 0) { failf(data, "Unknown SSL protocol error in connection to %s:%d ", conn->host.name, conn->port); return rc; } /* Could be a CERT problem */ SSL_strerror(errdetail, error_buffer, sizeof(error_buffer)); failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer); return rc; } } else /* we have been connected fine, get out of the connect loop */ break; while(1) { what = Curl_select(readfd, writefd, (int)timeout_ms); if(what > 0) /* reabable or writable, go loop in the outer loop */ break; else if(0 == what) { /* timeout */ failf(data, "SSL connection timeout"); return CURLE_OPERATION_TIMEDOUT; } else { /* anything that gets here is fatally bad */ failf(data, "select on SSL socket, errno: %d", Curl_ourerrno()); return CURLE_SSL_CONNECT_ERROR; } } /* while()-loop for the select() */ } /* while()-loop for the SSL_connect() */ /* Informational message */ infof (data, "SSL connection using %s\n", SSL_get_cipher(connssl->handle)); if(!ssl_sessionid) { /* Since this is not a cached session ID, then we want to stach this one in the cache! */ SSL_SESSION *ssl_sessionid; #ifdef HAVE_SSL_GET1_SESSION ssl_sessionid = SSL_get1_session(connssl->handle); /* SSL_get1_session() will increment the reference count and the session will stay in memory until explicitly freed with SSL_SESSION_free(3), regardless of its state. This function was introduced in openssl 0.9.5a. */ #else ssl_sessionid = SSL_get_session(connssl->handle); /* if SSL_get1_session() is unavailable, use SSL_get_session(). This is an inferior option because the session can be flushed at any time by openssl. It is included only so curl compiles under versions of openssl < 0.9.5a. WARNING: How curl behaves if it's session is flushed is untested. */ #endif retcode = Curl_ssl_addsessionid(conn, ssl_sessionid, 0 /* unknown size */); if(retcode) { failf(data, "failed to store ssl session"); return retcode; } } /* Get server's certificate (note: beware of dynamic allocation) - opt */ /* major serious hack alert -- we should check certificates * to authenticate the server; otherwise we risk man-in-the-middle * attack */ connssl->server_cert = SSL_get_peer_certificate(connssl->handle); if(!connssl->server_cert) { failf(data, "SSL: couldn't get peer certificate!"); return CURLE_SSL_PEER_CERTIFICATE; } infof (data, "Server certificate:\n"); str = X509_NAME_oneline(X509_get_subject_name(connssl->server_cert), NULL, 0); if(!str) { failf(data, "SSL: couldn't get X509-subject!"); X509_free(connssl->server_cert); connssl->server_cert = NULL; return CURLE_SSL_CONNECT_ERROR; } infof(data, "\t subject: %s\n", str); CRYPTO_free(str); certdate = X509_get_notBefore(connssl->server_cert); Curl_ASN1_UTCTIME_output(conn, "\t start date: ", certdate); certdate = X509_get_notAfter(connssl->server_cert); Curl_ASN1_UTCTIME_output(conn, "\t expire date: ", certdate); if(data->set.ssl.verifyhost) { retcode = verifyhost(conn, connssl->server_cert); if(retcode) { X509_free(connssl->server_cert); connssl->server_cert = NULL; return retcode; } } str = X509_NAME_oneline(X509_get_issuer_name(connssl->server_cert), NULL, 0); if(!str) { failf(data, "SSL: couldn't get X509-issuer name!"); retcode = CURLE_SSL_CONNECT_ERROR; } else { infof(data, "\t issuer: %s\n", str); CRYPTO_free(str); /* We could do all sorts of certificate verification stuff here before deallocating the certificate. */ lerr = data->set.ssl.certverifyresult= SSL_get_verify_result(connssl->handle); if(data->set.ssl.certverifyresult != X509_V_OK) { if(data->set.ssl.verifypeer) { /* We probably never reach this, because SSL_connect() will fail and we return earlyer if verifypeer is set? */ failf(data, "SSL certificate verify result: %s (%ld)", X509_verify_cert_error_string(lerr), lerr); retcode = CURLE_SSL_PEER_CERTIFICATE; } else infof(data, "SSL certificate verify result: %s (%ld)," " continuing anyway.\n", X509_verify_cert_error_string(err), lerr); } else infof(data, "SSL certificate verify ok.\n"); } X509_free(connssl->server_cert); connssl->server_cert = NULL; return retcode; }
/*- * doConnection - make a connection * Args: * scon = earlier ssl connection for session id, or NULL * Returns: * SSL * = the connection pointer. */ static SSL *doConnection(SSL *scon) { BIO *conn; SSL *serverCon; int width, i; fd_set readfds; if ((conn = BIO_new(BIO_s_connect())) == NULL) return (NULL); /* BIO_set_conn_port(conn,port);*/ BIO_set_conn_hostname(conn, host); if (scon == NULL) serverCon = SSL_new(tm_ctx); else { serverCon = scon; SSL_set_connect_state(serverCon); } SSL_set_bio(serverCon, conn, conn); #if 0 if (scon != NULL) SSL_set_session(serverCon, SSL_get_session(scon)); #endif /* ok, lets connect */ for (;;) { i = SSL_connect(serverCon); if (BIO_sock_should_retry(i)) { BIO_printf(bio_err, "DELAY\n"); i = SSL_get_fd(serverCon); width = i + 1; FD_ZERO(&readfds); FD_SET(i, &readfds); /* * Note: under VMS with SOCKETSHR the 2nd parameter is currently * of type (int *) whereas under other systems it is (void *) if * you don't have a cast it will choke the compiler: if you do * have a cast then you can either go for (int *) or (void *). */ select(width, (void *)&readfds, NULL, NULL, NULL); continue; } break; } if (i <= 0) { BIO_printf(bio_err, "ERROR\n"); if (verify_error != X509_V_OK) BIO_printf(bio_err, "verify error:%s\n", X509_verify_cert_error_string(verify_error)); else ERR_print_errors(bio_err); if (scon == NULL) SSL_free(serverCon); return NULL; } return serverCon; }
/* * This function loads all the client/CA certificates and CRLs. Setup the TLS * layer and do all necessary magic. */ static CURLcode cyassl_connect_step1(struct connectdata *conn, int sockindex) { char error_buffer[CYASSL_MAX_ERROR_SZ]; char *ciphers; struct Curl_easy *data = conn->data; struct ssl_connect_data* connssl = &conn->ssl[sockindex]; SSL_METHOD* req_method = NULL; curl_socket_t sockfd = conn->sock[sockindex]; #ifdef HAVE_SNI bool sni = FALSE; #define use_sni(x) sni = (x) #else #define use_sni(x) Curl_nop_stmt #endif if(connssl->state == ssl_connection_complete) return CURLE_OK; if(SSL_CONN_CONFIG(version_max) != CURL_SSLVERSION_MAX_NONE) { failf(data, "CyaSSL does not support to set maximum SSL/TLS version"); return CURLE_SSL_CONNECT_ERROR; } /* check to see if we've been told to use an explicit SSL/TLS version */ switch(SSL_CONN_CONFIG(version)) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: #if LIBCYASSL_VERSION_HEX >= 0x03003000 /* >= 3.3.0 */ /* minimum protocol version is set later after the CTX object is created */ req_method = SSLv23_client_method(); #else infof(data, "CyaSSL <3.3.0 cannot be configured to use TLS 1.0-1.2, " "TLS 1.0 is used exclusively\n"); req_method = TLSv1_client_method(); #endif use_sni(TRUE); break; case CURL_SSLVERSION_TLSv1_0: req_method = TLSv1_client_method(); use_sni(TRUE); break; case CURL_SSLVERSION_TLSv1_1: req_method = TLSv1_1_client_method(); use_sni(TRUE); break; case CURL_SSLVERSION_TLSv1_2: req_method = TLSv1_2_client_method(); use_sni(TRUE); break; case CURL_SSLVERSION_TLSv1_3: failf(data, "CyaSSL: TLS 1.3 is not yet supported"); return CURLE_SSL_CONNECT_ERROR; case CURL_SSLVERSION_SSLv3: #ifdef WOLFSSL_ALLOW_SSLV3 req_method = SSLv3_client_method(); use_sni(FALSE); #else failf(data, "CyaSSL does not support SSLv3"); return CURLE_NOT_BUILT_IN; #endif break; case CURL_SSLVERSION_SSLv2: failf(data, "CyaSSL does not support SSLv2"); return CURLE_SSL_CONNECT_ERROR; default: failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); return CURLE_SSL_CONNECT_ERROR; } if(!req_method) { failf(data, "SSL: couldn't create a method!"); return CURLE_OUT_OF_MEMORY; } if(BACKEND->ctx) SSL_CTX_free(BACKEND->ctx); BACKEND->ctx = SSL_CTX_new(req_method); if(!BACKEND->ctx) { failf(data, "SSL: couldn't create a context!"); return CURLE_OUT_OF_MEMORY; } switch(SSL_CONN_CONFIG(version)) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: #if LIBCYASSL_VERSION_HEX > 0x03004006 /* > 3.4.6 */ /* Versions 3.3.0 to 3.4.6 we know the minimum protocol version is whatever minimum version of TLS was built in and at least TLS 1.0. For later library versions that could change (eg TLS 1.0 built in but defaults to TLS 1.1) so we have this short circuit evaluation to find the minimum supported TLS version. We use wolfSSL_CTX_SetMinVersion and not CyaSSL_SetMinVersion because only the former will work before the user's CTX callback is called. */ if((wolfSSL_CTX_SetMinVersion(BACKEND->ctx, WOLFSSL_TLSV1) != 1) && (wolfSSL_CTX_SetMinVersion(BACKEND->ctx, WOLFSSL_TLSV1_1) != 1) && (wolfSSL_CTX_SetMinVersion(BACKEND->ctx, WOLFSSL_TLSV1_2) != 1)) { failf(data, "SSL: couldn't set the minimum protocol version"); return CURLE_SSL_CONNECT_ERROR; } #endif break; } ciphers = SSL_CONN_CONFIG(cipher_list); if(ciphers) { if(!SSL_CTX_set_cipher_list(BACKEND->ctx, ciphers)) { failf(data, "failed setting cipher list: %s", ciphers); return CURLE_SSL_CIPHER; } infof(data, "Cipher selection: %s\n", ciphers); } #ifndef NO_FILESYSTEM /* load trusted cacert */ if(SSL_CONN_CONFIG(CAfile)) { if(1 != SSL_CTX_load_verify_locations(BACKEND->ctx, SSL_CONN_CONFIG(CAfile), SSL_CONN_CONFIG(CApath))) { if(SSL_CONN_CONFIG(verifypeer)) { /* Fail if we insist on successfully verifying the server. */ failf(data, "error setting certificate verify locations:\n" " CAfile: %s\n CApath: %s", SSL_CONN_CONFIG(CAfile)? SSL_CONN_CONFIG(CAfile): "none", SSL_CONN_CONFIG(CApath)? SSL_CONN_CONFIG(CApath) : "none"); return CURLE_SSL_CACERT_BADFILE; } else { /* Just continue with a warning if no strict certificate verification is required. */ infof(data, "error setting certificate verify locations," " continuing anyway:\n"); } } else { /* Everything is fine. */ infof(data, "successfully set certificate verify locations:\n"); } infof(data, " CAfile: %s\n" " CApath: %s\n", SSL_CONN_CONFIG(CAfile) ? SSL_CONN_CONFIG(CAfile): "none", SSL_CONN_CONFIG(CApath) ? SSL_CONN_CONFIG(CApath): "none"); } /* Load the client certificate, and private key */ if(SSL_SET_OPTION(cert) && SSL_SET_OPTION(key)) { int file_type = do_file_type(SSL_SET_OPTION(cert_type)); if(SSL_CTX_use_certificate_file(BACKEND->ctx, SSL_SET_OPTION(cert), file_type) != 1) { failf(data, "unable to use client certificate (no key or wrong pass" " phrase?)"); return CURLE_SSL_CONNECT_ERROR; } file_type = do_file_type(SSL_SET_OPTION(key_type)); if(SSL_CTX_use_PrivateKey_file(BACKEND->ctx, SSL_SET_OPTION(key), file_type) != 1) { failf(data, "unable to set private key"); return CURLE_SSL_CONNECT_ERROR; } } #endif /* !NO_FILESYSTEM */ /* SSL always tries to verify the peer, this only says whether it should * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ SSL_CTX_set_verify(BACKEND->ctx, SSL_CONN_CONFIG(verifypeer)?SSL_VERIFY_PEER: SSL_VERIFY_NONE, NULL); #ifdef HAVE_SNI if(sni) { struct in_addr addr4; #ifdef ENABLE_IPV6 struct in6_addr addr6; #endif const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; size_t hostname_len = strlen(hostname); if((hostname_len < USHRT_MAX) && (0 == Curl_inet_pton(AF_INET, hostname, &addr4)) && #ifdef ENABLE_IPV6 (0 == Curl_inet_pton(AF_INET6, hostname, &addr6)) && #endif (CyaSSL_CTX_UseSNI(BACKEND->ctx, CYASSL_SNI_HOST_NAME, hostname, (unsigned short)hostname_len) != 1)) { infof(data, "WARNING: failed to configure server name indication (SNI) " "TLS extension\n"); } } #endif #ifdef HAVE_SUPPORTED_CURVES /* CyaSSL/wolfSSL does not send the supported ECC curves ext automatically: https://github.com/wolfSSL/wolfssl/issues/366 The supported curves below are those also supported by OpenSSL 1.0.2 and in the same order. */ CyaSSL_CTX_UseSupportedCurve(BACKEND->ctx, 0x17); /* secp256r1 */ CyaSSL_CTX_UseSupportedCurve(BACKEND->ctx, 0x19); /* secp521r1 */ CyaSSL_CTX_UseSupportedCurve(BACKEND->ctx, 0x18); /* secp384r1 */ #endif /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { CURLcode result = CURLE_OK; result = (*data->set.ssl.fsslctx)(data, BACKEND->ctx, data->set.ssl.fsslctxp); if(result) { failf(data, "error signaled by ssl ctx callback"); return result; } } #ifdef NO_FILESYSTEM else if(SSL_CONN_CONFIG(verifypeer)) { failf(data, "SSL: Certificates couldn't be loaded because CyaSSL was built" " with \"no filesystem\". Either disable peer verification" " (insecure) or if you are building an application with libcurl you" " can load certificates via CURLOPT_SSL_CTX_FUNCTION."); return CURLE_SSL_CONNECT_ERROR; } #endif /* Let's make an SSL structure */ if(BACKEND->handle) SSL_free(BACKEND->handle); BACKEND->handle = SSL_new(BACKEND->ctx); if(!BACKEND->handle) { failf(data, "SSL: couldn't create a context (handle)!"); return CURLE_OUT_OF_MEMORY; } #ifdef HAVE_ALPN if(conn->bits.tls_enable_alpn) { char protocols[128]; *protocols = '\0'; /* wolfSSL's ALPN protocol name list format is a comma separated string of protocols in descending order of preference, eg: "h2,http/1.1" */ #ifdef USE_NGHTTP2 if(data->set.httpversion >= CURL_HTTP_VERSION_2) { strcpy(protocols + strlen(protocols), NGHTTP2_PROTO_VERSION_ID ","); infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID); } #endif strcpy(protocols + strlen(protocols), ALPN_HTTP_1_1); infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1); if(wolfSSL_UseALPN(BACKEND->handle, protocols, (unsigned)strlen(protocols), WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != SSL_SUCCESS) { failf(data, "SSL: failed setting ALPN protocols"); return CURLE_SSL_CONNECT_ERROR; } } #endif /* HAVE_ALPN */ /* Check if there's a cached ID we can/should use here! */ if(SSL_SET_OPTION(primary.sessionid)) { void *ssl_sessionid = NULL; Curl_ssl_sessionid_lock(conn); if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL, sockindex)) { /* we got a session id, use it! */ if(!SSL_set_session(BACKEND->handle, ssl_sessionid)) { Curl_ssl_sessionid_unlock(conn); failf(data, "SSL: SSL_set_session failed: %s", ERR_error_string(SSL_get_error(BACKEND->handle, 0), error_buffer)); return CURLE_SSL_CONNECT_ERROR; } /* Informational message */ infof(data, "SSL re-using session ID\n"); } Curl_ssl_sessionid_unlock(conn); } /* pass the raw socket into the SSL layer */ if(!SSL_set_fd(BACKEND->handle, (int)sockfd)) { failf(data, "SSL: SSL_set_fd failed"); return CURLE_SSL_CONNECT_ERROR; } connssl->connecting_state = ssl_connect_2; return CURLE_OK; }
static int init_ssl(CLI *c) { int i, err; SSL_SESSION *old_session; if(!(c->ssl=SSL_new(ctx))) { sslerror("SSL_new"); return -1; } #if SSLEAY_VERSION_NUMBER >= 0x0922 SSL_set_session_id_context(c->ssl, sid_ctx, strlen(sid_ctx)); #endif if(options.option.client) { if(c->opt->session) { enter_critical_section(CRIT_SESSION); SSL_set_session(c->ssl, c->opt->session); leave_critical_section(CRIT_SESSION); } SSL_set_fd(c->ssl, c->remote_fd.fd); SSL_set_connect_state(c->ssl); } else { if(c->local_rfd.fd==c->local_wfd.fd) SSL_set_fd(c->ssl, c->local_rfd.fd); else { /* Does it make sence to have SSL on STDIN/STDOUT? */ SSL_set_rfd(c->ssl, c->local_rfd.fd); SSL_set_wfd(c->ssl, c->local_wfd.fd); } SSL_set_accept_state(c->ssl); } /* Setup some values for transfer() function */ if(options.option.client) { c->sock_rfd=&(c->local_rfd); c->sock_wfd=&(c->local_wfd); c->ssl_rfd=c->ssl_wfd=&(c->remote_fd); } else { c->sock_rfd=c->sock_wfd=&(c->remote_fd); c->ssl_rfd=&(c->local_rfd); c->ssl_wfd=&(c->local_wfd); } while(1) { if(options.option.client) i=SSL_connect(c->ssl); else i=SSL_accept(c->ssl); err=SSL_get_error(c->ssl, i); if(err==SSL_ERROR_NONE) break; /* ok -> done */ if(err==SSL_ERROR_WANT_READ || err==SSL_ERROR_WANT_WRITE) { s_poll_zero(&c->fds); s_poll_add(&c->fds, c->ssl_rfd->fd, err==SSL_ERROR_WANT_READ, err==SSL_ERROR_WANT_WRITE); switch(s_poll_wait(&c->fds, c->opt->timeout_busy)) { case -1: sockerror("init_ssl: s_poll_wait"); return -1; /* error */ case 0: s_log(LOG_INFO, "init_ssl: s_poll_wait timeout"); return -1; /* timeout */ case 1: break; /* OK */ default: s_log(LOG_ERR, "init_ssl: s_poll_wait unknown result"); return -1; /* error */ } continue; /* ok -> retry */ } if(err==SSL_ERROR_SYSCALL) { switch(get_last_socket_error()) { case EINTR: case EAGAIN: continue; } } if(options.option.client) sslerror("SSL_connect"); else sslerror("SSL_accept"); return -1; } if(SSL_session_reused(c->ssl)) { s_log(LOG_INFO, "SSL %s: previous session reused", options.option.client ? "connected" : "accepted"); } else { /* a new session was negotiated */ if(options.option.client) { s_log(LOG_INFO, "SSL connected: new session negotiated"); enter_critical_section(CRIT_SESSION); old_session=c->opt->session; c->opt->session=SSL_get1_session(c->ssl); /* store it */ if(old_session) SSL_SESSION_free(old_session); /* release the old one */ leave_critical_section(CRIT_SESSION); } else s_log(LOG_INFO, "SSL accepted: new session negotiated"); print_cipher(c); } return 0; /* OK */ }