remove dependency on GnuTLS for later replacement with OpenSSL

This commit is contained in:
Stephen Birarda 2014-05-20 10:47:11 -07:00 committed by wangyix
parent 9dde480bb2
commit f1581ef823
18 changed files with 47 additions and 718 deletions

View file

@ -39,8 +39,6 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) :
QCoreApplication(argc, argv),
_assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME)
{
DTLSClientSession::globalInit();
setOrganizationName("High Fidelity");
setOrganizationDomain("highfidelity.io");
setApplicationName("assignment-client");
@ -106,10 +104,6 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) :
this, &AssignmentClient::handleAuthenticationRequest);
}
AssignmentClient::~AssignmentClient() {
DTLSClientSession::globalDeinit();
}
void AssignmentClient::sendAssignmentRequest() {
if (!_currentAssignment) {
NodeList::getInstance()->sendAssignment(_requestAssignment);

View file

@ -21,7 +21,6 @@ class AssignmentClient : public QCoreApplication {
public:
AssignmentClient(int &argc, char **argv);
static const SharedAssignmentPointer& getCurrentAssignment() { return _currentAssignment; }
~AssignmentClient();
private slots:
void sendAssignmentRequest();
void readPendingDatagrams();

View file

@ -1,18 +0,0 @@
//
// DTLSServerSession.cpp
// domain-server/src
//
// Created by Stephen Birarda on 2014-04-01.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "DTLSServerSession.h"
DTLSServerSession::DTLSServerSession(QUdpSocket& dtlsSocket, HifiSockAddr& destinationSocket) :
DTLSSession(GNUTLS_SERVER, dtlsSocket, destinationSocket)
{
}

View file

@ -1,24 +0,0 @@
//
// DTLSServerSession.h
// domain-server/src
//
// Created by Stephen Birarda on 2014-04-01.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_DTLSServerSession_h
#define hifi_DTLSServerSession_h
#include <gnutls/dtls.h>
#include <DTLSSession.h>
class DTLSServerSession : public DTLSSession {
public:
DTLSServerSession(QUdpSocket& dtlsSocket, HifiSockAddr& destinationSocket);
};
#endif // hifi_DTLSServerSession_h

View file

@ -28,7 +28,6 @@
#include <UUID.h>
#include "DomainServerNodeData.h"
#include "DummyDTLSSession.h"
#include "DomainServer.h"
@ -39,18 +38,12 @@ DomainServer::DomainServer(int argc, char* argv[]) :
_allAssignments(),
_unfulfilledAssignments(),
_isUsingDTLS(false),
_x509Credentials(NULL),
_dhParams(NULL),
_priorityCache(NULL),
_dtlsSessions(),
_oauthProviderURL(),
_oauthClientID(),
_hostname(),
_networkReplyUUIDMap(),
_sessionAuthenticationHash()
{
gnutls_global_init();
setOrganizationName("High Fidelity");
setOrganizationDomain("highfidelity.io");
setApplicationName("domain-server");
@ -64,31 +57,10 @@ DomainServer::DomainServer(int argc, char* argv[]) :
qDebug() << "Setting up LimitedNodeList and assignments.";
setupNodeListAndAssignments();
if (_isUsingDTLS) {
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
// connect our socket to read datagrams received on the DTLS socket
connect(&nodeList->getDTLSSocket(), &QUdpSocket::readyRead, this, &DomainServer::readAvailableDTLSDatagrams);
}
_networkAccessManager = new QNetworkAccessManager(this);
}
}
DomainServer::~DomainServer() {
if (_x509Credentials) {
gnutls_certificate_free_credentials(*_x509Credentials);
gnutls_priority_deinit(*_priorityCache);
gnutls_dh_params_deinit(*_dhParams);
delete _x509Credentials;
delete _priorityCache;
delete _dhParams;
delete _cookieKey;
}
gnutls_global_deinit();
}
bool DomainServer::optionallyReadX509KeyAndCertificate() {
const QString X509_CERTIFICATE_OPTION = "cert";
const QString X509_PRIVATE_KEY_OPTION = "key";
@ -100,28 +72,28 @@ bool DomainServer::optionallyReadX509KeyAndCertificate() {
if (!certPath.isEmpty() && !keyPath.isEmpty()) {
// the user wants to use DTLS to encrypt communication with nodes
// let's make sure we can load the key and certificate
_x509Credentials = new gnutls_certificate_credentials_t;
gnutls_certificate_allocate_credentials(_x509Credentials);
// _x509Credentials = new gnutls_certificate_credentials_t;
// gnutls_certificate_allocate_credentials(_x509Credentials);
QString keyPassphraseString = QProcessEnvironment::systemEnvironment().value(X509_KEY_PASSPHRASE_ENV);
qDebug() << "Reading certificate file at" << certPath << "for DTLS.";
qDebug() << "Reading key file at" << keyPath << "for DTLS.";
int gnutlsReturn = gnutls_certificate_set_x509_key_file2(*_x509Credentials,
certPath.toLocal8Bit().constData(),
keyPath.toLocal8Bit().constData(),
GNUTLS_X509_FMT_PEM,
keyPassphraseString.toLocal8Bit().constData(),
0);
// int gnutlsReturn = gnutls_certificate_set_x509_key_file2(*_x509Credentials,
// certPath.toLocal8Bit().constData(),
// keyPath.toLocal8Bit().constData(),
// GNUTLS_X509_FMT_PEM,
// keyPassphraseString.toLocal8Bit().constData(),
// 0);
//
// if (gnutlsReturn < 0) {
// qDebug() << "Unable to load certificate or key file." << "Error" << gnutlsReturn << "- domain-server will now quit.";
// QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
// return false;
// }
if (gnutlsReturn < 0) {
qDebug() << "Unable to load certificate or key file." << "Error" << gnutlsReturn << "- domain-server will now quit.";
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
return false;
}
qDebug() << "Successfully read certificate and private key.";
// qDebug() << "Successfully read certificate and private key.";
// we need to also pass this certificate and private key to the HTTPS manager
// this is used for Oauth callbacks when authorizing users against a data server
@ -176,39 +148,6 @@ bool DomainServer::optionallySetupOAuth() {
return true;
}
bool DomainServer::optionallySetupDTLS() {
if (_x509Credentials) {
qDebug() << "Generating Diffie-Hellman parameters.";
// generate Diffie-Hellman parameters
// When short bit length is used, it might be wise to regenerate parameters often.
int dhBits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_LEGACY);
_dhParams = new gnutls_dh_params_t;
gnutls_dh_params_init(_dhParams);
gnutls_dh_params_generate2(*_dhParams, dhBits);
qDebug() << "Successfully generated Diffie-Hellman parameters.";
// set the D-H paramters on the X509 credentials
gnutls_certificate_set_dh_params(*_x509Credentials, *_dhParams);
// setup the key used for cookie verification
_cookieKey = new gnutls_datum_t;
gnutls_key_generate(_cookieKey, GNUTLS_COOKIE_KEY_SIZE);
_priorityCache = new gnutls_priority_t;
const char DTLS_PRIORITY_STRING[] = "PERFORMANCE:-VERS-TLS-ALL:+VERS-DTLS1.2:%SERVER_PRECEDENCE";
gnutls_priority_init(_priorityCache, DTLS_PRIORITY_STRING, NULL);
_isUsingDTLS = true;
qDebug() << "Initial DTLS setup complete.";
}
return true;
}
void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
const QString CUSTOM_PORT_OPTION = "port";
@ -552,8 +491,8 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
if (nodeInterestList.size() > 0) {
DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL;
int dataMTU = dtlsSession ? (int)gnutls_dtls_get_data_mtu(*dtlsSession->getGnuTLSSession()) : MAX_PACKET_SIZE;
// DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL;
int dataMTU = MAX_PACKET_SIZE;
if (nodeData->isAuthenticated()) {
// if this authenticated node has any interest types, send back those nodes as well
@ -589,11 +528,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
// we need to break here and start a new packet
// so send the current one
if (!dtlsSession) {
nodeList->writeDatagram(broadcastPacket, node, senderSockAddr);
} else {
dtlsSession->writeDatagram(broadcastPacket);
}
nodeList->writeDatagram(broadcastPacket, node, senderSockAddr);
// reset the broadcastPacket structure
broadcastPacket.resize(numBroadcastPacketLeadBytes);
@ -607,11 +542,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
}
// always write the last broadcastPacket
if (!dtlsSession) {
nodeList->writeDatagram(broadcastPacket, node, senderSockAddr);
} else {
dtlsSession->writeDatagram(broadcastPacket);
}
nodeList->writeDatagram(broadcastPacket, node, senderSockAddr);
}
}
@ -689,86 +620,6 @@ void DomainServer::readAvailableDatagrams() {
}
}
void DomainServer::readAvailableDTLSDatagrams() {
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
QUdpSocket& dtlsSocket = nodeList->getDTLSSocket();
static sockaddr senderSockAddr;
static socklen_t sockAddrSize = sizeof(senderSockAddr);
while (dtlsSocket.hasPendingDatagrams()) {
// check if we have an active DTLS session for this sender
QByteArray peekDatagram(dtlsSocket.pendingDatagramSize(), 0);
recvfrom(dtlsSocket.socketDescriptor(), peekDatagram.data(), dtlsSocket.pendingDatagramSize(),
MSG_PEEK, &senderSockAddr, &sockAddrSize);
HifiSockAddr senderHifiSockAddr(&senderSockAddr);
DTLSServerSession* existingSession = _dtlsSessions.value(senderHifiSockAddr);
if (existingSession) {
if (!existingSession->completedHandshake()) {
// check if we have completed handshake with this user
int handshakeReturn = gnutls_handshake(*existingSession->getGnuTLSSession());
if (handshakeReturn == 0) {
existingSession->setCompletedHandshake(true);
} else if (gnutls_error_is_fatal(handshakeReturn)) {
// this was a fatal error handshaking, so remove this session
qDebug() << "Fatal error -" << gnutls_strerror(handshakeReturn) << "- during DTLS handshake with"
<< senderHifiSockAddr;
_dtlsSessions.remove(senderHifiSockAddr);
}
} else {
// pull the data from this user off the stack and process it
int receivedBytes = gnutls_record_recv(*existingSession->getGnuTLSSession(),
peekDatagram.data(), peekDatagram.size());
if (receivedBytes > 0) {
processDatagram(peekDatagram.left(receivedBytes), senderHifiSockAddr);
} else if (gnutls_error_is_fatal(receivedBytes)) {
qDebug() << "Fatal error -" << gnutls_strerror(receivedBytes) << "- during DTLS handshake with"
<< senderHifiSockAddr;
}
}
} else {
// first we verify the cookie
// see http://gnutls.org/manual/html_node/DTLS-sessions.html for why this is required
gnutls_dtls_prestate_st prestate;
memset(&prestate, 0, sizeof(prestate));
int cookieValid = gnutls_dtls_cookie_verify(_cookieKey, &senderSockAddr, sizeof(senderSockAddr),
peekDatagram.data(), peekDatagram.size(), &prestate);
if (cookieValid < 0) {
// the cookie sent by the client was not valid
// send a valid one
DummyDTLSSession tempServerSession(LimitedNodeList::getInstance()->getDTLSSocket(), senderHifiSockAddr);
gnutls_dtls_cookie_send(_cookieKey, &senderSockAddr, sizeof(senderSockAddr), &prestate,
&tempServerSession, DTLSSession::socketPush);
// acutally pull the peeked data off the network stack so that it gets discarded
dtlsSocket.readDatagram(peekDatagram.data(), peekDatagram.size());
} else {
// cookie valid but no existing session - set up a new session now
DTLSServerSession* newServerSession = new DTLSServerSession(LimitedNodeList::getInstance()->getDTLSSocket(),
senderHifiSockAddr);
gnutls_session_t* gnutlsSession = newServerSession->getGnuTLSSession();
gnutls_priority_set(*gnutlsSession, *_priorityCache);
gnutls_credentials_set(*gnutlsSession, GNUTLS_CRD_CERTIFICATE, *_x509Credentials);
gnutls_dtls_prestate_set(*gnutlsSession, &prestate);
// handshake to begin the session
gnutls_handshake(*gnutlsSession);
qDebug() << "Beginning DTLS session with node at" << senderHifiSockAddr;
_dtlsSessions[senderHifiSockAddr] = newServerSession;
}
}
}
}
void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) {
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
@ -1210,14 +1061,6 @@ void DomainServer::nodeKilled(SharedNodePointer node) {
reinterpret_cast<DomainServerNodeData*>(otherNode->getLinkedData())->getSessionSecretHash().remove(node->getUUID());
}
}
if (_isUsingDTLS) {
// check if we need to remove a DTLS session from our in-memory hash
DTLSServerSession* existingSession = _dtlsSessions.take(nodeData->getSendingSockAddr());
if (existingSession) {
delete existingSession;
}
}
}
}

View file

@ -26,15 +26,12 @@
#include <HTTPSConnection.h>
#include <LimitedNodeList.h>
#include "DTLSServerSession.h"
typedef QSharedPointer<Assignment> SharedAssignmentPointer;
class DomainServer : public QCoreApplication, public HTTPSRequestHandler {
Q_OBJECT
public:
DomainServer(int argc, char* argv[]);
~DomainServer();
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url);
bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url);
@ -50,11 +47,9 @@ public slots:
private slots:
void readAvailableDatagrams();
void readAvailableDTLSDatagrams();
private:
void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
bool optionallySetupOAuth();
bool optionallySetupDTLS();
bool optionallyReadX509KeyAndCertificate();
void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
@ -96,12 +91,6 @@ private:
QVariantMap _argumentVariantMap;
bool _isUsingDTLS;
gnutls_certificate_credentials_t* _x509Credentials;
gnutls_dh_params_t* _dhParams;
gnutls_datum_t* _cookieKey;
gnutls_priority_t* _priorityCache;
QHash<HifiSockAddr, DTLSServerSession*> _dtlsSessions;
QNetworkAccessManager* _networkAccessManager;

View file

@ -173,10 +173,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_previousScriptLocation(),
_runningScriptsWidget(new RunningScriptsWidget(_window)),
_runningScriptsWidgetWasVisible(false)
{
// init GnuTLS for DTLS with domain-servers
DTLSClientSession::globalInit();
{
// read the ApplicationInfo.ini file for Name/Version/Domain information
QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat);
@ -431,8 +428,6 @@ Application::~Application() {
delete _glWidget;
AccountManager::getInstance().destroy();
DTLSClientSession::globalDeinit();
}
void Application::saveSettings() {

View file

@ -28,12 +28,35 @@ OAuthWebViewHandler::OAuthWebViewHandler() :
}
const char HIGH_FIDELITY_CA[] = "-----BEGIN CERTIFICATE-----\n"
"MIID6TCCA1KgAwIBAgIJANlfRkRD9A8bMA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD\n"
"VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5j\n"
"aXNjbzEbMBkGA1UEChMSSGlnaCBGaWRlbGl0eSwgSW5jMRMwEQYDVQQLEwpPcGVy\n"
"YXRpb25zMRgwFgYDVQQDEw9oaWdoZmlkZWxpdHkuaW8xIjAgBgkqhkiG9w0BCQEW\n"
"E29wc0BoaWdoZmlkZWxpdHkuaW8wHhcNMTQwMzI4MjIzMzM1WhcNMjQwMzI1MjIz\n"
"MzM1WjCBqjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNV\n"
"BAcTDVNhbiBGcmFuY2lzY28xGzAZBgNVBAoTEkhpZ2ggRmlkZWxpdHksIEluYzET\n"
"MBEGA1UECxMKT3BlcmF0aW9uczEYMBYGA1UEAxMPaGlnaGZpZGVsaXR5LmlvMSIw\n"
"IAYJKoZIhvcNAQkBFhNvcHNAaGlnaGZpZGVsaXR5LmlvMIGfMA0GCSqGSIb3DQEB\n"
"AQUAA4GNADCBiQKBgQDyo1euYiPPEdnvDZnIjWrrP230qUKMSj8SWoIkbTJF2hE8\n"
"2eP3YOgbgSGBzZ8EJBxIOuNmj9g9Eg6691hIKFqy5W0BXO38P04Gg+pVBvpHFGBi\n"
"wpqGbfsjaUDuYmBeJRcMO0XYkLCRQG+lAQNHoFDdItWAJfC3FwtP3OCDnz8cNwID\n"
"AQABo4IBEzCCAQ8wHQYDVR0OBBYEFCSv2kmiGg6VFMnxXzLDNP304cPAMIHfBgNV\n"
"HSMEgdcwgdSAFCSv2kmiGg6VFMnxXzLDNP304cPAoYGwpIGtMIGqMQswCQYDVQQG\n"
"EwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj\n"
"bzEbMBkGA1UEChMSSGlnaCBGaWRlbGl0eSwgSW5jMRMwEQYDVQQLEwpPcGVyYXRp\n"
"b25zMRgwFgYDVQQDEw9oaWdoZmlkZWxpdHkuaW8xIjAgBgkqhkiG9w0BCQEWE29w\n"
"c0BoaWdoZmlkZWxpdHkuaW+CCQDZX0ZEQ/QPGzAMBgNVHRMEBTADAQH/MA0GCSqG\n"
"SIb3DQEBBQUAA4GBAEkQl3p+lH5vuoCNgyfa67nL0MsBEt+5RSBOgjwCjjASjzou\n"
"FTv5w0he2OypgMQb8i/BYtS1lJSFqjPJcSM1Salzrm3xDOK5pOXJ7h6SQLPDVEyf\n"
"Hy2/9d/to+99+SOUlvfzfgycgjOc+s/AV7Y+GBd7uzGxUdrN4egCZW1F6/mH\n"
"-----END CERTIFICATE-----\n";
void OAuthWebViewHandler::addHighFidelityRootCAToSSLConfig() {
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
// add the High Fidelity root CA to the list of trusted CA certificates
QByteArray highFidelityCACertificate(reinterpret_cast<char*>(DTLSSession::highFidelityCADatum()->data),
DTLSSession::highFidelityCADatum()->size);
QByteArray highFidelityCACertificate(HIGH_FIDELITY_CA, sizeof(HIGH_FIDELITY_CA));
sslConfig.setCaCertificates(sslConfig.caCertificates() + QSslCertificate::fromData(highFidelityCACertificate));
// set the modified configuration

View file

@ -1,83 +0,0 @@
//
// DTLSClientSession.cpp
// libraries/networking/src
//
// Created by Stephen Birarda on 2014-04-01.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "DomainHandler.h"
#include "DTLSClientSession.h"
gnutls_certificate_credentials_t DTLSClientSession::_x509CACredentials;
void DTLSClientSession::globalInit() {
static bool initialized = false;
if (!initialized) {
gnutls_global_init();
gnutls_certificate_allocate_credentials(&_x509CACredentials);
gnutls_certificate_set_x509_trust_mem(_x509CACredentials, DTLSSession::highFidelityCADatum(), GNUTLS_X509_FMT_PEM);
gnutls_certificate_set_verify_function(_x509CACredentials, DTLSClientSession::verifyServerCertificate);
}
}
void DTLSClientSession::globalDeinit() {
gnutls_certificate_free_credentials(_x509CACredentials);
gnutls_global_deinit();
}
int DTLSClientSession::verifyServerCertificate(gnutls_session_t session) {
unsigned int verifyStatus = 0;
// grab the hostname from the domain handler that this session is associated with
DomainHandler* domainHandler = reinterpret_cast<DomainHandler*>(gnutls_session_get_ptr(session));
qDebug() << "Checking for" << domainHandler->getHostname() << "from cert.";
int certReturn = gnutls_certificate_verify_peers3(session,
domainHandler->getHostname().toLocal8Bit().constData(),
&verifyStatus);
if (certReturn < 0) {
return GNUTLS_E_CERTIFICATE_ERROR;
}
gnutls_certificate_type_t typeReturn = gnutls_certificate_type_get(session);
gnutls_datum_t printOut;
certReturn = gnutls_certificate_verification_status_print(verifyStatus, typeReturn, &printOut, 0);
if (certReturn < 0) {
return GNUTLS_E_CERTIFICATE_ERROR;
}
qDebug() << "Gnutls certificate verification status:" << reinterpret_cast<char *>(printOut.data);
#ifdef WIN32
free(printOut.data);
#else
gnutls_free(printOut.data);
#endif
if (verifyStatus != 0) {
qDebug() << "Server provided certificate for DTLS is not trusted. Can not complete handshake.";
return GNUTLS_E_CERTIFICATE_ERROR;
} else {
// certificate is valid, continue handshaking as normal
return 0;
}
}
DTLSClientSession::DTLSClientSession(QUdpSocket& dtlsSocket, HifiSockAddr& destinationSocket) :
DTLSSession(GNUTLS_CLIENT, dtlsSocket, destinationSocket)
{
gnutls_priority_set_direct(_gnutlsSession, "PERFORMANCE", NULL);
gnutls_credentials_set(_gnutlsSession, GNUTLS_CRD_CERTIFICATE, _x509CACredentials);
}

View file

@ -1,30 +0,0 @@
//
// DTLSClientSession.h
// libraries/networking/src
//
// Created by Stephen Birarda on 2014-04-01.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_DTLSClientSession_h
#define hifi_DTLSClientSession_h
#include "DTLSSession.h"
class DTLSClientSession : public DTLSSession {
public:
DTLSClientSession(QUdpSocket& dtlsSocket, HifiSockAddr& destinationSocket);
static void globalInit();
static void globalDeinit();
static int verifyServerCertificate(gnutls_session_t session);
static gnutls_certificate_credentials_t _x509CACredentials;
static bool _wasGloballyInitialized;
};
#endif // hifi_DTLSClientSession_h

View file

@ -1,145 +0,0 @@
//
// DTLSSession.cpp
// libraries/networking/src
//
// Created by Stephen Birarda on 2014-04-01.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <gnutls/dtls.h>
#include "NodeList.h"
#include "DTLSSession.h"
int DTLSSession::socketPullTimeout(gnutls_transport_ptr_t ptr, unsigned int ms) {
DTLSSession* session = static_cast<DTLSSession*>(ptr);
QUdpSocket& dtlsSocket = session->_dtlsSocket;
if (dtlsSocket.hasPendingDatagrams()) {
// peek the data on stack to see if it belongs to this session
static sockaddr senderSockAddr;
static socklen_t sockAddrSize = sizeof(senderSockAddr);
QByteArray peekDatagram(dtlsSocket.pendingDatagramSize(), 0);
recvfrom(dtlsSocket.socketDescriptor(), peekDatagram.data(), dtlsSocket.pendingDatagramSize(),
MSG_PEEK, &senderSockAddr, &sockAddrSize);
if (HifiSockAddr(&senderSockAddr) == session->_destinationSocket) {
// there is data for this session ready to be read
return 1;
} else {
// the next data from the dtlsSocket is not for this session
return 0;
}
} else {
// no data available on the dtlsSocket
return 0;
}
}
ssize_t DTLSSession::socketPull(gnutls_transport_ptr_t ptr, void* buffer, size_t size) {
DTLSSession* session = static_cast<DTLSSession*>(ptr);
QUdpSocket& dtlsSocket = session->_dtlsSocket;
HifiSockAddr pulledSockAddr;
qint64 bytesReceived = dtlsSocket.readDatagram(reinterpret_cast<char*>(buffer), size,
pulledSockAddr.getAddressPointer(), pulledSockAddr.getPortPointer());
if (bytesReceived == -1) {
// no data to pull, return -1
#if DTLS_VERBOSE_DEBUG
qDebug() << "Received no data on call to readDatagram";
#endif
return bytesReceived;
}
if (pulledSockAddr == session->_destinationSocket) {
// bytes received from the correct sender, return number of bytes received
#if DTLS_VERBOSE_DEBUG
qDebug() << "Received" << bytesReceived << "on DTLS socket from" << pulledSockAddr;
#endif
return bytesReceived;
}
// we pulled a packet not matching this session, so output that
qDebug() << "Denied connection from" << pulledSockAddr;
gnutls_transport_set_errno(session->_gnutlsSession, GNUTLS_E_AGAIN);
return -1;
}
gnutls_datum_t* DTLSSession::highFidelityCADatum() {
static gnutls_datum_t hifiCADatum;
static bool datumInitialized = false;
static unsigned char HIGHFIDELITY_ROOT_CA_CERT[] =
"-----BEGIN CERTIFICATE-----\n"
"MIID6TCCA1KgAwIBAgIJANlfRkRD9A8bMA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD\n"
"VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5j\n"
"aXNjbzEbMBkGA1UEChMSSGlnaCBGaWRlbGl0eSwgSW5jMRMwEQYDVQQLEwpPcGVy\n"
"YXRpb25zMRgwFgYDVQQDEw9oaWdoZmlkZWxpdHkuaW8xIjAgBgkqhkiG9w0BCQEW\n"
"E29wc0BoaWdoZmlkZWxpdHkuaW8wHhcNMTQwMzI4MjIzMzM1WhcNMjQwMzI1MjIz\n"
"MzM1WjCBqjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNV\n"
"BAcTDVNhbiBGcmFuY2lzY28xGzAZBgNVBAoTEkhpZ2ggRmlkZWxpdHksIEluYzET\n"
"MBEGA1UECxMKT3BlcmF0aW9uczEYMBYGA1UEAxMPaGlnaGZpZGVsaXR5LmlvMSIw\n"
"IAYJKoZIhvcNAQkBFhNvcHNAaGlnaGZpZGVsaXR5LmlvMIGfMA0GCSqGSIb3DQEB\n"
"AQUAA4GNADCBiQKBgQDyo1euYiPPEdnvDZnIjWrrP230qUKMSj8SWoIkbTJF2hE8\n"
"2eP3YOgbgSGBzZ8EJBxIOuNmj9g9Eg6691hIKFqy5W0BXO38P04Gg+pVBvpHFGBi\n"
"wpqGbfsjaUDuYmBeJRcMO0XYkLCRQG+lAQNHoFDdItWAJfC3FwtP3OCDnz8cNwID\n"
"AQABo4IBEzCCAQ8wHQYDVR0OBBYEFCSv2kmiGg6VFMnxXzLDNP304cPAMIHfBgNV\n"
"HSMEgdcwgdSAFCSv2kmiGg6VFMnxXzLDNP304cPAoYGwpIGtMIGqMQswCQYDVQQG\n"
"EwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj\n"
"bzEbMBkGA1UEChMSSGlnaCBGaWRlbGl0eSwgSW5jMRMwEQYDVQQLEwpPcGVyYXRp\n"
"b25zMRgwFgYDVQQDEw9oaWdoZmlkZWxpdHkuaW8xIjAgBgkqhkiG9w0BCQEWE29w\n"
"c0BoaWdoZmlkZWxpdHkuaW+CCQDZX0ZEQ/QPGzAMBgNVHRMEBTADAQH/MA0GCSqG\n"
"SIb3DQEBBQUAA4GBAEkQl3p+lH5vuoCNgyfa67nL0MsBEt+5RSBOgjwCjjASjzou\n"
"FTv5w0he2OypgMQb8i/BYtS1lJSFqjPJcSM1Salzrm3xDOK5pOXJ7h6SQLPDVEyf\n"
"Hy2/9d/to+99+SOUlvfzfgycgjOc+s/AV7Y+GBd7uzGxUdrN4egCZW1F6/mH\n"
"-----END CERTIFICATE-----\n";
if (!datumInitialized) {
hifiCADatum.data = HIGHFIDELITY_ROOT_CA_CERT;
hifiCADatum.size = sizeof(HIGHFIDELITY_ROOT_CA_CERT);
}
return &hifiCADatum;
}
DTLSSession::DTLSSession(int end, QUdpSocket& dtlsSocket, HifiSockAddr& destinationSocket) :
DummyDTLSSession(dtlsSocket, destinationSocket),
_completedHandshake(false)
{
gnutls_init(&_gnutlsSession, end | GNUTLS_DATAGRAM | GNUTLS_NONBLOCK);
// see http://gnutls.org/manual/html_node/Datagram-TLS-API.html#gnutls_005fdtls_005fset_005fmtu
const unsigned int DTLS_MAX_MTU = 1452;
gnutls_dtls_set_mtu(_gnutlsSession, DTLS_MAX_MTU);
const unsigned int DTLS_HANDSHAKE_RETRANSMISSION_TIMEOUT = DOMAIN_SERVER_CHECK_IN_MSECS;
const unsigned int DTLS_TOTAL_CONNECTION_TIMEOUT = 2 * NODE_SILENCE_THRESHOLD_MSECS;
gnutls_dtls_set_timeouts(_gnutlsSession, DTLS_HANDSHAKE_RETRANSMISSION_TIMEOUT, DTLS_TOTAL_CONNECTION_TIMEOUT);
gnutls_transport_set_ptr(_gnutlsSession, this);
gnutls_transport_set_push_function(_gnutlsSession, DummyDTLSSession::socketPush);
gnutls_transport_set_pull_function(_gnutlsSession, socketPull);
gnutls_transport_set_pull_timeout_function(_gnutlsSession, socketPullTimeout);
}
DTLSSession::~DTLSSession() {
gnutls_deinit(_gnutlsSession);
}
void DTLSSession::setCompletedHandshake(bool completedHandshake) {
_completedHandshake = completedHandshake;
qDebug() << "Completed DTLS handshake with" << _destinationSocket;
}
qint64 DTLSSession::writeDatagram(const QByteArray& datagram) {
// we don't need to put a hash in this packet, so just send it off
return gnutls_record_send(_gnutlsSession, datagram.data(), datagram.size());
}

View file

@ -1,44 +0,0 @@
//
// DTLSSession.h
// libraries/networking/src
//
// Created by Stephen Birarda on 2014-04-01.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_DTLSSession_h
#define hifi_DTLSSession_h
#include <QtNetwork/QUdpSocket>
#include <gnutls/gnutls.h>
#include "DummyDTLSSession.h"
#include "HifiSockAddr.h"
class DTLSSession : public DummyDTLSSession {
Q_OBJECT
public:
DTLSSession(int end, QUdpSocket& dtlsSocket, HifiSockAddr& destinationSocket);
~DTLSSession();
static int socketPullTimeout(gnutls_transport_ptr_t ptr, unsigned int ms);
static ssize_t socketPull(gnutls_transport_ptr_t ptr, void* buffer, size_t size);
static gnutls_datum_t* highFidelityCADatum();
qint64 writeDatagram(const QByteArray& datagram);
gnutls_session_t* getGnuTLSSession() { return &_gnutlsSession; }
bool completedHandshake() const { return _completedHandshake; }
void setCompletedHandshake(bool completedHandshake);
protected:
gnutls_session_t _gnutlsSession;
bool _completedHandshake;
};
#endif // hifi_DTLSSession_h

View file

@ -22,16 +22,11 @@ DomainHandler::DomainHandler(QObject* parent) :
_sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)),
_assignmentUUID(),
_isConnected(false),
_dtlsSession(NULL),
_handshakeTimer(NULL)
{
}
DomainHandler::~DomainHandler() {
delete _dtlsSession;
}
void DomainHandler::clearConnectionInfo() {
_uuid = QUuid();
_isConnected = false;
@ -41,38 +36,12 @@ void DomainHandler::clearConnectionInfo() {
delete _handshakeTimer;
_handshakeTimer = NULL;
}
delete _dtlsSession;
_dtlsSession = NULL;
}
void DomainHandler::reset() {
clearConnectionInfo();
_hostname = QString();
_sockAddr.setAddress(QHostAddress::Null);
delete _dtlsSession;
_dtlsSession = NULL;
}
const unsigned int DTLS_HANDSHAKE_INTERVAL_MSECS = 100;
void DomainHandler::initializeDTLSSession() {
if (!_dtlsSession) {
_dtlsSession = new DTLSClientSession(NodeList::getInstance()->getDTLSSocket(), _sockAddr);
gnutls_session_set_ptr(*_dtlsSession->getGnuTLSSession(), this);
// start a timer to complete the handshake process
_handshakeTimer = new QTimer(this);
connect(_handshakeTimer, &QTimer::timeout, this, &DomainHandler::completeDTLSHandshake);
// start the handshake right now
completeDTLSHandshake();
// start the timer to finish off the handshake
_handshakeTimer->start(DTLS_HANDSHAKE_INTERVAL_MSECS);
}
}
void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hostname) {
@ -120,30 +89,6 @@ void DomainHandler::setHostname(const QString& hostname) {
}
}
void DomainHandler::completeDTLSHandshake() {
int handshakeReturn = gnutls_handshake(*_dtlsSession->getGnuTLSSession());
if (handshakeReturn == 0) {
// we've shaken hands, so we're good to go now
_dtlsSession->setCompletedHandshake(true);
_handshakeTimer->stop();
delete _handshakeTimer;
_handshakeTimer = NULL;
// emit a signal so NodeList can handle incoming DTLS packets
emit completedDTLSHandshake();
} else if (gnutls_error_is_fatal(handshakeReturn)) {
// this was a fatal error handshaking, so remove this session
qDebug() << "Fatal error -" << gnutls_strerror(handshakeReturn)
<< "- during DTLS handshake with DS at"
<< qPrintable((_hostname.isEmpty() ? _sockAddr.getAddress().toString() : _hostname));
clearConnectionInfo();
}
}
void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) {
for (int i = 0; i < hostInfo.addresses().size(); i++) {
if (hostInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) {
@ -179,5 +124,5 @@ void DomainHandler::parseDTLSRequirementPacket(const QByteArray& dtlsRequirement
_sockAddr.setPort(dtlsPort);
initializeDTLSSession();
// initializeDTLSSession();
}

View file

@ -18,7 +18,6 @@
#include <QtCore/QUrl>
#include <QtNetwork/QHostInfo>
#include "DTLSClientSession.h"
#include "HifiSockAddr.h"
const QString DEFAULT_DOMAIN_HOSTNAME = "sandbox.highfidelity.io";
@ -32,7 +31,6 @@ class DomainHandler : public QObject {
Q_OBJECT
public:
DomainHandler(QObject* parent = 0);
~DomainHandler();
void clearConnectionInfo();
@ -56,29 +54,22 @@ public:
bool isConnected() const { return _isConnected; }
void setIsConnected(bool isConnected);
DTLSClientSession* getDTLSSession() { return _dtlsSession; }
void parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket);
private slots:
void completeDTLSHandshake();
void completedHostnameLookup(const QHostInfo& hostInfo);
signals:
void hostnameChanged(const QString& hostname);
void connectedToDomain(const QString& hostname);
void completedDTLSHandshake();
void DTLSConnectionLost();
private:
void reset();
void initializeDTLSSession();
QUuid _uuid;
QString _hostname;
HifiSockAddr _sockAddr;
QUuid _assignmentUUID;
bool _isConnected;
DTLSClientSession* _dtlsSession;
QTimer* _handshakeTimer;
};

View file

@ -1,31 +0,0 @@
//
// DummyDTLSSession.cpp
// libraries/networking/src
//
// Created by Stephen Birarda on 2014-04-04.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "DummyDTLSSession.h"
ssize_t DummyDTLSSession::socketPush(gnutls_transport_ptr_t ptr, const void* buffer, size_t size) {
DummyDTLSSession* session = static_cast<DummyDTLSSession*>(ptr);
QUdpSocket& dtlsSocket = session->_dtlsSocket;
#if DTLS_VERBOSE_DEBUG
qDebug() << "Pushing a message of size" << size << "to" << session->_destinationSocket;
#endif
return dtlsSocket.writeDatagram(reinterpret_cast<const char*>(buffer), size,
session->_destinationSocket.getAddress(), session->_destinationSocket.getPort());
}
DummyDTLSSession::DummyDTLSSession(QUdpSocket& dtlsSocket, const HifiSockAddr& destinationSocket) :
_dtlsSocket(dtlsSocket),
_destinationSocket(destinationSocket)
{
}

View file

@ -1,34 +0,0 @@
//
// DummyDTLSSession.h
// libraries/networking/src
//
// Created by Stephen Birarda on 2014-04-04.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_DummyDTLSSession_h
#define hifi_DummyDTLSSession_h
#include <QtNetwork/QUdpSocket>
#include <gnutls/gnutls.h>
#include "HifiSockAddr.h"
#define DTLS_VERBOSE_DEBUG 0
class DummyDTLSSession : public QObject {
Q_OBJECT
public:
DummyDTLSSession(QUdpSocket& dtlsSocket, const HifiSockAddr& destinationSocket);
static ssize_t socketPush(gnutls_transport_ptr_t ptr, const void* buffer, size_t size);
protected:
QUdpSocket& _dtlsSocket;
HifiSockAddr _destinationSocket;
};
#endif // hifi_DummyDTLSSession_h

View file

@ -68,9 +68,6 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned
// clear our NodeList when logout is requested
connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset);
// perform a function when DTLS handshake is completed
connect(&_domainHandler, &DomainHandler::completedDTLSHandshake, this, &NodeList::completedDTLSHandshake);
}
qint64 NodeList::sendStatsToDomainServer(const QJsonObject& statsObject) {
@ -117,30 +114,6 @@ void NodeList::timePingReply(const QByteArray& packet, const SharedNodePointer&
}
}
void NodeList::completedDTLSHandshake() {
// at this point, we've got a DTLS socket
// make this NodeList the handler of DTLS packets
connect(_dtlsSocket, &QUdpSocket::readyRead, this, &NodeList::processAvailableDTLSDatagrams);
}
void NodeList::processAvailableDTLSDatagrams() {
while (_dtlsSocket->hasPendingDatagrams()) {
QByteArray dtlsPacket(_dtlsSocket->pendingDatagramSize(), 0);
// pull the data from this user off the stack and process it
int receivedBytes = gnutls_record_recv(*_domainHandler.getDTLSSession()->getGnuTLSSession(),
dtlsPacket.data(), dtlsPacket.size());
if (receivedBytes > 0) {
// successful data receive, hand this off to processNodeData
processNodeData(_domainHandler.getSockAddr(), dtlsPacket.left(receivedBytes));
} else if (gnutls_error_is_fatal(receivedBytes)) {
qDebug() << "Fatal error -" << gnutls_strerror(receivedBytes) << "- receiving DTLS packet from domain-server.";
} else {
qDebug() << "non fatal receive" << receivedBytes;
}
}
}
void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet) {
switch (packetTypeForPacket(packet)) {
case PacketTypeDomainList: {
@ -365,19 +338,9 @@ void NodeList::sendDomainServerCheckIn() {
// send a STUN request to figure it out
sendSTUNRequest();
} else if (!_domainHandler.getIP().isNull()) {
DTLSClientSession* dtlsSession = _domainHandler.getDTLSSession();
bool isUsingDTLS = false;
if (dtlsSession) {
if (!dtlsSession->completedHandshake()) {
// if the handshake process is not complete then we can't check in, so return
return;
} else {
isUsingDTLS = true;
}
}
PacketType domainPacketType = !_domainHandler.isConnected()
? PacketTypeDomainConnectRequest : PacketTypeDomainListRequest;
@ -405,8 +368,6 @@ void NodeList::sendDomainServerCheckIn() {
if (!isUsingDTLS) {
writeDatagram(domainServerPacket, _domainHandler.getSockAddr(), QUuid());
} else {
dtlsSession->writeDatagram(domainServerPacket);
}
const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5;

View file

@ -83,8 +83,6 @@ public slots:
void reset();
void sendDomainServerCheckIn();
void pingInactiveNodes();
void completedDTLSHandshake();
void processAvailableDTLSDatagrams();
signals:
void limitOfSilentDomainCheckInsReached();
private: