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