2a2c17ec2c4ecbfb3a9eccff2af259fc4b986ba1
[samba.git] / source4 / scripting / python / samba / upgrade.py
1 # backend code for upgrading from Samba3
2 # Copyright Jelmer Vernooij 2005-2007
3 #
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.
8 #
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.
13 #
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/>.
16 #
17
18 """Support code for upgrading from Samba 3 to Samba 4."""
19
20 __docformat__ = "restructuredText"
21
22 import ldb
23 import time
24 import pwd
25
26 from samba import Ldb, registry
27 from samba.param import LoadParm
28 from samba.provision import provision, FILL_FULL, ProvisioningError
29 from samba.samba3 import passdb
30 from samba.samba3 import param as s3param
31 from samba.dcerpc import lsa, samr, security
32 from samba.dcerpc.security import dom_sid
33 from samba import dsdb
34 from samba.ndr import ndr_pack
35 from samba import unix2nttime
36
37
38 def import_sam_policy(samdb, policy, logger):
39     """Import a Samba 3 policy.
40
41     :param samdb: Samba4 SAM database
42     :param policy: Samba3 account policy
43     :param logger: Logger object
44     """
45
46     # Following entries are used -
47     #    min password length, password history, minimum password age,
48     #    maximum password age, lockout duration
49     #
50     # Following entries are not used -
51     #    reset count minutes, user must logon to change password,
52     #    bad lockout minutes, disconnect time
53
54     m = ldb.Message()
55     m.dn = samdb.get_default_basedn()
56     m['a01'] = ldb.MessageElement(str(policy['min password length']),
57         ldb.FLAG_MOD_REPLACE, 'minPwdLength')
58     m['a02'] = ldb.MessageElement(str(policy['password history']),
59         ldb.FLAG_MOD_REPLACE, 'pwdHistoryLength')
60
61     min_pw_age_unix = policy['minimum password age']
62     min_pw_age_nt = 0 - unix2nttime(min_pw_age_unix)
63     m['a03'] = ldb.MessageElement(str(min_pw_age_nt), ldb.FLAG_MOD_REPLACE,
64         'minPwdAge')
65
66     max_pw_age_unix = policy['maximum password age']
67     if (max_pw_age_unix == 0xFFFFFFFF):
68         max_pw_age_nt = 0
69     else:
70         max_pw_age_nt = unix2nttime(max_pw_age_unix)
71
72     m['a04'] = ldb.MessageElement(str(max_pw_age_nt), ldb.FLAG_MOD_REPLACE,
73                                   'maxPwdAge')
74
75     lockout_duration_mins = policy['lockout duration']
76     lockout_duration_nt = unix2nttime(lockout_duration_mins * 60)
77
78     m['a05'] = ldb.MessageElement(str(lockout_duration_nt),
79         ldb.FLAG_MOD_REPLACE, 'lockoutDuration')
80
81     try:
82         samdb.modify(m)
83     except ldb.LdbError, e:
84         logger.warn("Could not set account policy, (%s)", str(e))
85
86
87 def add_idmap_entry(idmapdb, sid, xid, xid_type, logger):
88     """Create idmap entry
89
90     :param idmapdb: Samba4 IDMAP database
91     :param sid: user/group sid
92     :param xid: user/group id
93     :param xid_type: type of id (UID/GID)
94     :param logger: Logger object
95     """
96
97     # First try to see if we already have this entry
98     found = False
99     msg = idmapdb.search(expression='objectSid=%s' % str(sid))
100     if msg.count == 1:
101         found = True
102
103     if found:
104         try:
105             m = ldb.Message()
106             m.dn = msg[0]['dn']
107             m['xidNumber'] = ldb.MessageElement(
108                 str(xid), ldb.FLAG_MOD_REPLACE, 'xidNumber')
109             m['type'] = ldb.MessageElement(
110                 xid_type, ldb.FLAG_MOD_REPLACE, 'type')
111             idmapdb.modify(m)
112         except ldb.LdbError, e:
113             logger.warn(
114                 'Could not modify idmap entry for sid=%s, id=%s, type=%s (%s)',
115                 str(sid), str(xid), xid_type, str(e))
116     else:
117         try:
118             idmapdb.add({"dn": "CN=%s" % str(sid),
119                         "cn": str(sid),
120                         "objectClass": "sidMap",
121                         "objectSid": ndr_pack(sid),
122                         "type": xid_type,
123                         "xidNumber": str(xid)})
124         except ldb.LdbError, e:
125             logger.warn(
126                 'Could not add idmap entry for sid=%s, id=%s, type=%s (%s)',
127                 str(sid), str(xid), xid_type, str(e))
128
129
130 def import_idmap(idmapdb, samba3, logger):
131     """Import idmap data.
132
133     :param idmapdb: Samba4 IDMAP database
134     :param samba3_idmap: Samba3 IDMAP database to import from
135     :param logger: Logger object
136     """
137
138     try:
139         samba3_idmap = samba3.get_idmap_db()
140     except IOError, e:
141         logger.warn('Cannot open idmap database, Ignoring: %s', str(e))
142         return
143
144     currentxid = max(samba3_idmap.get_user_hwm(), samba3_idmap.get_group_hwm())
145     lowerbound = currentxid
146     # FIXME: upperbound
147
148     m = ldb.Message()
149     m.dn = ldb.Dn(idmapdb, 'CN=CONFIG')
150     m['lowerbound'] = ldb.MessageElement(
151         str(lowerbound), ldb.FLAG_MOD_REPLACE, 'lowerBound')
152     m['xidNumber'] = ldb.MessageElement(
153         str(currentxid), ldb.FLAG_MOD_REPLACE, 'xidNumber')
154     idmapdb.modify(m)
155
156     for id_type, xid in samba3_idmap.ids():
157         if id_type == 'UID':
158             xid_type = 'ID_TYPE_UID'
159         elif id_type == 'GID':
160             xid_type = 'ID_TYPE_GID'
161         else:
162             logger.warn('Wrong type of entry in idmap (%s), Ignoring', id_type)
163             continue
164
165         sid = samba3_idmap.get_sid(xid, id_type)
166         add_idmap_entry(idmapdb, dom_sid(sid), xid, xid_type, logger)
167
168
169 def add_group_from_mapping_entry(samdb, groupmap, logger):
170     """Add or modify group from group mapping entry
171
172     param samdb: Samba4 SAM database
173     param groupmap: Groupmap entry
174     param logger: Logger object
175     """
176
177     # First try to see if we already have this entry
178     try:
179         msg = samdb.search(
180             base='<SID=%s>' % str(groupmap.sid), scope=ldb.SCOPE_BASE)
181         found = True
182     except ldb.LdbError, (ecode, emsg):
183         if ecode == ldb.ERR_NO_SUCH_OBJECT:
184             found = False
185         else:
186             raise ldb.LdbError(ecode, emsg)
187
188     if found:
189         logger.warn('Group already exists sid=%s, groupname=%s existing_groupname=%s, Ignoring.',
190                             str(groupmap.sid), groupmap.nt_name, msg[0]['sAMAccountName'][0])
191     else:
192         if groupmap.sid_name_use == lsa.SID_NAME_WKN_GRP:
193             # In a lot of Samba3 databases, aliases are marked as well known groups
194             (group_dom_sid, rid) = groupmap.sid.split()
195             if (group_dom_sid != security.dom_sid(security.SID_BUILTIN)):
196                 return
197
198         m = ldb.Message()
199         m.dn = ldb.Dn(samdb, "CN=%s,CN=Users,%s" % (groupmap.nt_name, samdb.get_default_basedn()))
200         m['a01'] = ldb.MessageElement(groupmap.nt_name, ldb.FLAG_MOD_ADD, 'cn')
201         m['a02'] = ldb.MessageElement('group', ldb.FLAG_MOD_ADD, 'objectClass')
202         m['a03'] = ldb.MessageElement(ndr_pack(groupmap.sid), ldb.FLAG_MOD_ADD, 'objectSid')
203         m['a04'] = ldb.MessageElement(groupmap.comment, ldb.FLAG_MOD_ADD, 'description')
204         m['a05'] = ldb.MessageElement(groupmap.nt_name, ldb.FLAG_MOD_ADD, 'sAMAccountName')
205
206         # Fix up incorrect 'well known' groups that are actually builtin (per test above) to be aliases
207         if groupmap.sid_name_use == lsa.SID_NAME_ALIAS or groupmap.sid_name_use == lsa.SID_NAME_WKN_GRP:
208             m['a06'] = ldb.MessageElement(str(dsdb.GTYPE_SECURITY_DOMAIN_LOCAL_GROUP), ldb.FLAG_MOD_ADD, 'groupType')
209
210         try:
211             samdb.add(m, controls=["relax:0"])
212         except ldb.LdbError, e:
213             logger.warn('Could not add group name=%s (%s)', groupmap.nt_name, str(e))
214
215
216 def add_users_to_group(samdb, group, members, logger):
217     """Add user/member to group/alias
218
219     param samdb: Samba4 SAM database
220     param group: Groupmap object
221     param members: List of member SIDs
222     param logger: Logger object
223     """
224     for member_sid in members:
225         m = ldb.Message()
226         m.dn = ldb.Dn(samdb, "<SID=%s>" % str(group.sid))
227         m['a01'] = ldb.MessageElement("<SID=%s>" % str(member_sid), ldb.FLAG_MOD_ADD, 'member')
228
229         try:
230             samdb.modify(m)
231         except ldb.LdbError, (ecode, emsg):
232             if ecode == ldb.ERR_ENTRY_ALREADY_EXISTS:
233                 logger.info("skipped re-adding member '%s' to group '%s': %s", member_sid, group.sid, emsg)
234             elif ecode == ldb.ERR_NO_SUCH_OBJECT:
235                 raise ProvisioningError("Could not add member '%s' to group '%s' as either group or user record doesn't exist: %s" % (member_sid, group.sid, emsg))
236             else:
237                 raise ProvisioningError("Could not add member '%s' to group '%s': %s" % (member_sid, group.sid, emsg))
238
239
240 def import_wins(samba4_winsdb, samba3_winsdb):
241     """Import settings from a Samba3 WINS database.
242
243     :param samba4_winsdb: WINS database to import to
244     :param samba3_winsdb: WINS database to import from
245     """
246
247     version_id = 0
248
249     for (name, (ttl, ips, nb_flags)) in samba3_winsdb.items():
250         version_id += 1
251
252         type = int(name.split("#", 1)[1], 16)
253
254         if type == 0x1C:
255             rType = 0x2
256         elif type & 0x80:
257             if len(ips) > 1:
258                 rType = 0x2
259             else:
260                 rType = 0x1
261         else:
262             if len(ips) > 1:
263                 rType = 0x3
264             else:
265                 rType = 0x0
266
267         if ttl > time.time():
268             rState = 0x0 # active
269         else:
270             rState = 0x1 # released
271
272         nType = ((nb_flags & 0x60) >> 5)
273
274         samba4_winsdb.add({"dn": "name=%s,type=0x%s" % tuple(name.split("#")),
275                            "type": name.split("#")[1],
276                            "name": name.split("#")[0],
277                            "objectClass": "winsRecord",
278                            "recordType": str(rType),
279                            "recordState": str(rState),
280                            "nodeType": str(nType),
281                            "expireTime": ldb.timestring(ttl),
282                            "isStatic": "0",
283                            "versionID": str(version_id),
284                            "address": ips})
285
286     samba4_winsdb.add({"dn": "cn=VERSION",
287                        "cn": "VERSION",
288                        "objectClass": "winsMaxVersion",
289                        "maxVersion": str(version_id)})
290
291
292 def enable_samba3sam(samdb, ldapurl):
293     """Enable Samba 3 LDAP URL database.
294
295     :param samdb: SAM Database.
296     :param ldapurl: Samba 3 LDAP URL
297     """
298     samdb.modify_ldif("""
299 dn: @MODULES
300 changetype: modify
301 replace: @LIST
302 @LIST: samldb,operational,objectguid,rdn_name,samba3sam
303 """)
304
305     samdb.add({"dn": "@MAP=samba3sam", "@MAP_URL": ldapurl})
306
307
308 smbconf_keep = [
309     "dos charset",
310     "unix charset",
311     "display charset",
312     "comment",
313     "path",
314     "directory",
315     "workgroup",
316     "realm",
317     "netbios name",
318     "netbios aliases",
319     "netbios scope",
320     "server string",
321     "interfaces",
322     "bind interfaces only",
323     "security",
324     "auth methods",
325     "encrypt passwords",
326     "null passwords",
327     "obey pam restrictions",
328     "password server",
329     "smb passwd file",
330     "private dir",
331     "passwd chat",
332     "password level",
333     "lanman auth",
334     "ntlm auth",
335     "client NTLMv2 auth",
336     "client lanman auth",
337     "client plaintext auth",
338     "read only",
339     "hosts allow",
340     "hosts deny",
341     "log level",
342     "debuglevel",
343     "log file",
344     "smb ports",
345     "large readwrite",
346     "max protocol",
347     "min protocol",
348     "unicode",
349     "read raw",
350     "write raw",
351     "disable netbios",
352     "nt status support",
353     "max mux",
354     "max xmit",
355     "name resolve order",
356     "max wins ttl",
357     "min wins ttl",
358     "time server",
359     "unix extensions",
360     "use spnego",
361     "server signing",
362     "client signing",
363     "max connections",
364     "paranoid server security",
365     "socket options",
366     "strict sync",
367     "max print jobs",
368     "printable",
369     "print ok",
370     "printer name",
371     "printer",
372     "map system",
373     "map hidden",
374     "map archive",
375     "preferred master",
376     "prefered master",
377     "local master",
378     "browseable",
379     "browsable",
380     "wins server",
381     "wins support",
382     "csc policy",
383     "strict locking",
384     "preload",
385     "auto services",
386     "lock dir",
387     "lock directory",
388     "pid directory",
389     "socket address",
390     "copy",
391     "include",
392     "available",
393     "volume",
394     "fstype",
395     "panic action",
396     "msdfs root",
397     "host msdfs",
398     "winbind separator"]
399
400
401 def upgrade_smbconf(oldconf, mark):
402     """Remove configuration variables not present in Samba4
403
404     :param oldconf: Old configuration structure
405     :param mark: Whether removed configuration variables should be
406         kept in the new configuration as "samba3:<name>"
407     """
408     data = oldconf.data()
409     newconf = LoadParm()
410
411     for s in data:
412         for p in data[s]:
413             keep = False
414             for k in smbconf_keep:
415                 if smbconf_keep[k] == p:
416                     keep = True
417                     break
418
419             if keep:
420                 newconf.set(s, p, oldconf.get(s, p))
421             elif mark:
422                 newconf.set(s, "samba3:" + p, oldconf.get(s, p))
423
424     return newconf
425
426 SAMBA3_PREDEF_NAMES = {
427         'HKLM': registry.HKEY_LOCAL_MACHINE,
428 }
429
430
431 def import_registry(samba4_registry, samba3_regdb):
432     """Import a Samba 3 registry database into the Samba 4 registry.
433
434     :param samba4_registry: Samba 4 registry handle.
435     :param samba3_regdb: Samba 3 registry database handle.
436     """
437     def ensure_key_exists(keypath):
438         (predef_name, keypath) = keypath.split("/", 1)
439         predef_id = SAMBA3_PREDEF_NAMES[predef_name]
440         keypath = keypath.replace("/", "\\")
441         return samba4_registry.create_key(predef_id, keypath)
442
443     for key in samba3_regdb.keys():
444         key_handle = ensure_key_exists(key)
445         for subkey in samba3_regdb.subkeys(key):
446             ensure_key_exists(subkey)
447         for (value_name, (value_type, value_data)) in samba3_regdb.values(key).items():
448             key_handle.set_value(value_name, value_type, value_data)
449
450
451 def upgrade_from_samba3(samba3, logger, targetdir, session_info=None, useeadb=False):
452     """Upgrade from samba3 database to samba4 AD database
453
454     :param samba3: samba3 object
455     :param logger: Logger object
456     :param targetdir: samba4 database directory
457     :param session_info: Session information
458     """
459
460     if samba3.lp.get("domain logons"):
461         serverrole = "domain controller"
462     else:
463         if samba3.lp.get("security") == "user":
464             serverrole = "standalone"
465         else:
466             serverrole = "member server"
467
468     domainname = samba3.lp.get("workgroup")
469     realm = samba3.lp.get("realm")
470     netbiosname = samba3.lp.get("netbios name")
471
472     # secrets db
473     try:
474         secrets_db = samba3.get_secrets_db()
475     except IOError, e:
476         raise ProvisioningError("Could not open '%s', the Samba3 secrets database: %s.  Perhaps you specified the incorrect smb.conf, --testparm or --libdir option?" % (samba3.privatedir_path("secrets.tdb"), str(e)))
477
478     if not domainname:
479         domainname = secrets_db.domains()[0]
480         logger.warning("No workgroup specified in smb.conf file, assuming '%s'",
481                 domainname)
482
483     if not realm:
484         if serverrole == "domain controller":
485             raise ProvisioningError("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 (it is the DNS name of the AD domain you wish to create.")
486         else:
487             realm = domainname.upper()
488             logger.warning("No realm specified in smb.conf file, assuming '%s'",
489                     realm)
490
491     # Find machine account and password
492     next_rid = 1000
493
494     try:
495         machinepass = secrets_db.get_machine_password(netbiosname)
496     except KeyError:
497         machinepass = None
498
499     # We must close the direct pytdb database before the C code loads it
500     secrets_db.close()
501
502     # Connect to old password backend
503     passdb.set_secrets_dir(samba3.lp.get("private dir"))
504     s3db = samba3.get_sam_db()
505
506     # Get domain sid
507     try:
508         domainsid = passdb.get_global_sam_sid()
509     except passdb.error:
510         raise Exception("Can't find domain sid for '%s', Exiting." % domainname)
511
512     # Get machine account, sid, rid
513     try:
514         machineacct = s3db.getsampwnam('%s$' % netbiosname)
515     except passdb.error:
516         machinerid = None
517         machinesid = None
518     else:
519         machinesid, machinerid = machineacct.user_sid.split()
520
521     # Export account policy
522     logger.info("Exporting account policy")
523     policy = s3db.get_account_policy()
524
525     # Export groups from old passdb backend
526     logger.info("Exporting groups")
527     grouplist = s3db.enum_group_mapping()
528     groupmembers = {}
529     for group in grouplist:
530         sid, rid = group.sid.split()
531         if sid == domainsid:
532             if rid >= next_rid:
533                 next_rid = rid + 1
534
535         # Get members for each group/alias
536         if group.sid_name_use == lsa.SID_NAME_ALIAS:
537             members = s3db.enum_aliasmem(group.sid)
538         elif group.sid_name_use == lsa.SID_NAME_DOM_GRP:
539             try:
540                 members = s3db.enum_group_members(group.sid)
541             except passdb.error:
542                 continue
543             groupmembers[group.nt_name] = members
544         elif group.sid_name_use == lsa.SID_NAME_WKN_GRP:
545             (group_dom_sid, rid) = group.sid.split()
546             if (group_dom_sid != security.dom_sid(security.SID_BUILTIN)):
547                 logger.warn("Ignoring 'well known' group '%s' (should already be in AD, and have no members)",
548                             group.nt_name)
549                 continue
550             # A number of buggy databases mix up well known groups and aliases.
551             members = s3db.enum_aliasmem(group.sid)
552         else:
553             logger.warn("Ignoring group '%s' with sid_name_use=%d",
554                         group.nt_name, group.sid_name_use)
555             continue
556
557     # Export users from old passdb backend
558     logger.info("Exporting users")
559     userlist = s3db.search_users(0)
560     userdata = {}
561     uids = {}
562     admin_user = None
563     for entry in userlist:
564         if machinerid and machinerid == entry['rid']:
565             continue
566         username = entry['account_name']
567         if entry['rid'] < 1000:
568             logger.info("  Skipping wellknown rid=%d (for username=%s)", entry['rid'], username)
569             continue
570         if entry['rid'] >= next_rid:
571             next_rid = entry['rid'] + 1
572
573         user = s3db.getsampwnam(username)
574         acct_type = (user.acct_ctrl & (samr.ACB_NORMAL|samr.ACB_WSTRUST|samr.ACB_SVRTRUST|samr.ACB_DOMTRUST))
575         if (acct_type == samr.ACB_NORMAL or acct_type == samr.ACB_WSTRUST or acct_type == samr.ACB_SVRTRUST):
576             pass
577         elif acct_type == samr.ACB_DOMTRUST:
578             logger.warn("  Skipping inter-domain trust from domain %s, this trust must be re-created as an AD trust" % username[:-1])
579             continue
580         elif acct_type == (samr.ACB_NORMAL|samr.ACB_WSTRUST) and username[-1] == '$':
581             logger.warn("  Fixing account %s which had both ACB_NORMAL (U) and ACB_WSTRUST (W) set.  Account will be marked as ACB_WSTRUST (W), i.e. as a domain member" % username)
582             user.acct_ctrl = (user.acct_ctrl & ~samr.ACB_NORMAL)
583         else:
584             raise ProvisioningError("""Failed to upgrade due to invalid account %s, account control flags 0x%08X must have exactly one of
585 ACB_NORMAL (N, 0x%08X), ACB_WSTRUST (W 0x%08X), ACB_SVRTRUST (S 0x%08X) or ACB_DOMTRUST (D 0x%08X).
586
587 Please fix this account before attempting to upgrade again
588 """
589                                     % (user.acct_flags, username,
590                                        samr.ACB_NORMAL, samr.ACB_WSTRUST, samr.ACB_SVRTRUST, samr.ACB_DOMTRUST))
591
592         userdata[username] = user
593         try:
594             uids[username] = s3db.sid_to_id(user.user_sid)[0]
595         except passdb.error:
596             try:
597                 uids[username] = pwd.getpwnam(username).pw_uid
598             except KeyError:
599                 pass
600
601         if not admin_user and username.lower() == 'root':
602             admin_user = username
603         if username.lower() == 'administrator':
604             admin_user = username
605
606     logger.info("Next rid = %d", next_rid)
607
608     # Check for same username/groupname
609     group_names = set([g.nt_name for g in grouplist])
610     user_names = set([u['account_name'] for u in userlist])
611     common_names = group_names.intersection(user_names)
612     if common_names:
613         logger.error("Following names are both user names and group names:")
614         for name in common_names:
615             logger.error("   %s" % name)
616         raise ProvisioningError("Please remove common user/group names before upgrade.")
617
618     # Check for same user sid/group sid
619     group_sids = set([str(g.sid) for g in grouplist])
620     user_sids = set(["%s-%u" % (domainsid, u['rid']) for u in userlist])
621     common_sids = group_sids.intersection(user_sids)
622     if common_sids:
623         logger.error("Following sids are both user and group sids:")
624         for sid in common_sids:
625             logger.error("   %s" % str(sid))
626         raise ProvisioningError("Please remove duplicate sid entries before upgrade.")
627
628     if serverrole == "domain controller":
629         dns_backend = "BIND9_FLATFILE"
630     else:
631         dns_backend = "NONE"
632
633     # Do full provision
634     result = provision(logger, session_info, None,
635                        targetdir=targetdir, realm=realm, domain=domainname,
636                        domainsid=str(domainsid), next_rid=next_rid,
637                        dc_rid=machinerid,
638                        dom_for_fun_level=dsdb.DS_DOMAIN_FUNCTION_2003,
639                        hostname=netbiosname, machinepass=machinepass,
640                        serverrole=serverrole, samdb_fill=FILL_FULL,
641                        useeadb=useeadb, dns_backend=dns_backend)
642
643     # Import WINS database
644     logger.info("Importing WINS database")
645
646     samba3_winsdb = None
647     try:
648         samba3_winsdb = samba3.get_wins_db()
649     except IOError, e:
650         logger.warn('Cannot open wins database, Ignoring: %s', str(e))
651
652     if samba3_winsdb:
653         import_wins(Ldb(result.paths.winsdb), samba3_winsdb)
654
655     # Set Account policy
656     logger.info("Importing Account policy")
657     import_sam_policy(result.samdb, policy, logger)
658
659     # Migrate IDMAP database
660     logger.info("Importing idmap database")
661     import_idmap(result.idmap, samba3, logger)
662
663     # Set the s3 context for samba4 configuration
664     new_lp_ctx = s3param.get_context()
665     new_lp_ctx.load(result.lp.configfile)
666     new_lp_ctx.set("private dir", result.lp.get("private dir"))
667     new_lp_ctx.set("state directory", result.lp.get("state directory"))
668     new_lp_ctx.set("lock directory", result.lp.get("lock directory"))
669
670     # Connect to samba4 backend
671     s4_passdb = passdb.PDB(new_lp_ctx.get("passdb backend"))
672
673     # Export groups to samba4 backend
674     logger.info("Importing groups")
675     for g in grouplist:
676         # Ignore uninitialized groups (gid = -1)
677         if g.gid != 0xffffffff:
678             add_idmap_entry(result.idmap, g.sid, g.gid, "GID", logger)
679             add_group_from_mapping_entry(result.samdb, g, logger)
680
681     # Export users to samba4 backend
682     logger.info("Importing users")
683     for username in userdata:
684         if username.lower() == 'administrator' or username.lower() == 'root':
685             continue
686         s4_passdb.add_sam_account(userdata[username])
687         if username in uids:
688             add_idmap_entry(result.idmap, userdata[username].user_sid, uids[username], "UID", logger)
689
690     logger.info("Adding users to groups")
691     for g in grouplist:
692         if g.nt_name in groupmembers:
693             add_users_to_group(result.samdb, g, groupmembers[g.nt_name], logger)
694
695     # Set password for administrator
696     if admin_user:
697         logger.info("Setting password for administrator")
698         admin_userdata = s4_passdb.getsampwnam("administrator")
699         admin_userdata.nt_passwd = userdata[admin_user].nt_passwd
700         if userdata[admin_user].lanman_passwd:
701             admin_userdata.lanman_passwd = userdata[admin_user].lanman_passwd
702         admin_userdata.pass_last_set_time = userdata[admin_user].pass_last_set_time
703         if userdata[admin_user].pw_history:
704             admin_userdata.pw_history = userdata[admin_user].pw_history
705         s4_passdb.update_sam_account(admin_userdata)
706         logger.info("Administrator password has been set to password of user '%s'", admin_user)
707
708     # FIXME: import_registry(registry.Registry(), samba3.get_registry())
709     # FIXME: shares