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 __docformat__ = "restructuredText"
17 from samba import Ldb, registry
18 from samba.param import LoadParm
19 from samba.provision import provision
21 def import_sam_policy(samldb, policy, dn):
22 """Import a Samba 3 policy database."""
23 samldb.modify_ldif("""
32 samba3ResetCountMinutes: %d
33 samba3UserMustLogonToChangePassword: %d
34 samba3BadLockoutMinutes: %d
35 samba3DisconnectTime: %d
37 """ % (dn, policy.min_password_length,
38 policy.password_history, policy.minimum_password_age,
39 policy.maximum_password_age, policy.lockout_duration,
40 policy.reset_count_minutes, policy.user_must_logon_to_change_password,
41 policy.bad_lockout_minutes, policy.disconnect_time))
44 def import_sam_account(samldb,acc,domaindn,domainsid):
45 """Import a Samba 3 SAM account.
47 :param samldb: Samba 4 SAM Database handle
48 :param acc: Samba 3 account
49 :param domaindn: Domain DN
50 :param domainsid: Domain SID."""
51 if acc.nt_username is None or acc.nt_username == "":
52 acc.nt_username = acc.username
54 if acc.fullname is None:
56 acc.fullname = pwd.getpwnam(acc.username)[4].split(",")[0]
60 if acc.fullname is None:
61 acc.fullname = acc.username
63 assert acc.fullname is not None
64 assert acc.nt_username is not None
67 "dn": "cn=%s,%s" % (acc.fullname, domaindn),
68 "objectClass": ["top", "user"],
69 "lastLogon": str(acc.logon_time),
70 "lastLogoff": str(acc.logoff_time),
71 "unixName": acc.username,
72 "sAMAccountName": acc.nt_username,
73 "cn": acc.nt_username,
74 "description": acc.acct_desc,
75 "primaryGroupID": str(acc.group_rid),
76 "badPwdcount": str(acc.bad_password_count),
77 "logonCount": str(acc.logon_count),
78 "samba3Domain": acc.domain,
79 "samba3DirDrive": acc.dir_drive,
80 "samba3MungedDial": acc.munged_dial,
81 "samba3Homedir": acc.homedir,
82 "samba3LogonScript": acc.logon_script,
83 "samba3ProfilePath": acc.profile_path,
84 "samba3Workstations": acc.workstations,
85 "samba3KickOffTime": str(acc.kickoff_time),
86 "samba3BadPwdTime": str(acc.bad_password_time),
87 "samba3PassLastSetTime": str(acc.pass_last_set_time),
88 "samba3PassCanChangeTime": str(acc.pass_can_change_time),
89 "samba3PassMustChangeTime": str(acc.pass_must_change_time),
90 "objectSid": "%s-%d" % (domainsid, acc.user_rid),
91 "lmPwdHash:": acc.lm_password,
92 "ntPwdHash:": acc.nt_password,
96 def import_sam_group(samldb, sid, gid, sid_name_use, nt_name, comment, domaindn):
97 """Upgrade a SAM group.
99 :param samldb: SAM database.
100 :param gid: Group GID
101 :param sid_name_use: SID name use
102 :param nt_name: NT Group Name
103 :param comment: NT Group Comment
104 :param domaindn: Domain DN
107 if sid_name_use == 5: # Well-known group
110 if nt_name in ("Domain Guests", "Domain Users", "Domain Admins"):
114 gr = grp.getgrnam(nt_name)
116 gr = grp.getgrgid(gid)
121 unixname = gr.gr_name
123 assert unixname is not None
126 "dn": "cn=%s,%s" % (nt_name, domaindn),
127 "objectClass": ["top", "group"],
128 "description": comment,
131 "unixName": unixname,
132 "samba3SidNameUse": str(sid_name_use)
136 def import_idmap(samdb,samba3_idmap,domaindn):
137 """Import idmap data.
139 :param samdb: SamDB handle.
140 :param samba3_idmap: Samba 3 IDMAP database to import from
141 :param domaindn: Domain DN.
145 "userHwm": str(samba3_idmap.get_user_hwm()),
146 "groupHwm": str(samba3_idmap.get_group_hwm())})
148 for uid in samba3_idmap.uids():
149 samdb.add({"dn": "SID=%s,%s" % (samba3_idmap.get_user_sid(uid), domaindn),
150 "SID": samba3_idmap.get_user_sid(uid),
154 for gid in samba3_idmap.uids():
155 samdb.add({"dn": "SID=%s,%s" % (samba3_idmap.get_group_sid(gid), domaindn),
156 "SID": samba3_idmap.get_group_sid(gid),
161 def import_wins(samba4_winsdb, samba3_winsdb):
162 """Import settings from a Samba3 WINS database.
164 :param samba4_winsdb: WINS database to import to
165 :param samba3_winsdb: WINS database to import from
169 for (name, (ttl, ips, nb_flags)) in samba3_winsdb.items():
172 type = int(name.split("#", 1)[1], 16)
187 if ttl > time.time():
188 rState = 0x0 # active
190 rState = 0x1 # released
192 nType = ((nb_flags & 0x60)>>5)
194 samba4_winsdb.add({"dn": "name=%s,type=0x%s" % tuple(name.split("#")),
195 "type": name.split("#")[1],
196 "name": name.split("#")[0],
197 "objectClass": "winsRecord",
198 "recordType": str(rType),
199 "recordState": str(rState),
200 "nodeType": str(nType),
201 "expireTime": ldb.timestring(ttl),
203 "versionID": str(version_id),
206 samba4_winsdb.add({"dn": "cn=VERSION",
208 "objectClass": "winsMaxVersion",
209 "maxVersion": str(version_id)})
211 def enable_samba3sam(samdb, ldapurl):
212 """Enable Samba 3 LDAP URL database.
214 :param samdb: SAM Database.
215 :param ldapurl: Samba 3 LDAP URL
217 samdb.modify_ldif("""
221 @LIST: samldb,operational,objectguid,rdn_name,samba3sam
224 samdb.add({"dn": "@MAP=samba3sam", "@MAP_URL": ldapurl})
241 "bind interfaces only",
246 "obey pam restrictions",
254 "client NTLMv2 auth",
255 "client lanman auth",
256 "client plaintext auth",
276 "name resolve order",
285 "paranoid server security",
321 def upgrade_smbconf(oldconf,mark):
322 """Remove configuration variables not present in Samba4
324 :param oldconf: Old configuration structure
325 :param mark: Whether removed configuration variables should be
326 kept in the new configuration as "samba3:<name>"
328 data = oldconf.data()
334 for k in smbconf_keep:
335 if smbconf_keep[k] == p:
340 newconf.set(s, p, oldconf.get(s, p))
342 newconf.set(s, "samba3:"+p, oldconf.get(s,p))
346 SAMBA3_PREDEF_NAMES = {
347 'HKLM': registry.HKEY_LOCAL_MACHINE,
350 def import_registry(samba4_registry, samba3_regdb):
351 """Import a Samba 3 registry database into the Samba 4 registry.
353 :param samba4_registry: Samba 4 registry handle.
354 :param samba3_regdb: Samba 3 registry database handle.
356 def ensure_key_exists(keypath):
357 (predef_name, keypath) = keypath.split("/", 1)
358 predef_id = SAMBA3_PREDEF_NAMES[predef_name]
359 keypath = keypath.replace("/", "\\")
360 return samba4_registry.create_key(predef_id, keypath)
362 for key in samba3_regdb.keys():
363 key_handle = ensure_key_exists(key)
364 for subkey in samba3_regdb.subkeys(key):
365 ensure_key_exists(subkey)
366 for (value_name, (value_type, value_data)) in samba3_regdb.values(key).items():
367 key_handle.set_value(value_name, value_type, value_data)
370 def upgrade_provision(samba3, setup_dir, logger, credentials, session_info,
372 oldconf = samba3.get_conf()
374 if oldconf.get("domain logons") == "True":
375 serverrole = "domain controller"
377 if oldconf.get("security") == "user":
378 serverrole = "standalone"
380 serverrole = "member server"
382 domainname = oldconf.get("workgroup")
383 realm = oldconf.get("realm")
384 netbiosname = oldconf.get("netbios name")
386 secrets_db = samba3.get_secrets_db()
388 if domainname is None:
389 domainname = secrets_db.domains()[0]
390 logger.warning("No domain specified in smb.conf file, assuming '%s'",
394 if oldconf.get("domain logons") == "True":
395 logger.warning("No realm specified in smb.conf file and being a DC. That upgrade path doesn't work! Please add a 'realm' directive to your old smb.conf to let us know which one you want to use (generally it's the upcased DNS domainname).")
398 realm = domainname.upper()
399 logger.warning("No realm specified in smb.conf file, assuming '%s'",
402 domainguid = secrets_db.get_domain_guid(domainname)
403 domainsid = secrets_db.get_sid(domainname)
404 if domainsid is None:
405 logger.warning("Can't find domain secrets for '%s'; using random SID",
408 if netbiosname is not None:
409 machinepass = secrets_db.get_machine_password(netbiosname)
413 result = provision(setup_dir=setup_dir, logger=logger,
414 session_info=session_info, credentials=credentials,
415 targetdir=targetdir, realm=realm, domain=domainname,
416 domainguid=domainguid, domainsid=domainsid,
417 hostname=netbiosname, machinepass=machinepass,
418 serverrole=serverrole)
420 import_wins(Ldb(result.paths.winsdb), samba3.get_wins_db())
422 # FIXME: import_registry(registry.Registry(), samba3.get_registry())
424 # FIXME: import_idmap(samdb,samba3.get_idmap_db(),domaindn)
426 groupdb = samba3.get_groupmapping_db()
427 for sid in groupdb.groupsids():
428 (gid, sid_name_use, nt_name, comment) = groupdb.get_group(sid)
429 # FIXME: import_sam_group(samdb, sid, gid, sid_name_use, nt_name, comment, domaindn)
433 passdb = samba3.get_sam_db()
436 #FIXME: import_sam_account(result.samdb, user, domaindn, domainsid)
438 if hasattr(passdb, 'ldap_url'):
439 logger.info("Enabling Samba3 LDAP mappings for SAM database")
441 enable_samba3sam(result.samdb, passdb.ldap_url)