]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
netcmd: models: add GroupManagedServiceAccount model
authorRob van der Linde <rob@catalyst.net.nz>
Thu, 8 Feb 2024 10:25:14 +0000 (23:25 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Fri, 16 Feb 2024 02:41:36 +0000 (02:41 +0000)
Signed-off-by: Rob van der Linde <rob@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Jo Sutton <josutton@catalyst.net.nz>
python/samba/netcmd/domain/models/__init__.py
python/samba/netcmd/domain/models/types.py
python/samba/netcmd/domain/models/user.py

index f77dbc36b374726726f29a42cde3551bca4c7e96..575a108a3124aa91c05c0ca38e302793449743e6 100644 (file)
@@ -30,6 +30,7 @@ from .model import MODELS
 from .schema import AttributeSchema, ClassSchema
 from .site import Site
 from .subnet import Subnet
-from .types import AccountType, GroupType, SystemFlags, UserAccountControl
-from .user import User
+from .types import (AccountType, GroupType, SupportedEncryptionTypes,
+                    SystemFlags, UserAccountControl)
+from .user import User, GroupManagedServiceAccount
 from .value_type import ValueType
index 562225ee9c394b7177b588454d7b2517eeccad18..a03333a470c9bd155e369cee68eb5619ccac4fd8 100644 (file)
 
 from enum import IntFlag
 
+from samba.dcerpc.security import (
+    KERB_ENCTYPE_FAST_SUPPORTED,
+    KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED,
+    KERB_ENCTYPE_CLAIMS_SUPPORTED,
+    KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED
+)
 from samba.dsdb import (
     ATYPE_SECURITY_GLOBAL_GROUP,
     ATYPE_SECURITY_LOCAL_GROUP,
@@ -54,6 +60,13 @@ from samba.dsdb import (
     UF_NO_AUTH_DATA_REQUIRED,
     UF_PARTIAL_SECRETS_ACCOUNT,
     UF_USE_AES_KEYS,
+    ENC_ALL_TYPES,
+    ENC_CRC32,
+    ENC_RSA_MD5,
+    ENC_RC4_HMAC_MD5,
+    ENC_HMAC_SHA1_96_AES128,
+    ENC_HMAC_SHA1_96_AES256,
+    ENC_HMAC_SHA1_96_AES256_SK,
     GTYPE_DISTRIBUTION_GLOBAL_GROUP,
     GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP,
     GTYPE_DISTRIBUTION_UNIVERSAL_GROUP,
@@ -134,3 +147,17 @@ class UserAccountControl(IntFlag):
     NO_AUTH_DATA_REQUIRED = UF_NO_AUTH_DATA_REQUIRED
     PARTIAL_SECRETS_ACCOUNT = UF_PARTIAL_SECRETS_ACCOUNT
     USE_AES_KEYS = UF_USE_AES_KEYS
+
+
+class SupportedEncryptionTypes(IntFlag):
+    ALL_TYPES = ENC_ALL_TYPES
+    CRC32 = ENC_CRC32
+    RSA_MD5 = ENC_RSA_MD5
+    RC4_HMAC_MD5 = ENC_RC4_HMAC_MD5
+    HMAC_SHA1_96_AES128 = ENC_HMAC_SHA1_96_AES128
+    HMAC_SHA1_96_AES256 = ENC_HMAC_SHA1_96_AES256
+    HMAC_SHA1_96_AES256_SK = ENC_HMAC_SHA1_96_AES256_SK
+    FAST_SUPPORTED = KERB_ENCTYPE_FAST_SUPPORTED
+    COMPOUND_IDENTITY_SUPPORTED = KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED
+    CLAIMS_SUPPORTED = KERB_ENCTYPE_CLAIMS_SUPPORTED
+    RESOURCE_SID_COMPRESSION_DISABLED = KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED
index 719cb8eb6f4e64c10f998c65a1d5528338e439a4..1af9576f6430bde47adbb622794d3035e300b86d 100644 (file)
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-from ldb import Dn
+from ldb import FLAG_MOD_ADD, Dn
 
+from samba.dcerpc import security
 from samba.dsdb import (DS_GUID_MANAGED_SERVICE_ACCOUNTS_CONTAINER,
                         DS_GUID_USERS_CONTAINER)
+from samba.ndr import ndr_unpack
 
-from .fields import (DnField, EnumField, IntegerField, SIDField, StringField,
-                     NtTimeField)
+from .fields import (BinaryField, DnField, EnumField, IntegerField, SDDLField,
+                     SIDField, StringField, NtTimeField)
 from .model import Model
-from .types import AccountType, UserAccountControl
+from .types import AccountType, SupportedEncryptionTypes, UserAccountControl
 
 
 class User(Model):
@@ -91,3 +93,66 @@ class User(Model):
             query = {"username": name}
 
         return cls.get(ldb, **query)
+
+
+class GroupManagedServiceAccount(User):
+    """A GroupManagedServiceAccount is a type of User with additional fields."""
+    managed_password_interval = IntegerField("msDS-ManagedPasswordInterval")
+    dns_host_name = StringField("dNSHostName")
+    group_msa_membership = SDDLField("msDS-GroupMSAMembership")
+    managed_password_id = BinaryField("msDS-ManagedPasswordId",
+                                      readonly=True, hidden=True)
+    managed_password_previous_id = BinaryField("msDS-ManagedPasswordPreviousId",
+                                               readonly=True, hidden=True)
+    supported_encryption_types = EnumField("msDS-SupportedEncryptionTypes",
+                                           SupportedEncryptionTypes)
+
+    @staticmethod
+    def get_base_dn(ldb):
+        """Return base Dn for Managed Service Accounts.
+
+        :param ldb: Ldb connection
+        :return: Dn to use for searching
+        """
+        return ldb.get_wellknown_dn(ldb.get_default_basedn(),
+                                    DS_GUID_MANAGED_SERVICE_ACCOUNTS_CONTAINER)
+
+    @staticmethod
+    def get_object_class():
+        return "msDS-GroupManagedServiceAccount"
+
+    def trustees(self, ldb):
+        """Returns list of trustees from the msDS-GroupMSAMembership SDDL.
+
+        :return: list of User objects
+        """
+        users = []
+        field = self.fields["group_msa_membership"]
+        sddl = self.group_msa_membership
+        message = field.to_db_value(ldb, sddl, FLAG_MOD_ADD)
+        desc = ndr_unpack(security.descriptor, message[0])
+
+        for ace in desc.dacl.aces:
+            users.append(User.get(ldb, object_sid=ace.trustee))
+
+        return users
+
+    @classmethod
+    def find(cls, ldb, name):
+        """Helper function to find a service account first by Dn then username.
+
+        If the Dn can't be parsed use sAMAccountName, automatically add the $.
+        """
+        try:
+            query = {"dn": Dn(ldb, name)}
+        except ValueError:
+            if name.endswith("$"):
+                query = {"username": name}
+            else:
+                query = {"username": name + "$"}
+
+        return cls.get(ldb, **query)
+
+    @staticmethod
+    def group_sddl(group):
+        return f"O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;{group.object_sid})"