1#!/usr/bin/python 2# 3# Raises domain and forest function levels 4# 5# Copyright Matthias Dieter Wallnoefer 2009 6# 7# This program is free software; you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation; either version 3 of the License, or 10# (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this program. If not, see <http://www.gnu.org/licenses/>. 19# 20 21import sys 22 23# Find right directory when running from source tree 24sys.path.insert(0, "bin/python") 25 26import samba.getopt as options 27import optparse 28import ldb 29 30from samba.auth import system_session 31from samba.samdb import SamDB 32from samba import DS_DOMAIN_FUNCTION_2000, DS_DOMAIN_FUNCTION_2003 33from samba import DS_DOMAIN_FUNCTION_2003_MIXED, DS_DOMAIN_FUNCTION_2008 34from samba import DS_DOMAIN_FUNCTION_2008_R2 35 36parser = optparse.OptionParser("domainlevel (show | raise <options>)") 37sambaopts = options.SambaOptions(parser) 38parser.add_option_group(sambaopts) 39parser.add_option_group(options.VersionOptions(parser)) 40credopts = options.CredentialsOptions(parser) 41parser.add_option_group(credopts) 42parser.add_option("-H", help="LDB URL for database or target server", type=str) 43parser.add_option("--quiet", help="Be quiet", action="store_true") 44parser.add_option("--forest", 45 help="The forest function level (2000 | 2003 | 2008 | 2008_R2). We don't support the 2003 with mixed domains (NT4 DC support) level.", type=str) 46parser.add_option("--domain", 47 help="The domain function level (2000 | 2003 | 2008 | 2008_R2). We don't support mixed/interim (NT4 DC support) levels.", type=str) 48opts, args = parser.parse_args() 49 50# 51# print a message if quiet is not set 52# 53def message(text): 54 if not opts.quiet: 55 print text 56 57if len(args) == 0: 58 parser.print_usage() 59 sys.exit(1) 60 61lp = sambaopts.get_loadparm() 62creds = credopts.get_credentials(lp) 63 64if opts.H is not None: 65 url = opts.H 66else: 67 url = lp.get("sam database") 68 69samdb = SamDB(url=url, session_info=system_session(), credentials=creds, lp=lp) 70 71domain_dn = SamDB.domain_dn(samdb) 72 73res_forest = samdb.search("CN=Partitions,CN=Configuration," + domain_dn, 74 scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"]) 75assert(len(res_forest) == 1) 76 77res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE, 78 attrs=["msDS-Behavior-Version", "nTMixedDomain"]) 79assert(len(res_domain) == 1) 80 81try: 82 level_forest = int(res_forest[0]["msDS-Behavior-Version"][0]) 83 level_domain = int(res_domain[0]["msDS-Behavior-Version"][0]) 84 level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0]) 85 86 if level_forest < 0 or level_domain < 0: 87 print "ERROR: Domain and/or forest functional level(s) is/are invalid. Correct them or reprovision!" 88 sys.exit(1) 89 if level_forest > level_domain: 90 print "ERROR: Forest function level is higher than the domain level(s). That can't be. Correct this or reprovision!" 91 sys.exit(1) 92except: 93 print "ERROR: Could not retrieve the actual domain and/or forest level!" 94 if args[0] == "show": 95 print "So the levels can't be displayed!" 96 sys.exit(1) 97 98if args[0] == "show": 99 message("Domain and forest function level for domain '" + domain_dn + "'") 100 if level_forest == DS_DOMAIN_FUNCTION_2003_MIXED: 101 message("\nATTENTION: You run SAMBA 4 on the 2003 with mixed domains (NT4 DC support) forest level. This isn't supported! Please raise!") 102 if (level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0) or level_domain == DS_DOMAIN_FUNCTION_2003_MIXED: 103 message("\nATTENTION: You run SAMBA 4 on a mixed/interim (NT4 DC support) domain level. This isn't supported! Please raise!") 104 105 message("") 106 107 if level_forest == DS_DOMAIN_FUNCTION_2000: 108 outstr = "2000" 109 elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED: 110 outstr = "2003 with mixed domains/interim (NT4 DC support)" 111 elif level_forest == DS_DOMAIN_FUNCTION_2003: 112 outstr = "2003" 113 elif level_forest == DS_DOMAIN_FUNCTION_2008: 114 outstr = "2008" 115 elif level_forest == DS_DOMAIN_FUNCTION_2008_R2: 116 outstr = "2008 R2" 117 else: 118 outstr = "higher than 2008 R2" 119 message("Forest function level: (Windows) " + outstr) 120 121 if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0: 122 outstr = "2000 mixed (NT4 DC support)" 123 elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0: 124 outstr = "2000" 125 elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED: 126 outstr = "2003 with mixed domains/interim (NT4 DC support)" 127 elif level_domain == DS_DOMAIN_FUNCTION_2003: 128 outstr = "2003" 129 elif level_domain == DS_DOMAIN_FUNCTION_2008: 130 outstr = "2008" 131 elif level_domain == DS_DOMAIN_FUNCTION_2008_R2: 132 outstr = "2008 R2" 133 else: 134 outstr = "higher than 2008 R2" 135 message("Domain function level: (Windows) " + outstr) 136 137elif args[0] == "raise": 138 msgs = [] 139 140 if opts.domain is not None: 141 arg = opts.domain 142 143 if arg == "2000": 144 new_level_domain = DS_DOMAIN_FUNCTION_2000 145 elif arg == "2003": 146 new_level_domain = DS_DOMAIN_FUNCTION_2003 147 elif arg == "2008": 148 new_level_domain = DS_DOMAIN_FUNCTION_2008 149 elif arg == "2008_R2": 150 new_level_domain = DS_DOMAIN_FUNCTION_2008_R2 151 else: 152 print "ERROR: Wrong argument '" + arg + "'!" 153 sys.exit(1) 154 155 if new_level_domain <= level_domain and level_domain_mixed == 0: 156 print "ERROR: Domain function level can't be smaller equal to the actual one!" 157 sys.exit(1) 158 159 # Deactivate mixed/interim domain support 160 if level_domain_mixed != 0: 161 m = ldb.Message() 162 m.dn = ldb.Dn(samdb, domain_dn) 163 m["nTMixedDomain"] = ldb.MessageElement("0", 164 ldb.FLAG_MOD_REPLACE, "nTMixedDomain") 165 samdb.modify(m) 166 167 m = ldb.Message() 168 m.dn = ldb.Dn(samdb, domain_dn) 169 m["msDS-Behavior-Version"]= ldb.MessageElement( 170 str(new_level_domain), ldb.FLAG_MOD_REPLACE, 171 "msDS-Behavior-Version") 172 samdb.modify(m) 173 174 level_domain = new_level_domain 175 176 msgs.append("Domain function level changed!") 177 178 if opts.forest is not None: 179 arg = opts.forest 180 181 if arg == "2000": 182 new_level_forest = DS_DOMAIN_FUNCTION_2000 183 elif arg == "2003": 184 new_level_forest = DS_DOMAIN_FUNCTION_2003 185 elif arg == "2008": 186 new_level_forest = DS_DOMAIN_FUNCTION_2008 187 elif arg == "2008_R2": 188 new_level_forest = DS_DOMAIN_FUNCTION_2008_R2 189 else: 190 print "ERROR: Wrong argument '" + arg + "'!" 191 sys.exit(1) 192 193 if new_level_forest <= level_forest: 194 print "ERROR: Forest function level can't be smaller equal to the actual one!" 195 sys.exit(1) 196 197 if new_level_forest > level_domain: 198 print "ERROR: Forest function level can't be higher than the domain function level(s). Please raise it/them first!" 199 sys.exit(1) 200 201 m = ldb.Message() 202 m.dn = ldb.Dn(samdb, "CN=Partitions,CN=Configuration," 203 + domain_dn) 204 m["msDS-Behavior-Version"]= ldb.MessageElement( 205 str(new_level_forest), ldb.FLAG_MOD_REPLACE, 206 "msDS-Behavior-Version") 207 samdb.modify(m) 208 209 msgs.append("Forest function level changed!") 210 211 msgs.append("All changes applied successfully!") 212 213 message("\n".join(msgs)) 214else: 215 print "ERROR: Wrong argument '" + args[0] + "'!" 216 sys.exit(1) 217