1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-11 05:18:09 +03:00

upgradeprovision: split the big script to put reusable functions appart

Signed-off-by: Jelmer Vernooij <jelmer@samba.org>
This commit is contained in:
Matthieu Patou 2010-01-31 22:06:01 +03:00 committed by Jelmer Vernooij
parent 2238260aa3
commit 3bd16415d2
2 changed files with 269 additions and 232 deletions

View File

@ -29,35 +29,33 @@ import sys
import random import random
import string import string
import re import re
import base64
import tempfile import tempfile
# Find right directory when running from source tree # Allow to run from s4 source directory (without installing samba)
sys.path.insert(0, "bin/python") sys.path.insert(0, "bin/python")
from base64 import b64encode
import samba import samba
import samba.getopt as options
from samba.credentials import DONT_USE_KERBEROS from samba.credentials import DONT_USE_KERBEROS
from samba.auth import system_session, admin_session from samba.auth import system_session, admin_session
from samba import Ldb, DS_DOMAIN_FUNCTION_2000, DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008, DS_DC_FUNCTION_2008_R2 from samba import Ldb
from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, FLAG_MOD_REPLACE, FLAG_MOD_ADD, FLAG_MOD_DELETE, MessageElement, Message, Dn
import ldb
import samba.getopt as options
from samba.samdb import SamDB from samba.samdb import SamDB
from samba import param from samba import param
from samba import glue from samba import glue
from samba.misc import messageEltFlagToString from samba.misc import messageEltFlagToString
from samba.provision import ProvisionNames,provision_paths_from_lp,find_setup_dir,FILL_FULL,provision, get_domain_descriptor, get_config_descriptor, secretsdb_self_join from samba.provision import find_setup_dir, get_domain_descriptor, get_config_descriptor, secretsdb_self_join
from samba.provisionexceptions import ProvisioningError from samba.provisionexceptions import ProvisioningError
from samba.schema import get_dnsyntax_attributes, get_linked_attributes, Schema, get_schema_descriptor from samba.schema import get_dnsyntax_attributes, get_linked_attributes, Schema, get_schema_descriptor
from samba.dcerpc import misc, security from samba.dcerpc import misc, security
from samba.ndr import ndr_pack, ndr_unpack from samba.ndr import ndr_pack, ndr_unpack
from samba.dcerpc.misc import SEC_CHAN_BDC from samba.dcerpc.misc import SEC_CHAN_BDC
from samba.upgradehelpers import dn_sort, get_paths, newprovision, find_provision_key_parameters, rmall
never=0 never=0
replace=2^ldb.FLAG_MOD_REPLACE replace=2^FLAG_MOD_REPLACE
add=2^ldb.FLAG_MOD_ADD add=2^FLAG_MOD_ADD
delete=2^ldb.FLAG_MOD_DELETE delete=2^FLAG_MOD_DELETE
#Errors are always logged #Errors are always logged
ERROR = -1 ERROR = -1
@ -152,42 +150,23 @@ session = system_session()
# simple helper to allow back and forth rename # simple helper to allow back and forth rename
def identic_rename(ldbobj,dn): def identic_rename(ldbobj,dn):
(before,sep,after)=str(dn).partition('=') (before,sep,after)=str(dn).partition('=')
ldbobj.rename(dn,ldb.Dn(ldbobj,"%s=foo%s"%(before,after))) ldbobj.rename(dn,Dn(ldbobj,"%s=foo%s"%(before,after)))
ldbobj.rename(ldb.Dn(ldbobj,"%s=foo%s"%(before,after)),dn) ldbobj.rename(Dn(ldbobj,"%s=foo%s"%(before,after)),dn)
# Create an array of backlinked attributes # Create an array of backlinked attributes
def populate_backlink(newpaths,creds,session,schemadn): def populate_backlink(newpaths,creds,session,schemadn):
newsam_ldb = Ldb(newpaths.samdb, session_info=session, credentials=creds,lp=lp) newsam_ldb = Ldb(newpaths.samdb, session_info=session, credentials=creds,lp=lp)
linkedAttHash = get_linked_attributes(ldb.Dn(newsam_ldb,str(schemadn)),newsam_ldb) linkedAttHash = get_linked_attributes(Dn(newsam_ldb,str(schemadn)),newsam_ldb)
backlinked.extend(linkedAttHash.values()) backlinked.extend(linkedAttHash.values())
# Create an array of attributes with a dn synthax (2.5.5.1) # Create an array of attributes with a dn synthax (2.5.5.1)
def populate_dnsyntax(newpaths,creds,session,schemadn): def populate_dnsyntax(newpaths,creds,session,schemadn):
newsam_ldb = Ldb(newpaths.samdb, session_info=session, credentials=creds,lp=lp) newsam_ldb = Ldb(newpaths.samdb, session_info=session, credentials=creds,lp=lp)
res = newsam_ldb.search(expression="(attributeSyntax=2.5.5.1)",base=ldb.Dn(newsam_ldb,str(schemadn)), scope=SCOPE_SUBTREE, attrs=["lDAPDisplayName"]) res = newsam_ldb.search(expression="(attributeSyntax=2.5.5.1)",base=Dn(newsam_ldb,str(schemadn)),
scope=SCOPE_SUBTREE, attrs=["lDAPDisplayName"])
for elem in res: for elem in res:
dn_syntax_att.append(elem["lDAPDisplayName"]) dn_syntax_att.append(elem["lDAPDisplayName"])
# Get Paths for important objects (ldb, keytabs ...)
def get_paths(targetdir=None,smbconf=None):
if targetdir is not None:
if (not os.path.exists(os.path.join(targetdir, "etc"))):
os.makedirs(os.path.join(targetdir, "etc"))
smbconf = os.path.join(targetdir, "etc", "smb.conf")
if smbconf is None:
smbconf = param.default_path()
if not os.path.exists(smbconf):
message(ERROR,"Unable to find smb.conf ..")
parser.print_usage()
sys.exit(1)
lp = param.LoadParm()
lp.load(smbconf)
# Normally we need the domain name for this function but for our needs it's
# pointless
paths = provision_paths_from_lp(lp,"foo")
return paths
def sanitychecks(credentials,session_info,names,paths): def sanitychecks(credentials,session_info,names,paths):
@ -206,93 +185,8 @@ domain with more than one DC, please demote the other DC before upgrading"%len(r
return 1 return 1
# This function guesses (fetches) informations needed to make a fresh provision
# from the current provision
# It includes: realm, workgroup, partitions, netbiosname, domain guid, ...
def guess_names_from_current_provision(credentials,session_info,paths):
lp = param.LoadParm()
lp.load(paths.smbconf)
names = ProvisionNames()
# NT domain, kerberos realm, root dn, domain dn, domain dns name
names.domain = string.upper(lp.get("workgroup"))
names.realm = lp.get("realm")
basedn = "DC=" + names.realm.replace(".",",DC=")
names.dnsdomain = names.realm
names.realm = string.upper(names.realm)
# netbiosname
secrets_ldb = Ldb(paths.secrets, session_info=session_info, credentials=credentials,lp=lp, options=["modules:samba_secrets"])
# Get the netbiosname first (could be obtained from smb.conf in theory)
attrs = ["sAMAccountName"]
res = secrets_ldb.search(expression="(flatname=%s)"%names.domain,base="CN=Primary Domains", scope=SCOPE_SUBTREE, attrs=attrs)
names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
names.smbconf = smbconf
# It's important here to let ldb load with the old module or it's quite
# certain that the LDB won't load ...
samdb = Ldb(paths.samdb, session_info=session_info,
credentials=credentials, lp=lp, options=["modules:samba_dsdb"])
# That's a bit simplistic but it's ok as long as we have only 3
# partitions
attrs2 = ["defaultNamingContext", "schemaNamingContext","configurationNamingContext","rootDomainNamingContext"]
current = samdb.search(expression="(objectClass=*)",base="", scope=SCOPE_BASE, attrs=attrs2)
names.configdn = current[0]["configurationNamingContext"]
configdn = str(names.configdn)
names.schemadn = current[0]["schemaNamingContext"]
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)))
names.domaindn=current[0]["defaultNamingContext"]
names.rootdn=current[0]["rootDomainNamingContext"]
# default site name
attrs3 = ["cn"]
res3= samdb.search(expression="(objectClass=*)",base="CN=Sites,"+configdn, scope=SCOPE_ONELEVEL, attrs=attrs3)
names.sitename = str(res3[0]["cn"])
# dns hostname and server dn
attrs4 = ["dNSHostName"]
res4= samdb.search(expression="(CN=%s)"%names.netbiosname,base="OU=Domain Controllers,"+basedn, \
scope=SCOPE_ONELEVEL, attrs=attrs4)
names.hostname = str(res4[0]["dNSHostName"]).replace("."+names.dnsdomain,"")
server_res = samdb.search(expression="serverReference=%s"%res4[0].dn, attrs=[], base=configdn)
names.serverdn = server_res[0].dn
# invocation id/objectguid
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]))
# domain guid/sid
attrs6 = ["objectGUID", "objectSid","msDS-Behavior-Version" ]
res6 = samdb.search(expression="(objectClass=*)",base=basedn, scope=SCOPE_BASE, attrs=attrs6)
names.domainguid = str(ndr_unpack( misc.GUID,res6[0]["objectGUID"][0]))
names.domainsid = ndr_unpack( security.dom_sid,res6[0]["objectSid"][0])
if res6[0].get("msDS-Behavior-Version") == None or int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
names.domainlevel = DS_DOMAIN_FUNCTION_2000
else:
names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
# policy guid
attrs7 = ["cn","displayName"]
res7 = samdb.search(expression="(displayName=Default Domain Policy)",base="CN=Policies,CN=System,"+basedn, \
scope=SCOPE_ONELEVEL, attrs=attrs7)
names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
# dc policy guid
attrs8 = ["cn","displayName"]
res8 = samdb.search(expression="(displayName=Default Domain Controllers Policy)",base="CN=Policies,CN=System,"+basedn, \
scope=SCOPE_ONELEVEL, attrs=attrs7)
if len(res8) == 1:
names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
else:
names.policyid_dc = None
return names
# Debug a little bit # Debug a little bit
def print_names(names): def print_provision_key_parameters(names):
message(GUESS, "rootdn :"+str(names.rootdn)) message(GUESS, "rootdn :"+str(names.rootdn))
message(GUESS, "configdn :"+str(names.configdn)) message(GUESS, "configdn :"+str(names.configdn))
message(GUESS, "schemadn :"+str(names.schemadn)) message(GUESS, "schemadn :"+str(names.schemadn))
@ -311,77 +205,6 @@ def print_names(names):
message(GUESS, "ntdsguid :"+names.ntdsguid) message(GUESS, "ntdsguid :"+names.ntdsguid)
message(GUESS, "domainlevel :"+str(names.domainlevel)) message(GUESS, "domainlevel :"+str(names.domainlevel))
# Create a fresh new reference provision
# This provision will be the reference for knowing what has changed in the
# since the latest upgrade in the current provision
def newprovision(names,setup_dir,creds,session,smbconf):
message(SIMPLE, "Creating a reference provision")
provdir=tempfile.mkdtemp(dir=paths.private_dir, prefix="referenceprovision")
if os.path.isdir(provdir):
rmall(provdir)
logstd=os.path.join(provdir,"log.std")
os.chdir(os.path.join(setup_dir,".."))
os.mkdir(provdir)
os.close(2)
sys.stderr = open("%s/provision.log"%provdir, "w")
message(PROVISION, "Reference provision stored in %s"%provdir)
message(PROVISION, "STDERR message of provision will be logged in %s/provision.log"%provdir)
sys.stderr = open("/dev/stdout", "w")
provision(setup_dir, messageprovision,
session, creds, smbconf=smbconf, 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=None,
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)
return provdir
# This function sorts two DNs in the lexicographical order and put higher level
# DN before.
# So given the dns cn=bar,cn=foo and cn=foo the later will be return as smaller
# (-1) as it has less level
def dn_sort(x,y):
p = re.compile(r'(?<!\\),')
tab1 = p.split(str(x))
tab2 = p.split(str(y))
min = 0
if (len(tab1) > len(tab2)):
min = len(tab2)
elif (len(tab1) < len(tab2)):
min = len(tab1)
else:
min = len(tab1)
len1=len(tab1)-1
len2=len(tab2)-1
space = " "
# Note: python range go up to upper limit but do not include it
for i in range(0,min):
ret=cmp(tab1[len1-i],tab2[len2-i])
if(ret != 0):
return ret
else:
if(i==min-1):
if(len1==len2):
message(ERROR,"PB PB PB"+space.join(tab1)+" / "+space.join(tab2))
if(len1>len2):
return 1
else:
return -1
return ret
# Check for security descriptors modifications return 1 if it is and 0 otherwise # Check for security descriptors modifications return 1 if it is and 0 otherwise
# it also populate hash structure for later use in the upgrade process # it also populate hash structure for later use in the upgrade process
def handle_security_desc(ischema,att,msgElt,hashallSD,old,new): def handle_security_desc(ischema,att,msgElt,hashallSD,old,new):
@ -406,7 +229,7 @@ def handle_security_desc(ischema,att,msgElt,hashallSD,old,new):
# It can be also if we want to do a merge of value instead of a simple replace # It can be also if we want to do a merge of value instead of a simple replace
def handle_special_case(att,delta,new,old,ischema): def handle_special_case(att,delta,new,old,ischema):
flag = delta.get(att).flags() flag = delta.get(att).flags()
if (att == "gPLink" or att == "gPCFileSysPath") and flag == ldb.FLAG_MOD_REPLACE and str(new[0].dn).lower() == str(old[0].dn).lower(): if (att == "gPLink" or att == "gPCFileSysPath") and flag == FLAG_MOD_REPLACE and str(new[0].dn).lower() == str(old[0].dn).lower():
delta.remove(att) delta.remove(att)
return 1 return 1
if att == "forceLogoff": if att == "forceLogoff":
@ -417,13 +240,12 @@ def handle_special_case(att,delta,new,old,ischema):
return 1 return 1
if (att == "adminDisplayName" or att == "adminDescription") and ischema: if (att == "adminDisplayName" or att == "adminDescription") and ischema:
return 1 return 1
if (str(old[0].dn) == "CN=Samba4-Local-Domain,%s"%(str(names.schemadn)) and att == "defaultObjectCategory" and flag == ldb.FLAG_MOD_REPLACE): if (str(old[0].dn) == "CN=Samba4-Local-Domain,%s"%(str(names.schemadn)) and att == "defaultObjectCategory" and flag == FLAG_MOD_REPLACE):
return 1 return 1
# if (str(old[0].dn) == "CN=S-1-5-11,CN=ForeignSecurityPrincipals,%s"%(str(names.rootdn)) and att == "description" and flag == ldb.FLAG_MOD_DELETE):
# return 1 if (str(old[0].dn) == "CN=Title,%s"%(str(names.schemadn)) and att == "rangeUpper" and flag == FLAG_MOD_REPLACE):
if (str(old[0].dn) == "CN=Title,%s"%(str(names.schemadn)) and att == "rangeUpper" and flag == ldb.FLAG_MOD_REPLACE):
return 1 return 1
if ( (att == "member" or att == "servicePrincipalName") and flag == ldb.FLAG_MOD_REPLACE): if ( (att == "member" or att == "servicePrincipalName") and flag == FLAG_MOD_REPLACE):
hash = {} hash = {}
newval = [] newval = []
@ -437,11 +259,11 @@ def handle_special_case(att,delta,new,old,ischema):
changeDelta=1 changeDelta=1
newval.append(str(elem)) newval.append(str(elem))
if changeDelta == 1: if changeDelta == 1:
delta[att] = ldb.MessageElement(newval, ldb.FLAG_MOD_REPLACE, att) delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
else: else:
delta.remove(att) delta.remove(att)
return 1 return 1
if (str(old[0].dn) == "%s"%(str(names.rootdn)) and att == "subRefs" and flag == ldb.FLAG_MOD_REPLACE): if (str(old[0].dn) == "%s"%(str(names.rootdn)) and att == "subRefs" and flag == FLAG_MOD_REPLACE):
return 1 return 1
if str(delta.dn).endswith("CN=DisplaySpecifiers,%s"%names.configdn): if str(delta.dn).endswith("CN=DisplaySpecifiers,%s"%names.configdn):
return 1 return 1
@ -466,7 +288,7 @@ def update_secrets(newpaths,paths,creds,session):
listMissing = [] listMissing = []
listPresent = [] listPresent = []
empty = ldb.Message() empty = Message()
for i in range(0,len(reference)): for i in range(0,len(reference)):
hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"] hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"]
@ -581,7 +403,7 @@ def add_missing_object(newsam_ldb,sam_ldb,dn,names,basedn,hash,index):
handle_special_add(sam_ldb,dn,names) handle_special_add(sam_ldb,dn,names)
reference = newsam_ldb.search(expression="dn=%s"%(str(dn)),base=basedn, reference = newsam_ldb.search(expression="dn=%s"%(str(dn)),base=basedn,
scope=SCOPE_SUBTREE,controls=["search_options:1:2"]) scope=SCOPE_SUBTREE,controls=["search_options:1:2"])
empty = ldb.Message() empty = Message()
delta = sam_ldb.msg_diff(empty,reference[0]) delta = sam_ldb.msg_diff(empty,reference[0])
for att in hashAttrNotCopied.keys(): for att in hashAttrNotCopied.keys():
delta.remove(att) delta.remove(att)
@ -726,7 +548,7 @@ def check_diff_name(newpaths,paths,creds,session,basedn,names,ischema):
if hashOverwrittenAtt.has_key(att) and hashOverwrittenAtt.get(att)==never: if hashOverwrittenAtt.has_key(att) and hashOverwrittenAtt.get(att)==never:
delta.remove(att) delta.remove(att)
continue continue
if handle_special_case(att,delta,reference,current,ischema)==0 and msgElt.flags()!=ldb.FLAG_MOD_ADD: if handle_special_case(att,delta,reference,current,ischema)==0 and msgElt.flags()!=FLAG_MOD_ADD:
i = 0 i = 0
if opts.debugchange or opts.debugall: if opts.debugchange or opts.debugall:
try: try:
@ -772,24 +594,24 @@ def update_sd(paths,creds,session,names):
# First update the SD for the rootdn # First update the SD for the rootdn
sam_ldb.set_session_info(session) sam_ldb.set_session_info(session)
res = sam_ldb.search(expression="objectClass=*",base=str(names.rootdn), scope=SCOPE_BASE,attrs=["dn","whenCreated"],controls=["search_options:1:2"]) res = sam_ldb.search(expression="objectClass=*",base=str(names.rootdn), scope=SCOPE_BASE,attrs=["dn","whenCreated"],controls=["search_options:1:2"])
delta = ldb.Message() delta = Message()
delta.dn = ldb.Dn(sam_ldb,str(res[0]["dn"])) delta.dn = Dn(sam_ldb,str(res[0]["dn"]))
descr = get_domain_descriptor(names.domainsid) descr = get_domain_descriptor(names.domainsid)
delta["nTSecurityDescriptor"] = ldb.MessageElement( descr,ldb.FLAG_MOD_REPLACE,"nTSecurityDescriptor" ) delta["nTSecurityDescriptor"] = MessageElement( descr,FLAG_MOD_REPLACE,"nTSecurityDescriptor" )
sam_ldb.modify(delta,["recalculate_sd:0"]) sam_ldb.modify(delta,["recalculate_sd:0"])
# Then the config dn # Then the config dn
res = sam_ldb.search(expression="objectClass=*",base=str(names.configdn), scope=SCOPE_BASE,attrs=["dn","whenCreated"],controls=["search_options:1:2"]) res = sam_ldb.search(expression="objectClass=*",base=str(names.configdn), scope=SCOPE_BASE,attrs=["dn","whenCreated"],controls=["search_options:1:2"])
delta = ldb.Message() delta = Message()
delta.dn = ldb.Dn(sam_ldb,str(res[0]["dn"])) delta.dn = Dn(sam_ldb,str(res[0]["dn"]))
descr = get_config_descriptor(names.domainsid) descr = get_config_descriptor(names.domainsid)
delta["nTSecurityDescriptor"] = ldb.MessageElement( descr,ldb.FLAG_MOD_REPLACE,"nTSecurityDescriptor" ) delta["nTSecurityDescriptor"] = MessageElement( descr,FLAG_MOD_REPLACE,"nTSecurityDescriptor" )
sam_ldb.modify(delta,["recalculate_sd:0"]) sam_ldb.modify(delta,["recalculate_sd:0"])
# Then the schema dn # Then the schema dn
res = sam_ldb.search(expression="objectClass=*",base=str(names.schemadn), scope=SCOPE_BASE,attrs=["dn","whenCreated"],controls=["search_options:1:2"]) res = sam_ldb.search(expression="objectClass=*",base=str(names.schemadn), scope=SCOPE_BASE,attrs=["dn","whenCreated"],controls=["search_options:1:2"])
delta = ldb.Message() delta = Message()
delta.dn = ldb.Dn(sam_ldb,str(res[0]["dn"])) delta.dn = Dn(sam_ldb,str(res[0]["dn"]))
descr = get_schema_descriptor(names.domainsid) descr = get_schema_descriptor(names.domainsid)
delta["nTSecurityDescriptor"] = ldb.MessageElement( descr,ldb.FLAG_MOD_REPLACE,"nTSecurityDescriptor" ) delta["nTSecurityDescriptor"] = MessageElement( descr,FLAG_MOD_REPLACE,"nTSecurityDescriptor" )
sam_ldb.modify(delta,["recalculate_sd:0"]) sam_ldb.modify(delta,["recalculate_sd:0"])
# Then the rest # Then the rest
@ -806,9 +628,9 @@ def update_sd(paths,creds,session,names):
for key in listkeys: for key in listkeys:
try: try:
delta = ldb.Message() delta = Message()
delta.dn = ldb.Dn(sam_ldb,key) delta.dn = Dn(sam_ldb,key)
delta["whenCreated"] = ldb.MessageElement( hash[key],ldb.FLAG_MOD_REPLACE,"whenCreated" ) delta["whenCreated"] = MessageElement( hash[key],FLAG_MOD_REPLACE,"whenCreated" )
sam_ldb.modify(delta,["recalculate_sd:0"]) sam_ldb.modify(delta,["recalculate_sd:0"])
except: except:
sam_ldb.transaction_cancel() sam_ldb.transaction_cancel()
@ -817,14 +639,6 @@ def update_sd(paths,creds,session,names):
return return
sam_ldb.transaction_commit() sam_ldb.transaction_commit()
def rmall(topdir):
for root, dirs, files in os.walk(topdir, topdown=False):
for name in files:
os.remove(os.path.join(root, name))
for name in dirs:
os.rmdir(os.path.join(root, name))
os.rmdir(topdir)
def update_basesamdb(newpaths,paths,names): def update_basesamdb(newpaths,paths,names):
message(SIMPLE,"Copy samdb") message(SIMPLE,"Copy samdb")
@ -899,24 +713,27 @@ def update_machine_account_password(paths,creds,session,names):
else: else:
secrets_ldb.transaction_cancel() secrets_ldb.transaction_cancel()
# From here start the big steps of the program
# First get files paths
paths=get_paths(smbconf=smbconf)
paths.setup = setup_dir
def setup_path(file): def setup_path(file):
return os.path.join(setup_dir, file) return os.path.join(setup_dir, file)
# From here start the big steps of the program
# First get files paths
paths=get_paths(param,smbconf=smbconf)
paths.setup = setup_dir
# Guess all the needed names (variables in fact) from the current # Guess all the needed names (variables in fact) from the current
# provision. # provision.
names = guess_names_from_current_provision(creds,session,paths)
names = find_provision_key_parameters(param,creds,session,paths,smbconf)
if not sanitychecks(creds,session,names,paths): if not sanitychecks(creds,session,names,paths):
print "Sanity checks for the upgrade fails, checks messages and correct it before rerunning upgradeprovision" message(SIMPLE,"Sanity checks for the upgrade fails, checks messages and correct it before rerunning upgradeprovision")
sys.exit(1) sys.exit(1)
# Let's see them # Let's see them
print_names(names) print_provision_key_parameters(names)
# With all this information let's create a fresh new provision used as reference # With all this information let's create a fresh new provision used as reference
provisiondir = newprovision(names,setup_dir,creds,session,smbconf) message(SIMPLE,"Creating a reference provision")
provisiondir = tempfile.mkdtemp(dir=paths.private_dir, prefix="referenceprovision")
newprovision(names,setup_dir,creds,session,smbconf,provisiondir,messageprovision)
# Get file paths of this new provision # Get file paths of this new provision
newpaths = get_paths(targetdir=provisiondir) newpaths = get_paths(param,targetdir=provisiondir)
populate_backlink(newpaths,creds,session,names.schemadn) populate_backlink(newpaths,creds,session,names.schemadn)
populate_dnsyntax(newpaths,creds,session,names.schemadn) populate_dnsyntax(newpaths,creds,session,names.schemadn)
# Check the difference # Check the difference

View File

@ -0,0 +1,220 @@
#!/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 sys
import string
import re
# Find right directory when running from source tree
import samba
from samba import Ldb, DS_DOMAIN_FUNCTION_2000
from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError
import ldb
from samba.provision import ProvisionNames,provision_paths_from_lp,FILL_FULL,provision
from samba.provisionexceptions import ProvisioningError
from samba.dcerpc import misc, security
from samba.ndr import ndr_pack, ndr_unpack
# Get Paths for important objects (ldb, keytabs ...)
def get_paths(param,targetdir=None,smbconf=None):
if targetdir is not None:
if (not os.path.exists(os.path.join(targetdir, "etc"))):
os.makedirs(os.path.join(targetdir, "etc"))
smbconf = os.path.join(targetdir, "etc", "smb.conf")
if smbconf is None:
smbconf = param.default_path()
if not os.path.exists(smbconf):
raise ProvisioningError("Unable to find smb.conf ...")
lp = param.LoadParm()
lp.load(smbconf)
paths = provision_paths_from_lp(lp,lp.get("realm"))
return paths
# This function guesses (fetches) informations needed to make a fresh provision
# from the current provision
# It includes: realm, workgroup, partitions, netbiosname, domain guid, ...
def find_provision_key_parameters(param,credentials,session_info,paths,smbconf):
lp = param.LoadParm()
lp.load(paths.smbconf)
names = ProvisionNames()
# NT domain, kerberos realm, root dn, domain dn, domain dns name
names.domain = string.upper(lp.get("workgroup"))
names.realm = lp.get("realm")
basedn = "DC=" + names.realm.replace(".",",DC=")
names.dnsdomain = names.realm
names.realm = string.upper(names.realm)
# netbiosname
secrets_ldb = Ldb(paths.secrets, session_info=session_info, credentials=credentials,lp=lp, options=["modules:samba_secrets"])
# Get the netbiosname first (could be obtained from smb.conf in theory)
attrs = ["sAMAccountName"]
res = secrets_ldb.search(expression="(flatname=%s)"%names.domain,base="CN=Primary Domains", scope=SCOPE_SUBTREE, attrs=attrs)
names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
names.smbconf = smbconf
# It's important here to let ldb load with the old module or it's quite
# certain that the LDB won't load ...
samdb = Ldb(paths.samdb, session_info=session_info,
credentials=credentials, lp=lp, options=["modules:samba_dsdb"])
# That's a bit simplistic but it's ok as long as we have only 3
# partitions
attrs2 = ["defaultNamingContext", "schemaNamingContext","configurationNamingContext","rootDomainNamingContext"]
current = samdb.search(expression="(objectClass=*)",base="", scope=SCOPE_BASE, attrs=attrs2)
names.configdn = current[0]["configurationNamingContext"]
configdn = str(names.configdn)
names.schemadn = current[0]["schemaNamingContext"]
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)))
names.domaindn=current[0]["defaultNamingContext"]
names.rootdn=current[0]["rootDomainNamingContext"]
# default site name
attrs3 = ["cn"]
res3= samdb.search(expression="(objectClass=*)",base="CN=Sites,"+configdn, scope=SCOPE_ONELEVEL, attrs=attrs3)
names.sitename = str(res3[0]["cn"])
# dns hostname and server dn
attrs4 = ["dNSHostName"]
res4= samdb.search(expression="(CN=%s)"%names.netbiosname,base="OU=Domain Controllers,"+basedn, \
scope=SCOPE_ONELEVEL, attrs=attrs4)
names.hostname = str(res4[0]["dNSHostName"]).replace("."+names.dnsdomain,"")
server_res = samdb.search(expression="serverReference=%s"%res4[0].dn, attrs=[], base=configdn)
names.serverdn = server_res[0].dn
# invocation id/objectguid
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]))
# domain guid/sid
attrs6 = ["objectGUID", "objectSid","msDS-Behavior-Version" ]
res6 = samdb.search(expression="(objectClass=*)",base=basedn, scope=SCOPE_BASE, attrs=attrs6)
names.domainguid = str(ndr_unpack( misc.GUID,res6[0]["objectGUID"][0]))
names.domainsid = ndr_unpack( security.dom_sid,res6[0]["objectSid"][0])
if res6[0].get("msDS-Behavior-Version") == None or int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
names.domainlevel = DS_DOMAIN_FUNCTION_2000
else:
names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
# policy guid
attrs7 = ["cn","displayName"]
res7 = samdb.search(expression="(displayName=Default Domain Policy)",base="CN=Policies,CN=System,"+basedn, \
scope=SCOPE_ONELEVEL, attrs=attrs7)
names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
# dc policy guid
attrs8 = ["cn","displayName"]
res8 = samdb.search(expression="(displayName=Default Domain Controllers Policy)",base="CN=Policies,CN=System,"+basedn, \
scope=SCOPE_ONELEVEL, attrs=attrs7)
if len(res8) == 1:
names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
else:
names.policyid_dc = None
return names
# Create a fresh new reference provision
# This provision will be the reference for knowing what has changed in the
# since the latest upgrade in the current provision
def newprovision(names,setup_dir,creds,session,smbconf,provdir,messagefunc):
if os.path.isdir(provdir):
rmall(provdir)
logstd=os.path.join(provdir,"log.std")
os.chdir(os.path.join(setup_dir,".."))
os.mkdir(provdir)
os.close(2)
sys.stderr = open("%s/provision.log"%provdir, "w")
messagefunc("Reference provision stored in %s"%provdir)
messagefunc("STDERR message of provision will be logged in %s/provision.log"%provdir)
sys.stderr = open("/dev/stdout", "w")
provision(setup_dir, messagefunc,
session, creds, smbconf=smbconf, 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=None,
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)
# This function sorts two DNs in the lexicographical order and put higher level
# DN before.
# So given the dns cn=bar,cn=foo and cn=foo the later will be return as smaller
# (-1) as it has less level
def dn_sort(x,y):
p = re.compile(r'(?<!\\),')
tab1 = p.split(str(x))
tab2 = p.split(str(y))
min = 0
if (len(tab1) > len(tab2)):
min = len(tab2)
elif (len(tab1) < len(tab2)):
min = len(tab1)
else:
min = len(tab1)
len1=len(tab1)-1
len2=len(tab2)-1
space = " "
# Note: python range go up to upper limit but do not include it
for i in range(0,min):
ret=cmp(tab1[len1-i],tab2[len2-i])
if(ret != 0):
return ret
else:
if(i==min-1):
assert len1!=len2,"PB PB PB"+space.join(tab1)+" / "+space.join(tab2)
if(len1>len2):
return 1
else:
return -1
return ret
def rmall(topdir):
for root, dirs, files in os.walk(topdir, topdown=False):
for name in files:
os.remove(os.path.join(root, name))
for name in dirs:
os.rmdir(os.path.join(root, name))
os.rmdir(topdir)