s4-samba-tool: Fix samba-tool fsmo seize
[kai/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 = int(-min_pw_age_unix * (1e7 * 60 * 60 * 24))
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 or max_pw_age_unix == 0:
69         max_pw_age_nt = -0x8000000000000000
70     else:
71         max_pw_age_nt = int(-max_pw_age_unix * (1e7 * 60 * 60 * 24))
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 (ID_TYPE_UID/ID_TYPE_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.debug("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             try:
538                 members = s3db.enum_aliasmem(group.sid)
539             except passdb.error:
540                 logger.warn("Ignoring group '%s' %s listed but then not found: %s",
541                             group.nt_name, group.sid, passdb.error)
542                 continue
543         elif group.sid_name_use == lsa.SID_NAME_DOM_GRP:
544             try:
545                 members = s3db.enum_group_members(group.sid)
546             except passdb.error:
547                 logger.warn("Ignoring group '%s' %s listed but then not found: %s",
548                             group.nt_name, group.sid, passdb.error)
549                 continue
550             groupmembers[group.nt_name] = members
551         elif group.sid_name_use == lsa.SID_NAME_WKN_GRP:
552             (group_dom_sid, rid) = group.sid.split()
553             if (group_dom_sid != security.dom_sid(security.SID_BUILTIN)):
554                 logger.warn("Ignoring 'well known' group '%s' (should already be in AD, and have no members)",
555                             group.nt_name)
556                 continue
557             # A number of buggy databases mix up well known groups and aliases.
558             try:
559                 members = s3db.enum_aliasmem(group.sid)
560             except passdb.error:
561                 logger.warn("Ignoring group '%s' %s listed but then not found: %s",
562                             group.nt_name, group.sid, passdb.error)
563                 continue
564         else:
565             logger.warn("Ignoring group '%s' with sid_name_use=%d",
566                         group.nt_name, group.sid_name_use)
567             continue
568
569     # Export users from old passdb backend
570     logger.info("Exporting users")
571     userlist = s3db.search_users(0)
572     userdata = {}
573     uids = {}
574     admin_user = None
575     for entry in userlist:
576         if machinerid and machinerid == entry['rid']:
577             continue
578         username = entry['account_name']
579         if entry['rid'] < 1000:
580             logger.info("  Skipping wellknown rid=%d (for username=%s)", entry['rid'], username)
581             continue
582         if entry['rid'] >= next_rid:
583             next_rid = entry['rid'] + 1
584
585         user = s3db.getsampwnam(username)
586         acct_type = (user.acct_ctrl & (samr.ACB_NORMAL|samr.ACB_WSTRUST|samr.ACB_SVRTRUST|samr.ACB_DOMTRUST))
587         if (acct_type == samr.ACB_NORMAL or acct_type == samr.ACB_WSTRUST or acct_type == samr.ACB_SVRTRUST):
588             pass
589         elif acct_type == samr.ACB_DOMTRUST:
590             logger.warn("  Skipping inter-domain trust from domain %s, this trust must be re-created as an AD trust" % username[:-1])
591             continue
592         elif acct_type == (samr.ACB_NORMAL|samr.ACB_WSTRUST) and username[-1] == '$':
593             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)
594             user.acct_ctrl = (user.acct_ctrl & ~samr.ACB_NORMAL)
595         else:
596             raise ProvisioningError("""Failed to upgrade due to invalid account %s, account control flags 0x%08X must have exactly one of
597 ACB_NORMAL (N, 0x%08X), ACB_WSTRUST (W 0x%08X), ACB_SVRTRUST (S 0x%08X) or ACB_DOMTRUST (D 0x%08X).
598
599 Please fix this account before attempting to upgrade again
600 """
601                                     % (user.acct_flags, username,
602                                        samr.ACB_NORMAL, samr.ACB_WSTRUST, samr.ACB_SVRTRUST, samr.ACB_DOMTRUST))
603
604         userdata[username] = user
605         try:
606             uids[username] = s3db.sid_to_id(user.user_sid)[0]
607         except passdb.error:
608             try:
609                 uids[username] = pwd.getpwnam(username).pw_uid
610             except KeyError:
611                 pass
612
613         if not admin_user and username.lower() == 'root':
614             admin_user = username
615         if username.lower() == 'administrator':
616             admin_user = username
617
618     logger.info("Next rid = %d", next_rid)
619
620     # Check for same username/groupname
621     group_names = set([g.nt_name for g in grouplist])
622     user_names = set([u['account_name'] for u in userlist])
623     common_names = group_names.intersection(user_names)
624     if common_names:
625         logger.error("Following names are both user names and group names:")
626         for name in common_names:
627             logger.error("   %s" % name)
628         raise ProvisioningError("Please remove common user/group names before upgrade.")
629
630     # Check for same user sid/group sid
631     group_sids = set([str(g.sid) for g in grouplist])
632     if len(grouplist) != len(group_sids):
633         raise ProvisioningError("Please remove duplicate group sid entries before upgrade.")
634     user_sids = set(["%s-%u" % (domainsid, u['rid']) for u in userlist])
635     if len(userlist) != len(user_sids):
636         raise ProvisioningError("Please remove duplicate user sid entries before upgrade.")
637     common_sids = group_sids.intersection(user_sids)
638     if common_sids:
639         logger.error("Following sids are both user and group sids:")
640         for sid in common_sids:
641             logger.error("   %s" % str(sid))
642         raise ProvisioningError("Please remove duplicate sid entries before upgrade.")
643
644     if serverrole == "ROLE_DOMAIN_BDC" or serverrole == "ROLE_DOMAIN_PDC":
645         dns_backend = "BIND9_DLZ"
646     else:
647         dns_backend = "NONE"
648
649     # Do full provision
650     result = provision(logger, session_info, None,
651                        targetdir=targetdir, realm=realm, domain=domainname,
652                        domainsid=str(domainsid), next_rid=next_rid,
653                        dc_rid=machinerid,
654                        dom_for_fun_level=dsdb.DS_DOMAIN_FUNCTION_2003,
655                        hostname=netbiosname.lower(), machinepass=machinepass,
656                        serverrole=serverrole, samdb_fill=FILL_FULL,
657                        useeadb=useeadb, dns_backend=dns_backend)
658     result.report_logger(logger)
659
660     # Import WINS database
661     logger.info("Importing WINS database")
662
663     samba3_winsdb = None
664     try:
665         samba3_winsdb = samba3.get_wins_db()
666     except IOError, e:
667         logger.warn('Cannot open wins database, Ignoring: %s', str(e))
668
669     if samba3_winsdb:
670         import_wins(Ldb(result.paths.winsdb), samba3_winsdb)
671
672     # Set Account policy
673     logger.info("Importing Account policy")
674     import_sam_policy(result.samdb, policy, logger)
675
676     # Migrate IDMAP database
677     logger.info("Importing idmap database")
678     import_idmap(result.idmap, samba3, logger)
679
680     # Set the s3 context for samba4 configuration
681     new_lp_ctx = s3param.get_context()
682     new_lp_ctx.load(result.lp.configfile)
683     new_lp_ctx.set("private dir", result.lp.get("private dir"))
684     new_lp_ctx.set("state directory", result.lp.get("state directory"))
685     new_lp_ctx.set("lock directory", result.lp.get("lock directory"))
686
687     # Connect to samba4 backend
688     s4_passdb = passdb.PDB(new_lp_ctx.get("passdb backend"))
689
690     # Export groups to samba4 backend
691     logger.info("Importing groups")
692     for g in grouplist:
693         # Ignore uninitialized groups (gid = -1)
694         if g.gid != -1:
695             add_idmap_entry(result.idmap, g.sid, g.gid, "ID_TYPE_GID", logger)
696             add_group_from_mapping_entry(result.samdb, g, logger)
697
698     # Export users to samba4 backend
699     logger.info("Importing users")
700     for username in userdata:
701         if username.lower() == 'administrator' or username.lower() == 'root':
702             continue
703         s4_passdb.add_sam_account(userdata[username])
704         if username in uids:
705             add_idmap_entry(result.idmap, userdata[username].user_sid, uids[username], "ID_TYPE_UID", logger)
706
707     logger.info("Adding users to groups")
708     for g in grouplist:
709         if g.nt_name in groupmembers:
710             add_users_to_group(result.samdb, g, groupmembers[g.nt_name], logger)
711
712     # Set password for administrator
713     if admin_user:
714         logger.info("Setting password for administrator")
715         admin_userdata = s4_passdb.getsampwnam("administrator")
716         admin_userdata.nt_passwd = userdata[admin_user].nt_passwd
717         if userdata[admin_user].lanman_passwd:
718             admin_userdata.lanman_passwd = userdata[admin_user].lanman_passwd
719         admin_userdata.pass_last_set_time = userdata[admin_user].pass_last_set_time
720         if userdata[admin_user].pw_history:
721             admin_userdata.pw_history = userdata[admin_user].pw_history
722         s4_passdb.update_sam_account(admin_userdata)
723         logger.info("Administrator password has been set to password of user '%s'", admin_user)
724
725     # FIXME: import_registry(registry.Registry(), samba3.get_registry())
726     # FIXME: shares