Esempio n. 1
33
SSL_CTX *SSLSocket::sslCreateCtx(bool client, bool verify, const char *CAfile, const char *CRTfile, const char *KEYfile, void *passwd) {
    SSL_CTX *sctx = SSL_CTX_new(client ? SSLv23_client_method() : SSLv23_server_method());
    if (sctx == NULL) {
        throw SSLSocketException ( "Could not create SSL context." );
    }
    else {
        // Generate a new DH key during each handshake
        SSL_CTX_set_options(sctx, SSL_OP_SINGLE_DH_USE);

        // The verification contextof certificates is activated
        if (verify) SSL_CTX_set_verify(sctx, SSL_VERIFY_PEER, NULL);

        // Sets the password of the private key
        SSL_CTX_set_default_passwd_cb_userdata(sctx, passwd);

        if (!client) {
            int s_server_session_id_context = 1;
            SSL_CTX_set_session_id_context(sctx, (const unsigned char*) &s_server_session_id_context, sizeof(s_server_session_id_context));
        }
        
        if (SSL_CTX_load_verify_locations(sctx, CAfile, NULL) == 0) {
            throw CertificateException ( "CA file could not be loaded." );
        }

        if (SSL_CTX_use_certificate_file(sctx, CRTfile, SSL_FILETYPE_PEM) == 0) {
            ERR_print_errors_fp(stderr);
            throw CertificateException ( "CRT file could not be loaded." );
        }

        if (SSL_CTX_use_PrivateKey_file(sctx, KEYfile, SSL_FILETYPE_PEM) == 0) {
            throw CertificateException ( "KEY file could not be loaded." );
        }
    }
    
    return sctx;
}
Esempio n. 2
0
int SSLSocket::checkSSL(int ret)
{
	if (!ssl)
	{
		return -1;
	}
	if (ret <= 0)
	{
		/* inspired by boost.asio (asio/ssl/detail/impl/engine.ipp, function engine::perform) and
		the SSL_get_error doc at <https://www.openssl.org/docs/ssl/SSL_get_error.html>. */
		auto err = SSL_get_error(ssl, ret);
		switch (err)
		{
			case SSL_ERROR_NONE:        // Fallthrough - YaSSL doesn't for example return an openssl compatible error on recv fail
			case SSL_ERROR_WANT_READ:   // Fallthrough
			case SSL_ERROR_WANT_WRITE:
				return -1;
			case SSL_ERROR_ZERO_RETURN:
				throw SocketException(STRING(CONNECTION_CLOSED));
			case SSL_ERROR_SYSCALL:
			{
				auto sys_err = ERR_get_error();
				if (sys_err == 0)
				{
					if (ret == 0)
					{
						dcdebug("TLS error: call ret = %d, SSL_get_error = %d, ERR_get_error = %lu\n", ret, err, sys_err);
						throw SSLSocketException(STRING(CONNECTION_CLOSED));
					}
					sys_err = getLastError();
				}
				throw SSLSocketException(sys_err);
			}
			default:
				//display the cert errors as first choice, if the error is not the certs display the error from the ssl.
				auto sys_err = ERR_get_error();
				string _error;
				int v_err = SSL_get_verify_result(ssl);
				if (v_err == X509_V_ERR_APPLICATION_VERIFICATION)
				{
					_error = "Keyprint mismatch";
				}
				else if (v_err != X509_V_OK)
				{
					_error = X509_verify_cert_error_string(v_err);
				}
				else
				{
					_error = ERR_error_string(sys_err, NULL);
				}
				//dcdebug("TLS error: call ret = %d, SSL_get_error = %d, ERR_get_error = " U64_FMT ",ERROR string: %s \n", ret, err, sys_err, ERR_error_string(sys_err, NULL));
				throw SSLSocketException(STRING(TLS_ERROR) + (_error.empty() ? "" : + ": " + _error));
		}
	}
	return ret;
}
Esempio n. 3
0
int SSLSocket::checkSSL(int ret) {
    if(!ssl) {
        return -1;
    }
    if(ret <= 0) {
        int err = SSL_get_error(ssl, ret);
        switch(err) {
        case SSL_ERROR_NONE:		// Fallthrough - YaSSL doesn't for example return an openssl compatible error on recv fail
        case SSL_ERROR_WANT_READ:	// Fallthrough
        case SSL_ERROR_WANT_WRITE:
            return -1;
        case SSL_ERROR_ZERO_RETURN:
            throw SocketException("Connection closed");
        default:
        {
            ssl.reset();
            // @todo replace 80 with MAX_ERROR_SZ or whatever's appropriate for yaSSL in some nice way...
            int error = ERR_get_error();
            throw SSLSocketException(str(boost::format("SSL Error %1%: %2%") % err % (error == 0 ? CSTRING(CONNECTION_CLOSED) : ERR_reason_error_string(error))));

        }
        }
    }
    return ret;
}
Esempio n. 4
0
// Establish a connection using an SSL layer
int SSLSocket::sslConnect(const char *addr, uint16_t port, int timeout) {
    conn.sslHandle = NULL;
    conn.socket = tcpConnect(addr, port, timeout);
    
    // Create an SSL struct for the connection
    conn.sslHandle = SSL_new(ctx);
    if (conn.sslHandle == NULL) {
        close();
        throw SSLSocketException ( "Could not create SSL object." );
    }
    // Connect the SSL struct to our connection
    if (!SSL_set_fd(conn.sslHandle, conn.socket)) {
        close();
        throw SSLSocketException ( "Could not connect the SSL object to the socket." );
    }
    // Initiate SSL handshake
    if (SSL_connect(conn.sslHandle) != 1) {
        close();
        throw SSLSocketException ( "Error during SSL handshake." );
    }
    return conn.socket;
}
Esempio n. 5
0
int SSLSocket::checkSSL(int ret) {
	if(!ssl) {
		return -1;
	}
	if(ret <= 0) {
		/* inspired by boost.asio (asio/ssl/detail/impl/engine.ipp, function engine::perform) and
		the SSL_get_error doc at <https://www.openssl.org/docs/ssl/SSL_get_error.html>. */
		auto err = SSL_get_error(ssl, ret);
		switch(err) {
		case SSL_ERROR_NONE:		// Fallthrough - YaSSL doesn't for example return an openssl compatible error on recv fail
		case SSL_ERROR_WANT_READ:	// Fallthrough
		case SSL_ERROR_WANT_WRITE:
			return -1;
		case SSL_ERROR_ZERO_RETURN:
			throw SocketException(STRING(CONNECTION_CLOSED));
		case SSL_ERROR_SYSCALL:
			{
				auto sys_err = ERR_get_error();
				if(sys_err == 0) {
					if(ret == 0) {
						dcdebug("TLS error: call ret = %d, SSL_get_error = %d, ERR_get_error = %d\n", ret, err, sys_err);
						throw SSLSocketException(STRING(CONNECTION_CLOSED));
					}
					sys_err = getLastError();
				}
				throw SSLSocketException(sys_err);
			}
		default:
			/* don't bother getting error messages from the codes because 1) there is some
			additional management necessary (eg SSL_load_error_strings) and 2) openssl error codes
			aren't shown to the end user; they only hit standard output in debug builds. */
			dcdebug("TLS error: call ret = %d, SSL_get_error = %d, ERR_get_error = %d\n", ret, err, ERR_get_error());
			throw SSLSocketException(STRING(TLS_ERROR));
		}
	}
	return ret;
}
bool SSLSocket::waitWant(int ret, uint64_t millis) {
#ifdef HEADER_OPENSSLV_H
	int err = SSL_get_error(ssl, ret);
	switch(err) {
	case SSL_ERROR_WANT_READ:
		return wait(millis, Socket::WAIT_READ) == WAIT_READ;
	case SSL_ERROR_WANT_WRITE:
		return wait(millis, Socket::WAIT_WRITE) == WAIT_WRITE;
#else
	int err = ssl->last_error;
	switch(err) {
	case GNUTLS_E_INTERRUPTED:
	case GNUTLS_E_AGAIN: 
	{
		int waitFor = wait(millis, Socket::WAIT_READ | Socket::WAIT_WRITE);
		return (waitFor & Socket::WAIT_READ) || (waitFor & Socket::WAIT_WRITE);
	}
#endif
	// Check if this is a fatal error...
	default: checkSSL(ret);
	}
	dcdebug("SSL: Unexpected fallthrough");
	// There was no error?
	return true;
}

int SSLSocket::read(void* aBuffer, int aBufLen) throw(SocketException) {
	if(!ssl) {
		return -1;
	}
	int len = checkSSL(SSL_read(ssl, aBuffer, aBufLen));

	if(len > 0) {
		stats.totalDown += len;
		//dcdebug("In(s): %.*s\n", len, (char*)aBuffer);
	}
	return len;
}

int SSLSocket::write(const void* aBuffer, int aLen) throw(SocketException) {
	if(!ssl) {
		return -1;
	}
	int ret = checkSSL(SSL_write(ssl, aBuffer, aLen));
	if(ret > 0) {
		stats.totalUp += ret;
		//dcdebug("Out(s): %.*s\n", ret, (char*)aBuffer);
	}
	return ret;
}

int SSLSocket::checkSSL(int ret) throw(SocketException) {
	if(!ssl) {
		return -1;
	}
	if(ret <= 0) {
		int err = SSL_get_error(ssl, ret);
		switch(err) {
			case SSL_ERROR_NONE:		// Fallthrough - YaSSL doesn't for example return an openssl compatible error on recv fail
			case SSL_ERROR_WANT_READ:	// Fallthrough
			case SSL_ERROR_WANT_WRITE:
				return -1;
			case SSL_ERROR_ZERO_RETURN:
#ifndef HEADER_OPENSSLV_H
				if(ssl->last_error == GNUTLS_E_INTERRUPTED || ssl->last_error == GNUTLS_E_AGAIN)
					return -1;
#endif				
				throw SocketException(STRING(CONNECTION_CLOSED));
			default:
				{
					ssl.reset();
					// @todo replace 80 with MAX_ERROR_SZ or whatever's appropriate for yaSSL in some nice way...
					char errbuf[80];

					/* TODO: better message for SSL_ERROR_SYSCALL
					 * If the error queue is empty (i.e. ERR_get_error() returns 0), ret can be used to find out more about the error: 
					 * If ret == 0, an EOF was observed that violates the protocol. If ret == -1, the underlying BIO reported an I/O error 
					 * (for socket I/O on Unix systems, consult errno for details).
					 */
					int error = ERR_get_error();
					sprintf(errbuf, "%s %d: %s", CSTRING(SSL_ERROR), err, (error == 0) ? CSTRING(CONNECTION_CLOSED) : ERR_reason_error_string(error));
					throw SSLSocketException(errbuf);
				}
		}
	}
	return ret;
}

int SSLSocket::wait(uint64_t millis, int waitFor) throw(SocketException) {
#ifdef HEADER_OPENSSLV_H
	if(ssl && (waitFor & Socket::WAIT_READ)) {
		/** @todo Take writing into account as well if reading is possible? */
		char c;
		if(SSL_peek(ssl, &c, 1) > 0)
			return WAIT_READ;
	}
#endif
	return Socket::wait(millis, waitFor);
}

bool SSLSocket::isTrusted() throw() {
	if(!ssl) {
		return false;
	}

#ifdef HEADER_OPENSSLV_H
	if(SSL_get_verify_result(ssl) != X509_V_OK) {
		return false;
	}
#else
	if(gnutls_certificate_verify_peers(((SSL*)ssl)->gnutls_state) != 0) {
		return false;
	}
#endif

	X509* cert = SSL_get_peer_certificate(ssl);
	if(!cert) {
		return false;
	}

	X509_free(cert);

	return true;
}

std::string SSLSocket::getCipherName() throw() {
	if(!ssl)
		return Util::emptyString;
	
	return SSL_get_cipher_name(ssl);
}

std::string SSLSocket::getDigest() const throw() {
#ifdef HEADER_OPENSSLV_H

	if(!ssl)
		return Util::emptyString;
	X509* x509 = SSL_get_peer_certificate(ssl);
	if(!x509)
		return Util::emptyString;
	
	return ssl::X509_digest(x509, EVP_sha1());
#else
	return Util::emptyString;
#endif
}

void SSLSocket::shutdown() throw() {
	if(ssl)
		SSL_shutdown(ssl);
}

void SSLSocket::close() throw() {
	if(ssl) {
		ssl.reset();
	}
	Socket::shutdown();
	Socket::close();
}

} // namespace dcpp
Esempio n. 7
0
int SSLSocket::read(void *buf, int num) const {
    int status = SSL_read(conn.sslHandle, buf, num);
    if (status < 0) throw SSLSocketException ( "Could not read from socket." );
    return status;
}
Esempio n. 8
0
int SSLSocket::write(const void *buf, int num) const {
    int status = SSL_write(conn.sslHandle, buf, num);
    if (status <= 0) throw SSLSocketException( "Could not write byte" );
    return status;
}