Skip to content

Commit 120818f

Browse files
committed
Python 2 and 3 compatibility!
1 parent 93e7cfa commit 120818f

File tree

3 files changed

+43
-35
lines changed

3 files changed

+43
-35
lines changed

ldapdomaindump/__init__.py

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,22 @@
2121
# SOFTWARE.
2222
#
2323
####################
24-
24+
from __future__ import unicode_literals
2525
import sys, os, re, codecs, json, argparse, getpass, base64
2626
# import class and constants
2727
from datetime import datetime
28-
from urllib import quote_plus
29-
28+
try:
29+
from urllib.parse import quote_plus
30+
except ImportError:
31+
from urllib import quote_plus
3032
import ldap3
3133
from ldap3 import Server, Connection, SIMPLE, SYNC, ALL, SASL, NTLM
3234
from ldap3.core.exceptions import LDAPKeyError, LDAPAttributeError, LDAPCursorError
3335
from ldap3.abstract import attribute, attrDef
3436
from ldap3.utils import dn
3537
from ldap3.protocol.formatters.formatters import format_sid
38+
from builtins import str
39+
from future.utils import itervalues, iteritems, native_str
3640

3741
# dnspython, for resolving hostnames
3842
import dns.resolver
@@ -452,7 +456,7 @@ def parseFlags(self, attr, flags_def):
452456
outflags = []
453457
if attr is None:
454458
return outflags
455-
for flag, val in flags_def.items():
459+
for flag, val in iteritems(flags_def):
456460
if attr.value & val:
457461
outflags.append(flag)
458462
return outflags
@@ -462,7 +466,7 @@ def parseTrustDirection(self, attr, flags_def):
462466
outflags = []
463467
if attr is None:
464468
return outflags
465-
for flag, val in flags_def.items():
469+
for flag, val in iteritems(flags_def):
466470
if attr.value == val:
467471
outflags.append(flag)
468472
return outflags
@@ -473,40 +477,40 @@ def generateHtmlTable(self, listable, attributes, header='', firstTable=True, sp
473477
#Only if this is the first table it is an actual table, the others are just bodies of the first table
474478
#This makes sure that multiple tables have their columns aligned to make it less messy
475479
if firstTable:
476-
of.append(u'<table>')
480+
of.append('<table>')
477481
#Table header
478482
if header != '':
479-
of.append(u'<thead><tr><td colspan="%d" id="cn_%s">%s</td></tr></thead>' % (len(attributes), self.formatId(header), header))
480-
of.append(u'<tbody><tr>')
483+
of.append('<thead><tr><td colspan="%d" id="cn_%s">%s</td></tr></thead>' % (len(attributes), self.formatId(header), header))
484+
of.append('<tbody><tr>')
481485
for hdr in attributes:
482486
try:
483487
#Print alias of this attribute if there is one
484-
of.append(u'<th>%s</th>' % self.htmlescape(attr_translations[hdr]))
488+
of.append('<th>%s</th>' % self.htmlescape(attr_translations[hdr]))
485489
except KeyError:
486-
of.append(u'<th>%s</th>' % self.htmlescape(hdr))
487-
of.append(u'</tr>\n')
490+
of.append('<th>%s</th>' % self.htmlescape(hdr))
491+
of.append('</tr>\n')
488492
for li in listable:
489493
#Whether we should format group objects separately
490494
if specialGroupsFormat and 'group' in li['objectClass'].values:
491495
#Give it an extra class and pass it to the function below to make sure the CN is a link
492496
liIsGroup = True
493-
of.append(u'<tr class="group">')
497+
of.append('<tr class="group">')
494498
else:
495499
liIsGroup = False
496-
of.append(u'<tr>')
500+
of.append('<tr>')
497501
for att in attributes:
498502
try:
499-
of.append(u'<td>%s</td>' % self.formatAttribute(li[att], liIsGroup))
503+
of.append('<td>%s</td>' % self.formatAttribute(li[att], liIsGroup))
500504
except (LDAPKeyError, LDAPCursorError):
501-
of.append(u'<td>&nbsp;</td>')
502-
of.append(u'</tr>\n')
503-
of.append(u'</tbody>\n')
504-
return u''.join(of)
505+
of.append('<td>&nbsp;</td>')
506+
of.append('</tr>\n')
507+
of.append('</tbody>\n')
508+
return ''.join(of)
505509

506510
#Generate several HTML tables for grouped reports
507511
def generateGroupedHtmlTables(self, groups, attributes):
508512
first = True
509-
for groupname, members in groups.iteritems():
513+
for groupname, members in iteritems(groups):
510514
yield self.generateHtmlTable(members, attributes, groupname, first, specialGroupsFormat=True)
511515
if first:
512516
first = False
@@ -566,13 +570,14 @@ def formatString(self, value):
566570
return value.strftime('%x %X')
567571
except ValueError:
568572
#Invalid date
569-
return u'0'
570-
if type(value) is unicode:
571-
return value#.encode('utf8')
573+
return '0'
574+
# Make sure it's a unicode string
575+
if type(value) is bytes:
576+
return value.encode('utf8')
572577
if type(value) is str:
573-
return unicode(value, errors='replace')#.encode('utf8')
578+
return value#.encode('utf8')
574579
if type(value) is int:
575-
return unicode(value)
580+
return str(value)
576581
if value is None:
577582
return ''
578583
#Other type: just return it
@@ -619,7 +624,7 @@ def formatAttribute(self, att, formatCnAsGroup=False):
619624

620625

621626
def formatCnWithGroupLink(self, cn):
622-
return u'Group: <a href="#cn_%s" title="%s">%s</a>' % (self.formatId(cn), self.htmlescape(cn), self.htmlescape(cn))
627+
return 'Group: <a href="#cn_%s" title="%s">%s</a>' % (self.formatId(cn), self.htmlescape(cn), self.htmlescape(cn))
623628

624629
#Convert a CN to a valid HTML id by replacing all non-ascii characters with a _
625630
def formatId(self, cn):
@@ -630,7 +635,7 @@ def formatGroupsHtml(self, grouplist):
630635
outcache = []
631636
for group in grouplist:
632637
cn = self.unescapecn(dn.parse_dn(group)[0][1])
633-
outcache.append(u'<a href="%s.html#cn_%s" title="%s">%s</a>' % (self.config.users_by_group, quote_plus(self.formatId(cn)), self.htmlescape(group), self.htmlescape(cn)))
638+
outcache.append('<a href="%s.html#cn_%s" title="%s">%s</a>' % (self.config.users_by_group, quote_plus(self.formatId(cn)), self.htmlescape(group), self.htmlescape(cn)))
634639
return ', '.join(outcache)
635640

636641
#Format groups to readable HTML
@@ -707,7 +712,7 @@ def generateJsonGroupedList(self, groups):
707712
#Start of the list
708713
yield '['
709714
firstGroup = True
710-
for group in groups.iteritems():
715+
for group in iteritems(groups):
711716
if not firstGroup:
712717
#Separate items
713718
yield ','
@@ -799,11 +804,11 @@ def generateTrustsReport(self, dd):
799804

800805
#Some quick logging helpers
801806
def log_warn(text):
802-
print '[!] %s' % text
807+
print('[!] %s' % text)
803808
def log_info(text):
804-
print '[*] %s' % text
809+
print('[*] %s' % text)
805810
def log_success(text):
806-
print '[+] %s' % text
811+
print('[+] %s' % text)
807812

808813
def main():
809814
parser = argparse.ArgumentParser(description='Domain information dumper via LDAP. Dumps users/computers/groups and OS/membership information to HTML/JSON/greppable output.')
@@ -813,8 +818,8 @@ def main():
813818
#Main parameters
814819
#maingroup = parser.add_argument_group("Main options")
815820
parser.add_argument("host", type=str, metavar='HOSTNAME', help="Hostname/ip or ldap://host:port connection string to connect to (use ldaps:// to use SSL)")
816-
parser.add_argument("-u", "--user", type=str, metavar='USERNAME', help="DOMAIN\\username for authentication, leave empty for anonymous authentication")
817-
parser.add_argument("-p", "--password", type=str, metavar='PASSWORD', help="Password or LM:NTLM hash, will prompt if not specified")
821+
parser.add_argument("-u", "--user", type=native_str, metavar='USERNAME', help="DOMAIN\\username for authentication, leave empty for anonymous authentication")
822+
parser.add_argument("-p", "--password", type=native_str, metavar='PASSWORD', help="Password or LM:NTLM hash, will prompt if not specified")
818823
parser.add_argument("-at", "--authtype", type=str, choices=['NTLM', 'SIMPLE'], default='NTLM', help="Authentication type (NTLM or SIMPLE, default: NTLM)")
819824

820825
#Output parameters
@@ -879,6 +884,7 @@ def main():
879884
# define the server and the connection
880885
s = Server(args.host, get_info=ALL)
881886
log_info('Connecting to host...')
887+
882888
c = Connection(s, user=args.user, password=args.password, authentication=authentication)
883889
log_info('Binding to host')
884890
# perform the Bind operation

ldapdomaindump/convert.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
from __future__ import unicode_literals
12
import argparse
23
import os
34
import logging
45
import json
56
import codecs
67
import re
78
from ldapdomaindump import trust_flags, trust_directions
9+
from builtins import str, itervalues, iteritems
810

911
logging.basicConfig()
1012
logger = logging.getLogger('ldd2bloodhound')
@@ -84,7 +86,7 @@ def write_groups(self):
8486
# Read group mapping - write to csv
8587
# file is already created here, we just append
8688
with codecs.open('group_membership.csv', 'a', 'utf-8') as outfile:
87-
for group in self.groups_by_dn.itervalues():
89+
for group in itervalues(self.groups_by_dn):
8890
for membergroup in group['memberOf']:
8991
try:
9092
outfile.write('%s,%s,%s\n' % (self.groups_by_dn[membergroup]['principal'], group['principal'], 'group'))

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
from setuptools import setup
22
setup(name='ldapdomaindump',
3-
version='0.8.7',
3+
version='0.9.0',
44
description='Active Directory information dumper via LDAP',
55
author='Dirk-jan Mollema',
66
author_email='dirkjan@sanoweb.nl',
77
url='https://github.com/dirkjanm/ldapdomaindump/',
88
packages=['ldapdomaindump'],
9-
install_requires=['dnspython','ldap3>=2.0'],
9+
install_requires=['dnspython', 'ldap3>=2.0', 'future'],
1010
package_data={'ldapdomaindump': ['style.css']},
1111
include_package_data=True,
1212
scripts=['bin/ldapdomaindump', 'bin/ldd2bloodhound']

0 commit comments

Comments
 (0)