From da30d21f6efeff8626aaafb2324435e8e66a2222 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 31 Mar 2014 14:02:11 -0700 Subject: [PATCH] complete inital DTLS setup in domain-server --- domain-server/src/DomainServer.cpp | 80 +++++++++++++++++++++++++++--- domain-server/src/DomainServer.h | 9 +++- libraries/shared/src/NodeList.cpp | 9 ++++ libraries/shared/src/NodeList.h | 2 + 4 files changed, 90 insertions(+), 10 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 80a86da8a0..df0935ba54 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -35,7 +35,10 @@ DomainServer::DomainServer(int argc, char* argv[]) : _HTTPManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this), _staticAssignmentHash(), _assignmentQueue(), - _x509Credentials() + _isUsingDTLS(false), + _x509Credentials(NULL), + _dhParams(NULL), + _priorityCache(NULL) { setOrganizationName("High Fidelity"); setOrganizationDomain("highfidelity.io"); @@ -44,29 +47,85 @@ DomainServer::DomainServer(int argc, char* argv[]) : _argumentList = arguments(); - if (readCertificateAndPrivateKey()) { + if (optionallySetupDTLS()) { // we either read a certificate and private key or were not passed one, good to load assignments // and set up the node list + qDebug() << "Setting up NodeList and assignments."; setupNodeListAndAssignments(); + + if (_isUsingDTLS) { + // we're using DTLS and our NodeList socket is good to go, so make the required DTLS changes + // DTLS requires that IP_DONTFRAG be set + // This is not accessible on some platforms (OS X) so we need to make sure DTLS still works without it + + NodeList* nodeList = NodeList::getInstance(); + +#if defined(IP_DONTFRAG) || defined(IP_MTU_DISCOVER) + qDebug() << "Making required DTLS changes to NodeList DTLS socket."; + + int socketHandle = NodeList::getInstance()->getDTLSSocket().socketDescriptor(); +#if defined(IP_DONTFRAG) + int optValue = 1;yea + setsockopt(socketHandle, IPPROTO_IP, IP_DONTFRAG, (const void*) optValue, sizeof(optValue)); +#elif defined(IP_MTU_DISCOVER) + int optValue = 1; + setsockopt(socketHandle, IPPROTO_IP, IP_MTU_DISCOVER, (const void*) optValue, sizeof(optValue)); +#endif +#endif + // connect our socket to read datagrams received on the DTLS socket + connect(&nodeList->getDTLSSocket(), &QUdpSocket::readyRead, this, &DomainServer::readAvailableDTLSDatagrams); + } } } -bool DomainServer::readCertificateAndPrivateKey() { +bool DomainServer::optionallySetupDTLS() { + if (readX509KeyAndCertificate()) { + 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); + + _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; + } else { + return false; + } +} + +bool DomainServer::readX509KeyAndCertificate() { const QString X509_CERTIFICATE_OPTION = "--cert"; const QString X509_PRIVATE_KEY_OPTION = "--key"; const QString X509_KEY_PASSPHRASE_ENV = "DOMAIN_SERVER_KEY_PASSPHRASE"; - + int certIndex = _argumentList.indexOf(X509_CERTIFICATE_OPTION); int keyIndex = _argumentList.indexOf(X509_PRIVATE_KEY_OPTION); if (certIndex != -1 && keyIndex != -1) { // the user wants to use DTLS to encrypt communication with nodes - // let's make sure we can load the ey - gnutls_certificate_allocate_credentials(&_x509Credentials); + // let's make sure we can load the key and certificate + _x509Credentials = new gnutls_certificate_credentials_t; + gnutls_certificate_allocate_credentials(_x509Credentials); QString keyPassphraseString = QProcessEnvironment::systemEnvironment().value(X509_KEY_PASSPHRASE_ENV); - int gnutlsReturn = gnutls_certificate_set_x509_key_file2(_x509Credentials, + int gnutlsReturn = gnutls_certificate_set_x509_key_file2(*_x509Credentials, _argumentList[certIndex + 1].toLocal8Bit().constData(), _argumentList[keyIndex + 1].toLocal8Bit().constData(), GNUTLS_X509_FMT_PEM, @@ -79,7 +138,8 @@ bool DomainServer::readCertificateAndPrivateKey() { return false; } - qDebug() << "Successfully read certificate and private key. Using DTLS for node communication."; + qDebug() << "Successfully read certificate and private key."; + } else if (certIndex != -1 || keyIndex != -1) { qDebug() << "Missing certificate or private key. domain-server will now quit."; QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); @@ -552,6 +612,10 @@ void DomainServer::readAvailableDatagrams() { } } +void DomainServer::readAvailableDTLSDatagrams() { + +} + QJsonObject DomainServer::jsonForSocket(const HifiSockAddr& socket) { QJsonObject socketJSON; diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 244621c7b1..ba2b47a2e7 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -45,9 +45,11 @@ private slots: void processCreateResponseFromDataServer(const QJsonObject& jsonObject); void readAvailableDatagrams(); + void readAvailableDTLSDatagrams(); private: void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid()); - bool readCertificateAndPrivateKey(); + bool optionallySetupDTLS(); + bool readX509KeyAndCertificate(); void requestAuthenticationFromPotentialNode(const HifiSockAddr& senderSockAddr); void addNodeToNodeListAndConfirmConnection(const QByteArray& packet, const HifiSockAddr& senderSockAddr, @@ -81,7 +83,10 @@ private: QStringList _argumentList; - gnutls_certificate_credentials_t _x509Credentials; + bool _isUsingDTLS; + gnutls_certificate_credentials_t* _x509Credentials; + gnutls_dh_params_t* _dhParams; + gnutls_priority_t* _priorityCache; }; #endif /* defined(__hifi__DomainServer__) */ diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 761ea40d55..b053db356e 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -89,6 +89,15 @@ NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) : _packetStatTimer.start(); } +QUdpSocket& NodeList::getDTLSSocket() { + if (_dtlsSocket.state() == QAbstractSocket::UnconnectedState) { + _dtlsSocket.bind(QHostAddress::AnyIPv4); + qDebug() << "NodeList DTLS socket is listening on" << _dtlsSocket.localPort(); + } + + return _dtlsSocket; +} + void NodeList::changeSendSocketBufferSize(int numSendBytes) { // change the socket send buffer size to be 1MB int oldBufferSize = 0; diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index d05d6a2fbc..24281e3aa3 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -72,6 +72,7 @@ public: void setSessionUUID(const QUuid& sessionUUID); QUdpSocket& getNodeSocket() { return _nodeSocket; } + QUdpSocket& getDTLSSocket(); bool packetVersionAndHashMatch(const QByteArray& packet); @@ -165,6 +166,7 @@ private: NodeHash _nodeHash; QMutex _nodeHashMutex; QUdpSocket _nodeSocket; + QUdpSocket _dtlsSocket; NodeType_t _ownerType; NodeSet _nodeTypesOfInterest; DomainInfo _domainInfo;