class LDAPBase(object): def __init__(self, host, creds, lp, two=False, quiet=False, descriptor=False, sort_aces=False, verbose=False, view="section", base="", scope="SUB", outf=sys.stdout, errf=sys.stderr, skip_missing_dn=True): ldb_options = [] samdb_url = host if not "://" in host: if os.path.isfile(host): samdb_url = "tdb://%s" % host else: samdb_url = "ldap://%s" % host # use 'paged_search' module when connecting remotely if samdb_url.lower().startswith("ldap://"): ldb_options = ["modules:paged_searches"] self.outf = outf self.errf = errf self.ldb = Ldb(url=samdb_url, credentials=creds, lp=lp, options=ldb_options) self.search_base = base self.search_scope = scope self.two_domains = two self.quiet = quiet self.descriptor = descriptor self.sort_aces = sort_aces self.view = view self.verbose = verbose self.host = host self.skip_missing_dn = skip_missing_dn self.base_dn = str(self.ldb.get_default_basedn()) self.root_dn = str(self.ldb.get_root_basedn()) self.config_dn = str(self.ldb.get_config_basedn()) self.schema_dn = str(self.ldb.get_schema_basedn()) self.domain_netbios = self.find_netbios() self.server_names = self.find_servers() self.domain_name = re.sub("[Dd][Cc]=", "", self.base_dn).replace(",", ".") self.domain_sid = self.find_domain_sid() self.get_sid_map() # # Log some domain controller specific place-holers that are being used # when compare content of two DCs. Uncomment for DEBUG purposes. if self.two_domains and not self.quiet: self.outf.write("\n* Place-holders for %s:\n" % self.host) self.outf.write(4 * " " + "${DOMAIN_DN} => %s\n" % self.base_dn) self.outf.write(4 * " " + "${DOMAIN_NETBIOS} => %s\n" % self.domain_netbios) self.outf.write(4 * " " + "${SERVER_NAME} => %s\n" % self.server_names) self.outf.write(4 * " " + "${DOMAIN_NAME} => %s\n" % self.domain_name) def find_domain_sid(self): res = self.ldb.search(base=self.base_dn, expression="(objectClass=*)", scope=SCOPE_BASE) return ndr_unpack(security.dom_sid, res[0]["objectSid"][0]) def find_servers(self): """ """ res = self.ldb.search(base="OU=Domain Controllers,%s" % self.base_dn, scope=SCOPE_SUBTREE, expression="(objectClass=computer)", attrs=["cn"]) assert len(res) > 0 srv = [] for x in res: srv.append(x["cn"][0]) return srv def find_netbios(self): res = self.ldb.search(base="CN=Partitions,%s" % self.config_dn, scope=SCOPE_SUBTREE, attrs=["nETBIOSName"]) assert len(res) > 0 for x in res: if "nETBIOSName" in x.keys(): return x["nETBIOSName"][0] def object_exists(self, object_dn): res = None try: res = self.ldb.search(base=object_dn, scope=SCOPE_BASE) except LdbError, (enum, estr): if enum == ERR_NO_SUCH_OBJECT: return False raise return len(res) == 1
class LDAPBase(object): def __init__(self, host, creds, lp, two=False, quiet=False, descriptor=False, sort_aces=False, verbose=False, view="section", base="", scope="SUB"): ldb_options = [] samdb_url = host if not "://" in host: if os.path.isfile(host): samdb_url = "tdb://%s" % host else: samdb_url = "ldap://%s" % host # use 'paged_search' module when connecting remotely if samdb_url.lower().startswith("ldap://"): ldb_options = ["modules:paged_searches"] self.ldb = Ldb(url=samdb_url, credentials=creds, lp=lp, options=ldb_options) self.search_base = base self.search_scope = scope self.two_domains = two self.quiet = quiet self.descriptor = descriptor self.sort_aces = sort_aces self.view = view self.verbose = verbose self.host = host self.base_dn = str(self.ldb.get_default_basedn()) self.root_dn = str(self.ldb.get_root_basedn()) self.config_dn = str(self.ldb.get_config_basedn()) self.schema_dn = str(self.ldb.get_schema_basedn()) self.domain_netbios = self.find_netbios() self.server_names = self.find_servers() self.domain_name = re.sub("[Dd][Cc]=", "", self.base_dn).replace(",", ".") self.domain_sid = self.find_domain_sid() self.get_guid_map() self.get_sid_map() # # Log some domain controller specific place-holers that are being used # when compare content of two DCs. Uncomment for DEBUG purposes. if self.two_domains and not self.quiet: self.outf.write("\n* Place-holders for %s:\n" % self.host) self.outf.write(4*" " + "${DOMAIN_DN} => %s\n" % self.base_dn) self.outf.write(4*" " + "${DOMAIN_NETBIOS} => %s\n" % self.domain_netbios) self.outf.write(4*" " + "${SERVER_NAME} => %s\n" % self.server_names) self.outf.write(4*" " + "${DOMAIN_NAME} => %s\n" % self.domain_name) def find_domain_sid(self): res = self.ldb.search(base=self.base_dn, expression="(objectClass=*)", scope=SCOPE_BASE) return ndr_unpack(security.dom_sid,res[0]["objectSid"][0]) def find_servers(self): """ """ res = self.ldb.search(base="OU=Domain Controllers,%s" % self.base_dn, \ scope=SCOPE_SUBTREE, expression="(objectClass=computer)", attrs=["cn"]) assert len(res) > 0 srv = [] for x in res: srv.append(x["cn"][0]) return srv def find_netbios(self): res = self.ldb.search(base="CN=Partitions,%s" % self.config_dn, \ scope=SCOPE_SUBTREE, attrs=["nETBIOSName"]) assert len(res) > 0 for x in res: if "nETBIOSName" in x.keys(): return x["nETBIOSName"][0] def object_exists(self, object_dn): res = None try: res = self.ldb.search(base=object_dn, scope=SCOPE_BASE) except LdbError, (enum, estr): if enum == ERR_NO_SUCH_OBJECT: return False raise return len(res) == 1
class LDAPBase(object): def __init__(self, host, creds, lp, two=False, quiet=False, descriptor=False, sort_aces=False, verbose=False, view="section", base="", scope="SUB", outf=sys.stdout, errf=sys.stderr, skip_missing_dn=True): ldb_options = [] samdb_url = host if "://" not in host: if os.path.isfile(host): samdb_url = "tdb://%s" % host else: samdb_url = "ldap://%s" % host # use 'paged_search' module when connecting remotely if samdb_url.lower().startswith("ldap://"): ldb_options = ["modules:paged_searches"] self.outf = outf self.errf = errf self.ldb = Ldb(url=samdb_url, credentials=creds, lp=lp, options=ldb_options) self.search_base = base self.search_scope = scope self.two_domains = two self.quiet = quiet self.descriptor = descriptor self.sort_aces = sort_aces self.view = view self.verbose = verbose self.host = host self.skip_missing_dn = skip_missing_dn self.base_dn = str(self.ldb.get_default_basedn()) self.root_dn = str(self.ldb.get_root_basedn()) self.config_dn = str(self.ldb.get_config_basedn()) self.schema_dn = str(self.ldb.get_schema_basedn()) self.domain_netbios = self.find_netbios() self.server_names = self.find_servers() self.domain_name = re.sub("[Dd][Cc]=", "", self.base_dn).replace(",", ".") self.domain_sid = self.find_domain_sid() self.get_sid_map() # # Log some domain controller specific place-holers that are being used # when compare content of two DCs. Uncomment for DEBUG purposes. if self.two_domains and not self.quiet: self.outf.write("\n* Place-holders for %s:\n" % self.host) self.outf.write(4 * " " + "${DOMAIN_DN} => %s\n" % self.base_dn) self.outf.write(4 * " " + "${DOMAIN_NETBIOS} => %s\n" % self.domain_netbios) self.outf.write(4 * " " + "${SERVER_NAME} => %s\n" % self.server_names) self.outf.write(4 * " " + "${DOMAIN_NAME} => %s\n" % self.domain_name) def find_domain_sid(self): res = self.ldb.search(base=self.base_dn, expression="(objectClass=*)", scope=SCOPE_BASE) return ndr_unpack(security.dom_sid, res[0]["objectSid"][0]) def find_servers(self): """ """ res = self.ldb.search(base="OU=Domain Controllers,%s" % self.base_dn, scope=SCOPE_SUBTREE, expression="(objectClass=computer)", attrs=["cn"]) assert len(res) > 0 return [str(x["cn"][0]) for x in res] def find_netbios(self): res = self.ldb.search(base="CN=Partitions,%s" % self.config_dn, scope=SCOPE_SUBTREE, attrs=["nETBIOSName"]) assert len(res) > 0 for x in res: if "nETBIOSName" in x: return x["nETBIOSName"][0] def object_exists(self, object_dn): res = None try: res = self.ldb.search(base=object_dn, scope=SCOPE_BASE) except LdbError as e2: (enum, estr) = e2.args if enum == ERR_NO_SUCH_OBJECT: return False raise return len(res) == 1 def delete_force(self, object_dn): try: self.ldb.delete(object_dn) except Ldb.LdbError as e: assert "No such object" in str(e) def get_attribute_name(self, key): """ Returns the real attribute name It resolved ranged results e.g. member;range=0-1499 """ m = RE_RANGED_RESULT.match(key) if m is None: return key return m.group(1) def get_attribute_values(self, object_dn, key, vals): """ Returns list with all attribute values It resolved ranged results e.g. member;range=0-1499 """ m = RE_RANGED_RESULT.match(key) if m is None: # no range, just return the values return vals attr = m.group(1) hi = int(m.group(3)) # get additional values in a loop # until we get a response with '*' at the end while True: n = "%s;range=%d-*" % (attr, hi + 1) res = self.ldb.search(base=object_dn, scope=SCOPE_BASE, attrs=[n]) assert len(res) == 1 res = dict(res[0]) del res["dn"] fm = None fvals = None for key in res: m = RE_RANGED_RESULT.match(key) if m is None: continue if m.group(1) != attr: continue fm = m fvals = list(res[key]) break if fm is None: break vals.extend(fvals) if fm.group(3) == "*": # if we got "*" we're done break assert int(fm.group(2)) == hi + 1 hi = int(fm.group(3)) return vals def get_attributes(self, object_dn): """ Returns dict with all default visible attributes """ res = self.ldb.search(base=object_dn, scope=SCOPE_BASE, attrs=["*"]) assert len(res) == 1 res = dict(res[0]) # 'Dn' element is not iterable and we have it as 'distinguishedName' del res["dn"] attributes = {} for key, vals in res.items(): name = self.get_attribute_name(key) # sort vals and return a list, help to compare vals = sorted(vals) attributes[name] = self.get_attribute_values(object_dn, key, vals) return attributes def get_descriptor_sddl(self, object_dn): res = self.ldb.search(base=object_dn, scope=SCOPE_BASE, attrs=["nTSecurityDescriptor"]) desc = res[0]["nTSecurityDescriptor"][0] desc = ndr_unpack(security.descriptor, desc) return desc.as_sddl(self.domain_sid) def guid_as_string(self, guid_blob): """ Translate binary representation of schemaIDGUID to standard string representation. @gid_blob: binary schemaIDGUID """ blob = "%s" % guid_blob stops = [4, 2, 2, 2, 6] index = 0 res = "" x = 0 while x < len(stops): tmp = "" y = 0 while y < stops[x]: c = hex(ord(blob[index])).replace("0x", "") c = [None, "0" + c, c][len(c)] if 2 * index < len(blob): tmp = c + tmp else: tmp += c index += 1 y += 1 res += tmp + " " x += 1 assert index == len(blob) return res.strip().replace(" ", "-") def get_sid_map(self): """ Build dictionary that maps GUID to 'name' attribute found in Schema or Extended-Rights. """ self.sid_map = {} res = self.ldb.search(base=self.base_dn, expression="(objectSid=*)", scope=SCOPE_SUBTREE, attrs=["objectSid", "sAMAccountName"]) for item in res: try: self.sid_map["%s" % ndr_unpack(security.dom_sid, item["objectSid"][0])] = str(item["sAMAccountName"][0]) except KeyError: pass
class LDAPBase(object): def __init__(self, host, creds, lp, two=False, quiet=False, descriptor=False, sort_aces=False, verbose=False, view="section", base="", scope="SUB", outf=sys.stdout, errf=sys.stderr, skip_missing_dn=True): ldb_options = [] samdb_url = host if not "://" in host: if os.path.isfile(host): samdb_url = "tdb://%s" % host else: samdb_url = "ldap://%s" % host # use 'paged_search' module when connecting remotely if samdb_url.lower().startswith("ldap://"): ldb_options = ["modules:paged_searches"] self.outf = outf self.errf = errf self.ldb = Ldb(url=samdb_url, credentials=creds, lp=lp, options=ldb_options) self.search_base = base self.search_scope = scope self.two_domains = two self.quiet = quiet self.descriptor = descriptor self.sort_aces = sort_aces self.view = view self.verbose = verbose self.host = host self.skip_missing_dn = skip_missing_dn self.base_dn = str(self.ldb.get_default_basedn()) self.root_dn = str(self.ldb.get_root_basedn()) self.config_dn = str(self.ldb.get_config_basedn()) self.schema_dn = str(self.ldb.get_schema_basedn()) self.domain_netbios = self.find_netbios() self.server_names = self.find_servers() self.domain_name = re.sub("[Dd][Cc]=", "", self.base_dn).replace(",", ".") self.domain_sid = self.find_domain_sid() self.get_sid_map() # # Log some domain controller specific place-holers that are being used # when compare content of two DCs. Uncomment for DEBUG purposes. if self.two_domains and not self.quiet: self.outf.write("\n* Place-holders for %s:\n" % self.host) self.outf.write(4*" " + "${DOMAIN_DN} => %s\n" % self.base_dn) self.outf.write(4*" " + "${DOMAIN_NETBIOS} => %s\n" % self.domain_netbios) self.outf.write(4*" " + "${SERVER_NAME} => %s\n" % self.server_names) self.outf.write(4*" " + "${DOMAIN_NAME} => %s\n" % self.domain_name) def find_domain_sid(self): res = self.ldb.search(base=self.base_dn, expression="(objectClass=*)", scope=SCOPE_BASE) return ndr_unpack(security.dom_sid,res[0]["objectSid"][0]) def find_servers(self): """ """ res = self.ldb.search(base="OU=Domain Controllers,%s" % self.base_dn, scope=SCOPE_SUBTREE, expression="(objectClass=computer)", attrs=["cn"]) assert len(res) > 0 srv = [] for x in res: srv.append(x["cn"][0]) return srv def find_netbios(self): res = self.ldb.search(base="CN=Partitions,%s" % self.config_dn, scope=SCOPE_SUBTREE, attrs=["nETBIOSName"]) assert len(res) > 0 for x in res: if "nETBIOSName" in x.keys(): return x["nETBIOSName"][0] def object_exists(self, object_dn): res = None try: res = self.ldb.search(base=object_dn, scope=SCOPE_BASE) except LdbError as e: (enum, estr) = e.args if enum == ERR_NO_SUCH_OBJECT: return False raise return len(res) == 1 def delete_force(self, object_dn): try: self.ldb.delete(object_dn) except Ldb.LdbError as e: assert "No such object" in str(e) def get_attribute_name(self, key): """ Returns the real attribute name It resolved ranged results e.g. member;range=0-1499 """ r = re.compile("^([^;]+);range=(\d+)-(\d+|\*)$") m = r.match(key) if m is None: return key return m.group(1) def get_attribute_values(self, object_dn, key, vals): """ Returns list with all attribute values It resolved ranged results e.g. member;range=0-1499 """ r = re.compile("^([^;]+);range=(\d+)-(\d+|\*)$") m = r.match(key) if m is None: # no range, just return the values return vals attr = m.group(1) hi = int(m.group(3)) # get additional values in a loop # until we get a response with '*' at the end while True: n = "%s;range=%d-*" % (attr, hi + 1) res = self.ldb.search(base=object_dn, scope=SCOPE_BASE, attrs=[n]) assert len(res) == 1 res = dict(res[0]) del res["dn"] fm = None fvals = None for key in res.keys(): m = r.match(key) if m is None: continue if m.group(1) != attr: continue fm = m fvals = list(res[key]) break if fm is None: break vals.extend(fvals) if fm.group(3) == "*": # if we got "*" we're done break assert int(fm.group(2)) == hi + 1 hi = int(fm.group(3)) return vals def get_attributes(self, object_dn): """ Returns dict with all default visible attributes """ res = self.ldb.search(base=object_dn, scope=SCOPE_BASE, attrs=["*"]) assert len(res) == 1 res = dict(res[0]) # 'Dn' element is not iterable and we have it as 'distinguishedName' del res["dn"] for key in res.keys(): vals = list(res[key]) del res[key] name = self.get_attribute_name(key) res[name] = self.get_attribute_values(object_dn, key, vals) return res def get_descriptor_sddl(self, object_dn): res = self.ldb.search(base=object_dn, scope=SCOPE_BASE, attrs=["nTSecurityDescriptor"]) desc = res[0]["nTSecurityDescriptor"][0] desc = ndr_unpack(security.descriptor, desc) return desc.as_sddl(self.domain_sid) def guid_as_string(self, guid_blob): """ Translate binary representation of schemaIDGUID to standard string representation. @gid_blob: binary schemaIDGUID """ blob = "%s" % guid_blob stops = [4, 2, 2, 2, 6] index = 0 res = "" x = 0 while x < len(stops): tmp = "" y = 0 while y < stops[x]: c = hex(ord(blob[index])).replace("0x", "") c = [None, "0" + c, c][len(c)] if 2 * index < len(blob): tmp = c + tmp else: tmp += c index += 1 y += 1 res += tmp + " " x += 1 assert index == len(blob) return res.strip().replace(" ", "-") def get_sid_map(self): """ Build dictionary that maps GUID to 'name' attribute found in Schema or Extended-Rights. """ self.sid_map = {} res = self.ldb.search(base=self.base_dn, expression="(objectSid=*)", scope=SCOPE_SUBTREE, attrs=["objectSid", "sAMAccountName"]) for item in res: try: self.sid_map["%s" % ndr_unpack(security.dom_sid, item["objectSid"][0])] = item["sAMAccountName"][0] except KeyError: pass