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"
12 from provision import provision, FILL_DRS
19 from samba.param import LoadParm
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 upgrade_provision(samba3, setup_dir, message, credentials, session_info, smbconf, targetdir):
212 oldconf = samba3.get_conf()
214 if oldconf.get("domain logons") == "True":
215 serverrole = "domain controller"
217 if oldconf.get("security") == "user":
218 serverrole = "standalone"
220 serverrole = "member server"
222 domainname = oldconf.get("workgroup")
224 domainname = str(domainname)
225 realm = oldconf.get("realm")
226 netbiosname = oldconf.get("netbios name")
228 secrets_db = samba3.get_secrets_db()
230 if domainname is None:
231 domainname = secrets_db.domains()[0]
232 message("No domain specified in smb.conf file, assuming '%s'" % domainname)
235 realm = domainname.lower()
236 message("No realm specified in smb.conf file, assuming '%s'\n" % realm)
238 domainguid = secrets_db.get_domain_guid(domainname)
239 domainsid = secrets_db.get_sid(domainname)
240 if domainsid is None:
241 message("Can't find domain secrets for '%s'; using random SID\n" % domainname)
243 if netbiosname is not None:
244 machinepass = secrets_db.get_machine_password(netbiosname)
248 result = provision(setup_dir=setup_dir, message=message,
249 samdb_fill=FILL_DRS, smbconf=smbconf, session_info=session_info,
250 credentials=credentials, realm=realm,
251 domain=domainname, domainsid=domainsid, domainguid=domainguid,
252 machinepass=machinepass, serverrole=serverrole, targetdir=targetdir)
254 import_wins(Ldb(result.paths.winsdb), samba3.get_wins_db())
256 # FIXME: import_registry(registry.Registry(), samba3.get_registry())
258 # FIXME: import_idmap(samdb,samba3.get_idmap_db(),domaindn)
260 groupdb = samba3.get_groupmapping_db()
261 for sid in groupdb.groupsids():
262 (gid, sid_name_use, nt_name, comment) = groupdb.get_group(sid)
263 # FIXME: import_sam_group(samdb, sid, gid, sid_name_use, nt_name, comment, domaindn)
267 passdb = samba3.get_sam_db()
270 #FIXME: import_sam_account(result.samdb, user, domaindn, domainsid)
272 if hasattr(passdb, 'ldap_url'):
273 message("Enabling Samba3 LDAP mappings for SAM database")
275 enable_samba3sam(result.samdb, passdb.ldap_url)
278 def enable_samba3sam(samdb, ldapurl):
279 """Enable Samba 3 LDAP URL database.
281 :param samdb: SAM Database.
282 :param ldapurl: Samba 3 LDAP URL
284 samdb.modify_ldif("""
288 @LIST: samldb,operational,objectguid,rdn_name,samba3sam
291 samdb.add({"dn": "@MAP=samba3sam", "@MAP_URL": ldapurl})
308 "bind interfaces only",
313 "obey pam restrictions",
321 "client NTLMv2 auth",
322 "client lanman auth",
323 "client plaintext auth",
343 "name resolve order",
352 "paranoid server security",
388 def upgrade_smbconf(oldconf,mark):
389 """Remove configuration variables not present in Samba4
391 :param oldconf: Old configuration structure
392 :param mark: Whether removed configuration variables should be
393 kept in the new configuration as "samba3:<name>"
395 data = oldconf.data()
401 for k in smbconf_keep:
402 if smbconf_keep[k] == p:
407 newconf.set(s, p, oldconf.get(s, p))
409 newconf.set(s, "samba3:"+p, oldconf.get(s,p))
413 SAMBA3_PREDEF_NAMES = {
414 'HKLM': registry.HKEY_LOCAL_MACHINE,
417 def import_registry(samba4_registry, samba3_regdb):
418 """Import a Samba 3 registry database into the Samba 4 registry.
420 :param samba4_registry: Samba 4 registry handle.
421 :param samba3_regdb: Samba 3 registry database handle.
423 def ensure_key_exists(keypath):
424 (predef_name, keypath) = keypath.split("/", 1)
425 predef_id = SAMBA3_PREDEF_NAMES[predef_name]
426 keypath = keypath.replace("/", "\\")
427 return samba4_registry.create_key(predef_id, keypath)
429 for key in samba3_regdb.keys():
430 key_handle = ensure_key_exists(key)
431 for subkey in samba3_regdb.subkeys(key):
432 ensure_key_exists(subkey)
433 for (value_name, (value_type, value_data)) in samba3_regdb.values(key).items():
434 key_handle.set_value(value_name, value_type, value_data)