132 lines
4.2 KiB
Python
132 lines
4.2 KiB
Python
from win32com.adsi import adsi
|
|
from win32com.adsi.adsicon import *
|
|
from win32com.adsi import adsicon
|
|
import pythoncom, pywintypes, win32security
|
|
|
|
options = None # set to optparse options object
|
|
|
|
ADsTypeNameMap = {}
|
|
|
|
def getADsTypeName(type_val):
|
|
# convert integer type to the 'typename' as known in the headerfiles.
|
|
if not ADsTypeNameMap:
|
|
for n, v in adsicon.__dict__.items():
|
|
if n.startswith("ADSTYPE_"):
|
|
ADsTypeNameMap[v] = n
|
|
return ADsTypeNameMap.get(type_val, hex(type_val))
|
|
|
|
def _guid_from_buffer(b):
|
|
return pywintypes.IID(b, True)
|
|
|
|
def _sid_from_buffer(b):
|
|
return str(pywintypes.SID(b))
|
|
|
|
_null_converter = lambda x: x
|
|
|
|
converters = {
|
|
'objectGUID' : _guid_from_buffer,
|
|
'objectSid' : _sid_from_buffer,
|
|
'instanceType' : getADsTypeName,
|
|
}
|
|
|
|
def log(level, msg, *args):
|
|
if options.verbose >= level:
|
|
print("log:", msg % args)
|
|
|
|
def getGC():
|
|
cont = adsi.ADsOpenObject("GC:", options.user, options.password, 0, adsi.IID_IADsContainer)
|
|
enum = adsi.ADsBuildEnumerator(cont)
|
|
# Only 1 child of the global catalog.
|
|
for e in enum:
|
|
gc = e.QueryInterface(adsi.IID_IDirectorySearch)
|
|
return gc
|
|
return None
|
|
|
|
def print_attribute(col_data):
|
|
prop_name, prop_type, values = col_data
|
|
if values is not None:
|
|
log(2, "property '%s' has type '%s'", prop_name, getADsTypeName(prop_type))
|
|
value = [converters.get(prop_name, _null_converter)(v[0]) for v in values]
|
|
if len(value) == 1:
|
|
value = value[0]
|
|
print(" %s=%r" % (prop_name, value))
|
|
else:
|
|
print(" %s is None" % (prop_name,))
|
|
|
|
def search():
|
|
gc = getGC()
|
|
if gc is None:
|
|
log(0, "Can't find the global catalog")
|
|
return
|
|
|
|
prefs = [(ADS_SEARCHPREF_SEARCH_SCOPE, (ADS_SCOPE_SUBTREE,))]
|
|
hr, statuses = gc.SetSearchPreference(prefs)
|
|
log(3, "SetSearchPreference returned %d/%r", hr, statuses)
|
|
|
|
if options.attributes:
|
|
attributes = options.attributes.split(",")
|
|
else:
|
|
attributes = None
|
|
|
|
h = gc.ExecuteSearch(options.filter, attributes)
|
|
hr = gc.GetNextRow(h)
|
|
while hr != S_ADS_NOMORE_ROWS:
|
|
print("-- new row --")
|
|
if attributes is None:
|
|
# Loop over all columns returned
|
|
while 1:
|
|
col_name = gc.GetNextColumnName(h)
|
|
if col_name is None:
|
|
break
|
|
data = gc.GetColumn(h, col_name)
|
|
print_attribute(data)
|
|
else:
|
|
# loop over attributes specified.
|
|
for a in attributes:
|
|
try:
|
|
data = gc.GetColumn(h, a)
|
|
print_attribute(data)
|
|
except adsi.error as details:
|
|
if details[0] != E_ADS_COLUMN_NOT_SET:
|
|
raise
|
|
print_attribute( (a, None, None) )
|
|
hr = gc.GetNextRow(h)
|
|
gc.CloseSearchHandle(h)
|
|
|
|
def main():
|
|
global options
|
|
from optparse import OptionParser
|
|
|
|
parser = OptionParser()
|
|
parser.add_option("-f", "--file", dest="filename",
|
|
help="write report to FILE", metavar="FILE")
|
|
parser.add_option("-v", "--verbose",
|
|
action="count", default=1,
|
|
help="increase verbosity of output")
|
|
parser.add_option("-q", "--quiet",
|
|
action="store_true",
|
|
help="suppress output messages")
|
|
|
|
parser.add_option("-U", "--user",
|
|
help="specify the username used to connect")
|
|
parser.add_option("-P", "--password",
|
|
help="specify the password used to connect")
|
|
parser.add_option("", "--filter",
|
|
default = "(&(objectCategory=person)(objectClass=User))",
|
|
help="specify the search filter")
|
|
parser.add_option("", "--attributes",
|
|
help="comma sep'd list of attribute names to print")
|
|
|
|
options, args = parser.parse_args()
|
|
if options.quiet:
|
|
if options.verbose != 1:
|
|
parser.error("Can not use '--verbose' and '--quiet'")
|
|
options.verbose = 0
|
|
|
|
if args:
|
|
parser.error("You need not specify args")
|
|
|
|
search()
|
|
|
|
if __name__=='__main__':
|
|
main()
|