Generate session token UUIDs to be signed with username

This commit is contained in:
bwent 2015-07-30 15:22:17 -07:00
parent 597316b208
commit 244cc016ab
10 changed files with 102 additions and 38 deletions

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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) {

View file

@ -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);

View file

@ -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();
}

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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);
}

View file

@ -73,7 +73,8 @@ namespace PacketType {
EntityQuery,
EntityAdd,
EntityErase,
EntityEdit
EntityEdit,
DomainServerConnectionToken
};
};