s4-s3-upgrade now look for -1 as the special 'not set' value
[samba.git] / source4 / scripting / python / samba / upgrade.py
1 # backend code for upgrading from Samba3
2 # Copyright Jelmer Vernooij 2005-2007
3 # Copyright Andrew Bartlett 2011
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #
18
19 """Support code for upgrading from Samba 3 to Samba 4."""
20
21 __docformat__ = "restructuredText"
22
23 import ldb
24 import time
25 import pwd
26
27 from samba import Ldb, registry
28 from samba.param import LoadParm
29 from samba.provision import provision, FILL_FULL, ProvisioningError
30 from samba.samba3 import passdb
31 from samba.samba3 import param as s3param
32 from samba.dcerpc import lsa, samr, security
33 from samba.dcerpc.security import dom_sid
34 from samba import dsdb
35 from samba.ndr import ndr_pack
36 from samba import unix2nttime
37
38
39 def import_sam_policy(samdb, policy, logger):
40     """Import a Samba 3 policy.
41
42     :param samdb: Samba4 SAM database
43     :param policy: Samba3 account policy
44     :param logger: Logger object
45     """
46
47     # Following entries are used -
48     #    min password length, password history, minimum password age,
49     #    maximum password age, lockout duration
50     #
51     # Following entries are not used -
52     #    reset count minutes, user must logon to change password,
53     #    bad lockout minutes, disconnect time
54
55     m = ldb.Message()
56     m.dn = samdb.get_default_basedn()
57     m['a01'] = ldb.MessageElement(str(policy['min password length']),
58         ldb.FLAG_MOD_REPLACE, 'minPwdLength')
59     m['a02'] = ldb.MessageElement(str(policy['password history']),
60         ldb.FLAG_MOD_REPLACE, 'pwdHistoryLength')
61
62     min_pw_age_unix = policy['minimum password age']
63     min_pw_age_nt = 0 - unix2nttime(min_pw_age_unix)
64     m['a03'] = ldb.MessageElement(str(min_pw_age_nt), ldb.FLAG_MOD_REPLACE,
65         'minPwdAge')
66
67     max_pw_age_unix = policy['maximum password age']
68     if (max_pw_age_unix == -1):
69         max_pw_age_nt = 0
70     else:
71         max_pw_age_nt = unix2nttime(max_pw_age_unix)
72
73     m['a04'] = ldb.MessageElement(str(max_pw_age_nt), ldb.FLAG_MOD_REPLACE,
74                                   'maxPwdAge')
75
76     lockout_duration_mins = policy['lockout duration']
77     lockout_duration_nt = unix2nttime(lockout_duration_mins * 60)
78
79     m['a05'] = ldb.MessageElement(str(lockout_duration_nt),
80         ldb.FLAG_MOD_REPLACE, 'lockoutDuration')
81
82     try:
83         samdb.modify(m)
84     except ldb.LdbError, e:
85         logger.warn("Could not set account policy, (%s)", str(e))
86
87
88 def add_idmap_entry(idmapdb, sid, xid, xid_type, logger):
89     """Create idmap entry
90
91     :param idmapdb: Samba4 IDMAP database
92     :param sid: user/group sid
93     :param xid: user/group id
94     :param xid_type: type of id (UID/GID)
95     :param logger: Logger object
96     """
97
98     # First try to see if we already have this entry
99     found = False
100     msg = idmapdb.search(expression='objectSid=%s' % str(sid))
101     if msg.count == 1:
102         found = True
103
104     if found:
105         try:
106             m = ldb.Message()
107             m.dn = msg[0]['dn']
108             m['xidNumber'] = ldb.MessageElement(
109                 str(xid), ldb.FLAG_MOD_REPLACE, 'xidNumber')
110             m['type'] = ldb.MessageElement(
111                 xid_type, ldb.FLAG_MOD_REPLACE, 'type')
112             idmapdb.modify(m)
113         except ldb.LdbError, e:
114             logger.warn(
115                 'Could not modify idmap entry for sid=%s, id=%s, type=%s (%s)',
116                 str(sid), str(xid), xid_type, str(e))
117     else:
118         try:
119             idmapdb.add({"dn": "CN=%s" % str(sid),
120                         "cn": str(sid),
121                         "objectClass": "sidMap",
122                         "objectSid": ndr_pack(sid),
123                         "type": xid_type,
124                         "xidNumber": str(xid)})
125         except ldb.LdbError, e:
126             logger.warn(
127                 'Could not add idmap entry for sid=%s, id=%s, type=%s (%s)',
128                 str(sid), str(xid), xid_type, str(e))
129
130
131 def import_idmap(idmapdb, samba3, logger):
132     """Import idmap data.
133
134     :param idmapdb: Samba4 IDMAP database
135     :param samba3_idmap: Samba3 IDMAP database to import from
136     :param logger: Logger object
137     """
138
139     try:
140         samba3_idmap = samba3.get_idmap_db()
141     except IOError, e:
142         logger.warn('Cannot open idmap database, Ignoring: %s', str(e))
143         return
144
145     currentxid = max(samba3_idmap.get_user_hwm(), samba3_idmap.get_group_hwm())
146     lowerbound = currentxid
147     # FIXME: upperbound
148
149     m = ldb.Message()
150     m.dn = ldb.Dn(idmapdb, 'CN=CONFIG')
151     m['lowerbound'] = ldb.MessageElement(
152         str(lowerbound), ldb.FLAG_MOD_REPLACE, 'lowerBound')
153     m['xidNumber'] = ldb.MessageElement(
154         str(currentxid), ldb.FLAG_MOD_REPLACE, 'xidNumber')
155     idmapdb.modify(m)
156
157     for id_type, xid in samba3_idmap.ids():
158         if id_type == 'UID':
159             xid_type = 'ID_TYPE_UID'
160         elif id_type == 'GID':
161             xid_type = 'ID_TYPE_GID'
162         else:
163             logger.warn('Wrong type of entry in idmap (%s), Ignoring', id_type)
164             continue
165
166         sid = samba3_idmap.get_sid(xid, id_type)
167         add_idmap_entry(idmapdb, dom_sid(sid), xid, xid_type, logger)
168
169
170 def add_group_from_mapping_entry(samdb, groupmap, logger):
171     """Add or modify group from group mapping entry
172
173     param samdb: Samba4 SAM database
174     param groupmap: Groupmap entry
175     param logger: Logger object
176     """
177
178     # First try to see if we already have this entry
179     try:
180         msg = samdb.search(
181             base='<SID=%s>' % str(groupmap.sid), scope=ldb.SCOPE_BASE)
182         found = True
183     except ldb.LdbError, (ecode, emsg):
184         if ecode == ldb.ERR_NO_SUCH_OBJECT:
185             found = False
186         else:
187             raise ldb.LdbError(ecode, emsg)
188
189     if found:
190         logger.warn('Group already exists sid=%s, groupname=%s existing_groupname=%s, Ignoring.',
191                             str(groupmap.sid), groupmap.nt_name, msg[0]['sAMAccountName'][0])
192     else:
193         if groupmap.sid_name_use == lsa.SID_NAME_WKN_GRP:
194             # In a lot of Samba3 databases, aliases are marked as well known groups
195             (group_dom_sid, rid) = groupmap.sid.split()
196             if (group_dom_sid != security.dom_sid(security.SID_BUILTIN)):
197                 return
198
199         m = ldb.Message()
200         m.dn = ldb.Dn(samdb, "CN=%s,CN=Users,%s" % (groupmap.nt_name, samdb.get_default_basedn()))
201         m['cn'] = ldb.MessageElement(groupmap.nt_name, ldb.FLAG_MOD_ADD, 'cn')
202         m['objectClass'] = ldb.MessageElement('group', ldb.FLAG_MOD_ADD, 'objectClass')
203         m['objectSid'] = ldb.MessageElement(ndr_pack(groupmap.sid), ldb.FLAG_MOD_ADD,
204             'objectSid')
205         m['sAMAccountName'] = ldb.MessageElement(groupmap.nt_name, ldb.FLAG_MOD_ADD,
206             'sAMAccountName')
207
208         if groupmap.comment:
209             m['description'] = ldb.MessageElement(groupmap.comment, ldb.FLAG_MOD_ADD,
210                 'description')
211
212         # Fix up incorrect 'well known' groups that are actually builtin (per test above) to be aliases
213         if groupmap.sid_name_use == lsa.SID_NAME_ALIAS or groupmap.sid_name_use == lsa.SID_NAME_WKN_GRP:
214             m['groupType'] = ldb.MessageElement(str(dsdb.GTYPE_SECURITY_DOMAIN_LOCAL_GROUP),
215                 ldb.FLAG_MOD_ADD, 'groupType')
216
217         try:
218             samdb.add(m, controls=["relax:0"])
219         except ldb.LdbError, e:
220             logger.warn('Could not add group name=%s (%s)', groupmap.nt_name, str(e))
221
222
223 def add_users_to_group(samdb, group, members, logger):
224     """Add user/member to group/alias
225
226     param samdb: Samba4 SAM database
227     param group: Groupmap object
228     param members: List of member SIDs
229     param logger: Logger object
230     """
231     for member_sid in members:
232         m = ldb.Message()
233         m.dn = ldb.Dn(samdb, "<SID=%s>" % str(group.sid))
234         m['a01'] = ldb.MessageElement("<SID=%s>" % str(member_sid), ldb.FLAG_MOD_ADD, 'member')
235
236         try:
237             samdb.modify(m)
238         except ldb.LdbError, (ecode, emsg):
239             if ecode == ldb.ERR_ENTRY_ALREADY_EXISTS:
240                 logger.info("skipped re-adding member '%s' to group '%s': %s", member_sid, group.sid, emsg)
241             elif ecode == ldb.ERR_NO_SUCH_OBJECT:
242                 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))
243             else:
244                 raise ProvisioningError("Could not add member '%s' to group '%s': %s" % (member_sid, group.sid, emsg))
245
246
247 def import_wins(samba4_winsdb, samba3_winsdb):
248     """Import settings from a Samba3 WINS database.
249
250     :param samba4_winsdb: WINS database to import to
251     :param samba3_winsdb: WINS database to import from
252     """
253
254     version_id = 0
255
256     for (name, (ttl, ips, nb_flags)) in samba3_winsdb.items():
257         version_id += 1
258
259         type = int(name.split("#", 1)[1], 16)
260
261         if type == 0x1C:
262             rType = 0x2
263         elif type & 0x80:
264             if len(ips) > 1:
265                 rType = 0x2
266             else:
267                 rType = 0x1
268         else:
269             if len(ips) > 1:
270                 rType = 0x3
271             else:
272                 rType = 0x0
273
274         if ttl > time.time():
275             rState = 0x0 # active
276         else:
277             rState = 0x1 # released
278
279         nType = ((nb_flags & 0x60) >> 5)
280
281         samba4_winsdb.add({"dn": "name=%s,type=0x%s" % tuple(name.split("#")),
282                            "type": name.split("#")[1],
283                            "name": name.split("#")[0],
284                            "objectClass": "winsRecord",
285                            "recordType": str(rType),
286                            "recordState": str(rState),
287                            "nodeType": str(nType),
288                            "expireTime": ldb.timestring(ttl),
289                            "isStatic": "0",
290                            "versionID": str(version_id),
291                            "address": ips})
292
293     samba4_winsdb.add({"dn": "cn=VERSION",
294                        "cn": "VERSION",
295                        "objectClass": "winsMaxVersion",
296                        "maxVersion": str(version_id)})
297
298
299 def enable_samba3sam(samdb, ldapurl):
300     """Enable Samba 3 LDAP URL database.
301
302     :param samdb: SAM Database.
303     :param ldapurl: Samba 3 LDAP URL
304     """
305     samdb.modify_ldif("""
306 dn: @MODULES
307 changetype: modify
308 replace: @LIST
309 @LIST: samldb,operational,objectguid,rdn_name,samba3sam
310 """)
311
312     samdb.add({"dn": "@MAP=samba3sam", "@MAP_URL": ldapurl})
313
314
315 smbconf_keep = [
316     "dos charset",
317     "unix charset",
318     "display charset",
319     "comment",
320     "path",
321     "directory",
322     "workgroup",
323     "realm",
324     "netbios name",
325     "netbios aliases",
326     "netbios scope",
327     "server string",
328     "interfaces",
329     "bind interfaces only",
330     "security",
331     "auth methods",
332     "encrypt passwords",
333     "null passwords",
334     "obey pam restrictions",
335     "password server",
336     "smb passwd file",
337     "private dir",
338     "passwd chat",
339     "password level",
340     "lanman auth",
341     "ntlm auth",
342     "client NTLMv2 auth",
343     "client lanman auth",
344     "client plaintext auth",
345     "read only",
346     "hosts allow",
347     "hosts deny",
348     "log level",
349     "debuglevel",
350     "log file",
351     "smb ports",
352     "large readwrite",
353     "max protocol",
354     "min protocol",
355     "unicode",
356     "read raw",
357     "write raw",
358     "disable netbios",
359     "nt status support",
360     "max mux",
361     "max xmit",
362     "name resolve order",
363     "max wins ttl",
364     "min wins ttl",
365     "time server",
366     "unix extensions",
367     "use spnego",
368     "server signing",
369     "client signing",
370     "max connections",
371     "paranoid server security",
372     "socket options",
373     "strict sync",
374     "max print jobs",
375     "printable",
376     "print ok",
377     "printer name",
378     "printer",
379     "map system",
380     "map hidden",
381     "map archive",
382     "preferred master",
383     "prefered master",
384     "local master",
385     "browseable",
386     "browsable",
387     "wins server",
388     "wins support",
389     "csc policy",
390     "strict locking",
391     "preload",
392     "auto services",
393     "lock dir",
394     "lock directory",
395     "pid directory",
396     "socket address",
397     "copy",
398     "include",
399     "available",
400     "volume",
401     "fstype",
402     "panic action",
403     "msdfs root",
404     "host msdfs",
405     "winbind separator"]
406
407
408 def upgrade_smbconf(oldconf, mark):
409     """Remove configuration variables not present in Samba4
410
411     :param oldconf: Old configuration structure
412     :param mark: Whether removed configuration variables should be
413         kept in the new configuration as "samba3:<name>"
414     """
415     data = oldconf.data()
416     newconf = LoadParm()
417
418     for s in data:
419         for p in data[s]:
420             keep = False
421             for k in smbconf_keep:
422                 if smbconf_keep[k] == p:
423                     keep = True
424                     break
425
426             if keep:
427                 newconf.set(s, p, oldconf.get(s, p))
428             elif mark:
429                 newconf.set(s, "samba3:" + p, oldconf.get(s, p))
430
431     return newconf
432
433 SAMBA3_PREDEF_NAMES = {
434         'HKLM': registry.HKEY_LOCAL_MACHINE,
435 }
436
437
438 def import_registry(samba4_registry, samba3_regdb):
439     """Import a Samba 3 registry database into the Samba 4 registry.
440
441     :param samba4_registry: Samba 4 registry handle.
442     :param samba3_regdb: Samba 3 registry database handle.
443     """
444     def ensure_key_exists(keypath):
445         (predef_name, keypath) = keypath.split("/", 1)
446         predef_id = SAMBA3_PREDEF_NAMES[predef_name]
447         keypath = keypath.replace("/", "\\")
448         return samba4_registry.create_key(predef_id, keypath)
449
450     for key in samba3_regdb.keys():
451         key_handle = ensure_key_exists(key)
452         for subkey in samba3_regdb.subkeys(key):
453             ensure_key_exists(subkey)
454         for (value_name, (value_type, value_data)) in samba3_regdb.values(key).items():
455             key_handle.set_value(value_name, value_type, value_data)
456
457
458 def upgrade_from_samba3(samba3, logger, targetdir, session_info=None, useeadb=False):
459     """Upgrade from samba3 database to samba4 AD database
460
461     :param samba3: samba3 object
462     :param logger: Logger object
463     :param targetdir: samba4 database directory
464     :param session_info: Session information
465     """
466     serverrole = samba3.lp.server_role()
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 --dbdir 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 == "ROLE_DOMAIN_BDC" or serverrole == "ROLE_DOMAIN_PDC":
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 == "ROLE_DOMAIN_BDC" or serverrole == "ROLE_DOMAIN_PDC":
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