Ban only by machine fingerprint, when possible

This commit is contained in:
David Kelly 2017-02-16 09:50:56 -07:00
parent a3c123818d
commit a8831e89ff

View file

@ -30,6 +30,7 @@
#include <NumericalConstants.h> #include <NumericalConstants.h>
#include <SettingHandle.h> #include <SettingHandle.h>
#include <AvatarData.h> //for KillAvatarReason #include <AvatarData.h> //for KillAvatarReason
#include <FingerprintUtils.h>
#include "DomainServerNodeData.h" #include "DomainServerNodeData.h"
const QString SETTINGS_DESCRIPTION_RELATIVE_PATH = "/resources/describe-settings.json"; const QString SETTINGS_DESCRIPTION_RELATIVE_PATH = "/resources/describe-settings.json";
@ -439,7 +440,7 @@ bool DomainServerSettingsManager::unpackPermissionsForKeypath(const QString& key
foreach (QVariant permsHash, permissionsList) { foreach (QVariant permsHash, permissionsList) {
NodePermissionsPointer perms { new NodePermissions(permsHash.toMap()) }; NodePermissionsPointer perms { new NodePermissions(permsHash.toMap()) };
QString id = perms->getID(); QString id = perms->getID();
NodePermissionsKey idKey = perms->getKey(); NodePermissionsKey idKey = perms->getKey();
if (mapPointer->contains(idKey)) { if (mapPointer->contains(idKey)) {
@ -484,7 +485,7 @@ void DomainServerSettingsManager::unpackPermissions() {
// make sure that this permission row is for a non-empty hardware // make sure that this permission row is for a non-empty hardware
if (perms->getKey().first.isEmpty()) { if (perms->getKey().first.isEmpty()) {
_macPermissions.remove(perms->getKey()); _macPermissions.remove(perms->getKey());
// we removed a row from the MAC permissions, we'll need a re-pack // we removed a row from the MAC permissions, we'll need a re-pack
needPack = true; needPack = true;
} }
@ -555,7 +556,7 @@ void DomainServerSettingsManager::unpackPermissions() {
QList<QHash<NodePermissionsKey, NodePermissionsPointer>> permissionsSets; QList<QHash<NodePermissionsKey, NodePermissionsPointer>> permissionsSets;
permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get() permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get()
<< _groupPermissions.get() << _groupForbiddens.get() << _groupPermissions.get() << _groupForbiddens.get()
<< _ipPermissions.get() << _macPermissions.get() << _ipPermissions.get() << _macPermissions.get()
<< _machineFingerprintPermissions.get(); << _machineFingerprintPermissions.get();
foreach (auto permissionSet, permissionsSets) { foreach (auto permissionSet, permissionsSets) {
@ -668,71 +669,68 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<Re
// ensure that the connect permission is clear // ensure that the connect permission is clear
userPermissions->clear(NodePermissions::Permission::canConnectToDomain); userPermissions->clear(NodePermissions::Permission::canConnectToDomain);
} else { } else {
// otherwise we apply the kick to the IP from active socket for this node and the MAC address // remove connect permissions for the machine fingerprint
// remove connect permissions for the IP (falling back to the public socket if not yet active)
auto& kickAddress = matchingNode->getActiveSocket()
? matchingNode->getActiveSocket()->getAddress()
: matchingNode->getPublicSocket().getAddress();
// probably isLoopback covers it, as whenever I try to ban an agent on same machine as the domain-server
// it is always 127.0.0.1, but looking at the public and local addresses just to be sure
// TODO: soon we will have feedback (in the form of a message to the client) after we kick. When we
// do, we will have a success flag, and perhaps a reason for failure. For now, just don't do it.
if (kickAddress == limitedNodeList->getPublicSockAddr().getAddress() ||
kickAddress == limitedNodeList->getLocalSockAddr().getAddress() ||
kickAddress.isLoopback() ) {
qWarning() << "attempt to kick node running on same machine as domain server, ignoring KickRequest";
return;
}
NodePermissionsKey ipAddressKey(kickAddress.toString(), QUuid());
// check if there were already permissions for the IP
bool hadIPPermissions = hasPermissionsForIP(kickAddress);
// grab or create permissions for the given IP address
auto ipPermissions = _ipPermissions[ipAddressKey];
if (!hadIPPermissions || ipPermissions->can(NodePermissions::Permission::canConnectToDomain)) {
newPermissions = true;
ipPermissions->clear(NodePermissions::Permission::canConnectToDomain);
}
// potentially remove connect permissions for the MAC address and machine fingerprint
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(matchingNode->getLinkedData()); DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(matchingNode->getLinkedData());
if (nodeData) { if (nodeData) {
// mac address first // get this machine's fingerprint
NodePermissionsKey macAddressKey(nodeData->getHardwareAddress(), 0); auto domainServerFingerprint = FingerprintUtils::getMachineFingerprint();
bool hadMACPermissions = hasPermissionsForMAC(nodeData->getHardwareAddress()); if (nodeData->getMachineFingerprint() == domainServerFingerprint) {
qWarning() << "attempt to kick node running on same machine as domain server (by fingerprint), ignoring KickRequest";
auto macPermissions = _macPermissions[macAddressKey]; return;
if (!hadMACPermissions || macPermissions->can(NodePermissions::Permission::canConnectToDomain)) {
newPermissions = true;
macPermissions->clear(NodePermissions::Permission::canConnectToDomain);
} }
// now for machine fingerprint
NodePermissionsKey machineFingerprintKey(nodeData->getMachineFingerprint().toString(), 0); NodePermissionsKey machineFingerprintKey(nodeData->getMachineFingerprint().toString(), 0);
// check if there were already permissions for the fingerprint
bool hadFingerprintPermissions = hasPermissionsForMachineFingerprint(nodeData->getMachineFingerprint()); bool hadFingerprintPermissions = hasPermissionsForMachineFingerprint(nodeData->getMachineFingerprint());
// grab or create permissions for the given fingerprint
auto fingerprintPermissions = _machineFingerprintPermissions[machineFingerprintKey]; auto fingerprintPermissions = _machineFingerprintPermissions[machineFingerprintKey];
// write them
if (!hadFingerprintPermissions || fingerprintPermissions->can(NodePermissions::Permission::canConnectToDomain)) { if (!hadFingerprintPermissions || fingerprintPermissions->can(NodePermissions::Permission::canConnectToDomain)) {
newPermissions = true; newPermissions = true;
fingerprintPermissions->clear(NodePermissions::Permission::canConnectToDomain); fingerprintPermissions->clear(NodePermissions::Permission::canConnectToDomain);
} }
} else {
// if no node data, all we can do is IP address
auto& kickAddress = matchingNode->getActiveSocket()
? matchingNode->getActiveSocket()->getAddress()
: matchingNode->getPublicSocket().getAddress();
// probably isLoopback covers it, as whenever I try to ban an agent on same machine as the domain-server
// it is always 127.0.0.1, but looking at the public and local addresses just to be sure
// TODO: soon we will have feedback (in the form of a message to the client) after we kick. When we
// do, we will have a success flag, and perhaps a reason for failure. For now, just don't do it.
if (kickAddress == limitedNodeList->getPublicSockAddr().getAddress() ||
kickAddress == limitedNodeList->getLocalSockAddr().getAddress() ||
kickAddress.isLoopback() ) {
qWarning() << "attempt to kick node running on same machine as domain server, ignoring KickRequest";
return;
}
NodePermissionsKey ipAddressKey(kickAddress.toString(), QUuid());
// check if there were already permissions for the IP
bool hadIPPermissions = hasPermissionsForIP(kickAddress);
// grab or create permissions for the given IP address
auto ipPermissions = _ipPermissions[ipAddressKey];
if (!hadIPPermissions || ipPermissions->can(NodePermissions::Permission::canConnectToDomain)) {
newPermissions = true;
ipPermissions->clear(NodePermissions::Permission::canConnectToDomain);
}
} }
} }
// if we are here, then we kicked them, so send the KillAvatar message to everyone
// if we are here, then we kicked them, so send the KillAvatar message
auto packet = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true); auto packet = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true);
packet->write(nodeUUID.toRfc4122()); packet->write(nodeUUID.toRfc4122());
packet->writePrimitive(KillAvatarReason::NoReason); packet->writePrimitive(KillAvatarReason::NoReason);
// send to avatar mixer, it sends the kill to everyone else // send to avatar mixer, it sends the kill to everyone else
limitedNodeList->broadcastToNodes(std::move(packet), NodeSet() << NodeType::AvatarMixer); limitedNodeList->broadcastToNodes(std::move(packet), NodeSet() << NodeType::AvatarMixer);
@ -743,7 +741,7 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<Re
// we've changed permissions, time to store them to disk and emit our signal to say they have changed // we've changed permissions, time to store them to disk and emit our signal to say they have changed
packPermissions(); packPermissions();
} }
// we emit this no matter what -- though if this isn't a new permission probably 2 people are racing to kick and this // we emit this no matter what -- though if this isn't a new permission probably 2 people are racing to kick and this
// person lost the race. No matter, just be sure this is called as otherwise it takes like 10s for the person being banned // person lost the race. No matter, just be sure this is called as otherwise it takes like 10s for the person being banned
// to go away // to go away