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