diff options
Diffstat (limited to 'source4/scripting/bin')
| -rwxr-xr-x | source4/scripting/bin/findprovisionusnranges | 174 | ||||
| -rwxr-xr-x | source4/scripting/bin/renamedc | 200 | ||||
| -rwxr-xr-x | source4/scripting/bin/samba_dnsupdate | 44 | ||||
| -rwxr-xr-x | source4/scripting/bin/samba_spnupdate | 6 | ||||
| -rwxr-xr-x | source4/scripting/bin/setup_dns.sh | 2 | ||||
| -rwxr-xr-x | source4/scripting/bin/testparm | 219 | ||||
| -rwxr-xr-x | source4/scripting/bin/upgradeprovision | 977 | ||||
| -rw-r--r-- | source4/scripting/bin/wscript_build | 5 | 
8 files changed, 979 insertions, 648 deletions
diff --git a/source4/scripting/bin/findprovisionusnranges b/source4/scripting/bin/findprovisionusnranges new file mode 100755 index 0000000000..c91e42e936 --- /dev/null +++ b/source4/scripting/bin/findprovisionusnranges @@ -0,0 +1,174 @@ +#!/usr/bin/python +# +# Helper for determining USN ranges created of modified by provision and +# upgradeprovision. +# Copyright (C) Matthieu Patou <mat@matws.net> 2009-2011 +# +# +# 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 sys +import optparse +import tempfile +sys.path.insert(0, "bin/python") + +from samba.credentials import DONT_USE_KERBEROS +from samba.auth import system_session +from samba import Ldb +import ldb +                 +import samba.getopt as options +from samba import param +from samba import _glue +from samba.upgradehelpers import get_paths +from samba.ndr import ndr_unpack +from samba.dcerpc import drsblobs, misc + +parser = optparse.OptionParser("provision [options]") +sambaopts = options.SambaOptions(parser) +parser.add_option_group(sambaopts) +parser.add_option_group(options.VersionOptions(parser)) +parser.add_option("--storedir", type="string", help="Directory where to store result files") +credopts = options.CredentialsOptions(parser) +parser.add_option_group(credopts) +opts = parser.parse_args()[0] +lp = sambaopts.get_loadparm() +smbconf = lp.configfile + +creds = credopts.get_credentials(lp) +creds.set_kerberos_state(DONT_USE_KERBEROS) +session = system_session() +paths = get_paths(param, smbconf=smbconf) +basedn="DC=" + lp.get("realm").replace(".",",DC=") +samdb = Ldb(paths.samdb, session_info=session, credentials=creds,lp=lp) + +hash_id = {} +ldif = "" +nb_obj = 0 + +res = samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dsServiceName"]) + +invocation = None +if res and len(res) == 1 and res[0]["dsServiceName"] != None: +    dn = ldb.Dn(samdb, str(res[0]["dsServiceName"])) +    res = samdb.search(base=str(dn), scope=ldb.SCOPE_BASE, attrs=["invocationId"], +                        controls=["search_options:1:2"]) + +    if res and len(res) == 1 and res[0]["invocationId"]: +        invocation = str(ndr_unpack(misc.GUID, res[0]["invocationId"][0]))    +    else: +        print "Unable to find invocation ID" +        sys.exit(1) +else: +    print "Unable to find attribute dsServiceName in rootDSE" +    sys.exit(1) + +res = samdb.search(base=basedn, expression="objectClass=*", +                                scope=ldb.SCOPE_SUBTREE, +                                attrs=["replPropertyMetaData"], +                                controls=["search_options:1:2"]) + +for e in res: +    nb_obj = nb_obj + 1 +    obj = ndr_unpack(drsblobs.replPropertyMetaDataBlob, +                        str(e["replPropertyMetaData"])).ctr + +    for o in obj.array: +        # like a timestamp but with the resolution of 1 minute +        minutestamp =_glue.nttime2unix(o.originating_change_time)/60 +        hash_ts = hash_id.get(str(o.originating_invocation_id)) +        if hash_ts == None: +            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) +            if ob == None: +                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 + +minobj = 5 +print "Here is a list of changes that modified more than %d objects in 1 minute." % minobj +print "Usually changes made by provision and upgradeprovision are those who affect a couple"\ +        " of hundred of objects or more" +print "Total number of objects: %d" % nb_obj +print  + +for id in hash_id: +    hash_ts = hash_id[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"] > minobj: +            dt = _glue.nttime2string(_glue.unix2nttime(k*60)) +            print "%s # of modification: %d  \tmin: %d max: %d" % (dt , obj["num"], +                                                                obj["min"], +                                                                obj["max"]) +        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] +            key2 = kept_record[i-1] +            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] +            if obj.get("skipped") == None: +                ldif = "%slastProvisionUSN: %d-%d;%s\n" % (ldif, obj["min"], +                            obj["max"], id)  + +if ldif != "": +    dest = opts.storedir +    if dest == None: +        dest = "/tmp" + +    file = tempfile.mktemp(dir=dest, prefix="usnprov", suffix=".ldif")         +    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(paths.samdb),file) +    ldif = "dn: @PROVISION\nprovisionnerID: %s\n%s" % (invocation, ldif) +    open(file,'w').write(ldif) + diff --git a/source4/scripting/bin/renamedc b/source4/scripting/bin/renamedc new file mode 100755 index 0000000000..0915b15783 --- /dev/null +++ b/source4/scripting/bin/renamedc @@ -0,0 +1,200 @@ +#!/usr/bin/env python +# vim: expandtab +# +# Copyright (C) Matthieu Patou <mat@matws.net> 2011 +# +# 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 optparse +import sys +# Allow to run from s4 source directory (without installing samba) +sys.path.insert(0, "bin/python") + +import ldb +import samba +import samba.getopt as options +import os + +from samba.credentials import DONT_USE_KERBEROS +from samba.auth import system_session +from samba import param +from samba.provision import find_provision_key_parameters, secretsdb_self_join +from samba.upgradehelpers import get_ldbs, get_paths + + +__docformat__ = "restructuredText" + +parser = optparse.OptionParser("provision [options]") +sambaopts = options.SambaOptions(parser) +parser.add_option_group(sambaopts) +parser.add_option_group(options.VersionOptions(parser)) +credopts = options.CredentialsOptions(parser) +parser.add_option_group(credopts) +parser.add_option("--oldname", +                  help="Old DC name") +parser.add_option("--newname", +                  help="New DC name") + +opts = parser.parse_args()[0] + +if len(sys.argv) == 1: +    opts.interactive = True +lp = sambaopts.get_loadparm() +smbconf = lp.configfile + +creds = credopts.get_credentials(lp) +creds.set_kerberos_state(DONT_USE_KERBEROS) + + +if __name__ == '__main__': +    global defSDmodified +    defSDmodified = False +   # 1) First get files paths +    paths = get_paths(param, smbconf=smbconf) +    # Get ldbs with the system session, it is needed for searching +    # provision parameters +    session = system_session() + +    ldbs = get_ldbs(paths, creds, session, lp) +    ldbs.sam.transaction_start() +    ldbs.secrets.transaction_start() + +    if opts.oldname is None or opts.newname is None: +        raise Exception("Option oldname or newname is missing") +    res = ldbs.sam.search(expression="(&(name=%s)(serverReferenceBL=*))" % opts.oldname) +    if res is None or len(res) != 1: +        raise Exception("Wrong number of result returned, are you sure of the old name %s" % +                opts.oldname) + +    # Ok got it then check that the new name is not used as well +    res2 = ldbs.sam.search(expression="(&(name=%s)(objectclass=computer))" % opts.newname) +    if len(res2) != 0: +        raise Exception("Seems that %s is a name that already exists, pick another one" % +                opts.newname) + +    names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap, +                                            paths, smbconf, lp) +  +    #First rename the entry +    # provision put the name in upper case so let's do it too ! +    newdn = str(res[0].dn).replace("CN=%s" % opts.oldname, "CN=%s" % opts.newname.upper()) +    dnobj = ldb.Dn(ldbs.sam, newdn) +    ldbs.sam.rename(res[0].dn, dnobj) + +    # Then change password and samaccountname and dnshostname +    msg = ldb.Message(dnobj) +    machinepass = samba.generate_random_password(128, 255) +    mputf16 = machinepass.encode('utf-16-le') + +    account = "%s$" % opts.newname.upper() +    msg["clearTextPassword"] = ldb.MessageElement(mputf16, +                                            ldb.FLAG_MOD_REPLACE, +                                            "clearTextPassword") + +    msg["sAMAccountName"] = ldb.MessageElement(account, +                                            ldb.FLAG_MOD_REPLACE, +                                            "sAMAccountName") + +    msg["dNSHostName"] = ldb.MessageElement("%s.%s" % (opts.newname, +                                            names.dnsdomain), +                                            ldb.FLAG_MOD_REPLACE, +                                            "dNSHostName") +    ldbs.sam.modify(msg) + +    # Do a self join one more time to resync the secrets file +    res = ldbs.sam.search(expression=("dn=%s" % newdn), +            attrs=["msDs-keyVersionNumber", "serverReferenceBL"]) +    assert(len(res) == 1) +    kvno = int(str(res[0]["msDs-keyVersionNumber"])) +    serverbldn = ldb.Dn(ldbs.sam, str(res[0]["serverReferenceBL"])) + +    secrets_msg = ldbs.secrets.search(expression="sAMAccountName=%s$" % +                                            opts.oldname.upper(), +                                            attrs=["secureChannelType"]) + +    secChanType = int(secrets_msg[0]["secureChannelType"][0]) + +    secretsdb_self_join(ldbs.secrets, domain=names.domain, +                realm=names.realm, +                domainsid=names.domainsid, +                dnsdomain=names.dnsdomain, +                netbiosname=opts.newname.upper(), +                machinepass=machinepass, +                key_version_number=kvno, +                secure_channel_type=secChanType) + +    # Update RID set reference as there is no back link for the moment. + +    res = ldbs.sam.search(expression="(objectClass=rIDSet)", base=newdn, attrs=[]) +    assert(len(res) == 1) +    newridset = str(res[0].dn) +    msg = ldb.Message(dnobj) + +    msg["rIDSetReferences"] = ldb.MessageElement(newridset, +                                            ldb.FLAG_MOD_REPLACE, +                                            "rIDSetReferences") +    ldbs.sam.modify(msg) + +    # Update the server's sites configuration +    if False: +        # Desactivated for the moment we have a couple of issues with site  +        # renaming first one is that it's currently forbidden +        # second one is that a lot of links are not backlinked +        # and so won't be updated when the DN change (ie. fmsowner ...) +        serverbl = str(serverbldn) +        dnparts = serverbl.split(",") +        dnparts[0] = "CN=%s" % opts.newname.upper() +        newserverref = ",".join(dnparts) + +        newserverrefdn = ldb.Dn(ldbs.sam, newserverref) + +        ldbs.sam.rename(serverbldn, newserverrefdn) + +        msg = ldb.Message(newserverrefdn) +        msg["dNSHostName"] = ldb.MessageElement("%s.%s" % (opts.newname, +                                                names.dnsdomain), +                                                ldb.FLAG_MOD_REPLACE, +                                                "dNSHostName") +        ldbs.sam.modify(msg) + +    try: +        ldbs.sam.transaction_prepare_commit() +        ldbs.secrets.transaction_prepare_commit() +    except Exception: +        ldbs.sam.rollback() +        ldbs.secrets.rollback() +        sys.exit(1) + +    try: +        ldbs.sam.transaction_commit() +        ldbs.secrets.transaction_commit() +    except Exception: +        ldbs.sam.rollback() +        ldbs.secrets.rollback() + +    # All good so far +    #print lp.get("private dir") +    cf = open(lp.configfile) +    ncfname = "%s.new" % lp.configfile +    newconf = open(ncfname, 'w') +    for l in cf.readlines(): +        if l.find("netbios name") > 0: +             newconf.write("\tnetbios name = %s\n" % opts.newname.upper()) +        else: +            newconf.write(l) +    newconf.close() +    cf.close() +    os.rename(ncfname, lp.configfile) + diff --git a/source4/scripting/bin/samba_dnsupdate b/source4/scripting/bin/samba_dnsupdate index e86fba2983..78d7dc1712 100755 --- a/source4/scripting/bin/samba_dnsupdate +++ b/source4/scripting/bin/samba_dnsupdate @@ -89,6 +89,17 @@ if len(IPs) == 0:      print "No IP interfaces - skipping DNS updates"      sys.exit(0) +IP6s = [] +IP4s = [] +for i in IPs: +    if i.find(':') != -1: +        if i.find('%') == -1: +            # we don't want link local addresses for DNS updates +            IP6s.append(i) +    else: +        IP4s.append(i) + +  if opts.verbose:      print "IPs: %s" % IPs @@ -122,7 +133,7 @@ class dnsobj(object):          if self.type == 'SRV':              self.dest = list[2].lower()              self.port = list[3] -        elif self.type == 'A': +        elif self.type in ['A', 'AAAA']:              self.ip   = list[2] # usually $IP, which gets replaced          elif self.type == 'CNAME':              self.dest = list[2].lower() @@ -132,6 +143,7 @@ class dnsobj(object):      def __str__(self):          if d.type == "A":     return "%s %s %s" % (self.type, self.name, self.ip) +        if d.type == "AAAA":  return "%s %s %s" % (self.type, self.name, self.ip)          if d.type == "SRV":   return "%s %s %s %s" % (self.type, self.name, self.dest, self.port)          if d.type == "CNAME": return "%s %s %s" % (self.type, self.name, self.dest) @@ -178,7 +190,7 @@ def check_dns_name(d):          if opts.verbose:              print "Failed to find DNS entry %s" % d          return False -    if d.type == 'A': +    if d.type in ['A', 'AAAA']:          # we need to be sure that our IP is there          for rdata in ans:              if str(rdata) == str(d.ip): @@ -210,7 +222,7 @@ def get_subst_vars():      global lp, am_rodc      vars = {} -    samdb = SamDB(url=lp.get("sam database"), session_info=system_session(), +    samdb = SamDB(url=lp.samdb_url(), session_info=system_session(),                    lp=lp)      vars['DNSDOMAIN'] = lp.get('realm').lower() @@ -247,6 +259,8 @@ def call_nsupdate(d):      f = os.fdopen(tmp_fd, 'w')      if d.type == "A":          f.write("update add %s %u A %s\n" % (normalised_name, default_ttl, d.ip)) +    if d.type == "AAAA": +        f.write("update add %s %u AAAA %s\n" % (normalised_name, default_ttl, d.ip))      if d.type == "SRV":          if d.existing_port is not None:              f.write("update delete %s SRV 0 %s %s %s\n" % (normalised_name, d.existing_weight, @@ -264,7 +278,7 @@ def call_nsupdate(d):      try:          cmd = nsupdate_cmd[:]          cmd.append(tmpfile) -        ret = subprocess.call(cmd, shell=False) +        ret = subprocess.call(cmd, shell=False, env={"KRB5CCNAME": ccachename})          if ret != 0:              if opts.fail_immediately:                  sys.exit(1) @@ -382,16 +396,28 @@ for line in file:      if line == '' or line[0] == "#":          continue      d = parse_dns_line(line, sub_vars) +    if d.type == 'A' and len(IP4s) == 0: +        continue +    if d.type == 'AAAA' and len(IP6s) == 0: +        continue      dns_list.append(d)  # now expand the entries, if any are A record with ip set to $IP  # then replace with multiple entries, one for each interface IP  for d in dns_list: -    if d.type == 'A' and d.ip == "$IP": -        d.ip = IPs[0] -        for i in range(len(IPs)-1): +    if d.ip != "$IP": +        continue +    if d.type == 'A': +        d.ip = IP4s[0] +        for i in range(len(IP4s)-1): +            d2 = dnsobj(str(d)) +            d2.ip = IP4s[i+1] +            dns_list.append(d2) +    if d.type == 'AAAA': +        d.ip = IP6s[0] +        for i in range(len(IP6s)-1):              d2 = dnsobj(str(d)) -            d2.ip = IPs[i+1] +            d2.ip = IP6s[i+1]              dns_list.append(d2)  # now check if the entries already exist on the DNS server @@ -412,7 +438,7 @@ for d in update_list:      if am_rodc:          if d.name.lower() == domain.lower():              continue -        if d.type != 'A': +        if not d.type in [ 'A', 'AAAA' ]:              call_rodc_update(d)          else:              call_nsupdate(d) diff --git a/source4/scripting/bin/samba_spnupdate b/source4/scripting/bin/samba_spnupdate index 1794f2bd26..9f8f4073d3 100755 --- a/source4/scripting/bin/samba_spnupdate +++ b/source4/scripting/bin/samba_spnupdate @@ -82,7 +82,7 @@ def get_subst_vars(samdb):  try:      private_dir = lp.get("private dir") -    secrets_path = os.path.join(private_dir, lp.get("secrets database")) +    secrets_path = os.path.join(private_dir, "secrets.ldb")      secrets_db = Ldb(url=secrets_path, session_info=system_session(),                       credentials=creds, lp=lp) @@ -103,9 +103,9 @@ try:      else:          credentials = None -    samdb = SamDB(url=lp.get("sam database"), session_info=system_session(), credentials=credentials, lp=lp) +    samdb = SamDB(url=lp.samdb_url(), session_info=system_session(), credentials=credentials, lp=lp)  except ldb.LdbError, (num, msg): -    print("Unable to open sam database %s : %s" % (lp.get("sam database"), msg)) +    print("Unable to open sam database %s : %s" % (lp.samdb_url(), msg))      sys.exit(1) diff --git a/source4/scripting/bin/setup_dns.sh b/source4/scripting/bin/setup_dns.sh index de4485fc07..bc2ae96b84 100755 --- a/source4/scripting/bin/setup_dns.sh +++ b/source4/scripting/bin/setup_dns.sh @@ -13,7 +13,7 @@ IP="$3"  RSUFFIX=$(echo $DOMAIN | sed s/[\.]/,DC=/g)  [ -z "$PRIVATEDIR" ] && { -    PRIVATEDIR=$(bin/testparm --section-name=global --parameter-name='private dir' --suppress-prompt 2> /dev/null) +    PRIVATEDIR=$(bin/samba-tool testparm --section-name=global --parameter-name='private dir' --suppress-prompt 2> /dev/null)  }  OBJECTGUID=$(bin/ldbsearch -s base -H "$PRIVATEDIR/sam.ldb" -b "CN=NTDS Settings,CN=$HOSTNAME,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=$RSUFFIX" objectguid|grep ^objectGUID| cut -d: -f2) diff --git a/source4/scripting/bin/testparm b/source4/scripting/bin/testparm deleted file mode 100755 index c30e46148c..0000000000 --- a/source4/scripting/bin/testparm +++ /dev/null @@ -1,219 +0,0 @@ -#!/usr/bin/env python -# vim: expandtab ft=python -# -#   Unix SMB/CIFS implementation. -#   Test validity of smb.conf -#   Copyright (C) Karl Auer 1993, 1994-1998 -# -#   Extensively modified by Andrew Tridgell, 1995 -#   Converted to popt by Jelmer Vernooij (jelmer@nl.linux.org), 2002 -#   Updated for Samba4 by Andrew Bartlett <abartlet@samba.org> 2006 -#   Converted to Python by Jelmer Vernooij <jelmer@samba.org> 2010 -#    -#   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/>. -# -# Testbed for loadparm.c/params.c -# -# This module simply loads a specified configuration file and -# if successful, dumps it's contents to stdout. Note that the -# operation is performed with DEBUGLEVEL at 3. -# -# Useful for a quick 'syntax check' of a configuration file. -# - -import logging -import optparse -import os -import sys - -# Find right directory when running from source tree -sys.path.insert(0, "bin/python") - -import samba -from samba import getopt as options - -# Here we do a set of 'hard coded' checks for bad -# configuration settings. - -def do_global_checks(lp, logger): -    valid = True - -    netbios_name = lp.get("netbios name") -    if not samba.valid_netbios_name(netbios_name): -        logger.error("netbios name %s is not a valid netbios name",  -                     netbios_name) -        valid = False - -    workgroup = lp.get("workgroup") -    if not samba.valid_netbios_name(workgroup): -        logger.error("workgroup name %s is not a valid netbios name",  -                     workgroup) -        valid = False - -    lockdir = lp.get("lockdir") - -    if not os.path.isdir(lockdir): -        logger.error("lock directory %s does not exist", lockdir) -        valid = False - -    piddir = lp.get("pid directory") - -    if not os.path.isdir(piddir): -        logger.error("pid directory %s does not exist", piddir) -        valid = False - -    winbind_separator = lp.get("winbind separator") - -    if len(winbind_separator) != 1: -        logger.error("the 'winbind separator' parameter must be a single " -                     "character.") -        valid = False - -    if winbind_separator == '+': -        logger.error("'winbind separator = +' might cause problems with group " -                     "membership.") -        valid = False - -    return valid - - -def allow_access(deny_list, allow_list, cname, caddr): -    raise NotImplementedError(allow_access) - - -def do_share_checks(lp, logger): -    valid = True -    for s in lp.services(): -        if len(s) > 12: -            logger.warning("You have some share names that are longer than 12 " -                "characters. These may not be accessible to some older " -                "clients. (Eg. Windows9x, WindowsMe, and not listed in " -                "smbclient in Samba 3.0.)") -            break - -    for s in lp.services(): -        deny_list = lp.get("hosts deny", s) -        allow_list = lp.get("hosts allow", s) -        if deny_list: -            for entry in deny_list: -                if "*" in entry or "?" in entry: -                    logger.error("Invalid character (* or ?) in hosts deny " -                                 "list (%s) for service %s.", entry, s) -                    valid = False - -        if allow_list: -            for entry in allow_list: -                if "*" in entry or "?" in entry: -                    logger.error("Invalid character (* or ?) in hosts allow " -                                 "list (%s) for service %s.", entry, s) -                    valid = False -    return valid - -def check_client_access(lp, cname, caddr): -    # this is totally ugly, a real `quick' hack -    for s in lp.services(): -        if (allow_access(lp.get("hosts deny"), lp.get("hosts allow"), cname, -                         caddr) and -            allow_access(lp.get("hosts deny", s), lp.get("hosts allow", s), -                         cname, caddr)): -            logger.info("Allow connection from %s (%s) to %s", cname, caddr, s) -        else: -            logger.info("Deny connection from %s (%s) to %s", cname, caddr, s) - - -if __name__ == '__main__': -    parser = optparse.OptionParser("testparm [OPTION...] [host-name] [host-ip]") -    parser.add_option("--section-name", type="string", metavar="SECTION", -            help="Limit testparm to a named section") -    parser.add_option("--parameter-name", type="string", metavar="PARAMETER", -            help="Limit testparm to a named parameter") -    parser.add_option("--client-name", type="string", metavar="HOSTNAME", -            help="Client DNS name for 'hosts allow' checking " -                 "(should match reverse lookup)") -    parser.add_option("--client-ip", type="string", metavar="IP", -            help="Client IP address for 'hosts allow' checking") -    parser.add_option("--suppress-prompt", action="store_true", default=False, -            help="Suppress prompt for enter") -    parser.add_option("-v", "--verbose", action="store_true",  -            default=False, help="Show default options too") -    parser.add_option_group(options.VersionOptions(parser)) -    # We need support for smb.conf macros before this will work again  -    parser.add_option("--server", type="string", -            help="Set %%L macro to servername") -    # These are harder to do with the new code structure -    parser.add_option("--show-all-parameters", action="store_true", -            default=False, help="Show the parameters, type, possible values") - -    sambaopts = options.SambaOptions(parser) -    parser.add_option_group(sambaopts) - -    opts, args = parser.parse_args() - -#  -#    if (show_all_parameters) { -#        show_parameter_list() -#        exit(0) -#    } - -    if len(args) > 0: -        cname = args[0] -    else: -        cname = None -    if len(args) > 1: -        caddr = args[1] -    else: -        caddr = None - -    if cname is not None and caddr is None: -        print "Both a DNS name and an IP address are required for the host " \ -              "access check." -        sys.exit(1) - -#   FIXME: We need support for smb.conf macros before this will work again  -# -#    if (new_local_machine) { -#        set_local_machine_name(new_local_machine, True) -#    } - -    lp = sambaopts.get_loadparm() - -    # We need this to force the output -    samba.set_debug_level(2) - -    logger = logging.getLogger("testparm") -    logger.addHandler(logging.StreamHandler(sys.stdout)) - -    logger.info("Loaded smb config files from %s", lp.configfile) -    logger.info("Loaded services file OK.") - -    valid = do_global_checks(lp, logger) -    valid = valid and do_share_checks(lp, logger) -    if cname is not None: -        check_client_access(lp, cname, caddr) -    else: -        if opts.section_name is not None or opts.parameter_name is not None: -            if opts.parameter_name is None: -                lp[opts.section_name].dump(sys.stdout, lp.default_service, -                                           opts.verbose) -            else: -                print lp.get(opts.parameter_name, opts.section_name) -        else: -            if not opts.suppress_prompt: -                print "Press enter to see a dump of your service definitions\n" -                sys.stdin.readline() -            lp.dump(sys.stdout, opts.verbose) -    if valid: -        sys.exit(0) -    else: -        sys.exit(1) diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index 8c79917d5e..e98b642776 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -42,9 +42,10 @@ from samba.credentials import DONT_USE_KERBEROS  from samba.auth import system_session, admin_session  from ldb import (SCOPE_SUBTREE, SCOPE_BASE,                  FLAG_MOD_REPLACE, FLAG_MOD_ADD, FLAG_MOD_DELETE, -                MessageElement, Message, Dn) +                MessageElement, Message, Dn, LdbError)  from samba import param, dsdb, Ldb -from samba.provision import (get_domain_descriptor, +from samba.common import confirm +from samba.provision import (get_domain_descriptor, find_provision_key_parameters,                              get_config_descriptor,                              ProvisioningError, get_last_provision_usn,                              get_max_usn, update_provision_usn, setup_path) @@ -52,7 +53,7 @@ from samba.schema import get_linked_attributes, Schema, get_schema_descriptor  from samba.dcerpc import security, drsblobs, xattr  from samba.ndr import ndr_unpack  from samba.upgradehelpers import (dn_sort, get_paths, newprovision, -                                 find_provision_key_parameters, get_ldbs, +                                 get_ldbs,                                   usn_in_range, identic_rename, get_diff_sddls,                                   update_secrets, CHANGE, ERROR, SIMPLE,                                   CHANGEALL, GUESS, CHANGESD, PROVISION, @@ -81,20 +82,25 @@ __docformat__ = "restructuredText"  # This is most probably because they are populated automatcally when object is  # created  # This also apply to imported object from reference provision -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, -                        "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 } +replAttrNotCopied = [   "dn", "whenCreated", "whenChanged", "objectGUID", +                        "parentGUID", "objectCategory", "distinguishedName", +                        "nTMixedDomain", "showInAdvancedViewOnly", +                        "instanceType", "msDS-Behavior-Version", "cn", +                        "lmPwdHistory", "pwdLastSet", "ntPwdHistory", +                        "unicodePwd", "dBCSPwd", "supplementalCredentials", +                        "gPCUserExtensionNames", "gPCMachineExtensionNames", +                        "maxPwdAge", "secret", "possibleInferiors", "privilege", +                        "sAMAccountType", "oEMInformation", "creationTime" ] +nonreplAttrNotCopied = ["uSNCreated", "replPropertyMetaData", "uSNChanged", +                        "nextRid" ,"rIDNextRID", "rIDPreviousAllocationPool"] + +nonDSDBAttrNotCopied = ["msDS-KeyVersionNumber", "priorSecret", "priorWhenChanged"] + + +attrNotCopied = replAttrNotCopied +attrNotCopied.extend(nonreplAttrNotCopied) +attrNotCopied.extend(nonDSDBAttrNotCopied)  # Usually for an object that already exists we do not overwrite attributes as  # they might have been changed for good reasons. Anyway for a few of them it's  # mandatory to replace them otherwise the provision will be broken somehow. @@ -108,15 +114,19 @@ hashOverwrittenAtt = {  "prefixMap": replace, "systemMayContain": replace,                          "wellKnownObjects":replace, "privilege":never,                          "defaultSecurityDescriptor": replace,                          "rIDAvailablePool": never, +                        "versionNumber" : add,                          "rIDNextRID": add, "rIDUsedPool": never,                          "defaultSecurityDescriptor": replace + add,                          "isMemberOfPartialAttributeSet": delete,                          "attributeDisplayNames": replace + add,                          "versionNumber": add} +dnNotToRecalculate = [] +dnToRecalculate = []  backlinked = []  forwardlinked = set()  dn_syntax_att = [] +not_replicated = []  def define_what_to_log(opts):      what = 0      if opts.debugchange: @@ -151,6 +161,8 @@ parser.add_option("--debugall", action="store_true",                    help="Print all available information (very verbose)")  parser.add_option("--resetfileacl", action="store_true",                    help="Force a reset on filesystem acls in sysvol / netlogon share") +parser.add_option("--fixntacl", action="store_true", +                  help="Only fix NT ACLs in sysvol / netlogon share")  parser.add_option("--full", action="store_true",                    help="Perform full upgrade of the samdb (schema, configuration, new objects, ...") @@ -236,6 +248,26 @@ def populate_links(samdb, schemadn):      for t in linkedAttHash.keys():          forwardlinked.add(t) +def isReplicated(att): +    """ Indicate if the attribute is replicated or not + +    :param att: Name of the attribute to be tested +    :return: True is the attribute is replicated, False otherwise +    """ + +    return (att not in not_replicated) + +def populateNotReplicated(samdb, schemadn): +    """Populate an array with all the attributes that are not replicated + +    :param samdb: A LDB object for sam.ldb file +    :param schemadn: DN of the schema for the partition""" +    res = samdb.search(expression="(&(objectclass=attributeSchema)(systemflags:1.2.840.113556.1.4.803:=1))", base=Dn(samdb, +                        str(schemadn)), scope=SCOPE_SUBTREE, +                        attrs=["lDAPDisplayName"]) +    for elem in res: +        not_replicated.append(str(elem["lDAPDisplayName"])) +  def populate_dnsyntax(samdb, schemadn):      """Populate an array with all the attributes that have DN synthax @@ -295,7 +327,7 @@ def print_provision_key_parameters(names):      message(GUESS, "domainlevel :" + str(names.domainlevel)) -def handle_special_case(att, delta, new, old, usn, basedn, aldb): +def handle_special_case(att, delta, new, old, useReplMetadata, basedn, aldb):      """Define more complicate update rules for some attributes      :param att: The attribute to be updated @@ -303,7 +335,8 @@ def handle_special_case(att, delta, new, old, usn, basedn, aldb):                    between the updated object and the reference one      :param new: The reference object      :param old: The Updated object -    :param usn: The highest usn modified by a previous (upgrade)provision +    :param useReplMetadata: A boolean that indicate if the update process +                use replPropertyMetaData to decide what has to be updated.      :param basedn: The base DN of the provision      :param aldb: An ldb object used to build DN      :return: True to indicate that the attribute should be kept, False for @@ -313,7 +346,7 @@ def handle_special_case(att, delta, new, old, usn, basedn, aldb):      # We do most of the special case handle if we do not have the      # highest usn as otherwise the replPropertyMetaData will guide us more      # correctly -    if usn is None: +    if not useReplMetadata:          if (att == "sPNMappings" and flag == FLAG_MOD_REPLACE and              ldb.Dn(aldb, "CN=Directory Service,CN=Windows NT,"                          "CN=Services,CN=Configuration,%s" % basedn) @@ -389,14 +422,14 @@ def handle_special_case(att, delta, new, old, usn, basedn, aldb):      if (att == "servicePrincipalName" and flag == FLAG_MOD_REPLACE):          hash = {}          newval = [] -        changeDelta=0 +        changeDelta = 0          for elem in old[0][att]:              hash[str(elem)]=1              newval.append(str(elem))          for elem in new[0][att]:              if not hash.has_key(str(elem)): -                changeDelta=1 +                changeDelta = 1                  newval.append(str(elem))          if changeDelta == 1:              delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att) @@ -583,7 +616,7 @@ def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index):              m = re.match(r".*-(\d+)$", sid)              if m and int(m.group(1))>999:                  delta.remove("objectSid") -        for att in hashAttrNotCopied.keys(): +        for att in attrNotCopied:              delta.remove(att)          for att in backlinked:              delta.remove(att) @@ -650,7 +683,7 @@ def add_deletedobj_containers(ref_samdb, samdb, names):              delta = samdb.msg_diff(empty, reference[0])              delta.dn = Dn(samdb, str(reference[0]["dn"])) -            for att in hashAttrNotCopied.keys(): +            for att in attrNotCopied:                  delta.remove(att)              modcontrols = ["relax:0", "provision:0"] @@ -771,8 +804,192 @@ msg_elt_flag_strs = {      ldb.FLAG_MOD_REPLACE: "MOD_REPLACE",      ldb.FLAG_MOD_DELETE: "MOD_DELETE" } +def checkKeepAttributeOldMtd(delta, att, reference, current, +                                    basedn, samdb): +    """ Check if we should keep the attribute modification or not. +        This function didn't use replicationMetadata to take a decision. + +        :param delta: A message diff object +        :param att: An attribute +        :param reference: A message object for the current entry comming from +                            the reference provision. +        :param current: A message object for the current entry commin from +                            the current provision. +        :param basedn: The DN of the partition +        :param samdb: A ldb connection to the sam database of the current provision. + +        :return: The modified message diff. +    """ +    # Old school way of handling things for pre alpha12 upgrade +    global defSDmodified +    isFirst = False +    txt = "" +    dn = current[0].dn + +    for att in list(delta): +        msgElt = delta.get(att) + +        if att == "nTSecurityDescriptor": +            defSDmodified = True +            delta.remove(att) +            continue + +        if att == "dn": +            continue + +        if not hashOverwrittenAtt.has_key(att): +            if msgElt.flags() != FLAG_MOD_ADD: +                if not handle_special_case(att, delta, reference, current, +                                            False, basedn, samdb): +                    if opts.debugchange or opts.debugall: +                        try: +                            dump_denied_change(dn, att, +                                msg_elt_flag_strs[msgElt.flags()], +                                current[0][att], reference[0][att]) +                        except KeyError: +                            dump_denied_change(dn, att, +                                msg_elt_flag_strs[msgElt.flags()], +                                current[0][att], None) +                    delta.remove(att) +                continue +        else: +            if hashOverwrittenAtt.get(att)&2**msgElt.flags() : +                continue +            elif  hashOverwrittenAtt.get(att)==never: +                delta.remove(att) +                continue + +    return delta + +def checkKeepAttributeWithMetadata(delta, att, message, reference, current, +                                    hash_attr_usn, basedn, usns, samdb): +    """ Check if we should keep the attribute modification or not + +        :param delta: A message diff object +        :param att: An attribute +        :param message: A function to print messages +        :param reference: A message object for the current entry comming from +                            the reference provision. +        :param current: A message object for the current entry commin from +                            the current provision. +        :param hash_attr_usn: A dictionnary with attribute name as keys, +                                USN and invocation id as values. +        :param basedn: The DN of the partition +        :param usns: A dictionnary with invocation ID as keys and USN ranges +                     as values. +        :param samdb: A ldb object pointing to the sam DB + +        :return: The modified message diff. +    """ +    global defSDmodified +    isFirst = True +    txt = "" +    dn = current[0].dn + +    for att in list(delta): +        if att in ["dn", "objectSid"]: +            delta.remove(att) +            continue + +        # We have updated by provision usn information so let's exploit +        # replMetadataProperties +        if att in forwardlinked: +            curval = current[0].get(att, ()) +            refval = reference[0].get(att, ()) +            handle_links(samdb, att, basedn, current[0]["dn"], +                            curval, refval, delta) +            continue + +        if isFirst and len(delta.items())>1: +            isFirst = False +            txt = "%s\n" % (str(dn)) + +        if handle_special_case(att, delta, reference, current, True, None, None): +            # This attribute is "complicated" to handle and handling +            # was done in handle_special_case +            continue + +        attrUSN = None +        if hash_attr_usn.get(att): +            [attrUSN, attInvId] = hash_attr_usn.get(att) + +        if attrUSN is None: +            # If it's a replicated attribute and we don't have any USN +            # information about it. It means that we never saw it before +            # so let's add it ! +            # If it is a replicated attribute but we are not master on it +            # (ie. not initially added in the provision we masterize). +            # attrUSN will be -1 +            if isReplicated(att): +                continue +            else: +                message(CHANGE, "Non replicated attribute %s changed" % att) +                continue -def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid): +        if att == "nTSecurityDescriptor": +            cursd = ndr_unpack(security.descriptor, +                str(current[0]["nTSecurityDescriptor"])) +            cursddl = cursd.as_sddl(names.domainsid) +            refsd = ndr_unpack(security.descriptor, +                str(reference[0]["nTSecurityDescriptor"])) +            refsddl = refsd.as_sddl(names.domainsid) + +            diff = get_diff_sddls(refsddl, cursddl) +            if diff == "": +                # FIXME find a way to have it only with huge huge verbose mode +                # message(CHANGE, "%ssd are identical" % txt) +                # txt = "" +                delta.remove(att) +                continue +            else: +                delta.remove(att) +                message(CHANGESD, "%ssd are not identical:\n%s" % (txt, diff)) +                txt = "" +                if attrUSN == -1: +                    message(CHANGESD, "But the SD has been changed by someonelse "\ +                                    "so it's impossible to know if the difference"\ +                                    " cames from the modification or from a previous bug") +                    dnNotToRecalculate.append(str(dn)) +                else: +                    dnToRecalculate.append(str(dn)) +                continue + +        if attrUSN == -1: +            # This attribute was last modified by another DC forget +            # about it +            message(CHANGE, "%sAttribute: %s has been " +                    "created/modified/deleted by another DC. " +                    "Doing nothing" % (txt, att)) +            txt = "" +            delta.remove(att) +            continue +        elif not usn_in_range(int(attrUSN), usns.get(attInvId)): +            message(CHANGE, "%sAttribute: %s was not " +                            "created/modified/deleted during a " +                            "provision or upgradeprovision. Current " +                            "usn: %d. Doing nothing" % (txt, att, +                                                        attrUSN)) +            txt = "" +            delta.remove(att) +            continue +        else: +            if att == "defaultSecurityDescriptor": +                defSDmodified = True +            if attrUSN: +                message(CHANGE, "%sAttribute: %s will be modified" +                                "/deleted it was last modified " +                                "during a provision. Current usn: " +                                "%d" % (txt, att, attrUSN)) +                txt = "" +            else: +                message(CHANGE, "%sAttribute: %s will be added because " +                                "it did not exist before" % (txt, att)) +                txt = "" +            continue + +    return delta + +def update_present(ref_samdb, samdb, basedn, listPresent, usns):      """ This function updates the object that are already present in the          provision @@ -782,10 +999,9 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):                     (ie. DC=foo, DC=bar)      :param listPresent: A list of object that is present in the provision      :param usns: A list of USN range modified by previous provision and -                 upgradeprovision -    :param invocationid: The value of the invocationid for the current DC""" +                 upgradeprovision grouped by invocation ID +    """ -    global defSDmodified      # This hash is meant to speedup lookup of attribute name from an oid,      # it's for the replPropertyMetaData handling      hash_oid_name = {} @@ -801,7 +1017,9 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):          raise ProvisioningError(msg)      changed = 0 -    controls = ["search_options:1:2", "sd_flags:1:2"] +    controls = ["search_options:1:2", "sd_flags:1:0"] +    if usns is not None: +            message(CHANGE, "Using replPropertyMetadata for change selection")      for dn in listPresent:          reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,                                          scope=SCOPE_SUBTREE, @@ -823,14 +1041,17 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):          delta = samdb.msg_diff(current[0], reference[0]) -        for att in hashAttrNotCopied.keys(): +        for att in backlinked:              delta.remove(att) -        for att in backlinked: +        for att in attrNotCopied:              delta.remove(att)          delta.remove("name") +        if len(delta.items()) == 1: +            continue +          if len(delta.items()) > 1 and usns is not None:              # Fetch the replPropertyMetaData              res = samdb.search(expression="dn=%s" % (str(dn)), base=basedn, @@ -844,145 +1065,25 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):                  # We put in this hash only modification                  # made on the current host                  att = hash_oid_name[samdb.get_oid_from_attid(o.attid)] -                if str(o.originating_invocation_id) == str(invocationid): -                # Note we could just use 1 here -                    hash_attr_usn[att] = o.originating_usn +                if str(o.originating_invocation_id) in usns.keys(): +                    hash_attr_usn[att] = [o.originating_usn, str(o.originating_invocation_id)]                  else: -                    hash_attr_usn[att] = -1 - -        isFirst = 0 -        txt = "" - -        for att in list(delta): -            if usns is not None: -                # We have updated by provision usn information so let's exploit -                # replMetadataProperties -                if att in forwardlinked: -                    curval = current[0].get(att, ()) -                    refval = reference[0].get(att, ()) -                    handle_links(samdb, att, basedn, current[0]["dn"], -                                    curval, refval, delta) -                    continue - -                if isFirst == 0 and len(delta.items())>1: -                    isFirst = 1 -                    txt = "%s\n" % (str(dn)) -                if att == "dn": -                    # There is always a dn attribute after a msg_diff -                    continue -                if att == "rIDAvailablePool": -                    delta.remove(att) -                    continue -                if att == "objectSid": -                    delta.remove(att) -                    continue -                if att == "creationTime": -                    delta.remove(att) -                    continue -                if att == "oEMInformation": -                    delta.remove(att) -                    continue -                if att == "msDs-KeyVersionNumber": -                # This is the kvno of the computer/user it's a very bad -                # idea to change it -                    delta.remove(att) -                    continue -                if handle_special_case(att, delta, reference, current, usns, basedn, samdb): -                    # This attribute is "complicated" to handle and handling -                    # was done in handle_special_case -                    continue -                attrUSN = hash_attr_usn.get(att) -                if att == "forceLogoff" and attrUSN is None: -                    continue -                if  attrUSN is None: -                    delta.remove(att) -                    continue -                if att == "nTSecurityDescriptor": -                    cursd = ndr_unpack(security.descriptor, -                        str(current[0]["nTSecurityDescriptor"])) -                    cursddl = cursd.as_sddl(names.domainsid) -                    refsd = ndr_unpack(security.descriptor, -                        str(reference[0]["nTSecurityDescriptor"])) -                    refsddl = cursd.as_sddl(names.domainsid) - -                    if get_diff_sddls(refsddl, cursddl) == "": -                       message(CHANGE, "sd are identical") -                    else: -                       message(CHANGE, "sd are not identical") -                if attrUSN == -1: -                    # This attribute was last modified by another DC forget -                    # about it -                    message(CHANGE, "%sAttribute: %s has been " -                            "created/modified/deleted by another DC. " -                            "Doing nothing" % (txt, att)) -                    txt = "" -                    delta.remove(att) -                    continue -                elif not usn_in_range(int(attrUSN), usns): -                    message(CHANGE, "%sAttribute: %s was not " -                                    "created/modified/deleted during a " -                                    "provision or upgradeprovision. Current " -                                    "usn: %d. Doing nothing" % (txt, att, -                                                                attrUSN)) -                    txt = "" -                    delta.remove(att) -                    continue -                else: -                    if att == "defaultSecurityDescriptor": -                        defSDmodified = True -                    if attrUSN: -                        message(CHANGE, "%sAttribute: %s will be modified" -                                        "/deleted it was last modified " -                                        "during a provision. Current usn: " -                                        "%d" % (txt, att, attrUSN)) -                        txt = "" -                    else: -                        message(CHANGE, "%sAttribute: %s will be added because " -                                        "it did not exist before" % (txt, att)) -                        txt = "" -                    continue +                    hash_attr_usn[att] = [-1, None] -            else: -            # Old school way of handling things for pre alpha12 upgrade -                defSDmodified = True -                msgElt = delta.get(att) - -                if att == "nTSecurityDescriptor": -                    delta.remove(att) -                    continue - -                if att == "dn": -                    continue - -                if not hashOverwrittenAtt.has_key(att): -                    if msgElt.flags() != FLAG_MOD_ADD: -                        if not handle_special_case(att, delta, reference, current, -                                                    usns, basedn, samdb): -                            if opts.debugchange or opts.debugall: -                                try: -                                    dump_denied_change(dn, att, -                                        msg_elt_flag_strs[msgElt.flags()], -                                        current[0][att], reference[0][att]) -                                except KeyError: -                                    dump_denied_change(dn, att, -                                        msg_elt_flag_strs[msgElt.flags()], -                                        current[0][att], None) -                            delta.remove(att) -                        continue -                else: -                    if hashOverwrittenAtt.get(att)&2**msgElt.flags() : -                        continue -                    elif  hashOverwrittenAtt.get(att)==never: -                        delta.remove(att) -                        continue +        if usns is not None: +            delta = checkKeepAttributeWithMetadata(delta, att, message, reference, +                                                    current, hash_attr_usn, +                                                    basedn, usns, samdb) +        else: +            delta =  checkKeepAttributeOldMtd(delta, att, reference, current, basedn, samdb)          delta.dn = dn          if len(delta.items()) >1: -            attributes=", ".join(delta.keys()) +            # Skip dn as the value is not really changed ... +            attributes=", ".join(delta.keys()[1:])              modcontrols = []              relaxedatt = ['iscriticalsystemobject', 'grouptype']              # Let's try to reduce as much as possible the use of relax control -            #for checkedatt in relaxedatt:              for attr in delta.keys():                  if attr.lower() in relaxedatt:                      modcontrols = ["relax:0", "provision:0"] @@ -1033,8 +1134,8 @@ def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs, pre      :param basedn: String value of the DN of the partition      :param names: List of key provision parameters      :param schema: A Schema object -    :param provisionUSNs:  The USNs modified by provision/upgradeprovision -                           last time +    :param provisionUSNs:  A dictionnary with range of USN modified during provision +                            or upgradeprovision. Ranges are grouped by invocationID.      :param prereloadfunc: A function that must be executed just before the reload                    of the schema      """ @@ -1096,7 +1197,7 @@ def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs, pre          message(SIMPLE, "Schema reloaded!")          changed = update_present(ref_samdb, samdb, basedn, listPresent, -                                    provisionUSNs, names.invocation) +                                    provisionUSNs)          message(SIMPLE, "There are %d changed objects" % (changed))          return 1 @@ -1138,7 +1239,7 @@ def check_updated_sd(ref_sam, cur_sam, names):                          str(current[i]["nTSecurityDescriptor"]))              sddl = cursd.as_sddl(names.domainsid)              if sddl != hash[key]: -                txt = get_diff_sddls(hash[key], sddl) +                txt = get_diff_sddls(hash[key], sddl, False)                  if txt != "":                      message(CHANGESD, "On object %s ACL is different"                                        " \n%s" % (current[i]["dn"], txt)) @@ -1152,37 +1253,38 @@ def fix_partition_sd(samdb, names):      :param samdb: An LDB object pointing to the sam of the current provision      :param names: A list of key provision parameters      """ +    alwaysRecalculate = False +    if len(dnToRecalculate) == 0 and len(dnNotToRecalculate) == 0: +        alwaysRecalculate = True + + +    # NC's DN can't be both in dnToRecalculate and dnNotToRecalculate      # First update the SD for the rootdn -    res = samdb.search(expression="objectClass=*", base=str(names.rootdn), -                         scope=SCOPE_BASE, attrs=["dn", "whenCreated"], -                         controls=["search_options:1:2"]) -    delta = Message() -    delta.dn = Dn(samdb, str(res[0]["dn"])) -    descr = get_domain_descriptor(names.domainsid) -    delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE, -                                                    "nTSecurityDescriptor") -    samdb.modify(delta) +    if alwaysRecalculate or str(names.rootdn) in dnToRecalculate: +        delta = Message() +        delta.dn = Dn(samdb, str(names.rootdn)) +        descr = get_domain_descriptor(names.domainsid) +        delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE, +                                                        "nTSecurityDescriptor") +        samdb.modify(delta) +      # Then the config dn -    res = samdb.search(expression="objectClass=*", base=str(names.configdn), -                        scope=SCOPE_BASE, attrs=["dn", "whenCreated"], -                        controls=["search_options:1:2"]) -    delta = Message() -    delta.dn = Dn(samdb, str(res[0]["dn"])) -    descr = get_config_descriptor(names.domainsid) -    delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE, -                                                    "nTSecurityDescriptor" ) -    samdb.modify(delta) -    # Then the schema dn -    res = samdb.search(expression="objectClass=*", base=str(names.schemadn), -                        scope=SCOPE_BASE, attrs=["dn", "whenCreated"], -                        controls=["search_options:1:2"]) +    if alwaysRecalculate or str(names.configdn) in dnToRecalculate: +        delta = Message() +        delta.dn = Dn(samdb, str(names.configdn)) +        descr = get_config_descriptor(names.domainsid) +        delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE, +                                                        "nTSecurityDescriptor" ) +        samdb.modify(delta) -    delta = Message() -    delta.dn = Dn(samdb, str(res[0]["dn"])) -    descr = get_schema_descriptor(names.domainsid) -    delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE, -                                                    "nTSecurityDescriptor" ) -    samdb.modify(delta) +    # Then the schema dn +    if alwaysRecalculate or str(names.schemadn) in dnToRecalculate: +        delta = Message() +        delta.dn = Dn(samdb, str(names.schemadn)) +        descr = get_schema_descriptor(names.domainsid) +        delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE, +                                                        "nTSecurityDescriptor" ) +        samdb.modify(delta)  def rebuild_sd(samdb, names):      """Rebuild security descriptor of the current provision from scratch @@ -1195,30 +1297,46 @@ def rebuild_sd(samdb, names):      :param names: List of key provision parameters""" +    fix_partition_sd(samdb, names) +    # List of namming contexts +    listNC = [str(names.rootdn), str(names.configdn), str(names.schemadn)]      hash = {} -    res = samdb.search(expression="objectClass=*", base=str(names.rootdn), +    if len(dnToRecalculate) == 0: +        res = samdb.search(expression="objectClass=*", base=str(names.rootdn),                          scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"],                          controls=["search_options:1:2"]) -    for obj in res: -        if not (str(obj["dn"]) == str(names.rootdn) or -            str(obj["dn"]) == str(names.configdn) or -            str(obj["dn"]) == str(names.schemadn)): -            hash[str(obj["dn"])] = obj["whenCreated"] - -    listkeys = hash.keys() -    listkeys.sort(dn_sort) - -    for key in listkeys: +        for obj in res: +                hash[str(obj["dn"])] = obj["whenCreated"] +    else: +        for dn in dnToRecalculate: +            if hash.has_key(dn): +                continue +            # fetch each dn to recalculate and their child within the same partition +            res = samdb.search(expression="objectClass=*", base=dn, +                        scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"]) +            for obj in res: +                hash[str(obj["dn"])] = obj["whenCreated"] + +    listKeys = list(set(hash.keys())) +    listKeys.sort(dn_sort) + +    if len(dnToRecalculate) != 0: +        message(CHANGESD, "%d DNs have been marked as needed to be recalculated"\ +                            ", recalculating %d due to inheritance" +                            % (len(dnToRecalculate), len(listKeys))) + +    for key in listKeys: +        if (key in listNC or +                    key in dnNotToRecalculate): +            continue +        delta = Message() +        delta.dn = Dn(samdb, key)          try: -            delta = Message() -            delta.dn = Dn(samdb, key)              delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE,                                                      "whenCreated" ) -            samdb.modify(delta, ["recalculate_sd:0"]) -        except: -            # XXX: We should always catch an explicit exception. -            # What could go wrong here? +            samdb.modify(delta, ["recalculate_sd:0","relax:0"]) +        except LdbError, e:              samdb.transaction_cancel()              res = samdb.search(expression="objectClass=*", base=str(names.rootdn),                                  scope=SCOPE_SUBTREE, @@ -1226,14 +1344,13 @@ def rebuild_sd(samdb, names):                                  controls=["search_options:1:2"])              badsd = ndr_unpack(security.descriptor,                          str(res[0]["nTSecurityDescriptor"])) -            print "bad stuff %s" % badsd.as_sddl(names.domainsid) +            message(ERROR, "On %s bad stuff %s" % (str(delta.dn),badsd.as_sddl(names.domainsid)))              return  def removeProvisionUSN(samdb):          attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"]          entry = samdb.search(expression="dn=@PROVISION", base = "",                                  scope=SCOPE_SUBTREE, -                                controls=["search_options:1:2"],                                  attrs=attrs)          empty = Message()          empty.dn = entry[0].dn @@ -1303,7 +1420,7 @@ def update_privilege(ref_private_path, cur_private_path):                  os.path.join(cur_private_path, "privilege.ldb")) -def update_samdb(ref_samdb, samdb, names, highestUSN, schema, prereloadfunc): +def update_samdb(ref_samdb, samdb, names, provisionUSNs, schema, prereloadfunc):      """Upgrade the SAM DB contents for all the provision partitions      :param ref_sambdb: An LDB object conntected to the sam.ldb of the reference @@ -1311,8 +1428,8 @@ def update_samdb(ref_samdb, samdb, names, highestUSN, schema, prereloadfunc):      :param samdb: An LDB object connected to the sam.ldb of the update                    provision      :param names: List of key provision parameters -    :param highestUSN:  The highest USN modified by provision/upgradeprovision -                        last time +    :param provisionUSNs:  A dictionnary with range of USN modified during provision +                            or upgradeprovision. Ranges are grouped by invocationID.      :param schema: A Schema object that represent the schema of the provision      :param prereloadfunc: A function that must be executed just before the reload                    of the schema @@ -1320,7 +1437,7 @@ def update_samdb(ref_samdb, samdb, names, highestUSN, schema, prereloadfunc):      message(SIMPLE, "Starting update of samdb")      ret = update_partition(ref_samdb, samdb, str(names.rootdn), names, -                            schema, highestUSN, prereloadfunc) +                            schema, provisionUSNs, prereloadfunc)      if ret:          message(SIMPLE, "Update of samdb finished")          return 1 @@ -1537,7 +1654,7 @@ def sync_calculated_attributes(samdb, names):  # This resulting object is filtered to remove all the back link attribute  # (ie. memberOf) as they will be created by the other linked object (ie.  # the one with the member attribute) -# All attributes specified in the hashAttrNotCopied associative array are +# All attributes specified in the attrNotCopied array are  # also removed it's most of the time generated attributes  # After missing entries have been added the update_partition function will @@ -1601,202 +1718,226 @@ if __name__ == '__main__':          # 4)          lastProvisionUSNs = get_last_provision_usn(ldbs.sam)          if lastProvisionUSNs is not None: +            v = 0 +            for k in lastProvisionUSNs.keys(): +                for r in lastProvisionUSNs[k]: +                    v = v + 1 +              message(CHANGE, -                "Find a last provision USN, %d range(s)" % len(lastProvisionUSNs)) +                "Find last provision USN, %d invocation(s) for a total of %d ranges" % \ +                            (len(lastProvisionUSNs.keys()), v /2 )) + +            if lastProvisionUSNs.get("default") != None: +                message(CHANGE, "Old style for usn ranges used") +                lastProvisionUSNs[str(names.invocation)] = lastProvisionUSNs["default"] +                del lastProvisionUSNs["default"] +        else: +            message(SIMPLE, "Your provision lacks provision range information") +            if confirm("Do you want to run findprovisionusnranges to try to find them ?", False): +                ldbs.groupedRollback() +                os.system("%s %s %s %s %s" % (os.path.join(os.path.dirname(sys.argv[0]), +                                            "findprovisionusnranges"), +                                        "--storedir", +                                        paths.private_dir, +                                        "-s", +                                        smbconf)) +                message(SIMPLE, "Once you applied/adapted the change(s) please restart the upgradeprovision script") +                sys.exit(0)          # Objects will be created with the admin session          # (not anymore system session)          adm_session = admin_session(lp, str(names.domainsid))          # So we reget handle on objects          # ldbs = get_ldbs(paths, creds, adm_session, lp) - -        if not sanitychecks(ldbs.sam, names): -            message(SIMPLE, "Sanity checks for the upgrade have failed. " -                            "Check the messages and correct the errors " -                            "before rerunning upgradeprovision") -            sys.exit(1) - -        # Let's see provision parameters -        print_provision_key_parameters(names) - -        # 5) With all this information let's create a fresh new provision used as -        # reference -        message(SIMPLE, "Creating a reference provision") -        provisiondir = tempfile.mkdtemp(dir=paths.private_dir, -                                        prefix="referenceprovision") -        newprovision(names, creds, session, smbconf, provisiondir, -                        provision_logger) - -        # TODO -        # 6) and 7) -        # We need to get a list of object which SD is directly computed from -        # defaultSecurityDescriptor. -        # This will allow us to know which object we can rebuild the SD in case -        # of change of the parent's SD or of the defaultSD. -        # Get file paths of this new provision -        newpaths = get_paths(param, targetdir=provisiondir) -        new_ldbs = get_ldbs(newpaths, creds, session, lp) -        new_ldbs.startTransactions() - -        # 8) Populate some associative array to ease the update process -        # List of attribute which are link and backlink -        populate_links(new_ldbs.sam, names.schemadn) -        # List of attribute with ASN DN synthax) -        populate_dnsyntax(new_ldbs.sam, names.schemadn) -        # 9) -        update_privilege(newpaths.private_dir, paths.private_dir) -        # 10) -        oem = getOEMInfo(ldbs.sam, str(names.rootdn)) -        # Do some modification on sam.ldb -        ldbs.groupedCommit() -        new_ldbs.groupedCommit() -        deltaattr = None -# 11) -        if re.match(".*alpha((9)|(\d\d+)).*", str(oem)): -            # 11) A -            # Starting from alpha9 we can consider that the structure is quite ok -            # and that we should do only dela -            deltaattr = delta_update_basesamdb(newpaths.samdb, -                                                paths.samdb, -                                                creds, -                                                session, -                                                lp, -                                                message) -        else: -            # 11) B -            simple_update_basesamdb(newpaths, paths, names) -            ldbs = get_ldbs(paths, creds, session, lp) -            removeProvisionUSN(ldbs.sam) - -        ldbs.startTransactions() -        minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1 -        new_ldbs.startTransactions() - -        # 12) -        schema = Schema(names.domainsid, schemadn=str(names.schemadn)) -        # We create a closure that will be invoked just before schema reload -        def schemareloadclosure(): -            basesam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp, -                            options=["modules:"]) -            doit = False -            if deltaattr is not None and len(deltaattr) > 1: -                doit = True -            if doit: -                deltaattr.remove("dn") -                for att in deltaattr: -                    if att.lower() == "dn": -                        continue -                    if (deltaattr.get(att) is not None -                        and deltaattr.get(att).flags() != FLAG_MOD_ADD): -                        doit = False -                    elif deltaattr.get(att) is None: -                        doit = False -            if doit: -                message(CHANGE, "Applying delta to @ATTRIBUTES") -                deltaattr.dn = ldb.Dn(basesam, "@ATTRIBUTES") -                basesam.modify(deltaattr) -            else: -                message(CHANGE, "Not applying delta to @ATTRIBUTES because " -                                "there is not only add") -        # 13) -        if opts.full: -            if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs, -                                schema, schemareloadclosure): -                message(SIMPLE, "Rolling back all changes. Check the cause" -                                " of the problem") -                message(SIMPLE, "Your system is as it was before the upgrade") +        if not opts.fixntacl: +            if not sanitychecks(ldbs.sam, names): +                message(SIMPLE, "Sanity checks for the upgrade have failed. " +                        "Check the messages and correct the errors " +                        "before rerunning upgradeprovision")                  ldbs.groupedRollback() -                new_ldbs.groupedRollback() -                shutil.rmtree(provisiondir)                  sys.exit(1) -        else: -            # Try to reapply the change also when we do not change the sam -            # as the delta_upgrade -            schemareloadclosure() -            sync_calculated_attributes(ldbs.sam, names) + +            # Let's see provision parameters +            print_provision_key_parameters(names) + +            # 5) With all this information let's create a fresh new provision used as +            # reference +            message(SIMPLE, "Creating a reference provision") +            provisiondir = tempfile.mkdtemp(dir=paths.private_dir, +                            prefix="referenceprovision") +            newprovision(names, creds, session, smbconf, provisiondir, +                    provision_logger) + +            # TODO +            # 6) and 7) +            # We need to get a list of object which SD is directly computed from +            # defaultSecurityDescriptor. +            # This will allow us to know which object we can rebuild the SD in case +            # of change of the parent's SD or of the defaultSD. +            # Get file paths of this new provision +            newpaths = get_paths(param, targetdir=provisiondir) +            new_ldbs = get_ldbs(newpaths, creds, session, lp) +            new_ldbs.startTransactions() + +            populateNotReplicated(new_ldbs.sam, names.schemadn) +            # 8) Populate some associative array to ease the update process +            # List of attribute which are link and backlink +            populate_links(new_ldbs.sam, names.schemadn) +            # List of attribute with ASN DN synthax) +            populate_dnsyntax(new_ldbs.sam, names.schemadn) +            # 9) +            update_privilege(newpaths.private_dir, paths.private_dir) +            # 10) +            oem = getOEMInfo(ldbs.sam, str(names.rootdn)) +            # Do some modification on sam.ldb +            ldbs.groupedCommit() +            new_ldbs.groupedCommit() +            deltaattr = None +        # 11) +            if re.match(".*alpha((9)|(\d\d+)).*", str(oem)): +                # 11) A +                # Starting from alpha9 we can consider that the structure is quite ok +                # and that we should do only dela +                deltaattr = delta_update_basesamdb(newpaths.samdb, +                                paths.samdb, +                                creds, +                                session, +                                lp, +                                message) +            else: +                # 11) B +                simple_update_basesamdb(newpaths, paths, names) +                ldbs = get_ldbs(paths, creds, session, lp) +                removeProvisionUSN(ldbs.sam) + +            ldbs.startTransactions() +            minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1 +            new_ldbs.startTransactions() + +            # 12) +            schema = Schema(names.domainsid, schemadn=str(names.schemadn)) +            # We create a closure that will be invoked just before schema reload +            def schemareloadclosure(): +                basesam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp, +                        options=["modules:"]) +                doit = False +                if deltaattr is not None and len(deltaattr) > 1: +                    doit = True +                if doit: +                    deltaattr.remove("dn") +                    for att in deltaattr: +                        if att.lower() == "dn": +                            continue +                        if (deltaattr.get(att) is not None +                            and deltaattr.get(att).flags() != FLAG_MOD_ADD): +                            doit = False +                        elif deltaattr.get(att) is None: +                            doit = False +                if doit: +                    message(CHANGE, "Applying delta to @ATTRIBUTES") +                    deltaattr.dn = ldb.Dn(basesam, "@ATTRIBUTES") +                    basesam.modify(deltaattr) +                else: +                    message(CHANGE, "Not applying delta to @ATTRIBUTES because " +                        "there is not only add") +            # 13) +            if opts.full: +                if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs, +                        schema, schemareloadclosure): +                    message(SIMPLE, "Rolling back all changes. Check the cause" +                            " of the problem") +                    message(SIMPLE, "Your system is as it was before the upgrade") +                    ldbs.groupedRollback() +                    new_ldbs.groupedRollback() +                    shutil.rmtree(provisiondir) +                    sys.exit(1) +            else: +                # Try to reapply the change also when we do not change the sam +                # as the delta_upgrade +                schemareloadclosure() +                sync_calculated_attributes(ldbs.sam, names) +                res = ldbs.sam.search(expression="(samaccountname=dns)", +                        scope=SCOPE_SUBTREE, attrs=["dn"], +                        controls=["search_options:1:2"]) +                if len(res) > 0: +                    message(SIMPLE, "You still have the old DNS object for managing " +                            "dynamic DNS, but you didn't supply --full so " +                            "a correct update can't be done") +                    ldbs.groupedRollback() +                    new_ldbs.groupedRollback() +                    shutil.rmtree(provisiondir) +                    sys.exit(1) +            # 14) +            update_secrets(new_ldbs.secrets, ldbs.secrets, message) +            # 14bis)              res = ldbs.sam.search(expression="(samaccountname=dns)", -                                scope=SCOPE_SUBTREE, attrs=["dn"], -                                controls=["search_options:1:2"]) -            if len(res) > 0: -                message(SIMPLE, "You still have the old DNS object for managing " -                                "dynamic DNS, but you didn't supply --full so " -                                "a correct update can't be done") -                ldbs.groupedRollback() -                new_ldbs.groupedRollback() -                shutil.rmtree(provisiondir) -                sys.exit(1) -        # 14) -        update_secrets(new_ldbs.secrets, ldbs.secrets, message) -        # 14bis) -        res = ldbs.sam.search(expression="(samaccountname=dns)", -                                  scope=SCOPE_SUBTREE, attrs=["dn"], -                                  controls=["search_options:1:2"]) - -        if (len(res) == 1): -            ldbs.sam.delete(res[0]["dn"]) -            res2 = ldbs.secrets.search(expression="(samaccountname=dns)", -                                  scope=SCOPE_SUBTREE, attrs=["dn"]) -            update_dns_account_password(ldbs.sam, ldbs.secrets, names) -            message(SIMPLE, "IMPORTANT!!! " -                            "If you were using Dynamic DNS before you need " -                            "to update your configuration, so that the " -                            "tkey-gssapi-credential has the following value: " -                            "DNS/%s.%s" % (names.netbiosname.lower(), -                                           names.realm.lower())) -        # 15) -        message(SIMPLE, "Update machine account") -        update_machine_account_password(ldbs.sam, ldbs.secrets, names) - -        # 16) SD should be created with admin but as some previous acl were so wrong -        # that admin can't modify them we have first to recreate them with the good -        # form but with system account and then give the ownership to admin ... -        if not re.match(r'.*alpha(9|\d\d+)', str(oem)): -            message(SIMPLE, "Fixing old povision SD") -            fix_partition_sd(ldbs.sam, names) -            rebuild_sd(ldbs.sam, names) - -        # We calculate the max USN before recalculating the SD because we might -        # touch object that have been modified after a provision and we do not -        # want that the next upgradeprovision thinks that it has a green light -        # to modify them - -        # 17) -        maxUSN = get_max_usn(ldbs.sam, str(names.rootdn)) - -        # 18) We rebuild SD only if defaultSecurityDescriptor is modified -        # But in fact we should do it also if one object has its SD modified as -        # child might need rebuild -        if defSDmodified: -            message(SIMPLE, "Updating SD") -            ldbs.sam.set_session_info(adm_session) -            # Alpha10 was a bit broken still -            if re.match(r'.*alpha(\d|10)', str(oem)): -                fix_partition_sd(ldbs.sam, names) -            rebuild_sd(ldbs.sam, names) - -        # 19) -        # Now we are quite confident in the recalculate process of the SD, we make -        # it optional. -        # Also the check must be done in a clever way as for the moment we just -        # compare SDDL -        if opts.debugchangesd: -            check_updated_sd(new_ldbs.sam, ldbs.sam, names) - -        # 20) -        updateOEMInfo(ldbs.sam, str(names.rootdn)) -        # 21) -        check_for_DNS(newpaths.private_dir, paths.private_dir) -        # 22) -        if lastProvisionUSNs is not None: -            update_provision_usn(ldbs.sam, minUSN, maxUSN) -        if opts.full and (names.policyid is None or names.policyid_dc is None): -            update_policyids(names, ldbs.sam) -        if opts.full or opts.resetfileacl: +                        scope=SCOPE_SUBTREE, attrs=["dn"], +                        controls=["search_options:1:2"]) + +            if (len(res) == 1): +                ldbs.sam.delete(res[0]["dn"]) +                res2 = ldbs.secrets.search(expression="(samaccountname=dns)", +                        scope=SCOPE_SUBTREE, attrs=["dn"]) +                update_dns_account_password(ldbs.sam, ldbs.secrets, names) +                message(SIMPLE, "IMPORTANT!!! " +                        "If you were using Dynamic DNS before you need " +                        "to update your configuration, so that the " +                        "tkey-gssapi-credential has the following value: " +                        "DNS/%s.%s" % (names.netbiosname.lower(), +                            names.realm.lower())) +            # 15) +            message(SIMPLE, "Update machine account") +            update_machine_account_password(ldbs.sam, ldbs.secrets, names) + +            dnToRecalculate.sort(dn_sort) +            # 16) SD should be created with admin but as some previous acl were so wrong +            # that admin can't modify them we have first to recreate them with the good +            # form but with system account and then give the ownership to admin ... +            if str(oem) != "" and not re.match(r'.*alpha(9|\d\d+)', str(oem)): +                message(SIMPLE, "Fixing very old provision SD") +                rebuild_sd(ldbs.sam, names) + +            # We calculate the max USN before recalculating the SD because we might +            # touch object that have been modified after a provision and we do not +            # want that the next upgradeprovision thinks that it has a green light +            # to modify them + +            # 17) +            maxUSN = get_max_usn(ldbs.sam, str(names.rootdn)) + +            # 18) We rebuild SD if a we have a list of DN to recalculate or if the +            # defSDmodified is set. +            if defSDmodified or len(dnToRecalculate) >0: +                message(SIMPLE, "Some defaultSecurityDescriptors and/or" +                                "securityDescriptor have changed, recalculating SD ") +                ldbs.sam.set_session_info(adm_session) +                rebuild_sd(ldbs.sam, names) + +            # 19) +            # Now we are quite confident in the recalculate process of the SD, we make +            # it optional. And we don't do it if there is DN that we must touch +            # as we are assured that on this DNs we will have differences ! +            # Also the check must be done in a clever way as for the moment we just +            # compare SDDL +            if len(dnNotToRecalculate) == 0 and (opts.debugchangesd or opts.debugall): +                message(CHANGESD, "Checking recalculated SDs") +                check_updated_sd(new_ldbs.sam, ldbs.sam, names) + +            # 20) +            updateOEMInfo(ldbs.sam, str(names.rootdn)) +            # 21) +            check_for_DNS(newpaths.private_dir, paths.private_dir) +            # 22) +            if lastProvisionUSNs is not None: +                update_provision_usn(ldbs.sam, minUSN, maxUSN, names.invocation) +            if opts.full and (names.policyid is None or names.policyid_dc is None): +                update_policyids(names, ldbs.sam) +        if opts.full or opts.resetfileacl or opts.fixntacl:              try:                  update_gpo(paths, ldbs.sam, names, lp, message, 1)              except ProvisioningError, e:                  message(ERROR, "The policy for domain controller is missing. " -                               "You should restart upgradeprovision with --full") +                            "You should restart upgradeprovision with --full")              except IOError, e:                  message(ERROR, "Setting ACL not supported on your filesystem")          else: @@ -1804,25 +1945,29 @@ if __name__ == '__main__':                  update_gpo(paths, ldbs.sam, names, lp, message, 0)              except ProvisioningError, e:                  message(ERROR, "The policy for domain controller is missing. " -                               "You should restart upgradeprovision with --full") -        ldbs.groupedCommit() -        new_ldbs.groupedCommit() -        message(SIMPLE, "Upgrade finished!") -        # remove reference provision now that everything is done ! -        # So we have reindexed first if need when the merged schema was reloaded -        # (as new attributes could have quick in) -        # But the second part of the update (when we update existing objects -        # can also have an influence on indexing as some attribute might have their -        # searchflag modificated -        message(SIMPLE, "Reopenning samdb to trigger reindexing if needed " -                        "after modification") -        samdb = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp) -        message(SIMPLE, "Reindexing finished") - -        shutil.rmtree(provisiondir) +                            "You should restart upgradeprovision with --full") +        if not opts.fixntacl: +            ldbs.groupedCommit() +            new_ldbs.groupedCommit() +            message(SIMPLE, "Upgrade finished!") +            # remove reference provision now that everything is done ! +            # So we have reindexed first if need when the merged schema was reloaded +            # (as new attributes could have quick in) +            # But the second part of the update (when we update existing objects +            # can also have an influence on indexing as some attribute might have their +            # searchflag modificated +            message(SIMPLE, "Reopenning samdb to trigger reindexing if needed " +                    "after modification") +            samdb = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp) +            message(SIMPLE, "Reindexing finished") + +            shutil.rmtree(provisiondir) +        else: +            ldbs.groupedRollback() +            message(SIMPLE, "ACLs fixed !")      except StandardError, err:          message(ERROR, "A problem occurred while trying to upgrade your " -                       "provision. A full backup is located at %s" % backupdir) +                   "provision. A full backup is located at %s" % backupdir)          if opts.debugall or opts.debugchange:              (typ, val, tb) = sys.exc_info()              traceback.print_exception(typ, val, tb) diff --git a/source4/scripting/bin/wscript_build b/source4/scripting/bin/wscript_build new file mode 100644 index 0000000000..e52b32bc02 --- /dev/null +++ b/source4/scripting/bin/wscript_build @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +bld.SAMBA_SCRIPT('samba_dnsupdate', pattern='samba_dnsupdate', installdir='.') +bld.SAMBA_SCRIPT('samba_spnupdate', pattern='samba_spnupdate', installdir='.') +bld.SAMBA_SCRIPT('upgradeprovision', pattern='upgradeprovision', installdir='.')  | 
