1 # backend code for upgrading from Samba3
2 # Copyright Jelmer Vernooij 2005-2007
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 """Support code for upgrading from Samba 3 to Samba 4."""
20 __docformat__ = "restructuredText"
27 from samba import Ldb, registry
28 from samba.param import LoadParm
29 from samba.provision import provision
31 def import_sam_policy(samldb, policy, dn):
32 """Import a Samba 3 policy database."""
33 samldb.modify_ldif("""
42 samba3ResetCountMinutes: %d
43 samba3UserMustLogonToChangePassword: %d
44 samba3BadLockoutMinutes: %d
45 samba3DisconnectTime: %d
47 """ % (dn, policy.min_password_length,
48 policy.password_history, policy.minimum_password_age,
49 policy.maximum_password_age, policy.lockout_duration,
50 policy.reset_count_minutes, policy.user_must_logon_to_change_password,
51 policy.bad_lockout_minutes, policy.disconnect_time))
54 def import_sam_account(samldb,acc,domaindn,domainsid):
55 """Import a Samba 3 SAM account.
57 :param samldb: Samba 4 SAM Database handle
58 :param acc: Samba 3 account
59 :param domaindn: Domain DN
60 :param domainsid: Domain SID."""
61 if acc.nt_username is None or acc.nt_username == "":
62 acc.nt_username = acc.username
64 if acc.fullname is None:
66 acc.fullname = pwd.getpwnam(acc.username)[4].split(",")[0]
70 if acc.fullname is None:
71 acc.fullname = acc.username
73 assert acc.fullname is not None
74 assert acc.nt_username is not None
77 "dn": "cn=%s,%s" % (acc.fullname, domaindn),
78 "objectClass": ["top", "user"],
79 "lastLogon": str(acc.logon_time),
80 "lastLogoff": str(acc.logoff_time),
81 "unixName": acc.username,
82 "sAMAccountName": acc.nt_username,
83 "cn": acc.nt_username,
84 "description": acc.acct_desc,
85 "primaryGroupID": str(acc.group_rid),
86 "badPwdcount": str(acc.bad_password_count),
87 "logonCount": str(acc.logon_count),
88 "samba3Domain": acc.domain,
89 "samba3DirDrive": acc.dir_drive,
90 "samba3MungedDial": acc.munged_dial,
91 "samba3Homedir": acc.homedir,
92 "samba3LogonScript": acc.logon_script,
93 "samba3ProfilePath": acc.profile_path,
94 "samba3Workstations": acc.workstations,
95 "samba3KickOffTime": str(acc.kickoff_time),
96 "samba3BadPwdTime": str(acc.bad_password_time),
97 "samba3PassLastSetTime": str(acc.pass_last_set_time),
98 "samba3PassCanChangeTime": str(acc.pass_can_change_time),
99 "samba3PassMustChangeTime": str(acc.pass_must_change_time),
100 "objectSid": "%s-%d" % (domainsid, acc.user_rid),
101 "lmPwdHash:": acc.lm_password,
102 "ntPwdHash:": acc.nt_password,
106 def import_sam_group(samldb, sid, gid, sid_name_use, nt_name, comment, domaindn):
107 """Upgrade a SAM group.
109 :param samldb: SAM database.
110 :param gid: Group GID
111 :param sid_name_use: SID name use
112 :param nt_name: NT Group Name
113 :param comment: NT Group Comment
114 :param domaindn: Domain DN
117 if sid_name_use == 5: # Well-known group
120 if nt_name in ("Domain Guests", "Domain Users", "Domain Admins"):
124 gr = grp.getgrnam(nt_name)
126 gr = grp.getgrgid(gid)
131 unixname = gr.gr_name
133 assert unixname is not None
136 "dn": "cn=%s,%s" % (nt_name, domaindn),
137 "objectClass": ["top", "group"],
138 "description": comment,
141 "unixName": unixname,
142 "samba3SidNameUse": str(sid_name_use)
146 def import_idmap(samdb,samba3_idmap,domaindn):
147 """Import idmap data.
149 :param samdb: SamDB handle.
150 :param samba3_idmap: Samba 3 IDMAP database to import from
151 :param domaindn: Domain DN.
155 "userHwm": str(samba3_idmap.get_user_hwm()),
156 "groupHwm": str(samba3_idmap.get_group_hwm())})
158 for uid in samba3_idmap.uids():
159 samdb.add({"dn": "SID=%s,%s" % (samba3_idmap.get_user_sid(uid), domaindn),
160 "SID": samba3_idmap.get_user_sid(uid),
164 for gid in samba3_idmap.uids():
165 samdb.add({"dn": "SID=%s,%s" % (samba3_idmap.get_group_sid(gid), domaindn),
166 "SID": samba3_idmap.get_group_sid(gid),
171 def import_wins(samba4_winsdb, samba3_winsdb):
172 """Import settings from a Samba3 WINS database.
174 :param samba4_winsdb: WINS database to import to
175 :param samba3_winsdb: WINS database to import from
179 for (name, (ttl, ips, nb_flags)) in samba3_winsdb.items():
182 type = int(name.split("#", 1)[1], 16)
197 if ttl > time.time():
198 rState = 0x0 # active
200 rState = 0x1 # released
202 nType = ((nb_flags & 0x60)>>5)
204 samba4_winsdb.add({"dn": "name=%s,type=0x%s" % tuple(name.split("#")),
205 "type": name.split("#")[1],
206 "name": name.split("#")[0],
207 "objectClass": "winsRecord",
208 "recordType": str(rType),
209 "recordState": str(rState),
210 "nodeType": str(nType),
211 "expireTime": ldb.timestring(ttl),
213 "versionID": str(version_id),
216 samba4_winsdb.add({"dn": "cn=VERSION",
218 "objectClass": "winsMaxVersion",
219 "maxVersion": str(version_id)})
221 def enable_samba3sam(samdb, ldapurl):
222 """Enable Samba 3 LDAP URL database.
224 :param samdb: SAM Database.
225 :param ldapurl: Samba 3 LDAP URL
227 samdb.modify_ldif("""
231 @LIST: samldb,operational,objectguid,rdn_name,samba3sam
234 samdb.add({"dn": "@MAP=samba3sam", "@MAP_URL": ldapurl})
251 "bind interfaces only",
256 "obey pam restrictions",
264 "client NTLMv2 auth",
265 "client lanman auth",
266 "client plaintext auth",
286 "name resolve order",
295 "paranoid server security",
331 def upgrade_smbconf(oldconf,mark):
332 """Remove configuration variables not present in Samba4
334 :param oldconf: Old configuration structure
335 :param mark: Whether removed configuration variables should be
336 kept in the new configuration as "samba3:<name>"
338 data = oldconf.data()
344 for k in smbconf_keep:
345 if smbconf_keep[k] == p:
350 newconf.set(s, p, oldconf.get(s, p))
352 newconf.set(s, "samba3:"+p, oldconf.get(s,p))
356 SAMBA3_PREDEF_NAMES = {
357 'HKLM': registry.HKEY_LOCAL_MACHINE,
360 def import_registry(samba4_registry, samba3_regdb):
361 """Import a Samba 3 registry database into the Samba 4 registry.
363 :param samba4_registry: Samba 4 registry handle.
364 :param samba3_regdb: Samba 3 registry database handle.
366 def ensure_key_exists(keypath):
367 (predef_name, keypath) = keypath.split("/", 1)
368 predef_id = SAMBA3_PREDEF_NAMES[predef_name]
369 keypath = keypath.replace("/", "\\")
370 return samba4_registry.create_key(predef_id, keypath)
372 for key in samba3_regdb.keys():
373 key_handle = ensure_key_exists(key)
374 for subkey in samba3_regdb.subkeys(key):
375 ensure_key_exists(subkey)
376 for (value_name, (value_type, value_data)) in samba3_regdb.values(key).items():
377 key_handle.set_value(value_name, value_type, value_data)
380 def upgrade_provision(samba3, logger, credentials, session_info,
382 oldconf = samba3.get_conf()
384 if oldconf.get("domain logons") == "True":
385 serverrole = "domain controller"
387 if oldconf.get("security") == "user":
388 serverrole = "standalone"
390 serverrole = "member server"
392 domainname = oldconf.get("workgroup")
393 realm = oldconf.get("realm")
394 netbiosname = oldconf.get("netbios name")
396 secrets_db = samba3.get_secrets_db()
398 if domainname is None:
399 domainname = secrets_db.domains()[0]
400 logger.warning("No domain specified in smb.conf file, assuming '%s'",
404 if oldconf.get("domain logons") == "True":
405 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).")
408 realm = domainname.upper()
409 logger.warning("No realm specified in smb.conf file, assuming '%s'",
412 domainguid = secrets_db.get_domain_guid(domainname)
413 domainsid = secrets_db.get_sid(domainname)
414 if domainsid is None:
415 logger.warning("Can't find domain secrets for '%s'; using random SID",
418 if netbiosname is not None:
419 machinepass = secrets_db.get_machine_password(netbiosname)
423 result = provision(logger=logger,
424 session_info=session_info, credentials=credentials,
425 targetdir=targetdir, realm=realm, domain=domainname,
426 domainguid=domainguid, domainsid=domainsid,
427 hostname=netbiosname, machinepass=machinepass,
428 serverrole=serverrole)
430 import_wins(Ldb(result.paths.winsdb), samba3.get_wins_db())
432 # FIXME: import_registry(registry.Registry(), samba3.get_registry())
434 # FIXME: import_idmap(samdb,samba3.get_idmap_db(),domaindn)
436 groupdb = samba3.get_groupmapping_db()
437 for sid in groupdb.groupsids():
438 (gid, sid_name_use, nt_name, comment) = groupdb.get_group(sid)
439 # FIXME: import_sam_group(samdb, sid, gid, sid_name_use, nt_name, comment, domaindn)
443 passdb = samba3.get_sam_db()
446 #FIXME: import_sam_account(result.samdb, user, domaindn, domainsid)
448 if hasattr(passdb, 'ldap_url'):
449 logger.info("Enabling Samba3 LDAP mappings for SAM database")
451 enable_samba3sam(result.samdb, passdb.ldap_url)