s4-s3-upgrade: Max/min password age policy is in seconds, not days
[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))
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))
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     if samba3.lp.get("ldapsam:trusted") is None:
473         samba3.lp.set("ldapsam:trusted", "yes")
474
475     # secrets db
476     try:
477         secrets_db = samba3.get_secrets_db()
478     except IOError, e:
479         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)))
480
481     if not domainname:
482         domainname = secrets_db.domains()[0]
483         logger.warning("No workgroup specified in smb.conf file, assuming '%s'",
484                 domainname)
485
486     if not realm:
487         if serverrole == "ROLE_DOMAIN_BDC" or serverrole == "ROLE_DOMAIN_PDC":
488             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.")
489         else:
490             realm = domainname.upper()
491             logger.warning("No realm specified in smb.conf file, assuming '%s'",
492                     realm)
493
494     # Find machine account and password
495     next_rid = 1000
496
497     try:
498         machinepass = secrets_db.get_machine_password(netbiosname)
499     except KeyError:
500         machinepass = None
501
502     # We must close the direct pytdb database before the C code loads it
503     secrets_db.close()
504
505     # Connect to old password backend
506     passdb.set_secrets_dir(samba3.lp.get("private dir"))
507     s3db = samba3.get_sam_db()
508
509     # Get domain sid
510     try:
511         domainsid = passdb.get_global_sam_sid()
512     except passdb.error:
513         raise Exception("Can't find domain sid for '%s', Exiting." % domainname)
514
515     # Get machine account, sid, rid
516     try:
517         machineacct = s3db.getsampwnam('%s$' % netbiosname)
518     except passdb.error:
519         machinerid = None
520         machinesid = None
521     else:
522         machinesid, machinerid = machineacct.user_sid.split()
523
524     # Export account policy
525     logger.info("Exporting account policy")
526     policy = s3db.get_account_policy()
527
528     # Export groups from old passdb backend
529     logger.info("Exporting groups")
530     grouplist = s3db.enum_group_mapping()
531     groupmembers = {}
532     for group in grouplist:
533         sid, rid = group.sid.split()
534         if sid == domainsid:
535             if rid >= next_rid:
536                 next_rid = rid + 1
537
538         # Get members for each group/alias
539         if group.sid_name_use == lsa.SID_NAME_ALIAS:
540             try:
541                 members = s3db.enum_aliasmem(group.sid)
542                 groupmembers[str(group.sid)] = members
543             except passdb.error, e:
544                 logger.warn("Ignoring group '%s' %s listed but then not found: %s",
545                             group.nt_name, group.sid, e)
546                 continue
547         elif group.sid_name_use == lsa.SID_NAME_DOM_GRP:
548             try:
549                 members = s3db.enum_group_members(group.sid)
550                 groupmembers[str(group.sid)] = members
551             except passdb.error, e:
552                 logger.warn("Ignoring group '%s' %s listed but then not found: %s",
553                             group.nt_name, group.sid, e)
554                 continue
555         elif group.sid_name_use == lsa.SID_NAME_WKN_GRP:
556             (group_dom_sid, rid) = group.sid.split()
557             if (group_dom_sid != security.dom_sid(security.SID_BUILTIN)):
558                 logger.warn("Ignoring 'well known' group '%s' (should already be in AD, and have no members)",
559                             group.nt_name)
560                 continue
561             # A number of buggy databases mix up well known groups and aliases.
562             try:
563                 members = s3db.enum_aliasmem(group.sid)
564                 groupmembers[str(group.sid)] = members
565             except passdb.error, e:
566                 logger.warn("Ignoring group '%s' %s listed but then not found: %s",
567                             group.nt_name, group.sid, e)
568                 continue
569         else:
570             logger.warn("Ignoring group '%s' %s with sid_name_use=%d",
571                         group.nt_name, group.sid, group.sid_name_use)
572             continue
573
574     # Export users from old passdb backend
575     logger.info("Exporting users")
576     userlist = s3db.search_users(0)
577     userdata = {}
578     uids = {}
579     admin_user = None
580     for entry in userlist:
581         if machinerid and machinerid == entry['rid']:
582             continue
583         username = entry['account_name']
584         if entry['rid'] < 1000:
585             logger.info("  Skipping wellknown rid=%d (for username=%s)", entry['rid'], username)
586             continue
587         if entry['rid'] >= next_rid:
588             next_rid = entry['rid'] + 1
589
590         user = s3db.getsampwnam(username)
591         acct_type = (user.acct_ctrl & (samr.ACB_NORMAL|samr.ACB_WSTRUST|samr.ACB_SVRTRUST|samr.ACB_DOMTRUST))
592         if (acct_type == samr.ACB_NORMAL or acct_type == samr.ACB_WSTRUST or acct_type == samr.ACB_SVRTRUST):
593             pass
594         elif acct_type == samr.ACB_DOMTRUST:
595             logger.warn("  Skipping inter-domain trust from domain %s, this trust must be re-created as an AD trust" % username[:-1])
596             continue
597         elif acct_type == (samr.ACB_NORMAL|samr.ACB_WSTRUST) and username[-1] == '$':
598             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)
599             user.acct_ctrl = (user.acct_ctrl & ~samr.ACB_NORMAL)
600         else:
601             raise ProvisioningError("""Failed to upgrade due to invalid account %s, account control flags 0x%08X must have exactly one of
602 ACB_NORMAL (N, 0x%08X), ACB_WSTRUST (W 0x%08X), ACB_SVRTRUST (S 0x%08X) or ACB_DOMTRUST (D 0x%08X).
603
604 Please fix this account before attempting to upgrade again
605 """
606                                     % (user.acct_flags, username,
607                                        samr.ACB_NORMAL, samr.ACB_WSTRUST, samr.ACB_SVRTRUST, samr.ACB_DOMTRUST))
608
609         userdata[username] = user
610         try:
611             uids[username] = s3db.sid_to_id(user.user_sid)[0]
612         except passdb.error:
613             try:
614                 uids[username] = pwd.getpwnam(username).pw_uid
615             except KeyError:
616                 pass
617
618         if not admin_user and username.lower() == 'root':
619             admin_user = username
620         if username.lower() == 'administrator':
621             admin_user = username
622
623         try:
624             group_memberships = s3db.enum_group_memberships(user);
625             for group in group_memberships:
626                 if str(group) in groupmembers:
627                     if user.user_sid not in groupmembers[str(group)]:
628                         groupmembers[str(group)].append(user.user_sid)
629                 else:
630                     groupmembers[str(group)] = [user.user_sid];
631         except passdb.error, e:
632             logger.warn("Ignoring group memberships of '%s' %s: %s",
633                         username, user.user_sid, e)
634
635
636     logger.info("Next rid = %d", next_rid)
637
638     # Check for same username/groupname
639     group_names = set([g.nt_name for g in grouplist])
640     user_names = set([u['account_name'] for u in userlist])
641     common_names = group_names.intersection(user_names)
642     if common_names:
643         logger.error("Following names are both user names and group names:")
644         for name in common_names:
645             logger.error("   %s" % name)
646         raise ProvisioningError("Please remove common user/group names before upgrade.")
647
648     # Check for same user sid/group sid
649     group_sids = set([str(g.sid) for g in grouplist])
650     if len(grouplist) != len(group_sids):
651         raise ProvisioningError("Please remove duplicate group sid entries before upgrade.")
652     user_sids = set(["%s-%u" % (domainsid, u['rid']) for u in userlist])
653     if len(userlist) != len(user_sids):
654         raise ProvisioningError("Please remove duplicate user sid entries before upgrade.")
655     common_sids = group_sids.intersection(user_sids)
656     if common_sids:
657         logger.error("Following sids are both user and group sids:")
658         for sid in common_sids:
659             logger.error("   %s" % str(sid))
660         raise ProvisioningError("Please remove duplicate sid entries before upgrade.")
661
662     if serverrole == "ROLE_DOMAIN_BDC" or serverrole == "ROLE_DOMAIN_PDC":
663         dns_backend = "BIND9_DLZ"
664     else:
665         dns_backend = "NONE"
666
667     # Do full provision
668     result = provision(logger, session_info, None,
669                        targetdir=targetdir, realm=realm, domain=domainname,
670                        domainsid=str(domainsid), next_rid=next_rid,
671                        dc_rid=machinerid,
672                        dom_for_fun_level=dsdb.DS_DOMAIN_FUNCTION_2003,
673                        hostname=netbiosname.lower(), machinepass=machinepass,
674                        serverrole=serverrole, samdb_fill=FILL_FULL,
675                        useeadb=useeadb, dns_backend=dns_backend)
676     result.report_logger(logger)
677
678     # Import WINS database
679     logger.info("Importing WINS database")
680
681     samba3_winsdb = None
682     try:
683         samba3_winsdb = samba3.get_wins_db()
684     except IOError, e:
685         logger.warn('Cannot open wins database, Ignoring: %s', str(e))
686
687     if samba3_winsdb:
688         import_wins(Ldb(result.paths.winsdb), samba3_winsdb)
689
690     # Set Account policy
691     logger.info("Importing Account policy")
692     import_sam_policy(result.samdb, policy, logger)
693
694     # Migrate IDMAP database
695     logger.info("Importing idmap database")
696     import_idmap(result.idmap, samba3, logger)
697
698     # Set the s3 context for samba4 configuration
699     new_lp_ctx = s3param.get_context()
700     new_lp_ctx.load(result.lp.configfile)
701     new_lp_ctx.set("private dir", result.lp.get("private dir"))
702     new_lp_ctx.set("state directory", result.lp.get("state directory"))
703     new_lp_ctx.set("lock directory", result.lp.get("lock directory"))
704
705     # Connect to samba4 backend
706     s4_passdb = passdb.PDB(new_lp_ctx.get("passdb backend"))
707
708     # Export groups to samba4 backend
709     logger.info("Importing groups")
710     for g in grouplist:
711         # Ignore uninitialized groups (gid = -1)
712         if g.gid != -1:
713             add_idmap_entry(result.idmap, g.sid, g.gid, "ID_TYPE_GID", logger)
714             add_group_from_mapping_entry(result.samdb, g, logger)
715
716     # Export users to samba4 backend
717     logger.info("Importing users")
718     for username in userdata:
719         if username.lower() == 'administrator' or username.lower() == 'root':
720             continue
721         s4_passdb.add_sam_account(userdata[username])
722         if username in uids:
723             add_idmap_entry(result.idmap, userdata[username].user_sid, uids[username], "ID_TYPE_UID", logger)
724
725     logger.info("Adding users to groups")
726     for g in grouplist:
727         if str(g.sid) in groupmembers:
728             add_users_to_group(result.samdb, g, groupmembers[str(g.sid)], logger)
729
730     # Set password for administrator
731     if admin_user:
732         logger.info("Setting password for administrator")
733         admin_userdata = s4_passdb.getsampwnam("administrator")
734         admin_userdata.nt_passwd = userdata[admin_user].nt_passwd
735         if userdata[admin_user].lanman_passwd:
736             admin_userdata.lanman_passwd = userdata[admin_user].lanman_passwd
737         admin_userdata.pass_last_set_time = userdata[admin_user].pass_last_set_time
738         if userdata[admin_user].pw_history:
739             admin_userdata.pw_history = userdata[admin_user].pw_history
740         s4_passdb.update_sam_account(admin_userdata)
741         logger.info("Administrator password has been set to password of user '%s'", admin_user)
742
743     # FIXME: import_registry(registry.Registry(), samba3.get_registry())
744     # FIXME: shares