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 # Where prefix is any of:
24 def upgrade_sam_policy(policy,dn):
34 samba3ResetCountMinutes: %d
35 samba3UserMustLogonToChangePassword: %d
36 samba3BadLockoutMinutes: %d
37 samba3DisconnectTime: %d
39 """ % (dn, policy.min_password_length,
40 policy.password_history, policy.minimum_password_age,
41 policy.maximum_password_age, policy.lockout_duration,
42 policy.reset_count_minutes, policy.user_must_logon_to_change_password,
43 policy.bad_lockout_minutes, policy.disconnect_time)
47 def upgrade_sam_account(ldb,acc,domaindn,domainsid):
48 """Upgrade a SAM account."""
49 if acc.nt_username is None or acc.nt_username == "":
50 acc.nt_username = acc.username
52 if acc.fullname is None:
53 acc.fullname = pwd.getpwnam(acc.fullname)[4]
55 acc.fullname = acc.fullname.split(",")[0]
57 if acc.fullname is None:
58 acc.fullname = acc.username
60 assert acc.fullname is not None
61 assert acc.nt_username is not None
63 ldif = """dn: cn=%s,%s
81 samba3Workstations: %s
84 samba3PassLastSetTime: %d
85 samba3PassCanChangeTime: %d
86 samba3PassMustChangeTime: %d
91 """ % (ldb.dn_escape(acc.fullname), domaindn, acc.logon_time, acc.logoff_time, acc.username, acc.nt_username, acc.nt_username,
92 acc.acct_desc, acc.group_rid, acc.bad_password_count, acc.logon_count,
93 acc.domain, acc.dir_drive, acc.munged_dial, acc.homedir, acc.logon_script,
94 acc.profile_path, acc.workstations, acc.kickoff_time, acc.bad_password_time,
95 acc.pass_last_set_time, acc.pass_can_change_time, acc.pass_must_change_time, domainsid, acc.user_rid,
96 ldb.encode(acc.lm_pw), ldb.encode(acc.nt_pw))
100 def upgrade_sam_group(group,domaindn):
101 """Upgrade a SAM group."""
102 if group.sid_name_use == 5: # Well-known group
105 if group.nt_name in ("Domain Guests", "Domain Users", "Domain Admins"):
109 gr = grp.getgrnam(grp.nt_name)
111 gr = grp.getgrgid(grp.gid)
114 group.unixname = "UNKNOWN"
116 group.unixname = gr.gr_name
118 assert group.unixname is not None
120 ldif = """dn: cn=%s,%s
128 """ % (group.nt_name, domaindn,
129 group.comment, group.nt_name, group.sid, group.unixname, group.sid_name_use)
133 def import_idmap(samba4_idmap,samba3_idmap,domaindn):
136 "userHwm": str(samba3_idmap.get_user_hwm()),
137 "groupHwm": str(samba3_idmap.get_group_hwm())})
139 for uid in samba3_idmap.uids():
140 samba4_idmap.add({"dn": "SID=%s,%s" % (samba3_idmap.get_user_sid(uid), domaindn),
141 "SID": samba3_idmap.get_user_sid(uid),
145 for gid in samba3_idmap.uids():
146 samba4_idmap.add({"dn": "SID=%s,%s" % (samba3_idmap.get_group_sid(gid), domaindn),
147 "SID": samba3_idmap.get_group_sid(gid),
152 def import_wins(samba4_winsdb, samba3_winsdb):
153 """Import settings from a Samba3 WINS database."""
157 for (name, (ttl, ips, nb_flags)) in samba3_winsdb.items():
162 type = int(name.split("#", 1)[1], 16)
177 if ttl > time.time():
178 rState = 0x0 # active
180 rState = 0x1 # released
182 nType = ((nb_flags & 0x60)>>5)
184 samba4_winsdb.add({"dn": "name=%s,type=0x%s" % name.split("#"),
185 "type": name.split("#")[1],
186 "name": name.split("#")[0],
187 "objectClass": "winsRecord",
188 "recordType": str(rType),
189 "recordState": str(rState),
190 "nodeType": str(nType),
191 "expireTime": ldb.ldaptime(ttl),
193 "versionID": str(version_id),
196 samba4_winsdb.add({"dn": "CN=VERSION",
197 "objectClass": "winsMaxVersion",
198 "maxVersion": str(version_id)})
200 def upgrade_provision(samba3, setup_dir, message, credentials, session_info, lp, paths):
201 oldconf = samba3.get_conf()
203 if oldconf.get("domain logons") == "True":
204 serverrole = "domain controller"
206 if oldconf.get("security") == "user":
207 serverrole = "standalone"
209 serverrole = "member server"
211 lp.set("server role", serverrole)
212 domainname = oldconf.get("workgroup")
214 domainname = str(domainname)
215 lp.set("workgroup", domainname)
216 realm = oldconf.get("realm")
217 netbiosname = oldconf.get("netbios name")
219 secrets_db = samba3.get_secrets_db()
221 if domainname is None:
222 domainname = secrets_db.domains()[0]
223 message("No domain specified in smb.conf file, assuming '%s'" % domainname)
226 realm = domainname.lower()
227 message("No realm specified in smb.conf file, assuming '%s'\n" % realm)
228 lp.set("realm", realm)
230 domainguid = secrets_db.get_domain_guid(domainname)
231 domainsid = secrets_db.get_sid(domainname)
232 if domainsid is None:
233 message("Can't find domain secrets for '%s'; using random SID\n" % domainname)
235 if netbiosname is not None:
236 machinepass = secrets_db.get_machine_password(netbiosname)
240 provision(lp=lp, setup_dir=setup_dir, message=message, blank=True, ldapbackend=None, paths=paths, session_info=session_info,
241 credentials=credentials, realm=realm, domain=domainname,
242 domainsid=domainsid, domainguid=domainguid, machinepass=machinepass, serverrole=serverrole)
258 "bind interfaces only",
263 "obey pam restrictions",
271 "client NTLMv2 auth",
272 "client lanman auth",
273 "client plaintext auth",
293 "name resolve order",
302 "paranoid server security",
338 def upgrade_smbconf(oldconf,mark):
339 """Remove configuration variables not present in Samba4
341 :param oldconf: Old configuration structure
342 :param mark: Whether removed configuration variables should be
343 kept in the new configuration as "samba3:<name>"
345 data = oldconf.data()
346 newconf = param_init()
351 for k in smbconf_keep:
352 if smbconf_keep[k] == p:
357 newconf.set(s, p, oldconf.get(s, p))
359 newconf.set(s, "samba3:"+p, oldconf.get(s,p))
363 SAMBA3_PREDEF_NAMES = {
364 'HKLM': registry.HKEY_LOCAL_MACHINE,
367 def import_registry(samba4_registry, samba3_regdb):
368 """Import a Samba 3 registry database into the Samba 4 registry.
370 :param samba4_registry: Samba 4 registry handle.
371 :param samba3_regdb: Samba 3 registry database handle.
373 def ensure_key_exists(keypath):
374 (predef_name, keypath) = keypath.split("/", 1)
375 predef_id = SAMBA3_PREDEF_NAMES[predef_name]
376 keypath = keypath.replace("/", "\\")
377 return samba4_registry.create_key(predef_id, keypath)
379 for key in samba3_regdb.keys():
380 key_handle = ensure_key_exists(key)
381 for subkey in samba3_regdb.subkeys(key):
382 ensure_key_exists(subkey)
383 for (value_name, (value_type, value_data)) in samba3_regdb.values(key).items():
384 key_handle.set_value(value_name, value_type, value_data)
387 def upgrade(subobj, samba3, message, paths, session_info, credentials):
389 samdb = Ldb(paths.samdb, session_info=session_info, credentials=credentials)
391 message("Writing configuration")
392 newconf = upgrade_smbconf(samba3.configuration,True)
393 newconf.save(paths.smbconf)
395 message("Importing account policies")
396 samdb.modify_ldif(upgrade_sam_policy(samba3,subobj.BASEDN))
397 regdb = Ldb(paths.hklm)
400 dn: value=RefusePasswordChange,key=Parameters,key=Netlogon,key=Services,key=CurrentControlSet,key=System,HIVE=NONE
405 """ % policy.refuse_machine_password_change)
407 message("Importing users")
408 for account in samba3.samaccounts:
409 msg = "... " + account.username
410 ldif = upgrade_sam_account(samdb, accounts,subobj.BASEDN,subobj.DOMAINSID)
414 # FIXME: Ignore 'Record exists' errors
415 msg += "... error: " + str(e)
419 message("Importing groups")
420 for mapping in samba3.groupmappings:
421 msg = "... " + mapping.nt_name
422 ldif = upgrade_sam_group(mapping, subobj.BASEDN)
427 # FIXME: Ignore 'Record exists' errors
428 msg += "... error: " + str(e)
432 message("Importing WINS data")
433 winsdb = Ldb(paths.winsdb)
436 ldif = upgrade_wins(samba3)
439 # figure out ldapurl, if applicable
441 pdb = samba3.configuration.get_list("passdb backend")
444 if len(backend) >= 7 and backend[0:7] == "ldapsam":
445 ldapurl = backend[7:]
447 # URL was not specified in passdb backend but ldap /is/ used
449 ldapurl = "ldap://%s" % samba3.configuration.get("ldap server")
451 # Enable samba3sam module if original passdb backend was ldap
452 if ldapurl is not None:
453 message("Enabling Samba3 LDAP mappings for SAM database")
455 enable_samba3sam(samdb)
460 def enable_samba3sam(samdb):
465 @LIST: samldb,operational,objectguid,rdn_name,samba3sam
468 samdb.add({"dn": "@MAP=samba3sam", "@MAP_URL": ldapurl})