2010-01-31 22:06:01 +03:00
#!/usr/bin/python
#
# Helpers for provision stuff
# Copyright (C) Matthieu Patou <mat@matws.net> 2009-2010
#
# 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/>.
import os
import string
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
2010-06-08 00:01:16 +04:00
from samba import Ldb , version , ntacls
from samba . dsdb import DS_DOMAIN_FUNCTION_2000
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
2010-06-15 23:41:39 +04:00
from samba . provision import ( ProvisionNames , provision_paths_from_lp ,
getpolicypath , set_gpo_acl , create_gpo_struct ,
FILL_FULL , provision , ProvisioningError ,
setsysvolacl )
2010-06-08 00:01:16 +04:00
from samba . dcerpc import misc , security , xattr
2010-03-01 05:25:07 +03:00
from samba . ndr import ndr_unpack
2010-06-20 04:32:23 +04:00
from samba . samdb import SamDB
2010-01-31 22:06:01 +03:00
2010-05-02 19:56:31 +04: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
# I still keep them commented because I plan soon to make more cleaner
2010-06-08 00:01:16 +04:00
ERROR = - 1
SIMPLE = 0x00
CHANGE = 0x01
CHANGESD = 0x02
GUESS = 0x04
PROVISION = 0x08
CHANGEALL = 0xff
hashAttrNotCopied = { " dn " : 1 , " whenCreated " : 1 , " whenChanged " : 1 ,
" objectGUID " : 1 , " uSNCreated " : 1 ,
" replPropertyMetaData " : 1 , " uSNChanged " : 1 ,
" parentGUID " : 1 , " objectCategory " : 1 ,
" distinguishedName " : 1 , " nTMixedDomain " : 1 ,
" showInAdvancedViewOnly " : 1 , " instanceType " : 1 ,
" msDS-Behavior-Version " : 1 , " nextRid " : 1 , " cn " : 1 ,
" versionNumber " : 1 , " lmPwdHistory " : 1 , " pwdLastSet " : 1 ,
" ntPwdHistory " : 1 , " unicodePwd " : 1 , " dBCSPwd " : 1 ,
" supplementalCredentials " : 1 , " gPCUserExtensionNames " : 1 ,
" gPCMachineExtensionNames " : 1 , " maxPwdAge " : 1 , " secret " : 1 ,
" possibleInferiors " : 1 , " privilege " : 1 ,
" sAMAccountType " : 1 }
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
def startTransactions ( self ) :
self . sam . transaction_start ( )
self . secrets . transaction_start ( )
self . idmap . transaction_start ( )
self . privilege . 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
try :
self . sam . transaction_cancel ( )
except :
ok = False
try :
self . secrets . transaction_cancel ( )
except :
ok = False
try :
self . idmap . transaction_cancel ( )
except :
ok = False
try :
self . privilege . transaction_cancel ( )
except :
ok = False
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 :
self . sam . transaction_prepare_commit ( )
self . secrets . transaction_prepare_commit ( )
self . idmap . transaction_prepare_commit ( )
self . privilege . transaction_prepare_commit ( )
except :
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 :
self . sam . transaction_commit ( )
self . secrets . transaction_commit ( )
self . idmap . transaction_commit ( )
self . privilege . transaction_commit ( )
except :
return self . groupedRollback ( )
# 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
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 ( )
2010-06-20 04:32:23 +04:00
ldbs . sam = SamDB ( paths . samdb , session_info = session , credentials = creds , lp = lp , options = [ " modules:samba_dsdb " ] )
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 :
2010-06-08 00:52:25 +04: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 ] ) :
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 :
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 ) :
2010-06-13 17:32:41 +04:00
raise ProvisioningError ( " Unable to find smb.conf " )
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) " ,
base = " CN=Policies,CN=System, " + str ( names . rootdn ) ,
scope = SCOPE_ONELEVEL , attrs = [ " cn " , " displayName " ] )
names . policyid = str ( res [ 0 ] [ " cn " ] ) . replace ( " { " , " " ) . replace ( " } " , " " )
# dc policy guid
res2 = samdb . search ( expression = " (displayName=Default Domain Controllers " \
" Policy) " ,
base = " CN=Policies,CN=System, " + str ( names . rootdn ) ,
scope = SCOPE_ONELEVEL , attrs = [ " cn " , " displayName " ] )
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
2010-06-08 00:01:16 +04:00
def find_provision_key_parameters ( samdb , secretsdb , idmapdb , paths , smbconf , lp ) :
2010-03-01 05:29:47 +03:00
""" Get key provision parameters (realm, domain, ...) from a given provision
2010-05-02 19:56:31 +04:00
: param samdb : An LDB object connected to the sam . ldb file
: param secretsdb : An LDB object connected to the secrets . ldb file
2010-06-08 00:01:16 +04:00
: param idmapdb : An LDB object connected to the idmap . ldb file
2010-03-01 05:29:47 +03:00
: param paths : A list of path to provision object
: param smbconf : Path to the smb . conf file
2010-05-02 19:56:31 +04:00
: param lp : A LoadParm object
2010-06-20 03:56:52 +04:00
: return : A list of key provision parameters
"""
2010-03-01 05:29:47 +03:00
names = ProvisionNames ( )
names . adminpass = None
2010-05-02 19:56:31 +04:00
2010-03-01 05:29:47 +03:00
# NT domain, kerberos realm, root dn, domain dn, domain dns name
names . domain = string . upper ( lp . get ( " workgroup " ) )
names . realm = lp . get ( " realm " )
2010-06-08 00:01:16 +04:00
basedn = " DC= " + names . realm . replace ( " . " , " ,DC= " )
names . dnsdomain = names . realm . lower ( )
2010-03-01 05:29:47 +03:00
names . realm = string . upper ( names . realm )
# netbiosname
# Get the netbiosname first (could be obtained from smb.conf in theory)
2010-06-15 23:41:39 +04:00
res = secretsdb . search ( expression = " (flatname= %s ) " % \
names . domain , base = " CN=Primary Domains " ,
scope = SCOPE_SUBTREE , attrs = [ " sAMAccountName " ] )
2010-03-01 05:29:47 +03:00
names . netbiosname = str ( res [ 0 ] [ " sAMAccountName " ] ) . replace ( " $ " , " " )
names . smbconf = smbconf
# That's a bit simplistic but it's ok as long as we have only 3
# partitions
2010-04-09 01:18:17 +04:00
current = samdb . search ( expression = " (objectClass=*) " ,
base = " " , scope = SCOPE_BASE ,
attrs = [ " defaultNamingContext " , " schemaNamingContext " ,
" configurationNamingContext " , " rootDomainNamingContext " ] )
2010-03-01 05:29:47 +03:00
names . configdn = current [ 0 ] [ " configurationNamingContext " ]
configdn = str ( names . configdn )
names . schemadn = current [ 0 ] [ " schemaNamingContext " ]
2010-06-15 23:41:39 +04:00
if not ( ldb . Dn ( samdb , basedn ) == ( ldb . Dn ( samdb ,
current [ 0 ] [ " defaultNamingContext " ] [ 0 ] ) ) ) :
raise ProvisioningError ( ( " basedn in %s ( %s ) and from %s ( %s ) "
" is not the same ... " % ( paths . samdb ,
str ( current [ 0 ] [ " defaultNamingContext " ] [ 0 ] ) ,
paths . smbconf , basedn ) ) )
2010-03-01 05:29:47 +03:00
names . domaindn = current [ 0 ] [ " defaultNamingContext " ]
names . rootdn = current [ 0 ] [ " rootDomainNamingContext " ]
# default site name
2010-04-09 01:18:17 +04:00
res3 = samdb . search ( expression = " (objectClass=*) " ,
2010-06-15 23:41:39 +04:00
base = " CN=Sites, " + configdn , scope = SCOPE_ONELEVEL , attrs = [ " cn " ] )
2010-03-01 05:29:47 +03:00
names . sitename = str ( res3 [ 0 ] [ " cn " ] )
# dns hostname and server dn
2010-04-09 01:18:17 +04:00
res4 = samdb . search ( expression = " (CN= %s ) " % names . netbiosname ,
2010-06-15 23:41:39 +04:00
base = " OU=Domain Controllers, %s " % basedn ,
scope = SCOPE_ONELEVEL , attrs = [ " dNSHostName " ] )
names . hostname = str ( res4 [ 0 ] [ " dNSHostName " ] ) . replace ( " . " + names . dnsdomain , " " )
2010-03-01 05:29:47 +03:00
2010-06-11 01:12:53 +04:00
server_res = samdb . search ( expression = " serverReference= %s " % res4 [ 0 ] . dn ,
2010-06-15 23:41:39 +04:00
attrs = [ ] , base = configdn )
2010-03-01 05:29:47 +03:00
names . serverdn = server_res [ 0 ] . dn
# invocation id/objectguid
2010-04-08 20:57:09 +04:00
res5 = samdb . search ( expression = " (objectClass=*) " ,
base = " CN=NTDS Settings, %s " % str ( names . serverdn ) , scope = SCOPE_BASE ,
attrs = [ " invocationID " , " objectGUID " ] )
names . invocation = str ( ndr_unpack ( misc . GUID , res5 [ 0 ] [ " invocationId " ] [ 0 ] ) )
names . ntdsguid = str ( ndr_unpack ( misc . GUID , res5 [ 0 ] [ " objectGUID " ] [ 0 ] ) )
2010-03-01 05:29:47 +03:00
# domain guid/sid
2010-06-15 23:41:39 +04:00
res6 = samdb . search ( expression = " (objectClass=*) " , base = basedn ,
2010-04-08 20:57:09 +04:00
scope = SCOPE_BASE , attrs = [ " objectGUID " ,
" objectSid " , " msDS-Behavior-Version " ] )
2010-06-15 23:41:39 +04:00
names . domainguid = str ( ndr_unpack ( misc . GUID , res6 [ 0 ] [ " objectGUID " ] [ 0 ] ) )
names . domainsid = ndr_unpack ( security . dom_sid , res6 [ 0 ] [ " objectSid " ] [ 0 ] )
2010-06-20 14:06:50 +04:00
if res6 [ 0 ] . get ( " msDS-Behavior-Version " ) is None or \
2010-06-15 23:41:39 +04:00
int ( res6 [ 0 ] [ " msDS-Behavior-Version " ] [ 0 ] ) < DS_DOMAIN_FUNCTION_2000 :
2010-03-01 05:29:47 +03:00
names . domainlevel = DS_DOMAIN_FUNCTION_2000
else :
names . domainlevel = int ( res6 [ 0 ] [ " msDS-Behavior-Version " ] [ 0 ] )
# policy guid
2010-06-11 01:12:53 +04:00
res7 = samdb . search ( expression = " (displayName=Default Domain Policy) " ,
2010-06-15 23:41:39 +04:00
base = " CN=Policies,CN=System, " + basedn ,
scope = SCOPE_ONELEVEL , attrs = [ " cn " , " displayName " ] )
2010-03-01 05:29:47 +03:00
names . policyid = str ( res7 [ 0 ] [ " cn " ] ) . replace ( " { " , " " ) . replace ( " } " , " " )
# dc policy guid
2010-06-15 23:41:39 +04:00
res8 = samdb . search ( expression = " (displayName=Default Domain Controllers " \
" Policy) " ,
base = " CN=Policies,CN=System, " + basedn ,
scope = SCOPE_ONELEVEL , attrs = [ " cn " , " displayName " ] )
2010-03-01 05:29:47 +03:00
if len ( res8 ) == 1 :
names . policyid_dc = str ( res8 [ 0 ] [ " cn " ] ) . replace ( " { " , " " ) . replace ( " } " , " " )
else :
names . policyid_dc = None
2010-06-15 23:41:39 +04:00
res9 = idmapdb . search ( expression = " (cn= %s ) " % \
( security . SID_BUILTIN_ADMINISTRATORS ) ,
attrs = [ " xidNumber " ] )
2010-06-08 00:01:16 +04:00
if len ( res9 ) == 1 :
names . wheel_gid = res9 [ 0 ] [ " xidNumber " ]
else :
raise ProvisioningError ( " Unable to find uid/gid for Domain Admins rid " )
2010-03-01 05:29:47 +03:00
return names
2010-01-31 22:06:01 +03:00
2010-06-13 17:05:50 +04:00
def newprovision ( names , setup_dir , creds , session , smbconf , provdir , logger ) :
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 setup_dis : Directory where the setup files are stored
: 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-06-13 17:13:12 +04: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 . chdir ( os . path . join ( setup_dir , " .. " ) )
os . mkdir ( provdir )
2010-06-13 17:05:50 +04:00
logger . info ( " Provision stored in %s " , provdir )
provision ( setup_dir , logger , session , creds , smbconf = smbconf ,
2010-04-09 01:18:17 +04:00
targetdir = provdir , samdb_fill = FILL_FULL , realm = names . realm ,
domain = names . domain , domainguid = names . domainguid ,
domainsid = str ( names . domainsid ) , ntdsguid = names . ntdsguid ,
policyguid = names . policyid , policyguid_dc = names . policyid_dc ,
hostname = names . netbiosname , hostip = None , hostip6 = None ,
invocationid = names . invocation , adminpass = names . adminpass ,
krbtgtpass = None , machinepass = None , dnspass = None , root = None ,
nobody = None , wheel = None , users = None ,
serverrole = " domain controller " , ldap_backend_extra_port = None ,
backend_type = None , ldapadminpass = None , ol_mmr_urls = None ,
slapd_path = None , setup_ds_path = None , nosync = None ,
dom_for_fun_level = names . domainlevel ,
ldap_dryrun_mode = None , useeadb = True )
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 ) )
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 ) :
ret = cmp ( 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 :
2010-03-01 05:41:52 +03:00
if i == minimum - 1 :
2010-06-15 23:41:39 +04:00
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-08 00:52:25 +04:00
( before , sep , after ) = str ( dn ) . partition ( ' = ' )
ldbobj . rename ( dn , ldb . Dn ( ldbobj , " %s =foo %s " % ( before , after ) ) )
ldbobj . rename ( ldb . Dn ( ldbobj , " %s =foo %s " % ( before , after ) ) , dn )
2010-06-20 03:56:52 +04:00
2010-06-08 00:52:25 +04:00
def chunck_acl ( acl ) :
""" Return separate ACE of an ACL
: param acl : A string representing the ACL
: return : A hash with different parts
"""
p = re . compile ( r ' ( \ w+)?( \ (.*? \ )) ' )
tab = p . findall ( acl )
hash = { }
hash [ " aces " ] = [ ]
for e in tab :
if len ( e [ 0 ] ) > 0 :
hash [ " flags " ] = e [ 0 ]
hash [ " aces " ] . append ( e [ 1 ] )
return hash
def chunck_sddl ( sddl ) :
""" Return separate parts of the SDDL (owner, group, ...)
: param sddl : An string containing the SDDL to chunk
: return : A hash with the different chunk
"""
p = re . compile ( r ' ([OGDS]:)(.*?)(?=(?:[GDS]:|$)) ' )
tab = p . findall ( sddl )
hash = { }
for e in tab :
if e [ 0 ] == " O: " :
hash [ " owner " ] = e [ 1 ]
if e [ 0 ] == " G: " :
hash [ " group " ] = e [ 1 ]
if e [ 0 ] == " D: " :
hash [ " dacl " ] = e [ 1 ]
if e [ 0 ] == " S: " :
hash [ " sacl " ] = e [ 1 ]
return hash
def get_diff_sddls ( refsddl , cursddl ) :
""" Get the difference between 2 sddl
This function split the textual representation of ACL into smaller
chunck in order to not to report a simple permutation as a difference
: param refsddl : First sddl to compare
: param cursddl : Second sddl to compare
: return : A string that explain difference between sddls """
txt = " "
hash_new = chunck_sddl ( cursddl )
hash_ref = chunck_sddl ( refsddl )
if hash_new [ " owner " ] != hash_ref [ " owner " ] :
txt = " \t Owner mismatch: %s (in ref) %s " \
" (in current) \n " % ( hash_ref [ " owner " ] , hash_new [ " owner " ] )
if hash_new [ " group " ] != hash_ref [ " group " ] :
txt = " %s \t Group mismatch: %s (in ref) %s " \
" (in current) \n " % ( txt , hash_ref [ " group " ] , hash_new [ " group " ] )
for part in [ " dacl " , " sacl " ] :
if hash_new . has_key ( part ) and hash_ref . has_key ( part ) :
# both are present, check if they contain the same ACE
2010-06-20 14:06:50 +04:00
h_new = set ( )
h_ref = set ( )
2010-06-08 00:52:25 +04:00
c_new = chunck_acl ( hash_new [ part ] )
c_ref = chunck_acl ( hash_ref [ part ] )
for elem in c_new [ " aces " ] :
2010-06-20 14:06:50 +04:00
h_new . add ( elem )
2010-06-08 00:52:25 +04:00
for elem in c_ref [ " aces " ] :
2010-06-20 14:06:50 +04:00
h_ref . add ( elem )
2010-06-08 00:52:25 +04:00
2010-06-20 14:06:50 +04:00
for k in h_ref :
2010-06-08 00:52:25 +04:00
if h_new . has_key ( k ) :
2010-06-20 14:06:50 +04:00
h_new . remove ( k )
h_ref . remove ( k )
2010-06-08 00:52:25 +04:00
2010-06-20 14:06:50 +04:00
if len ( h_new ) + len ( h_ref ) > 0 :
2010-06-08 00:52:25 +04:00
txt = " %s \t Part %s is different between reference " \
" and current here is the detail: \n " % ( txt , part )
2010-06-20 14:06:50 +04:00
for item in h_new :
2010-06-08 00:52:25 +04:00
txt = " %s \t \t %s ACE is not present in the " \
" reference \n " % ( txt , item )
2010-06-20 14:06:50 +04:00
for item in h_ref :
2010-06-08 00:52:25 +04:00
txt = " %s \t \t %s ACE is not present in the " \
" current \n " % ( txt , item )
elif hash_new . has_key ( part ) and not hash_ref . has_key ( part ) :
txt = " %s \t Reference ACL hasn ' t a %s part \n " % ( txt , part )
elif not hash_new . has_key ( part ) and hash_ref . has_key ( part ) :
txt = " %s \t Current ACL hasn ' t a %s part \n " % ( txt , part )
return txt
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
of the reference provision
: param secrets_ldb : An LDB object that is connected to the secrets . ldb
of the updated provision
"""
messagefunc ( SIMPLE , " update secrets.ldb " )
reference = newsecrets_ldb . search ( expression = " dn=@MODULES " , base = " " ,
scope = SCOPE_SUBTREE )
current = secrets_ldb . search ( expression = " dn=@MODULES " , base = " " ,
scope = SCOPE_SUBTREE )
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 = " " ,
scope = SCOPE_SUBTREE , attrs = [ " dn " ] )
current = secrets_ldb . search ( expression = " objectClass=top " , base = " " ,
scope = SCOPE_SUBTREE , attrs = [ " dn " ] )
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 ( ) :
if not hash . has_key ( k ) :
listMissing . append ( hash_new [ k ] )
else :
listPresent . append ( hash_new [ k ] )
for entry in listMissing :
reference = newsecrets_ldb . search ( expression = " dn= %s " % entry ,
base = " " , scope = SCOPE_SUBTREE )
current = secrets_ldb . search ( expression = " dn= %s " % entry ,
base = " " , scope = SCOPE_SUBTREE )
delta = secrets_ldb . msg_diff ( empty , reference [ 0 ] )
for att in hashAttrNotCopied . keys ( ) :
delta . remove ( att )
2010-06-15 23:41:39 +04:00
messagefunc ( CHANGE , " Entry %s is missing from secrets.ldb " % \
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 :
reference = newsecrets_ldb . search ( expression = " dn= %s " % entry ,
base = " " , scope = SCOPE_SUBTREE )
current = secrets_ldb . search ( expression = " dn= %s " % entry , base = " " ,
scope = SCOPE_SUBTREE )
delta = secrets_ldb . msg_diff ( current [ 0 ] , reference [ 0 ] )
for att in hashAttrNotCopied . keys ( ) :
delta . remove ( att )
for att in delta :
if att == " name " :
messagefunc ( CHANGE , " Found attribute name on %s , " \
" must rename the DN " % ( current [ 0 ] . dn ) )
identic_rename ( secrets_ldb , reference [ 0 ] . dn )
else :
delta . remove ( att )
for entry in listPresent :
reference = newsecrets_ldb . search ( expression = " dn= %s " % entry , base = " " ,
scope = SCOPE_SUBTREE )
current = secrets_ldb . search ( expression = " dn= %s " % entry , base = " " ,
scope = SCOPE_SUBTREE )
delta = secrets_ldb . msg_diff ( current [ 0 ] , reference [ 0 ] )
for att in hashAttrNotCopied . keys ( ) :
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-06-15 23:41:39 +04:00
" Adding/Changing attribute %s to %s " % \
( att , current [ 0 ] . dn ) )
2010-06-08 00:01:16 +04:00
delta . dn = current [ 0 ] . dn
secrets_ldb . modify ( delta )
def getOEMInfo ( samdb , rootdn ) :
""" Return OEM Information on the top level
Samba4 use to store version info in this field
: param samdb : An LDB object connect to sam . ldb
: param rootdn : Root DN of the domain
: return : The content of the field oEMInformation ( if any ) """
res = samdb . search ( expression = " (objectClass=*) " , base = str ( rootdn ) ,
scope = SCOPE_BASE , attrs = [ " dn " , " oEMInformation " ] )
if len ( res ) > 0 :
info = res [ 0 ] [ " oEMInformation " ]
return info
else :
return " "
def updateOEMInfo ( samdb , rootdn ) :
""" Update the OEMinfo field to add information about upgrade
: 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 = . . . )
"""
res = samdb . search ( expression = " (objectClass=*) " , base = rootdn ,
scope = SCOPE_BASE , attrs = [ " dn " , " oEMInformation " ] )
if len ( res ) > 0 :
info = res [ 0 ] [ " oEMInformation " ]
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 ,
2010-06-15 23:41:39 +04:00
" oEMInformation " )
2010-06-08 00:01:16 +04:00
samdb . modify ( delta )
def update_gpo ( paths , samdb , names , lp , message , force = 0 ) :
""" Create missing GPO file object if needed
Set ACL correctly also .
Check ACLs for sysvol / netlogon dirs also
"""
2010-06-20 03:56:52 +04:00
resetacls = False
2010-06-08 00:01:16 +04:00
try :
ntacls . checkset_backend ( lp , None , None )
eadbname = lp . get ( " posix:eadb " )
if eadbname is not None and eadbname != " " :
try :
2010-06-15 23:41:39 +04:00
attribute = samba . xattr_tdb . wrap_getxattr ( eadbname ,
paths . sysvol , xattr . XATTR_NTACL_NAME )
2010-06-08 00:01:16 +04:00
except :
attribute = samba . xattr_native . wrap_getxattr ( paths . sysvol ,
2010-06-15 23:41:39 +04:00
xattr . XATTR_NTACL_NAME )
2010-06-08 00:01:16 +04:00
else :
attribute = samba . xattr_native . wrap_getxattr ( paths . sysvol ,
2010-06-15 23:41:39 +04:00
xattr . XATTR_NTACL_NAME )
2010-06-08 00:01:16 +04:00
except :
2010-06-20 03:56:52 +04:00
resetacls = True
2010-06-08 00:01:16 +04:00
if force :
2010-06-20 03:56:52 +04:00
resetacls = True
2010-06-08 00:01:16 +04:00
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 )
# We always reinforce acls on GPO folder because they have to be in sync
# with the one in DS
2010-06-14 02:14:48 +04:00
try :
set_gpo_acl ( paths . sysvol , names . dnsdomain , names . domainsid ,
names . domaindn , samdb , lp )
except TypeError , e :
message ( ERROR , " Unable to set ACLs on policies related objects, if not using posix:eadb, you must be root to do it " )
2010-06-08 00:01:16 +04:00
if resetacls :
2010-06-14 02:14:48 +04:00
try :
setsysvolacl ( samdb , paths . netlogon , paths . sysvol , names . wheel_gid ,
names . domainsid , names . dnsdomain , names . domaindn , lp )
except TypeError , e :
message ( ERROR , " Unable to set ACLs on sysvol share, if not using posix:eadb, you must be root to do it " )
2010-06-08 00:01:16 +04:00
def delta_update_basesamdb ( refsam , sam , creds , session , lp , message ) :
""" Update the provision container db: sam.ldb
This function is aimed for alpha9 and newer ;
: param refsam : Path to the samdb in the reference provision
: param sam : Path to the samdb in the upgraded provision
: param creds : Credential used for openning LDB files
: param session : Session to use for openning LDB files
: param lp : A loadparam object """
message ( SIMPLE ,
" Update base samdb by searching difference with reference one " )
refsam = Ldb ( refsam , session_info = session , credentials = creds ,
lp = lp , options = [ " modules: " ] )
sam = Ldb ( sam , session_info = session , credentials = creds , lp = lp ,
options = [ " modules: " ] )
empty = ldb . Message ( )
reference = refsam . search ( expression = " " )
for refentry in reference :
entry = sam . search ( expression = " dn= %s " % refentry [ " dn " ] ,
scope = SCOPE_SUBTREE )
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 \
delta . get ( samba . provision . LAST_PROVISION_USN_ATTRIBUTE ) :
delta . remove ( samba . provision . LAST_PROVISION_USN_ATTRIBUTE )
delta . dn = refentry . dn
sam . add ( delta )
else :
delta = sam . msg_diff ( entry [ 0 ] , refentry )
if str ( refentry . dn ) == " @PROVISION " and \
delta . get ( samba . provision . LAST_PROVISION_USN_ATTRIBUTE ) :
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
def construct_existor_expr ( attrs ) :
""" Construct a exists or LDAP search expression.
ie ( | ( foo = * ) ( bar = * )
: param attrs : List of attribute on which we want to create the search
expression .
: return : A string representing the expression , if attrs is empty an
empty string is returned """
expr = " "
if len ( attrs ) > 0 :
expr = " (| "
for att in attrs :
expr = " %s ( %s =*) " % ( expr , att )
expr = " %s ) " % expr
return expr
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
entry = samdb . search ( expression = expr , base = ldb . Dn ( samdb , str ( rootdn ) ) ,
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 ) :
if hashAtt . has_key ( att ) :
hashAtt [ att ] [ str ( ent . dn ) . lower ( ) ] = str ( ent [ att ] )
else :
hashAtt [ att ] = { }
hashAtt [ att ] [ str ( ent . dn ) . lower ( ) ] = str ( ent [ att ] )
return hashAtt