From b401502c55b34ea1d87e043fc6f8059bd55c95c8 Mon Sep 17 00:00:00 2001 From: Rob van der Linde Date: Thu, 8 Feb 2024 23:25:14 +1300 Subject: [PATCH] netcmd: models: add GroupManagedServiceAccount model Signed-off-by: Rob van der Linde Reviewed-by: Andrew Bartlett Reviewed-by: Jo Sutton --- python/samba/netcmd/domain/models/__init__.py | 5 +- python/samba/netcmd/domain/models/types.py | 27 +++++++ python/samba/netcmd/domain/models/user.py | 73 ++++++++++++++++++- 3 files changed, 99 insertions(+), 6 deletions(-) diff --git a/python/samba/netcmd/domain/models/__init__.py b/python/samba/netcmd/domain/models/__init__.py index f77dbc36b37..575a108a312 100644 --- a/python/samba/netcmd/domain/models/__init__.py +++ b/python/samba/netcmd/domain/models/__init__.py @@ -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 diff --git a/python/samba/netcmd/domain/models/types.py b/python/samba/netcmd/domain/models/types.py index 562225ee9c3..a03333a470c 100644 --- a/python/samba/netcmd/domain/models/types.py +++ b/python/samba/netcmd/domain/models/types.py @@ -22,6 +22,12 @@ 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 diff --git a/python/samba/netcmd/domain/models/user.py b/python/samba/netcmd/domain/models/user.py index 719cb8eb6f4..1af9576f643 100644 --- a/python/samba/netcmd/domain/models/user.py +++ b/python/samba/netcmd/domain/models/user.py @@ -20,15 +20,17 @@ # along with this program. If not, see . # -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})"