diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json
index 257dcaa783..dd0e4ad4a1 100644
--- a/domain-server/resources/describe-settings.json
+++ b/domain-server/resources/describe-settings.json
@@ -845,6 +845,78 @@
}
],
+ "columns": [
+ {
+ "name": "permissions_id",
+ "label": ""
+ },
+ {
+ "name": "id_can_connect",
+ "label": "Connect",
+ "type": "checkbox",
+ "editable": true,
+ "default": false
+ },
+ {
+ "name": "id_can_adjust_locks",
+ "label": "Lock / Unlock",
+ "type": "checkbox",
+ "editable": true,
+ "default": false
+ },
+ {
+ "name": "id_can_rez",
+ "label": "Rez",
+ "type": "checkbox",
+ "editable": true,
+ "default": false
+ },
+ {
+ "name": "id_can_rez_tmp",
+ "label": "Rez Temporary",
+ "type": "checkbox",
+ "editable": true,
+ "default": false
+ },
+ {
+ "name": "id_can_write_to_asset_server",
+ "label": "Write Assets",
+ "type": "checkbox",
+ "editable": true,
+ "default": false
+ },
+ {
+ "name": "id_can_connect_past_max_capacity",
+ "label": "Ignore Max Capacity",
+ "type": "checkbox",
+ "editable": true,
+ "default": false
+ },
+ {
+ "name": "id_can_kick",
+ "label": "Kick Users",
+ "type": "checkbox",
+ "editable": true,
+ "default": false
+ }
+ ]
+ },
+ {
+ "name": "machine_fingerprint_permissions",
+ "type": "table",
+ "caption": "Permissions for Users with Machine Fingerprints",
+ "can_add_new_rows": true,
+ "groups": [
+ {
+ "label": "Machine Fingerprint",
+ "span": 1
+ },
+ {
+ "label": "Permissions ?",
+ "span": 7
+ }
+ ],
+
"columns": [
{
"name": "permissions_id",
diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp
index f55a2073d1..97f3e1a697 100644
--- a/domain-server/src/DomainGatekeeper.cpp
+++ b/domain-server/src/DomainGatekeeper.cpp
@@ -120,19 +120,21 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointersetPlaceName(nodeConnection.placeName);
qDebug() << "Allowed connection from node" << uuidStringWithoutCurlyBraces(node->getUUID())
- << "on" << message->getSenderSockAddr() << "with MAC" << nodeConnection.hardwareAddress;
+ << "on" << message->getSenderSockAddr() << "with MAC" << nodeConnection.hardwareAddress
+ << "and machine fingerprint" << nodeConnection.machineFingerprint;
// signal that we just connected a node so the DomainServer can get it a list
// and broadcast its presence right away
emit connectedNode(node);
} else {
qDebug() << "Refusing connection from node at" << message->getSenderSockAddr()
- << "with hardware address" << nodeConnection.hardwareAddress;
+ << "with hardware address" << nodeConnection.hardwareAddress
+ << "and machine fingerprint" << nodeConnection.machineFingerprint;
}
}
-NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QString verifiedUsername,
- const QHostAddress& senderAddress, const QString& hardwareAddress) {
+NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QString verifiedUsername, const QHostAddress& senderAddress,
+ const QString& hardwareAddress, const QUuid& machineFingerprint) {
NodePermissions userPerms;
userPerms.setAll(false);
@@ -155,6 +157,11 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin
#ifdef WANT_DEBUG
qDebug() << "| user-permissions: specific MAC matches, so:" << userPerms;
+#endif
+ } else if (_server->_settingsManager.hasPermissionsForMachineFingerprint(machineFingerprint)) {
+ userPerms = _server->_settingsManager.getPermissionsForMachineFingerprint(machineFingerprint);
+#ifdef WANT_DEBUG
+ qDebug(() << "| user-permissions: specific Machine Fingerprint matches, so: " << userPerms;
#endif
} else if (_server->_settingsManager.hasPermissionsForIP(senderAddress)) {
// this user comes from an IP we have in our permissions table, apply those permissions
@@ -274,13 +281,15 @@ void DomainGatekeeper::updateNodePermissions() {
HifiSockAddr connectingAddr = node->getActiveSocket() ? *node->getActiveSocket() : node->getPublicSocket();
QString hardwareAddress;
+ QUuid machineFingerprint;
DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData());
if (nodeData) {
hardwareAddress = nodeData->getHardwareAddress();
+ machineFingerprint = nodeData->getMachineFingerprint();
}
- userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, connectingAddr.getAddress(), hardwareAddress);
+ userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, connectingAddr.getAddress(), hardwareAddress, machineFingerprint);
}
node->setPermissions(userPerms);
@@ -334,6 +343,8 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo
nodeData->setWalletUUID(it->second.getWalletUUID());
nodeData->setNodeVersion(it->second.getNodeVersion());
nodeData->setHardwareAddress(nodeConnection.hardwareAddress);
+ nodeData->setMachineFingerprint(nodeConnection.machineFingerprint);
+
nodeData->setWasAssigned(true);
// cleanup the PendingAssignedNodeData for this assignment now that it's connecting
@@ -396,7 +407,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
}
userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, nodeConnection.senderSockAddr.getAddress(),
- nodeConnection.hardwareAddress);
+ nodeConnection.hardwareAddress, nodeConnection.machineFingerprint);
if (!userPerms.can(NodePermissions::Permission::canConnectToDomain)) {
sendConnectionDeniedPacket("You lack the required permissions to connect to this domain.",
@@ -455,6 +466,9 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
// set the hardware address passed in the connect request
nodeData->setHardwareAddress(nodeConnection.hardwareAddress);
+ // set the machine fingerprint passed in the connect request
+ nodeData->setMachineFingerprint(nodeConnection.machineFingerprint);
+
// also add an interpolation to DomainServerNodeData so that servers can get username in stats
nodeData->addOverrideForKey(USERNAME_UUID_REPLACEMENT_STATS_KEY,
uuidStringWithoutCurlyBraces(newNode->getUUID()), username);
diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h
index b17d0f61a4..163f255411 100644
--- a/domain-server/src/DomainGatekeeper.h
+++ b/domain-server/src/DomainGatekeeper.h
@@ -107,8 +107,8 @@ private:
QSet _domainOwnerFriends; // keep track of friends of the domain owner
QSet _inFlightGroupMembershipsRequests; // keep track of which we've already asked for
- NodePermissions setPermissionsForUser(bool isLocalUser, QString verifiedUsername,
- const QHostAddress& senderAddress, const QString& hardwareAddress);
+ NodePermissions setPermissionsForUser(bool isLocalUser, QString verifiedUsername, const QHostAddress& senderAddress,
+ const QString& hardwareAddress, const QUuid& machineFingerprint);
void getGroupMemberships(const QString& username);
// void getIsGroupMember(const QString& username, const QUuid groupID);
diff --git a/domain-server/src/DomainServerNodeData.h b/domain-server/src/DomainServerNodeData.h
index ff637844f1..db41c77cf2 100644
--- a/domain-server/src/DomainServerNodeData.h
+++ b/domain-server/src/DomainServerNodeData.h
@@ -56,7 +56,10 @@ public:
void setHardwareAddress(const QString& hardwareAddress) { _hardwareAddress = hardwareAddress; }
const QString& getHardwareAddress() { return _hardwareAddress; }
-
+
+ void setMachineFingerprint(const QUuid& machineFingerprint) { _machineFingerprint = machineFingerprint; }
+ const QUuid& getMachineFingerprint() { return _machineFingerprint; }
+
void addOverrideForKey(const QString& key, const QString& value, const QString& overrideValue);
void removeOverrideForKey(const QString& key, const QString& value);
@@ -85,6 +88,7 @@ private:
NodeSet _nodeInterestSet;
QString _nodeVersion;
QString _hardwareAddress;
+ QUuid _machineFingerprint;
QString _placeName;
diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp
index ebcffc262f..05cec07f80 100644
--- a/domain-server/src/DomainServerSettingsManager.cpp
+++ b/domain-server/src/DomainServerSettingsManager.cpp
@@ -444,6 +444,9 @@ void DomainServerSettingsManager::packPermissions() {
// save settings for MAC addresses
packPermissionsForMap("permissions", _macPermissions, MAC_PERMISSIONS_KEYPATH);
+ // save settings for Machine Fingerprint
+ packPermissionsForMap("permissions", _machineFingerprintPermissions, MACHINE_FINGERPRINT_PERMISSIONS_KEYPATH);
+
// save settings for groups
packPermissionsForMap("permissions", _groupPermissions, GROUP_PERMISSIONS_KEYPATH);
@@ -522,6 +525,18 @@ void DomainServerSettingsManager::unpackPermissions() {
}
});
+ needPack |= unpackPermissionsForKeypath(MACHINE_FINGERPRINT_PERMISSIONS_KEYPATH, &_machineFingerprintPermissions,
+ [&](NodePermissionsPointer perms){
+ // make sure that this permission row has valid machine fingerprint
+ if (QUuid(perms->getKey().first) == QUuid()) {
+ _machineFingerprintPermissions.remove(perms->getKey());
+
+ // we removed a row, so we'll need a re-pack
+ needPack = true;
+ }
+
+ });
+
needPack |= unpackPermissionsForKeypath(GROUP_PERMISSIONS_KEYPATH, &_groupPermissions,
[&](NodePermissionsPointer perms){
@@ -575,7 +590,9 @@ void DomainServerSettingsManager::unpackPermissions() {
QList> permissionsSets;
permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get()
<< _groupPermissions.get() << _groupForbiddens.get()
- << _ipPermissions.get() << _macPermissions.get();
+ << _ipPermissions.get() << _macPermissions.get()
+ << _machineFingerprintPermissions.get();
+
foreach (auto permissionSet, permissionsSets) {
QHashIterator i(permissionSet);
while (i.hasNext()) {
@@ -707,9 +724,10 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointerclear(NodePermissions::Permission::canConnectToDomain);
}
- // potentially remove connect permissions for the MAC address
+ // potentially remove connect permissions for the MAC address and machine fingerprint
DomainServerNodeData* nodeData = reinterpret_cast(matchingNode->getLinkedData());
if (nodeData) {
+ // mac address first
NodePermissionsKey macAddressKey(nodeData->getHardwareAddress(), 0);
bool hadMACPermissions = hasPermissionsForMAC(nodeData->getHardwareAddress());
@@ -721,6 +739,18 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointerclear(NodePermissions::Permission::canConnectToDomain);
}
+
+ // now for machine fingerprint
+ NodePermissionsKey machineFingerprintKey(nodeData->getMachineFingerprint().toString(), 0);
+
+ bool hadFingerprintPermissions = hasPermissionsForMachineFingerprint(nodeData->getMachineFingerprint());
+
+ auto fingerprintPermissions = _machineFingerprintPermissions[machineFingerprintKey];
+
+ if (!hadFingerprintPermissions || fingerprintPermissions->can(NodePermissions::Permission::canConnectToDomain)) {
+ newPermissions = true;
+ fingerprintPermissions->clear(NodePermissions::Permission::canConnectToDomain);
+ }
}
}
@@ -834,6 +864,16 @@ NodePermissions DomainServerSettingsManager::getPermissionsForMAC(const QString&
return nullPermissions;
}
+NodePermissions DomainServerSettingsManager::getPermissionsForMachineFingerprint(const QUuid& machineFingerprint) const {
+ NodePermissionsKey fingerprintKey = NodePermissionsKey(machineFingerprint.toString(), 0);
+ if (_machineFingerprintPermissions.contains(fingerprintKey)) {
+ return *(_machineFingerprintPermissions[fingerprintKey].get());
+ }
+ NodePermissions nullPermissions;
+ nullPermissions.setAll(false);
+ return nullPermissions;
+}
+
NodePermissions DomainServerSettingsManager::getPermissionsForGroup(const QString& groupName, QUuid rankID) const {
NodePermissionsKey groupRankKey = NodePermissionsKey(groupName, rankID);
if (_groupPermissions.contains(groupRankKey)) {
diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h
index 8c6155b3c0..2b5f9518a0 100644
--- a/domain-server/src/DomainServerSettingsManager.h
+++ b/domain-server/src/DomainServerSettingsManager.h
@@ -32,6 +32,7 @@ const QString AGENT_STANDARD_PERMISSIONS_KEYPATH = "security.standard_permission
const QString AGENT_PERMISSIONS_KEYPATH = "security.permissions";
const QString IP_PERMISSIONS_KEYPATH = "security.ip_permissions";
const QString MAC_PERMISSIONS_KEYPATH = "security.mac_permissions";
+const QString MACHINE_FINGERPRINT_PERMISSIONS_KEYPATH = "security.machine_fingerprint_permissions";
const QString GROUP_PERMISSIONS_KEYPATH = "security.group_permissions";
const QString GROUP_FORBIDDENS_KEYPATH = "security.group_forbiddens";
@@ -70,6 +71,10 @@ public:
bool hasPermissionsForMAC(const QString& macAddress) const { return _macPermissions.contains(macAddress, 0); }
NodePermissions getPermissionsForMAC(const QString& macAddress) const;
+ // these give access to permissions for specific machine fingerprints from the domain-server settings page
+ bool hasPermissionsForMachineFingerprint(const QUuid& machineFingerprint) { return _machineFingerprintPermissions.contains(machineFingerprint.toString(), 0); }
+ NodePermissions getPermissionsForMachineFingerprint(const QUuid& machineFingerprint) const;
+
// these give access to permissions for specific groups from the domain-server settings page
bool havePermissionsForGroup(const QString& groupName, QUuid rankID) const {
return _groupPermissions.contains(groupName, rankID);
@@ -152,6 +157,7 @@ private:
NodePermissionsMap _ipPermissions; // permissions granted by node IP address
NodePermissionsMap _macPermissions; // permissions granted by node MAC address
+ NodePermissionsMap _machineFingerprintPermissions; // permissions granted by Machine Fingerprint
NodePermissionsMap _groupPermissions; // permissions granted by membership to specific groups
NodePermissionsMap _groupForbiddens; // permissions denied due to membership in a specific group
diff --git a/domain-server/src/NodeConnectionData.cpp b/domain-server/src/NodeConnectionData.cpp
index 93d6802d84..0a3782d79b 100644
--- a/domain-server/src/NodeConnectionData.cpp
+++ b/domain-server/src/NodeConnectionData.cpp
@@ -32,6 +32,9 @@ NodeConnectionData NodeConnectionData::fromDataStream(QDataStream& dataStream, c
// read the hardware address sent by the client
dataStream >> newHeader.hardwareAddress;
+
+ // now the machine fingerprint
+ dataStream >> newHeader.machineFingerprint;
}
dataStream >> newHeader.nodeType
diff --git a/domain-server/src/NodeConnectionData.h b/domain-server/src/NodeConnectionData.h
index bcbbdf0a40..dd9ca6b650 100644
--- a/domain-server/src/NodeConnectionData.h
+++ b/domain-server/src/NodeConnectionData.h
@@ -29,6 +29,7 @@ public:
QList interestList;
QString placeName;
QString hardwareAddress;
+ QUuid machineFingerprint;
QByteArray protocolVersion;
};
diff --git a/libraries/networking/CMakeLists.txt b/libraries/networking/CMakeLists.txt
index 8ef67cb2a4..288e98d5a5 100644
--- a/libraries/networking/CMakeLists.txt
+++ b/libraries/networking/CMakeLists.txt
@@ -17,6 +17,7 @@ find_package(TBB REQUIRED)
if (APPLE)
find_library(FRAMEWORK_IOKIT IOKit)
+ find_library(CORE_FOUNDATION CoreFoundation)
endif ()
if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include")
@@ -32,7 +33,7 @@ target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES} ${TBB_LIBRARIES})
# IOKit is needed for getting machine fingerprint
if (APPLE)
- target_link_libraries(${TARGET_NAME} ${FRAMEWORK_IOKIT})
+ target_link_libraries(${TARGET_NAME} ${FRAMEWORK_IOKIT} ${CORE_FOUNDATION})
endif (APPLE)
# libcrypto uses dlopen in libdl
diff --git a/libraries/networking/src/FingerprintUtils.cpp b/libraries/networking/src/FingerprintUtils.cpp
index 42a617e93d..70a2f8ab2c 100644
--- a/libraries/networking/src/FingerprintUtils.cpp
+++ b/libraries/networking/src/FingerprintUtils.cpp
@@ -10,8 +10,13 @@
//
#include "FingerprintUtils.h"
+
#include
+
#include
+#include
+#include
+
#ifdef Q_OS_WIN
#include
#include
@@ -44,7 +49,15 @@ QString FingerprintUtils::getMachineFingerprintString() {
HRESULT hres;
IWbemLocator *pLoc = NULL;
- // initialize com
+ // initialize com. Interface already does, but other
+ // users of this lib don't necessarily do so.
+ hres = CoInitializeEx(0, COINIT_MULTITHREADED);
+ if (FAILED(hres)) {
+ qDebug() << "Failed to initialize COM library!";
+ return uuidString;
+ }
+
+ // initialize WbemLocator
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
@@ -164,6 +177,11 @@ QUuid FingerprintUtils::getMachineFingerprint() {
// any errors in getting the string
QUuid uuid(uuidString);
if (uuid == QUuid()) {
+ // if you cannot read a fallback key cuz we aren't saving them, just generate one for
+ // this session and move on
+ if (DependencyManager::get().isNull()) {
+ return QUuid::createUuid();
+ }
// read fallback key (if any)
Settings settings;
uuid = QUuid(settings.value(FALLBACK_FINGERPRINT_KEY).toString());
diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp
index b464b14224..f76189b13a 100644
--- a/libraries/networking/src/NodeList.cpp
+++ b/libraries/networking/src/NodeList.cpp
@@ -27,6 +27,7 @@
#include "AddressManager.h"
#include "Assignment.h"
#include "HifiSockAddr.h"
+#include "FingerprintUtils.h"
#include "NetworkLogging.h"
#include "udt/PacketHeaders.h"
@@ -371,6 +372,10 @@ void NodeList::sendDomainServerCheckIn() {
}
packetStream << hardwareAddress;
+
+ // add in machine fingerprint
+ QUuid machineFingerprint = FingerprintUtils::getMachineFingerprint();
+ packetStream << machineFingerprint;
}
// pack our data to send to the domain-server including
diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp
index 98be26a5f6..d3347f8e53 100644
--- a/libraries/networking/src/udt/PacketHeaders.cpp
+++ b/libraries/networking/src/udt/PacketHeaders.cpp
@@ -67,7 +67,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
return static_cast(DomainConnectionDeniedVersion::IncludesExtraInfo);
case PacketType::DomainConnectRequest:
- return static_cast(DomainConnectRequestVersion::HasMACAddress);
+ return static_cast(DomainConnectRequestVersion::HasMachineFingerprint);
case PacketType::DomainServerAddedNode:
return static_cast(DomainServerAddedNodeVersion::PermissionsGrid);
diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h
index 73a5b885f0..4a16e8ffe8 100644
--- a/libraries/networking/src/udt/PacketHeaders.h
+++ b/libraries/networking/src/udt/PacketHeaders.h
@@ -212,7 +212,8 @@ enum class DomainConnectRequestVersion : PacketVersion {
NoHostname = 17,
HasHostname,
HasProtocolVersions,
- HasMACAddress
+ HasMACAddress,
+ HasMachineFingerprint
};
enum class DomainConnectionDeniedVersion : PacketVersion {