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, provision
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(policy,dn):
82 samba3ResetCountMinutes: %d
83 samba3UserMustLogonToChangePassword: %d
84 samba3BadLockoutMinutes: %d
85 samba3DisconnectTime: %d
87 """ % (dn, policy.min_password_length,
88 policy.password_history, policy.minimum_password_age,
89 policy.maximum_password_age, policy.lockout_duration,
90 policy.reset_count_minutes, policy.user_must_logon_to_change_password,
91 policy.bad_lockout_minutes, 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):
450 samdb = Ldb(paths.samdb, session_info=session_info, credentials=credentials)
452 message("Writing configuration")
453 newconf = upgrade_smbconf(samba3.configuration,True)
454 newconf.save(paths.smbconf)
456 message("Importing account policies")
457 samdb.modify_ldif(upgrade_sam_policy(samba3,subobj.BASEDN))
458 regdb = Ldb(paths.hklm)
461 dn: value=RefusePasswordChange,key=Parameters,key=Netlogon,key=Services,key=CurrentControlSet,key=System,HIVE=NONE
466 """ % policy.refuse_machine_password_change)
468 message("Importing users")
469 for account in samba3.samaccounts:
470 msg = "... " + account.username
471 ldif = upgrade_sam_account(samdb, accounts,subobj.BASEDN,subobj.DOMAINSID)
475 # FIXME: Ignore 'Record exists' errors
476 msg += "... error: " + str(e)
480 message("Importing groups")
481 for mapping in samba3.groupmappings:
482 msg = "... " + mapping.nt_name
483 ldif = upgrade_sam_group(mapping, subobj.BASEDN)
488 # FIXME: Ignore 'Record exists' errors
489 msg += "... error: " + str(e)
493 message("Importing registry data")
494 for hive in ["hkcr","hkcu","hklm","hkpd","hku","hkpt"]:
495 message("... " + hive)
496 regdb = Ldb(paths[hive])
497 ldif = upgrade_registry(samba3.registry, hive, regdb)
503 # FIXME: Ignore 'Record exists' errors
504 msg += "... error: " + str(e)
508 message("Importing WINS data")
509 winsdb = Ldb(paths.winsdb)
512 ldif = upgrade_wins(samba3)
515 # figure out ldapurl, if applicable
517 pdb = samba3.configuration.get_list("passdb backend")
520 if len(backend) >= 7 and backend[0:7] == "ldapsam":
521 ldapurl = backend[7:]
523 # URL was not specified in passdb backend but ldap /is/ used
525 ldapurl = "ldap://%s" % samba3.configuration.get("ldap server")
527 # Enable samba3sam module if original passdb backend was ldap
528 if ldapurl is not None:
529 message("Enabling Samba3 LDAP mappings for SAM database")
531 enable_samba3sam(samdb)
535 def upgrade_verify(subobj, samba3, paths, message):
536 message("Verifying account policies")
538 samldb = Ldb(paths.samdb)
540 for account in samba3.samaccounts:
541 msg = samldb.search("(&(sAMAccountName=" + account.nt_username + ")(objectclass=user))")
542 assert(len(msg) >= 1)
548 def enable_samba3sam(samdb):
553 @LIST: samldb,operational,objectguid,rdn_name,samba3sam
556 samdb.add({"dn": "@MAP=samba3sam", "@MAP_URL": ldapurl})