From 2a3f5c04bdfac49d509fe5761cf7371965d21089 Mon Sep 17 00:00:00 2001 From: Amitay Isaacs Date: Thu, 18 Aug 2011 15:21:32 +1000 Subject: s3upgrade: Add idmap migration, users/groups import Added users/groups import from s3 using python wrapper for passdb. Fix idmap entries for users/groups when migrating from s3 idmap. Pair-Programmed-With: Andrew Bartlett Signed-off-by: Andrew Bartlett --- source4/scripting/python/samba/upgrade.py | 233 +++++++++++++++++++++++++----- 1 file changed, 193 insertions(+), 40 deletions(-) diff --git a/source4/scripting/python/samba/upgrade.py b/source4/scripting/python/samba/upgrade.py index 351581c6ee..38e6ed87bb 100644 --- a/source4/scripting/python/samba/upgrade.py +++ b/source4/scripting/python/samba/upgrade.py @@ -29,6 +29,11 @@ from samba.param import LoadParm from samba.provision import provision, FILL_FULL from samba.samba3 import passdb from samba.samba3 import param as s3param +from samba.dcerpc import lsa +from samba.dcerpc.security import dom_sid +from samba import dsdb +from samba.ndr import ndr_pack + def import_sam_policy(samldb, policy, dn): """Import a Samba 3 policy database.""" @@ -145,29 +150,131 @@ def import_sam_group(samldb, sid, gid, sid_name_use, nt_name, comment, domaindn) }) -def import_idmap(samdb,samba3_idmap,domaindn): +def add_idmap_entry(idmapdb, sid, xid, xid_type, logger): + """Create idmap entry""" + + # First try to see if we already have this entry + found = False + try: + msg = idmapdb.search(expression='objectSid=%s' % str(sid)) + if msg.count == 1: + found = True + except Exception, e: + raise e + + if found: + print msg.count + print dir(msg) + try: + m = ldb.Message() + m.dn = ldb.Dn(idmapdb, msg[0]['dn']) + m['xidNumber'] = ldb.MessageElement(str(xid), ldb.FLAG_MOD_REPLACE, 'xidNumber') + m['type'] = ldb.MessageElement(xid_type, ldb.FLAG_MOD_REPLACE, 'type') + idmapdb.modify(m) + except ldb.LdbError, e: + logger.warn('Could not modify idmap entry for sid=%s, id=%s, type=%s (%s)', + str(sid), str(xid), xid_type, str(e)) + except Exception, e: + raise e + else: + try: + idmapdb.add({"dn": "CN=%s" % str(sid), + "cn": str(sid), + "objectClass": "sidMap", + "objectSid": ndr_pack(sid), + "type": xid_type, + "xidNumber": str(xid)}) + except ldb.LdbError, e: + logger.warn('Could not add idmap entry for sid=%s, id=%s, type=%s (%s)', + str(sid), str(xid), xid_type, str(e)) + except Exception, e: + raise e + + +def import_idmap(idmapdb, samba3_idmap, logger): """Import idmap data. - :param samdb: SamDB handle. :param samba3_idmap: Samba 3 IDMAP database to import from - :param domaindn: Domain DN. """ - samdb.add({ - "dn": domaindn, - "userHwm": str(samba3_idmap.get_user_hwm()), - "groupHwm": str(samba3_idmap.get_group_hwm())}) - for uid in samba3_idmap.uids(): - samdb.add({"dn": "SID=%s,%s" % (samba3_idmap.get_user_sid(uid), domaindn), - "SID": samba3_idmap.get_user_sid(uid), - "type": "user", - "unixID": str(uid)}) + currentxid = max(samba3_idmap.get_user_hwm(), samba3_idmap.get_group_hwm()) + lowerbound = currentxid + # FIXME: upperbound + + m = ldb.Message() + m.dn = ldb.Dn(idmapdb, 'CN=CONFIG') + m['lowerbound'] = ldb.MessageElement(str(lowerbound), ldb.FLAG_MOD_REPLACE, 'lowerBound') + m['xidNumber'] = ldb.MessageElement(str(currentxid), ldb.FLAG_MOD_REPLACE, 'xidNumber') + idmapdb.modify(m) + + for id_type, xid in samba3_idmap.ids(): + if id_type == 'UID': + xid_type = 'ID_TYPE_UID' + elif id_type == 'GID': + xid_type = 'ID_TYPE_GID' + else: + logger.warn('Wrong type of entry in idmap (%s), Ignoring', id_type) + continue + + sid = samba3_idmap.get_sid(xid, id_type) + add_idmap_entry(idmapdb, dom_sid(sid), xid, xid_type, logger) + + +def add_group_from_mapping_entry(samdb, groupmap, logger): + """Add or modify group from group mapping entry""" + + # First try to see if we already have this entry + try: + msg = samdb.search(base='' % str(groupmap.sid), scope=ldb.SCOPE_BASE) + found = True + except ldb.LdbError, (ecode, emsg): + if ecode == ldb.ERR_NO_SUCH_OBJECT: + found = False + else: + raise ldb.LdbError(ecode, emsg) + except Exception, e: + raise e + + if found: + logger.warn('Group already exists sid=%s, groupname=%s existing_groupname=%s, Ignoring.', + str(groupmap.sid), groupmap.nt_name, msg[0]['sAMAccountName'][0]) + else: + if groupmap.sid_name_use == lsa.SID_NAME_WKN_GRP: + return + + m = ldb.Message() + m.dn = ldb.Dn(samdb, "CN=%s,CN=Users,%s" % (groupmap.nt_name, samdb.get_default_basedn())) + m['a01'] = ldb.MessageElement(groupmap.nt_name, ldb.FLAG_MOD_ADD, 'cn') + m['a02'] = ldb.MessageElement('group', ldb.FLAG_MOD_ADD, 'objectClass') + m['a03'] = ldb.MessageElement(ndr_pack(groupmap.sid), ldb.FLAG_MOD_ADD, 'objectSid') + m['a04'] = ldb.MessageElement(groupmap.comment, ldb.FLAG_MOD_ADD, 'description') + m['a05'] = ldb.MessageElement(groupmap.nt_name, ldb.FLAG_MOD_ADD, 'sAMAccountName') + + if groupmap.sid_name_use == lsa.SID_NAME_ALIAS: + m['a06'] = ldb.MessageElement(str(dsdb.GTYPE_SECURITY_DOMAIN_LOCAL_GROUP), ldb.FLAG_MOD_ADD, 'groupType') + + try: + samdb.add(m, controls=["relax:0"]) + except ldb.LdbError, e: + logger.warn('Could not add group name=%s (%s)', groupmap.nt_name, str(e)) + except Exception, e: + raise(e) + + +def add_users_to_group(samdb, group, members): + """Add user/member to group/alias""" - for gid in samba3_idmap.uids(): - samdb.add({"dn": "SID=%s,%s" % (samba3_idmap.get_group_sid(gid), domaindn), - "SID": samba3_idmap.get_group_sid(gid), - "type": "group", - "unixID": str(gid)}) + for member_sid in members: + m = ldb.Message() + m.dn = ldb.Dn(samdb, "" % str(member_sid), ldb.FLAG_MOD_REPLACE, 'member') + + try: + samdb.modify(m) + except ldb.LdbError, e: + logger.warn("Could not add member to group '%s'", groupmap.nt_name) + except Exception, e: + raise(e) def import_wins(samba4_winsdb, samba3_winsdb): @@ -428,69 +535,115 @@ def upgrade_from_samba3(samba3, logger, session_info, smbconf, targetdir): # We must close the direct pytdb database before the C code loads it secrets_db.close() - # We must load the group mapping into memory before the passdb code touches it - groupdb = samba3.get_groupmapping_db() - for sid in groupdb.groupsids(): - (gid, sid_name_use, nt_name, comment) = groupdb.get_group(sid) - # FIXME: import_sam_group(samdb, sid, gid, sid_name_use, nt_name, comment, domaindn) - groupdb.close() - passdb.set_secrets_dir(samba3.libdir) + # Get domain sid try: - domainsid = str(passdb.get_global_sam_sid()) + domainsid = passdb.get_global_sam_sid() except: - pass + raise Exception("Can't find domain sid for '%s', Exiting." % domainname) + # Get machine account, sid, rid try: machineacct = old_passdb.getsampwnam('%s$' % netbiosname) machinesid, machinerid = machineacct.user_sid.split() except: pass - if domainsid is None: - logger.warning("Can't find domain secrets for '%s'; using random SID", - domainname) + # Connect to old password backend + old_passdb = passdb.PDB(oldconf.get('passdb backend')) + + # Import groups from old passdb backend + logger.info("Exporting groups") + grouplist = old_passdb.enum_group_mapping() + groupmembers = {} + for group in grouplist: + sid, rid = group.sid.split() + if sid == domainsid: + if rid >= next_rid: + next_rid = rid + 1 + + # Get members for each group/alias + if group.sid_name_use == lsa.SID_NAME_ALIAS or group.sid_name_use == lsa.SID_NAME_WKN_GRP: + members = old_passdb.enum_aliasmem(group.sid) + elif group.sid_name_use == lsa.SID_NAME_DOM_GRP: + try: + members = old_passdb.enum_group_members(group.sid) + except: + continue + else: + logger.warn("Ignoring group '%s' with sid_name_use=%d", + group.nt_name, group.sid_name_use) + continue + groupmembers[group.nt_name] = members + # Import users from old passdb backend - old_passdb = passdb.PDB(oldconf.get('passdb backend')) + logger.info("Exporting users") userlist = old_passdb.search_users(0) userdata = {} + uids = {} for entry in userlist: if machinerid and machinerid == entry['rid']: continue username = entry['account_name'] if entry['rid'] < 1000: - print("Skipping wellknown rid=%d (for username=%s)\n" % (entry['rid'], username)) + logger.info(" Skipping wellknown rid=%d (for username=%s)", entry['rid'], username) continue if entry['rid'] >= next_rid: next_rid = entry['rid'] + 1 userdata[username] = old_passdb.getsampwnam(username) + try: + uids[username] = old_passdb.sid_to_id(userdata[username].user_sid)[0] + except: + try: + uids[username] = pwd.getpwnam(username).pw_uid + except: + pass + + logger.info("Next rid = %d", next_rid) # Do full provision result = provision(logger, session_info, None, targetdir=targetdir, realm=realm, domain=domainname, - domainsid=domainsid, next_rid=next_rid, + domainsid=str(domainsid), next_rid=next_rid, dc_rid=machinerid, hostname=netbiosname, machinepass=machinepass, serverrole=serverrole, samdb_fill=FILL_FULL) + logger.info("Import WINS") import_wins(Ldb(result.paths.winsdb), samba3.get_wins_db()) - # FIXME: import_registry(registry.Registry(), samba3.get_registry()) - - # FIXME: import_idmap(samdb,samba3.get_idmap_db(),domaindn) - - # FIXME: Aliases - - # Export users to samba4 backend new_smbconf = result.lp.configfile newconf = s3param.get_context() newconf.load(new_smbconf) + # Migrate idmap + logger.info("Migrating idmap database") + import_idmap(result.idmap, samba3.get_idmap_db(), logger) + + # Connect to samba4 backend new_passdb = passdb.PDB('samba4') + # Export groups to samba4 backend + logger.info("Importing groups") + for g in grouplist: + # Ignore uninitialized groups (gid = -1) + if g.gid != 0xffffffff: + add_idmap_entry(result.idmap, g.sid, g.gid, "GID", logger) + add_group_from_mapping_entry(result.samdb, g, logger) + + # Export users to samba4 backend + logger.info("Importing users") for username in userdata: - print "adding user %s" % username new_passdb.add_sam_account(userdata[username]) + if username in uids: + add_idmap_entry(result.idmap, userdata[username].user_sid, uids[username], "UID", logger) + + logger.info("Adding users to groups") + for g in grouplist: + if g.nt_name in groupmembers: + add_users_to_group(result.samdb, g, groupmembers[g.nt_name]) + + # FIXME: import_registry(registry.Registry(), samba3.get_registry()) -- cgit