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.
19 :name: The registry key name.
20 :return: A matching DN."""
26 for el in name.split("/"):
27 dn = "key=%s," % el + dn
31 # Where prefix is any of:
39 def upgrade_registry(regdb,prefix,ldb):
40 """Migrate registry contents."""
41 assert regdb is not None
42 prefix_up = prefix.upper()
46 pts = rk.name.split("/")
48 # Only handle selected hive
49 if pts[0].upper() != prefix_up:
52 keydn = regkey_to_dn(rk.name)
54 pts = rk.name.split("/")
56 # Convert key name to dn
64 ldif[rk.name + " (" + rv.name + ")"] = """
68 data:: %s""" % (keydn, rv.name, rv.name, rv.type, ldb.encode(rv.data))
72 def upgrade_sam_policy(samba3,dn):
82 samba3ResetCountMinutes: %d
83 samba3UserMustLogonToChangePassword: %d
84 samba3BadLockoutMinutes: %d
85 samba3DisconnectTime: %d
87 """ % (dn, samba3.policy.min_password_length,
88 samba3.policy.password_history, samba3.policy.minimum_password_age,
89 samba3.policy.maximum_password_age, samba3.policy.lockout_duration,
90 samba3.policy.reset_count_minutes, samba3.policy.user_must_logon_to_change_password,
91 samba3.policy.bad_lockout_minutes, samba3.policy.disconnect_time)
95 def upgrade_sam_account(ldb,acc,domaindn,domainsid):
96 """Upgrade a SAM account."""
97 if acc.nt_username is None or acc.nt_username == "":
98 acc.nt_username = acc.username
100 if acc.fullname is None:
101 acc.fullname = pwd.getpwnam(acc.fullname)[4]
103 acc.fullname = acc.fullname.split(",")[0]
105 if acc.fullname is None:
106 acc.fullname = acc.username
108 assert acc.fullname is not None
109 assert acc.nt_username is not None
111 ldif = """dn: cn=%s,%s
127 samba3LogonScript: %s
128 samba3ProfilePath: %s
129 samba3Workstations: %s
130 samba3KickOffTime: %d
132 samba3PassLastSetTime: %d
133 samba3PassCanChangeTime: %d
134 samba3PassMustChangeTime: %d
139 """ % (ldb.dn_escape(acc.fullname), domaindn, acc.logon_time, acc.logoff_time, acc.username, acc.nt_username, acc.nt_username,
140 acc.acct_desc, acc.group_rid, acc.bad_password_count, acc.logon_count,
141 acc.domain, acc.dir_drive, acc.munged_dial, acc.homedir, acc.logon_script,
142 acc.profile_path, acc.workstations, acc.kickoff_time, acc.bad_password_time,
143 acc.pass_last_set_time, acc.pass_can_change_time, acc.pass_must_change_time, domainsid, acc.user_rid,
144 ldb.encode(acc.lm_pw), ldb.encode(acc.nt_pw))
148 def upgrade_sam_group(group,domaindn):
149 """Upgrade a SAM group."""
150 if group.sid_name_use == 5: # Well-known group
153 if group.nt_name in ("Domain Guests", "Domain Users", "Domain Admins"):
157 gr = grp.getgrnam(grp.nt_name)
159 gr = grp.getgrgid(grp.gid)
162 group.unixname = "UNKNOWN"
164 group.unixname = gr.gr_name
166 assert group.unixname is not None
168 ldif = """dn: cn=%s,%s
176 """ % (group.nt_name, domaindn,
177 group.comment, group.nt_name, group.sid, group.unixname, group.sid_name_use)
181 def upgrade_winbind(samba3,domaindn):
188 """ % (samba3.idmap.user_hwm, samba3.idmap.group_hwm)
190 for m in samba3.idmap.mappings:
195 unixID: %d""" % (m.sid, domaindn, m.sid, m.type, m.unix_id)
199 def upgrade_wins(samba3):
200 """Upgrade the WINS database."""
204 for e in samba3.winsentries:
206 ttl = sys.unix2nttime(e.ttl)
226 rState = 0x0 # active
228 rState = 0x1 # released
230 nType = ((e.nb_flags & 0x60)>>5)
233 dn: name=%s,type=0x%02X
236 objectClass: winsRecord
243 """ % (e.name, e.type, e.type, e.name,
244 rType, rState, nType,
245 ldaptime(ttl), version_id)
248 ldif += "address: %s\n" % ip
252 objectClass: winsMaxVersion
258 def upgrade_provision(lp, samba3):
259 domainname = samba3.configuration.get("workgroup")
261 if domainname is None:
262 domainname = samba3.secrets.domains[0].name
263 print "No domain specified in smb.conf file, assuming '%s'\n" % domainname
265 domsec = samba3.find_domainsecrets(domainname)
266 hostsec = samba3.find_domainsecrets(hostname())
267 realm = samba3.configuration.get("realm")
271 print "No realm specified in smb.conf file, assuming '%s'\n" % realm
275 subobj.domain = domainname
277 if domsec is not None:
278 subobj.DOMAINGUID = domsec.guid
279 subobj.DOMAINSID = domsec.sid
281 print "Can't find domain secrets for '%s'; using random SID and GUID\n" % domainname
282 subobj.DOMAINGUID = uuid.random()
283 subobj.DOMAINSID = randsid()
286 hostguid = hostsec.guid
287 subobj.krbtgtpass = randpass(12)
288 subobj.machinepass = randpass(12)
289 subobj.adminpass = randpass(12)
290 subobj.datestring = datestring()
291 subobj.root = findnss(pwd.getpwnam, "root")[4]
292 subobj.nobody = findnss(pwd.getpwnam, "nobody")[4]
293 subobj.nogroup = findnss(grp.getgrnam, "nogroup", "nobody")[2]
294 subobj.wheel = findnss(grp.getgrnam, "wheel", "root")[2]
295 subobj.users = findnss(grp.getgrnam, "users", "guest", "other")[2]
296 subobj.dnsdomain = subobj.realm.lower()
297 subobj.dnsname = "%s.%s" % (subobj.hostname.lower(), subobj.dnsdomain)
298 subobj.basedn = "DC=" + ",DC=".join(subobj.realm.split("."))
299 rdn_list = subobj.dnsdomain.split(".")
300 subobj.domaindn = "DC=" + ",DC=".join(rdn_list)
301 subobj.domaindn_ldb = "users.ldb"
302 subobj.rootdn = subobj.domaindn
304 modules_list = ["rootdse",
317 subobj.modules_list = ",".join(modules_list)
335 "bind interfaces only",
340 "obey pam restrictions",
348 "client NTLMv2 auth",
349 "client lanman auth",
350 "client plaintext auth",
370 "name resolve order",
379 "paranoid server security",
415 def upgrade_smbconf(oldconf,mark):
416 """Remove configuration variables not present in Samba4
418 :param oldconf: Old configuration structure
419 :param mark: Whether removed configuration variables should be
420 kept in the new configuration as "samba3:<name>"
422 data = oldconf.data()
423 newconf = param_init()
428 for k in smbconf_keep:
429 if smbconf_keep[k] == p:
434 newconf.set(s, p, oldconf.get(s, p))
436 newconf.set(s, "samba3:"+p, oldconf.get(s,p))
438 if oldconf.get("domain logons") == "True":
439 newconf.set("server role", "domain controller")
441 if oldconf.get("security") == "user":
442 newconf.set("server role", "standalone")
444 newconf.set("server role", "member server")
448 def upgrade(subobj, samba3, message, paths, session_info, credentials):
451 samdb = Ldb(paths.samdb, session_info=session_info, credentials=credentials)
453 message("Writing configuration")
454 newconf = upgrade_smbconf(samba3.configuration,True)
455 newconf.save(paths.smbconf)
457 message("Importing account policies")
458 ldif = upgrade_sam_policy(samba3,subobj.BASEDN)
460 regdb = Ldb(paths.hklm)
463 dn: value=RefusePasswordChange,key=Parameters,key=Netlogon,key=Services,key=CurrentControlSet,key=System,HIVE=NONE
468 """ % samba3.policy.refuse_machine_password_change)
470 message("Importing users")
471 for account in samba3.samaccounts:
472 msg = "... " + account.username
473 ldif = upgrade_sam_account(samdb, accounts,subobj.BASEDN,subobj.DOMAINSID)
477 # FIXME: Ignore 'Record exists' errors
478 msg += "... error: " + str(e)
482 message("Importing groups")
483 for mapping in samba3.groupmappings:
484 msg = "... " + mapping.nt_name
485 ldif = upgrade_sam_group(mapping, subobj.BASEDN)
490 # FIXME: Ignore 'Record exists' errors
491 msg += "... error: " + str(e)
495 message("Importing registry data")
496 for hive in ["hkcr","hkcu","hklm","hkpd","hku","hkpt"]:
497 message("... " + hive)
498 regdb = Ldb(paths[hive])
499 ldif = upgrade_registry(samba3.registry, hive, regdb)
505 # FIXME: Ignore 'Record exists' errors
506 msg += "... error: " + str(e)
510 message("Importing WINS data")
511 winsdb = Ldb(paths.winsdb)
514 ldif = upgrade_wins(samba3)
517 # figure out ldapurl, if applicable
519 pdb = samba3.configuration.get_list("passdb backend")
522 if len(backend) >= 7 and backend[0:7] == "ldapsam":
523 ldapurl = backend[7:]
525 # URL was not specified in passdb backend but ldap /is/ used
527 ldapurl = "ldap://%s" % samba3.configuration.get("ldap server")
529 # Enable samba3sam module if original passdb backend was ldap
530 if ldapurl is not None:
531 message("Enabling Samba3 LDAP mappings for SAM database")
537 @LIST: samldb,operational,objectguid,rdn_name,samba3sam
542 @MAP_URL: %s""" % ldapurl)
546 def upgrade_verify(subobj, samba3, paths, message):
547 message("Verifying account policies")
549 samldb = Ldb(paths.samdb)
551 for account in samba3.samaccounts:
552 msg = samldb.search("(&(sAMAccountName=" + account.nt_username + ")(objectclass=user))")
553 assert(len(msg) >= 1)