QCryptoTLSSessionHandshakeStatus qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *session) { if (session->handshakeComplete) { return QCRYPTO_TLS_HANDSHAKE_COMPLETE; } else if (gnutls_record_get_direction(session->handle) == 0) { return QCRYPTO_TLS_HANDSHAKE_RECVING; } else { return QCRYPTO_TLS_HANDSHAKE_SENDING; } }
int CTlsSocket::ContinueHandshake() { m_pOwner->LogMessage(Debug_Verbose, _T("CTlsSocket::ContinueHandshake()")); wxASSERT(m_session); wxASSERT(m_tlsState == handshake); int res = gnutls_handshake(m_session); while (res == GNUTLS_E_AGAIN || res == GNUTLS_E_INTERRUPTED) { if ( gnutls_record_get_direction(m_session) != 1 || !m_canWriteToSocket ) break; res = gnutls_handshake(m_session); } if (!res) { m_pOwner->LogMessage(Debug_Info, _T("TLS Handshake successful")); if (ResumedSession()) m_pOwner->LogMessage(Debug_Info, _T("TLS Session resumed")); const wxString protocol = GetProtocolName(); const wxString keyExchange = GetKeyExchange(); const wxString cipherName = GetCipherName(); const wxString macName = GetMacName(); m_pOwner->LogMessage(Debug_Info, _T("Protocol: %s, Key exchange: %s, Cipher: %s, MAC: %s"), protocol.c_str(), keyExchange.c_str(), cipherName.c_str(), macName.c_str()); res = VerifyCertificate(); if (res != FZ_REPLY_OK) return res; if (m_shutdown_requested) { int error = Shutdown(); if (!error || error != EAGAIN) { CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::close); CSocketEventDispatcher::Get().SendEvent(evt); } } return FZ_REPLY_OK; } else if (res == GNUTLS_E_AGAIN || res == GNUTLS_E_INTERRUPTED) return FZ_REPLY_WOULDBLOCK; Failure(res, ECONNABORTED); return FZ_REPLY_ERROR; }
void SslSocket::handshake(int revents) { TRACE("handshake(0x%04x)", revents); int rv = gnutls_handshake(session_); if (rv == GNUTLS_E_SUCCESS) { // handshake either completed or failed TRACE("SSL handshake complete. (time: %.4f)", ev_now(loop()) - ctime_); setState(Operational); setMode(Read); if (handshakeCallback_) { handshakeCallback_(this, handshakeData_); } } else if (rv != GNUTLS_E_AGAIN && rv != GNUTLS_E_INTERRUPTED) { TRACE("SSL handshake failed (%d): %s", rv, gnutls_strerror(rv)); close(); if (handshakeCallback_) { handshakeCallback_(this, handshakeData_); } } else { TRACE("SSL partial handshake: (%d)", gnutls_record_get_direction(session_)); switch (gnutls_record_get_direction(session_)) { case 0: // read setMode(Read); break; case 1: // write setMode(Write); break; default: break; } } }
static Eina_Bool _process_data(gnutls_session_t client, Ecore_Fd_Handler * fd_handler) { static int ret, lastret; static unsigned int count = 0; if (!done) { lastret = ret; ret = gnutls_handshake(client); count++; if (gnutls_record_get_direction(client)) ecore_main_fd_handler_active_set(fd_handler, ECORE_FD_WRITE); else ecore_main_fd_handler_active_set(fd_handler, ECORE_FD_READ); /* avoid printing messages infinity times */ if (lastret != ret && ret != 0 && ret != GNUTLS_E_AGAIN) { print("gnutls returned with: %s - %s", gnutls_strerror_name(ret), gnutls_strerror(ret)); if ((ret == GNUTLS_E_WARNING_ALERT_RECEIVED) || (ret == GNUTLS_E_FATAL_ALERT_RECEIVED)) print("Also received alert: %s", gnutls_alert_get_name (gnutls_alert_get(client))); print("last out: %s", SSL_GNUTLS_PRINT_HANDSHAKE_STATUS (gnutls_handshake_get_last_out(client))); print("last in: %s", SSL_GNUTLS_PRINT_HANDSHAKE_STATUS (gnutls_handshake_get_last_in(client))); } if (gnutls_error_is_fatal(ret)) { print("yarrr this be an error!"); exit(1); } } if (ret == GNUTLS_E_SUCCESS) { done = 1; //print("Handshake successful in %u handshake calls!", count); ecore_main_loop_quit(); } return ECORE_CALLBACK_RENEW; }
int CTlsSocket::ContinueHandshake() { m_pOwner->LogMessage(MessageType::Debug_Verbose, _T("CTlsSocket::ContinueHandshake()")); wxASSERT(m_session); wxASSERT(m_tlsState == TlsState::handshake); int res = gnutls_handshake(m_session); while (res == GNUTLS_E_AGAIN || res == GNUTLS_E_INTERRUPTED) { if (!(gnutls_record_get_direction(m_session) ? m_canWriteToSocket : m_canReadFromSocket)) { break; } res = gnutls_handshake(m_session); } if (!res) { m_pOwner->LogMessage(MessageType::Debug_Info, _T("TLS Handshake successful")); if (ResumedSession()) m_pOwner->LogMessage(MessageType::Debug_Info, _T("TLS Session resumed")); const wxString protocol = GetProtocolName(); const wxString keyExchange = GetKeyExchange(); const wxString cipherName = GetCipherName(); const wxString macName = GetMacName(); m_pOwner->LogMessage(MessageType::Debug_Info, _T("Protocol: %s, Key exchange: %s, Cipher: %s, MAC: %s"), protocol, keyExchange, cipherName, macName); res = VerifyCertificate(); if (res != FZ_REPLY_OK) return res; if (m_shutdown_requested) { int error = Shutdown(); if (!error || error != EAGAIN) { m_pEvtHandler->send_event<CSocketEvent>(this, SocketEventType::close, 0); } } return FZ_REPLY_OK; } else if (res == GNUTLS_E_AGAIN || res == GNUTLS_E_INTERRUPTED) return FZ_REPLY_WOULDBLOCK; Failure(res, true); return FZ_REPLY_ERROR; }
bool Handshake(issl_session* session, StreamSocket* user) { int ret = gnutls_handshake(session->sess); if (ret < 0) { if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) { // Handshake needs resuming later, read() or write() would have blocked. if(gnutls_record_get_direction(session->sess) == 0) { // gnutls_handshake() wants to read() again. session->status = ISSL_HANDSHAKING_READ; ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE); } else { // gnutls_handshake() wants to write() again. session->status = ISSL_HANDSHAKING_WRITE; ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE); } } else { user->SetError("Handshake Failed - " + std::string(gnutls_strerror(ret))); CloseSession(session); session->status = ISSL_CLOSING; } return false; } else { // Change the seesion state session->status = ISSL_HANDSHAKEN; VerifyCertificate(session,user); // Finish writing, if any left ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE); return true; } }
static int tlsg_session_upflags( Sockbuf *sb, tls_session *session, int rc ) { tlsg_session *s = (tlsg_session *)session; if ( rc != GNUTLS_E_INTERRUPTED && rc != GNUTLS_E_AGAIN ) return 0; switch (gnutls_record_get_direction (s->session)) { case 0: sb->sb_trans_needs_read = 1; return 1; case 1: sb->sb_trans_needs_write = 1; return 1; } return 0; }
static int stream__handshake (evcom_stream *stream) { assert(SECURE(stream)); int r = gnutls_handshake(stream->session); if (gnutls_error_is_fatal(r)) { stream->gnutls_errorno = r; stream->send_action = stream_send__close; stream->recv_action = stream_recv__close; return OKAY; } ev_timer_again(D_LOOP_(stream) &stream->timeout_watcher); if (r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN) { if (0 == gnutls_record_get_direction((stream)->session)) { ev_io_start(D_LOOP_(stream) &(stream)->read_watcher); ev_io_stop(D_LOOP_(stream) &(stream)->write_watcher); } else { ev_io_stop(D_LOOP_(stream) &(stream)->read_watcher); ev_io_start(D_LOOP_(stream) &(stream)->write_watcher); } assert(stream->recv_action == stream__handshake); assert(stream->send_action == stream__handshake); return AGAIN; } assert(!CONNECTED(stream)); stream->flags |= EVCOM_CONNECTED; if (stream->on_connect) stream->on_connect(stream); /* evcom_stream_force_close might have been called. */ if (stream->recvfd >= 0 && stream->sendfd >= 0) { ev_io_start(D_LOOP_(stream) &stream->read_watcher); ev_io_start(D_LOOP_(stream) &stream->write_watcher); stream->send_action = stream_send__data; stream->recv_action = stream_recv__data; } return OKAY; }
// Returns 1 if handshake succeeded, 0 if it is still in progress, -1 if it failed int Handshake(StreamSocket* user) { int ret = gnutls_handshake(this->sess); if (ret < 0) { if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) { // Handshake needs resuming later, read() or write() would have blocked. this->status = ISSL_HANDSHAKING; if (gnutls_record_get_direction(this->sess) == 0) { // gnutls_handshake() wants to read() again. SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE); } else { // gnutls_handshake() wants to write() again. SocketEngine::ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE); } return 0; } else { user->SetError("Handshake Failed - " + std::string(gnutls_strerror(ret))); CloseSession(); return -1; } } else { // Change the seesion state this->status = ISSL_HANDSHAKEN; VerifyCertificate(); // Finish writing, if any left SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE); return 1; } }
static int do_tls_poll(URLContext *h, int ret) { TLSContext *c = h->priv_data; struct pollfd p = { c->fd, 0, 0 }; #if CONFIG_GNUTLS switch (ret) { case GNUTLS_E_AGAIN: case GNUTLS_E_INTERRUPTED: break; case GNUTLS_E_WARNING_ALERT_RECEIVED: av_log(h, AV_LOG_WARNING, "%s\n", gnutls_strerror(ret)); break; default: av_log(h, AV_LOG_ERROR, "%s\n", gnutls_strerror(ret)); return AVERROR(EIO); } if (gnutls_record_get_direction(c->session)) p.events = POLLOUT; else p.events = POLLIN; #elif CONFIG_OPENSSL ret = SSL_get_error(c->ssl, ret); if (ret == SSL_ERROR_WANT_READ) { p.events = POLLIN; } else if (ret == SSL_ERROR_WANT_WRITE) { p.events = POLLOUT; } else { av_log(h, AV_LOG_ERROR, "%s\n", ERR_error_string(ERR_get_error(), NULL)); return AVERROR(EIO); } #endif if (h->flags & AVIO_FLAG_NONBLOCK) return AVERROR(EAGAIN); while (1) { int n = poll(&p, 1, 100); if (n > 0) break; if (ff_check_interrupt(&h->interrupt_callback)) return AVERROR(EINTR); } return 0; }
void CTlsSocket::OnSend() { m_pOwner->LogMessage(Debug_Debug, _T("CTlsSocket::OnSend()")); m_canWriteToSocket = true; if (!m_session) return; if (!gnutls_record_get_direction(m_session)) return; if (m_tlsState == handshake) Handshake(); else if (m_tlsState == closing) ContinueShutdown(); else if (m_tlsState == conn) { CheckResumeFailedWrite(); TriggerEvents(); } }
void CTlsSocket::OnSend() { m_pOwner->LogMessage(MessageType::Debug_Debug, _T("CTlsSocket::OnSend()")); m_canWriteToSocket = true; if (!m_session) return; const int direction = gnutls_record_get_direction(m_session); if (!direction && !m_lastWriteFailed) return; if (m_tlsState == TlsState::handshake) ContinueHandshake(); else if (m_tlsState == TlsState::closing) ContinueShutdown(); else if (m_tlsState == TlsState::conn) { CheckResumeFailedReadWrite(); TriggerEvents(); } }
static int do_ssl_handshake(rb_fde_t *F, PF * callback) { int ret; int flags; ret = gnutls_handshake(SSL_P(F)); if (ret < 0) { if ((ret == GNUTLS_E_INTERRUPTED && rb_ignore_errno(errno)) || ret == GNUTLS_E_AGAIN) { if (gnutls_record_get_direction(SSL_P(F)) == 0) flags = RB_SELECT_READ; else flags = RB_SELECT_WRITE; rb_setselect(F, flags, callback, NULL); return 0; } F->ssl_errno = ret; return -1; } return 1; /* handshake is finished..go about life */ }
void TLSSocket_GnuTLS::sendRaw(const byte_t* buffer, const size_t count) { m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ); for (size_t size = count ; size > 0 ; ) { resetException(); ssize_t ret = gnutls_record_send( *m_session->m_gnutlsSession, buffer, static_cast <size_t>(size) ); throwException(); if (ret < 0) { if (ret == GNUTLS_E_AGAIN) { if (gnutls_record_get_direction(*m_session->m_gnutlsSession) == 0) { m_wrapped->waitForRead(); } else { m_wrapped->waitForWrite(); } continue; } TLSSession_GnuTLS::throwTLSException("gnutls_record_send", static_cast <int>(ret)); } else { buffer += ret; size -= ret; } } }
static int stream_send__gnutls_bye (evcom_stream *stream) { assert(SECURE(stream)); int r = gnutls_bye(stream->session, GNUTLS_SHUT_WR); if (r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN) { assert(1 == gnutls_record_get_direction((stream)->session)); assert(stream->send_action == stream_send__gnutls_bye); return AGAIN; } if (gnutls_error_is_fatal(r)) { stream->gnutls_errorno = r; stream->send_action = stream_send__close; return OKAY; } stream->flags &= ~EVCOM_WRITABLE; stream->send_action = stream_send__wait_for_eof; return OKAY; }
static int vncws_start_tls_handshake(struct VncState *vs) { int ret = gnutls_handshake(vs->tls.session); if (ret < 0) { if (!gnutls_error_is_fatal(ret)) { VNC_DEBUG("Handshake interrupted (blocking)\n"); if (!gnutls_record_get_direction(vs->tls.session)) { qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs); } else { qemu_set_fd_handler(vs->csock, NULL, vncws_tls_handshake_io, vs); } return 0; } VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret)); vnc_client_error(vs); return -1; } if (vs->vd->tls.x509verify) { if (vnc_tls_validate_certificate(vs) < 0) { VNC_DEBUG("Client verification failed\n"); vnc_client_error(vs); return -1; } else { VNC_DEBUG("Client verification passed\n"); } } VNC_DEBUG("Handshake done, switching to TLS data mode\n"); qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read, NULL, vs); return 0; }
/** * @return TLS_HANDSHAKE_ERROR if the TLS handshake failed. * TLS_HANDSHAKE_RETRY if the handshake is incomplete; thus * tls_handshake() should called again on the next I/O event. * TLS_HANDSHAKE_FINISHED if the TLS handshake succeeded. Note * that this is also returned if TLS is disabled. Therefore * this does not imply an encrypted connection. */ enum tls_handshake_result tls_handshake(struct gnutella_socket *s) { gnutls_session session; bool do_warn; int ret; socket_check(s); /* * For connect-back probes, the handshake will probably fail. We use * TLS anyway to avoid getting blocked which the remote peer would * not notice. Thus suppress warnings for failed handshakes in this * case. */ do_warn = SOCK_TYPE_CONNBACK != s->type; session = tls_socket_get_session(s); g_return_val_if_fail(session, TLS_HANDSHAKE_ERROR); g_return_val_if_fail(SOCK_TLS_INITIALIZED == s->tls.stage, TLS_HANDSHAKE_ERROR); ret = gnutls_handshake(session); switch (ret) { case 0: if (GNET_PROPERTY(tls_debug) > 3) { g_debug("TLS handshake succeeded"); } tls_socket_evt_change(s, SOCK_CONN_INCOMING == s->direction ? INPUT_EVENT_R : INPUT_EVENT_W); if (GNET_PROPERTY(tls_debug > 3)) { tls_print_session_info(s->addr, s->port, session, SOCK_CONN_INCOMING == s->direction); } tls_signal_pending(s); return TLS_HANDSHAKE_FINISHED; case GNUTLS_E_AGAIN: case GNUTLS_E_INTERRUPTED: tls_socket_evt_change(s, gnutls_record_get_direction(session) ? INPUT_EVENT_WX : INPUT_EVENT_RX); if (GNET_PROPERTY(tls_debug) > 3) { g_debug("TLS handshake proceeding..."); } tls_signal_pending(s); return TLS_HANDSHAKE_RETRY; case GNUTLS_E_PULL_ERROR: case GNUTLS_E_PUSH_ERROR: /* Logging already done by tls_transport_debug() */ break; case GNUTLS_E_UNEXPECTED_PACKET_LENGTH: if ((SOCK_F_EOF | SOCK_F_CONNRESET) & s->flags) { /* Remote peer has hung up */ break; } /* FALLTHROUGH */ default: if (do_warn && GNET_PROPERTY(tls_debug)) { g_carp("gnutls_handshake() failed: host=%s (%s) error=\"%s\"", host_addr_port_to_string(s->addr, s->port), SOCK_CONN_INCOMING == s->direction ? "incoming" : "outgoing", gnutls_strerror(ret)); } } return TLS_HANDSHAKE_ERROR; }
int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout) { int work_done = 0; char magic_pkt; while (1) { int len = vpninfo->ip_info.mtu; unsigned char *buf; if (!dtls_pkt || len > dtls_pkt_max) { realloc_inplace(dtls_pkt, sizeof(struct pkt) + len); if (!dtls_pkt) { vpn_progress(vpninfo, PRG_ERR, "Allocation failed\n"); break; } dtls_pkt_max = len; } buf = dtls_pkt->data - 1; len = DTLS_RECV(vpninfo->dtls_ssl, buf, len + 1); if (len <= 0) break; vpn_progress(vpninfo, PRG_TRACE, _("Received DTLS packet 0x%02x of %d bytes\n"), buf[0], len); vpninfo->dtls_times.last_rx = time(NULL); switch (buf[0]) { case AC_PKT_DATA: dtls_pkt->len = len - 1; queue_packet(&vpninfo->incoming_queue, dtls_pkt); dtls_pkt = NULL; work_done = 1; break; case AC_PKT_DPD_OUT: vpn_progress(vpninfo, PRG_TRACE, _("Got DTLS DPD request\n")); /* FIXME: What if the packet doesn't get through? */ magic_pkt = AC_PKT_DPD_RESP; if (DTLS_SEND(vpninfo->dtls_ssl, &magic_pkt, 1) != 1) vpn_progress(vpninfo, PRG_ERR, _("Failed to send DPD response. Expect disconnect\n")); continue; case AC_PKT_DPD_RESP: vpn_progress(vpninfo, PRG_TRACE, _("Got DTLS DPD response\n")); break; case AC_PKT_KEEPALIVE: vpn_progress(vpninfo, PRG_TRACE, _("Got DTLS Keepalive\n")); break; default: vpn_progress(vpninfo, PRG_ERR, _("Unknown DTLS packet type %02x, len %d\n"), buf[0], len); if (1) { /* Some versions of OpenSSL have bugs with receiving out-of-order * packets. Not only do they wrongly decide to drop packets if * two packets get swapped in transit, but they also _fail_ to * drop the packet in non-blocking mode; instead they return * the appropriate length of garbage. So don't abort... for now. */ break; } else { vpninfo->quit_reason = "Unknown packet received"; return 1; } } } switch (keepalive_action(&vpninfo->dtls_times, timeout)) { case KA_REKEY: { int ret; vpn_progress(vpninfo, PRG_INFO, _("DTLS rekey due\n")); /* There ought to be a method of rekeying DTLS without tearing down the CSTP session and restarting, but we don't (yet) know it */ ret = cstp_reconnect(vpninfo); if (ret) { vpn_progress(vpninfo, PRG_ERR, _("Reconnect failed\n")); vpninfo->quit_reason = "CSTP reconnect failed"; return ret; } if (dtls_restart(vpninfo)) vpn_progress(vpninfo, PRG_ERR, _("DTLS rekey failed\n")); return 1; } case KA_DPD_DEAD: vpn_progress(vpninfo, PRG_ERR, _("DTLS Dead Peer Detection detected dead peer!\n")); /* Fall back to SSL, and start a new DTLS connection */ dtls_restart(vpninfo); return 1; case KA_DPD: vpn_progress(vpninfo, PRG_TRACE, _("Send DTLS DPD\n")); magic_pkt = AC_PKT_DPD_OUT; if (DTLS_SEND(vpninfo->dtls_ssl, &magic_pkt, 1) != 1) vpn_progress(vpninfo, PRG_ERR, _("Failed to send DPD request. Expect disconnect\n")); /* last_dpd will just have been set */ vpninfo->dtls_times.last_tx = vpninfo->dtls_times.last_dpd; work_done = 1; break; case KA_KEEPALIVE: /* No need to send an explicit keepalive if we have real data to send */ if (vpninfo->outgoing_queue) break; vpn_progress(vpninfo, PRG_TRACE, _("Send DTLS Keepalive\n")); magic_pkt = AC_PKT_KEEPALIVE; if (DTLS_SEND(vpninfo->dtls_ssl, &magic_pkt, 1) != 1) vpn_progress(vpninfo, PRG_ERR, _("Failed to send keepalive request. Expect disconnect\n")); time(&vpninfo->dtls_times.last_tx); work_done = 1; break; case KA_NONE: ; } /* Service outgoing packet queue */ FD_CLR(vpninfo->dtls_fd, &vpninfo->select_wfds); while (vpninfo->outgoing_queue) { struct pkt *this = vpninfo->outgoing_queue; int ret; vpninfo->outgoing_queue = this->next; vpninfo->outgoing_qlen--; /* One byte of header */ this->hdr[7] = AC_PKT_DATA; #if defined(DTLS_OPENSSL) ret = SSL_write(vpninfo->dtls_ssl, &this->hdr[7], this->len + 1); if (ret <= 0) { ret = SSL_get_error(vpninfo->dtls_ssl, ret); if (ret == SSL_ERROR_WANT_WRITE) { FD_SET(vpninfo->dtls_fd, &vpninfo->select_wfds); vpninfo->outgoing_queue = this; vpninfo->outgoing_qlen++; } else if (ret != SSL_ERROR_WANT_READ) { /* If it's a real error, kill the DTLS connection and requeue the packet to be sent over SSL */ vpn_progress(vpninfo, PRG_ERR, _("DTLS got write error %d. Falling back to SSL\n"), ret); openconnect_report_ssl_errors(vpninfo); dtls_restart(vpninfo); vpninfo->outgoing_queue = this; vpninfo->outgoing_qlen++; work_done = 1; } return work_done; } #elif defined(DTLS_GNUTLS) ret = gnutls_record_send(vpninfo->dtls_ssl, &this->hdr[7], this->len + 1); if (ret <= 0) { if (ret != GNUTLS_E_AGAIN) { vpn_progress(vpninfo, PRG_ERR, _("DTLS got write error: %s. Falling back to SSL\n"), gnutls_strerror(ret)); dtls_restart(vpninfo); vpninfo->outgoing_queue = this; vpninfo->outgoing_qlen++; work_done = 1; } else if (gnutls_record_get_direction(vpninfo->dtls_ssl)) { FD_SET(vpninfo->dtls_fd, &vpninfo->select_wfds); vpninfo->outgoing_queue = this; vpninfo->outgoing_qlen++; } return work_done; } #endif time(&vpninfo->dtls_times.last_tx); vpn_progress(vpninfo, PRG_TRACE, _("Sent DTLS packet of %d bytes; DTLS send returned %d\n"), this->len, ret); free(this); } return work_done; }
/* this function does a SSL/TLS (re-)handshake */ static CURLcode handshake(struct connectdata *conn, int sockindex, bool duringconnect, bool nonblocking) { struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; gnutls_session session = conn->ssl[sockindex].session; curl_socket_t sockfd = conn->sock[sockindex]; long timeout_ms; int rc; int what; for(;;) { /* check allowed time left */ timeout_ms = Curl_timeleft(data, NULL, duringconnect); if(timeout_ms < 0) { /* no need to continue if time already is up */ failf(data, "SSL connection timeout"); return CURLE_OPERATION_TIMEDOUT; } /* if ssl is expecting something, check if it's available. */ if(connssl->connecting_state == ssl_connect_2_reading || connssl->connecting_state == ssl_connect_2_writing) { curl_socket_t writefd = ssl_connect_2_writing== connssl->connecting_state?sockfd:CURL_SOCKET_BAD; curl_socket_t readfd = ssl_connect_2_reading== connssl->connecting_state?sockfd:CURL_SOCKET_BAD; what = Curl_socket_ready(readfd, writefd, nonblocking?0: timeout_ms?timeout_ms:1000); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); return CURLE_SSL_CONNECT_ERROR; } else if(0 == what) { if(nonblocking) return CURLE_OK; else if(timeout_ms) { /* timeout */ failf(data, "SSL connection timeout at %ld", timeout_ms); return CURLE_OPERATION_TIMEDOUT; } } /* socket is readable or writable */ } rc = gnutls_handshake(session); if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { connssl->connecting_state = gnutls_record_get_direction(session)? ssl_connect_2_writing:ssl_connect_2_reading; continue; if(nonblocking) return CURLE_OK; } else if((rc < 0) && !gnutls_error_is_fatal(rc)) { const char *strerr = NULL; if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) { int alert = gnutls_alert_get(session); strerr = gnutls_alert_get_name(alert); } if(strerr == NULL) strerr = gnutls_strerror(rc); failf(data, "gnutls_handshake() warning: %s", strerr); } else if(rc < 0) { const char *strerr = NULL; if(rc == GNUTLS_E_FATAL_ALERT_RECEIVED) { int alert = gnutls_alert_get(session); strerr = gnutls_alert_get_name(alert); } if(strerr == NULL) strerr = gnutls_strerror(rc); failf(data, "gnutls_handshake() failed: %s", strerr); return CURLE_SSL_CONNECT_ERROR; } /* Reset our connect state machine */ connssl->connecting_state = ssl_connect_1; return CURLE_OK; } }
b_input_condition ssl_getdirection( void *conn ) { return( gnutls_record_get_direction( ((struct scd*)conn)->session ) ? B_EV_IO_WRITE : B_EV_IO_READ ); }
bool session::get_record_direction () const { return gnutls_record_get_direction (s); }
int CTlsSocket::DoCallGnutlsRecordRecv(void* data, size_t len) { int res = gnutls_record_recv(m_session, data, len); while( (res == GNUTLS_E_AGAIN || res == GNUTLS_E_INTERRUPTED) && m_canReadFromSocket && !gnutls_record_get_direction(m_session)) { // Spurious EAGAIN. Can happen if GnuTLS gets a partial // record and the socket got closed. // The unexpected close is being ignored in this case, unless // gnutls_record_recv is being called again. // Manually call gnutls_record_recv as in case of eof on the socket, // we are not getting another receive event. m_pOwner->LogMessage(MessageType::Debug_Verbose, _T("gnutls_record_recv returned spurious EAGAIN")); res = gnutls_record_recv(m_session, data, len); } return res; }
/** * Check and handle error return codes after failed calls to SSL functions. * * OpenSSL: * SSL_connect(), SSL_accept(), SSL_do_handshake(), SSL_read(), SSL_peek(), or * SSL_write() on ssl. * * GnuTLS: * gnutlsssl_read(), gnutls_write() or gnutls_handshake(). * * @param c The connection handle. * @prarm code The return code. * @param fname The name of the function in which the error occurred. * @return -1 on fatal errors, 0 if we can try again later. */ static int ConnSSL_HandleError(CONNECTION * c, const int code, const char *fname) { #ifdef HAVE_LIBSSL int ret = SSL_ERROR_SYSCALL; unsigned long sslerr; int real_errno = errno; ret = SSL_get_error(c->ssl_state.ssl, code); switch (ret) { case SSL_ERROR_WANT_READ: io_event_del(c->sock, IO_WANTWRITE); Conn_OPTION_ADD(c, CONN_SSL_WANT_READ); return 0; /* try again later */ case SSL_ERROR_WANT_WRITE: io_event_del(c->sock, IO_WANTREAD); Conn_OPTION_ADD(c, CONN_SSL_WANT_WRITE); /* fall through */ case SSL_ERROR_NONE: return 0; /* try again later */ case SSL_ERROR_ZERO_RETURN: LogDebug("SSL connection shut down normally."); break; case SSL_ERROR_SYSCALL: /* SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT, * and SSL_ERROR_WANT_X509_LOOKUP */ sslerr = ERR_get_error(); if (sslerr) { Log(LOG_ERR, "SSL error: %s [in %s()]!", ERR_error_string(sslerr, NULL), fname); } else { switch (code) { /* EOF that violated protocol */ case 0: Log(LOG_ERR, "SSL error, client disconnected [in %s()]!", fname); break; case -1: /* low level socket I/O error, check errno */ Log(LOG_ERR, "SSL error: %s [in %s()]!", strerror(real_errno), fname); } } break; case SSL_ERROR_SSL: LogOpenSSLError("SSL protocol error", fname); break; default: Log(LOG_ERR, "Unknown SSL error %d [in %s()]!", ret, fname); } ConnSSL_Free(c); return -1; #endif #ifdef HAVE_LIBGNUTLS switch (code) { case GNUTLS_E_AGAIN: case GNUTLS_E_INTERRUPTED: if (gnutls_record_get_direction(c->ssl_state.gnutls_session)) { Conn_OPTION_ADD(c, CONN_SSL_WANT_WRITE); io_event_del(c->sock, IO_WANTREAD); } else { Conn_OPTION_ADD(c, CONN_SSL_WANT_READ); io_event_del(c->sock, IO_WANTWRITE); } break; default: assert(code < 0); if (gnutls_error_is_fatal(code)) { Log(LOG_ERR, "SSL error: %s [%s].", gnutls_strerror(code), fname); ConnSSL_Free(c); return -1; } } return 0; #endif }
static Ecore_Con_Ssl_Error _ecore_con_ssl_client_init_gnutls(Ecore_Con_Client *cl) { const gnutls_datum_t *cert_list; unsigned int iter, cert_list_size; const char *priority = "NONE:%VERIFY_ALLOW_X509_V1_CA_CRT:+RSA:+DHE-RSA:+DHE-DSS:+ANON-DH:+COMP-DEFLATE:+COMP-NULL:+CTYPE-X509:+SHA1:+SHA256:+SHA384:+SHA512:+AES-256-CBC:+AES-128-CBC:+3DES-CBC:+VERS-TLS1.2:+VERS-TLS1.1:+VERS-TLS1.0:+VERS-SSL3.0"; int ret = 0; switch (cl->ssl_state) { case ECORE_CON_SSL_STATE_DONE: return ECORE_CON_SSL_ERROR_NONE; case ECORE_CON_SSL_STATE_INIT: if (cl->host_server->type & ECORE_CON_USE_SSL2) /* not supported because of security issues */ return ECORE_CON_SSL_ERROR_SSL2_NOT_SUPPORTED; switch (cl->host_server->type & ECORE_CON_SSL) { case ECORE_CON_USE_SSL3: case ECORE_CON_USE_SSL3 | ECORE_CON_LOAD_CERT: priority = "NONE:%VERIFY_ALLOW_X509_V1_CA_CRT:+RSA:+DHE-RSA:+DHE-DSS:+ANON-DH:+COMP-DEFLATE:+COMP-NULL:+CTYPE-X509:+SHA1:+SHA256:+SHA384:+SHA512:+AES-256-CBC:+AES-128-CBC:+3DES-CBC:!VERS-TLS1.0:!VERS-TLS1.1"; break; case ECORE_CON_USE_TLS: case ECORE_CON_USE_TLS | ECORE_CON_LOAD_CERT: priority = "NONE:%VERIFY_ALLOW_X509_V1_CA_CRT:+RSA:+DHE-RSA:+DHE-DSS:+ANON-DH:+COMP-DEFLATE:+COMP-NULL:+CTYPE-X509:+SHA1:+SHA256:+SHA384:+SHA512:+AES-256-CBC:+AES-128-CBC:+3DES-CBC:!VERS-SSL3.0"; break; case ECORE_CON_USE_MIXED: case ECORE_CON_USE_MIXED | ECORE_CON_LOAD_CERT: break; default: return ECORE_CON_SSL_ERROR_NONE; } _client_connected++; SSL_ERROR_CHECK_GOTO_ERROR(ret = gnutls_init(&cl->session, GNUTLS_SERVER)); SSL_ERROR_CHECK_GOTO_ERROR(ret = gnutls_session_ticket_key_generate(&cl->session_ticket)); SSL_ERROR_CHECK_GOTO_ERROR(ret = gnutls_session_ticket_enable_server(cl->session, &cl->session_ticket)); INF("Applying priority string: %s", priority); SSL_ERROR_CHECK_GOTO_ERROR(ret = gnutls_priority_set_direct(cl->session, priority, NULL)); SSL_ERROR_CHECK_GOTO_ERROR(ret = gnutls_credentials_set(cl->session, GNUTLS_CRD_CERTIFICATE, cl->host_server->cert)); // SSL_ERROR_CHECK_GOTO_ERROR(ret = gnutls_credentials_set(cl->session, GNUTLS_CRD_PSK, cl->host_server->pskcred_s)); if (!cl->host_server->use_cert) SSL_ERROR_CHECK_GOTO_ERROR(ret = gnutls_credentials_set(cl->session, GNUTLS_CRD_ANON, cl->host_server->anoncred_s)); gnutls_certificate_server_set_request(cl->session, GNUTLS_CERT_REQUEST); gnutls_dh_set_prime_bits(cl->session, 2048); gnutls_transport_set_ptr(cl->session, (gnutls_transport_ptr_t)((intptr_t)cl->fd)); cl->ssl_state = ECORE_CON_SSL_STATE_HANDSHAKING; case ECORE_CON_SSL_STATE_HANDSHAKING: if (!cl->session) { DBG("Client was previously lost, going to error condition"); goto error; } DBG("calling gnutls_handshake()"); ret = gnutls_handshake(cl->session); SSL_ERROR_CHECK_GOTO_ERROR(gnutls_error_is_fatal(ret)); if (!ret) { cl->handshaking = EINA_FALSE; cl->ssl_state = ECORE_CON_SSL_STATE_DONE; } else { if (gnutls_record_get_direction(cl->session)) ecore_main_fd_handler_active_set(cl->fd_handler, ECORE_FD_WRITE); else ecore_main_fd_handler_active_set(cl->fd_handler, ECORE_FD_READ); return ECORE_CON_SSL_ERROR_NONE; } default: break; } if (!cl->host_server->verify) /* not verifying certificates, so we're done! */ return ECORE_CON_SSL_ERROR_NONE; ret = 0; /* use CRL/CA lists to verify */ SSL_ERROR_CHECK_GOTO_ERROR(ret = gnutls_certificate_verify_peers2(cl->session, &iter)); if (iter & GNUTLS_CERT_INVALID) ERR("The certificate is not trusted."); else if (iter & GNUTLS_CERT_SIGNER_NOT_FOUND) ERR("The certificate hasn't got a known issuer."); else if (iter & GNUTLS_CERT_REVOKED) ERR("The certificate has been revoked."); else if (iter & GNUTLS_CERT_EXPIRED) ERR("The certificate has expired"); else if (iter & GNUTLS_CERT_NOT_ACTIVATED) ERR("The certificate is not yet activated"); if (iter) goto error; if (gnutls_certificate_type_get(cl->session) != GNUTLS_CRT_X509) { ERR("Warning: PGP certificates are not yet supported!"); goto error; } SSL_ERROR_CHECK_GOTO_ERROR(!(cert_list = gnutls_certificate_get_peers(cl->session, &cert_list_size))); SSL_ERROR_CHECK_GOTO_ERROR(!cert_list_size); DBG("SSL certificate verification succeeded!"); return ECORE_CON_SSL_ERROR_NONE; error: _gnutls_print_errors(ret); if ((ret == GNUTLS_E_WARNING_ALERT_RECEIVED) || (ret == GNUTLS_E_FATAL_ALERT_RECEIVED)) ERR("Also received alert: %s", gnutls_alert_get_name(gnutls_alert_get(cl->session))); if (cl->session && (cl->ssl_state != ECORE_CON_SSL_STATE_DONE)) { ERR("last out: %s", SSL_GNUTLS_PRINT_HANDSHAKE_STATUS(gnutls_handshake_get_last_out(cl->session))); ERR("last in: %s", SSL_GNUTLS_PRINT_HANDSHAKE_STATUS(gnutls_handshake_get_last_in(cl->session))); } _ecore_con_ssl_client_shutdown_gnutls(cl); return ECORE_CON_SSL_ERROR_SERVER_INIT_FAILED; }
static int stream_recv__data (evcom_stream *stream) { char buf[EVCOM_CHUNKSIZE]; size_t buf_size = EVCOM_CHUNKSIZE; ssize_t recved; while (READABLE(stream)) { assert(CONNECTED(stream)); if (PAUSED(stream)) { stream->recv_action = stream_recv__wait_for_resume; return OKAY; } #if EVCOM_HAVE_GNUTLS if (SECURE(stream)) { recved = gnutls_record_recv(stream->session, buf, buf_size); if (gnutls_error_is_fatal(recved)) { stream->gnutls_errorno = recved; stream->recv_action = stream_recv__close; return OKAY; } if (recved == GNUTLS_E_INTERRUPTED || recved == GNUTLS_E_AGAIN) { if (1 == gnutls_record_get_direction((stream)->session)) { fprintf(stderr, "(evcom) gnutls recv: unexpected switch direction!\n"); ev_io_stop(D_LOOP_(stream) &(stream)->read_watcher); ev_io_start(D_LOOP_(stream) &(stream)->write_watcher); } return AGAIN; } /* A server may also receive GNUTLS_E_REHANDSHAKE when a client has * initiated a andshake. In that case the server can only initiate a * handshake or terminate the connection. */ if (recved == GNUTLS_E_REHANDSHAKE) { assert(WRITABLE(stream)); stream->recv_action = stream__handshake; stream->send_action = stream__handshake; return OKAY; } } else #endif /* EVCOM_HAVE_GNUTLS */ { recved = read(stream->recvfd, buf, buf_size); } if (recved < 0) { if (errno == EAGAIN || errno == EINTR) { assert(stream->recv_action == stream_recv__data); return AGAIN; } if (errno != ECONNRESET) { evcom_perror("recv()", stream->errorno); } stream->errorno = errno; stream->recv_action = stream_recv__close; return OKAY; } ev_timer_again(D_LOOP_(stream) &stream->timeout_watcher); assert(recved >= 0); if (recved == 0) { stream->flags &= ~EVCOM_READABLE; ev_io_stop(D_LOOP_(stream) &stream->read_watcher); stream->recv_action = stream_recv__wait_for_close; } /* NOTE: EOF is signaled with recved == 0 on callback */ if (stream->on_read) stream->on_read(stream, buf, recved); if (recved == 0) { return OKAY; } } return AGAIN; }
static int stream_send__data (evcom_stream *stream) { ssize_t sent; while (!evcom_queue_empty(&stream->out)) { assert(WRITABLE(stream)); evcom_queue *q = evcom_queue_last(&stream->out); evcom_buf *buf = evcom_queue_data(q, evcom_buf, queue); #if EVCOM_HAVE_GNUTLS if (SECURE(stream)) { sent = gnutls_record_send(stream->session, buf->base + buf->written, buf->len - buf->written); if (sent == GNUTLS_E_INTERRUPTED || sent == GNUTLS_E_AGAIN) { if (0 == gnutls_record_get_direction((stream)->session)) { fprintf(stderr, "(evcom) gnutls send: unexpected switch direction!\n"); ev_io_start(D_LOOP_(stream) &(stream)->read_watcher); ev_io_stop(D_LOOP_(stream) &(stream)->write_watcher); } return AGAIN; } if (gnutls_error_is_fatal(sent)) { stream->gnutls_errorno = sent; stream->send_action = stream_send__close; return OKAY; } } else #endif // EVCOM_HAVE_GNUTLS { /* TODO use writev() here? */ sent = nosigpipe_stream_send(stream, buf->base + buf->written, buf->len - buf->written); } if (sent <= 0) { switch (errno) { case EAGAIN: case EINTR: assert(stream->send_action == stream_send__data); return AGAIN; default: stream->errorno = errno; evcom_perror("send()", errno); /* pass through */ case EPIPE: stream->send_action = stream_send__close; return OKAY; } } ev_timer_again(D_LOOP_(stream) &stream->timeout_watcher); assert(sent >= 0); buf->written += sent; if (buf->written == buf->len) { evcom_queue_remove(q); if (buf->release) buf->release(buf); } } assert(evcom_queue_empty(&stream->out)); stream->send_action = stream_send__drain; return OKAY; }
/* this function does a SSL/TLS (re-)handshake */ static CURLcode handshake(struct connectdata *conn, int sockindex, bool duringconnect, bool nonblocking) { struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; gnutls_session session = conn->ssl[sockindex].session; curl_socket_t sockfd = conn->sock[sockindex]; long timeout_ms; int rc; int what; while(1) { /* check allowed time left */ timeout_ms = Curl_timeleft(conn, NULL, duringconnect); if(timeout_ms < 0) { /* no need to continue if time already is up */ failf(data, "SSL connection timeout"); return CURLE_OPERATION_TIMEDOUT; } /* if ssl is expecting something, check if it's available. */ if(connssl->connecting_state == ssl_connect_2_reading || connssl->connecting_state == ssl_connect_2_writing) { curl_socket_t writefd = ssl_connect_2_writing== connssl->connecting_state?sockfd:CURL_SOCKET_BAD; curl_socket_t readfd = ssl_connect_2_reading== connssl->connecting_state?sockfd:CURL_SOCKET_BAD; what = Curl_socket_ready(readfd, writefd, nonblocking?0:(int)timeout_ms); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); return CURLE_SSL_CONNECT_ERROR; } else if(0 == what) { if(nonblocking) { return CURLE_OK; } else { /* timeout */ failf(data, "SSL connection timeout"); return CURLE_OPERATION_TIMEDOUT; } } /* socket is readable or writable */ } rc = gnutls_handshake(session); if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { connssl->connecting_state = gnutls_record_get_direction(session)? ssl_connect_2_writing:ssl_connect_2_reading; if(nonblocking) { return CURLE_OK; } } else if (rc < 0) { failf(data, "gnutls_handshake() failed: %s", gnutls_strerror(rc)); } else { /* Reset our connect state machine */ connssl->connecting_state = ssl_connect_1; return CURLE_OK; } } }
int network_connect_gnutls_handshake_fd_cb (void *arg_hook_connect, int fd) { struct t_hook *hook_connect; int rc, direction, flags; /* make C compiler happy */ (void) fd; hook_connect = (struct t_hook *)arg_hook_connect; rc = gnutls_handshake (*HOOK_CONNECT(hook_connect, gnutls_sess)); if ((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { direction = gnutls_record_get_direction (*HOOK_CONNECT(hook_connect, gnutls_sess)); flags = HOOK_FD(HOOK_CONNECT(hook_connect, handshake_hook_fd), flags); if ((((flags & HOOK_FD_FLAG_READ) == HOOK_FD_FLAG_READ) && (direction != 0)) || (((flags & HOOK_FD_FLAG_WRITE) == HOOK_FD_FLAG_WRITE) && (direction != 1))) { HOOK_FD(HOOK_CONNECT(hook_connect, handshake_hook_fd), flags) = (direction) ? HOOK_FD_FLAG_WRITE: HOOK_FD_FLAG_READ; } } else if (rc != GNUTLS_E_SUCCESS) { (void) (HOOK_CONNECT(hook_connect, callback)) (hook_connect->callback_data, WEECHAT_HOOK_CONNECT_GNUTLS_HANDSHAKE_ERROR, rc, gnutls_strerror (rc), HOOK_CONNECT(hook_connect, handshake_ip_address)); unhook (hook_connect); } else { fcntl (HOOK_CONNECT(hook_connect, sock), F_SETFL, HOOK_CONNECT(hook_connect, handshake_fd_flags)); #if LIBGNUTLS_VERSION_NUMBER < 0x02090a /* * gnutls only has the gnutls_certificate_set_verify_function() * function since version 2.9.10. We need to call our verify * function manually after the handshake for old gnutls versions */ if (hook_connect_gnutls_verify_certificates (*HOOK_CONNECT(hook_connect, gnutls_sess)) != 0) { (void) (HOOK_CONNECT(hook_connect, callback)) (hook_connect->callback_data, WEECHAT_HOOK_CONNECT_GNUTLS_HANDSHAKE_ERROR, rc, "Error in the certificate.", HOOK_CONNECT(hook_connect, handshake_ip_address)); unhook (hook_connect); return WEECHAT_RC_OK; } #endif unhook (HOOK_CONNECT(hook_connect, handshake_hook_fd)); (void) (HOOK_CONNECT(hook_connect, callback)) (hook_connect->callback_data, WEECHAT_HOOK_CONNECT_OK, 0, NULL, HOOK_CONNECT(hook_connect, handshake_ip_address)); unhook (hook_connect); } return WEECHAT_RC_OK; }
void TLSSocket_GnuTLS::handshake() { shared_ptr <timeoutHandler> toHandler = m_wrapped->getTimeoutHandler(); if (toHandler) { toHandler->resetTimeOut(); } if (getTracer()) { getTracer()->traceSend("Beginning SSL/TLS handshake"); } // Start handshaking process try { while (true) { resetException(); const int ret = gnutls_handshake(*m_session->m_gnutlsSession); throwException(); if (ret < 0) { if (ret == GNUTLS_E_AGAIN) { if (gnutls_record_get_direction(*m_session->m_gnutlsSession) == 0) { m_wrapped->waitForRead(); } else { m_wrapped->waitForWrite(); } } else if (ret == GNUTLS_E_INTERRUPTED) { // Non-fatal error } else { TLSSession_GnuTLS::throwTLSException("gnutls_handshake", ret); } } else { // Successful handshake break; } } } catch (...) { throw; } // Verify server's certificate(s) shared_ptr <security::cert::certificateChain> certs = getPeerCertificates(); if (certs == NULL) { throw exceptions::tls_exception("No peer certificate."); } m_session->getCertificateVerifier()->verify(certs, getPeerName()); m_connected = true; }
int network_connect_child_read_cb (void *arg_hook_connect, int fd) { struct t_hook *hook_connect; char buffer[1], buf_size[6], *cb_error, *cb_ip_address, *error; int num_read; long size_msg; #ifdef HAVE_GNUTLS int rc, direction; #endif /* make C compiler happy */ (void) fd; hook_connect = (struct t_hook *)arg_hook_connect; cb_error = NULL; cb_ip_address = NULL; num_read = read (HOOK_CONNECT(hook_connect, child_read), buffer, sizeof (buffer)); if (num_read > 0) { if (buffer[0] - '0' == WEECHAT_HOOK_CONNECT_OK) { /* connection ok, read IP address */ buf_size[5] = '\0'; num_read = read (HOOK_CONNECT(hook_connect, child_read), buf_size, 5); if (num_read == 5) { error = NULL; size_msg = strtol (buf_size, &error, 10); if (error && !error[0] && (size_msg > 0)) { cb_ip_address = malloc (size_msg + 1); if (cb_ip_address) { num_read = read (HOOK_CONNECT(hook_connect, child_read), cb_ip_address, size_msg); if (num_read == size_msg) cb_ip_address[size_msg] = '\0'; else { free (cb_ip_address); cb_ip_address = NULL; } } } } #ifdef HAVE_GNUTLS if (HOOK_CONNECT(hook_connect, gnutls_sess)) { /* * the socket needs to be non-blocking since the call to * gnutls_handshake can block */ HOOK_CONNECT(hook_connect, handshake_fd_flags) = fcntl (HOOK_CONNECT(hook_connect, sock), F_GETFL); if (HOOK_CONNECT(hook_connect, handshake_fd_flags) == -1) HOOK_CONNECT(hook_connect, handshake_fd_flags) = 0; fcntl (HOOK_CONNECT(hook_connect, sock), F_SETFL, HOOK_CONNECT(hook_connect, handshake_fd_flags) | O_NONBLOCK); gnutls_transport_set_ptr (*HOOK_CONNECT(hook_connect, gnutls_sess), (gnutls_transport_ptr) ((ptrdiff_t) HOOK_CONNECT(hook_connect, sock))); if (HOOK_CONNECT(hook_connect, gnutls_dhkey_size) > 0) { gnutls_dh_set_prime_bits (*HOOK_CONNECT(hook_connect, gnutls_sess), (unsigned int) HOOK_CONNECT(hook_connect, gnutls_dhkey_size)); } rc = gnutls_handshake (*HOOK_CONNECT(hook_connect, gnutls_sess)); if ((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { /* * gnutls was unable to proceed with the handshake without * blocking: non fatal error, we just have to wait for an * event about handshake */ unhook (HOOK_CONNECT(hook_connect, hook_fd)); HOOK_CONNECT(hook_connect, hook_fd) = NULL; direction = gnutls_record_get_direction (*HOOK_CONNECT(hook_connect, gnutls_sess)); HOOK_CONNECT(hook_connect, handshake_ip_address) = cb_ip_address; HOOK_CONNECT(hook_connect, handshake_hook_fd) = hook_fd (hook_connect->plugin, HOOK_CONNECT(hook_connect, sock), (!direction ? 1 : 0), (direction ? 1 : 0), 0, &network_connect_gnutls_handshake_fd_cb, hook_connect); HOOK_CONNECT(hook_connect, handshake_hook_timer) = hook_timer (hook_connect->plugin, CONFIG_INTEGER(config_network_gnutls_handshake_timeout) * 1000, 0, 1, &network_connect_gnutls_handshake_timer_cb, hook_connect); return WEECHAT_RC_OK; } else if (rc != GNUTLS_E_SUCCESS) { (void) (HOOK_CONNECT(hook_connect, callback)) (hook_connect->callback_data, WEECHAT_HOOK_CONNECT_GNUTLS_HANDSHAKE_ERROR, rc, gnutls_strerror (rc), cb_ip_address); unhook (hook_connect); if (cb_ip_address) free (cb_ip_address); return WEECHAT_RC_OK; } fcntl (HOOK_CONNECT(hook_connect, sock), F_SETFL, HOOK_CONNECT(hook_connect, handshake_fd_flags)); #if LIBGNUTLS_VERSION_NUMBER < 0x02090a /* * gnutls only has the gnutls_certificate_set_verify_function() * function since version 2.9.10. We need to call our verify * function manually after the handshake for old gnutls versions */ if (hook_connect_gnutls_verify_certificates (*HOOK_CONNECT(hook_connect, gnutls_sess)) != 0) { (void) (HOOK_CONNECT(hook_connect, callback)) (hook_connect->callback_data, WEECHAT_HOOK_CONNECT_GNUTLS_HANDSHAKE_ERROR, rc, "Error in the certificate.", cb_ip_address); unhook (hook_connect); if (cb_ip_address) free (cb_ip_address); return WEECHAT_RC_OK; } #endif } #endif } else { /* connection error, read error */ buf_size[5] = '\0'; num_read = read (HOOK_CONNECT(hook_connect, child_read), buf_size, 5); if (num_read == 5) { error = NULL; size_msg = strtol (buf_size, &error, 10); if (error && !error[0] && (size_msg > 0)) { cb_error = malloc (size_msg + 1); if (cb_error) { num_read = read (HOOK_CONNECT(hook_connect, child_read), cb_error, size_msg); if (num_read == size_msg) cb_error[size_msg] = '\0'; else { free (cb_error); cb_error = NULL; } } } } } (void) (HOOK_CONNECT(hook_connect, callback)) (hook_connect->callback_data, buffer[0] - '0', 0, cb_error, cb_ip_address); unhook (hook_connect); } if (cb_error) free (cb_error); if (cb_ip_address) free (cb_ip_address); return WEECHAT_RC_OK; }