From 244cc016ab1b7c19ac8b49bdf87cbc9cbc9da634 Mon Sep 17 00:00:00 2001 From: bwent Date: Thu, 30 Jul 2015 15:22:17 -0700 Subject: [PATCH] Generate session token UUIDs to be signed with username --- domain-server/src/DomainServer.cpp | 50 +++++++++++++++---- domain-server/src/DomainServer.h | 6 ++- .../networking/src/DataServerAccountInfo.cpp | 29 ++++++----- .../networking/src/DataServerAccountInfo.h | 2 +- libraries/networking/src/DomainHandler.cpp | 2 + libraries/networking/src/DomainHandler.h | 6 ++- libraries/networking/src/NodeList.cpp | 37 ++++++++++---- libraries/networking/src/NodeList.h | 2 + .../networking/src/udt/PacketHeaders.cpp | 3 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- 10 files changed, 102 insertions(+), 38 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index c13de0449e..d9bd18580c 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -285,6 +285,7 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) { packetReceiver.registerListener(PacketType::DomainConnectRequest, this, "processConnectRequestPacket"); packetReceiver.registerListener(PacketType::DomainListRequest, this, "processListRequestPacket"); packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket"); + packetReceiver.registerListener(PacketType::DomainServerConnectionToken, this, "processConnectRequestPacket"); packetReceiver.registerListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket"); packetReceiver.registerListener(PacketType::ICEPing, this, "processICEPingPacket"); packetReceiver.registerListener(PacketType::ICEPingReply, this, "processICEPingReplyPacket"); @@ -579,7 +580,6 @@ const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer void DomainServer::processConnectRequestPacket(QSharedPointer packet) { NodeType_t nodeType; HifiSockAddr publicSockAddr, localSockAddr; - if (packet->getPayloadSize() == 0) { return; @@ -625,15 +625,35 @@ void DomainServer::processConnectRequestPacket(QSharedPointer packet) return; } } - } QList nodeInterestList; QString username; QByteArray usernameSignature; - packetStream >> nodeInterestList >> username >> usernameSignature; - + packetStream >> nodeInterestList; + + if (packet->bytesLeftToRead() > 0) { + // try to verify username and usernameSignature + packetStream >> username >> usernameSignature; + } else { + + QUuid& connectionToken = _connectionTokenHash[username]; + + if(connectionToken.isNull()) { + // set up the connection token packet + static auto connectionTokenPacket = NLPacket::create(PacketType::DomainServerConnectionToken, NUM_BYTES_RFC4122_UUID); + connectionToken->write(connectionToken.toRfc4122()); + nodeList->sendUnreliablePacket(connectionTokenPacket, packet->getSenderSockAddr()); + + return; + } else { + // reset existing packet + connectionToken->reset(); + } + + } + auto limitedNodeList = DependencyManager::get(); QString reason; @@ -736,6 +756,7 @@ void DomainServer::processConnectRequestPacket(QSharedPointer packet) } } + void DomainServer::processListRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { NodeType_t throwawayNodeType; @@ -754,6 +775,7 @@ void DomainServer::processListRequestPacket(QSharedPointer packet, Sha sendDomainListToNode(sendingNode, packet->getSenderSockAddr(), nodeInterestList.toSet()); } + unsigned int DomainServer::countConnectedUsers() { unsigned int result = 0; auto nodeList = DependencyManager::get(); @@ -766,11 +788,12 @@ unsigned int DomainServer::countConnectedUsers() { } -bool DomainServer::verifyUsersKey(const QString& username, +bool DomainServer::verifyUserSignature(const QString& username, const QByteArray& usernameSignature, QString& reasonReturn) { // it's possible this user can be allowed to connect, but we need to check their username signature - + + QByteArray publicKeyArray = _userPublicKeys.value(username); if (!publicKeyArray.isEmpty()) { // if we do have a public key for the user, check for a signature match @@ -787,13 +810,20 @@ bool DomainServer::verifyUsersKey(const QString& username, reinterpret_cast(usernameSignature.constData()), reinterpret_cast(decryptedArray.data()), rsaPublicKey, RSA_PKCS1_PADDING); - + + QByteArray lowercaseUsername = username.toLower().toUtf8(); + QUuid connectionToken = _connectionTokenHash[username]; + QByteArray usernameWithToken = lowercaseUsername.append(connectionToken); + if (decryptResult != -1) { - if (username.toLower() == decryptedArray) { + if (usernameWithToken == decryptedArray) { qDebug() << "Username signature matches for" << username << "- allowing connection."; // free up the public key before we return RSA_free(rsaPublicKey); + + // remove the username's connection token from the hash + _connectionTokenHash.remove(username); return true; } else { @@ -839,7 +869,7 @@ bool DomainServer::shouldAllowConnectionFromNode(const QString& username, _settingsManager.valueOrDefaultValueForKeyPath(ALLOWED_USERS_SETTINGS_KEYPATH).toStringList(); if (allowedUsers.contains(username, Qt::CaseInsensitive)) { - if (!verifyUsersKey(username, usernameSignature, reasonReturn)) { + if (!verifyUserSignature(username, usernameSignature, reasonReturn)) { return false; } } else { @@ -857,7 +887,7 @@ bool DomainServer::shouldAllowConnectionFromNode(const QString& username, valueForKeyPath(_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH); QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList(); if (allowedEditors.contains(username)) { - if (verifyUsersKey(username, usernameSignature, reasonReturn)) { + if (verifyUserSignature(username, usernameSignature, reasonReturn)) { return true; } } diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 7786fb34ac..fe79ec4889 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -57,7 +57,7 @@ public slots: void processRequestAssignmentPacket(QSharedPointer packet); void processConnectRequestPacket(QSharedPointer packet); - void processListRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); + void processListRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode);; void processNodeJSONStatsPacket(QSharedPointer packet, SharedNodePointer sendingNode); void processPathQueryPacket(QSharedPointer packet); void processICEPingPacket(QSharedPointer packet); @@ -90,7 +90,7 @@ private: void pingPunchForConnectingPeer(const SharedNetworkPeer& peer); unsigned int countConnectedUsers(); - bool verifyUsersKey (const QString& username, const QByteArray& usernameSignature, QString& reasonReturn); + bool verifyUserSignature (const QString& username, const QByteArray& usernameSignature, QString& reasonReturn); bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature, const HifiSockAddr& senderSockAddr, QString& reasonReturn); @@ -149,6 +149,8 @@ private: QSet _webAuthenticationStateSet; QHash _cookieSessionHash; + + QHash _connectionTokenHash; QHash _userPublicKeys; diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index a85aa588a8..bdef718ff4 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -128,8 +128,7 @@ void DataServerAccountInfo::setProfileInfoFromJSON(const QJsonObject& jsonObject setWalletID(QUuid(user["wallet_id"].toString())); } -const QByteArray& DataServerAccountInfo::getUsernameSignature() { - if (_usernameSignature.isEmpty()) { +QByteArray DataServerAccountInfo::getUsernameSignature(const QUuid& connectionToken) { if (!_privateKey.isEmpty()) { const char* privateKeyData = _privateKey.constData(); RSA* rsaPrivateKey = d2i_RSAPrivateKey(NULL, @@ -137,29 +136,33 @@ const QByteArray& DataServerAccountInfo::getUsernameSignature() { _privateKey.size()); if (rsaPrivateKey) { QByteArray lowercaseUsername = _username.toLower().toUtf8(); - _usernameSignature.resize(RSA_size(rsaPrivateKey)); + QByteArray usernameWithToken = lowercaseUsername.append(connectionToken.toRfc4122()); + + QByteArray usernameSignature(RSA_size(rsaPrivateKey), 0); int encryptReturn = RSA_private_encrypt(lowercaseUsername.size(), - reinterpret_cast(lowercaseUsername.constData()), - reinterpret_cast(_usernameSignature.data()), + reinterpret_cast(usernameWithToken.constData()), + reinterpret_cast(usernameSignature.data()), rsaPrivateKey, RSA_PKCS1_PADDING); - if (encryptReturn == -1) { - qCDebug(networking) << "Error encrypting username signature."; - qCDebug(networking) << "Will re-attempt on next domain-server check in."; - _usernameSignature = QByteArray(); - } + // free the private key RSA struct now that we are done with it RSA_free(rsaPrivateKey); + + if (encryptReturn == -1) { + qCDebug(networking) << "Error encrypting username signature."; + qCDebug(networking) << "Will re-attempt on next domain-server check in."; + } else { + return usernameSignature; + } + } else { qCDebug(networking) << "Could not create RSA struct from QByteArray private key."; qCDebug(networking) << "Will re-attempt on next domain-server check in."; } } - } - - return _usernameSignature; + return QByteArray(); } void DataServerAccountInfo::setPrivateKey(const QByteArray& privateKey) { diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index d7f9d5167a..0d8157ccf3 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -43,7 +43,7 @@ public: const QUuid& getWalletID() const { return _walletID; } void setWalletID(const QUuid& walletID); - const QByteArray& getUsernameSignature(); + QByteArray getUsernameSignature(const QUuid& connectionToken); bool hasPrivateKey() const { return !_privateKey.isEmpty(); } void setPrivateKey(const QByteArray& privateKey); diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index afb362053e..cd53d2608e 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -29,6 +29,7 @@ DomainHandler::DomainHandler(QObject* parent) : _uuid(), _sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)), _assignmentUUID(), + _connectionToken(), _iceDomainID(), _iceClientID(), _iceServerSockAddr(), @@ -61,6 +62,7 @@ void DomainHandler::clearSettings() { void DomainHandler::softReset() { qCDebug(networking) << "Resetting current domain connection information."; + _connectionToken = QUuid(); clearConnectionInfo(); clearSettings(); } diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 6079035f8b..7bb0582914 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -50,9 +50,12 @@ public: unsigned short getPort() const { return _sockAddr.getPort(); } void setPort(quint16 port) { _sockAddr.setPort(port); } + const QUuid& getConnectionToken() const { return _connectionToken; } + void setConnectionToken(const QUuid& connectionToken) { _connectionToken = connectionToken; } + const QUuid& getAssignmentUUID() const { return _assignmentUUID; } void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; } - + const QUuid& getICEDomainID() const { return _iceDomainID; } const QUuid& getICEClientID() const { return _iceClientID; } @@ -114,6 +117,7 @@ private: QString _hostname; HifiSockAddr _sockAddr; QUuid _assignmentUUID; + QUuid _connectionToken; QUuid _iceDomainID; QUuid _iceClientID; HifiSockAddr _iceServerSockAddr; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index dfa3ef86a5..6d62a7bbdc 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -94,7 +94,7 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned packetReceiver.registerListener(PacketType::PingReply, this, "processPingReplyPacket"); packetReceiver.registerListener(PacketType::ICEPing, this, "processICEPingPacket"); packetReceiver.registerListener(PacketType::DomainServerAddedNode, this, "processDomainServerAddedNode"); - + packetReceiver.registerListener(PacketType::DomainServerConnectionToken, this, "processDomainServerConnectionTokenPacket"); packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_domainHandler, "processICEResponsePacket"); packetReceiver.registerListener(PacketType::DomainServerRequireDTLS, &_domainHandler, "processDTLSRequirementPacket"); packetReceiver.registerListener(PacketType::ICEPingReply, &_domainHandler, "processICEPingReplyPacket"); @@ -274,17 +274,22 @@ void NodeList::sendDomainServerCheckIn() { // pack our data to send to the domain-server packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList(); - + // if this is a connect request, and we can present a username signature, send it along - if (!_domainHandler.isConnected()) { + if (!_domainHandler.isConnected() ) { DataServerAccountInfo& accountInfo = AccountManager::getInstance().getAccountInfo(); packetStream << accountInfo.getUsername(); - - const QByteArray& usernameSignature = AccountManager::getInstance().getAccountInfo().getUsernameSignature(); - - if (!usernameSignature.isEmpty()) { - qCDebug(networking) << "Including username signature in domain connect request."; - packetStream << usernameSignature; + + // get connection token from the domain-server + const QUuid& connectionToken = _domainHandler.getConnectionToken(); + + if(!connectionToken.isNull()) { + const QByteArray& usernameSignature = AccountManager::getInstance().getAccountInfo().getUsernameSignature(connectionToken); + + if (!usernameSignature.isEmpty()) { + qCDebug(networking) << "Including username signature in domain connect request."; + packetStream << usernameSignature; + } } } @@ -361,6 +366,7 @@ void NodeList::sendDSPathQuery(const QString& newPath) { } } + void NodeList::processDomainServerPathResponse(QSharedPointer packet) { // This is a response to a path query we theoretically made. // In the future we may want to check that this was actually from our DS and for a query we actually made. @@ -450,6 +456,19 @@ void NodeList::pingPunchForDomainServer() { } } + +void NodeList::processDomainServerConnectionToken(QSharedPointer packet) { + if (_domainHandler.getSockAddr().isNull()) { + // refuse to process this packet if we aren't currently connected to the DS + return; + } + + // read in the connection token from the packet, then send domain-server checkin + _domainHandler.setConnectionToken(QUuid::fromRfc4122(packet->read(NUM_BYTES_RFC4122_UUID))); + sendDomainServerCheckIn(); + +} + void NodeList::processDomainServerList(QSharedPointer packet) { if (_domainHandler.getSockAddr().isNull()) { // refuse to process this packet if we aren't currently connected to the DS diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index e3f8feeeda..b05e19d349 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -76,6 +76,8 @@ public slots: void processDomainServerAddedNode(QSharedPointer packet); void processDomainServerPathResponse(QSharedPointer packet); + void processDomainServerConnectionToken(QSharedPointer packet); + void processPingPacket(QSharedPointer packet, SharedNodePointer sendingNode); void processPingReplyPacket(QSharedPointer packet, SharedNodePointer sendingNode); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 0591ac30fe..90b4d57e05 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -27,7 +27,7 @@ const QSet SEQUENCE_NUMBERED_PACKETS = QSet NON_SOURCED_PACKETS = QSet() << StunResponse << CreateAssignment << RequestAssignment << DomainServerRequireDTLS << DomainConnectRequest - << DomainList << DomainConnectionDenied + << DomainList << DomainConnectionDenied << DomainServerConnectionToken << DomainServerPathQuery << DomainServerPathResponse << DomainServerAddedNode << ICEServerPeerInformation << ICEServerQuery << ICEServerHeartbeat @@ -119,6 +119,7 @@ QString nameForPacketType(PacketType::Value packetType) { PACKET_TYPE_NAME_LOOKUP(ICEPingReply); PACKET_TYPE_NAME_LOOKUP(EntityAdd); PACKET_TYPE_NAME_LOOKUP(EntityEdit); + PACKET_TYPE_NAME_LOOKUP(DomainServerConnectionToken); default: return QString("Type: ") + QString::number((int)packetType); } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 3f3f165e87..46f834db74 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -73,7 +73,8 @@ namespace PacketType { EntityQuery, EntityAdd, EntityErase, - EntityEdit + EntityEdit, + DomainServerConnectionToken }; };