Skip to content

第二部分 认证与目录服务

第 3 章 LDAP 目录服务(ldap)

该文件主要实现了 LDAP、LDAPS、GC(全局编录)和 Kerberos 协议的登录函数。

    def login(self, user='', password='', domain='', lmhash='', nthash='',authenticationChoice='sicilyNegotiate'):
        .....

    def kerberosLogin(self, user, password, domain='', lmhash='', nthash='', aesKey='', kdcHost=None, TGT=None,TGS=None, useCache=True):
        .....

还有ldap进行搜索操作的函数

    def search(self, searchBase=None, scope=None, derefAliases=None, sizeLimit=0, timeLimit=0, typesOnly=False,searchFilter='(objectClass=*)', attributes=None, searchControls=None, perRecordCallback=None):

其余函数主要是配合 search 的过滤器函数、发送 bind / search 请求的 sendReceive 函数,以及错误处理函数。

ldap.py 中这 3 个函数,是后续编写脚本时(无论域内还是域外)最常用的函数。

这边顺便说下 GC 协议:全局编录(Global Catalog)可以通俗理解为一个跨域的缓存数据库接口。全局编录服务器是 Active Directory 域服务(AD DS)林中所有对象的集合——它是一个域控制器,存储本域目录中所有对象的完全副本,以及林中其他域对象的部分只读副本,用于响应全局编录查询。端口为 3268(LDAP)和 3269(LDAPS),扫描域控时又可以多关注两个端口了。

ldapasn1.py — LDAP 请求数据结构(ASN.1)

该文件主要定义了 LDAP 请求中各参数的数据结构,可以类比成 Go 编程中的 struct。

class SearchResultEntry(univ.Sequence):
    tagSet = univ.Sequence.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 4))
    componentType = namedtype.NamedTypes(
        namedtype.NamedType('objectName', LDAPDN()),
        namedtype.NamedType('attributes', PartialAttributeList())
    )

一般用在回调函数中判定请求的结构是否正确

# eg.examples/GetADUsers.py


   def run(self):
        .....
        try:
            logging.debug('Search Filter=%s' % searchFilter)
            sc = ldap.SimplePagedResultsControl(size=100)
            ldapConnection.search(searchFilter=searchFilter,
                                  attributes=['sAMAccountName', 'pwdLastSet', 'mail', 'lastLogon'],
                                  sizeLimit=0, searchControls = [sc], perRecordCallback=self.processRecord)
          ......

  def processRecord(self, item):
        if isinstance(item, ldapasn1.SearchResultEntry) is not True:
            return
            .....

这边插一个 pyasn1 官方示例。使用 pyasn1 可以从 ASN.1 数据结构构建 Python 对象,例如下面的 ASN.1 数据结构:

Record ::= SEQUENCE {
  id        INTEGER,
  room  [0] INTEGER OPTIONAL,
  house [1] INTEGER DEFAULT 0
}

可以这样在 pyasn1 中表达:

class Record(Sequence):
    componentType = NamedTypes(
        NamedType('id', Integer()),
        OptionalNamedType(
            'room', Integer().subtype(
                implicitTag=Tag(tagClassContext, tagFormatSimple, 0)
            )
        ),
        DefaultedNamedType(
            'house', Integer(0).subtype(
                implicitTag=Tag(tagClassContext, tagFormatSimple, 1)
            )
        )
    )

ldaptypes.py — ACL 安全描述符结构(ACE / DACL)

该文件主要定义了 ACL 中各类安全描述符结构(ACE、DACL 等)。

ACE_TYPES = [
    ACCESS_ALLOWED_ACE,
    ACCESS_ALLOWED_OBJECT_ACE,
    ACCESS_DENIED_ACE,
    ACCESS_DENIED_OBJECT_ACE,
    ACCESS_ALLOWED_CALLBACK_ACE,
    ACCESS_DENIED_CALLBACK_ACE,
    ACCESS_ALLOWED_CALLBACK_OBJECT_ACE,
    ACCESS_DENIED_CALLBACK_OBJECT_ACE,
    SYSTEM_AUDIT_ACE,
    SYSTEM_AUDIT_OBJECT_ACE,
    SYSTEM_AUDIT_CALLBACK_ACE,
    SYSTEM_MANDATORY_LABEL_ACE,
    SYSTEM_AUDIT_CALLBACK_OBJECT_ACE,
    SYSTEM_RESOURCE_ATTRIBUTE_ACE,
    SYSTEM_SCOPED_POLICY_ID_ACE
]

实际使用中主要用于构造 ACL 变更相关的请求。

 #  eg./examples/ldap_shell.py
    def create_allow_ace(self, sid):
        nace = ldaptypes.ACE()
        nace['AceType'] = ldaptypes.ACCESS_ALLOWED_ACE.ACE_TYPE
        nace['AceFlags'] = 0x00
        acedata = ldaptypes.ACCESS_ALLOWED_ACE()
        acedata['Mask'] = ldaptypes.ACCESS_MASK()
        acedata['Mask']['Mask'] = 983551 # Full control
        acedata['Sid'] = ldaptypes.LDAP_SID()
        acedata['Sid'].fromCanonical(sid)
        nace['Ace'] = acedata
        return nace