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 {