2 # -*- coding: utf-8 -*-
3 # This tests the restrictions on userAccountControl that apply even if write access is permitted
5 # Copyright Samuel Cabrero 2014 <samuelcabrero@kernevil.me>
6 # Copyright Andrew Bartlett 2014 <abartlet@samba.org>
8 # Licenced under the GPLv3
15 import samba
.getopt
as options
20 sys
.path
.insert(0, "bin/python")
21 from samba
.tests
.subunitrun
import TestProgram
, SubunitOptions
23 from samba
.subunit
.run
import SubunitTestRunner
24 from samba
.auth
import system_session
25 from samba
.samdb
import SamDB
26 from samba
.dcerpc
import samr
, security
, lsa
27 from samba
.credentials
import Credentials
28 from samba
.ndr
import ndr_unpack
, ndr_pack
29 from samba
.tests
import delete_force
30 from samba
import gensec
, sd_utils
31 from samba
.credentials
import DONT_USE_KERBEROS
32 from ldb
import SCOPE_SUBTREE
, SCOPE_BASE
, LdbError
33 from ldb
import Message
, MessageElement
, Dn
34 from ldb
import FLAG_MOD_ADD
, FLAG_MOD_REPLACE
, FLAG_MOD_DELETE
35 from samba
.dsdb
import UF_SCRIPT
, UF_ACCOUNTDISABLE
, UF_00000004
, UF_HOMEDIR_REQUIRED
, \
36 UF_LOCKOUT
,UF_PASSWD_NOTREQD
, UF_PASSWD_CANT_CHANGE
, UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED
,\
37 UF_TEMP_DUPLICATE_ACCOUNT
, UF_NORMAL_ACCOUNT
, UF_00000400
, UF_INTERDOMAIN_TRUST_ACCOUNT
, \
38 UF_WORKSTATION_TRUST_ACCOUNT
, UF_SERVER_TRUST_ACCOUNT
, UF_00004000
, \
39 UF_00008000
, UF_DONT_EXPIRE_PASSWD
, UF_MNS_LOGON_ACCOUNT
, UF_SMARTCARD_REQUIRED
, \
40 UF_TRUSTED_FOR_DELEGATION
, UF_NOT_DELEGATED
, UF_USE_DES_KEY_ONLY
, UF_DONT_REQUIRE_PREAUTH
, \
41 UF_PASSWORD_EXPIRED
, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION
, UF_NO_AUTH_DATA_REQUIRED
, \
42 UF_PARTIAL_SECRETS_ACCOUNT
, UF_USE_AES_KEYS
45 parser
= optparse
.OptionParser("user_account_control.py [options] <host>")
46 sambaopts
= options
.SambaOptions(parser
)
47 parser
.add_option_group(sambaopts
)
48 parser
.add_option_group(options
.VersionOptions(parser
))
50 # use command line creds if available
51 credopts
= options
.CredentialsOptions(parser
)
52 parser
.add_option_group(credopts
)
53 opts
, args
= parser
.parse_args()
61 ldaphost
= "ldap://%s" % host
64 start
= host
.rindex("://")
65 host
= host
.lstrip(start
+3)
67 lp
= sambaopts
.get_loadparm()
68 creds
= credopts
.get_credentials(lp
)
69 creds
.set_gensec_features(creds
.get_gensec_features() | gensec
.FEATURE_SEAL
)
71 bits
= [UF_SCRIPT
, UF_ACCOUNTDISABLE
, UF_00000004
, UF_HOMEDIR_REQUIRED
,
72 UF_LOCKOUT
,UF_PASSWD_NOTREQD
, UF_PASSWD_CANT_CHANGE
,
73 UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED
,
74 UF_TEMP_DUPLICATE_ACCOUNT
, UF_NORMAL_ACCOUNT
, UF_00000400
,
75 UF_INTERDOMAIN_TRUST_ACCOUNT
,
76 UF_WORKSTATION_TRUST_ACCOUNT
, UF_SERVER_TRUST_ACCOUNT
, UF_00004000
,
77 UF_00008000
, UF_DONT_EXPIRE_PASSWD
, UF_MNS_LOGON_ACCOUNT
, UF_SMARTCARD_REQUIRED
,
78 UF_TRUSTED_FOR_DELEGATION
, UF_NOT_DELEGATED
, UF_USE_DES_KEY_ONLY
,
79 UF_DONT_REQUIRE_PREAUTH
,
80 UF_PASSWORD_EXPIRED
, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION
,
81 UF_NO_AUTH_DATA_REQUIRED
,
82 UF_PARTIAL_SECRETS_ACCOUNT
, UF_USE_AES_KEYS
,
83 int("0x10000000", 16), int("0x20000000", 16), int("0x40000000", 16), int("0x80000000", 16)]
85 account_types
= set([UF_NORMAL_ACCOUNT
, UF_WORKSTATION_TRUST_ACCOUNT
, UF_SERVER_TRUST_ACCOUNT
])
88 class UserAccountControlTests(samba
.tests
.TestCase
):
89 def add_computer_ldap(self
, computername
, others
=None, samdb
=None):
92 dn
= "CN=%s,OU=test_computer_ou1,%s" % (computername
, self
.base_dn
)
93 domainname
= ldb
.Dn(self
.samdb
, self
.samdb
.domain_dn()).canonical_str().replace("/", "")
94 samaccountname
= "%s$" % computername
95 dnshostname
= "%s.%s" % (computername
, domainname
)
98 "objectclass": "computer"}
99 if others
is not None:
100 msg_dict
= dict(msg_dict
.items() + others
.items())
102 msg
= ldb
.Message
.from_dict(self
.samdb
, msg_dict
)
103 msg
["sAMAccountName"] = samaccountname
105 print "Adding computer account %s" % computername
108 def get_creds(self
, target_username
, target_password
):
109 creds_tmp
= Credentials()
110 creds_tmp
.set_username(target_username
)
111 creds_tmp
.set_password(target_password
)
112 creds_tmp
.set_domain(creds
.get_domain())
113 creds_tmp
.set_realm(creds
.get_realm())
114 creds_tmp
.set_workstation(creds
.get_workstation())
115 creds_tmp
.set_gensec_features(creds_tmp
.get_gensec_features()
116 | gensec
.FEATURE_SEAL
)
117 creds_tmp
.set_kerberos_state(DONT_USE_KERBEROS
) # kinit is too expensive to use in a tight loop
121 super(UserAccountControlTests
, self
).setUp()
122 self
.admin_creds
= creds
123 self
.admin_samdb
= SamDB(url
=ldaphost
,
124 session_info
=system_session(),
125 credentials
=self
.admin_creds
, lp
=lp
)
127 self
.unpriv_user
= "testuser1"
128 self
.unpriv_user_pw
= "samba123@"
129 self
.unpriv_creds
= self
.get_creds(self
.unpriv_user
, self
.unpriv_user_pw
)
131 self
.admin_samdb
.newuser(self
.unpriv_user
, self
.unpriv_user_pw
)
132 res
= self
.admin_samdb
.search("CN=%s,CN=Users,%s" % (self
.unpriv_user
, self
.admin_samdb
.domain_dn()),
135 self
.assertEqual(1, len(res
))
137 self
.unpriv_user_sid
= ndr_unpack(security
.dom_sid
, res
[0]["objectSid"][0])
138 self
.unpriv_user_dn
= res
[0].dn
140 self
.samdb
= SamDB(url
=ldaphost
, credentials
=self
.unpriv_creds
, lp
=lp
)
141 self
.domain_sid
= security
.dom_sid(self
.samdb
.get_domain_sid())
142 self
.base_dn
= self
.samdb
.domain_dn()
144 self
.samr
= samr
.samr("ncacn_ip_tcp:%s[seal]" % host
, lp
, self
.unpriv_creds
)
145 self
.samr_handle
= self
.samr
.Connect2(None, security
.SEC_FLAG_MAXIMUM_ALLOWED
)
146 self
.samr_domain
= self
.samr
.OpenDomain(self
.samr_handle
, security
.SEC_FLAG_MAXIMUM_ALLOWED
, self
.domain_sid
)
148 self
.sd_utils
= sd_utils
.SDUtils(self
.admin_samdb
)
150 self
.admin_samdb
.create_ou("OU=test_computer_ou1," + self
.base_dn
)
151 self
.unpriv_user_sid
= self
.sd_utils
.get_object_sid(self
.unpriv_user_dn
)
152 mod
= "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(self
.unpriv_user_sid
)
154 old_sd
= self
.sd_utils
.read_sd_on_dn("OU=test_computer_ou1," + self
.base_dn
)
156 self
.sd_utils
.dacl_add_ace("OU=test_computer_ou1," + self
.base_dn
, mod
)
158 self
.add_computer_ldap("testcomputer-t")
160 self
.sd_utils
.modify_sd_on_dn("OU=test_computer_ou1," + self
.base_dn
, old_sd
)
162 self
.computernames
= ["testcomputer-0"]
164 # Get the SD of the template account, then force it to match
165 # what we expect for SeMachineAccountPrivilege accounts, so we
166 # can confirm we created the accounts correctly
167 self
.sd_reference_cc
= self
.sd_utils
.read_sd_on_dn("CN=testcomputer-t,OU=test_computer_ou1,%s" % (self
.base_dn
))
169 self
.sd_reference_modify
= self
.sd_utils
.read_sd_on_dn("CN=testcomputer-t,OU=test_computer_ou1,%s" % (self
.base_dn
))
170 for ace
in self
.sd_reference_modify
.dacl
.aces
:
171 if ace
.type == security
.SEC_ACE_TYPE_ACCESS_ALLOWED
and ace
.trustee
== self
.unpriv_user_sid
:
172 ace
.access_mask
= ace
.access_mask | security
.SEC_ADS_SELF_WRITE | security
.SEC_ADS_WRITE_PROP
174 # Now reconnect without domain admin rights
175 self
.samdb
= SamDB(url
=ldaphost
, credentials
=self
.unpriv_creds
, lp
=lp
)
179 super(UserAccountControlTests
, self
).tearDown()
180 for computername
in self
.computernames
:
181 delete_force(self
.admin_samdb
, "CN=%s,OU=test_computer_ou1,%s" % (computername
, self
.base_dn
))
182 delete_force(self
.admin_samdb
, "CN=testcomputer-t,OU=test_computer_ou1,%s" % (self
.base_dn
))
183 delete_force(self
.admin_samdb
, "OU=test_computer_ou1,%s" % (self
.base_dn
))
184 delete_force(self
.admin_samdb
, "CN=%s,CN=Users,%s" % (self
.unpriv_user
, self
.base_dn
))
186 def test_add_computer_sd_cc(self
):
187 user_sid
= self
.sd_utils
.get_object_sid(self
.unpriv_user_dn
)
188 mod
= "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid
)
190 old_sd
= self
.sd_utils
.read_sd_on_dn("OU=test_computer_ou1," + self
.base_dn
)
192 self
.sd_utils
.dacl_add_ace("OU=test_computer_ou1," + self
.base_dn
, mod
)
194 computername
=self
.computernames
[0]
195 sd
= ldb
.MessageElement((ndr_pack(self
.sd_reference_modify
)),
197 "nTSecurityDescriptor")
198 self
.add_computer_ldap(computername
,
199 others
={"nTSecurityDescriptor": sd
})
201 res
= self
.admin_samdb
.search("%s" % self
.base_dn
,
202 expression
="(&(objectClass=computer)(samAccountName=%s$))" % computername
,
204 attrs
=["ntSecurityDescriptor"])
206 desc
= res
[0]["nTSecurityDescriptor"][0]
207 desc
= ndr_unpack(security
.descriptor
, desc
, allow_remaining
=True)
209 sddl
= desc
.as_sddl(self
.domain_sid
)
210 self
.assertEqual(self
.sd_reference_modify
.as_sddl(self
.domain_sid
), sddl
)
214 m
["description"]= ldb
.MessageElement(
215 ("A description"), ldb
.FLAG_MOD_REPLACE
,
221 m
["userAccountControl"] = ldb
.MessageElement(str(samba
.dsdb
.UF_SERVER_TRUST_ACCOUNT
),
222 ldb
.FLAG_MOD_REPLACE
, "userAccountControl")
225 self
.fail("Unexpectedly able to set userAccountControl to be a DC on %s" % m
.dn
)
226 except LdbError
, (enum
, estr
):
227 self
.assertEqual(ldb
.ERR_INSUFFICIENT_ACCESS_RIGHTS
, enum
)
231 m
["userAccountControl"] = ldb
.MessageElement(str(samba
.dsdb
.UF_WORKSTATION_TRUST_ACCOUNT|samba
.dsdb
.UF_PARTIAL_SECRETS_ACCOUNT
),
232 ldb
.FLAG_MOD_REPLACE
, "userAccountControl")
235 self
.fail("Unexpectedly able to set userAccountControl to be an RODC on %s" % m
.dn
)
236 except LdbError
, (enum
, estr
):
237 self
.assertEqual(ldb
.ERR_INSUFFICIENT_ACCESS_RIGHTS
, enum
)
241 m
["userAccountControl"] = ldb
.MessageElement(str(samba
.dsdb
.UF_WORKSTATION_TRUST_ACCOUNT
),
242 ldb
.FLAG_MOD_REPLACE
, "userAccountControl")
245 self
.fail("Unexpectedly able to set userAccountControl to be an Workstation on %s" % m
.dn
)
246 except LdbError
, (enum
, estr
):
247 self
.assertEqual(ldb
.ERR_INSUFFICIENT_ACCESS_RIGHTS
, enum
)
251 m
["userAccountControl"] = ldb
.MessageElement(str(samba
.dsdb
.UF_NORMAL_ACCOUNT
),
252 ldb
.FLAG_MOD_REPLACE
, "userAccountControl")
257 m
["primaryGroupID"] = ldb
.MessageElement(str(security
.DOMAIN_RID_ADMINS
),
258 ldb
.FLAG_MOD_REPLACE
, "primaryGroupID")
261 except LdbError
, (enum
, estr
):
262 self
.assertEqual(ldb
.ERR_UNWILLING_TO_PERFORM
, enum
)
266 def test_mod_computer_cc(self
):
267 user_sid
= self
.sd_utils
.get_object_sid(self
.unpriv_user_dn
)
268 mod
= "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid
)
270 old_sd
= self
.sd_utils
.read_sd_on_dn("OU=test_computer_ou1," + self
.base_dn
)
272 self
.sd_utils
.dacl_add_ace("OU=test_computer_ou1," + self
.base_dn
, mod
)
274 computername
=self
.computernames
[0]
275 self
.add_computer_ldap(computername
)
277 res
= self
.admin_samdb
.search("%s" % self
.base_dn
,
278 expression
="(&(objectClass=computer)(samAccountName=%s$))" % computername
,
284 m
["description"]= ldb
.MessageElement(
285 ("A description"), ldb
.FLAG_MOD_REPLACE
,
291 m
["userAccountControl"] = ldb
.MessageElement(str(samba
.dsdb
.UF_WORKSTATION_TRUST_ACCOUNT|samba
.dsdb
.UF_PARTIAL_SECRETS_ACCOUNT
),
292 ldb
.FLAG_MOD_REPLACE
, "userAccountControl")
295 self
.fail("Unexpectedly able to set userAccountControl on %s" % m
.dn
)
296 except LdbError
, (enum
, estr
):
297 self
.assertEqual(ldb
.ERR_INSUFFICIENT_ACCESS_RIGHTS
, enum
)
301 m
["userAccountControl"] = ldb
.MessageElement(str(samba
.dsdb
.UF_SERVER_TRUST_ACCOUNT
),
302 ldb
.FLAG_MOD_REPLACE
, "userAccountControl")
306 except LdbError
, (enum
, estr
):
307 self
.assertEqual(ldb
.ERR_INSUFFICIENT_ACCESS_RIGHTS
, enum
)
311 m
["userAccountControl"] = ldb
.MessageElement(str(samba
.dsdb
.UF_NORMAL_ACCOUNT
),
312 ldb
.FLAG_MOD_REPLACE
, "userAccountControl")
317 m
["userAccountControl"] = ldb
.MessageElement(str(samba
.dsdb
.UF_WORKSTATION_TRUST_ACCOUNT
),
318 ldb
.FLAG_MOD_REPLACE
, "userAccountControl")
321 self
.fail("Unexpectedly able to set userAccountControl to be an Workstation on %s" % m
.dn
)
322 except LdbError
, (enum
, estr
):
323 self
.assertEqual(ldb
.ERR_INSUFFICIENT_ACCESS_RIGHTS
, enum
)
326 def test_admin_mod_uac(self
):
327 computername
=self
.computernames
[0]
328 self
.add_computer_ldap(computername
, samdb
=self
.admin_samdb
)
330 res
= self
.admin_samdb
.search("%s" % self
.base_dn
,
331 expression
="(&(objectClass=computer)(samAccountName=%s$))" % computername
,
333 attrs
=["userAccountControl"])
335 self
.assertEqual(int(res
[0]["userAccountControl"][0]), UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD
)
339 m
["userAccountControl"] = ldb
.MessageElement(str(UF_WORKSTATION_TRUST_ACCOUNT|UF_PARTIAL_SECRETS_ACCOUNT|UF_TRUSTED_FOR_DELEGATION
),
340 ldb
.FLAG_MOD_REPLACE
, "userAccountControl")
342 self
.admin_samdb
.modify(m
)
343 self
.fail("Unexpectedly able to set userAccountControl to UF_WORKSTATION_TRUST_ACCOUNT|UF_PARTIAL_SECRETS_ACCOUNT|UF_TRUSTED_FOR_DELEGATION on %s" % m
.dn
)
344 except LdbError
, (enum
, estr
):
345 self
.assertEqual(ldb
.ERR_OTHER
, enum
)
349 m
["userAccountControl"] = ldb
.MessageElement(str(UF_WORKSTATION_TRUST_ACCOUNT|UF_PARTIAL_SECRETS_ACCOUNT
),
350 ldb
.FLAG_MOD_REPLACE
, "userAccountControl")
351 self
.admin_samdb
.modify(m
)
353 res
= self
.admin_samdb
.search("%s" % self
.base_dn
,
354 expression
="(&(objectClass=computer)(samAccountName=%s$))" % computername
,
356 attrs
=["userAccountControl"])
358 self
.assertEqual(int(res
[0]["userAccountControl"][0]), UF_WORKSTATION_TRUST_ACCOUNT|UF_PARTIAL_SECRETS_ACCOUNT
)
361 m
["userAccountControl"] = ldb
.MessageElement(str(UF_ACCOUNTDISABLE
),
362 ldb
.FLAG_MOD_REPLACE
, "userAccountControl")
363 self
.admin_samdb
.modify(m
)
365 res
= self
.admin_samdb
.search("%s" % self
.base_dn
,
366 expression
="(&(objectClass=computer)(samAccountName=%s$))" % computername
,
368 attrs
=["userAccountControl"])
370 self
.assertEqual(int(res
[0]["userAccountControl"][0]), UF_NORMAL_ACCOUNT| UF_ACCOUNTDISABLE
)
373 def test_uac_bits_set(self
):
374 user_sid
= self
.sd_utils
.get_object_sid(self
.unpriv_user_dn
)
375 mod
= "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid
)
377 old_sd
= self
.sd_utils
.read_sd_on_dn("OU=test_computer_ou1," + self
.base_dn
)
379 self
.sd_utils
.dacl_add_ace("OU=test_computer_ou1," + self
.base_dn
, mod
)
381 computername
=self
.computernames
[0]
382 self
.add_computer_ldap(computername
)
384 res
= self
.admin_samdb
.search("%s" % self
.base_dn
,
385 expression
="(&(objectClass=computer)(samAccountName=%s$))" % computername
,
391 m
["description"]= ldb
.MessageElement(
392 ("A description"), ldb
.FLAG_MOD_REPLACE
,
396 # These bits are privileged, but authenticated users have that CAR by default, so this is a pain to test
397 priv_to_auth_users_bits
= set([UF_PASSWD_NOTREQD
, UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED
,
398 UF_DONT_EXPIRE_PASSWD
])
400 # These bits really are privileged, or can't be changed from UF_NORMAL as a non-admin
401 priv_bits
= set([UF_INTERDOMAIN_TRUST_ACCOUNT
, UF_SERVER_TRUST_ACCOUNT
,
402 UF_TRUSTED_FOR_DELEGATION
, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION
,
403 UF_WORKSTATION_TRUST_ACCOUNT
])
405 invalid_bits
= set([UF_TEMP_DUPLICATE_ACCOUNT
, UF_PARTIAL_SECRETS_ACCOUNT
])
410 m
["userAccountControl"] = ldb
.MessageElement(str(bit|UF_PASSWD_NOTREQD
),
411 ldb
.FLAG_MOD_REPLACE
, "userAccountControl")
414 if (bit
in priv_bits
):
415 self
.fail("Unexpectedly able to set userAccountControl bit 0x%08X on %s" % (bit
, m
.dn
))
416 except LdbError
, (enum
, estr
):
417 if bit
in invalid_bits
:
418 self
.assertEqual(enum
, ldb
.ERR_OTHER
, "was not able to set 0x%08X on %s" % (bit
, m
.dn
))
419 # No point going on, try the next bit
421 elif (bit
in priv_bits
):
422 self
.assertEqual(ldb
.ERR_INSUFFICIENT_ACCESS_RIGHTS
, enum
)
424 self
.fail("Unable to set userAccountControl bit 0x%08X on %s: %s" % (bit
, m
.dn
, estr
))
427 def uac_bits_unrelated_modify_helper(self
, account_type
):
428 user_sid
= self
.sd_utils
.get_object_sid(self
.unpriv_user_dn
)
429 mod
= "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid
)
431 old_sd
= self
.sd_utils
.read_sd_on_dn("OU=test_computer_ou1," + self
.base_dn
)
433 self
.sd_utils
.dacl_add_ace("OU=test_computer_ou1," + self
.base_dn
, mod
)
435 computername
=self
.computernames
[0]
436 self
.add_computer_ldap(computername
, others
={"userAccountControl": [str(account_type
)]})
438 res
= self
.admin_samdb
.search("%s" % self
.base_dn
,
439 expression
="(&(objectClass=computer)(samAccountName=%s$))" % computername
,
441 attrs
=["userAccountControl"])
442 self
.assertEqual(int(res
[0]["userAccountControl"][0]), account_type
)
446 m
["description"]= ldb
.MessageElement(
447 ("A description"), ldb
.FLAG_MOD_REPLACE
,
451 invalid_bits
= set([UF_TEMP_DUPLICATE_ACCOUNT
, UF_PARTIAL_SECRETS_ACCOUNT
])
453 # UF_LOCKOUT isn't actually ignored, it changes other
454 # attributes but does not stick here. See MS-SAMR 2.2.1.13
455 # UF_FLAG Codes clarification that UF_SCRIPT and
456 # UF_PASSWD_CANT_CHANGE are simply ignored by both clients and
457 # servers. Other bits are ignored as they are undefined, or
458 # are not set into the attribute (instead triggering other
460 ignored_bits
= set([UF_SCRIPT
, UF_00000004
, UF_LOCKOUT
, UF_PASSWD_CANT_CHANGE
,
461 UF_00000400
, UF_00004000
, UF_00008000
, UF_PASSWORD_EXPIRED
,
462 int("0x10000000", 16), int("0x20000000", 16), int("0x40000000", 16), int("0x80000000", 16)])
463 super_priv_bits
= set([UF_INTERDOMAIN_TRUST_ACCOUNT
])
465 priv_to_remove_bits
= set([UF_TRUSTED_FOR_DELEGATION
, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION
, UF_WORKSTATION_TRUST_ACCOUNT
])
468 # Reset this to the initial position, just to be sure
471 m
["userAccountControl"] = ldb
.MessageElement(str(account_type
),
472 ldb
.FLAG_MOD_REPLACE
, "userAccountControl")
473 self
.admin_samdb
.modify(m
)
475 res
= self
.admin_samdb
.search("%s" % self
.base_dn
,
476 expression
="(&(objectClass=computer)(samAccountName=%s$))" % computername
,
478 attrs
=["userAccountControl"])
480 self
.assertEqual(int(res
[0]["userAccountControl"][0]), account_type
)
484 m
["userAccountControl"] = ldb
.MessageElement(str(bit|UF_PASSWD_NOTREQD
),
485 ldb
.FLAG_MOD_REPLACE
, "userAccountControl")
487 self
.admin_samdb
.modify(m
)
488 if bit
in invalid_bits
:
489 self
.fail("Should have been unable to set userAccountControl bit 0x%08X on %s" % (bit
, m
.dn
))
491 except LdbError
, (enum
, estr
):
492 if bit
in invalid_bits
:
493 self
.assertEqual(enum
, ldb
.ERR_OTHER
)
494 # No point going on, try the next bit
496 elif bit
in super_priv_bits
:
497 self
.assertEqual(enum
, ldb
.ERR_INSUFFICIENT_ACCESS_RIGHTS
)
498 # No point going on, try the next bit
501 self
.fail("Unable to set userAccountControl bit 0x%08X on %s: %s" % (bit
, m
.dn
, estr
))
503 res
= self
.admin_samdb
.search("%s" % self
.base_dn
,
504 expression
="(&(objectClass=computer)(samAccountName=%s$))" % computername
,
506 attrs
=["userAccountControl"])
508 if bit
in ignored_bits
:
509 self
.assertEqual(int(res
[0]["userAccountControl"][0]), UF_NORMAL_ACCOUNT|UF_PASSWD_NOTREQD
, "Bit 0x%08x shouldn't stick" % bit
)
511 if bit
in account_types
:
512 self
.assertEqual(int(res
[0]["userAccountControl"][0]), bit|UF_PASSWD_NOTREQD
, "Bit 0x%08x didn't stick" % bit
)
514 self
.assertEqual(int(res
[0]["userAccountControl"][0]), bit|UF_NORMAL_ACCOUNT|UF_PASSWD_NOTREQD
, "Bit 0x%08x didn't stick" % bit
)
519 m
["userAccountControl"] = ldb
.MessageElement(str(bit|UF_PASSWD_NOTREQD|UF_ACCOUNTDISABLE
),
520 ldb
.FLAG_MOD_REPLACE
, "userAccountControl")
523 except LdbError
, (enum
, estr
):
524 self
.fail("Unable to set userAccountControl bit 0x%08X on %s: %s" % (bit
, m
.dn
, estr
))
526 res
= self
.admin_samdb
.search("%s" % self
.base_dn
,
527 expression
="(&(objectClass=computer)(samAccountName=%s$))" % computername
,
529 attrs
=["userAccountControl"])
531 if bit
in account_types
:
532 self
.assertEqual(int(res
[0]["userAccountControl"][0]),
533 bit|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD
,
534 "bit 0X%08x should have been added (0X%08x vs 0X%08x)"
535 % (bit
, int(res
[0]["userAccountControl"][0]),
536 bit|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD
))
537 elif bit
in ignored_bits
:
538 self
.assertEqual(int(res
[0]["userAccountControl"][0]),
539 UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD
,
540 "bit 0X%08x should have been added (0X%08x vs 0X%08x)"
541 % (bit
, int(res
[0]["userAccountControl"][0]),
542 UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD
))
545 self
.assertEqual(int(res
[0]["userAccountControl"][0]),
546 bit|UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD
,
547 "bit 0X%08x should have been added (0X%08x vs 0X%08x)"
548 % (bit
, int(res
[0]["userAccountControl"][0]),
549 bit|UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD
))
554 m
["userAccountControl"] = ldb
.MessageElement(str(UF_PASSWD_NOTREQD|UF_ACCOUNTDISABLE
),
555 ldb
.FLAG_MOD_REPLACE
, "userAccountControl")
557 if bit
in priv_to_remove_bits
:
558 self
.fail("Should have been unable to remove userAccountControl bit 0x%08X on %s" % (bit
, m
.dn
))
560 except LdbError
, (enum
, estr
):
561 if bit
in priv_to_remove_bits
:
562 self
.assertEqual(enum
, ldb
.ERR_INSUFFICIENT_ACCESS_RIGHTS
)
564 self
.fail("Unexpectedly unable to remove userAccountControl bit 0x%08X on %s: %s" % (bit
, m
.dn
, estr
))
566 res
= self
.admin_samdb
.search("%s" % self
.base_dn
,
567 expression
="(&(objectClass=computer)(samAccountName=%s$))" % computername
,
569 attrs
=["userAccountControl"])
571 if bit
in priv_to_remove_bits
:
572 if bit
in account_types
:
573 self
.assertEqual(int(res
[0]["userAccountControl"][0]),
574 bit|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD
,
575 "bit 0X%08x should not have been removed" % bit
)
577 self
.assertEqual(int(res
[0]["userAccountControl"][0]),
578 bit|UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD
,
579 "bit 0X%08x should not have been removed" % bit
)
581 self
.assertEqual(int(res
[0]["userAccountControl"][0]),
582 UF_NORMAL_ACCOUNT|UF_ACCOUNTDISABLE|UF_PASSWD_NOTREQD
,
583 "bit 0X%08x should have been removed" % bit
)
585 def test_uac_bits_unrelated_modify_normal(self
):
586 self
.uac_bits_unrelated_modify_helper(UF_NORMAL_ACCOUNT
)
588 def test_uac_bits_unrelated_modify_workstation(self
):
589 self
.uac_bits_unrelated_modify_helper(UF_WORKSTATION_TRUST_ACCOUNT
)
591 def test_uac_bits_add(self
):
592 computername
=self
.computernames
[0]
594 user_sid
= self
.sd_utils
.get_object_sid(self
.unpriv_user_dn
)
595 mod
= "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid
)
597 old_sd
= self
.sd_utils
.read_sd_on_dn("OU=test_computer_ou1," + self
.base_dn
)
599 self
.sd_utils
.dacl_add_ace("OU=test_computer_ou1," + self
.base_dn
, mod
)
601 invalid_bits
= set([UF_TEMP_DUPLICATE_ACCOUNT
, UF_PARTIAL_SECRETS_ACCOUNT
])
602 # These bits are privileged, but authenticated users have that CAR by default, so this is a pain to test
603 priv_to_auth_users_bits
= set([UF_PASSWD_NOTREQD
, UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED
,
604 UF_DONT_EXPIRE_PASSWD
])
606 # These bits really are privileged
607 priv_bits
= set([UF_INTERDOMAIN_TRUST_ACCOUNT
, UF_SERVER_TRUST_ACCOUNT
,
608 UF_TRUSTED_FOR_DELEGATION
, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION
])
612 self
.add_computer_ldap(computername
, others
={"userAccountControl": [str(bit
)]})
613 delete_force(self
.admin_samdb
, "CN=%s,OU=test_computer_ou1,%s" % (computername
, self
.base_dn
))
615 self
.fail("Unexpectdly able to set userAccountControl bit 0x%08X on %s" % (bit
, computername
))
617 except LdbError
, (enum
, estr
):
618 if bit
in invalid_bits
:
619 self
.assertEqual(enum
, ldb
.ERR_OTHER
, "Invalid bit 0x%08X was able to be set on %s" % (bit
, computername
))
620 # No point going on, try the next bit
622 elif bit
in priv_bits
:
623 self
.assertEqual(enum
, ldb
.ERR_INSUFFICIENT_ACCESS_RIGHTS
)
626 self
.fail("Unable to set userAccountControl bit 0x%08X on %s: %s" % (bit
, computername
, estr
))
628 def test_primarygroupID_cc_add(self
):
629 computername
=self
.computernames
[0]
631 user_sid
= self
.sd_utils
.get_object_sid(self
.unpriv_user_dn
)
632 mod
= "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid
)
634 old_sd
= self
.sd_utils
.read_sd_on_dn("OU=test_computer_ou1," + self
.base_dn
)
636 self
.sd_utils
.dacl_add_ace("OU=test_computer_ou1," + self
.base_dn
, mod
)
638 # When creating a new object, you can not ever set the primaryGroupID
639 self
.add_computer_ldap(computername
, others
={"primaryGroupID": [str(security
.DOMAIN_RID_ADMINS
)]})
640 self
.fail("Unexpectedly able to set primaryGruopID to be an admin on %s" % computername
)
641 except LdbError
, (enum
, estr
):
642 self
.assertEqual(enum
, ldb
.ERR_UNWILLING_TO_PERFORM
)
645 def test_primarygroupID_priv_DC_modify(self
):
646 computername
=self
.computernames
[0]
648 self
.add_computer_ldap(computername
,
649 others
={"userAccountControl": [str(UF_SERVER_TRUST_ACCOUNT
)]},
650 samdb
=self
.admin_samdb
)
651 res
= self
.admin_samdb
.search("%s" % self
.base_dn
,
652 expression
="(&(objectClass=computer)(samAccountName=%s$))" % computername
,
658 m
.dn
= ldb
.Dn(self
.admin_samdb
, "<SID=%s-%d>" % (str(self
.domain_sid
),
659 security
.DOMAIN_RID_USERS
))
660 m
["member"]= ldb
.MessageElement(
661 [str(res
[0].dn
)], ldb
.FLAG_MOD_ADD
,
663 self
.admin_samdb
.modify(m
)
667 m
["primaryGroupID"]= ldb
.MessageElement(
668 [str(security
.DOMAIN_RID_USERS
)], ldb
.FLAG_MOD_REPLACE
,
671 self
.admin_samdb
.modify(m
)
673 # When creating a new object, you can not ever set the primaryGroupID
674 self
.fail("Unexpectedly able to set primaryGroupID to be other than DCS on %s" % computername
)
675 except LdbError
, (enum
, estr
):
676 self
.assertEqual(enum
, ldb
.ERR_UNWILLING_TO_PERFORM
)
678 def test_primarygroupID_priv_member_modify(self
):
679 computername
=self
.computernames
[0]
681 self
.add_computer_ldap(computername
,
682 others
={"userAccountControl": [str(UF_WORKSTATION_TRUST_ACCOUNT|UF_PARTIAL_SECRETS_ACCOUNT
)]},
683 samdb
=self
.admin_samdb
)
684 res
= self
.admin_samdb
.search("%s" % self
.base_dn
,
685 expression
="(&(objectClass=computer)(samAccountName=%s$))" % computername
,
691 m
.dn
= ldb
.Dn(self
.admin_samdb
, "<SID=%s-%d>" % (str(self
.domain_sid
),
692 security
.DOMAIN_RID_USERS
))
693 m
["member"]= ldb
.MessageElement(
694 [str(res
[0].dn
)], ldb
.FLAG_MOD_ADD
,
696 self
.admin_samdb
.modify(m
)
700 m
["primaryGroupID"]= ldb
.MessageElement(
701 [str(security
.DOMAIN_RID_USERS
)], ldb
.FLAG_MOD_REPLACE
,
704 self
.admin_samdb
.modify(m
)
706 # When creating a new object, you can not ever set the primaryGroupID
707 self
.fail("Unexpectedly able to set primaryGroupID to be other than DCS on %s" % computername
)
708 except LdbError
, (enum
, estr
):
709 self
.assertEqual(enum
, ldb
.ERR_UNWILLING_TO_PERFORM
)
712 def test_primarygroupID_priv_user_modify(self
):
713 computername
=self
.computernames
[0]
715 self
.add_computer_ldap(computername
,
716 others
={"userAccountControl": [str(UF_WORKSTATION_TRUST_ACCOUNT
)]},
717 samdb
=self
.admin_samdb
)
718 res
= self
.admin_samdb
.search("%s" % self
.base_dn
,
719 expression
="(&(objectClass=computer)(samAccountName=%s$))" % computername
,
725 m
.dn
= ldb
.Dn(self
.admin_samdb
, "<SID=%s-%d>" % (str(self
.domain_sid
),
726 security
.DOMAIN_RID_ADMINS
))
727 m
["member"]= ldb
.MessageElement(
728 [str(res
[0].dn
)], ldb
.FLAG_MOD_ADD
,
730 self
.admin_samdb
.modify(m
)
734 m
["primaryGroupID"]= ldb
.MessageElement(
735 [str(security
.DOMAIN_RID_ADMINS
)], ldb
.FLAG_MOD_REPLACE
,
737 self
.admin_samdb
.modify(m
)
740 runner
= SubunitTestRunner()
742 if not runner
.run(unittest
.makeSuite(UserAccountControlTests
)).wasSuccessful():