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(samba3, setup_dir, message, credentials, session_info, paths):
259 oldconf = samba3.get_conf()
261 if oldconf.get("domain logons") == "True":
262 serverrole = "domain controller"
264 if oldconf.get("security") == "user":
265 serverrole = "standalone"
267 serverrole = "member server"
269 domainname = oldconf.get("workgroup")
270 realm = oldconf.get("realm")
271 netbiosname = oldconf.get("netbios name")
273 secrets_db = samba3.get_secrets_db()
275 if domainname is None:
276 domainname = secrets_db.domains()[0]
277 message("No domain specified in smb.conf file, assuming '%s'" % domainname)
280 realm = domainname.lower()
281 message("No realm specified in smb.conf file, assuming '%s'\n" % realm)
283 domainguid = secrets_db.get_domain_guid(domainname)
284 domainsid = secrets_db.get_sid(domainsid)
285 if domainsid is None:
286 message("Can't find domain secrets for '%s'; using random SID\n" % domainname)
288 if netbiosname is not None:
289 machinepass = secrets_db.get_machine_password(netbiosname)
293 provision(lp, setup_dir, message, blank=True, paths=path, session_info=session_info,
294 credentials=credentials, realm=realm, domain=domainname,
295 domainsid=domainsid, domainguid=domainguid, machinepass=machinepass, serverrole=serverrole)
311 "bind interfaces only",
316 "obey pam restrictions",
324 "client NTLMv2 auth",
325 "client lanman auth",
326 "client plaintext auth",
346 "name resolve order",
355 "paranoid server security",
391 def upgrade_smbconf(oldconf,mark):
392 """Remove configuration variables not present in Samba4
394 :param oldconf: Old configuration structure
395 :param mark: Whether removed configuration variables should be
396 kept in the new configuration as "samba3:<name>"
398 data = oldconf.data()
399 newconf = param_init()
404 for k in smbconf_keep:
405 if smbconf_keep[k] == p:
410 newconf.set(s, p, oldconf.get(s, p))
412 newconf.set(s, "samba3:"+p, oldconf.get(s,p))
416 def upgrade(subobj, samba3, message, paths, session_info, credentials):
418 samdb = Ldb(paths.samdb, session_info=session_info, credentials=credentials)
420 message("Writing configuration")
421 newconf = upgrade_smbconf(samba3.configuration,True)
422 newconf.save(paths.smbconf)
424 message("Importing account policies")
425 samdb.modify_ldif(upgrade_sam_policy(samba3,subobj.BASEDN))
426 regdb = Ldb(paths.hklm)
429 dn: value=RefusePasswordChange,key=Parameters,key=Netlogon,key=Services,key=CurrentControlSet,key=System,HIVE=NONE
434 """ % policy.refuse_machine_password_change)
436 message("Importing users")
437 for account in samba3.samaccounts:
438 msg = "... " + account.username
439 ldif = upgrade_sam_account(samdb, accounts,subobj.BASEDN,subobj.DOMAINSID)
443 # FIXME: Ignore 'Record exists' errors
444 msg += "... error: " + str(e)
448 message("Importing groups")
449 for mapping in samba3.groupmappings:
450 msg = "... " + mapping.nt_name
451 ldif = upgrade_sam_group(mapping, subobj.BASEDN)
456 # FIXME: Ignore 'Record exists' errors
457 msg += "... error: " + str(e)
461 message("Importing registry data")
462 for hive in ["hkcr","hkcu","hklm","hkpd","hku","hkpt"]:
463 message("... " + hive)
464 regdb = Ldb(paths[hive])
465 ldif = upgrade_registry(samba3.registry, hive, regdb)
471 # FIXME: Ignore 'Record exists' errors
472 msg += "... error: " + str(e)
476 message("Importing WINS data")
477 winsdb = Ldb(paths.winsdb)
480 ldif = upgrade_wins(samba3)
483 # figure out ldapurl, if applicable
485 pdb = samba3.configuration.get_list("passdb backend")
488 if len(backend) >= 7 and backend[0:7] == "ldapsam":
489 ldapurl = backend[7:]
491 # URL was not specified in passdb backend but ldap /is/ used
493 ldapurl = "ldap://%s" % samba3.configuration.get("ldap server")
495 # Enable samba3sam module if original passdb backend was ldap
496 if ldapurl is not None:
497 message("Enabling Samba3 LDAP mappings for SAM database")
499 enable_samba3sam(samdb)
503 def upgrade_verify(subobj, samba3, paths, message):
504 message("Verifying account policies")
506 samldb = Ldb(paths.samdb)
508 for account in samba3.samaccounts:
509 msg = samldb.search("(&(sAMAccountName=" + account.nt_username + ")(objectclass=user))")
510 assert(len(msg) >= 1)
516 def enable_samba3sam(samdb):
521 @LIST: samldb,operational,objectguid,rdn_name,samba3sam
524 samdb.add({"dn": "@MAP=samba3sam", "@MAP_URL": ldapurl})