3 # backend code for upgrading from Samba3
4 # Copyright Jelmer Vernooij 2005-2007
5 # Released under the GNU GPL v3 or later
8 """Support code for upgrading from Samba 3 to Samba 4."""
10 from provision import findnss
16 def regkey_to_dn(name):
17 """Convert a registry key to a DN."""
23 for el in name.split("/"):
24 dn = "key=%s," % el + dn
28 # Where prefix is any of:
36 def upgrade_registry(regdb,prefix,ldb):
37 """Migrate registry contents."""
38 assert regdb is not None
39 prefix_up = prefix.upper()
43 pts = rk.name.split("/")
45 # Only handle selected hive
46 if pts[0].upper() != prefix_up:
49 keydn = regkey_to_dn(rk.name)
51 pts = rk.name.split("/")
53 # Convert key name to dn
61 ldif[rk.name + " (" + rv.name + ")"] = """
65 data:: %s""" % (keydn, rv.name, rv.name, rv.type, ldb.encode(rv.data))
69 def upgrade_sam_policy(samba3,dn):
79 samba3ResetCountMinutes: %d
80 samba3UserMustLogonToChangePassword: %d
81 samba3BadLockoutMinutes: %d
82 samba3DisconnectTime: %d
84 """ % (dn, samba3.policy.min_password_length,
85 samba3.policy.password_history, samba3.policy.minimum_password_age,
86 samba3.policy.maximum_password_age, samba3.policy.lockout_duration,
87 samba3.policy.reset_count_minutes, samba3.policy.user_must_logon_to_change_password,
88 samba3.policy.bad_lockout_minutes, samba3.policy.disconnect_time)
92 def upgrade_sam_account(ldb,acc,domaindn,domainsid):
93 """Upgrade a SAM account."""
94 if acc.nt_username is None or acc.nt_username == "":
95 acc.nt_username = acc.username
97 if acc.fullname is None:
98 acc.fullname = pwd.getpwnam(acc.fullname)[4]
100 acc.fullname = acc.fullname.split(",")[0]
102 if acc.fullname is None:
103 acc.fullname = acc.username
105 assert acc.fullname is not None
106 assert acc.nt_username is not None
108 ldif = """dn: cn=%s,%s
124 samba3LogonScript: %s
125 samba3ProfilePath: %s
126 samba3Workstations: %s
127 samba3KickOffTime: %d
129 samba3PassLastSetTime: %d
130 samba3PassCanChangeTime: %d
131 samba3PassMustChangeTime: %d
136 """ % (ldb.dn_escape(acc.fullname), domaindn, acc.logon_time, acc.logoff_time, acc.username, acc.nt_username, acc.nt_username,
137 acc.acct_desc, acc.group_rid, acc.bad_password_count, acc.logon_count,
138 acc.domain, acc.dir_drive, acc.munged_dial, acc.homedir, acc.logon_script,
139 acc.profile_path, acc.workstations, acc.kickoff_time, acc.bad_password_time,
140 acc.pass_last_set_time, acc.pass_can_change_time, acc.pass_must_change_time, domainsid, acc.user_rid,
141 ldb.encode(acc.lm_pw), ldb.encode(acc.nt_pw))
145 def upgrade_sam_group(group,domaindn):
146 """Upgrade a SAM group."""
147 if group.sid_name_use == 5: # Well-known group
150 if group.nt_name in ("Domain Guests", "Domain Users", "Domain Admins"):
154 gr = grp.getgrnam(grp.nt_name)
156 gr = grp.getgrgid(grp.gid)
159 group.unixname = "UNKNOWN"
161 group.unixname = gr.gr_name
163 assert group.unixname is not None
165 ldif = """dn: cn=%s,%s
173 """ % (group.nt_name, domaindn,
174 group.comment, group.nt_name, group.sid, group.unixname, group.sid_name_use)
178 def upgrade_winbind(samba3,domaindn):
185 """ % (samba3.idmap.user_hwm, samba3.idmap.group_hwm)
187 for m in samba3.idmap.mappings:
192 unixID: %d""" % (m.sid, domaindn, m.sid, m.type, m.unix_id)
196 def upgrade_wins(samba3):
197 """Upgrade the WINS database."""
201 for e in samba3.winsentries:
203 ttl = sys.unix2nttime(e.ttl)
223 rState = 0x0 # active
225 rState = 0x1 # released
227 nType = ((e.nb_flags & 0x60)>>5)
230 dn: name=%s,type=0x%02X
233 objectClass: winsRecord
240 """ % (e.name, e.type, e.type, e.name,
241 rType, rState, nType,
242 ldaptime(ttl), version_id)
245 ldif += "address: %s\n" % ip
249 objectClass: winsMaxVersion
255 def upgrade_provision(lp, samba3):
258 domainname = samba3.configuration.get("workgroup")
260 if domainname is None:
261 domainname = samba3.secrets.domains[0].name
262 print "No domain specified in smb.conf file, assuming '%s'\n" % domainname
264 domsec = samba3.find_domainsecrets(domainname)
265 hostsec = samba3.find_domainsecrets(hostname())
266 realm = samba3.configuration.get("realm")
270 print "No realm specified in smb.conf file, assuming '%s'\n" % realm
274 subobj.domain = domainname
275 subobj.hostname = hostname()
277 assert subobj.realm is not None
278 assert subobj.domain is not None
279 assert subobj.hostname is not None
281 subobj.HOSTIP = hostip()
282 if domsec is not None:
283 subobj.DOMAINGUID = domsec.guid
284 subobj.DOMAINSID = domsec.sid
286 print "Can't find domain secrets for '%s'; using random SID and GUID\n" % domainname
287 subobj.DOMAINGUID = uuid.random()
288 subobj.DOMAINSID = randsid()
291 subobj.HOSTGUID = hostsec.guid
293 subobj.HOSTGUID = uuid.random()
294 subobj.invocationid = uuid.random()
295 subobj.krbtgtpass = randpass(12)
296 subobj.machinepass = randpass(12)
297 subobj.adminpass = randpass(12)
298 subobj.datestring = datestring()
299 subobj.root = findnss(pwd.getpwnam, "root")[4]
300 subobj.nobody = findnss(pwd.getpwnam, "nobody")[4]
301 subobj.nogroup = findnss(grp.getgrnam, "nogroup", "nobody")[2]
302 subobj.wheel = findnss(grp.getgrnam, "wheel", "root")[2]
303 subobj.users = findnss(grp.getgrnam, "users", "guest", "other")[2]
304 subobj.dnsdomain = subobj.realm.lower()
305 subobj.dnsname = "%s.%s" % (subobj.hostname.lower(), subobj.dnsdomain)
306 subobj.basedn = "DC=" + ",DC=".join(subobj.realm.split("."))
307 rdn_list = subobj.dnsdomain.split(".")
308 subobj.domaindn = "DC=" + ",DC=".join(rdn_list)
309 subobj.domaindn_ldb = "users.ldb"
310 subobj.rootdn = subobj.domaindn
312 modules_list = ["rootdse",
325 subobj.modules_list = ",".join(modules_list)
343 "bind interfaces only",
348 "obey pam restrictions",
356 "client NTLMv2 auth",
357 "client lanman auth",
358 "client plaintext auth",
378 "name resolve order",
387 "paranoid server security",
423 def upgrade_smbconf(oldconf,mark):
424 """Remove configuration variables not present in Samba4
426 :param oldconf: Old configuration structure
427 :param mark: Whether removed configuration variables should be
428 kept in the new configuration as "samba3:<name>"
430 data = oldconf.data()
431 newconf = param_init()
436 for k in smbconf_keep:
437 if smbconf_keep[k] == p:
442 newconf.set(s, p, oldconf.get(s, p))
444 newconf.set(s, "samba3:"+p, oldconf.get(s,p))
446 if oldconf.get("domain logons") == "True":
447 newconf.set("server role", "domain controller")
449 if oldconf.get("security") == "user":
450 newconf.set("server role", "standalone")
452 newconf.set("server role", "member server")
456 def upgrade(subobj, samba3, message, paths, session_info, credentials):
459 samdb = Ldb(paths.samdb, session_info=session_info, credentials=credentials)
461 message("Writing configuration")
462 newconf = upgrade_smbconf(samba3.configuration,True)
463 newconf.save(paths.smbconf)
465 message("Importing account policies")
466 ldif = upgrade_sam_policy(samba3,subobj.BASEDN)
468 regdb = Ldb(paths.hklm)
471 dn: value=RefusePasswordChange,key=Parameters,key=Netlogon,key=Services,key=CurrentControlSet,key=System,HIVE=NONE
476 """ % samba3.policy.refuse_machine_password_change)
478 message("Importing users")
479 for account in samba3.samaccounts:
480 msg = "... " + account.username
481 ldif = upgrade_sam_account(samdb, accounts,subobj.BASEDN,subobj.DOMAINSID)
485 # FIXME: Ignore 'Record exists' errors
486 msg += "... error: " + str(e)
490 message("Importing groups")
491 for mapping in samba3.groupmappings:
492 msg = "... " + mapping.nt_name
493 ldif = upgrade_sam_group(mapping, subobj.BASEDN)
498 # FIXME: Ignore 'Record exists' errors
499 msg += "... error: " + str(e)
503 message("Importing registry data")
504 for hive in ["hkcr","hkcu","hklm","hkpd","hku","hkpt"]:
505 message("... " + hive)
506 regdb = Ldb(paths[hive])
507 ldif = upgrade_registry(samba3.registry, hive, regdb)
513 # FIXME: Ignore 'Record exists' errors
514 msg += "... error: " + str(e)
518 message("Importing WINS data")
519 winsdb = Ldb(paths.winsdb)
522 ldif = upgrade_wins(samba3)
525 # figure out ldapurl, if applicable
527 pdb = samba3.configuration.get_list("passdb backend")
530 if len(backend) >= 7 and backend[0:7] == "ldapsam":
531 ldapurl = backend[7:]
533 # URL was not specified in passdb backend but ldap /is/ used
535 ldapurl = "ldap://%s" % samba3.configuration.get("ldap server")
537 # Enable samba3sam module if original passdb backend was ldap
538 if ldapurl is not None:
539 message("Enabling Samba3 LDAP mappings for SAM database")
545 @LIST: samldb,operational,objectguid,rdn_name,samba3sam
550 @MAP_URL: %s""" % ldapurl)
554 def upgrade_verify(subobj, samba3, paths, message):
555 message("Verifying account policies")
557 samldb = Ldb(paths.samdb)
559 for account in samba3.samaccounts:
560 msg = samldb.search("(&(sAMAccountName=" + account.nt_username + ")(objectclass=user))")
561 assert(len(msg) >= 1)