2007-12-17 10:20:20 +03:00
# Unix SMB/CIFS implementation.
2010-06-20 03:28:39 +04:00
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
2009-09-19 23:57:41 +04:00
# Copyright (C) Matthias Dieter Wallnoefer 2009
2007-12-17 10:20:20 +03:00
#
# Based on the original in EJS:
2008-01-25 03:02:13 +03:00
# Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
2011-10-21 20:05:07 +04:00
# Copyright (C) Giampaolo Lauria <lauria2@yahoo.com> 2011
2010-11-28 05:34:47 +03:00
#
2007-12-17 10:20:20 +03:00
# 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.
2010-11-28 05:34:47 +03:00
#
2007-12-17 10:20:20 +03:00
# 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.
2010-11-28 05:34:47 +03:00
#
2007-12-17 10:20:20 +03:00
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
2007-12-30 03:14:15 +03:00
""" Convenience functions for using the SAM. """
2007-12-17 10:20:20 +03:00
import samba
2007-12-18 04:21:14 +03:00
import ldb
2008-08-30 01:32:44 +04:00
import time
2009-03-12 07:20:25 +03:00
import base64
2011-08-05 07:09:35 +04:00
import os
2017-12-07 23:38:28 +03:00
import re
2015-09-22 03:11:04 +03:00
from samba import dsdb , dsdb_dns
2010-06-22 20:03:15 +04:00
from samba . ndr import ndr_unpack , ndr_pack
2010-11-28 05:34:47 +03:00
from samba . dcerpc import drsblobs , misc
2011-08-05 05:17:06 +04:00
from samba . common import normalise_int32
2020-09-11 23:29:46 +03:00
from samba . common import get_bytes , cmp
2018-02-26 19:04:00 +03:00
from samba . dcerpc import security
2020-12-03 20:32:09 +03:00
from samba import is_ad_dc_built
2020-09-14 20:12:37 +03:00
import binascii
2007-12-17 10:20:20 +03:00
2008-05-22 19:42:18 +04:00
__docformat__ = " restructuredText "
2010-11-28 05:34:47 +03:00
2018-03-20 04:38:19 +03:00
def get_default_backend_store ( ) :
return " tdb "
2020-10-22 18:29:56 +03:00
class SamDBError ( Exception ) :
pass
class SamDBNotFoundError ( SamDBError ) :
pass
2018-07-30 09:20:39 +03:00
2007-12-17 10:20:20 +03:00
class SamDB ( samba . Ldb ) :
2007-12-30 03:14:15 +03:00
""" The SAM database. """
2008-04-14 13:51:02 +04:00
2010-06-22 20:03:15 +04:00
hash_oid_name = { }
2015-05-25 19:17:55 +03:00
hash_well_known = { }
2010-06-22 20:03:15 +04:00
2009-08-15 17:20:09 +04:00
def __init__ ( self , url = None , lp = None , modules_dir = None , session_info = None ,
2018-01-08 21:43:18 +03:00
credentials = None , flags = ldb . FLG_DONT_CREATE_DB ,
options = None , global_schema = True ,
2010-09-19 06:28:05 +04:00
auto_connect = True , am_rodc = None ) :
2008-04-14 13:51:02 +04:00
self . lp = lp
2010-04-20 05:48:51 +04:00
if not auto_connect :
url = None
elif url is None and lp is not None :
2011-06-02 09:43:40 +04:00
url = lp . samdb_url ( )
2009-08-15 17:20:09 +04:00
2011-08-03 05:31:45 +04:00
self . url = url
2009-08-15 17:20:09 +04:00
super ( SamDB , self ) . __init__ ( url = url , lp = lp , modules_dir = modules_dir ,
2018-07-30 09:16:12 +03:00
session_info = session_info , credentials = credentials , flags = flags ,
options = options )
2009-08-15 17:20:09 +04:00
2010-04-07 14:11:12 +04:00
if global_schema :
2010-06-20 17:22:49 +04:00
dsdb . _dsdb_set_global_schema ( self )
2008-04-14 13:51:02 +04:00
2010-09-19 06:28:05 +04:00
if am_rodc is not None :
dsdb . _dsdb_set_am_rodc ( self , am_rodc )
2010-05-17 13:49:37 +04:00
2009-08-15 17:20:09 +04:00
def connect ( self , url = None , flags = 0 , options = None ) :
2011-09-02 08:42:50 +04:00
''' connect to the database '''
2011-08-05 07:09:35 +04:00
if self . lp is not None and not os . path . exists ( url ) :
2010-04-20 05:48:51 +04:00
url = self . lp . private_path ( url )
2011-08-03 05:31:45 +04:00
self . url = url
2010-04-20 05:48:51 +04:00
super ( SamDB , self ) . connect ( url = url , flags = flags ,
2018-07-30 09:16:12 +03:00
options = options )
2007-12-18 04:21:14 +03:00
2010-09-19 07:22:23 +04:00
def am_rodc ( self ) :
2011-09-02 08:42:50 +04:00
''' return True if we are an RODC '''
2010-09-19 07:22:23 +04:00
return dsdb . _am_rodc ( self )
2011-12-07 02:56:31 +04:00
def am_pdc ( self ) :
''' return True if we are an PDC emulator '''
return dsdb . _am_pdc ( self )
2009-09-18 22:16:05 +04:00
def domain_dn ( self ) :
2011-09-02 08:42:50 +04:00
''' return the domain DN '''
2010-11-15 08:41:59 +03:00
return str ( self . get_default_basedn ( ) )
2009-09-18 22:16:05 +04:00
2018-04-28 08:22:29 +03:00
def schema_dn ( self ) :
''' return the schema partition dn '''
return str ( self . get_schema_basedn ( ) )
2011-11-08 23:59:19 +04:00
def disable_account ( self , search_filter ) :
""" Disables an account
: param search_filter : LDAP filter to find the user ( eg
samccountname = name )
"""
2011-11-15 02:54:29 +04:00
flags = samba . dsdb . UF_ACCOUNTDISABLE
2011-11-08 23:59:19 +04:00
self . toggle_userAccountFlags ( search_filter , flags , on = True )
2010-09-29 02:55:22 +04:00
def enable_account ( self , search_filter ) :
2009-09-18 22:16:05 +04:00
""" Enables an account
2010-11-28 05:34:47 +03:00
: param search_filter : LDAP filter to find the user ( eg
samccountname = name )
2007-12-17 10:20:20 +03:00
"""
2011-06-24 18:37:26 +04:00
flags = samba . dsdb . UF_ACCOUNTDISABLE | samba . dsdb . UF_PASSWD_NOTREQD
self . toggle_userAccountFlags ( search_filter , flags , on = False )
2011-10-21 20:05:07 +04:00
def toggle_userAccountFlags ( self , search_filter , flags , flags_str = None ,
on = True , strict = False ) :
2012-03-07 02:31:55 +04:00
""" Toggle_userAccountFlags
2011-06-24 18:37:26 +04:00
: param search_filter : LDAP filter to find the user ( eg
samccountname = name )
2012-03-07 02:31:55 +04:00
: param flags : samba . dsdb . UF_ * flags
: param on : on = True ( default ) = > set , on = False = > unset
: param strict : strict = False ( default ) ignore if no action is needed
2011-06-24 18:37:26 +04:00
strict = True raises an Exception if . . .
"""
2009-09-18 22:16:05 +04:00
res = self . search ( base = self . domain_dn ( ) , scope = ldb . SCOPE_SUBTREE ,
2010-09-29 02:55:22 +04:00
expression = search_filter , attrs = [ " userAccountControl " ] )
2011-06-01 08:41:51 +04:00
if len ( res ) == 0 :
2011-10-21 20:05:07 +04:00
raise Exception ( " Unable to find account where ' %s ' " % search_filter )
2009-09-18 22:16:05 +04:00
assert ( len ( res ) == 1 )
2011-06-24 18:37:26 +04:00
account_dn = res [ 0 ] . dn
old_uac = int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] )
if on :
if strict and ( old_uac & flags ) :
2011-10-21 20:05:07 +04:00
error = " Account flag(s) ' %s ' already set " % flags_str
2011-06-24 18:37:26 +04:00
raise Exception ( error )
new_uac = old_uac | flags
else :
if strict and not ( old_uac & flags ) :
2011-10-21 20:05:07 +04:00
error = " Account flag(s) ' %s ' already unset " % flags_str
2011-06-24 18:37:26 +04:00
raise Exception ( error )
2009-09-18 22:16:05 +04:00
2011-06-24 18:37:26 +04:00
new_uac = old_uac & ~ flags
if old_uac == new_uac :
return
2008-03-28 04:08:54 +03:00
2007-12-17 10:20:20 +03:00
mod = """
dn : % s
changetype : modify
2011-06-24 18:37:26 +04:00
delete : userAccountControl
userAccountControl : % u
add : userAccountControl
2007-12-17 10:20:20 +03:00
userAccountControl : % u
2011-06-24 18:37:26 +04:00
""" % (account_dn, old_uac, new_uac)
2007-12-30 03:14:15 +03:00
self . modify_ldif ( mod )
2010-11-28 05:34:47 +03:00
2010-09-29 02:55:22 +04:00
def force_password_change_at_next_login ( self , search_filter ) :
2009-09-18 22:16:05 +04:00
""" Forces a password change at next login
2010-11-28 05:34:47 +03:00
: param search_filter : LDAP filter to find the user ( eg
samccountname = name )
2009-06-18 06:38:04 +04:00
"""
2009-09-18 22:16:05 +04:00
res = self . search ( base = self . domain_dn ( ) , scope = ldb . SCOPE_SUBTREE ,
2010-09-29 02:55:22 +04:00
expression = search_filter , attrs = [ ] )
2011-06-01 08:46:04 +04:00
if len ( res ) == 0 :
raise Exception ( ' Unable to find user " %s " ' % search_filter )
2009-09-18 22:16:05 +04:00
assert ( len ( res ) == 1 )
user_dn = res [ 0 ] . dn
2009-06-18 06:38:04 +04:00
mod = """
dn : % s
changetype : modify
replace : pwdLastSet
pwdLastSet : 0
2020-10-22 18:29:56 +03:00
""" % (user_dn)
self . modify_ldif ( mod )
def unlock_account ( self , search_filter ) :
""" Unlock a user account by resetting lockoutTime to 0.
This does also reset the badPwdCount to 0.
: param search_filter : LDAP filter to find the user ( e . g .
sAMAccountName = username )
"""
res = self . search ( base = self . domain_dn ( ) ,
scope = ldb . SCOPE_SUBTREE ,
expression = search_filter ,
attrs = [ ] )
if len ( res ) == 0 :
raise SamDBNotFoundError ( ' Unable to find user " %s " ' % search_filter )
if len ( res ) != 1 :
raise SamDBError ( ' User " %s " is not unique ' % search_filter )
user_dn = res [ 0 ] . dn
mod = """
dn : % s
changetype : modify
replace : lockoutTime
lockoutTime : 0
2009-06-18 06:38:04 +04:00
""" % (user_dn)
self . modify_ldif ( mod )
2010-06-07 20:10:28 +04:00
def newgroup ( self , groupname , groupou = None , grouptype = None ,
2014-10-18 02:34:35 +04:00
description = None , mailaddress = None , notes = None , sd = None ,
gidnumber = None , nisdomain = None ) :
2010-06-07 20:10:28 +04:00
""" Adds a new group with additional parameters
: param groupname : Name of the new group
: param grouptype : Type of the new group
: param description : Description of the new group
: param mailaddress : Email address of the new group
: param notes : Notes of the new group
2014-10-18 02:34:35 +04:00
: param gidnumber : GID Number of the new group
: param nisdomain : NIS Domain Name of the new group
2010-11-24 18:17:15 +03:00
: param sd : security descriptor of the object
2010-06-07 20:10:28 +04:00
"""
2020-08-25 23:16:30 +03:00
if groupou :
group_dn = " CN= %s , %s , %s " % ( groupname , groupou , self . domain_dn ( ) )
else :
group_dn = " CN= %s , %s " % ( groupname , self . get_wellknown_dn (
self . get_default_basedn ( ) ,
dsdb . DS_GUID_USERS_CONTAINER ) )
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
# The new user record. Note the reliance on the SAMLDB module which
2019-03-25 17:02:45 +03:00
# fills in the default information
2010-06-20 03:28:39 +04:00
ldbmessage = { " dn " : group_dn ,
2018-07-30 09:16:12 +03:00
" sAMAccountName " : groupname ,
" objectClass " : " group " }
2010-06-07 20:10:28 +04:00
2010-06-20 03:28:39 +04:00
if grouptype is not None :
2011-08-05 05:17:06 +04:00
ldbmessage [ " groupType " ] = normalise_int32 ( grouptype )
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if description is not None :
ldbmessage [ " description " ] = description
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if mailaddress is not None :
ldbmessage [ " mail " ] = mailaddress
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if notes is not None :
ldbmessage [ " info " ] = notes
2010-06-07 20:10:28 +04:00
2014-10-18 02:34:35 +04:00
if gidnumber is not None :
ldbmessage [ " gidNumber " ] = normalise_int32 ( gidnumber )
if nisdomain is not None :
2015-06-11 22:20:55 +03:00
ldbmessage [ " msSFU30Name " ] = groupname
2014-10-18 02:34:35 +04:00
ldbmessage [ " msSFU30NisDomain " ] = nisdomain
2010-11-24 18:17:15 +03:00
if sd is not None :
ldbmessage [ " nTSecurityDescriptor " ] = ndr_pack ( sd )
2010-06-20 13:59:49 +04:00
self . add ( ldbmessage )
2010-06-07 20:10:28 +04:00
2010-06-20 04:32:23 +04:00
def deletegroup ( self , groupname ) :
2010-06-07 20:10:28 +04:00
""" Deletes a group
: param groupname : Name of the target group
"""
2011-07-28 11:14:28 +04:00
groupfilter = " (&(sAMAccountName= %s )(objectCategory= %s , %s )) " % ( ldb . binary_encode ( groupname ) , " CN=Group,CN=Schema,CN=Configuration " , self . domain_dn ( ) )
2010-06-07 20:10:28 +04:00
self . transaction_start ( )
try :
targetgroup = self . search ( base = self . domain_dn ( ) , scope = ldb . SCOPE_SUBTREE ,
2018-07-30 09:16:12 +03:00
expression = groupfilter , attrs = [ ] )
2010-06-07 20:10:28 +04:00
if len ( targetgroup ) == 0 :
2010-06-20 04:32:23 +04:00
raise Exception ( ' Unable to find group " %s " ' % groupname )
2010-06-07 20:10:28 +04:00
assert ( len ( targetgroup ) == 1 )
2010-09-29 02:35:51 +04:00
self . delete ( targetgroup [ 0 ] . dn )
2012-02-25 18:56:25 +04:00
except :
2010-06-07 20:10:28 +04:00
self . transaction_cancel ( )
raise
else :
self . transaction_commit ( )
2019-08-09 17:19:52 +03:00
def group_member_filter ( self , member , member_types ) :
filter = " "
2019-12-30 15:57:11 +03:00
all_member_types = [ ' user ' ,
' group ' ,
' computer ' ,
' serviceaccount ' ,
' contact ' ,
]
if ' all ' in member_types :
member_types = all_member_types
2019-12-30 15:57:26 +03:00
for member_type in member_types :
if member_type not in all_member_types :
raise Exception ( ' Invalid group member type " %s " . '
' Valid types are %s and all. ' %
( member_type , " , " . join ( all_member_types ) ) )
2019-08-09 17:19:52 +03:00
if ' user ' in member_types :
2019-12-30 18:27:59 +03:00
filter + = ( ' (&(sAMAccountName= %s )(samAccountType= %d )) ' %
( ldb . binary_encode ( member ) , dsdb . ATYPE_NORMAL_ACCOUNT ) )
2019-08-09 17:19:52 +03:00
if ' group ' in member_types :
2019-08-09 17:45:51 +03:00
filter + = ( ' (&(sAMAccountName= %s ) '
' (objectClass=group) '
' (!(groupType:1.2.840.113556.1.4.803:=1))) ' %
2019-08-09 17:19:52 +03:00
ldb . binary_encode ( member ) )
2019-08-09 17:26:58 +03:00
if ' computer ' in member_types :
samaccountname = member
if member [ - 1 ] != ' $ ' :
samaccountname = " %s $ " % member
filter + = ( ' (&(samAccountType= %d ) '
' (!(objectCategory=msDS-ManagedServiceAccount)) '
' (sAMAccountName= %s )) ' %
( dsdb . ATYPE_WORKSTATION_TRUST ,
ldb . binary_encode ( samaccountname ) ) )
if ' serviceaccount ' in member_types :
samaccountname = member
if member [ - 1 ] != ' $ ' :
samaccountname = " %s $ " % member
filter + = ( ' (&(samAccountType= %d ) '
' (objectCategory=msDS-ManagedServiceAccount) '
' (sAMAccountName= %s )) ' %
( dsdb . ATYPE_WORKSTATION_TRUST ,
ldb . binary_encode ( samaccountname ) ) )
if ' contact ' in member_types :
filter + = ( ' (&(objectCategory=Person)(!(objectSid=*))(name= %s )) ' %
ldb . binary_encode ( member ) )
2019-08-09 17:19:52 +03:00
filter = " (| %s ) " % filter
return filter
2012-06-19 14:43:08 +04:00
def add_remove_group_members ( self , groupname , members ,
2019-08-09 17:19:52 +03:00
add_members_operation = True ,
2019-12-30 16:54:32 +03:00
member_types = [ ' user ' , ' group ' , ' computer ' ] ,
member_base_dn = None ) :
2010-06-07 20:10:28 +04:00
""" Adds or removes group members
: param groupname : Name of the target group
2012-06-19 14:43:08 +04:00
: param members : list of group members
2010-11-28 16:09:30 +03:00
: param add_members_operation : Defines if its an add or remove
operation
2010-06-07 20:10:28 +04:00
"""
2011-07-28 11:14:28 +04:00
groupfilter = " (&(sAMAccountName= %s )(objectCategory= %s , %s )) " % (
ldb . binary_encode ( groupname ) , " CN=Group,CN=Schema,CN=Configuration " , self . domain_dn ( ) )
2010-06-07 20:10:28 +04:00
self . transaction_start ( )
try :
targetgroup = self . search ( base = self . domain_dn ( ) , scope = ldb . SCOPE_SUBTREE ,
2018-07-30 09:16:12 +03:00
expression = groupfilter , attrs = [ ' member ' ] )
2010-06-07 20:10:28 +04:00
if len ( targetgroup ) == 0 :
2010-06-20 04:32:23 +04:00
raise Exception ( ' Unable to find group " %s " ' % groupname )
2010-06-07 20:10:28 +04:00
assert ( len ( targetgroup ) == 1 )
modified = False
addtargettogroup = """
dn : % s
changetype : modify
""" % (str(targetgroup[0].dn))
2012-06-19 14:43:08 +04:00
for member in members :
2019-12-17 18:26:23 +03:00
targetmember_dn = None
2019-12-30 16:54:32 +03:00
if member_base_dn is None :
member_base_dn = self . domain_dn ( )
2019-12-17 18:26:23 +03:00
2018-02-26 19:04:00 +03:00
try :
membersid = security . dom_sid ( member )
2019-12-17 18:26:23 +03:00
targetmember_dn = " <SID= %s > " % str ( membersid )
2018-02-26 19:04:00 +03:00
except TypeError as e :
2019-12-17 18:26:23 +03:00
pass
if targetmember_dn is None :
try :
member_dn = ldb . Dn ( self , member )
if member_dn . get_linearized ( ) == member_dn . extended_str ( 1 ) :
full_member_dn = self . normalize_dn_in_domain ( member_dn )
else :
full_member_dn = member_dn
targetmember_dn = full_member_dn . extended_str ( 1 )
except ValueError as e :
pass
if targetmember_dn is None :
filter = self . group_member_filter ( member , member_types )
2019-12-30 16:54:32 +03:00
targetmember = self . search ( base = member_base_dn ,
2019-12-17 18:26:23 +03:00
scope = ldb . SCOPE_SUBTREE ,
expression = filter ,
attrs = [ ] )
if len ( targetmember ) > 1 :
targetmemberlist_str = " "
for msg in targetmember :
targetmemberlist_str + = " %s \n " % msg . get ( " dn " )
raise Exception ( ' Found multiple results for " %s " : \n %s ' %
( member , targetmemberlist_str ) )
if len ( targetmember ) != 1 :
raise Exception ( ' Unable to find " %s " . Operation cancelled. ' % member )
targetmember_dn = targetmember [ 0 ] . dn . extended_str ( 1 )
2018-11-14 19:29:07 +03:00
if add_members_operation is True and ( targetgroup [ 0 ] . get ( ' member ' ) is None or get_bytes ( targetmember_dn ) not in [ str ( x ) for x in targetgroup [ 0 ] [ ' member ' ] ] ) :
2010-09-29 02:35:51 +04:00
modified = True
addtargettogroup + = """ add: member
2010-06-07 20:10:28 +04:00
member : % s
2018-02-26 19:04:00 +03:00
""" % (str(targetmember_dn))
2010-06-07 20:10:28 +04:00
2018-11-14 19:29:07 +03:00
elif add_members_operation is False and ( targetgroup [ 0 ] . get ( ' member ' ) is not None and get_bytes ( targetmember_dn ) in targetgroup [ 0 ] [ ' member ' ] ) :
2010-09-29 02:35:51 +04:00
modified = True
addtargettogroup + = """ delete: member
2010-06-07 20:10:28 +04:00
member : % s
2018-02-26 19:04:00 +03:00
""" % (str(targetmember_dn))
2010-06-07 20:10:28 +04:00
if modified is True :
2010-09-29 02:35:51 +04:00
self . modify_ldif ( addtargettogroup )
2010-06-07 20:10:28 +04:00
2012-02-25 18:56:25 +04:00
except :
2010-06-07 20:10:28 +04:00
self . transaction_cancel ( )
raise
else :
self . transaction_commit ( )
2020-08-24 17:34:35 +03:00
def prepare_attr_replace ( self , msg , old , attr_name , value ) :
""" Changes the MessageElement with the given attr_name of the
given Message . If the value is " " set an empty value and the flag
FLAG_MOD_DELETE , otherwise set the new value and FLAG_MOD_REPLACE .
If the value is None or the Message contains the attr_name with this
value , nothing will changed . """
# skip unchanged attribute
if value is None :
return
if attr_name in old and str ( value ) == str ( old [ attr_name ] ) :
return
# remove attribute
if len ( value ) == 0 :
if attr_name in old :
el = ldb . MessageElement ( [ ] , ldb . FLAG_MOD_DELETE , attr_name )
msg . add ( el )
return
# change attribute
el = ldb . MessageElement ( value , ldb . FLAG_MOD_REPLACE , attr_name )
msg . add ( el )
2020-08-24 17:39:01 +03:00
def fullname_from_names ( self , given_name = None , initials = None , surname = None ,
old_attrs = { } , fallback_default = " " ) :
""" Prepares new combined fullname, using the name parts.
Used for things like displayName or cn .
Use the original name values , if no new one is specified . """
attrs = { " givenName " : given_name ,
" initials " : initials ,
" sn " : surname }
# if the attribute is not specified, try to use the old one
for attr_name , attr_value in attrs . items ( ) :
if attr_value == None and attr_name in old_attrs :
attrs [ attr_name ] = str ( old_attrs [ attr_name ] )
# add '.' to initials if initals are not None and not "" and if the initials
# don't have already a '.' at the end
if attrs [ " initials " ] and not attrs [ " initials " ] . endswith ( ' . ' ) :
attrs [ " initials " ] + = ' . '
# remove empty values (None and '')
attrs_values = list ( filter ( None , attrs . values ( ) ) )
# fullname is the combination of not-empty values as string, separated by ' '
fullname = ' ' . join ( attrs_values )
if fullname == ' ' :
return fallback_default
return fullname
2010-05-09 12:54:19 +04:00
def newuser ( self , username , password ,
2018-07-30 09:16:12 +03:00
force_password_change_at_next_login_req = False ,
useusernameascn = False , userou = None , surname = None , givenname = None ,
initials = None , profilepath = None , scriptpath = None , homedrive = None ,
homedirectory = None , jobtitle = None , department = None , company = None ,
description = None , mailaddress = None , internetaddress = None ,
telephonenumber = None , physicaldeliveryoffice = None , sd = None ,
setpassword = True , uidnumber = None , gidnumber = None , gecos = None ,
loginshell = None , uid = None , nisdomain = None , unixhome = None ,
smartcard_required = False ) :
2010-06-07 20:10:28 +04:00
""" Adds a new user with additional parameters
2009-09-18 22:16:05 +04:00
2009-09-21 01:49:05 +04:00
: param username : Name of the new user
2007-12-30 03:14:15 +03:00
: param password : Password for the new user
2009-09-21 01:49:05 +04:00
: param force_password_change_at_next_login_req : Force password change
2010-11-28 05:34:47 +03:00
: param useusernameascn : Use username as cn rather that firstname +
initials + lastname
2010-06-07 20:10:28 +04:00
: param userou : Object container ( without domainDN postfix ) for new user
: param surname : Surname of the new user
: param givenname : First name of the new user
: param initials : Initials of the new user
: param profilepath : Profile path of the new user
: param scriptpath : Logon script path of the new user
: param homedrive : Home drive of the new user
: param homedirectory : Home directory of the new user
: param jobtitle : Job title of the new user
: param department : Department of the new user
: param company : Company of the new user
: param description : of the new user
: param mailaddress : Email address of the new user
: param internetaddress : Home page of the new user
: param telephonenumber : Phone number of the new user
: param physicaldeliveryoffice : Office location of the new user
2010-11-23 18:48:53 +03:00
: param sd : security descriptor of the object
: param setpassword : optionally disable password reset
2012-09-30 06:31:59 +04:00
: param uidnumber : RFC2307 Unix numeric UID of the new user
: param gidnumber : RFC2307 Unix primary GID of the new user
: param gecos : RFC2307 Unix GECOS field of the new user
: param loginshell : RFC2307 Unix login shell of the new user
: param uid : RFC2307 Unix username of the new user
2015-01-31 21:44:26 +03:00
: param nisdomain : RFC2307 Unix NIS domain of the new user
: param unixhome : RFC2307 Unix home directory of the new user
2016-06-02 15:17:17 +03:00
: param smartcard_required : set the UF_SMARTCARD_REQUIRED bit of the new user
2010-06-20 03:28:39 +04:00
"""
2010-06-07 20:10:28 +04:00
2020-08-24 17:39:01 +03:00
displayname = self . fullname_from_names ( given_name = givenname ,
initials = initials ,
surname = surname )
2010-06-20 03:28:39 +04:00
cn = username
2018-10-26 10:25:59 +03:00
if useusernameascn is None and displayname != " " :
2010-06-07 20:10:28 +04:00
cn = displayname
2020-08-25 21:44:02 +03:00
if userou :
user_dn = " CN= %s , %s , %s " % ( cn , userou , self . domain_dn ( ) )
else :
user_dn = " CN= %s , %s " % ( cn , self . get_wellknown_dn (
self . get_default_basedn ( ) ,
dsdb . DS_GUID_USERS_CONTAINER ) )
2009-02-11 19:54:58 +03:00
2010-10-19 12:56:07 +04:00
dnsdomain = ldb . Dn ( self , self . domain_dn ( ) ) . canonical_str ( ) . replace ( " / " , " " )
user_principal_name = " %s @ %s " % ( username , dnsdomain )
2010-06-08 23:33:56 +04:00
# The new user record. Note the reliance on the SAMLDB module which
2019-03-25 17:02:45 +03:00
# fills in the default information
2010-06-20 03:28:39 +04:00
ldbmessage = { " dn " : user_dn ,
2010-09-29 02:35:51 +04:00
" sAMAccountName " : username ,
2010-10-16 22:51:09 +04:00
" userPrincipalName " : user_principal_name ,
2010-09-29 02:35:51 +04:00
" objectClass " : " user " }
2010-06-07 20:10:28 +04:00
2016-06-02 15:17:17 +03:00
if smartcard_required :
2018-08-22 08:06:23 +03:00
ldbmessage [ " userAccountControl " ] = str ( dsdb . UF_NORMAL_ACCOUNT |
dsdb . UF_SMARTCARD_REQUIRED )
2016-06-02 15:17:17 +03:00
setpassword = False
2010-06-20 03:28:39 +04:00
if surname is not None :
2010-06-08 23:33:56 +04:00
ldbmessage [ " sn " ] = surname
2010-06-07 20:10:28 +04:00
2010-06-20 03:28:39 +04:00
if givenname is not None :
2010-06-08 23:33:56 +04:00
ldbmessage [ " givenName " ] = givenname
2010-06-07 20:10:28 +04:00
2018-10-26 10:25:59 +03:00
if displayname != " " :
2010-06-08 23:33:56 +04:00
ldbmessage [ " displayName " ] = displayname
ldbmessage [ " name " ] = displayname
2010-06-07 20:10:28 +04:00
2010-06-20 03:28:39 +04:00
if initials is not None :
2010-06-08 23:33:56 +04:00
ldbmessage [ " initials " ] = ' %s . ' % initials
2010-06-07 20:10:28 +04:00
2010-06-20 03:28:39 +04:00
if profilepath is not None :
2010-06-08 23:33:56 +04:00
ldbmessage [ " profilePath " ] = profilepath
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if scriptpath is not None :
ldbmessage [ " scriptPath " ] = scriptpath
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if homedrive is not None :
ldbmessage [ " homeDrive " ] = homedrive
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if homedirectory is not None :
ldbmessage [ " homeDirectory " ] = homedirectory
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if jobtitle is not None :
ldbmessage [ " title " ] = jobtitle
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if department is not None :
ldbmessage [ " department " ] = department
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if company is not None :
ldbmessage [ " company " ] = company
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if description is not None :
ldbmessage [ " description " ] = description
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if mailaddress is not None :
ldbmessage [ " mail " ] = mailaddress
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if internetaddress is not None :
ldbmessage [ " wWWHomePage " ] = internetaddress
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if telephonenumber is not None :
ldbmessage [ " telephoneNumber " ] = telephonenumber
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if physicaldeliveryoffice is not None :
ldbmessage [ " physicalDeliveryOfficeName " ] = physicaldeliveryoffice
2010-06-07 20:10:28 +04:00
2010-11-23 18:48:53 +03:00
if sd is not None :
ldbmessage [ " nTSecurityDescriptor " ] = ndr_pack ( sd )
2012-09-30 06:31:59 +04:00
ldbmessage2 = None
2015-01-31 21:44:26 +03:00
if any ( map ( lambda b : b is not None , ( uid , uidnumber , gidnumber , gecos ,
2018-07-30 09:16:12 +03:00
loginshell , nisdomain , unixhome ) ) ) :
2012-09-30 06:31:59 +04:00
ldbmessage2 = ldb . Message ( )
ldbmessage2 . dn = ldb . Dn ( self , user_dn )
if uid is not None :
ldbmessage2 [ " uid " ] = ldb . MessageElement ( str ( uid ) , ldb . FLAG_MOD_REPLACE , ' uid ' )
if uidnumber is not None :
ldbmessage2 [ " uidNumber " ] = ldb . MessageElement ( str ( uidnumber ) , ldb . FLAG_MOD_REPLACE , ' uidNumber ' )
if gidnumber is not None :
ldbmessage2 [ " gidNumber " ] = ldb . MessageElement ( str ( gidnumber ) , ldb . FLAG_MOD_REPLACE , ' gidNumber ' )
if gecos is not None :
ldbmessage2 [ " gecos " ] = ldb . MessageElement ( str ( gecos ) , ldb . FLAG_MOD_REPLACE , ' gecos ' )
if loginshell is not None :
ldbmessage2 [ " loginShell " ] = ldb . MessageElement ( str ( loginshell ) , ldb . FLAG_MOD_REPLACE , ' loginShell ' )
2015-01-31 21:44:26 +03:00
if unixhome is not None :
ldbmessage2 [ " unixHomeDirectory " ] = ldb . MessageElement (
str ( unixhome ) , ldb . FLAG_MOD_REPLACE , ' unixHomeDirectory ' )
if nisdomain is not None :
ldbmessage2 [ " msSFU30NisDomain " ] = ldb . MessageElement (
str ( nisdomain ) , ldb . FLAG_MOD_REPLACE , ' msSFU30NisDomain ' )
ldbmessage2 [ " msSFU30Name " ] = ldb . MessageElement (
str ( username ) , ldb . FLAG_MOD_REPLACE , ' msSFU30Name ' )
ldbmessage2 [ " unixUserPassword " ] = ldb . MessageElement (
' ABCD!efgh12345$67890 ' , ldb . FLAG_MOD_REPLACE ,
' unixUserPassword ' )
2012-09-30 06:31:59 +04:00
2010-06-08 23:33:56 +04:00
self . transaction_start ( )
try :
2010-06-07 20:10:28 +04:00
self . add ( ldbmessage )
2012-09-30 06:31:59 +04:00
if ldbmessage2 :
self . modify ( ldbmessage2 )
2009-02-11 19:54:58 +03:00
2009-09-19 23:57:41 +04:00
# Sets the password for it
2010-11-23 18:48:53 +03:00
if setpassword :
2018-02-02 00:35:25 +03:00
self . setpassword ( ( " (distinguishedName= %s ) " %
ldb . binary_encode ( user_dn ) ) ,
password ,
2010-11-23 18:48:53 +03:00
force_password_change_at_next_login_req )
2012-02-25 18:56:25 +04:00
except :
2009-02-11 19:54:58 +03:00
self . transaction_cancel ( )
raise
2010-04-08 23:01:17 +04:00
else :
self . transaction_commit ( )
2008-03-28 04:08:54 +03:00
2019-03-19 19:55:37 +03:00
def newcontact ( self ,
fullcontactname = None ,
ou = None ,
surname = None ,
givenname = None ,
initials = None ,
displayname = None ,
jobtitle = None ,
department = None ,
company = None ,
description = None ,
mailaddress = None ,
internetaddress = None ,
telephonenumber = None ,
mobilenumber = None ,
physicaldeliveryoffice = None ) :
""" Adds a new contact with additional parameters
: param fullcontactname : Optional full name of the new contact
: param ou : Object container for new contact
: param surname : Surname of the new contact
: param givenname : First name of the new contact
: param initials : Initials of the new contact
: param displayname : displayName of the new contact
: param jobtitle : Job title of the new contact
: param department : Department of the new contact
: param company : Company of the new contact
: param description : Description of the new contact
: param mailaddress : Email address of the new contact
: param internetaddress : Home page of the new contact
: param telephonenumber : Phone number of the new contact
: param mobilenumber : Primary mobile number of the new contact
: param physicaldeliveryoffice : Office location of the new contact
"""
# Prepare the contact name like the RSAT, using the name parts.
2020-08-24 17:39:01 +03:00
cn = self . fullname_from_names ( given_name = givenname ,
initials = initials ,
surname = surname )
2019-03-19 19:55:37 +03:00
# Use the specified fullcontactname instead of the previously prepared
# contact name, if it is specified.
# This is similar to the "Full name" value of the RSAT.
if fullcontactname is not None :
cn = fullcontactname
if fullcontactname is None and cn == " " :
raise Exception ( ' No name for contact specified ' )
contactcontainer_dn = self . domain_dn ( )
if ou :
contactcontainer_dn = self . normalize_dn_in_domain ( ou )
contact_dn = " CN= %s , %s " % ( cn , contactcontainer_dn )
ldbmessage = { " dn " : contact_dn ,
" objectClass " : " contact " ,
}
if surname is not None :
ldbmessage [ " sn " ] = surname
if givenname is not None :
ldbmessage [ " givenName " ] = givenname
if displayname is not None :
ldbmessage [ " displayName " ] = displayname
if initials is not None :
ldbmessage [ " initials " ] = ' %s . ' % initials
if jobtitle is not None :
ldbmessage [ " title " ] = jobtitle
if department is not None :
ldbmessage [ " department " ] = department
if company is not None :
ldbmessage [ " company " ] = company
if description is not None :
ldbmessage [ " description " ] = description
if mailaddress is not None :
ldbmessage [ " mail " ] = mailaddress
if internetaddress is not None :
ldbmessage [ " wWWHomePage " ] = internetaddress
if telephonenumber is not None :
ldbmessage [ " telephoneNumber " ] = telephonenumber
if mobilenumber is not None :
ldbmessage [ " mobile " ] = mobilenumber
if physicaldeliveryoffice is not None :
ldbmessage [ " physicalDeliveryOfficeName " ] = physicaldeliveryoffice
self . add ( ldbmessage )
return cn
2017-12-07 23:38:28 +03:00
def newcomputer ( self , computername , computerou = None , description = None ,
2018-03-13 06:47:58 +03:00
prepare_oldjoin = False , ip_address_list = None ,
service_principal_name_list = None ) :
2017-12-07 23:38:28 +03:00
""" Adds a new user with additional parameters
: param computername : Name of the new computer
: param computerou : Object container for new computer
: param description : Description of the new computer
: param prepare_oldjoin : Preset computer password for oldjoin mechanism
2018-03-13 06:47:58 +03:00
: param ip_address_list : ip address list for DNS A or AAAA record
: param service_principal_name_list : string list of servicePincipalName
2017-12-07 23:38:28 +03:00
"""
cn = re . sub ( r " \ $$ " , " " , computername )
if cn . count ( ' $ ' ) :
raise Exception ( ' Illegal computername " %s " ' % computername )
samaccountname = " %s $ " % cn
2020-08-26 17:15:07 +03:00
computercontainer_dn = self . get_wellknown_dn ( self . get_default_basedn ( ) ,
dsdb . DS_GUID_COMPUTERS_CONTAINER )
2017-12-07 23:38:28 +03:00
if computerou :
computercontainer_dn = self . normalize_dn_in_domain ( computerou )
computer_dn = " CN= %s , %s " % ( cn , computercontainer_dn )
ldbmessage = { " dn " : computer_dn ,
" sAMAccountName " : samaccountname ,
" objectClass " : " computer " ,
}
if description is not None :
ldbmessage [ " description " ] = description
2018-03-13 06:47:58 +03:00
if service_principal_name_list :
ldbmessage [ " servicePrincipalName " ] = service_principal_name_list
2017-12-07 23:38:28 +03:00
accountcontrol = str ( dsdb . UF_WORKSTATION_TRUST_ACCOUNT |
dsdb . UF_ACCOUNTDISABLE )
if prepare_oldjoin :
accountcontrol = str ( dsdb . UF_WORKSTATION_TRUST_ACCOUNT )
ldbmessage [ " userAccountControl " ] = accountcontrol
2018-03-13 06:47:58 +03:00
if ip_address_list :
ldbmessage [ ' dNSHostName ' ] = ' {} . {} ' . format (
cn , self . domain_dns_name ( ) )
2017-12-07 23:38:28 +03:00
self . transaction_start ( )
try :
self . add ( ldbmessage )
if prepare_oldjoin :
password = cn . lower ( )
self . setpassword ( ( " (distinguishedName= %s ) " %
ldb . binary_encode ( computer_dn ) ) ,
password , False )
except :
self . transaction_cancel ( )
raise
else :
self . transaction_commit ( )
2011-08-15 06:06:59 +04:00
def deleteuser ( self , username ) :
""" Deletes a user
: param username : Name of the target user
"""
filter = " (&(sAMAccountName= %s )(objectCategory= %s , %s )) " % ( ldb . binary_encode ( username ) , " CN=Person,CN=Schema,CN=Configuration " , self . domain_dn ( ) )
self . transaction_start ( )
try :
target = self . search ( base = self . domain_dn ( ) , scope = ldb . SCOPE_SUBTREE ,
expression = filter , attrs = [ ] )
if len ( target ) == 0 :
raise Exception ( ' Unable to find user " %s " ' % username )
assert ( len ( target ) == 1 )
self . delete ( target [ 0 ] . dn )
2012-02-25 18:56:25 +04:00
except :
2011-08-15 06:06:59 +04:00
self . transaction_cancel ( )
raise
else :
self . transaction_commit ( )
2010-09-29 02:55:22 +04:00
def setpassword ( self , search_filter , password ,
2018-07-30 09:16:12 +03:00
force_change_at_next_login = False , username = None ) :
2009-09-18 22:16:05 +04:00
""" Sets the password for a user
2010-11-28 05:34:47 +03:00
: param search_filter : LDAP filter to find the user ( eg
samccountname = name )
2008-03-28 04:08:54 +03:00
: param password : Password for the user
2010-04-04 05:30:03 +04:00
: param force_change_at_next_login : Force password change
2008-03-28 04:08:54 +03:00
"""
self . transaction_start ( )
2009-02-11 19:54:58 +03:00
try :
2009-08-15 17:20:09 +04:00
res = self . search ( base = self . domain_dn ( ) , scope = ldb . SCOPE_SUBTREE ,
2010-09-29 02:55:22 +04:00
expression = search_filter , attrs = [ ] )
2010-04-15 11:15:25 +04:00
if len ( res ) == 0 :
2010-09-29 02:55:22 +04:00
raise Exception ( ' Unable to find user " %s " ' % ( username or search_filter ) )
2010-11-29 08:57:25 +03:00
if len ( res ) > 1 :
raise Exception ( ' Matched %u multiple users with filter " %s " ' % ( len ( res ) , search_filter ) )
2009-02-11 19:54:58 +03:00
user_dn = res [ 0 ] . dn
2020-07-04 05:27:06 +03:00
if not isinstance ( password , str ) :
2018-05-14 15:38:20 +03:00
pw = password . decode ( ' utf-8 ' )
else :
pw = password
pw = ( ' " ' + pw + ' " ' ) . encode ( ' utf-16-le ' )
2009-09-10 02:46:51 +04:00
setpw = """
dn : % s
changetype : modify
2010-07-08 11:36:30 +04:00
replace : unicodePwd
unicodePwd : : % s
2018-01-30 20:09:00 +03:00
""" % (user_dn, base64.b64encode(pw).decode( ' utf-8 ' ))
2009-02-11 19:54:58 +03:00
2009-09-10 02:46:51 +04:00
self . modify_ldif ( setpw )
2009-02-11 19:54:58 +03:00
2010-04-04 05:30:03 +04:00
if force_change_at_next_login :
2009-09-21 01:49:05 +04:00
self . force_password_change_at_next_login (
2018-07-30 09:14:13 +03:00
" (distinguishedName= " + str ( user_dn ) + " ) " )
2009-06-17 03:14:17 +04:00
2009-02-11 19:54:58 +03:00
# modify the userAccountControl to remove the disabled bit
2010-09-29 02:55:22 +04:00
self . enable_account ( search_filter )
2012-02-25 18:56:25 +04:00
except :
2009-02-11 19:54:58 +03:00
self . transaction_cancel ( )
raise
2010-04-08 23:01:17 +04:00
else :
self . transaction_commit ( )
2007-12-17 10:20:20 +03:00
2010-09-29 02:55:22 +04:00
def setexpiry ( self , search_filter , expiry_seconds , no_expiry_req = False ) :
2009-09-18 22:16:05 +04:00
""" Sets the account expiry for a user
2010-11-28 05:34:47 +03:00
: param search_filter : LDAP filter to find the user ( eg
samaccountname = name )
2008-08-30 01:32:44 +04:00
: param expiry_seconds : expiry time from now in seconds
2009-09-21 01:49:05 +04:00
: param no_expiry_req : if set , then don ' t expire password
2008-08-30 01:32:44 +04:00
"""
2009-02-11 19:54:58 +03:00
self . transaction_start ( )
try :
res = self . search ( base = self . domain_dn ( ) , scope = ldb . SCOPE_SUBTREE ,
2018-07-30 09:16:12 +03:00
expression = search_filter ,
attrs = [ " userAccountControl " , " accountExpires " ] )
2011-06-01 08:46:04 +04:00
if len ( res ) == 0 :
raise Exception ( ' Unable to find user " %s " ' % search_filter )
2009-09-21 01:49:05 +04:00
assert ( len ( res ) == 1 )
2009-09-18 22:16:05 +04:00
user_dn = res [ 0 ] . dn
2009-02-11 19:54:58 +03:00
userAccountControl = int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] )
accountExpires = int ( res [ 0 ] [ " accountExpires " ] [ 0 ] )
2009-09-21 01:49:05 +04:00
if no_expiry_req :
2009-02-11 19:54:58 +03:00
userAccountControl = userAccountControl | 0x10000
accountExpires = 0
else :
userAccountControl = userAccountControl & ~ 0x10000
2010-04-04 02:30:34 +04:00
accountExpires = samba . unix2nttime ( expiry_seconds + int ( time . time ( ) ) )
2009-02-11 19:54:58 +03:00
2009-09-18 22:16:05 +04:00
setexp = """
2009-03-12 05:13:14 +03:00
dn : % s
changetype : modify
replace : userAccountControl
userAccountControl : % u
replace : accountExpires
accountExpires : % u
2009-09-18 22:16:05 +04:00
""" % (user_dn, userAccountControl, accountExpires)
self . modify_ldif ( setexp )
2012-02-25 18:56:25 +04:00
except :
2009-02-11 19:54:58 +03:00
self . transaction_cancel ( )
raise
2010-04-08 23:01:17 +04:00
else :
self . transaction_commit ( )
2010-04-04 05:30:03 +04:00
def set_domain_sid ( self , sid ) :
""" Change the domain SID used by this LDB.
: param sid : The new domain sid to use .
"""
2010-06-20 17:22:49 +04:00
dsdb . _samdb_set_domain_sid ( self , sid )
2010-04-04 05:30:03 +04:00
def get_domain_sid ( self ) :
2010-11-28 05:34:47 +03:00
""" Read the domain SID used by this LDB. """
2010-08-20 01:26:53 +04:00
return dsdb . _samdb_get_domain_sid ( self )
2010-04-04 05:30:03 +04:00
2010-11-28 16:09:30 +03:00
domain_sid = property ( get_domain_sid , set_domain_sid ,
2018-10-25 12:09:59 +03:00
doc = " SID for the domain " )
2010-11-28 16:09:30 +03:00
2010-04-04 05:30:03 +04:00
def set_invocation_id ( self , invocation_id ) :
""" Set the invocation id for this SamDB handle.
: param invocation_id : GUID of the invocation id .
"""
2010-06-20 17:22:49 +04:00
dsdb . _dsdb_set_ntds_invocation_id ( self , invocation_id )
2010-04-04 05:30:03 +04:00
2010-11-28 16:09:30 +03:00
def get_invocation_id ( self ) :
""" Get the invocation_id id """
return dsdb . _samdb_ntds_invocation_id ( self )
invocation_id = property ( get_invocation_id , set_invocation_id ,
2018-10-25 12:09:59 +03:00
doc = " Invocation ID GUID " )
2010-11-28 16:09:30 +03:00
2010-06-20 03:28:39 +04:00
def get_oid_from_attid ( self , attid ) :
2010-06-20 17:22:49 +04:00
return dsdb . _dsdb_get_oid_from_attid ( self , attid )
2010-06-20 03:28:39 +04:00
2010-11-28 05:34:47 +03:00
def get_attid_from_lDAPDisplayName ( self , ldap_display_name ,
2018-07-30 09:16:12 +03:00
is_schema_nc = False ) :
2011-06-22 08:41:50 +04:00
''' return the attribute ID for a LDAP attribute as an integer as found in DRSUAPI '''
2010-11-28 05:34:47 +03:00
return dsdb . _dsdb_get_attid_from_lDAPDisplayName ( self ,
2018-07-30 09:16:12 +03:00
ldap_display_name , is_schema_nc )
2010-08-24 16:08:27 +04:00
2011-06-22 08:41:50 +04:00
def get_syntax_oid_from_lDAPDisplayName ( self , ldap_display_name ) :
''' return the syntax OID for a LDAP attribute as a string '''
return dsdb . _dsdb_get_syntax_oid_from_lDAPDisplayName ( self , ldap_display_name )
2011-07-07 00:29:58 +04:00
def get_systemFlags_from_lDAPDisplayName ( self , ldap_display_name ) :
''' return the systemFlags for a LDAP attribute as a integer '''
return dsdb . _dsdb_get_systemFlags_from_lDAPDisplayName ( self , ldap_display_name )
def get_linkId_from_lDAPDisplayName ( self , ldap_display_name ) :
''' return the linkID for a LDAP attribute as a integer '''
return dsdb . _dsdb_get_linkId_from_lDAPDisplayName ( self , ldap_display_name )
2011-07-11 08:27:21 +04:00
def get_lDAPDisplayName_by_attid ( self , attid ) :
''' return the lDAPDisplayName from an integer DRS attribute ID '''
return dsdb . _dsdb_get_lDAPDisplayName_by_attid ( self , attid )
2011-07-11 10:55:11 +04:00
def get_backlink_from_lDAPDisplayName ( self , ldap_display_name ) :
''' return the attribute name of the corresponding backlink from the name
of a forward link attribute . If there is no backlink return None '''
return dsdb . _dsdb_get_backlink_from_lDAPDisplayName ( self , ldap_display_name )
2010-04-20 05:48:51 +04:00
def set_ntds_settings_dn ( self , ntds_settings_dn ) :
2010-11-28 05:34:47 +03:00
""" Set the NTDS Settings DN, as would be returned on the dsServiceName
rootDSE attribute .
2010-04-20 05:48:51 +04:00
This allows the DN to be set before the database fully exists
: param ntds_settings_dn : The new DN to use
"""
2010-06-20 17:22:49 +04:00
dsdb . _samdb_set_ntds_settings_dn ( self , ntds_settings_dn )
2010-04-20 05:48:51 +04:00
2010-04-04 05:30:03 +04:00
def get_ntds_GUID ( self ) :
2010-11-28 05:34:47 +03:00
""" Get the NTDS objectGUID """
2010-06-20 17:22:49 +04:00
return dsdb . _samdb_ntds_objectGUID ( self )
2010-04-04 05:30:03 +04:00
2020-12-23 15:00:34 +03:00
def get_timestr ( self ) :
""" Get the current time as generalized time string """
res = self . search ( base = " " ,
scope = ldb . SCOPE_BASE ,
attrs = [ " currentTime " ] )
return str ( res [ 0 ] [ " currentTime " ] [ 0 ] )
def get_time ( self ) :
""" Get the current time as UNIX time """
return ldb . string_to_time ( self . get_timestr ( ) )
def get_nttime ( self ) :
""" Get the current time as NT time """
return samba . unix2nttime ( self . get_time ( ) )
2010-04-04 05:30:03 +04:00
def server_site_name ( self ) :
2010-11-28 05:34:47 +03:00
""" Get the server site name """
2010-06-20 17:22:49 +04:00
return dsdb . _samdb_server_site_name ( self )
2010-04-09 00:07:42 +04:00
2011-08-22 11:40:45 +04:00
def host_dns_name ( self ) :
""" return the DNS name of this host """
res = self . search ( base = ' ' , scope = ldb . SCOPE_BASE , attrs = [ ' dNSHostName ' ] )
2018-04-26 20:22:21 +03:00
return str ( res [ 0 ] [ ' dNSHostName ' ] [ 0 ] )
2011-08-22 11:40:45 +04:00
def domain_dns_name ( self ) :
""" return the DNS name of the domain root """
domain_dn = self . get_default_basedn ( )
return domain_dn . canonical_str ( ) . split ( ' / ' ) [ 0 ]
2020-11-10 03:46:28 +03:00
def domain_netbios_name ( self ) :
""" return the NetBIOS name of the domain root """
domain_dn = self . get_default_basedn ( )
dns_name = self . domain_dns_name ( )
filter = " (&(objectClass=crossRef)(nETBIOSName=*)(ncName= %s )(dnsroot= %s )) " % ( domain_dn , dns_name )
partitions_dn = self . get_partitions_dn ( )
res = self . search ( partitions_dn ,
scope = ldb . SCOPE_ONELEVEL ,
expression = filter )
try :
netbios_domain = res [ 0 ] [ " nETBIOSName " ] [ 0 ] . decode ( )
except IndexError :
return None
return netbios_domain
2011-08-22 11:40:45 +04:00
def forest_dns_name ( self ) :
""" return the DNS name of the forest root """
forest_dn = self . get_root_basedn ( )
return forest_dn . canonical_str ( ) . split ( ' / ' ) [ 0 ]
2010-04-09 00:07:42 +04:00
def load_partition_usn ( self , base_dn ) :
2010-06-20 17:22:49 +04:00
return dsdb . _dsdb_load_partition_usn ( self , base_dn )
2010-06-20 04:32:23 +04:00
2012-08-10 02:44:04 +04:00
def set_schema ( self , schema , write_indices_and_attributes = True ) :
self . set_schema_from_ldb ( schema . ldb , write_indices_and_attributes = write_indices_and_attributes )
2010-06-20 04:32:23 +04:00
2012-08-10 02:44:04 +04:00
def set_schema_from_ldb ( self , ldb_conn , write_indices_and_attributes = True ) :
dsdb . _dsdb_set_schema_from_ldb ( self , ldb_conn , write_indices_and_attributes )
2010-06-20 04:32:23 +04:00
2018-05-19 05:19:58 +03:00
def set_schema_update_now ( self ) :
ldif = """
dn :
changetype : modify
add : schemaUpdateNow
schemaUpdateNow : 1
"""
self . modify_ldif ( ldif )
2010-11-05 06:06:10 +03:00
def dsdb_DsReplicaAttribute ( self , ldb , ldap_display_name , ldif_elements ) :
2011-06-17 05:29:44 +04:00
''' convert a list of attribute values to a DRSUAPI DsReplicaAttribute '''
2010-11-05 06:06:10 +03:00
return dsdb . _dsdb_DsReplicaAttribute ( ldb , ldap_display_name , ldif_elements )
2011-06-17 05:29:44 +04:00
def dsdb_normalise_attributes ( self , ldb , ldap_display_name , ldif_elements ) :
''' normalise a list of attribute values '''
return dsdb . _dsdb_normalise_attributes ( ldb , ldap_display_name , ldif_elements )
2010-06-22 20:03:15 +04:00
def get_attribute_from_attid ( self , attid ) :
""" Get from an attid the associated attribute
2010-11-28 05:34:47 +03:00
: param attid : The attribute id for searched attribute
: return : The name of the attribute associated with this id
2010-06-22 20:03:15 +04:00
"""
if len ( self . hash_oid_name . keys ( ) ) == 0 :
self . _populate_oid_attid ( )
2018-01-30 20:52:11 +03:00
if self . get_oid_from_attid ( attid ) in self . hash_oid_name :
2010-06-22 20:03:15 +04:00
return self . hash_oid_name [ self . get_oid_from_attid ( attid ) ]
else :
return None
def _populate_oid_attid ( self ) :
2010-11-28 05:34:47 +03:00
""" Populate the hash hash_oid_name.
2010-06-22 20:03:15 +04:00
2010-11-28 05:34:47 +03:00
This hash contains the oid of the attribute as a key and
its display name as a value
2010-06-22 20:03:15 +04:00
"""
self . hash_oid_name = { }
res = self . search ( expression = " objectClass=attributeSchema " ,
2018-07-30 09:15:34 +03:00
controls = [ " search_options:1:2 " ] ,
attrs = [ " attributeID " ,
2018-07-30 09:16:12 +03:00
" lDAPDisplayName " ] )
2010-06-22 20:03:15 +04:00
if len ( res ) > 0 :
for e in res :
strDisplay = str ( e . get ( " lDAPDisplayName " ) )
self . hash_oid_name [ str ( e . get ( " attributeID " ) ) ] = strDisplay
def get_attribute_replmetadata_version ( self , dn , att ) :
2010-11-28 05:34:47 +03:00
""" Get the version field trom the replPropertyMetaData for
the given field
2010-06-22 20:03:15 +04:00
2010-11-28 05:34:47 +03:00
: param dn : The on which we want to get the version
: param att : The name of the attribute
: return : The value of the version field in the replPropertyMetaData
for the given attribute . None if the attribute is not replicated
2010-06-22 20:03:15 +04:00
"""
2011-10-25 22:10:30 +04:00
res = self . search ( expression = " distinguishedName= %s " % dn ,
2018-07-30 09:15:34 +03:00
scope = ldb . SCOPE_SUBTREE ,
controls = [ " search_options:1:2 " ] ,
attrs = [ " replPropertyMetaData " ] )
2010-06-22 20:03:15 +04:00
if len ( res ) == 0 :
return None
repl = ndr_unpack ( drsblobs . replPropertyMetaDataBlob ,
2018-01-30 20:52:11 +03:00
res [ 0 ] [ " replPropertyMetaData " ] [ 0 ] )
2010-06-22 20:03:15 +04:00
ctr = repl . ctr
if len ( self . hash_oid_name . keys ( ) ) == 0 :
self . _populate_oid_attid ( )
for o in ctr . array :
# Search for Description
att_oid = self . get_oid_from_attid ( o . attid )
2018-01-30 20:52:11 +03:00
if att_oid in self . hash_oid_name and \
2010-06-22 20:03:15 +04:00
att . lower ( ) == self . hash_oid_name [ att_oid ] . lower ( ) :
return o . version
return None
2010-11-28 05:34:47 +03:00
def set_attribute_replmetadata_version ( self , dn , att , value ,
2018-07-30 09:16:12 +03:00
addifnotexist = False ) :
2011-10-25 22:10:30 +04:00
res = self . search ( expression = " distinguishedName= %s " % dn ,
2018-07-30 09:15:34 +03:00
scope = ldb . SCOPE_SUBTREE ,
controls = [ " search_options:1:2 " ] ,
attrs = [ " replPropertyMetaData " ] )
2010-06-22 20:03:15 +04:00
if len ( res ) == 0 :
return None
repl = ndr_unpack ( drsblobs . replPropertyMetaDataBlob ,
2018-01-30 20:52:11 +03:00
res [ 0 ] [ " replPropertyMetaData " ] [ 0 ] )
2010-06-22 20:03:15 +04:00
ctr = repl . ctr
now = samba . unix2nttime ( int ( time . time ( ) ) )
found = False
if len ( self . hash_oid_name . keys ( ) ) == 0 :
self . _populate_oid_attid ( )
for o in ctr . array :
# Search for Description
att_oid = self . get_oid_from_attid ( o . attid )
2018-01-30 20:52:11 +03:00
if att_oid in self . hash_oid_name and \
2010-06-22 20:03:15 +04:00
att . lower ( ) == self . hash_oid_name [ att_oid ] . lower ( ) :
found = True
seq = self . sequence_number ( ldb . SEQ_NEXT )
o . version = value
o . originating_change_time = now
o . originating_invocation_id = misc . GUID ( self . get_invocation_id ( ) )
o . originating_usn = seq
o . local_usn = seq
2010-08-10 18:19:40 +04:00
2018-07-30 09:18:03 +03:00
if not found and addifnotexist and len ( ctr . array ) > 0 :
2010-08-10 18:19:40 +04:00
o2 = drsblobs . replPropertyMetaData1 ( )
o2 . attid = 589914
att_oid = self . get_oid_from_attid ( o2 . attid )
seq = self . sequence_number ( ldb . SEQ_NEXT )
o2 . version = value
o2 . originating_change_time = now
o2 . originating_invocation_id = misc . GUID ( self . get_invocation_id ( ) )
o2 . originating_usn = seq
o2 . local_usn = seq
found = True
tab = ctr . array
tab . append ( o2 )
ctr . count = ctr . count + 1
ctr . array = tab
2018-07-30 09:17:14 +03:00
if found :
2010-06-22 20:03:15 +04:00
replBlob = ndr_pack ( repl )
msg = ldb . Message ( )
msg . dn = res [ 0 ] . dn
2018-07-30 09:16:12 +03:00
msg [ " replPropertyMetaData " ] = \
ldb . MessageElement ( replBlob ,
ldb . FLAG_MOD_REPLACE ,
" replPropertyMetaData " )
2010-06-22 20:03:15 +04:00
self . modify ( msg , [ " local_oid:1.3.6.1.4.1.7165.4.3.14:0 " ] )
2010-06-20 04:32:23 +04:00
def write_prefixes_from_schema ( self ) :
2010-06-20 17:22:49 +04:00
dsdb . _dsdb_write_prefixes_from_schema_to_ldb ( self )
2010-09-09 11:34:55 +04:00
def get_partitions_dn ( self ) :
return dsdb . _dsdb_get_partitions_dn ( self )
2010-11-22 19:34:18 +03:00
2012-07-06 12:12:58 +04:00
def get_nc_root ( self , dn ) :
return dsdb . _dsdb_get_nc_root ( self , dn )
2012-07-06 13:57:58 +04:00
def get_wellknown_dn ( self , nc_root , wkguid ) :
2015-05-25 19:17:55 +03:00
h_nc = self . hash_well_known . get ( str ( nc_root ) )
dn = None
if h_nc is not None :
dn = h_nc . get ( wkguid )
if dn is None :
dn = dsdb . _dsdb_get_wellknown_dn ( self , nc_root , wkguid )
if dn is None :
return dn
if h_nc is None :
self . hash_well_known [ str ( nc_root ) ] = { }
h_nc = self . hash_well_known [ str ( nc_root ) ]
h_nc [ wkguid ] = dn
return dn
2012-07-06 13:57:58 +04:00
2010-11-22 19:34:18 +03:00
def set_minPwdAge ( self , value ) :
2020-07-04 05:05:16 +03:00
if not isinstance ( value , bytes ) :
2018-11-06 22:58:48 +03:00
value = str ( value ) . encode ( ' utf8 ' )
2010-11-22 19:34:18 +03:00
m = ldb . Message ( )
m . dn = ldb . Dn ( self , self . domain_dn ( ) )
m [ " minPwdAge " ] = ldb . MessageElement ( value , ldb . FLAG_MOD_REPLACE , " minPwdAge " )
self . modify ( m )
def get_minPwdAge ( self ) :
res = self . search ( self . domain_dn ( ) , scope = ldb . SCOPE_BASE , attrs = [ " minPwdAge " ] )
if len ( res ) == 0 :
return None
2018-07-30 09:22:34 +03:00
elif " minPwdAge " not in res [ 0 ] :
2010-11-22 19:34:18 +03:00
return None
else :
2018-07-12 07:57:43 +03:00
return int ( res [ 0 ] [ " minPwdAge " ] [ 0 ] )
2010-11-23 12:20:12 +03:00
2014-01-31 04:27:05 +04:00
def set_maxPwdAge ( self , value ) :
2020-07-04 05:05:16 +03:00
if not isinstance ( value , bytes ) :
2018-11-06 22:58:48 +03:00
value = str ( value ) . encode ( ' utf8 ' )
2014-01-31 04:27:05 +04:00
m = ldb . Message ( )
m . dn = ldb . Dn ( self , self . domain_dn ( ) )
m [ " maxPwdAge " ] = ldb . MessageElement ( value , ldb . FLAG_MOD_REPLACE , " maxPwdAge " )
self . modify ( m )
def get_maxPwdAge ( self ) :
res = self . search ( self . domain_dn ( ) , scope = ldb . SCOPE_BASE , attrs = [ " maxPwdAge " ] )
if len ( res ) == 0 :
return None
2018-07-30 09:22:34 +03:00
elif " maxPwdAge " not in res [ 0 ] :
2014-01-31 04:27:05 +04:00
return None
else :
2018-07-12 07:57:43 +03:00
return int ( res [ 0 ] [ " maxPwdAge " ] [ 0 ] )
2014-01-31 04:27:05 +04:00
2010-12-02 11:55:56 +03:00
def set_minPwdLength ( self , value ) :
2020-07-04 05:05:16 +03:00
if not isinstance ( value , bytes ) :
2018-11-06 22:58:48 +03:00
value = str ( value ) . encode ( ' utf8 ' )
2010-12-02 11:55:56 +03:00
m = ldb . Message ( )
m . dn = ldb . Dn ( self , self . domain_dn ( ) )
m [ " minPwdLength " ] = ldb . MessageElement ( value , ldb . FLAG_MOD_REPLACE , " minPwdLength " )
self . modify ( m )
def get_minPwdLength ( self ) :
res = self . search ( self . domain_dn ( ) , scope = ldb . SCOPE_BASE , attrs = [ " minPwdLength " ] )
if len ( res ) == 0 :
return None
2018-07-30 09:22:34 +03:00
elif " minPwdLength " not in res [ 0 ] :
2010-12-02 11:55:56 +03:00
return None
else :
2018-07-12 07:57:43 +03:00
return int ( res [ 0 ] [ " minPwdLength " ] [ 0 ] )
2010-12-02 11:55:56 +03:00
def set_pwdProperties ( self , value ) :
2020-07-04 05:05:16 +03:00
if not isinstance ( value , bytes ) :
2018-11-06 22:58:48 +03:00
value = str ( value ) . encode ( ' utf8 ' )
2010-12-02 11:55:56 +03:00
m = ldb . Message ( )
m . dn = ldb . Dn ( self , self . domain_dn ( ) )
m [ " pwdProperties " ] = ldb . MessageElement ( value , ldb . FLAG_MOD_REPLACE , " pwdProperties " )
self . modify ( m )
def get_pwdProperties ( self ) :
res = self . search ( self . domain_dn ( ) , scope = ldb . SCOPE_BASE , attrs = [ " pwdProperties " ] )
if len ( res ) == 0 :
return None
2018-07-30 09:22:34 +03:00
elif " pwdProperties " not in res [ 0 ] :
2010-12-02 11:55:56 +03:00
return None
else :
2018-07-12 07:57:43 +03:00
return int ( res [ 0 ] [ " pwdProperties " ] [ 0 ] )
2010-12-02 11:55:56 +03:00
2010-11-23 12:20:12 +03:00
def set_dsheuristics ( self , dsheuristics ) :
m = ldb . Message ( )
m . dn = ldb . Dn ( self , " CN=Directory Service,CN=Windows NT,CN=Services, %s "
% self . get_config_basedn ( ) . get_linearized ( ) )
if dsheuristics is not None :
2018-07-30 09:16:12 +03:00
m [ " dSHeuristics " ] = \
ldb . MessageElement ( dsheuristics ,
ldb . FLAG_MOD_REPLACE ,
" dSHeuristics " )
2010-11-23 12:20:12 +03:00
else :
2018-07-30 09:16:12 +03:00
m [ " dSHeuristics " ] = \
ldb . MessageElement ( [ ] , ldb . FLAG_MOD_DELETE ,
" dSHeuristics " )
2010-11-23 12:20:12 +03:00
self . modify ( m )
def get_dsheuristics ( self ) :
res = self . search ( " CN=Directory Service,CN=Windows NT,CN=Services, %s "
% self . get_config_basedn ( ) . get_linearized ( ) ,
scope = ldb . SCOPE_BASE , attrs = [ " dSHeuristics " ] )
if len ( res ) == 0 :
dsheuristics = None
elif " dSHeuristics " in res [ 0 ] :
dsheuristics = res [ 0 ] [ " dSHeuristics " ] [ 0 ]
else :
dsheuristics = None
return dsheuristics
2010-11-23 15:31:34 +03:00
def create_ou ( self , ou_dn , description = None , name = None , sd = None ) :
""" Creates an organizationalUnit object
: param ou_dn : dn of the new object
: param description : description attribute
: param name : name atttribute
: param sd : security descriptor of the object , can be
an SDDL string or security . descriptor type
"""
2010-11-25 15:25:28 +03:00
m = { " dn " : ou_dn ,
" objectClass " : " organizationalUnit " }
2010-11-23 15:31:34 +03:00
if description :
2010-11-25 20:57:51 +03:00
m [ " description " ] = description
2010-11-23 15:31:34 +03:00
if name :
2010-11-25 20:57:51 +03:00
m [ " name " ] = name
2010-11-23 15:31:34 +03:00
if sd :
2010-11-25 20:57:51 +03:00
m [ " nTSecurityDescriptor " ] = ndr_pack ( sd )
2010-11-23 15:31:34 +03:00
self . add ( m )
2011-11-28 10:19:50 +04:00
def sequence_number ( self , seq_type ) :
""" Returns the value of the sequence number according to the requested type
: param seq_type : type of sequence number
"""
self . transaction_start ( )
try :
seq = super ( SamDB , self ) . sequence_number ( seq_type )
2012-02-25 18:56:25 +04:00
except :
2012-07-03 05:27:21 +04:00
self . transaction_cancel ( )
raise
2011-11-28 10:19:50 +04:00
else :
self . transaction_commit ( )
return seq
2011-12-04 17:23:34 +04:00
def get_dsServiceName ( self ) :
''' get the NTDS DN from the rootDSE '''
res = self . search ( base = " " , scope = ldb . SCOPE_BASE , attrs = [ " dsServiceName " ] )
2018-08-08 19:03:48 +03:00
return str ( res [ 0 ] [ " dsServiceName " ] [ 0 ] )
2011-12-04 17:23:34 +04:00
def get_serverName ( self ) :
''' get the server DN from the rootDSE '''
res = self . search ( base = " " , scope = ldb . SCOPE_BASE , attrs = [ " serverName " ] )
2018-08-08 19:03:48 +03:00
return str ( res [ 0 ] [ " serverName " ] [ 0 ] )
2015-09-22 03:11:04 +03:00
2017-06-09 07:05:31 +03:00
def dns_lookup ( self , dns_name , dns_partition = None ) :
2015-09-22 03:11:04 +03:00
''' Do a DNS lookup in the database, returns the NDR database structures '''
2017-06-09 07:05:31 +03:00
if dns_partition is None :
return dsdb_dns . lookup ( self , dns_name )
else :
return dsdb_dns . lookup ( self , dns_name ,
dns_partition = dns_partition )
2015-09-22 03:11:04 +03:00
2015-09-22 06:32:57 +03:00
def dns_extract ( self , el ) :
''' Return the NDR database structures from a dnsRecord element '''
2017-04-11 03:43:22 +03:00
return dsdb_dns . extract ( self , el )
2015-09-22 06:32:57 +03:00
2015-09-22 03:11:04 +03:00
def dns_replace ( self , dns_name , new_records ) :
2015-10-14 06:56:41 +03:00
''' Do a DNS modification on the database, sets the NDR database
structures on a DNS name
'''
2015-09-22 03:11:04 +03:00
return dsdb_dns . replace ( self , dns_name , new_records )
2015-10-14 06:56:41 +03:00
def dns_replace_by_dn ( self , dn , new_records ) :
''' Do a DNS modification on the database, sets the NDR database
structures on a LDB DN
This routine is important because if the last record on the DN
is removed , this routine will put a tombstone in the record .
'''
return dsdb_dns . replace_by_dn ( self , dn , new_records )
2016-07-18 04:11:10 +03:00
def garbage_collect_tombstones ( self , dn , current_time ,
tombstone_lifetime = None ) :
''' garbage_collect_tombstones(lp, samdb, [dn], current_time, tombstone_lifetime)
- > ( num_objects_expunged , num_links_expunged ) '''
2020-12-03 20:32:09 +03:00
if not is_ad_dc_built ( ) :
raise SamDBError ( ' Cannot garbage collect tombstones: ' \
' AD DC was not built ' )
2016-07-18 04:11:10 +03:00
if tombstone_lifetime is None :
return dsdb . _dsdb_garbage_collect_tombstones ( self , dn ,
current_time )
else :
return dsdb . _dsdb_garbage_collect_tombstones ( self , dn ,
current_time ,
tombstone_lifetime )
2016-10-28 06:08:57 +03:00
def create_own_rid_set ( self ) :
''' create a RID set for this DSA '''
return dsdb . _dsdb_create_own_rid_set ( self )
def allocate_rid ( self ) :
''' return a new RID from the RID Pool on this DSA '''
return dsdb . _dsdb_allocate_rid ( self )
2018-01-24 19:06:50 +03:00
2021-05-24 03:59:59 +03:00
def next_free_rid ( self ) :
''' return the next free RID from the RID Pool on this DSA.
: note : This function is not intended for general use , and care must be
taken if it is used to generate objectSIDs . The returned RID is not
formally reserved for use , creating the possibility of duplicate
objectSIDs .
'''
rid , _ = self . free_rid_bounds ( )
return rid
def free_rid_bounds ( self ) :
''' return the low and high bounds (inclusive) of RIDs that are
available for use in this DSA ' s current RID pool.
: note : This function is not intended for general use , and care must be
taken if it is used to generate objectSIDs . The returned range of
RIDs is not formally reserved for use , creating the possibility of
duplicate objectSIDs .
'''
# Get DN of this server's RID Set
server_name_dn = ldb . Dn ( self , self . get_serverName ( ) )
res = self . search ( base = server_name_dn ,
scope = ldb . SCOPE_BASE ,
attrs = [ " serverReference " ] )
try :
server_ref = res [ 0 ] [ " serverReference " ]
except KeyError :
raise ldb . LdbError (
ldb . ERR_NO_SUCH_ATTRIBUTE ,
" No RID Set DN - "
" Cannot find attribute serverReference of %s "
" to calculate reference dn " % server_name_dn ) from None
server_ref_dn = ldb . Dn ( self , server_ref [ 0 ] . decode ( " utf-8 " ) )
res = self . search ( base = server_ref_dn ,
scope = ldb . SCOPE_BASE ,
attrs = [ " rIDSetReferences " ] )
try :
rid_set_refs = res [ 0 ] [ " rIDSetReferences " ]
except KeyError :
raise ldb . LdbError (
ldb . ERR_NO_SUCH_ATTRIBUTE ,
" No RID Set DN - "
" Cannot find attribute rIDSetReferences of %s "
" to calculate reference dn " % server_ref_dn ) from None
rid_set_dn = ldb . Dn ( self , rid_set_refs [ 0 ] . decode ( " utf-8 " ) )
# Get the alloc pools and next RID of this RID Set
res = self . search ( base = rid_set_dn ,
scope = ldb . SCOPE_BASE ,
attrs = [ " rIDAllocationPool " ,
" rIDPreviousAllocationPool " ,
" rIDNextRID " ] )
uint32_max = 2 * * 32 - 1
uint64_max = 2 * * 64 - 1
try :
alloc_pool = int ( res [ 0 ] [ " rIDAllocationPool " ] [ 0 ] )
except KeyError :
alloc_pool = uint64_max
if alloc_pool == uint64_max :
raise ldb . LdbError ( ldb . ERR_OPERATIONS_ERROR ,
" Bad RID Set %s " % rid_set_dn )
try :
prev_pool = int ( res [ 0 ] [ " rIDPreviousAllocationPool " ] [ 0 ] )
except KeyError :
prev_pool = uint64_max
try :
next_rid = int ( res [ 0 ] [ " rIDNextRID " ] [ 0 ] )
except KeyError :
next_rid = uint32_max
# If we never used a pool, set up our first pool
if prev_pool == uint64_max or next_rid == uint32_max :
prev_pool = alloc_pool
next_rid = prev_pool & uint32_max
2021-06-01 03:03:38 +03:00
else :
next_rid + = 1
2021-05-24 03:59:59 +03:00
# Now check if our current pool is still usable
prev_pool_lo = prev_pool & uint32_max
prev_pool_hi = prev_pool >> 32
if next_rid > prev_pool_hi :
# We need a new pool, check if we already have a new one
# Otherwise we return an error code.
if alloc_pool == prev_pool :
raise ldb . LdbError ( ldb . ERR_OPERATIONS_ERROR ,
" RID pools out of RIDs " )
# Now use the new pool
prev_pool = alloc_pool
prev_pool_lo = prev_pool & uint32_max
prev_pool_hi = prev_pool >> 32
next_rid = prev_pool_lo
if next_rid < prev_pool_lo or next_rid > prev_pool_hi :
raise ldb . LdbError ( ldb . ERR_OPERATIONS_ERROR ,
" Bad RID chosen %d from range %d - %d " %
( next_rid , prev_pool_lo , prev_pool_hi ) )
return next_rid , prev_pool_hi
2018-01-24 19:06:50 +03:00
def normalize_dn_in_domain ( self , dn ) :
2018-02-08 06:46:29 +03:00
''' return a new DN expanded by adding the domain DN
If the dn is already a child of the domain DN , just
return it as - is .
2018-01-24 19:06:50 +03:00
: param dn : relative dn
2018-02-08 06:46:29 +03:00
'''
2018-01-24 19:06:50 +03:00
domain_dn = ldb . Dn ( self , self . domain_dn ( ) )
2018-02-08 06:27:17 +03:00
if isinstance ( dn , ldb . Dn ) :
dn = str ( dn )
2018-01-24 19:06:50 +03:00
full_dn = ldb . Dn ( self , dn )
if not full_dn . is_child_of ( domain_dn ) :
full_dn . add_base ( domain_dn )
return full_dn
2020-09-14 20:12:37 +03:00
class dsdb_Dn ( object ) :
''' a class for binary DN '''
def __init__ ( self , samdb , dnstring , syntax_oid = None ) :
''' create a dsdb_Dn '''
if syntax_oid is None :
# auto-detect based on string
if dnstring . startswith ( " B: " ) :
syntax_oid = dsdb . DSDB_SYNTAX_BINARY_DN
elif dnstring . startswith ( " S: " ) :
syntax_oid = dsdb . DSDB_SYNTAX_STRING_DN
else :
syntax_oid = dsdb . DSDB_SYNTAX_OR_NAME
if syntax_oid in [ dsdb . DSDB_SYNTAX_BINARY_DN , dsdb . DSDB_SYNTAX_STRING_DN ] :
# it is a binary DN
colons = dnstring . split ( ' : ' )
if len ( colons ) < 4 :
raise RuntimeError ( " Invalid DN %s " % dnstring )
prefix_len = 4 + len ( colons [ 1 ] ) + int ( colons [ 1 ] )
self . prefix = dnstring [ 0 : prefix_len ]
self . binary = self . prefix [ 3 + len ( colons [ 1 ] ) : - 1 ]
self . dnstring = dnstring [ prefix_len : ]
else :
self . dnstring = dnstring
self . prefix = ' '
self . binary = ' '
self . dn = ldb . Dn ( samdb , self . dnstring )
def __str__ ( self ) :
return self . prefix + str ( self . dn . extended_str ( mode = 1 ) )
def __cmp__ ( self , other ) :
''' compare dsdb_Dn values similar to parsed_dn_compare() '''
dn1 = self
dn2 = other
guid1 = dn1 . dn . get_extended_component ( " GUID " )
guid2 = dn2 . dn . get_extended_component ( " GUID " )
v = cmp ( guid1 , guid2 )
if v != 0 :
return v
v = cmp ( dn1 . binary , dn2 . binary )
return v
# In Python3, __cmp__ is replaced by these 6 methods
def __eq__ ( self , other ) :
return self . __cmp__ ( other ) == 0
def __ne__ ( self , other ) :
return self . __cmp__ ( other ) != 0
def __lt__ ( self , other ) :
return self . __cmp__ ( other ) < 0
def __le__ ( self , other ) :
return self . __cmp__ ( other ) < = 0
def __gt__ ( self , other ) :
return self . __cmp__ ( other ) > 0
def __ge__ ( self , other ) :
return self . __cmp__ ( other ) > = 0
def get_binary_integer ( self ) :
''' return binary part of a dsdb_Dn as an integer, or None '''
if self . prefix == ' ' :
return None
return int ( self . binary , 16 )
def get_bytes ( self ) :
''' return binary as a byte string '''
return binascii . unhexlify ( self . binary )