mirror of
https://github.com/overte-org/overte.git
synced 2025-04-16 10:28:57 +02:00
Generate session token UUIDs to be signed with username
This commit is contained in:
parent
597316b208
commit
244cc016ab
10 changed files with 102 additions and 38 deletions
|
@ -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<NLPacket> packet) {
|
||||
NodeType_t nodeType;
|
||||
HifiSockAddr publicSockAddr, localSockAddr;
|
||||
|
||||
|
||||
if (packet->getPayloadSize() == 0) {
|
||||
return;
|
||||
|
@ -625,15 +625,35 @@ void DomainServer::processConnectRequestPacket(QSharedPointer<NLPacket> packet)
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QList<NodeType_t> 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<LimitedNodeList>();
|
||||
|
||||
QString reason;
|
||||
|
@ -736,6 +756,7 @@ void DomainServer::processConnectRequestPacket(QSharedPointer<NLPacket> packet)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void DomainServer::processListRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
|
||||
|
||||
NodeType_t throwawayNodeType;
|
||||
|
@ -754,6 +775,7 @@ void DomainServer::processListRequestPacket(QSharedPointer<NLPacket> packet, Sha
|
|||
sendDomainListToNode(sendingNode, packet->getSenderSockAddr(), nodeInterestList.toSet());
|
||||
}
|
||||
|
||||
|
||||
unsigned int DomainServer::countConnectedUsers() {
|
||||
unsigned int result = 0;
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
@ -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<const unsigned char*>(usernameSignature.constData()),
|
||||
reinterpret_cast<unsigned char*>(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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ public slots:
|
|||
|
||||
void processRequestAssignmentPacket(QSharedPointer<NLPacket> packet);
|
||||
void processConnectRequestPacket(QSharedPointer<NLPacket> packet);
|
||||
void processListRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
|
||||
void processListRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);;
|
||||
void processNodeJSONStatsPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
|
||||
void processPathQueryPacket(QSharedPointer<NLPacket> packet);
|
||||
void processICEPingPacket(QSharedPointer<NLPacket> 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<QUuid> _webAuthenticationStateSet;
|
||||
QHash<QUuid, DomainServerWebSessionData> _cookieSessionHash;
|
||||
|
||||
QHash<QString, QUuid> _connectionTokenHash;
|
||||
|
||||
QHash<QString, QByteArray> _userPublicKeys;
|
||||
|
||||
|
|
|
@ -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<const unsigned char*>(lowercaseUsername.constData()),
|
||||
reinterpret_cast<unsigned char*>(_usernameSignature.data()),
|
||||
reinterpret_cast<const unsigned char*>(usernameWithToken.constData()),
|
||||
reinterpret_cast<unsigned char*>(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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<NLPacket> 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<NLPacket> 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<NLPacket> packet) {
|
||||
if (_domainHandler.getSockAddr().isNull()) {
|
||||
// refuse to process this packet if we aren't currently connected to the DS
|
||||
|
|
|
@ -76,6 +76,8 @@ public slots:
|
|||
void processDomainServerAddedNode(QSharedPointer<NLPacket> packet);
|
||||
void processDomainServerPathResponse(QSharedPointer<NLPacket> packet);
|
||||
|
||||
void processDomainServerConnectionToken(QSharedPointer<NLPacket> packet);
|
||||
|
||||
void processPingPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
|
||||
void processPingReplyPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ const QSet<PacketType::Value> SEQUENCE_NUMBERED_PACKETS = QSet<PacketType::Value
|
|||
const QSet<PacketType::Value> NON_SOURCED_PACKETS = QSet<PacketType::Value>()
|
||||
<< 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);
|
||||
}
|
||||
|
|
|
@ -73,7 +73,8 @@ namespace PacketType {
|
|||
EntityQuery,
|
||||
EntityAdd,
|
||||
EntityErase,
|
||||
EntityEdit
|
||||
EntityEdit,
|
||||
DomainServerConnectionToken
|
||||
};
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue