LDAP SCHEMA DESIGN_Synopsis (大纲)

本文探讨了如何在一个组织内利用单一的LDAP目录为多种应用程序提供服务。重点在于目录的设计及其实现过程中的注意事项,包括避免后期重组、区分不同类型的数据、以及提高查询性能的设计技巧。

 

Synopsis (大纲)

It is possible to make one LDAP directory serve many applications in an organisation. This has the advantage of reducing the effort required to maintain the data, but it does mean that the design must be thought out very carefully before implementation starts.

LDAP directories are structured as a tree of entries, where each entry consists of a set of attribute-value pairs describing one object. The objects are often people, organisations, and departments, but can be anything at all. Schema is the term used to describe the shape of the directory and the rules that govern its content.

在一个特定的组织中,经常会出现一个 LDAP 目录为多个应用程序提供服务的情况。这种做法有利于减少数据维护的工作量,但如果采取这种设计,就必须在实施之前进行认真的思考,规划。在结构上, LDAP 目录是一棵由实体( entity )组成的树( tree )。一个实体则是一个包含多个属性值对的对象。对应通常是人、组织,或者部门,或者其他任何事物。模式这个概念用来描述目录的的结构以及内容应该遵循的规范。

A hypothetical organisation is described, with requirements for “white pages” directory service as well as a wide range of authentication, authorisation, and application-specific directory needs.The issues arising from the LDAP standards are discussed, along with the problems of maintaining compatibility with a range of existing LDAP clients.

本文档描述了一个“假象的”组织,对该组织的描述满足以下需求:采用平面的描述方式;授权,认证,程序规范目录需求。讨论的主题包括 LDAP 标准,以及由兼容已经存在的 LDAP 客户端带来的兼容性问题。

A plan is proposed for the layout of the directory tree, with particular emphasis on avoiding the need to re-organise it later. This involves careful separation of the data describing people,departments, groups, and application-specific objects. A simple approach to entry design is shown, based on the use of locally-defined auxiliary object classes. The effects of schema design on lookup performance are discussed. Some design tricks and pitfalls are presented, based on recent consulting experience.

本文档还提出了一个目录树的推荐布局,该布局强调“避免今后可能出现的重新组织”。为了满足这一要求,在设计时需要注意区分人,部门,组织以及程序细节的对象。为此本文档展示了一个简单的基于使用本地定义的 object classentry 设计策略。与此同时,与 schema 设计相关的查询性能问题在本文档中也有所涉及;并基于近期的咨询经验,给出了一些设计手段和缺陷预防措施。

1 LDAP

LDAP can be used to access information describing people, organisations, roles, services, and many other sorts of entity. It is a standard and widely-implemented protocol, which makes it extremely valuable for integrating multiple applications that need to share common data.

人们已经习惯于用 LDAP 来访问描述人,组织,角色,服务以及其他种类实体的信息。 LDAP 已经成为一个标准的,被广泛应用的协议,这一协议对在多个应用程序中共享数据有着重要价值。

It is important to understand the LDAP data model when considering schema. It is different from the relational model used by most well-known database systems, and this affects the way LDAP systems are designed and used.

要讨论 schema 设计,首先要理解 LDAP 数据模型。 LDAP 中使用到的数据模型与在其他数据库系统中大行其道的关系型模型不同,这一差异导致了 LDAP 系统的不同设计方式和应用方式。

Strictly speaking, LDAP is a protocol - the Lightweight Directory Access Protocol. It is not a database or even a directory although the term LDAP directory is often used to describe a directory service that is accessed using the LDAP protocol. LDAP derives from the X.500/ISO-9594 standards, and was originally intended as a simplified protocol for small computers to use when accessing X.500 systems. In recent years, LDAP has expanded and is now just as complex as X.500 but it still shares the same data model and part of the same distributed-service model.

严格来讲, LDAP 是一个协议( Lightweight Directory Access Protocol 轻型目录访问协议)尽管 LDAP 目录通常用来描述使用 LDAP 进行访问的目录服务,但 LDAP 本身不是一个数据库也不是一个目录。 LDAP 这个概念来源于 X.500/ISO-9594 标准。这个概念本来用于表示小型机使用的,访问 X.500 系统的简单协议。但最近这些年, LDAP 已经被扩展到与 X.500 本身几乎同样复杂。但无论如何, LDAP 还是来用共享相同的数据模型和一部分相同的分布式服务模型。

[RFC3377]

1.1 The LDAP data structure

An LDAP Directory stores information in a tree structure known as the Directory Information Tree (DIT). The nodes in the tree are directory entries, and each entry contains information in attribute-value form. Some attributes may have multiple values; others are restricted to a single value. The set of attributes that may be present in an entry is determined by the objectClass attribute, which is always present. objectClass is a multi-valued attribute and each value defines a set of mandatory and/or optional attributes.

LDAP 目录将信息保存在树形结构中,这种结构被称为 DIT 。树中的节点称为实体,每个实体中保持的信息以属性值的方式存在。某些属性可能是多值的也可能是单值的。某个实体中可能出现的属性在该实体的 objectClassobjectClass 是必然出现的)中描述。 objectClass 是一个多值属性,它的每一个值都会定义必须出现 / 可选的属性值。

Each node in the DIT has a name called the Relative Distinguished Name (RDN) which is unique among the peer nodes under its parent. It also has a globally-unique name called the Distinguished Name (DN) made up from the name of the node itself plus the names of all its superior nodes up to the root of the DIT.

每个 DIT 中的节点都拥有一个叫做 RDN 的名字。同一节点的所有子节点中,该名字是唯一的。除此之外,每个节点还会有一个全局唯一的名字叫做 DNDN 由节点自己的名字加上其高级节点的所有名字序列组成。

1.2 The LDAP Distributed Service model

X.500 was conceived as a global directory service. As such, it was expected to hold hundreds of millions of entries and be managed by thousands of different organisations. This led to a service model based on many co-operating servers known as DSAs (Directory System Agents). Each DSA can hold data from one or more arcs of the DIT, and is provided with “knowledge” so that it can direct queries for on-local data to the appropriate place. Queries are expected to outnumber updates by a very large factor, and absolute data consistency across the DIT is not supported, so data can be replicated easily for performance and resilience.

X.500 本认为是一个全局的目录服务。因此,它应该能够保存成千上万的实体,这些实体由上千个不同的组织进行管理。这就引出了一个基于多个合作 serverDSA )的服务模式。每个 DSA 可以保持一个或多个 DIT 结构,与此同时, DSA 应该能够提供能够将对本地数据的查询路由到恰当之处的知识。由于一个重要因素,查询次数应该远远大于修改次数。 LDAP 并不支持跨越 DIT 的,绝对的数据一致性。因此数据可能因为性能和弹性因素被轻松的复制。

An LDAP server is effectively a DSA and it follows the same rules, though LDAP does not have such a good distributed-service model for very large networks. Most current LDAP deployments are limited to operation within a single organisation, so although data replication is commonly used, the other features of the distributed service model do not get so much exercise. X.500 and LDAP have many similarities with DNS in terms of data model and service model, but the directory systems are capable of more complex operations.

尽管 LDAP 并不能实现全网络的的分布式服务模型,一个 LDAP Server 实际上就是一个遵循同一规则的 DSA 。大多数并行的 LDAP 部署都仅仅局限于在同一个组织内部,所以尽管数据复制随处可见,其他分布式服务的特征却并不是很常见。 X.500LDAP 在数据模型和服务模型上与 DNS 在概念上有很多相同之处,只不过目录系统包含更多复杂的操作。

 

<think> 我们正在解决使用WinLDAP时,在设置LDAP_OPT_SERVER_CERTIFICATE选项为LDAP_OPT_ON后,ldap_bind_s崩溃的问题。 根据用户描述,他们在执行ldap_set_option(ld, LDAP_OPT_SERVER_CERTIFICATE, LDAP_OPT_ON)后,调用ldap_bind_s时崩溃。 可能的原因及解决方案: 1. **内存或句柄问题**:在调用ldap_set_option之前,LDAP会话句柄(ld)可能未正确初始化,或者已经被释放。 解决方案:确保正确初始化LDAP会话句柄,并确保在设置选项和绑定期间该句柄有效。 2. **选项设置时机**:设置证书验证选项的时机不正确。应该在初始化LDAP会话之后,但在执行任何操作(如绑定)之前设置。 解决方案:确保调用顺序为:ldap_init -> ldap_set_option -> ... (其他设置) -> ldap_bind_s。 3. **选项值LDAP_OPT_ON的含义**:在WinLDAP中,LDAP_OPT_SERVER_CERTIFICATE选项用于设置服务器证书验证的回调函数。直接设置为LDAP_OPT_ON可能不正确,因为该选项期望的是一个回调函数,而不是一个开关值。 实际上,根据微软文档,LDAP_OPT_SERVER_CERTIFICATE选项对应的值应该是一个指向回调函数的指针,该函数用于验证服务器证书。如果我们不想验证,应该使用LDAP_OPT_SSL选项并设置为LDAP_OPT_OFF,或者使用LDAP_OPT_SERVER_CERTIFICATE并提供一个始终返回成功的回调函数。 4. **正确的禁用证书验证方法**:在测试环境中,我们通常希望禁用证书验证。在WinLDAP中,正确的方法有两种: a) 设置LDAP_OPT_SSL为LDAP_OPT_OFF(但这样会完全禁用SSL,不推荐)。 b) 设置LDAP_OPT_SERVER_CERTIFICATE选项为一个回调函数,该函数总是返回TRUE(表示接受任何证书)。 5. **回调函数示例**:提供一个简单的回调函数,总是返回TRUE(即接受服务器证书)。 因此,崩溃的原因很可能是我们将一个整数值(LDAP_OPT_ON)作为函数指针传递给了系统,当系统试图调用这个“函数”时,导致非法访问。 解决方案:改为设置一个回调函数。 步骤: 1. 定义一个回调函数,用于验证证书。如果我们要忽略验证,则始终返回TRUE。 2. 将该回调函数通过ldap_set_option设置给LDAP_OPT_SERVER_CERTIFICATE选项。 示例代码: ```c #include <winldap.h> #include <winber.h> // 定义证书验证回调函数,总是返回TRUE表示接受任何证书 BOOL CALLBACK IgnoreCertVerify( __in PLDAP Connection, __in PCCERT_CONTEXT* ppServerCert, __in void* pvArg ) { return TRUE; } int main() { LDAP* ld = NULL; // 初始化LDAP会话,使用SSL端口636 ld = ldap_sslinit("ldap.example.com", 636, 1); if (ld == NULL) { // 处理错误 } // 设置选项:使用回调函数忽略证书验证 ldap_set_option(ld, LDAP_OPT_SERVER_CERTIFICATE, (void*)IgnoreCertVerify); // 设置版本(可选,但推荐) ULONG version = LDAP_VERSION3; ldap_set_option(ld, LDAP_OPT_VERSION, &version); // 执行绑定 ULONG rc = ldap_bind_s(ld, "cn=admin,dc=example,dc=com", "password", LDAP_AUTH_SIMPLE); if (rc != LDAP_SUCCESS) { // 处理错误 } // ... 其他操作 ldap_unbind(ld); return 0; } ``` 注意:在生产环境中,应该实现一个真正的证书验证回调函数,检查证书的有效性。 6. **其他注意事项**: - 确保在设置选项时使用正确的选项值类型。LDAP_OPT_SERVER_CERTIFICATE需要一个函数指针,而LDAP_OPT_ON是一个整数值(1),不能用作函数指针。 - 检查LDAP句柄初始化是否成功(非NULL)。 - 如果仍然崩溃,检查是否有多线程问题?确保LDAP句柄不被多个线程同时使用。 引用说明: 根据WinLDAP文档,LDAP_OPT_SERVER_CERTIFICATE选项用于设置一个回调函数,当需要验证服务器证书时调用。这个回调函数的签名必须是:`int (*)(LDAP*, PCCERT_CONTEXT*, void*)`,但实际在wincrypt.h中,PCCERT_CONTEXT是指向CERT_CONTEXT的指针,因此回调函数应返回BOOL(TRUE表示接受,FALSE表示拒绝)[^1]。 因此,之前错误地将LDAP_OPT_ON(一个整数值)作为函数指针传递,导致崩溃。 相关问题: 1. 如何在WinLDAP中正确实现服务器证书验证? 2. 除了忽略证书验证,还有其他方法解决自签名证书的问题吗? 3. WinLDAP中如何处理证书吊销检查? 4. 在多线程环境中如何使用WinLDAP的证书回调函数? 注意:上述代码示例适用于测试环境。在生产环境中忽略证书验证会带来安全风险,应使用受信任的证书或正确验证证书链。 </think> ### WinLDAP 中 `ldap_set_option(ld, LDAP_OPT_SERVER_CERTIFICATE, LDAP_OPT_ON)` 导致 `ldap_bind_s` 崩溃的解决方案 #### 崩溃根本原因分析 1. **选项值类型不匹配** `LDAP_OPT_SERVER_CERTIFICATE` 要求传入 **回调函数指针**,而不是整型常量 `LDAP_OPT_ON`。 错误代码会被解释为无效内存地址,导致访问冲突崩溃。[^1][^2] $$\text{错误类型} = \begin{cases} \text{预期} & \text{函数指针} \\ \text{实际} & \text{整数 } (1) \end{cases}$$ 2. **线程安全问题** WinLDAP 的证书回调在多线程环境下需全局同步,未同步时可能触发竞争条件[^4]。 3. **内存管理缺陷** 未初始化的 LDAP 句柄或无效上下文指针引发空指针异常。 --- ### 分步解决方案 #### 步骤1:定义证书验证回调函数 ```c #include <winldap.h> #include <wincrypt.h> // 始终返回 TRUE 的简单回调(跳过验证) BOOL CALLBACK IgnoreCertVerify( __in PLDAP Connection, __in PCCERT_CONTEXT* ppServerCert, __in void* pvArg ) { return TRUE; // 接受所有证书 } ``` #### 步骤2:正确设置回调函数 ```c LDAP* ld = ldap_sslinit("ldap.example.com", 636, 1); // 1=启用SSL // 正确设置回调函数(非LDAP_OPT_ON!) ldap_set_option( ld, LDAP_OPT_SERVER_CERTIFICATE, (void*)IgnoreCertVerify // 传入函数指针 ); // 验证选项设置结果 void* funcPtr = NULL; ldap_get_option( ld, LDAP_OPT_SERVER_CERTIFICATE, &funcPtr ); assert(funcPtr == IgnoreCertVerify); // 确保设置成功 ``` #### 步骤3:安全的绑定调用 ```c // 绑定前验证句柄有效性 if (ld == NULL || (ULONG_PTR)ld <= 0x10000) { fprintf(stderr, "无效LDAP句柄: 0x%p\n", ld); abort(); } // 同步调用(避免多线程竞争) ULONG rc = ldap_bind_s( ld, "cn=admin,dc=example,dc=com", L"password", // UNICODE字符串 LDAP_AUTH_SIMPLE ); if (rc != LDAP_SUCCESS) { fprintf(stderr, "绑定失败: 0x%X\n", rc); } ``` --- ### 崩溃预防最佳实践 1. **回调函数必备** 永远不要将 `LDAP_OPT_ON` 用于证书选项,必须实现回调函数[^2]。 2. **内存安全初始化** ```c // 安全初始化模式 LDAP* ld = ldap_init("ldap.example.com", 389); ldap_set_option(ld, LDAP_OPT_SSL, LDAP_OPT_ON); // 显式启用SSL ``` 3. **多线程防护** ```c // 全局初始化(仅一次) static CRITICAL_SECTION ldapCs; InitializeCriticalSection(&ldapCs); // 线程安全调用 EnterCriticalSection(&ldapCs); ldap_set_option(ld, ...); LeaveCriticalSection(&ldapCs); ``` --- ### 替代解决方案(无回调) ```c // 方法1:完全禁用SSL验证(不推荐) ldap_set_option(ld, LDAP_OPT_SSL, LDAP_OPT_OFF); // 方法2:注册系统信任的证书 certutil -addstore "Root" ldap_server.cer // 需先导入证书 ``` --- ### 根本原因验证流程 ```mermaid graph TD A[崩溃] --> B{检查参数类型} B -->|LDAP_OPT_ON| C[类型错误] B -->|回调函数| D[类型正确] C --> E[访问冲突] D --> F[执行回调] F -->|返回TRUE| G[绑定成功] F -->|返回FALSE| H[绑定失败] ``` > **警告**:生产环境必须实现真正的证书验证逻辑,示例中的 `IgnoreCertVerify` 仅用于测试[^1]。 --- ### 相关问题 1. 如何实现完整的 LDAPS 证书链验证回调? 2. WinLDAP 中如何处理证书吊销列表 (CRL) 检查? 3. 在多线程服务中如何安全共享 LDAP 连接? 4. 使用自签名证书时如何避免 `CERT_E_UNTRUSTEDROOT` 错误? [^1]: 原始讨论表明选项设置错误会导致意外行为 [^2]: PHP 示例展示正确选项设置需结构体而非简单值 [^3]: 基础 LDAP 操作需确保参数有效性 [^4]: 选项处理涉及复杂内存管理
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值