2022-03-15 09:24:38 +03:00
# Unix SMB/CIFS implementation.
# Copyright (C) Catalyst.NET Ltd 2022
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import os
import random
import string
import sys
import time
import ldb
from samba import param
from samba . auth import system_session
from samba . credentials import Credentials
from samba . dcerpc import security
from samba . ndr import ndr_unpack
from samba . samdb import SamDB
from samba . tests import (
DynamicTestCase ,
TestCase ,
delete_force ,
env_get_var_value ,
)
sys . path . insert ( 0 , ' bin/python ' )
os . environ [ ' PYTHONUNBUFFERED ' ] = ' 1 '
@DynamicTestCase
class SidStringTests ( TestCase ) :
@classmethod
def setUpDynamicTestCases ( cls ) :
if env_get_var_value ( ' CHECK_ALL_COMBINATIONS ' ,
allow_missing = True ) :
for x in string . ascii_uppercase :
for y in string . ascii_uppercase :
code = x + y
if code not in cls . cases :
cls . cases [ code ] = None
for code , expected_sid in cls . cases . items ( ) :
name = code
cls . generate_dynamic_test ( ' test_sid_string ' , name ,
code , expected_sid )
@classmethod
def setUpClass ( cls ) :
super ( ) . setUpClass ( )
server = os . environ [ ' DC_SERVER ' ]
host = f ' ldap:// { server } '
lp = param . LoadParm ( )
lp . load ( os . environ [ ' SMB_CONF_PATH ' ] )
creds = Credentials ( )
creds . guess ( lp )
creds . set_username ( env_get_var_value ( ' DC_USERNAME ' ) )
creds . set_password ( env_get_var_value ( ' DC_PASSWORD ' ) )
cls . ldb = SamDB ( host , credentials = creds ,
session_info = system_session ( lp ) , lp = lp )
cls . base_dn = cls . ldb . domain_dn ( )
cls . schema_dn = cls . ldb . get_schema_basedn ( ) . get_linearized ( )
2023-04-05 06:16:21 +03:00
cls . timestamp = str ( int ( time . time ( ) ) )
2022-03-15 09:24:38 +03:00
def _test_sid_string_with_args ( self , code , expected_sid ) :
random_suffix = random . randint ( 0 , 100000 )
2023-04-05 06:16:21 +03:00
class_name = f ' my-Sid-String-Class- { self . timestamp } - { random_suffix } '
2022-03-15 09:24:38 +03:00
class_ldap_display_name = class_name . replace ( ' - ' , ' ' )
class_dn = f ' CN= { class_name } , { self . schema_dn } '
2022-08-25 11:21:01 +03:00
governs_id = f ' 1.3.6.1.4.1.7165.4.6.2.9. { random_suffix } '
if expected_sid is not None :
# Append the RID to our OID to ensure more uniqueness.
rid = expected_sid . rsplit ( ' - ' , 1 ) [ 1 ]
governs_id + = f ' . { rid } '
2022-03-15 09:24:38 +03:00
ldif = f '''
dn : { class_dn }
objectClass : classSchema
cn : { class_name }
2022-08-25 11:21:01 +03:00
governsId : { governs_id }
2022-03-15 09:24:38 +03:00
subClassOf : top
possSuperiors : domainDNS
defaultSecurityDescriptor : O : { code }
'''
try :
self . ldb . add_ldif ( ldif )
except ldb . LdbError as err :
num , _ = err . args
self . assertEqual ( num , ldb . ERR_UNWILLING_TO_PERFORM )
self . assertIsNone ( expected_sid )
return
# Search for created objectclass
res = self . ldb . search ( class_dn , scope = ldb . SCOPE_BASE ,
attrs = [ ' defaultSecurityDescriptor ' ] )
self . assertEqual ( 1 , len ( res ) )
self . assertEqual ( res [ 0 ] . get ( ' defaultSecurityDescriptor ' , idx = 0 ) ,
f ' O: { code } ' . encode ( ' utf-8 ' ) )
ldif = '''
dn :
changetype : modify
add : schemaUpdateNow
schemaUpdateNow : 1
'''
self . ldb . modify_ldif ( ldif )
2023-04-05 06:16:21 +03:00
object_name = f ' sddl_ { self . timestamp } _ { random_suffix } '
2022-03-15 09:24:38 +03:00
object_dn = f ' CN= { object_name } , { self . base_dn } '
ldif = f '''
dn : { object_dn }
objectClass : { class_ldap_display_name }
cn : { object_name }
'''
self . ldb . add_ldif ( ldif )
# Search for created object
res = self . ldb . search ( object_dn , scope = ldb . SCOPE_BASE ,
attrs = [ ' nTSecurityDescriptor ' ] )
self . assertEqual ( 1 , len ( res ) )
# Delete the object
delete_force ( self . ldb , object_dn )
data = res [ 0 ] . get ( ' nTSecurityDescriptor ' , idx = 0 )
descriptor = ndr_unpack ( security . descriptor , data )
domain_sid = self . ldb . get_domain_sid ( )
if expected_sid is None :
expected_sid = f ' { domain_sid } - { security . DOMAIN_RID_ADMINS } '
else :
expected_sid = expected_sid . format ( domain_sid = domain_sid )
owner_sid = str ( descriptor . owner_sid )
self . assertEqual ( expected_sid , owner_sid )
cases = {
' AA ' : ' S-1-5-32-579 ' ,
' AC ' : ' S-1-15-2-1 ' ,
' AN ' : ' S-1-5-7 ' ,
' AO ' : ' S-1-5-32-548 ' ,
' AP ' : ' {domain_sid} -525 ' ,
' AS ' : ' S-1-18-1 ' ,
' AU ' : ' S-1-5-11 ' ,
' BA ' : ' S-1-5-32-544 ' ,
' BG ' : ' S-1-5-32-546 ' ,
' BO ' : ' S-1-5-32-551 ' ,
' BU ' : ' S-1-5-32-545 ' ,
' CA ' : ' {domain_sid} -517 ' ,
' CD ' : ' S-1-5-32-574 ' ,
' CG ' : ' S-1-3-1 ' ,
' CN ' : ' {domain_sid} -522 ' ,
' CO ' : ' S-1-3-0 ' ,
' CY ' : ' S-1-5-32-569 ' ,
' DC ' : ' {domain_sid} -515 ' ,
' DD ' : ' {domain_sid} -516 ' ,
' DG ' : ' {domain_sid} -514 ' ,
' DU ' : ' {domain_sid} -513 ' ,
' EA ' : ' {domain_sid} -519 ' ,
' ED ' : ' S-1-5-9 ' ,
' EK ' : ' {domain_sid} -527 ' ,
' ER ' : ' S-1-5-32-573 ' ,
' ES ' : ' S-1-5-32-576 ' ,
' HA ' : ' S-1-5-32-578 ' ,
' HI ' : ' S-1-16-12288 ' ,
' IS ' : ' S-1-5-32-568 ' ,
' IU ' : ' S-1-5-4 ' ,
' KA ' : ' {domain_sid} -526 ' ,
' LA ' : ' {domain_sid} -500 ' ,
' LG ' : ' {domain_sid} -501 ' ,
' LS ' : ' S-1-5-19 ' ,
' LU ' : ' S-1-5-32-559 ' ,
' LW ' : ' S-1-16-4096 ' ,
' ME ' : ' S-1-16-8192 ' ,
' MP ' : ' S-1-16-8448 ' ,
' MS ' : ' S-1-5-32-577 ' ,
' MU ' : ' S-1-5-32-558 ' ,
' NO ' : ' S-1-5-32-556 ' ,
' NS ' : ' S-1-5-20 ' ,
' NU ' : ' S-1-5-2 ' ,
' OW ' : ' S-1-3-4 ' ,
' PA ' : ' {domain_sid} -520 ' ,
' PO ' : ' S-1-5-32-550 ' ,
' PS ' : ' S-1-5-10 ' ,
' PU ' : ' S-1-5-32-547 ' ,
' RA ' : ' S-1-5-32-575 ' ,
' RC ' : ' S-1-5-12 ' ,
' RD ' : ' S-1-5-32-555 ' ,
' RE ' : ' S-1-5-32-552 ' ,
' RM ' : ' S-1-5-32-580 ' ,
' RO ' : ' {domain_sid} -498 ' ,
' RS ' : ' {domain_sid} -553 ' ,
' RU ' : ' S-1-5-32-554 ' ,
' SA ' : ' {domain_sid} -518 ' ,
' SI ' : ' S-1-16-16384 ' ,
' SO ' : ' S-1-5-32-549 ' ,
' SS ' : ' S-1-18-2 ' ,
' SU ' : ' S-1-5-6 ' ,
' SY ' : ' S-1-5-18 ' ,
# Not tested, as it always gives us an OPERATIONS_ERROR with Windows.
# 'UD': 'S-1-5-84-0-0-0-0-0',
' WD ' : ' S-1-1-0 ' ,
' WR ' : ' S-1-5-33 ' ,
' aa ' : ' S-1-5-32-579 ' ,
' Aa ' : ' S-1-5-32-579 ' ,
' aA ' : ' S-1-5-32-579 ' ,
' BR ' : None ,
' IF ' : None ,
' LK ' : None ,
}
if __name__ == ' __main__ ' :
global_asn1_print = False
global_hexdump = False
import unittest
unittest . main ( )