2018-12-12 03:40:43 +03:00
#!/usr/bin/env python3
2014-12-08 05:07:59 +03:00
# -*- coding: utf-8 -*-
# This tests the restrictions on userAccountControl that apply even if write access is permitted
#
# Copyright Samuel Cabrero 2014 <samuelcabrero@kernevil.me>
# Copyright Andrew Bartlett 2014 <abartlet@samba.org>
#
# Licenced under the GPLv3
#
import optparse
import sys
import unittest
import samba
import samba . getopt as options
import samba . tests
import ldb
sys . path . insert ( 0 , " bin/python " )
2015-03-16 19:22:01 +03:00
from samba . subunit . run import SubunitTestRunner
2014-12-08 05:07:59 +03:00
from samba . auth import system_session
from samba . samdb import SamDB
2023-08-25 02:12:34 +03:00
from samba . dcerpc import samr , security
2014-12-08 05:07:59 +03:00
from samba . credentials import Credentials
from samba . ndr import ndr_unpack , ndr_pack
2021-08-30 05:37:06 +03:00
from samba . tests import delete_force , DynamicTestCase
2014-12-08 05:07:59 +03:00
from samba import gensec , sd_utils
from samba . credentials import DONT_USE_KERBEROS
from ldb import SCOPE_SUBTREE , SCOPE_BASE , LdbError
from samba . dsdb import UF_SCRIPT , UF_ACCOUNTDISABLE , UF_00000004 , UF_HOMEDIR_REQUIRED , \
2018-07-30 09:19:05 +03:00
UF_LOCKOUT , UF_PASSWD_NOTREQD , UF_PASSWD_CANT_CHANGE , UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED , \
2014-12-08 05:07:59 +03:00
UF_TEMP_DUPLICATE_ACCOUNT , UF_NORMAL_ACCOUNT , UF_00000400 , UF_INTERDOMAIN_TRUST_ACCOUNT , \
UF_WORKSTATION_TRUST_ACCOUNT , UF_SERVER_TRUST_ACCOUNT , UF_00004000 , \
UF_00008000 , UF_DONT_EXPIRE_PASSWD , UF_MNS_LOGON_ACCOUNT , UF_SMARTCARD_REQUIRED , \
UF_TRUSTED_FOR_DELEGATION , UF_NOT_DELEGATED , UF_USE_DES_KEY_ONLY , UF_DONT_REQUIRE_PREAUTH , \
UF_PASSWORD_EXPIRED , UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION , UF_NO_AUTH_DATA_REQUIRED , \
UF_PARTIAL_SECRETS_ACCOUNT , UF_USE_AES_KEYS
2021-08-30 05:37:06 +03:00
from samba import dsdb
2014-12-08 05:07:59 +03:00
2016-03-11 05:17:43 +03:00
parser = optparse . OptionParser ( " user_account_control.py [options] <host> " )
2014-12-08 05:07:59 +03:00
sambaopts = options . SambaOptions ( parser )
parser . add_option_group ( sambaopts )
parser . add_option_group ( options . VersionOptions ( parser ) )
# use command line creds if available
credopts = options . CredentialsOptions ( parser )
parser . add_option_group ( credopts )
opts , args = parser . parse_args ( )
if len ( args ) < 1 :
parser . print_usage ( )
sys . exit ( 1 )
host = args [ 0 ]
2018-07-30 09:22:34 +03:00
if " :// " not in host :
2014-12-08 05:07:59 +03:00
ldaphost = " ldap:// %s " % host
else :
ldaphost = host
start = host . rindex ( " :// " )
2018-07-30 09:18:25 +03:00
host = host . lstrip ( start + 3 )
2014-12-08 05:07:59 +03:00
lp = sambaopts . get_loadparm ( )
creds = credopts . get_credentials ( lp )
creds . set_gensec_features ( creds . get_gensec_features ( ) | gensec . FEATURE_SEAL )
bits = [ UF_SCRIPT , UF_ACCOUNTDISABLE , UF_00000004 , UF_HOMEDIR_REQUIRED ,
2018-07-30 09:19:05 +03:00
UF_LOCKOUT , UF_PASSWD_NOTREQD , UF_PASSWD_CANT_CHANGE ,
2014-12-08 05:07:59 +03:00
UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED ,
UF_TEMP_DUPLICATE_ACCOUNT , UF_NORMAL_ACCOUNT , UF_00000400 ,
UF_INTERDOMAIN_TRUST_ACCOUNT ,
UF_WORKSTATION_TRUST_ACCOUNT , UF_SERVER_TRUST_ACCOUNT , UF_00004000 ,
UF_00008000 , UF_DONT_EXPIRE_PASSWD , UF_MNS_LOGON_ACCOUNT , UF_SMARTCARD_REQUIRED ,
UF_TRUSTED_FOR_DELEGATION , UF_NOT_DELEGATED , UF_USE_DES_KEY_ONLY ,
UF_DONT_REQUIRE_PREAUTH ,
UF_PASSWORD_EXPIRED , UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION ,
UF_NO_AUTH_DATA_REQUIRED ,
UF_PARTIAL_SECRETS_ACCOUNT , UF_USE_AES_KEYS ,
int ( " 0x10000000 " , 16 ) , int ( " 0x20000000 " , 16 ) , int ( " 0x40000000 " , 16 ) , int ( " 0x80000000 " , 16 ) ]
2021-08-30 09:17:47 +03:00
account_types = set ( [ UF_NORMAL_ACCOUNT , UF_WORKSTATION_TRUST_ACCOUNT , UF_SERVER_TRUST_ACCOUNT , UF_INTERDOMAIN_TRUST_ACCOUNT ] )
2015-01-06 06:49:14 +03:00
2014-12-08 05:07:59 +03:00
2021-08-30 05:37:06 +03:00
@DynamicTestCase
2014-12-08 05:07:59 +03:00
class UserAccountControlTests ( samba . tests . TestCase ) :
2021-08-30 05:37:06 +03:00
@classmethod
def setUpDynamicTestCases ( cls ) :
2021-10-22 05:42:08 +03:00
for priv in [ ( True , " priv " ) , ( False , " cc " ) ] :
for account_type in [ UF_NORMAL_ACCOUNT ,
UF_WORKSTATION_TRUST_ACCOUNT ,
UF_SERVER_TRUST_ACCOUNT ] :
account_type_str = dsdb . user_account_control_flag_bit_to_string ( account_type )
for objectclass in [ " computer " , " user " ] :
2023-02-24 02:57:57 +03:00
for name in [ ( " oc_uac_lock$ " , " withdollar " ) ,
2021-10-22 05:42:08 +03:00
( " oc_uac_lock " , " plain " ) ] :
test_name = f " { account_type_str } _ { objectclass } _ { priv [ 1 ] } _ { name [ 1 ] } "
cls . generate_dynamic_test ( " test_objectclass_uac_dollar_lock " ,
test_name ,
account_type ,
objectclass ,
name [ 0 ] ,
priv [ 0 ] )
for priv in [ ( True , " priv " ) , ( False , " wp " ) ] :
for account_type in [ UF_NORMAL_ACCOUNT ,
UF_WORKSTATION_TRUST_ACCOUNT ,
UF_SERVER_TRUST_ACCOUNT ] :
account_type_str = dsdb . user_account_control_flag_bit_to_string ( account_type )
for account_type2 in [ UF_NORMAL_ACCOUNT ,
UF_WORKSTATION_TRUST_ACCOUNT ,
UF_SERVER_TRUST_ACCOUNT ] :
for how in [ " replace " , " deladd " ] :
account_type2_str = dsdb . user_account_control_flag_bit_to_string ( account_type2 )
test_name = f " { account_type_str } _ { account_type2_str } _ { how } _ { priv [ 1 ] } "
cls . generate_dynamic_test ( " test_objectclass_uac_mod_lock " ,
test_name ,
account_type ,
account_type2 ,
how ,
priv [ 0 ] )
2021-10-29 13:33:32 +03:00
for objectclass in [ " computer " , " user " ] :
account_types = [ UF_NORMAL_ACCOUNT ]
if objectclass == " computer " :
account_types . append ( UF_WORKSTATION_TRUST_ACCOUNT )
account_types . append ( UF_SERVER_TRUST_ACCOUNT )
for account_type in account_types :
account_type_str = (
dsdb . user_account_control_flag_bit_to_string (
account_type ) )
for account_type2 in [ UF_NORMAL_ACCOUNT ,
UF_WORKSTATION_TRUST_ACCOUNT ,
UF_SERVER_TRUST_ACCOUNT ,
UF_PARTIAL_SECRETS_ACCOUNT ,
None ] :
if account_type2 is None :
account_type2_str = None
else :
account_type2_str = (
dsdb . user_account_control_flag_bit_to_string (
account_type2 ) )
for objectclass2 in [ " computer " , " user " , None ] :
for name2 in [ ( " oc_uac_lock " , " remove_dollar " ) ,
( None , " keep_dollar " ) ] :
test_name = ( f " { priv [ 1 ] } _ { objectclass } _ "
f " { account_type_str } _to_ "
f " { objectclass2 } _ "
f " { account_type2_str } _ "
f " { name2 [ 1 ] } " )
cls . generate_dynamic_test ( " test_mod_lock " ,
test_name ,
objectclass ,
objectclass2 ,
account_type ,
account_type2 ,
name2 [ 0 ] ,
priv [ 0 ] )
2021-09-13 01:21:03 +03:00
for account_type in [ UF_NORMAL_ACCOUNT ,
UF_WORKSTATION_TRUST_ACCOUNT ,
UF_SERVER_TRUST_ACCOUNT ] :
account_type_str = dsdb . user_account_control_flag_bit_to_string ( account_type )
for objectclass in [ " user " , " computer " ] :
for how in [ " replace " , " deladd " ] :
test_name = f " { account_type_str } _ { objectclass } _ { how } "
cls . generate_dynamic_test ( " test_objectclass_mod_lock " ,
test_name ,
account_type ,
objectclass ,
how )
2021-08-30 05:37:06 +03:00
for account_type in [ UF_NORMAL_ACCOUNT , UF_WORKSTATION_TRUST_ACCOUNT ] :
account_type_str = dsdb . user_account_control_flag_bit_to_string ( account_type )
cls . generate_dynamic_test ( " test_uac_bits_unrelated_modify " ,
account_type_str , account_type )
2021-08-30 05:51:27 +03:00
for bit in bits :
try :
bit_str = dsdb . user_account_control_flag_bit_to_string ( bit )
except KeyError :
bit_str = hex ( bit )
cls . generate_dynamic_test ( " test_uac_bits_add " ,
bit_str , bit , bit_str )
2021-08-30 05:54:39 +03:00
cls . generate_dynamic_test ( " test_uac_bits_set " ,
bit_str , bit , bit_str )
2021-08-30 09:17:47 +03:00
cls . generate_dynamic_test ( " test_uac_bits_add " ,
" UF_NORMAL_ACCOUNT_UF_PASSWD_NOTREQD " ,
UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD ,
" UF_NORMAL_ACCOUNT|UF_PASSWD_NOTREQD " )
2021-08-30 05:51:27 +03:00
2014-12-08 05:07:59 +03:00
def add_computer_ldap ( self , computername , others = None , samdb = None ) :
if samdb is None :
samdb = self . samdb
2021-08-30 01:07:31 +03:00
dn = " CN= %s , %s " % ( computername , self . OU )
2014-12-08 05:07:59 +03:00
samaccountname = " %s $ " % computername
msg_dict = {
" dn " : dn ,
" objectclass " : " computer " }
if others is not None :
2018-11-19 18:04:21 +03:00
msg_dict = dict ( list ( msg_dict . items ( ) ) + list ( others . items ( ) ) )
2014-12-08 05:07:59 +03:00
2018-07-30 09:17:02 +03:00
msg = ldb . Message . from_dict ( self . samdb , msg_dict )
2014-12-08 05:07:59 +03:00
msg [ " sAMAccountName " ] = samaccountname
2018-03-09 16:57:01 +03:00
print ( " Adding computer account %s " % computername )
2014-12-08 05:07:59 +03:00
samdb . add ( msg )
2021-10-22 06:18:51 +03:00
def add_user_ldap ( self , username , others = None , samdb = None ) :
if samdb is None :
samdb = self . samdb
dn = " CN= %s , %s " % ( username , self . OU )
samaccountname = " %s " % username
msg_dict = {
" dn " : dn ,
" objectclass " : " user " }
if others is not None :
msg_dict = dict ( list ( msg_dict . items ( ) ) + list ( others . items ( ) ) )
msg = ldb . Message . from_dict ( self . samdb , msg_dict )
msg [ " sAMAccountName " ] = samaccountname
print ( " Adding user account %s " % username )
samdb . add ( msg )
2014-12-08 05:07:59 +03:00
def get_creds ( self , target_username , target_password ) :
creds_tmp = Credentials ( )
creds_tmp . set_username ( target_username )
creds_tmp . set_password ( target_password )
creds_tmp . set_domain ( creds . get_domain ( ) )
creds_tmp . set_realm ( creds . get_realm ( ) )
creds_tmp . set_workstation ( creds . get_workstation ( ) )
creds_tmp . set_gensec_features ( creds_tmp . get_gensec_features ( )
| gensec . FEATURE_SEAL )
2018-07-30 09:19:33 +03:00
creds_tmp . set_kerberos_state ( DONT_USE_KERBEROS ) # kinit is too expensive to use in a tight loop
2014-12-08 05:07:59 +03:00
return creds_tmp
def setUp ( self ) :
super ( UserAccountControlTests , self ) . setUp ( )
self . admin_creds = creds
self . admin_samdb = SamDB ( url = ldaphost ,
session_info = system_session ( ) ,
credentials = self . admin_creds , lp = lp )
2016-06-22 16:08:43 +03:00
self . domain_sid = security . dom_sid ( self . admin_samdb . get_domain_sid ( ) )
self . base_dn = self . admin_samdb . domain_dn ( )
2014-12-08 05:07:59 +03:00
self . unpriv_user = " testuser1 "
self . unpriv_user_pw = " samba123@ "
self . unpriv_creds = self . get_creds ( self . unpriv_user , self . unpriv_user_pw )
2021-08-30 01:07:31 +03:00
self . OU = " OU=test_computer_ou1, %s " % ( self . base_dn )
delete_force ( self . admin_samdb , self . OU , controls = [ " tree_delete:0 " ] )
2016-06-22 16:08:43 +03:00
delete_force ( self . admin_samdb , " CN= %s ,CN=Users, %s " % ( self . unpriv_user , self . base_dn ) )
2014-12-08 05:07:59 +03:00
self . admin_samdb . newuser ( self . unpriv_user , self . unpriv_user_pw )
res = self . admin_samdb . search ( " CN= %s ,CN=Users, %s " % ( self . unpriv_user , self . admin_samdb . domain_dn ( ) ) ,
scope = SCOPE_BASE ,
attrs = [ " objectSid " ] )
self . assertEqual ( 1 , len ( res ) )
self . unpriv_user_sid = ndr_unpack ( security . dom_sid , res [ 0 ] [ " objectSid " ] [ 0 ] )
self . unpriv_user_dn = res [ 0 ] . dn
2021-08-30 01:10:56 +03:00
self . addCleanup ( self . admin_samdb . delete , self . unpriv_user_dn )
2014-12-08 05:07:59 +03:00
self . samdb = SamDB ( url = ldaphost , credentials = self . unpriv_creds , lp = lp )
2016-06-22 16:08:43 +03:00
self . samr = samr . samr ( " ncacn_ip_tcp: %s [seal] " % host , lp , self . unpriv_creds )
2014-12-08 05:07:59 +03:00
self . samr_handle = self . samr . Connect2 ( None , security . SEC_FLAG_MAXIMUM_ALLOWED )
self . samr_domain = self . samr . OpenDomain ( self . samr_handle , security . SEC_FLAG_MAXIMUM_ALLOWED , self . domain_sid )
self . sd_utils = sd_utils . SDUtils ( self . admin_samdb )
2021-08-30 01:07:31 +03:00
self . admin_samdb . create_ou ( self . OU )
2021-08-30 01:10:56 +03:00
self . addCleanup ( self . admin_samdb . delete , self . OU , [ " tree_delete:1 " ] )
2014-12-08 05:07:59 +03:00
self . unpriv_user_sid = self . sd_utils . get_object_sid ( self . unpriv_user_dn )
mod = " (OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;; %s ) " % str ( self . unpriv_user_sid )
2021-08-30 01:07:31 +03:00
old_sd = self . sd_utils . read_sd_on_dn ( self . OU )
2014-12-08 05:07:59 +03:00
2021-08-30 01:07:31 +03:00
self . sd_utils . dacl_add_ace ( self . OU , mod )
2014-12-08 05:07:59 +03:00
self . add_computer_ldap ( " testcomputer-t " )
2021-08-30 01:07:31 +03:00
self . sd_utils . modify_sd_on_dn ( self . OU , old_sd )
2014-12-08 05:07:59 +03:00
self . computernames = [ " testcomputer-0 " ]
# Get the SD of the template account, then force it to match
# what we expect for SeMachineAccountPrivilege accounts, so we
# can confirm we created the accounts correctly
2021-08-30 01:07:31 +03:00
self . sd_reference_cc = self . sd_utils . read_sd_on_dn ( " CN=testcomputer-t, %s " % ( self . OU ) )
2014-12-08 05:07:59 +03:00
2021-08-30 01:07:31 +03:00
self . sd_reference_modify = self . sd_utils . read_sd_on_dn ( " CN=testcomputer-t, %s " % ( self . OU ) )
2014-12-08 05:07:59 +03:00
for ace in self . sd_reference_modify . dacl . aces :
if ace . type == security . SEC_ACE_TYPE_ACCESS_ALLOWED and ace . trustee == self . unpriv_user_sid :
ace . access_mask = ace . access_mask | security . SEC_ADS_SELF_WRITE | security . SEC_ADS_WRITE_PROP
# Now reconnect without domain admin rights
self . samdb = SamDB ( url = ldaphost , credentials = self . unpriv_creds , lp = lp )
def test_add_computer_sd_cc ( self ) :
user_sid = self . sd_utils . get_object_sid ( self . unpriv_user_dn )
2021-10-22 21:10:35 +03:00
mod = f " (OA;CI;WDCC; { dsdb . DS_GUID_SCHEMA_CLASS_COMPUTER } ;; { user_sid } ) "
2014-12-08 05:07:59 +03:00
2021-08-30 01:07:31 +03:00
self . sd_utils . dacl_add_ace ( self . OU , mod )
2014-12-08 05:07:59 +03:00
2018-07-30 09:18:03 +03:00
computername = self . computernames [ 0 ]
2014-12-08 05:07:59 +03:00
sd = ldb . MessageElement ( ( ndr_pack ( self . sd_reference_modify ) ) ,
ldb . FLAG_MOD_ADD ,
" nTSecurityDescriptor " )
2021-10-25 13:10:56 +03:00
try :
self . add_computer_ldap ( computername ,
others = { " nTSecurityDescriptor " : sd } )
except LdbError as e :
self . fail ( str ( e ) )
2014-12-08 05:07:59 +03:00
res = self . admin_samdb . search ( " %s " % self . base_dn ,
expression = " (&(objectClass=computer)(samAccountName= %s $)) " % computername ,
scope = SCOPE_SUBTREE ,
attrs = [ " ntSecurityDescriptor " ] )
desc = res [ 0 ] [ " nTSecurityDescriptor " ] [ 0 ]
desc = ndr_unpack ( security . descriptor , desc , allow_remaining = True )
sddl = desc . as_sddl ( self . domain_sid )
self . assertEqual ( self . sd_reference_modify . as_sddl ( self . domain_sid ) , sddl )
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
2018-07-30 09:18:03 +03:00
m [ " description " ] = ldb . MessageElement (
2014-12-08 05:07:59 +03:00
( " A description " ) , ldb . FLAG_MOD_REPLACE ,
" description " )
self . samdb . modify ( m )
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
m [ " userAccountControl " ] = ldb . MessageElement ( str ( samba . dsdb . UF_SERVER_TRUST_ACCOUNT ) ,
ldb . FLAG_MOD_REPLACE , " userAccountControl " )
2021-09-16 07:09:24 +03:00
self . assertRaisesLdbError ( ldb . ERR_INSUFFICIENT_ACCESS_RIGHTS ,
f " Unexpectedly able to set userAccountControl to be a DC on { m . dn } " ,
self . samdb . modify , m )
2014-12-08 05:07:59 +03:00
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
2018-08-22 09:26:09 +03:00
m [ " userAccountControl " ] = ldb . MessageElement ( str ( samba . dsdb . UF_WORKSTATION_TRUST_ACCOUNT |
samba . dsdb . UF_PARTIAL_SECRETS_ACCOUNT ) ,
2014-12-08 05:07:59 +03:00
ldb . FLAG_MOD_REPLACE , " userAccountControl " )
2021-09-16 07:09:24 +03:00
self . assertRaisesLdbError ( ldb . ERR_INSUFFICIENT_ACCESS_RIGHTS ,
f " Unexpectedly able to set userAccountControl to be a RODC on { m . dn } " ,
self . samdb . modify , m )
2014-12-08 05:07:59 +03:00
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
m [ " userAccountControl " ] = ldb . MessageElement ( str ( samba . dsdb . UF_WORKSTATION_TRUST_ACCOUNT ) ,
ldb . FLAG_MOD_REPLACE , " userAccountControl " )
2021-09-16 07:09:24 +03:00
self . assertRaisesLdbError ( ldb . ERR_INSUFFICIENT_ACCESS_RIGHTS ,
f " Unexpectedly able to set userAccountControl to be a Workstation on { m . dn } " ,
self . samdb . modify , m )
2015-11-18 07:36:21 +03:00
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
2021-08-30 09:17:47 +03:00
m [ " userAccountControl " ] = ldb . MessageElement ( str ( samba . dsdb . UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD ) ,
2015-11-18 07:36:21 +03:00
ldb . FLAG_MOD_REPLACE , " userAccountControl " )
2021-09-17 04:41:40 +03:00
try :
self . samdb . modify ( m )
except LdbError as e :
( enum , estr ) = e . args
self . fail ( f " got { estr } setting userAccountControl to UF_NORMAL_ACCOUNT|UF_PASSWD_NOTREQD " )
2014-12-08 05:07:59 +03:00
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
m [ " primaryGroupID " ] = ldb . MessageElement ( str ( security . DOMAIN_RID_ADMINS ) ,
ldb . FLAG_MOD_REPLACE , " primaryGroupID " )
2021-09-16 07:09:24 +03:00
self . assertRaisesLdbError ( ldb . ERR_UNWILLING_TO_PERFORM ,
f " Unexpectedly able to set primaryGroupID on { m . dn } " ,
self . samdb . modify , m )
2014-12-08 05:07:59 +03:00
def test_mod_computer_cc ( self ) :
user_sid = self . sd_utils . get_object_sid ( self . unpriv_user_dn )
mod = " (OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;; %s ) " % str ( user_sid )
2021-08-30 01:07:31 +03:00
self . sd_utils . dacl_add_ace ( self . OU , mod )
2014-12-08 05:07:59 +03:00
2018-07-30 09:18:03 +03:00
computername = self . computernames [ 0 ]
2014-12-08 05:07:59 +03:00
self . add_computer_ldap ( computername )
res = self . admin_samdb . search ( " %s " % self . base_dn ,
expression = " (&(objectClass=computer)(samAccountName= %s $)) " % computername ,
scope = SCOPE_SUBTREE ,
attrs = [ ] )
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
2018-07-30 09:18:03 +03:00
m [ " description " ] = ldb . MessageElement (
2014-12-08 05:07:59 +03:00
( " A description " ) , ldb . FLAG_MOD_REPLACE ,
" description " )
self . samdb . modify ( m )
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
2018-08-22 09:26:09 +03:00
m [ " userAccountControl " ] = ldb . MessageElement ( str ( samba . dsdb . UF_WORKSTATION_TRUST_ACCOUNT |
samba . dsdb . UF_PARTIAL_SECRETS_ACCOUNT ) ,
2014-12-08 05:07:59 +03:00
ldb . FLAG_MOD_REPLACE , " userAccountControl " )
2021-09-16 07:09:24 +03:00
self . assertRaisesLdbError ( ldb . ERR_INSUFFICIENT_ACCESS_RIGHTS ,
f " Unexpectedly able to set userAccountControl as RODC on { m . dn } " ,
self . samdb . modify , m )
2014-12-08 05:07:59 +03:00
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
m [ " userAccountControl " ] = ldb . MessageElement ( str ( samba . dsdb . UF_SERVER_TRUST_ACCOUNT ) ,
ldb . FLAG_MOD_REPLACE , " userAccountControl " )
2021-09-16 07:09:24 +03:00
self . assertRaisesLdbError ( ldb . ERR_INSUFFICIENT_ACCESS_RIGHTS ,
f " Unexpectedly able to set userAccountControl as DC on { m . dn } " ,
self . samdb . modify , m )
2021-08-30 09:17:47 +03:00
2014-12-08 05:07:59 +03:00
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
2021-08-30 09:17:47 +03:00
m [ " userAccountControl " ] = ldb . MessageElement ( str ( samba . dsdb . UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD ) ,
2014-12-08 05:07:59 +03:00
ldb . FLAG_MOD_REPLACE , " userAccountControl " )
2021-10-22 13:41:23 +03:00
self . assertRaisesLdbError ( ldb . ERR_OBJECT_CLASS_VIOLATION ,
f " Unexpectedly able to set userAccountControl as to UF_NORMAL_ACCOUNT|UF_PASSWD_NOTREQD on { m . dn } " ,
self . samdb . modify , m )
2014-12-08 05:07:59 +03:00
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
m [ " userAccountControl " ] = ldb . MessageElement ( str ( samba . dsdb . UF_WORKSTATION_TRUST_ACCOUNT ) ,
ldb . FLAG_MOD_REPLACE , " userAccountControl " )
2021-09-16 07:09:24 +03:00
self . assertRaisesLdbError ( ldb . ERR_INSUFFICIENT_ACCESS_RIGHTS ,
f " Unexpectedly able to set userAccountControl to be a workstation on { m . dn } " ,
self . samdb . modify , m )
2015-11-18 07:36:21 +03:00
2021-08-30 09:17:47 +03:00
def test_add_computer_cc_normal_bare ( self ) :
user_sid = self . sd_utils . get_object_sid ( self . unpriv_user_dn )
2021-10-22 21:10:35 +03:00
mod = f " (OA;CI;CC; { dsdb . DS_GUID_SCHEMA_CLASS_COMPUTER } ;; { user_sid } ) "
2021-08-30 09:17:47 +03:00
self . sd_utils . dacl_add_ace ( self . OU , mod )
computername = self . computernames [ 0 ]
sd = ldb . MessageElement ( ( ndr_pack ( self . sd_reference_modify ) ) ,
ldb . FLAG_MOD_ADD ,
" nTSecurityDescriptor " )
2021-10-25 13:10:56 +03:00
try :
self . add_computer_ldap ( computername ,
others = { " nTSecurityDescriptor " : sd } )
except LdbError as e :
self . fail ( str ( e ) )
2021-08-30 09:17:47 +03:00
res = self . admin_samdb . search ( " %s " % self . base_dn ,
expression = " (&(objectClass=computer)(samAccountName= %s $)) " % computername ,
scope = SCOPE_SUBTREE ,
attrs = [ " ntSecurityDescriptor " ] )
desc = res [ 0 ] [ " nTSecurityDescriptor " ] [ 0 ]
desc = ndr_unpack ( security . descriptor , desc , allow_remaining = True )
sddl = desc . as_sddl ( self . domain_sid )
self . assertEqual ( self . sd_reference_modify . as_sddl ( self . domain_sid ) , sddl )
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
m [ " description " ] = ldb . MessageElement (
( " A description " ) , ldb . FLAG_MOD_REPLACE ,
" description " )
self . samdb . modify ( m )
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
m [ " userAccountControl " ] = ldb . MessageElement ( str ( samba . dsdb . UF_NORMAL_ACCOUNT ) ,
ldb . FLAG_MOD_REPLACE , " userAccountControl " )
2021-10-22 12:54:52 +03:00
self . assertRaisesLdbError ( [ ldb . ERR_OBJECT_CLASS_VIOLATION ,
ldb . ERR_UNWILLING_TO_PERFORM ] ,
2023-10-02 04:25:52 +03:00
" Unexpectedly able to set userAccountControl to be a Normal "
" account without |UF_PASSWD_NOTREQD " ,
2021-09-16 07:09:24 +03:00
self . samdb . modify , m )
2021-08-30 09:17:47 +03:00
2014-12-08 05:07:59 +03:00
def test_admin_mod_uac ( self ) :
2018-07-30 09:18:03 +03:00
computername = self . computernames [ 0 ]
2014-12-08 05:07:59 +03:00
self . add_computer_ldap ( computername , samdb = self . admin_samdb )
res = self . admin_samdb . search ( " %s " % self . base_dn ,
expression = " (&(objectClass=computer)(samAccountName= %s $)) " % computername ,
scope = SCOPE_SUBTREE ,
attrs = [ " userAccountControl " ] )
2021-10-22 13:41:23 +03:00
self . assertEqual ( int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] ) , ( UF_WORKSTATION_TRUST_ACCOUNT |
2018-08-22 09:26:09 +03:00
UF_ACCOUNTDISABLE |
UF_PASSWD_NOTREQD ) )
2014-12-08 05:07:59 +03:00
2014-12-10 04:15:54 +03:00
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
2018-08-22 09:26:09 +03:00
m [ " userAccountControl " ] = ldb . MessageElement ( str ( UF_WORKSTATION_TRUST_ACCOUNT |
UF_PARTIAL_SECRETS_ACCOUNT |
UF_TRUSTED_FOR_DELEGATION ) ,
2014-12-10 04:15:54 +03:00
ldb . FLAG_MOD_REPLACE , " userAccountControl " )
2021-09-16 07:09:24 +03:00
self . assertRaisesLdbError ( ldb . ERR_OTHER ,
2023-10-02 04:25:52 +03:00
" Unexpectedly able to set userAccountControl to "
2021-09-16 07:09:24 +03:00
" UF_WORKSTATION_TRUST_ACCOUNT|UF_PARTIAL_SECRETS_ACCOUNT| "
2023-10-02 04:25:52 +03:00
f " UF_TRUSTED_FOR_DELEGATION on { m . dn } " ,
2021-09-16 07:09:24 +03:00
self . admin_samdb . modify , m )
2014-12-10 04:15:54 +03:00
2014-12-08 05:07:59 +03:00
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
2018-08-22 09:26:09 +03:00
m [ " userAccountControl " ] = ldb . MessageElement ( str ( UF_WORKSTATION_TRUST_ACCOUNT |
UF_PARTIAL_SECRETS_ACCOUNT ) ,
2014-12-08 05:07:59 +03:00
ldb . FLAG_MOD_REPLACE , " userAccountControl " )
self . admin_samdb . modify ( m )
res = self . admin_samdb . search ( " %s " % self . base_dn ,
expression = " (&(objectClass=computer)(samAccountName= %s $)) " % computername ,
scope = SCOPE_SUBTREE ,
attrs = [ " userAccountControl " ] )
2018-08-22 09:26:09 +03:00
self . assertEqual ( int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] ) , ( UF_WORKSTATION_TRUST_ACCOUNT |
UF_PARTIAL_SECRETS_ACCOUNT ) )
2014-12-08 05:07:59 +03:00
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
m [ " userAccountControl " ] = ldb . MessageElement ( str ( UF_ACCOUNTDISABLE ) ,
ldb . FLAG_MOD_REPLACE , " userAccountControl " )
2021-09-17 04:41:40 +03:00
try :
self . admin_samdb . modify ( m )
except LdbError as e :
( enum , estr ) = e . args
self . fail ( f " got { estr } setting userAccountControl to UF_ACCOUNTDISABLE (as admin) " )
2014-12-08 05:07:59 +03:00
res = self . admin_samdb . search ( " %s " % self . base_dn ,
expression = " (&(objectClass=computer)(samAccountName= %s $)) " % computername ,
scope = SCOPE_SUBTREE ,
attrs = [ " userAccountControl " ] )
2018-07-30 09:18:03 +03:00
self . assertEqual ( int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] ) , UF_NORMAL_ACCOUNT | UF_ACCOUNTDISABLE )
2014-12-08 05:07:59 +03:00
2021-08-30 05:54:39 +03:00
def _test_uac_bits_set_with_args ( self , bit , bit_str ) :
2014-12-08 05:07:59 +03:00
user_sid = self . sd_utils . get_object_sid ( self . unpriv_user_dn )
2021-10-22 06:18:51 +03:00
# Allow the creation of any children and write to any
# attributes (this is not a test of ACLs, this is a test of
# non-ACL userAccountControl rules
mod = f " (OA;CI;WP;;; { user_sid } )(OA;;CC;;; { user_sid } ) "
2014-12-08 05:07:59 +03:00
2021-08-30 01:07:31 +03:00
self . sd_utils . dacl_add_ace ( self . OU , mod )
2014-12-08 05:07:59 +03:00
2021-10-22 06:18:51 +03:00
# We want to start with UF_NORMAL_ACCOUNT, so we make a user
2018-07-30 09:18:03 +03:00
computername = self . computernames [ 0 ]
2021-10-22 06:18:51 +03:00
self . add_user_ldap ( computername )
2014-12-08 05:07:59 +03:00
res = self . admin_samdb . search ( " %s " % self . base_dn ,
2021-10-22 06:18:51 +03:00
expression = " (&(objectClass=user)(cn= %s )) " % computername ,
2014-12-08 05:07:59 +03:00
scope = SCOPE_SUBTREE ,
attrs = [ ] )
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
2018-07-30 09:18:03 +03:00
m [ " description " ] = ldb . MessageElement (
2014-12-08 05:07:59 +03:00
( " A description " ) , ldb . FLAG_MOD_REPLACE ,
" description " )
self . samdb . modify ( m )
# These bits are privileged, but authenticated users have that CAR by default, so this is a pain to test
priv_to_auth_users_bits = set ( [ UF_PASSWD_NOTREQD , UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED ,
UF_DONT_EXPIRE_PASSWD ] )
2015-11-18 07:36:21 +03:00
# These bits really are privileged, or can't be changed from UF_NORMAL as a non-admin
2014-12-08 05:07:59 +03:00
priv_bits = set ( [ UF_INTERDOMAIN_TRUST_ACCOUNT , UF_SERVER_TRUST_ACCOUNT ,
2015-11-18 07:36:21 +03:00
UF_TRUSTED_FOR_DELEGATION , UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION ,
UF_WORKSTATION_TRUST_ACCOUNT ] )
2014-12-08 05:07:59 +03:00
2014-12-10 04:15:54 +03:00
invalid_bits = set ( [ UF_TEMP_DUPLICATE_ACCOUNT , UF_PARTIAL_SECRETS_ACCOUNT ] )
2014-12-08 05:07:59 +03:00
2021-08-30 05:54:39 +03:00
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
m [ " userAccountControl " ] = ldb . MessageElement ( str ( bit | UF_PASSWD_NOTREQD ) ,
ldb . FLAG_MOD_REPLACE , " userAccountControl " )
try :
self . samdb . modify ( m )
if ( bit in priv_bits ) :
self . fail ( " Unexpectedly able to set userAccountControl bit 0x %08X ( %s ), on %s "
% ( bit , bit_str , m . dn ) )
2021-10-22 12:40:06 +03:00
if ( bit in account_types and bit != UF_NORMAL_ACCOUNT ) :
self . fail ( " Unexpectedly able to set userAccountControl bit 0x %08X ( %s ), on %s "
% ( bit , bit_str , m . dn ) )
2021-08-30 05:54:39 +03:00
except LdbError as e :
( enum , estr ) = e . args
if bit in invalid_bits :
self . assertEqual ( enum ,
ldb . ERR_OTHER ,
" was not able to set 0x %08X ( %s ) on %s "
% ( bit , bit_str , m . dn ) )
2021-10-22 12:40:06 +03:00
elif ( bit in account_types ) :
self . assertIn ( enum , [ ldb . ERR_OBJECT_CLASS_VIOLATION , ldb . ERR_INSUFFICIENT_ACCESS_RIGHTS ] )
2021-08-30 05:54:39 +03:00
elif ( bit in priv_bits ) :
self . assertEqual ( ldb . ERR_INSUFFICIENT_ACCESS_RIGHTS , enum )
else :
self . fail ( " Unable to set userAccountControl bit 0x %08X ( %s ) on %s : %s "
% ( bit , bit_str , m . dn , estr ) )
2014-12-08 05:07:59 +03:00
2021-08-30 05:37:06 +03:00
def _test_uac_bits_unrelated_modify_with_args ( self , account_type ) :
2014-12-08 05:07:59 +03:00
user_sid = self . sd_utils . get_object_sid ( self . unpriv_user_dn )
2021-10-22 06:18:51 +03:00
# Allow the creation of any children and write to any
# attributes (this is not a test of ACLs, this is a test of
# non-ACL userAccountControl rules
mod = f " (OA;CI;WP;;; { user_sid } )(OA;;CC;;; { user_sid } ) "
2014-12-08 05:07:59 +03:00
2021-08-30 01:07:31 +03:00
self . sd_utils . dacl_add_ace ( self . OU , mod )
2014-12-08 05:07:59 +03:00
2018-07-30 09:18:03 +03:00
computername = self . computernames [ 0 ]
2021-08-30 09:17:47 +03:00
if account_type == UF_WORKSTATION_TRUST_ACCOUNT :
self . add_computer_ldap ( computername )
2021-10-22 06:18:51 +03:00
else :
self . add_user_ldap ( computername )
2014-12-08 05:07:59 +03:00
2021-08-30 09:17:47 +03:00
res = self . admin_samdb . search ( self . OU ,
2021-10-22 06:18:51 +03:00
expression = f " (&(objectclass=user)(cn= { computername } )) " ,
2014-12-08 05:07:59 +03:00
scope = SCOPE_SUBTREE ,
2015-01-06 06:49:14 +03:00
attrs = [ " userAccountControl " ] )
2021-08-30 09:17:47 +03:00
self . assertEqual ( len ( res ) , 1 )
orig_uac = int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] )
2021-10-22 06:18:51 +03:00
self . assertEqual ( orig_uac & account_type ,
account_type )
2014-12-08 05:07:59 +03:00
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
2018-07-30 09:18:03 +03:00
m [ " description " ] = ldb . MessageElement (
2014-12-08 05:07:59 +03:00
( " A description " ) , ldb . FLAG_MOD_REPLACE ,
" description " )
self . samdb . modify ( m )
2014-12-10 04:15:54 +03:00
invalid_bits = set ( [ UF_TEMP_DUPLICATE_ACCOUNT , UF_PARTIAL_SECRETS_ACCOUNT ] )
2014-12-08 05:07:59 +03:00
2015-01-06 06:49:14 +03:00
# UF_LOCKOUT isn't actually ignored, it changes other
# attributes but does not stick here. See MS-SAMR 2.2.1.13
# UF_FLAG Codes clarification that UF_SCRIPT and
# UF_PASSWD_CANT_CHANGE are simply ignored by both clients and
# servers. Other bits are ignored as they are undefined, or
# are not set into the attribute (instead triggering other
# events).
ignored_bits = set ( [ UF_SCRIPT , UF_00000004 , UF_LOCKOUT , UF_PASSWD_CANT_CHANGE ,
UF_00000400 , UF_00004000 , UF_00008000 , UF_PASSWORD_EXPIRED ,
int ( " 0x10000000 " , 16 ) , int ( " 0x20000000 " , 16 ) , int ( " 0x40000000 " , 16 ) , int ( " 0x80000000 " , 16 ) ] )
2014-12-08 05:07:59 +03:00
super_priv_bits = set ( [ UF_INTERDOMAIN_TRUST_ACCOUNT ] )
2015-11-18 07:36:21 +03:00
priv_to_remove_bits = set ( [ UF_TRUSTED_FOR_DELEGATION , UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION , UF_WORKSTATION_TRUST_ACCOUNT ] )
2014-12-08 05:07:59 +03:00
for bit in bits :
2015-01-06 06:49:14 +03:00
# Reset this to the initial position, just to be sure
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
2021-08-30 09:17:47 +03:00
m [ " userAccountControl " ] = ldb . MessageElement ( str ( orig_uac ) ,
2015-01-06 06:49:14 +03:00
ldb . FLAG_MOD_REPLACE , " userAccountControl " )
2021-09-17 04:41:40 +03:00
try :
self . admin_samdb . modify ( m )
except LdbError as e :
( enum , estr ) = e . args
self . fail ( f " got { estr } resetting userAccountControl to initial value { orig_uac : #08x } " )
2015-01-06 06:49:14 +03:00
res = self . admin_samdb . search ( " %s " % self . base_dn ,
2021-10-22 06:18:51 +03:00
expression = " (&(objectClass=user)(cn= %s )) " % computername ,
2015-01-06 06:49:14 +03:00
scope = SCOPE_SUBTREE ,
attrs = [ " userAccountControl " ] )
2021-10-22 13:41:23 +03:00
self . assertEqual ( len ( res ) , 1 )
reset_uac = int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] )
self . assertEqual ( orig_uac , reset_uac )
2015-01-06 06:49:14 +03:00
2014-12-08 05:07:59 +03:00
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
2018-08-22 09:26:09 +03:00
m [ " userAccountControl " ] = ldb . MessageElement ( str ( bit | UF_PASSWD_NOTREQD ) ,
2014-12-08 05:07:59 +03:00
ldb . FLAG_MOD_REPLACE , " userAccountControl " )
try :
self . admin_samdb . modify ( m )
2021-08-30 09:17:47 +03:00
2014-12-08 05:07:59 +03:00
if bit in invalid_bits :
self . fail ( " Should have been unable to set userAccountControl bit 0x %08X on %s " % ( bit , m . dn ) )
2018-02-23 17:34:23 +03:00
except LdbError as e1 :
( enum , estr ) = e1 . args
2014-12-08 05:07:59 +03:00
if bit in invalid_bits :
self . assertEqual ( enum , ldb . ERR_OTHER )
# No point going on, try the next bit
continue
elif bit in super_priv_bits :
2021-10-22 13:41:23 +03:00
self . assertIn ( enum , ( ldb . ERR_INSUFFICIENT_ACCESS_RIGHTS ,
ldb . ERR_OBJECT_CLASS_VIOLATION ) )
2014-12-08 05:07:59 +03:00
# No point going on, try the next bit
continue
2021-08-30 09:17:47 +03:00
elif ( account_type == UF_NORMAL_ACCOUNT ) \
and ( bit in account_types ) \
and ( bit != account_type ) :
2021-10-22 13:41:23 +03:00
self . assertIn ( enum , ( ldb . ERR_UNWILLING_TO_PERFORM ,
ldb . ERR_OBJECT_CLASS_VIOLATION ) )
2021-08-30 09:17:47 +03:00
continue
elif ( account_type == UF_WORKSTATION_TRUST_ACCOUNT ) \
and ( bit != UF_NORMAL_ACCOUNT ) \
and ( bit != account_type ) :
self . assertEqual ( enum , ldb . ERR_UNWILLING_TO_PERFORM )
continue
2014-12-08 05:07:59 +03:00
else :
self . fail ( " Unable to set userAccountControl bit 0x %08X on %s : %s " % ( bit , m . dn , estr ) )
2015-01-06 06:49:14 +03:00
res = self . admin_samdb . search ( " %s " % self . base_dn ,
2021-10-22 06:18:51 +03:00
expression = " (&(objectClass=user)(cn= %s )) " % computername ,
2015-01-06 06:49:14 +03:00
scope = SCOPE_SUBTREE ,
attrs = [ " userAccountControl " ] )
if bit in ignored_bits :
2018-08-22 09:26:09 +03:00
self . assertEqual ( int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] ) ,
UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD ,
" Bit 0x %08x shouldn ' t stick " % bit )
2015-01-06 06:49:14 +03:00
else :
if bit in account_types :
2018-08-22 09:26:09 +03:00
self . assertEqual ( int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] ) ,
bit | UF_PASSWD_NOTREQD ,
" Bit 0x %08x didn ' t stick " % bit )
2015-01-06 06:49:14 +03:00
else :
2018-08-22 09:26:09 +03:00
self . assertEqual ( int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] ) ,
bit | UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD ,
" Bit 0x %08x didn ' t stick " % bit )
2015-01-06 06:49:14 +03:00
2014-12-08 05:07:59 +03:00
try :
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
2018-08-22 09:26:09 +03:00
m [ " userAccountControl " ] = ldb . MessageElement ( str ( bit | UF_PASSWD_NOTREQD | UF_ACCOUNTDISABLE ) ,
2014-12-08 05:07:59 +03:00
ldb . FLAG_MOD_REPLACE , " userAccountControl " )
self . samdb . modify ( m )
2018-02-23 17:34:23 +03:00
except LdbError as e2 :
( enum , estr ) = e2 . args
2014-12-08 05:07:59 +03:00
self . fail ( " Unable to set userAccountControl bit 0x %08X on %s : %s " % ( bit , m . dn , estr ) )
2015-11-18 07:36:21 +03:00
res = self . admin_samdb . search ( " %s " % self . base_dn ,
2021-10-22 06:18:51 +03:00
expression = " (&(objectClass=user)(cn= %s )) " % computername ,
2015-11-18 07:36:21 +03:00
scope = SCOPE_SUBTREE ,
attrs = [ " userAccountControl " ] )
if bit in account_types :
self . assertEqual ( int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] ) ,
2018-08-22 09:26:09 +03:00
bit | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD ,
2015-11-18 07:36:21 +03:00
" bit 0X %08x should have been added (0X %08x vs 0X %08x ) "
% ( bit , int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] ) ,
2018-08-22 09:26:09 +03:00
bit | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD ) )
2015-11-18 07:36:21 +03:00
elif bit in ignored_bits :
self . assertEqual ( int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] ) ,
2018-08-22 09:26:09 +03:00
UF_NORMAL_ACCOUNT | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD ,
2015-11-18 07:36:21 +03:00
" bit 0X %08x should have been added (0X %08x vs 0X %08x ) "
% ( bit , int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] ) ,
2018-08-22 09:26:09 +03:00
UF_NORMAL_ACCOUNT | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD ) )
2015-11-18 07:36:21 +03:00
else :
self . assertEqual ( int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] ) ,
2018-08-22 09:26:09 +03:00
bit | UF_NORMAL_ACCOUNT | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD ,
2015-11-18 07:36:21 +03:00
" bit 0X %08x should have been added (0X %08x vs 0X %08x ) "
% ( bit , int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] ) ,
2018-08-22 09:26:09 +03:00
bit | UF_NORMAL_ACCOUNT | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD ) )
2015-11-18 07:36:21 +03:00
2014-12-08 05:07:59 +03:00
try :
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
2018-08-22 09:26:09 +03:00
m [ " userAccountControl " ] = ldb . MessageElement ( str ( UF_PASSWD_NOTREQD | UF_ACCOUNTDISABLE ) ,
2014-12-08 05:07:59 +03:00
ldb . FLAG_MOD_REPLACE , " userAccountControl " )
self . samdb . modify ( m )
if bit in priv_to_remove_bits :
self . fail ( " Should have been unable to remove userAccountControl bit 0x %08X on %s " % ( bit , m . dn ) )
2018-02-23 17:34:23 +03:00
except LdbError as e3 :
( enum , estr ) = e3 . args
2021-10-22 13:41:23 +03:00
if account_type == UF_WORKSTATION_TRUST_ACCOUNT :
# Because removing any bit would change the account back to a user, which is locked by objectclass
self . assertIn ( enum , ( ldb . ERR_OBJECT_CLASS_VIOLATION , ldb . ERR_INSUFFICIENT_ACCESS_RIGHTS ) )
elif bit in priv_to_remove_bits :
2014-12-08 05:07:59 +03:00
self . assertEqual ( enum , ldb . ERR_INSUFFICIENT_ACCESS_RIGHTS )
else :
2015-11-18 07:36:21 +03:00
self . fail ( " Unexpectedly unable to remove userAccountControl bit 0x %08X on %s : %s " % ( bit , m . dn , estr ) )
2014-12-08 05:07:59 +03:00
2015-01-06 06:49:14 +03:00
res = self . admin_samdb . search ( " %s " % self . base_dn ,
2021-10-22 06:18:51 +03:00
expression = " (&(objectClass=user)(cn= %s )) " % computername ,
2015-01-06 06:49:14 +03:00
scope = SCOPE_SUBTREE ,
attrs = [ " userAccountControl " ] )
if bit in priv_to_remove_bits :
2015-11-18 07:36:21 +03:00
if bit in account_types :
self . assertEqual ( int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] ) ,
2018-08-22 09:26:09 +03:00
bit | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD ,
2015-11-18 07:36:21 +03:00
" bit 0X %08x should not have been removed " % bit )
else :
self . assertEqual ( int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] ) ,
2018-08-22 09:26:09 +03:00
bit | UF_NORMAL_ACCOUNT | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD ,
2015-11-18 07:36:21 +03:00
" bit 0X %08x should not have been removed " % bit )
2021-10-22 13:41:23 +03:00
elif account_type != UF_WORKSTATION_TRUST_ACCOUNT :
2015-01-06 06:49:14 +03:00
self . assertEqual ( int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] ) ,
2018-08-22 09:26:09 +03:00
UF_NORMAL_ACCOUNT | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD ,
2015-01-06 06:49:14 +03:00
" bit 0X %08x should have been removed " % bit )
2021-08-30 05:51:27 +03:00
def _test_uac_bits_add_with_args ( self , bit , bit_str ) :
2018-07-30 09:18:03 +03:00
computername = self . computernames [ 0 ]
2014-12-08 05:07:59 +03:00
user_sid = self . sd_utils . get_object_sid ( self . unpriv_user_dn )
mod = " (OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;; %s ) " % str ( user_sid )
2021-08-30 01:07:31 +03:00
self . sd_utils . dacl_add_ace ( self . OU , mod )
2014-12-08 05:07:59 +03:00
2021-08-30 09:17:47 +03:00
invalid_bits = set ( [ UF_TEMP_DUPLICATE_ACCOUNT ] )
# UF_NORMAL_ACCOUNT is invalid alone, needs UF_PASSWD_NOTREQD
unwilling_bits = set ( [ UF_NORMAL_ACCOUNT ] )
2014-12-08 05:07:59 +03:00
# These bits are privileged, but authenticated users have that CAR by default, so this is a pain to test
priv_to_auth_users_bits = set ( [ UF_PASSWD_NOTREQD , UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED ,
UF_DONT_EXPIRE_PASSWD ] )
# These bits really are privileged
priv_bits = set ( [ UF_INTERDOMAIN_TRUST_ACCOUNT , UF_SERVER_TRUST_ACCOUNT ,
2021-08-30 09:17:47 +03:00
UF_TRUSTED_FOR_DELEGATION , UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION ,
UF_PARTIAL_SECRETS_ACCOUNT ] )
if bit not in account_types and ( ( bit & UF_NORMAL_ACCOUNT ) == 0 ) :
bit_add = bit | UF_WORKSTATION_TRUST_ACCOUNT
else :
bit_add = bit
2014-12-08 05:07:59 +03:00
2021-08-30 05:51:27 +03:00
try :
2021-08-30 09:17:47 +03:00
self . add_computer_ldap ( computername , others = { " userAccountControl " : [ str ( bit_add ) ] } )
2021-08-30 05:51:27 +03:00
delete_force ( self . admin_samdb , " CN= %s , %s " % ( computername , self . OU ) )
if bit in priv_bits :
2023-08-02 11:44:32 +03:00
self . fail ( " Unexpectedly able to set userAccountControl bit 0x %08X ( %s ) on %s "
2021-08-30 05:51:27 +03:00
% ( bit , bit_str , computername ) )
except LdbError as e4 :
( enum , estr ) = e4 . args
if bit in invalid_bits :
self . assertEqual ( enum ,
ldb . ERR_OTHER ,
" Invalid bit 0x %08X ( %s ) was able to be set on %s "
% ( bit ,
bit_str ,
computername ) )
elif bit in priv_bits :
2021-10-22 13:41:23 +03:00
if bit == UF_INTERDOMAIN_TRUST_ACCOUNT :
self . assertIn ( enum , ( ldb . ERR_OBJECT_CLASS_VIOLATION ,
ldb . ERR_INSUFFICIENT_ACCESS_RIGHTS ) )
else :
self . assertEqual ( enum , ldb . ERR_INSUFFICIENT_ACCESS_RIGHTS )
2021-08-30 09:17:47 +03:00
elif bit in unwilling_bits :
2021-10-22 13:41:23 +03:00
# This can fail early as user in a computer is not permitted as non-admin
self . assertIn ( enum , ( ldb . ERR_UNWILLING_TO_PERFORM ,
ldb . ERR_OBJECT_CLASS_VIOLATION ) )
elif bit & UF_NORMAL_ACCOUNT :
# This can fail early as user in a computer is not permitted as non-admin
self . assertIn ( enum , ( ldb . ERR_UNWILLING_TO_PERFORM ,
ldb . ERR_OBJECT_CLASS_VIOLATION ) )
2021-08-30 05:51:27 +03:00
else :
self . fail ( " Unable to set userAccountControl bit 0x %08X ( %s ) on %s : %s "
% ( bit ,
bit_str ,
computername ,
estr ) )
2014-12-08 05:07:59 +03:00
2014-12-10 05:54:11 +03:00
def test_primarygroupID_cc_add ( self ) :
2018-07-30 09:18:03 +03:00
computername = self . computernames [ 0 ]
2014-12-10 05:54:11 +03:00
user_sid = self . sd_utils . get_object_sid ( self . unpriv_user_dn )
2021-10-22 21:10:35 +03:00
ace_cc = f " (OA;;CC; { dsdb . DS_GUID_SCHEMA_CLASS_COMPUTER } ;; { user_sid } ) "
ace_wp_dnshostname = f " (OA;CI;WP; { dsdb . DS_GUID_SCHEMA_ATTR_DNS_HOST_NAME } ;; { user_sid } ) "
ace_wp_primarygroupid = f " (OA;CI;WP; { dsdb . DS_GUID_SCHEMA_ATTR_PRIMARY_GROUP_ID } ;; { user_sid } ) "
mod = ace_cc + ace_wp_dnshostname + ace_wp_primarygroupid
2014-12-10 05:54:11 +03:00
2021-08-30 01:07:31 +03:00
self . sd_utils . dacl_add_ace ( self . OU , mod )
2014-12-10 05:54:11 +03:00
try :
# When creating a new object, you can not ever set the primaryGroupID
self . add_computer_ldap ( computername , others = { " primaryGroupID " : [ str ( security . DOMAIN_RID_ADMINS ) ] } )
self . fail ( " Unexpectedly able to set primaryGruopID to be an admin on %s " % computername )
2018-02-23 17:34:23 +03:00
except LdbError as e13 :
( enum , estr ) = e13 . args
2014-12-10 05:54:11 +03:00
self . assertEqual ( enum , ldb . ERR_UNWILLING_TO_PERFORM )
def test_primarygroupID_priv_DC_modify ( self ) :
2018-07-30 09:18:03 +03:00
computername = self . computernames [ 0 ]
2014-12-10 05:54:11 +03:00
self . add_computer_ldap ( computername ,
others = { " userAccountControl " : [ str ( UF_SERVER_TRUST_ACCOUNT ) ] } ,
samdb = self . admin_samdb )
res = self . admin_samdb . search ( " %s " % self . base_dn ,
expression = " (&(objectClass=computer)(samAccountName= %s $)) " % computername ,
scope = SCOPE_SUBTREE ,
attrs = [ " " ] )
m = ldb . Message ( )
m . dn = ldb . Dn ( self . admin_samdb , " <SID= %s - %d > " % ( str ( self . domain_sid ) ,
security . DOMAIN_RID_USERS ) )
2018-07-30 09:18:03 +03:00
m [ " member " ] = ldb . MessageElement (
2014-12-10 05:54:11 +03:00
[ str ( res [ 0 ] . dn ) ] , ldb . FLAG_MOD_ADD ,
" member " )
self . admin_samdb . modify ( m )
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
2018-07-30 09:18:03 +03:00
m [ " primaryGroupID " ] = ldb . MessageElement (
2014-12-10 05:54:11 +03:00
[ str ( security . DOMAIN_RID_USERS ) ] , ldb . FLAG_MOD_REPLACE ,
" primaryGroupID " )
try :
self . admin_samdb . modify ( m )
# When creating a new object, you can not ever set the primaryGroupID
self . fail ( " Unexpectedly able to set primaryGroupID to be other than DCS on %s " % computername )
2018-02-23 17:34:23 +03:00
except LdbError as e14 :
( enum , estr ) = e14 . args
2014-12-10 05:54:11 +03:00
self . assertEqual ( enum , ldb . ERR_UNWILLING_TO_PERFORM )
def test_primarygroupID_priv_member_modify ( self ) :
2018-07-30 09:18:03 +03:00
computername = self . computernames [ 0 ]
2014-12-10 05:54:11 +03:00
self . add_computer_ldap ( computername ,
2018-08-22 09:26:09 +03:00
others = { " userAccountControl " : [ str ( UF_WORKSTATION_TRUST_ACCOUNT | UF_PARTIAL_SECRETS_ACCOUNT ) ] } ,
2014-12-10 05:54:11 +03:00
samdb = self . admin_samdb )
res = self . admin_samdb . search ( " %s " % self . base_dn ,
expression = " (&(objectClass=computer)(samAccountName= %s $)) " % computername ,
scope = SCOPE_SUBTREE ,
attrs = [ " " ] )
m = ldb . Message ( )
m . dn = ldb . Dn ( self . admin_samdb , " <SID= %s - %d > " % ( str ( self . domain_sid ) ,
security . DOMAIN_RID_USERS ) )
2018-07-30 09:18:03 +03:00
m [ " member " ] = ldb . MessageElement (
2014-12-10 05:54:11 +03:00
[ str ( res [ 0 ] . dn ) ] , ldb . FLAG_MOD_ADD ,
" member " )
self . admin_samdb . modify ( m )
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
2018-07-30 09:18:03 +03:00
m [ " primaryGroupID " ] = ldb . MessageElement (
2014-12-10 05:54:11 +03:00
[ str ( security . DOMAIN_RID_USERS ) ] , ldb . FLAG_MOD_REPLACE ,
" primaryGroupID " )
2021-09-16 07:09:24 +03:00
self . assertRaisesLdbError ( ldb . ERR_UNWILLING_TO_PERFORM ,
f " Unexpectedly able to set primaryGroupID to be other than DCS on { m . dn } " ,
self . admin_samdb . modify , m )
2014-12-10 05:54:11 +03:00
def test_primarygroupID_priv_user_modify ( self ) :
2018-07-30 09:18:03 +03:00
computername = self . computernames [ 0 ]
2014-12-10 05:54:11 +03:00
self . add_computer_ldap ( computername ,
others = { " userAccountControl " : [ str ( UF_WORKSTATION_TRUST_ACCOUNT ) ] } ,
samdb = self . admin_samdb )
res = self . admin_samdb . search ( " %s " % self . base_dn ,
expression = " (&(objectClass=computer)(samAccountName= %s $)) " % computername ,
scope = SCOPE_SUBTREE ,
attrs = [ " " ] )
m = ldb . Message ( )
m . dn = ldb . Dn ( self . admin_samdb , " <SID= %s - %d > " % ( str ( self . domain_sid ) ,
security . DOMAIN_RID_ADMINS ) )
2018-07-30 09:18:03 +03:00
m [ " member " ] = ldb . MessageElement (
2014-12-10 05:54:11 +03:00
[ str ( res [ 0 ] . dn ) ] , ldb . FLAG_MOD_ADD ,
" member " )
self . admin_samdb . modify ( m )
m = ldb . Message ( )
m . dn = res [ 0 ] . dn
2018-07-30 09:18:03 +03:00
m [ " primaryGroupID " ] = ldb . MessageElement (
2014-12-10 05:54:11 +03:00
[ str ( security . DOMAIN_RID_ADMINS ) ] , ldb . FLAG_MOD_REPLACE ,
" primaryGroupID " )
self . admin_samdb . modify ( m )
2014-12-08 05:07:59 +03:00
2021-10-22 05:42:08 +03:00
def _test_objectclass_uac_dollar_lock_with_args ( self ,
account_type ,
objectclass ,
name ,
priv ) :
2021-09-13 01:21:03 +03:00
dn = " CN= %s , %s " % ( name , self . OU )
msg_dict = {
" dn " : dn ,
" objectclass " : objectclass ,
" samAccountName " : name ,
" userAccountControl " : str ( account_type | UF_PASSWD_NOTREQD ) }
account_type_str = dsdb . user_account_control_flag_bit_to_string ( account_type )
print ( f " Adding account { name } as { account_type_str } with objectclass { objectclass } " )
2021-10-22 05:42:08 +03:00
if priv :
samdb = self . admin_samdb
2021-09-13 01:21:03 +03:00
else :
2021-10-22 05:42:08 +03:00
user_sid = self . sd_utils . get_object_sid ( self . unpriv_user_dn )
mod = " (OA;;CC;;; %s ) " % str ( user_sid )
self . sd_utils . dacl_add_ace ( self . OU , mod )
samdb = self . samdb
enum = ldb . SUCCESS
try :
samdb . add ( msg_dict )
except ldb . LdbError as e :
( enum , msg ) = e . args
if ( account_type == UF_SERVER_TRUST_ACCOUNT
and objectclass != " computer " ) :
self . assertEqual ( enum , ldb . ERR_OBJECT_CLASS_VIOLATION )
return
if priv == False and account_type == UF_SERVER_TRUST_ACCOUNT :
self . assertEqual ( enum , ldb . ERR_INSUFFICIENT_ACCESS_RIGHTS )
return
if ( objectclass == " user "
and account_type != UF_NORMAL_ACCOUNT ) :
self . assertEqual ( enum , ldb . ERR_OBJECT_CLASS_VIOLATION )
return
if ( not priv and objectclass == " computer "
and account_type == UF_NORMAL_ACCOUNT ) :
self . assertEqual ( enum , ldb . ERR_OBJECT_CLASS_VIOLATION )
return
if priv and account_type == UF_NORMAL_ACCOUNT :
self . assertEqual ( enum , 0 )
return
if ( priv == False and
account_type != UF_NORMAL_ACCOUNT and
name [ - 1 ] != ' $ ' ) :
self . assertEqual ( enum , ldb . ERR_UNWILLING_TO_PERFORM )
return
self . assertEqual ( enum , 0 )
2021-09-13 01:21:03 +03:00
2021-10-29 13:33:32 +03:00
def _test_mod_lock_with_args ( self ,
objectclass ,
objectclass2 ,
account_type ,
account_type2 ,
name2 ,
priv ) :
name = " oc_uac_lock$ "
dn = " CN= %s , %s " % ( name , self . OU )
msg_dict = {
" dn " : dn ,
" objectclass " : objectclass ,
" samAccountName " : name ,
" userAccountControl " : str ( account_type | UF_PASSWD_NOTREQD ) }
account_type_str = dsdb . user_account_control_flag_bit_to_string (
account_type )
print ( f " Adding account { name } as { account_type_str } "
f " with objectclass { objectclass } " )
# Create the object as admin
self . admin_samdb . add ( msg_dict )
if priv :
samdb = self . admin_samdb
else :
samdb = self . samdb
user_sid = self . sd_utils . get_object_sid ( self . unpriv_user_dn )
# We want to test what the underlying rules for non-admins regardless
# of security descriptors are, so set this very, dangerously, broadly
mod = f " (OA;;WP;;; { user_sid } ) "
self . sd_utils . dacl_add_ace ( dn , mod )
msg = " Modifying account "
if name2 is not None :
msg + = f " to { name2 } "
if account_type2 is not None :
account_type2_str = dsdb . user_account_control_flag_bit_to_string (
account_type2 )
msg + = f " as { account_type2_str } "
else :
account_type2_str = None
if objectclass2 is not None :
msg + = f " with objectClass { objectclass2 } "
print ( msg )
msg = ldb . Message ( ldb . Dn ( samdb , dn ) )
if objectclass2 is not None :
msg [ " objectClass " ] = ldb . MessageElement ( objectclass2 ,
ldb . FLAG_MOD_REPLACE ,
" objectClass " )
if name2 is not None :
msg [ " sAMAccountName " ] = ldb . MessageElement ( name2 ,
ldb . FLAG_MOD_REPLACE ,
" sAMAccountName " )
if account_type2 is not None :
msg [ " userAccountControl " ] = ldb . MessageElement (
str ( account_type2 | UF_PASSWD_NOTREQD ) ,
ldb . FLAG_MOD_REPLACE ,
" userAccountControl " )
enum = ldb . SUCCESS
try :
samdb . modify ( msg )
except ldb . LdbError as e :
enum , _ = e . args
# Setting userAccountControl to be an RODC is not allowed.
if account_type2 == UF_PARTIAL_SECRETS_ACCOUNT :
self . assertEqual ( enum , ldb . ERR_OTHER )
return
# Unprivileged users cannot change userAccountControl. The exception is
# changing a non-normal account to UF_WORKSTATION_TRUST_ACCOUNT, which
# is allowed.
if ( not priv
and account_type2 is not None
and account_type != account_type2
and ( account_type == UF_NORMAL_ACCOUNT
or account_type2 != UF_WORKSTATION_TRUST_ACCOUNT ) ) :
self . assertIn ( enum , [ ldb . ERR_INSUFFICIENT_ACCESS_RIGHTS ,
ldb . ERR_OBJECT_CLASS_VIOLATION ] )
return
# A non-computer account cannot have UF_SERVER_TRUST_ACCOUNT.
if objectclass == " user " and account_type2 == UF_SERVER_TRUST_ACCOUNT :
self . assertIn ( enum , [ ldb . ERR_UNWILLING_TO_PERFORM ,
ldb . ERR_OBJECT_CLASS_VIOLATION ] )
return
# The objectClass is not allowed to change.
if objectclass2 is not None and objectclass != objectclass2 :
self . assertIn ( enum , [ ldb . ERR_OBJECT_CLASS_VIOLATION ,
ldb . ERR_UNWILLING_TO_PERFORM ] )
return
# Unprivileged users cannot remove the trailing dollar from a computer
# account.
if not priv and objectclass == " computer " and (
name2 is not None and name2 [ - 1 ] != " $ " ) :
self . assertEqual ( enum , ldb . ERR_UNWILLING_TO_PERFORM )
return
self . assertEqual ( enum , 0 )
return
2021-09-13 01:21:03 +03:00
def _test_objectclass_uac_mod_lock_with_args ( self ,
account_type ,
account_type2 ,
2021-10-22 05:42:08 +03:00
how ,
priv ) :
2021-09-13 01:21:03 +03:00
name = " uac_mod_lock$ "
dn = " CN= %s , %s " % ( name , self . OU )
if account_type == UF_NORMAL_ACCOUNT :
objectclass = " user "
else :
objectclass = " computer "
msg_dict = {
" dn " : dn ,
" objectclass " : objectclass ,
" samAccountName " : name ,
" userAccountControl " : str ( account_type | UF_PASSWD_NOTREQD ) }
account_type_str \
= dsdb . user_account_control_flag_bit_to_string ( account_type )
account_type2_str \
= dsdb . user_account_control_flag_bit_to_string ( account_type2 )
print ( f " Adding account { name } as { account_type_str } with objectclass { objectclass } " )
2021-10-22 05:42:08 +03:00
if priv :
samdb = self . admin_samdb
else :
samdb = self . samdb
user_sid = self . sd_utils . get_object_sid ( self . unpriv_user_dn )
# Create the object as admin
2021-09-13 01:21:03 +03:00
self . admin_samdb . add ( msg_dict )
2021-10-22 05:42:08 +03:00
# We want to test what the underlying rules for non-admins
# regardless of security descriptors are, so set this very,
# dangerously, broadly
mod = " (OA;;WP;;; %s ) " % str ( user_sid )
self . sd_utils . dacl_add_ace ( dn , mod )
2021-09-13 01:21:03 +03:00
m = ldb . Message ( )
2021-10-22 05:42:08 +03:00
m . dn = ldb . Dn ( samdb , dn )
2021-09-13 01:21:03 +03:00
if how == " replace " :
m [ " userAccountControl " ] = ldb . MessageElement ( str ( account_type2 | UF_PASSWD_NOTREQD ) ,
ldb . FLAG_MOD_REPLACE , " userAccountControl " )
elif how == " deladd " :
m [ " 0userAccountControl " ] = ldb . MessageElement ( [ ] ,
ldb . FLAG_MOD_DELETE , " userAccountControl " )
m [ " 1userAccountControl " ] = ldb . MessageElement ( str ( account_type2 | UF_PASSWD_NOTREQD ) ,
ldb . FLAG_MOD_ADD , " userAccountControl " )
else :
raise ValueError ( f " { how } was not a valid argument " )
2021-10-22 05:42:08 +03:00
if ( account_type == account_type2 ) :
samdb . modify ( m )
elif ( account_type == UF_NORMAL_ACCOUNT ) and \
( account_type2 == UF_SERVER_TRUST_ACCOUNT ) and not priv :
2021-10-22 12:54:52 +03:00
self . assertRaisesLdbError ( [ ldb . ERR_OBJECT_CLASS_VIOLATION ,
ldb . ERR_INSUFFICIENT_ACCESS_RIGHTS ] ,
2021-10-22 05:42:08 +03:00
f " Should have been unable to change { account_type_str } to { account_type2_str } " ,
samdb . modify , m )
elif ( account_type == UF_NORMAL_ACCOUNT ) and \
( account_type2 == UF_SERVER_TRUST_ACCOUNT ) and priv :
2021-10-22 12:54:52 +03:00
self . assertRaisesLdbError ( [ ldb . ERR_OBJECT_CLASS_VIOLATION ,
ldb . ERR_UNWILLING_TO_PERFORM ] ,
2021-10-22 05:42:08 +03:00
f " Should have been unable to change { account_type_str } to { account_type2_str } " ,
samdb . modify , m )
elif ( account_type == UF_WORKSTATION_TRUST_ACCOUNT ) and \
( account_type2 == UF_SERVER_TRUST_ACCOUNT ) and not priv :
self . assertRaisesLdbError ( ldb . ERR_INSUFFICIENT_ACCESS_RIGHTS ,
f " Should have been unable to change { account_type_str } to { account_type2_str } " ,
samdb . modify , m )
elif priv :
samdb . modify ( m )
elif ( account_type in [ UF_SERVER_TRUST_ACCOUNT ,
UF_WORKSTATION_TRUST_ACCOUNT ] ) and \
2021-09-13 01:21:03 +03:00
( account_type2 in [ UF_SERVER_TRUST_ACCOUNT ,
UF_WORKSTATION_TRUST_ACCOUNT ] ) :
2021-10-22 05:42:08 +03:00
samdb . modify ( m )
2021-09-13 01:21:03 +03:00
elif ( account_type == account_type2 ) :
2021-10-22 05:42:08 +03:00
samdb . modify ( m )
2021-09-13 01:21:03 +03:00
else :
2021-10-22 05:42:08 +03:00
self . assertRaisesLdbError ( ldb . ERR_OBJECT_CLASS_VIOLATION ,
2021-09-13 01:21:03 +03:00
f " Should have been unable to change { account_type_str } to { account_type2_str } " ,
2021-10-22 05:42:08 +03:00
samdb . modify , m )
2021-09-13 01:21:03 +03:00
def _test_objectclass_mod_lock_with_args ( self ,
account_type ,
objectclass ,
how ) :
name = " uac_mod_lock$ "
dn = " CN= %s , %s " % ( name , self . OU )
if objectclass == " computer " :
new_objectclass = [ " top " ,
" person " ,
" organizationalPerson " ,
" user " ]
elif objectclass == " user " :
new_objectclass = [ " top " ,
" person " ,
" organizationalPerson " ,
" user " ,
" computer " ]
msg_dict = {
" dn " : dn ,
" objectclass " : objectclass ,
" samAccountName " : name ,
" userAccountControl " : str ( account_type | UF_PASSWD_NOTREQD ) }
account_type_str = dsdb . user_account_control_flag_bit_to_string ( account_type )
print ( f " Adding account { name } as { account_type_str } with objectclass { objectclass } " )
try :
self . admin_samdb . add ( msg_dict )
2023-02-24 02:57:57 +03:00
if ( objectclass == " user "
2021-09-13 01:21:03 +03:00
and account_type != UF_NORMAL_ACCOUNT ) :
2023-10-02 04:25:52 +03:00
self . fail ( f " Able to create { account_type_str } on { objectclass } " )
2021-09-13 01:21:03 +03:00
except LdbError as e :
( enum , estr ) = e . args
self . assertEqual ( enum , ldb . ERR_OBJECT_CLASS_VIOLATION )
if objectclass == " user " and account_type != UF_NORMAL_ACCOUNT :
return
m = ldb . Message ( )
m . dn = ldb . Dn ( self . admin_samdb , dn )
if how == " replace " :
m [ " objectclass " ] = ldb . MessageElement ( new_objectclass ,
ldb . FLAG_MOD_REPLACE , " objectclass " )
elif how == " adddel " :
m [ " 0objectclass " ] = ldb . MessageElement ( [ ] ,
ldb . FLAG_MOD_DELETE , " objectclass " )
m [ " 1objectclass " ] = ldb . MessageElement ( new_objectclass ,
ldb . FLAG_MOD_ADD , " objectclass " )
2021-10-22 12:54:52 +03:00
self . assertRaisesLdbError ( [ ldb . ERR_OBJECT_CLASS_VIOLATION ,
ldb . ERR_UNWILLING_TO_PERFORM ] ,
2023-10-02 04:25:52 +03:00
f " Should have been unable to change objectclass of a { objectclass } " ,
2021-09-13 01:21:03 +03:00
self . admin_samdb . modify , m )
2014-12-08 05:07:59 +03:00
runner = SubunitTestRunner ( )
rc = 0
2023-10-02 05:07:54 +03:00
if not runner . run ( unittest . TestLoader ( ) . loadTestsFromTestCase (
UserAccountControlTests ) ) . wasSuccessful ( ) :
2014-12-08 05:07:59 +03:00
rc = 1
sys . exit ( rc )