2010-01-31 22:06:01 +03:00
# Helpers for provision stuff
2012-03-17 11:18:39 +04:00
# Copyright (C) Matthieu Patou <mat@matws.net> 2009-2012
2010-01-31 22:06:01 +03:00
#
# Based on provision a Samba4 server by
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008
#
#
# 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/>.
2018-03-09 16:53:45 +03:00
from __future__ import print_function
2018-04-27 10:15:28 +03:00
from __future__ import division
2011-02-27 12:16:43 +03:00
""" Helpers used for upgrading between different database formats. """
2010-01-31 22:06:01 +03:00
import os
import re
2010-03-01 05:29:47 +03:00
import shutil
2010-06-08 00:01:16 +04:00
import samba
2010-01-31 22:06:01 +03:00
2018-07-30 05:13:14 +03:00
from samba . compat import cmp_fn
2010-06-08 00:01:16 +04:00
from samba import Ldb , version , ntacls
2010-03-01 05:25:07 +03:00
from ldb import SCOPE_SUBTREE , SCOPE_ONELEVEL , SCOPE_BASE
2010-01-31 22:06:01 +03:00
import ldb
2011-04-23 13:47:27 +04:00
from samba . provision import ( provision_paths_from_lp ,
2018-07-30 09:16:12 +03:00
getpolicypath , set_gpos_acl , create_gpo_struct ,
provision , ProvisioningError ,
setsysvolacl , secretsdb_self_join )
2013-09-09 03:54:23 +04:00
from samba . provision . common import FILL_FULL
2013-02-17 15:44:56 +04:00
from samba . dcerpc import xattr , drsblobs , security
2010-07-04 16:38:54 +04:00
from samba . dcerpc . misc import SEC_CHAN_BDC
2012-03-17 11:18:39 +04:00
from samba . ndr import ndr_unpack
2010-06-20 04:32:23 +04:00
from samba . samdb import SamDB
2012-03-17 11:18:39 +04:00
from samba import _glue
import tempfile
2010-01-31 22:06:01 +03:00
2010-11-28 15:45:56 +03:00
# All the ldb related to registry are commented because the path for them is
# relative in the provisionPath object
# And so opening them create a file in the current directory which is not what
# we want
2010-05-02 19:56:31 +04:00
# I still keep them commented because I plan soon to make more cleaner
2018-07-30 09:17:44 +03:00
ERROR = - 1
SIMPLE = 0x00
CHANGE = 0x01
CHANGESD = 0x02
GUESS = 0x04
PROVISION = 0x08
CHANGEALL = 0xff
2010-06-08 00:01:16 +04:00
2010-11-28 15:45:56 +03:00
hashAttrNotCopied = set ( [ " dn " , " whenCreated " , " whenChanged " , " objectGUID " ,
2018-07-30 09:16:12 +03:00
" uSNCreated " , " replPropertyMetaData " , " uSNChanged " , " parentGUID " ,
" objectCategory " , " distinguishedName " , " nTMixedDomain " ,
" showInAdvancedViewOnly " , " instanceType " , " msDS-Behavior-Version " ,
" nextRid " , " cn " , " versionNumber " , " lmPwdHistory " , " pwdLastSet " ,
" ntPwdHistory " , " unicodePwd " , " dBCSPwd " , " supplementalCredentials " ,
" gPCUserExtensionNames " , " gPCMachineExtensionNames " , " maxPwdAge " , " secret " ,
" possibleInferiors " , " privilege " , " sAMAccountType " ] )
2010-11-28 15:45:56 +03:00
2010-05-02 19:56:31 +04:00
class ProvisionLDB ( object ) :
2010-06-20 04:32:23 +04:00
2010-05-02 19:56:31 +04:00
def __init__ ( self ) :
self . sam = None
self . secrets = None
self . idmap = None
self . privilege = None
self . hkcr = None
self . hkcu = None
self . hku = None
self . hklm = None
2012-02-27 06:50:37 +04:00
def dbs ( self ) :
return ( self . sam , self . secrets , self . idmap , self . privilege )
2010-05-02 19:56:31 +04:00
def startTransactions ( self ) :
2012-02-27 06:50:37 +04:00
for db in self . dbs ( ) :
db . transaction_start ( )
2010-06-16 11:25:19 +04:00
# TO BE DONE
2010-05-02 19:56:31 +04:00
# self.hkcr.transaction_start()
# self.hkcu.transaction_start()
# self.hku.transaction_start()
# self.hklm.transaction_start()
def groupedRollback ( self ) :
2010-06-16 11:25:19 +04:00
ok = True
2012-02-27 06:50:37 +04:00
for db in self . dbs ( ) :
try :
db . transaction_cancel ( )
except Exception :
ok = False
2010-06-16 11:25:19 +04:00
return ok
# TO BE DONE
2010-05-02 19:56:31 +04:00
# self.hkcr.transaction_cancel()
# self.hkcu.transaction_cancel()
# self.hku.transaction_cancel()
# self.hklm.transaction_cancel()
def groupedCommit ( self ) :
2010-06-16 11:25:19 +04:00
try :
2012-02-27 06:50:37 +04:00
for db in self . dbs ( ) :
db . transaction_prepare_commit ( )
2010-11-29 06:15:57 +03:00
except Exception :
2010-06-16 11:25:19 +04:00
return self . groupedRollback ( )
# TO BE DONE
2010-05-02 19:56:31 +04:00
# self.hkcr.transaction_prepare_commit()
# self.hkcu.transaction_prepare_commit()
# self.hku.transaction_prepare_commit()
# self.hklm.transaction_prepare_commit()
2010-06-16 11:25:19 +04:00
try :
2012-02-27 06:50:37 +04:00
for db in self . dbs ( ) :
db . transaction_commit ( )
2010-11-29 06:15:57 +03:00
except Exception :
2010-06-16 11:25:19 +04:00
return self . groupedRollback ( )
2010-11-28 15:45:56 +03:00
2010-06-16 11:25:19 +04:00
# TO BE DONE
2010-05-02 19:56:31 +04:00
# self.hkcr.transaction_commit()
# self.hkcu.transaction_commit()
# self.hku.transaction_commit()
# self.hklm.transaction_commit()
2010-06-16 11:25:19 +04:00
return True
2010-05-02 19:56:31 +04:00
2012-02-27 06:50:37 +04:00
2010-05-02 19:56:31 +04:00
def get_ldbs ( paths , creds , session , lp ) :
""" Return LDB object mapped on most important databases
: param paths : An object holding the different importants paths for provision object
: param creds : Credential used for openning LDB files
: param session : Session to use for openning LDB files
: param lp : A loadparam object
: return : A ProvisionLDB object that contains LDB object for the different LDB files of the provision """
ldbs = ProvisionLDB ( )
2018-01-08 21:43:18 +03:00
ldbs . sam = SamDB ( paths . samdb ,
session_info = session ,
credentials = creds ,
lp = lp ,
options = [ " modules:samba_dsdb " ] ,
flags = 0 )
2010-05-02 19:56:31 +04:00
ldbs . secrets = Ldb ( paths . secrets , session_info = session , credentials = creds , lp = lp )
ldbs . idmap = Ldb ( paths . idmapdb , session_info = session , credentials = creds , lp = lp )
ldbs . privilege = Ldb ( paths . privilege , session_info = session , credentials = creds , lp = lp )
# ldbs.hkcr = Ldb(paths.hkcr, session_info=session, credentials=creds, lp=lp)
# ldbs.hkcu = Ldb(paths.hkcu, session_info=session, credentials=creds, lp=lp)
# ldbs.hku = Ldb(paths.hku, session_info=session, credentials=creds, lp=lp)
# ldbs.hklm = Ldb(paths.hklm, session_info=session, credentials=creds, lp=lp)
return ldbs
2010-03-01 05:25:07 +03:00
2010-06-20 03:56:52 +04:00
2010-06-08 00:52:25 +04:00
def usn_in_range ( usn , range ) :
""" Check if the usn is in one of the range provided.
To do so , the value is checked to be between the lower bound and
higher bound of a range
: param usn : A integer value corresponding to the usn that we want to update
: param range : A list of integer representing ranges , lower bounds are in
the even indices , higher in odd indices
2010-06-20 03:56:52 +04:00
: return : True if the usn is in one of the range , False otherwise
"""
2010-06-08 00:52:25 +04:00
idx = 0
2010-06-20 03:56:52 +04:00
cont = True
ok = False
while cont :
2018-07-30 09:17:44 +03:00
if idx == len ( range ) :
2010-06-20 03:56:52 +04:00
cont = False
2010-06-08 00:52:25 +04:00
continue
if usn < int ( range [ idx ] ) :
2018-07-30 09:18:03 +03:00
if idx % 2 == 1 :
2010-06-20 03:56:52 +04:00
ok = True
cont = False
2010-06-08 00:52:25 +04:00
if usn == int ( range [ idx ] ) :
2010-06-20 03:56:52 +04:00
cont = False
ok = True
2010-06-08 00:52:25 +04:00
idx = idx + 1
return ok
2010-06-20 03:56:52 +04:00
2010-03-01 05:25:07 +03:00
def get_paths ( param , targetdir = None , smbconf = None ) :
2010-03-01 05:29:47 +03:00
""" Get paths to important provision objects (smb.conf, ldb files, ...)
2010-02-21 21:29:36 +03:00
2010-03-01 05:29:47 +03:00
: param param : Param object
: param targetdir : Directory where the provision is ( or will be ) stored
: param smbconf : Path to the smb . conf file
: return : A list with the path of important provision objects """
if targetdir is not None :
2012-02-07 04:09:41 +04:00
if not os . path . exists ( targetdir ) :
os . mkdir ( targetdir )
2010-03-01 05:29:47 +03:00
etcdir = os . path . join ( targetdir , " etc " )
if not os . path . exists ( etcdir ) :
os . makedirs ( etcdir )
smbconf = os . path . join ( etcdir , " smb.conf " )
if smbconf is None :
smbconf = param . default_path ( )
2010-01-31 22:06:01 +03:00
2010-03-01 05:29:47 +03:00
if not os . path . exists ( smbconf ) :
2015-03-04 07:49:36 +03:00
raise ProvisioningError ( " Unable to find smb.conf at %s " % smbconf )
2010-01-31 22:06:01 +03:00
2010-03-01 05:29:47 +03:00
lp = param . LoadParm ( )
lp . load ( smbconf )
2010-06-13 17:32:41 +04:00
paths = provision_paths_from_lp ( lp , lp . get ( " realm " ) )
2010-03-01 05:29:47 +03:00
return paths
2010-01-31 22:06:01 +03:00
2010-06-14 12:28:58 +04:00
def update_policyids ( names , samdb ) :
""" Update policy ids that could have changed after sam update
: param names : List of key provision parameters
: param samdb : An Ldb object conntected with the sam DB
"""
# policy guid
res = samdb . search ( expression = " (displayName=Default Domain Policy) " ,
2018-07-30 09:15:34 +03:00
base = " CN=Policies,CN=System, " + str ( names . rootdn ) ,
scope = SCOPE_ONELEVEL , attrs = [ " cn " , " displayName " ] )
2010-06-14 12:28:58 +04:00
names . policyid = str ( res [ 0 ] [ " cn " ] ) . replace ( " { " , " " ) . replace ( " } " , " " )
# dc policy guid
2010-11-28 05:34:47 +03:00
res2 = samdb . search ( expression = " (displayName=Default Domain Controllers "
2010-06-14 12:28:58 +04:00
" Policy) " ,
2018-07-30 09:15:34 +03:00
base = " CN=Policies,CN=System, " + str ( names . rootdn ) ,
scope = SCOPE_ONELEVEL , attrs = [ " cn " , " displayName " ] )
2010-06-14 12:28:58 +04:00
if len ( res2 ) == 1 :
names . policyid_dc = str ( res2 [ 0 ] [ " cn " ] ) . replace ( " { " , " " ) . replace ( " } " , " " )
else :
names . policyid_dc = None
2010-01-31 22:06:01 +03:00
2010-06-20 03:56:52 +04:00
2017-10-05 00:01:27 +03:00
def newprovision ( names , session , smbconf , provdir , logger , base_schema = None ) :
2010-03-01 05:29:47 +03:00
""" Create a new provision.
2010-03-01 05:25:07 +03:00
This provision will be the reference for knowing what has changed in the
since the latest upgrade in the current provision
2010-02-21 21:29:36 +03:00
2010-03-01 05:29:47 +03:00
: param names : List of provision parameters
: param creds : Credentials for the authentification
: param session : Session object
: param smbconf : Path to the smb . conf file
: param provdir : Directory where the provision will be stored
2010-11-28 05:34:47 +03:00
: param logger : A Logger
2010-04-09 01:18:17 +04:00
"""
2010-03-01 05:29:47 +03:00
if os . path . isdir ( provdir ) :
shutil . rmtree ( provdir )
os . mkdir ( provdir )
2010-06-13 17:05:50 +04:00
logger . info ( " Provision stored in %s " , provdir )
2013-09-26 21:19:18 +04:00
return provision ( logger , session , smbconf = smbconf ,
2018-07-30 09:16:12 +03:00
targetdir = provdir , samdb_fill = FILL_FULL , realm = names . realm ,
domain = names . domain , domainguid = names . domainguid ,
domainsid = names . domainsid , ntdsguid = names . ntdsguid ,
policyguid = names . policyid , policyguid_dc = names . policyid_dc ,
hostname = names . netbiosname . lower ( ) , hostip = None , hostip6 = None ,
invocationid = names . invocation , adminpass = names . adminpass ,
krbtgtpass = None , machinepass = None , dnspass = None , root = None ,
nobody = None , users = None ,
serverrole = " domain controller " ,
backend_type = None , ldapadminpass = None , ol_mmr_urls = None ,
slapd_path = None ,
dom_for_fun_level = names . domainlevel , dns_backend = names . dns_backend ,
useeadb = True , use_ntvfs = True , base_schema = base_schema )
2010-01-31 22:06:01 +03:00
2010-03-01 05:41:52 +03:00
2010-06-13 17:05:50 +04:00
def dn_sort ( x , y ) :
2010-04-08 20:57:09 +04:00
""" Sorts two DNs in the lexicographical order it and put higher level DN
before .
So given the dns cn = bar , cn = foo and cn = foo the later will be return as
smaller
2010-03-01 05:29:47 +03:00
: param x : First object to compare
: param y : Second object to compare
"""
2010-06-08 00:52:25 +04:00
p = re . compile ( r ' (?<! \\ ), ? ' )
2010-03-01 05:29:47 +03:00
tab1 = p . split ( str ( x ) )
tab2 = p . split ( str ( y ) )
2010-03-01 05:41:52 +03:00
minimum = min ( len ( tab1 ) , len ( tab2 ) )
2018-07-30 09:18:25 +03:00
len1 = len ( tab1 ) - 1
len2 = len ( tab2 ) - 1
2010-03-01 05:29:47 +03:00
# Note: python range go up to upper limit but do not include it
2010-06-13 17:05:50 +04:00
for i in range ( 0 , minimum ) :
2018-07-30 09:18:25 +03:00
ret = cmp_fn ( tab1 [ len1 - i ] , tab2 [ len2 - i ] )
2010-03-01 05:41:52 +03:00
if ret != 0 :
2010-03-01 05:29:47 +03:00
return ret
else :
2018-07-30 09:18:25 +03:00
if i == minimum - 1 :
assert len1 != len2 , " PB PB PB " + " " . join ( tab1 ) + " / " + " " . join ( tab2 )
2010-03-01 05:41:52 +03:00
if len1 > len2 :
2010-03-01 05:29:47 +03:00
return 1
else :
return - 1
return ret
2010-06-08 00:52:25 +04:00
2010-06-20 03:56:52 +04:00
2010-06-08 00:52:25 +04:00
def identic_rename ( ldbobj , dn ) :
""" Perform a back and forth rename to trigger renaming on attribute that
2010-06-20 03:56:52 +04:00
can ' t be directly modified.
2010-06-08 00:52:25 +04:00
: param lbdobj : An Ldb Object
2010-06-20 03:56:52 +04:00
: param dn : DN of the object to manipulate
"""
2010-06-25 10:46:13 +04:00
( before , after ) = str ( dn ) . split ( ' = ' , 1 )
2011-02-04 03:05:40 +03:00
# we need to use relax to avoid the subtree_rename constraints
ldbobj . rename ( dn , ldb . Dn ( ldbobj , " %s =foo %s " % ( before , after ) ) , [ " relax:0 " ] )
ldbobj . rename ( ldb . Dn ( ldbobj , " %s =foo %s " % ( before , after ) ) , dn , [ " relax:0 " ] )
2010-06-08 00:52:25 +04:00
2010-06-20 03:56:52 +04:00
2010-06-08 00:01:16 +04:00
def update_secrets ( newsecrets_ldb , secrets_ldb , messagefunc ) :
""" Update secrets.ldb
: param newsecrets_ldb : An LDB object that is connected to the secrets . ldb
2010-11-28 05:34:47 +03:00
of the reference provision
2010-06-08 00:01:16 +04:00
: param secrets_ldb : An LDB object that is connected to the secrets . ldb
2010-11-28 05:34:47 +03:00
of the updated provision
2010-06-08 00:01:16 +04:00
"""
2011-06-19 01:17:44 +04:00
messagefunc ( SIMPLE , " Update of secrets.ldb " )
2011-07-13 07:05:19 +04:00
reference = newsecrets_ldb . search ( base = " @MODULES " , scope = SCOPE_BASE )
current = secrets_ldb . search ( base = " @MODULES " , scope = SCOPE_BASE )
2010-06-08 00:01:16 +04:00
assert reference , " Reference modules list can not be empty "
if len ( current ) == 0 :
# No modules present
delta = secrets_ldb . msg_diff ( ldb . Message ( ) , reference [ 0 ] )
delta . dn = reference [ 0 ] . dn
secrets_ldb . add ( reference [ 0 ] )
else :
delta = secrets_ldb . msg_diff ( current [ 0 ] , reference [ 0 ] )
delta . dn = current [ 0 ] . dn
secrets_ldb . modify ( delta )
reference = newsecrets_ldb . search ( expression = " objectClass=top " , base = " " ,
2018-07-30 09:15:34 +03:00
scope = SCOPE_SUBTREE , attrs = [ " dn " ] )
2010-06-08 00:01:16 +04:00
current = secrets_ldb . search ( expression = " objectClass=top " , base = " " ,
2018-07-30 09:15:34 +03:00
scope = SCOPE_SUBTREE , attrs = [ " dn " ] )
2010-06-08 00:01:16 +04:00
hash_new = { }
hash = { }
listMissing = [ ]
listPresent = [ ]
empty = ldb . Message ( )
for i in range ( 0 , len ( reference ) ) :
hash_new [ str ( reference [ i ] [ " dn " ] ) . lower ( ) ] = reference [ i ] [ " dn " ]
# Create a hash for speeding the search of existing object in the
# current provision
for i in range ( 0 , len ( current ) ) :
hash [ str ( current [ i ] [ " dn " ] ) . lower ( ) ] = current [ i ] [ " dn " ]
for k in hash_new . keys ( ) :
2018-04-04 02:19:48 +03:00
if k not in hash :
2010-06-08 00:01:16 +04:00
listMissing . append ( hash_new [ k ] )
else :
listPresent . append ( hash_new [ k ] )
for entry in listMissing :
2011-10-25 22:10:30 +04:00
reference = newsecrets_ldb . search ( expression = " distinguishedName= %s " % entry ,
2018-07-30 09:15:34 +03:00
base = " " , scope = SCOPE_SUBTREE )
2011-10-25 22:10:30 +04:00
current = secrets_ldb . search ( expression = " distinguishedName= %s " % entry ,
2018-07-30 09:15:34 +03:00
base = " " , scope = SCOPE_SUBTREE )
2010-06-08 00:01:16 +04:00
delta = secrets_ldb . msg_diff ( empty , reference [ 0 ] )
2010-11-28 15:45:56 +03:00
for att in hashAttrNotCopied :
2010-06-08 00:01:16 +04:00
delta . remove ( att )
2010-11-28 05:34:47 +03:00
messagefunc ( CHANGE , " Entry %s is missing from secrets.ldb " %
2010-06-15 23:41:39 +04:00
reference [ 0 ] . dn )
2010-06-08 00:01:16 +04:00
for att in delta :
messagefunc ( CHANGE , " Adding attribute %s " % att )
delta . dn = reference [ 0 ] . dn
secrets_ldb . add ( delta )
for entry in listPresent :
2011-10-25 22:10:30 +04:00
reference = newsecrets_ldb . search ( expression = " distinguishedName= %s " % entry ,
2018-07-30 09:15:34 +03:00
base = " " , scope = SCOPE_SUBTREE )
2011-10-25 22:10:30 +04:00
current = secrets_ldb . search ( expression = " distinguishedName= %s " % entry , base = " " ,
2018-07-30 09:15:34 +03:00
scope = SCOPE_SUBTREE )
2010-06-08 00:01:16 +04:00
delta = secrets_ldb . msg_diff ( current [ 0 ] , reference [ 0 ] )
2010-11-28 15:45:56 +03:00
for att in hashAttrNotCopied :
2010-06-08 00:01:16 +04:00
delta . remove ( att )
for att in delta :
if att == " name " :
2010-11-28 05:34:47 +03:00
messagefunc ( CHANGE , " Found attribute name on %s , "
2010-06-08 00:01:16 +04:00
" must rename the DN " % ( current [ 0 ] . dn ) )
identic_rename ( secrets_ldb , reference [ 0 ] . dn )
else :
delta . remove ( att )
for entry in listPresent :
2011-10-25 22:10:30 +04:00
reference = newsecrets_ldb . search ( expression = " distinguishedName= %s " % entry , base = " " ,
2018-07-30 09:15:34 +03:00
scope = SCOPE_SUBTREE )
2011-10-25 22:10:30 +04:00
current = secrets_ldb . search ( expression = " distinguishedName= %s " % entry , base = " " ,
2018-07-30 09:15:34 +03:00
scope = SCOPE_SUBTREE )
2010-06-08 00:01:16 +04:00
delta = secrets_ldb . msg_diff ( current [ 0 ] , reference [ 0 ] )
2010-11-28 15:45:56 +03:00
for att in hashAttrNotCopied :
2010-06-08 00:01:16 +04:00
delta . remove ( att )
for att in delta :
2010-06-14 12:28:58 +04:00
if att == " msDS-KeyVersionNumber " :
delta . remove ( att )
2010-06-08 00:01:16 +04:00
if att != " dn " :
messagefunc ( CHANGE ,
2010-11-28 05:34:47 +03:00
" Adding/Changing attribute %s to %s " %
2010-06-15 23:41:39 +04:00
( att , current [ 0 ] . dn ) )
2010-06-08 00:01:16 +04:00
delta . dn = current [ 0 ] . dn
secrets_ldb . modify ( delta )
2010-10-26 16:37:50 +04:00
res2 = secrets_ldb . search ( expression = " (samaccountname=dns) " ,
2018-07-30 09:15:34 +03:00
scope = SCOPE_SUBTREE , attrs = [ " dn " ] )
2010-10-26 16:37:50 +04:00
2012-02-27 06:50:37 +04:00
if len ( res2 ) == 1 :
2010-10-26 16:37:50 +04:00
messagefunc ( SIMPLE , " Remove old dns account " )
secrets_ldb . delete ( res2 [ 0 ] [ " dn " ] )
2010-11-28 05:34:47 +03:00
2010-06-08 00:01:16 +04:00
def getOEMInfo ( samdb , rootdn ) :
2010-11-28 05:34:47 +03:00
""" Return OEM Information on the top level Samba4 use to store version
info in this field
2010-06-08 00:01:16 +04:00
: param samdb : An LDB object connect to sam . ldb
: param rootdn : Root DN of the domain
2010-11-28 05:34:47 +03:00
: return : The content of the field oEMInformation ( if any )
"""
2010-06-08 00:01:16 +04:00
res = samdb . search ( expression = " (objectClass=*) " , base = str ( rootdn ) ,
2018-07-30 09:15:34 +03:00
scope = SCOPE_BASE , attrs = [ " dn " , " oEMInformation " ] )
2011-06-15 15:20:06 +04:00
if len ( res ) > 0 and res [ 0 ] . get ( " oEMInformation " ) :
2010-06-08 00:01:16 +04:00
info = res [ 0 ] [ " oEMInformation " ]
return info
else :
return " "
2010-11-28 05:34:47 +03:00
2010-06-08 00:01:16 +04:00
def updateOEMInfo ( samdb , rootdn ) :
""" Update the OEMinfo field to add information about upgrade
2010-11-28 05:34:47 +03:00
: param samdb : an LDB object connected to the sam DB
: param rootdn : The string representation of the root DN of
the provision ( ie . DC = . . . , DC = . . . )
2010-06-08 00:01:16 +04:00
"""
res = samdb . search ( expression = " (objectClass=*) " , base = rootdn ,
2018-07-30 09:15:34 +03:00
scope = SCOPE_BASE , attrs = [ " dn " , " oEMInformation " ] )
2010-06-08 00:01:16 +04:00
if len ( res ) > 0 :
2011-06-15 15:20:06 +04:00
if res [ 0 ] . get ( " oEMInformation " ) :
info = str ( res [ 0 ] [ " oEMInformation " ] )
else :
info = " "
2010-06-08 00:01:16 +04:00
info = " %s , upgrade to %s " % ( info , version )
delta = ldb . Message ( )
delta . dn = ldb . Dn ( samdb , str ( res [ 0 ] [ " dn " ] ) )
delta [ " oEMInformation " ] = ldb . MessageElement ( info , ldb . FLAG_MOD_REPLACE ,
2018-07-30 09:17:02 +03:00
" oEMInformation " )
2010-06-08 00:01:16 +04:00
samdb . modify ( delta )
2013-02-16 01:51:51 +04:00
def update_gpo ( paths , samdb , names , lp , message ) :
2010-06-08 00:01:16 +04:00
""" Create missing GPO file object if needed
"""
dir = getpolicypath ( paths . sysvol , names . dnsdomain , names . policyid )
if not os . path . isdir ( dir ) :
create_gpo_struct ( dir )
2010-06-20 14:06:50 +04:00
if names . policyid_dc is None :
2010-06-14 12:28:58 +04:00
raise ProvisioningError ( " Policy ID for Domain controller is missing " )
2010-06-08 00:01:16 +04:00
dir = getpolicypath ( paths . sysvol , names . dnsdomain , names . policyid_dc )
if not os . path . isdir ( dir ) :
create_gpo_struct ( dir )
2011-07-31 22:05:23 +04:00
2010-06-15 12:49:19 +04:00
def increment_calculated_keyversion_number ( samdb , rootdn , hashDns ) :
""" For a given hash associating dn and a number, this function will
update the replPropertyMetaData of each dn in the hash , so that the
calculated value of the msDs - KeyVersionNumber is equal or superior to the
one associated to the given dn .
: param samdb : An SamDB object pointing to the sam
: param rootdn : The base DN where we want to start
: param hashDns : A hash with dn as key and number representing the
minimum value of msDs - KeyVersionNumber that we want to
have
"""
entry = samdb . search ( expression = ' (objectClass=user) ' ,
base = ldb . Dn ( samdb , str ( rootdn ) ) ,
scope = SCOPE_SUBTREE , attrs = [ " msDs-KeyVersionNumber " ] ,
controls = [ " search_options:1:2 " ] )
done = 0
2010-08-10 18:19:40 +04:00
hashDone = { }
2010-06-15 12:49:19 +04:00
if len ( entry ) == 0 :
raise ProvisioningError ( " Unable to find msDs-KeyVersionNumber " )
else :
for e in entry :
2018-05-11 18:37:44 +03:00
if str ( e . dn ) . lower ( ) in hashDns :
2010-06-15 12:49:19 +04:00
val = e . get ( " msDs-KeyVersionNumber " )
if not val :
2010-08-10 18:19:40 +04:00
val = " 0 "
2010-06-15 12:49:19 +04:00
version = int ( str ( hashDns [ str ( e . dn ) . lower ( ) ] ) )
if int ( str ( val ) ) < version :
2010-08-10 17:39:29 +04:00
done = done + 1
2010-06-15 12:49:19 +04:00
samdb . set_attribute_replmetadata_version ( str ( e . dn ) ,
2018-07-30 09:15:34 +03:00
" unicodePwd " ,
version , True )
2010-08-12 01:25:27 +04:00
def delta_update_basesamdb ( refsampath , sampath , creds , session , lp , message ) :
2010-06-08 00:01:16 +04:00
""" Update the provision container db: sam.ldb
This function is aimed for alpha9 and newer ;
2010-08-12 01:25:27 +04:00
: param refsampath : Path to the samdb in the reference provision
: param sampath : Path to the samdb in the upgraded provision
2010-06-08 00:01:16 +04:00
: param creds : Credential used for openning LDB files
: param session : Session to use for openning LDB files
2010-08-12 01:25:27 +04:00
: param lp : A loadparam object
: return : A msg_diff object with the difference between the @ATTRIBUTES
of the current provision and the reference provision
"""
2010-06-08 00:01:16 +04:00
message ( SIMPLE ,
" Update base samdb by searching difference with reference one " )
2010-08-12 01:25:27 +04:00
refsam = Ldb ( refsampath , session_info = session , credentials = creds ,
2018-07-30 09:15:34 +03:00
lp = lp , options = [ " modules: " ] )
2010-08-12 01:25:27 +04:00
sam = Ldb ( sampath , session_info = session , credentials = creds , lp = lp ,
2018-07-30 09:15:34 +03:00
options = [ " modules: " ] )
2010-06-08 00:01:16 +04:00
empty = ldb . Message ( )
2010-08-12 01:25:27 +04:00
deltaattr = None
2010-06-08 00:01:16 +04:00
reference = refsam . search ( expression = " " )
for refentry in reference :
2011-10-25 22:10:30 +04:00
entry = sam . search ( expression = " distinguishedName= %s " % refentry [ " dn " ] ,
2018-07-30 09:15:34 +03:00
scope = SCOPE_SUBTREE )
2010-06-08 00:01:16 +04:00
if not len ( entry ) :
delta = sam . msg_diff ( empty , refentry )
message ( CHANGE , " Adding %s to sam db " % str ( refentry . dn ) )
if str ( refentry . dn ) == " @PROVISION " and \
2018-07-30 09:14:45 +03:00
delta . get ( samba . provision . LAST_PROVISION_USN_ATTRIBUTE ) :
2010-06-08 00:01:16 +04:00
delta . remove ( samba . provision . LAST_PROVISION_USN_ATTRIBUTE )
delta . dn = refentry . dn
sam . add ( delta )
else :
delta = sam . msg_diff ( entry [ 0 ] , refentry )
2010-08-12 01:25:27 +04:00
if str ( refentry . dn ) == " @ATTRIBUTES " :
deltaattr = sam . msg_diff ( refentry , entry [ 0 ] )
2010-06-08 00:01:16 +04:00
if str ( refentry . dn ) == " @PROVISION " and \
2018-07-30 09:14:45 +03:00
delta . get ( samba . provision . LAST_PROVISION_USN_ATTRIBUTE ) :
2010-06-08 00:01:16 +04:00
delta . remove ( samba . provision . LAST_PROVISION_USN_ATTRIBUTE )
if len ( delta . items ( ) ) > 1 :
delta . dn = refentry . dn
sam . modify ( delta )
2010-06-15 12:50:29 +04:00
2010-08-12 01:25:27 +04:00
return deltaattr
2010-06-15 12:50:29 +04:00
2010-11-28 05:34:47 +03:00
2010-06-15 12:50:29 +04:00
def construct_existor_expr ( attrs ) :
""" Construct a exists or LDAP search expression.
: param attrs : List of attribute on which we want to create the search
2010-11-28 05:34:47 +03:00
expression .
2010-06-15 12:50:29 +04:00
: return : A string representing the expression , if attrs is empty an
2010-11-28 05:34:47 +03:00
empty string is returned
"""
2010-06-15 12:50:29 +04:00
expr = " "
if len ( attrs ) > 0 :
expr = " (| "
for att in attrs :
expr = " %s ( %s =*) " % ( expr , att )
expr = " %s ) " % expr
return expr
2010-07-04 16:38:54 +04:00
def update_machine_account_password ( samdb , secrets_ldb , names ) :
""" Update (change) the password of the current DC both in the SAM db and in
secret one
: param samdb : An LDB object related to the sam . ldb file of a given provision
: param secrets_ldb : An LDB object related to the secrets . ldb file of a given
provision
: param names : List of key provision parameters """
expression = " samAccountName= %s $ " % names . netbiosname
secrets_msg = secrets_ldb . search ( expression = expression ,
2018-07-30 09:15:34 +03:00
attrs = [ " secureChannelType " ] )
2010-07-04 16:38:54 +04:00
if int ( secrets_msg [ 0 ] [ " secureChannelType " ] [ 0 ] ) == SEC_CHAN_BDC :
res = samdb . search ( expression = expression , attrs = [ ] )
assert ( len ( res ) == 1 )
2010-11-12 20:45:07 +03:00
msg = ldb . Message ( res [ 0 ] . dn )
2016-08-23 13:40:24 +03:00
machinepass = samba . generate_random_machine_password ( 128 , 255 )
2010-11-12 20:45:07 +03:00
mputf16 = machinepass . encode ( ' utf-16-le ' )
msg [ " clearTextPassword " ] = ldb . MessageElement ( mputf16 ,
2018-07-30 09:16:12 +03:00
ldb . FLAG_MOD_REPLACE ,
" clearTextPassword " )
2010-11-12 20:45:07 +03:00
samdb . modify ( msg )
2010-07-04 16:38:54 +04:00
res = samdb . search ( expression = ( " samAccountName= %s $ " % names . netbiosname ) ,
2018-07-30 09:16:12 +03:00
attrs = [ " msDs-keyVersionNumber " ] )
2010-07-04 16:38:54 +04:00
assert ( len ( res ) == 1 )
kvno = int ( str ( res [ 0 ] [ " msDs-keyVersionNumber " ] ) )
secChanType = int ( secrets_msg [ 0 ] [ " secureChannelType " ] [ 0 ] )
secretsdb_self_join ( secrets_ldb , domain = names . domain ,
2018-07-30 09:16:12 +03:00
realm = names . realm ,
domainsid = names . domainsid ,
dnsdomain = names . dnsdomain ,
netbiosname = names . netbiosname ,
machinepass = machinepass ,
key_version_number = kvno ,
secure_channel_type = secChanType )
2010-07-04 16:38:54 +04:00
else :
raise ProvisioningError ( " Unable to find a Secure Channel "
" of type SEC_CHAN_BDC " )
2010-10-26 16:37:50 +04:00
def update_dns_account_password ( samdb , secrets_ldb , names ) :
""" Update (change) the password of the dns both in the SAM db and in
secret one
: param samdb : An LDB object related to the sam . ldb file of a given provision
: param secrets_ldb : An LDB object related to the secrets . ldb file of a given
provision
: param names : List of key provision parameters """
expression = " samAccountName=dns- %s " % names . netbiosname
secrets_msg = secrets_ldb . search ( expression = expression )
if len ( secrets_msg ) == 1 :
res = samdb . search ( expression = expression , attrs = [ ] )
assert ( len ( res ) == 1 )
msg = ldb . Message ( res [ 0 ] . dn )
machinepass = samba . generate_random_password ( 128 , 255 )
mputf16 = machinepass . encode ( ' utf-16-le ' )
msg [ " clearTextPassword " ] = ldb . MessageElement ( mputf16 ,
2018-07-30 09:16:12 +03:00
ldb . FLAG_MOD_REPLACE ,
" clearTextPassword " )
2010-10-26 16:37:50 +04:00
samdb . modify ( msg )
res = samdb . search ( expression = expression ,
2018-07-30 09:16:12 +03:00
attrs = [ " msDs-keyVersionNumber " ] )
2010-10-26 16:37:50 +04:00
assert ( len ( res ) == 1 )
kvno = str ( res [ 0 ] [ " msDs-keyVersionNumber " ] )
msg = ldb . Message ( secrets_msg [ 0 ] . dn )
msg [ " secret " ] = ldb . MessageElement ( machinepass ,
2018-07-30 09:15:34 +03:00
ldb . FLAG_MOD_REPLACE ,
" secret " )
2010-10-26 16:37:50 +04:00
msg [ " msDS-KeyVersionNumber " ] = ldb . MessageElement ( kvno ,
2018-07-30 09:16:12 +03:00
ldb . FLAG_MOD_REPLACE ,
" msDS-KeyVersionNumber " )
2010-10-26 16:37:50 +04:00
secrets_ldb . modify ( msg )
2010-07-04 16:38:54 +04:00
2018-05-01 06:54:07 +03:00
def update_krbtgt_account_password ( samdb ) :
2015-02-23 06:50:43 +03:00
""" Update (change) the password of the krbtgt account
2018-05-01 06:54:07 +03:00
: param samdb : An LDB object related to the sam . ldb file of a given provision """
2015-02-23 06:50:43 +03:00
expression = " samAccountName=krbtgt "
res = samdb . search ( expression = expression , attrs = [ ] )
assert ( len ( res ) == 1 )
msg = ldb . Message ( res [ 0 ] . dn )
2016-08-23 13:40:24 +03:00
machinepass = samba . generate_random_machine_password ( 128 , 255 )
2015-02-23 06:50:43 +03:00
mputf16 = machinepass . encode ( ' utf-16-le ' )
msg [ " clearTextPassword " ] = ldb . MessageElement ( mputf16 ,
ldb . FLAG_MOD_REPLACE ,
" clearTextPassword " )
samdb . modify ( msg )
2010-06-15 12:50:29 +04:00
def search_constructed_attrs_stored ( samdb , rootdn , attrs ) :
""" Search a given sam DB for calculated attributes that are
still stored in the db .
: param samdb : An LDB object pointing to the sam
: param rootdn : The base DN where the search should start
: param attrs : A list of attributes to be searched
: return : A hash with attributes as key and an array of
array . Each array contains the dn and the associated
values for this attribute as they are stored in the
sam . """
hashAtt = { }
expr = construct_existor_expr ( attrs )
if expr == " " :
return hashAtt
2010-06-15 12:49:19 +04:00
entry = samdb . search ( expression = expr , base = ldb . Dn ( samdb , str ( rootdn ) ) ,
2010-06-15 12:50:29 +04:00
scope = SCOPE_SUBTREE , attrs = attrs ,
controls = [ " search_options:1:2 " , " bypassoperational:0 " ] )
if len ( entry ) == 0 :
# Nothing anymore
return hashAtt
for ent in entry :
for att in attrs :
if ent . get ( att ) :
2018-05-11 18:37:44 +03:00
if att in hashAtt :
2010-06-15 12:50:29 +04:00
hashAtt [ att ] [ str ( ent . dn ) . lower ( ) ] = str ( ent [ att ] )
else :
hashAtt [ att ] = { }
hashAtt [ att ] [ str ( ent . dn ) . lower ( ) ] = str ( ent [ att ] )
return hashAtt
2010-08-12 17:28:28 +04:00
2012-03-17 11:18:39 +04:00
def findprovisionrange ( samdb , basedn ) :
""" Find ranges of usn grouped by invocation id and then by timestamp
rouned at 1 minute
: param samdb : An LDB object pointing to the samdb
: param basedn : The DN of the forest
: return : A two level dictionary with invoication id as the
first level , timestamp as the second one and then
max , min , and number as subkeys , representing respectivily
the maximum usn for the range , the minimum usn and the number
of object with usn in this range .
"""
nb_obj = 0
hash_id = { }
res = samdb . search ( base = basedn , expression = " objectClass=* " ,
2018-07-30 09:15:34 +03:00
scope = ldb . SCOPE_SUBTREE ,
attrs = [ " replPropertyMetaData " ] ,
controls = [ " search_options:1:2 " ] )
2012-03-17 11:18:39 +04:00
for e in res :
nb_obj = nb_obj + 1
obj = ndr_unpack ( drsblobs . replPropertyMetaDataBlob ,
2018-07-30 09:15:34 +03:00
str ( e [ " replPropertyMetaData " ] ) ) . ctr
2012-03-17 11:18:39 +04:00
for o in obj . array :
# like a timestamp but with the resolution of 1 minute
2018-07-30 09:18:03 +03:00
minutestamp = _glue . nttime2unix ( o . originating_change_time ) / / 60
2012-03-17 11:18:39 +04:00
hash_ts = hash_id . get ( str ( o . originating_invocation_id ) )
2012-09-27 20:30:47 +04:00
if hash_ts is None :
2012-03-17 11:18:39 +04:00
ob = { }
ob [ " min " ] = o . originating_usn
ob [ " max " ] = o . originating_usn
ob [ " num " ] = 1
ob [ " list " ] = [ str ( e . dn ) ]
hash_ts = { }
else :
ob = hash_ts . get ( minutestamp )
2012-09-27 20:30:47 +04:00
if ob is None :
2012-03-17 11:18:39 +04:00
ob = { }
ob [ " min " ] = o . originating_usn
ob [ " max " ] = o . originating_usn
ob [ " num " ] = 1
ob [ " list " ] = [ str ( e . dn ) ]
else :
if ob [ " min " ] > o . originating_usn :
ob [ " min " ] = o . originating_usn
if ob [ " max " ] < o . originating_usn :
ob [ " max " ] = o . originating_usn
if not ( str ( e . dn ) in ob [ " list " ] ) :
ob [ " num " ] = ob [ " num " ] + 1
ob [ " list " ] . append ( str ( e . dn ) )
hash_ts [ minutestamp ] = ob
hash_id [ str ( o . originating_invocation_id ) ] = hash_ts
return ( hash_id , nb_obj )
def print_provision_ranges ( dic , limit_print , dest , samdb_path , invocationid ) :
""" print the differents ranges passed as parameter
: param dic : A dictionnary as returned by findprovisionrange
: param limit_print : minimum number of object in a range in order to print it
: param dest : Destination directory
: param samdb_path : Path to the sam . ldb file
: param invoicationid : Invocation ID for the current provision
"""
ldif = " "
for id in dic :
hash_ts = dic [ id ]
sorted_keys = [ ]
sorted_keys . extend ( hash_ts . keys ( ) )
sorted_keys . sort ( )
kept_record = [ ]
for k in sorted_keys :
obj = hash_ts [ k ]
if obj [ " num " ] > limit_print :
2018-07-30 09:18:25 +03:00
dt = _glue . nttime2string ( _glue . unix2nttime ( k * 60 ) )
2018-07-30 09:17:14 +03:00
print ( " %s # of modification: %d \t min: %d max: %d " % ( dt , obj [ " num " ] ,
2018-07-30 09:16:12 +03:00
obj [ " min " ] ,
obj [ " max " ] ) )
2012-03-17 11:18:39 +04:00
if hash_ts [ k ] [ " num " ] > 600 :
kept_record . append ( k )
# Let's try to concatenate consecutive block if they are in the almost same minutestamp
for i in range ( 0 , len ( kept_record ) ) :
if i != 0 :
key1 = kept_record [ i ]
2018-07-30 09:18:25 +03:00
key2 = kept_record [ i - 1 ]
2012-03-17 11:18:39 +04:00
if key1 - key2 == 1 :
# previous record is just 1 minute away from current
if int ( hash_ts [ key1 ] [ " min " ] ) == int ( hash_ts [ key2 ] [ " max " ] ) + 1 :
# Copy the highest USN in the previous record
# and mark the current as skipped
hash_ts [ key2 ] [ " max " ] = hash_ts [ key1 ] [ " max " ]
hash_ts [ key1 ] [ " skipped " ] = True
for k in kept_record :
obj = hash_ts [ k ]
2012-09-27 20:30:47 +04:00
if obj . get ( " skipped " ) is None :
2012-03-17 11:18:39 +04:00
ldif = " %s lastProvisionUSN: %d - %d ; %s \n " % ( ldif , obj [ " min " ] ,
2018-07-30 09:16:12 +03:00
obj [ " max " ] , id )
2012-03-17 11:18:39 +04:00
if ldif != " " :
file = tempfile . mktemp ( dir = dest , prefix = " usnprov " , suffix = " .ldif " )
2018-03-09 16:53:45 +03:00
print ( )
print ( " To track the USNs modified/created by provision and upgrade proivsion, " )
print ( " the following ranges are proposed to be added to your provision sam.ldb: \n %s " % ldif )
print ( " We recommend to review them, and if it ' s correct to integrate the following ldif: %s in your sam.ldb " % file )
print ( " You can load this file like this: ldbadd -H %s %s \n " % ( str ( samdb_path ) , file ) )
2012-03-17 11:18:39 +04:00
ldif = " dn: @PROVISION \n provisionnerID: %s \n %s " % ( invocationid , ldif )
open ( file , ' w ' ) . write ( ldif )
2010-08-12 17:28:28 +04:00
def int64range2str ( value ) :
""" Display the int64 range stored in value as xxx-yyy
: param value : The int64 range
: return : A string of the representation of the range
"""
lvalue = long ( value )
2018-07-30 09:18:32 +03:00
str = " %d - %d " % ( lvalue & 0xFFFFFFFF , lvalue >> 32 )
2010-08-12 17:28:28 +04:00
return str