#!/usr/bin/python2.6 # # Univention helper script # to remove computer with DC objects from Samba 4 and UDM # # Copyright 2012-2013 Univention GmbH # # http://www.univention.de/ # # All rights reserved. # # The source code of this program is made available # under the terms of the GNU Affero General Public License version 3 # (GNU AGPL V3) as published by the Free Software Foundation. # # Binary versions of this program provided by Univention to you as # well as other copyrighted, protected or trademarked materials like # Logos, graphics, fonts, specific documentations and configurations, # cryptographic keys etc. are subject to a license agreement between # you and Univention and not subject to the GNU AGPL V3. # # In the case you use this program under the terms of the GNU AGPL V3, # the program is provided in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public # License with the Debian GNU/Linux or Univention distribution in file # /usr/share/common-licenses/AGPL-3; if not, see # . from optparse import OptionParser, OptionValueError import sys, os from univention import config_registry import samba from samba.samdb import SamDB from samba.auth import system_session from samba.param import LoadParm import univention.admin.uldap import univention.admin.uexceptions as uexceptions import univention.admin.modules univention.admin.modules.update() import univention.admin.objects import univention.admin.config import traceback SAMBA_DIR = '/var/lib/samba' SAMBA_PRIVATE_DIR = os.path.join(SAMBA_DIR, 'private') def purge_computer_with_DC_objects(ucr, computername, binddn, bindpw): lp = LoadParm() lp.load('/etc/samba/smb.conf') samdb = SamDB(os.path.join(SAMBA_PRIVATE_DIR, "sam.ldb"), session_info=system_session(lp), lp=lp) backlink_attribute_list = ["serverReferenceBL", "frsComputerReferenceBL", "msDFSR-ComputerReferenceBL"] msgs = samdb.search(base=ucr["samba4/ldap/base"], scope=samba.ldb.SCOPE_SUBTREE, expression="(&(objectClass=computer)(sAMAccountName=%s$))" % computername, attrs=backlink_attribute_list) if not msgs: print "Samba 4 computer account '%s' not found." % (computername,) sys.exit(1) answer = raw_input("Really remove %s from Samba 4 and UDM? [y/N]: " % computername) if not answer.lower() in ('y', 'yes'): print "Ok, stopping as requested.\n" sys.exit(2) answer = raw_input("If you are really sure type YES and hit enter: ") if not answer == 'YES': print "The answer was not 'YES', confirmation failed.\n" sys.exit(1) else: print "Ok, continuing as requested.\n" ## remove objects from Samba 4 SAM database obj = msgs[0] for backlink_attribute in backlink_attribute_list: if backlink_attribute in obj: backlink_object = obj[backlink_attribute][0] try: print "Removing %s from SAM database." % (backlink_object,) samdb.delete(backlink_object, ["tree_delete:0"]) except: print >>sys.stderr, "Removal of Samba 4 %s objects %s from Samba 4 SAM database failed." % (backlink_attribute, backlink_object,) print traceback.format_exc() ## Now delete the Samba 4 computer account and sub-objects ## Cannot use tree_delete on isCriticalSystemObject, perform recursive delete like ldbdel code does it: msgs = samdb.search(base=obj.dn, scope=samba.ldb.SCOPE_SUBTREE, attrs=["dn"]) obj_dn_list = [obj.dn for obj in msgs] obj_dn_list.sort(key=len) obj_dn_list.reverse() for obj_dn in obj_dn_list: try: print "Removing %s from SAM database." % (obj_dn,) samdb.delete(obj_dn) except: print >>sys.stderr, "Removal of Samba 4 computer account object %s from Samba 4 SAM database failed." % (obj_dn,) print >>sys.stderr, traceback.format_exc() ## Finally, for consistency remove S4 computer object from UDM try: lo = univention.admin.uldap.access(host=ucr["ldap/master"], base=ucr["ldap/base"], binddn=binddn, bindpw=bindpw, start_tls=2) except Exception, e: print 'authentication error: %s' % str(e) sys.exit(1) computer_filter = "(&(objectClass=univentionHost)(uid=%s$))" % computername result = lo.search(filter=computer_filter, base=ucr["ldap/base"], scope='sub', attr=['univentionObjectType'], unique=1) if result and len(result)>0 and result[0] and len(result[0])>0 and result[0][0]: univentionObjectType = result[0][1]['univentionObjectType'][0] module = univention.admin.modules.get(univentionObjectType) position = univention.admin.uldap.position(ucr["ldap/base"]) univention.admin.modules.init(lo, position, module) co = univention.admin.config.config() filter = univention.admin.filter.expression('name', computername) objs = module.lookup(co, lo, filter, scope='domain', base=position.getDomain(), unique=1) if objs: print "Removing Samba 4 computer account '%s' from Univention Directory Manager" % computername obj = objs[0] try: obj.remove() except univention.admin.uexceptions.ldapError, e: print >>sys.stderr, "Removal of UDM computer account %s via UDM failed (univentionObjectType: %s)." % (computername, univentionObjectType,) sys.exit(1) if univention.admin.objects.wantsCleanup(obj): univention.admin.objects.performCleanup(obj) if __name__ == '__main__': parser = OptionParser("%prog [options] ") # parser.add_option("-v", "--verbose", action="store_true") parser.add_option("--computername", dest="computername", help="Hostname of the Samba computer account to delete") parser.add_option("-U", "--bind_account", dest="bind_account") parser.add_option("-P", "--bind_password", dest="bind_password") opts, args = parser.parse_args() if not opts.computername: parser.print_help() sys.exit(1) ucr = config_registry.ConfigRegistry() ucr.load() if (opts.bind_account and opts.bind_password): machine_secret = '' if os.path.exists('/etc/machine.secret'): with file('/etc/machine.secret') as f: machine_secret = f.read().rstrip('\n') else: print "/etc/machine.secret missing, maybe the system is not joined yet?" sys.exit(1) try: lo = univention.admin.uldap.access(host=ucr["ldap/server/name"], base=ucr["ldap/base"], binddn=ucr["ldap/hostdn"], bindpw=machine_secret, start_tls=0) except Exception, e: print 'authentication error: %s' % str(e) sys.exit(1) user_searchfilter = "(&(|(&(objectClass=posixAccount)(objectClass=shadowAccount))(objectClass=sambaSamAccount))(uid=%s))" % opts.bind_account result = lo.searchDn(filter=user_searchfilter, base=ucr["ldap/base"], scope='sub', unique=1) if result: binddn = result[0] else: print "Cannot determine DN for bind account %s" % opts.bind_account sys.exit(1) bindpw = opts.bind_password elif os.path.exists('/etc/ldap.secret'): binddn = "cn=admin,%s" % ucr["ldap/base"] with file('/etc/ldap.secret') as f: bindpw = f.read().rstrip('\n') else: print "On this system the options --bind_account and --bind_password are required." parser.print_help() sys.exit(1) purge_computer_with_DC_objects(ucr, opts.computername, binddn, bindpw)