LDAP API
介绍
这篇文档定义了 LDAP API (C语言版)。它使用方便,功能强大,大致内容有以下几个方面:
简单浏览LDAP模型
应用程序怎样使用API去获取LDAP信息
详细介绍API 调用函数
举例使用API 及部分样本代码
下面我们分别介绍。
简单浏览LDAP模型
LDAP 是以client-server 模型为基础的,在此模型中,客户机可以建立与LDAP服务器的连接,从而发送请求,接收应答。
LDAP的信息模型是基于实体(entry)的,每个实体都代表着某种对象类型的特例。比如组织机构、用户、网络、计算机等。每一种实体又包含有许多属性,每种属性又由它的实体所代表的对象的类型决定。
实体用树型结构组织再一起,通常根据政治上的,地理学的,和组织的关系进行分类。每一个实体都有唯一的名字通过它的RDN与它的同属实体相连,至于RDN及DN的命名和使用在《毕业设计报告(一)》中已经介绍过了,这里不在详述。
三、LDAP API使用浏览
一个应用使用LDAP API 的一般步骤:
建立与LDAP server的连接。如使用函数调用 ldap_open()等。
核对身份 ldap server 和(或)X.500 DSA,如可以使用ldap_bind().
执行某些LDAP操作,返回某些结果。
最后断开连接。
API的操作可以被同步执行,也可以异步执行。同步的函数调用以_s结尾。例如:同步查找操作可以通过调用ldap_search_s()来完成。异步调用将返回一个result,用来表示操作结果(如,常量LDAP_SUCCESS或者其它错误代码)。异步调用还将返回初始化操作的消息id。异步操作可以通过调用ldap_abandon()而抛弃。
结果和错误信息被作为一个不透明的结构LDAPMessage 返回,函数调用的目的就是分析这一结构,进一步研究被返回的实体和属性等。有时也负责解释这些属性。下面我们将详细介绍API函数调用。
四、 LDAP API函数调用
这里所有的调用都含有一个“连接手柄”(connection handle)它指向关于每个连接的信息的LDAP结构。许多调用的返回结果都是LDAPMessage结构形式。下面我们会描绘出这些必要的结构。
1. 建立连接
ldap_open() 建立一个同 LDAP server的连接。有关定义如下:
LDAP *ldap_open( char *hostname, int portno );
参数说明:
hostname 空格分开的主机名字表或点分开的LDAP server 可以连接到的主机的IP地址串。主机试着依次连接表中列出的每个名字直到某个连接成功时才停止。
portno 欲连接的TCP端口号。如果省略则为 LDAP_PORT.
如果连接不能建立,则返回值为NULL。
2. Authenticating to the directory
ldap_bind() 和它的同类派生常用来识别目录:
int ldap_bind( LDAP *ld, char *dn, char *cred, int method );
int ldap_bind_s( LDAP *ld, char *dn, char *cred, int method );
int ldap_simple_bind( LDAP *ld, char *dn, char *passwd );
int ldap_simple_bind_s( LDAP *ld, char *dn, char *passwd );
int ldap_kerberos_bind( LDAP *ld, char *dn );
int ldap_kerberos_bind_s( LDAP *ld, char *dn );
参数说明:
ld 连接手柄;
dn 被bind的实体名;
cred 识别证书;
method LDAP_AUTH_SIMPLE, LDAP_AUTH_KRBV41, 或
LDAP_AUTH_KRBV42, 用以表明要使用的识别方法。
passwd 为 ldap_simple_bind()而准备的口令,并与实体中的
userPassword 属性比较。
3. Closing the connection
ldap_unbind() 用来解开同目录的联系并断开建立的连接。
int ldap_unbind( LDAP *ld );
参数说明:
ld 连接手柄;
通过调用ldap_unbind() ,则ld 连接手柄就无效了。
4. Searching
ldap_search() 等用来查找 LDAP目录,返回与实体匹配的属性。
struct timeval {
long tv_sec;
long tv_usec;
};
int ldap_search(
LDAP *ld,
char *base,
int scope,
char *filter,
char *attrs[],
int attrsonly
);
int ldap_search_s(
LDAP *ld,
char *base,
int scope,
char *filter,
char *attrs[],
int attrsonly,
LDAPMessage **res
);
int ldap_search_st(
LDAP *ld,
char *base,
int scope,
char *filter,
char *attrs[],
int attrsonly,
struct timeval *timeout,
LDAPMessage **res
);
参数说明:
ld 连接手柄;
base 开始寻找的基础对象的DN
Scope :LDAP_SCOPE_BASE,LDAP_SCOPE_ONELEVEL, 或LDAP_SCOPE_SUBTREE,用来表明查找的范围。
filter :是一个表示LDAP查询的filter,是字符串表示,它的格式
定义在RFC 1588中。
attrs :一系列指针字符用于表征查找返回属性
attrsevly:布尔参数:0表示返回属性类型和值;非0,仅返回类型。
timeout 为 ldap_search_st() 而说明当地查找操作的超时值。
res 为同步调用准备的结果参数,记录查找完成的结果。
在ld连接手柄中有三个域控制查找操作的进行。它们是:
ld_sizelimit 查找实体实体返回的个数,如果为零,表示无限制。
ld_timelimit 查找时间限制,如果为零,表示无限制。
ld_deref LDAP_DEREF_NEVER,LDAP_DEREF_SEARCHING,
LDAP_DEREF_FINDING,LDAP_DEREF_ALWAYS之一。
用来说明再查找中别名怎麽处理。
LDAP_DEREF_NEVER: 在搜索中或者查找那基础对象时
做不复引用别名。
LDAP_DEREF_SEARCHING: 在基础对象的附属的搜索
中而不是查找那基础对象时做复引用别名。
LDAP_DEREF_FINDING: 在基础对象而不是其附属的
搜索中做复引用别名。
LDAP_DEREF_ALWAYS: 在搜索中或者查找那基础对
象时做都复引用别名。
一个异步查找通过调用ldap_search()进行初始化.该操作返
回本初始化查找的消息id,要想得到这个结果,可以调用ldap_result().
一个同步查找可以调用ldap_search_s() 或 ldap_search_st()来
实现.除了 ldap_search_st() 多了一个参数描述查找时限外,这两个
函数的功能基本是一样的。他们都返回一个查找结果,是LDAP
_SUCCESS 或一些错误信息(看下面的错误处理Error Handling).
查找操作返回的实体(如果有)必包含一个参数res .这个参数对调
用者来说是不透明的。Entries, attributes, values等等都必须调用下面
的分析程序才能进行分析。包含参数 res 的 结果只有不再使用且调
用 ldap_msgfree()时才被释放掉。
5. Reading an entry
LDAP 不直接支持读操作,但此操作可以基于实体的DN的查找来仿效。其参数设置如下:
scope LDAP_SCOPE_BASE,
filter "(objectclass=*)".
则attrs 包含有返回的属性表。
6. Listing the children of an entry
LDAP 也不直接支持表操作,同上我们有:
scope LDAP_SCOPE_ONELEVEL,
filter "(objectclass=*)".
则attrs 就包含了要返回的每个子女实体的属性表。
7. Modifying an entry
ldap_modify() 和 ldap_modify_s() 常用来修改现存的LDAP
实体。有关定义如下:
typedef struct ldapmod {
int mod_op;
char *mod_type;
union {
char **modv_strvals;
struct berval **modv_bvals;
} mod_vals;
} LDAPMod;
#define mod_values mod_vals.modv_strvals
#define mod_bvalues mod_vals.modv_bvals
int ldap_modify( LDAP *ld, char *dn, LDAPMod *mods[] );
int ldap_modify_s( LDAP *ld, char *dn, LDAPMod *mods[] );
参数说明:
ld 连接手柄;
dn 被修改的实体名;
mods 修改表,填入修改方式,如ADD、DELETE等。
LDAPMod 结构中的域解释如下:
mod_op 修改操作,它可以是LDAP_MOD_ADD,
LDAP_MOD_DELETE,或LDAP_MOD_REPLACE.
这个域也用来表明包含在mod_vals union中的值的类
型.
mod_type 被修改的属性的类型。
mod_vals 需要增加、删除或替换的值。
ldap_modify_s() 返回的是来自修改操作的LDAP 错误代码,可以
调用ldap_perror()及其一类的函数来解释。
ldap_modify() 返回的是它对请求初始化的消息 id ,或者是代表错误的值 -1 ,这个操作结果可以通过调用 ldap_result()来获得。
Modifying the RDN of an entry
ldap_modrdn()和 ldap_modrdn_s() 常用来改变LDAP 实体的名字。
int ldap_modrdn(
LDAP *ld,
char *dn,
char *newrdn,
int deleteoldrdn
);
int ldap_modrdn_s(
LDAP *ld,
char *dn,
char *newrdn,
int deleteoldrdn
);
参数说明:
ld 连接手柄 ;
dn 实体名,它的RDN要被改变;
newrdn 新的 RDN ;
deleteoldrdn 是一个布尔值;用来控制旧的RDN属性值是作为表的某一属性保存下来(0),还是把它删掉(非0)。
ldap_modrdn_s() 是同步的,返回一个LDAP 错误代码,表示操作出口。
ldap_modrdn() 则是异步的,返回的是它对请求初始化的消息 id ,或者是代表错误的值 -1 ,这个操作结果可以通过调用 ldap_result()来获得。
9. Adding an entry
ldap_add() 和 ldap_add_s() 用来增加实体到 LDAP目录上。
int ldap_add( LDAP *ld, char *dn, LDAPMod *attrs[] );
int ldap_add_s( LDAP *ld, char *dn, LDAPMod *attrs[] );
参数说明:
ld 连接手柄;
dn 被增加的实体名;
attrs 实体的属性表,详细说明在ldap_modify()里定义的
LDAPMod 结构中。其中mod_type 和 mod_vals 两个域应被填上。
注意一点被加实体的父母节点必须已经存在。
ldap_add_s() 和ldap_add()的区别同上面的解释一样。
10. Deleting an entry
ldap_delete() 和 ldap_delete_s() 用于从LDAP 目录中删除实体。
int ldap_delete( LDAP *ld, char *dn );
int ldap_delete_s( LDAP *ld, char *dn );
参数说明:
ld 连接手柄;
dn 被删实体的名字.
注意一点:被删实体在LDAP结构中必须是一个叶实体,不能有子女,至于删除完整子树,LDAP还不支持。
ldap_delete_s() 和ldap_delete() 与前面的解释雷同。
五、放弃操作的调用
ldap_abandon() 用于执行放弃操作的命令。
int ldap_abandon( LDAP *ld, int msgid );
ldap_abandon() 放弃的操作是由消息 id msgid确定的. 如果放弃操作成功,则返回值为0,否则为 -1.如果该次调用成功,那麽通过调用ldap_result(),则不返回任何结果。
Calls for obtaining results
ldap_result() 就是用来获得上次异步初始化操作的结果。ldap_ms
-gfree() 用于释放ldap_result()或某个同步查找调用之前的调用获得的结果。
int ldap_result(
LDAP *ld,
int msgid,
int all,
struct timeval *timeout,
LDAPMessage **res
);
int ldap_msgfree( LDAPMessage *res );
参数说明:
ld 连接手柄;
msgid 信息id ,用以确定那个结果需要返回的操作或者
是 LDAP_RES_ANY (如果需要结果);
all 布尔参数,仅对查找结果有意义。如果为零,则查找结
果(实体)一产生就返回;否则查找结果变化时返回。
timeout 结果返回的等待时间。如果为 NULL ,则 ldap_result()
会一直等到结果有效才返回。如果它的值为零则指定为
一个探询行为。
res 对 ldap_result()来说,它是包含此次操作结果的参数;对
ldap_msgfree()来说,它是将被释放的结果链。
如果完成成功,ldap_result() 将返回结果的类型,存于参数res 中。
它必是下列常量之一:
LDAP_RES_BIND
LDAP_RES_SEARCH_ENTRY
LDAP_RES_SEARCH_RESULT
LDAP_RES_MODIFY
LDAP_RES_ADD
LDAP_RES_DELETE
LDAP_RES_MODRDN
LDAP_RES_COMPARE
如果超时, ldap_result() 返回为0;如果错误产生,则返回-1
同时ld结构中的ld_errno 域也相应地被设置。
ldap_msgfree() 释放被指定为参数res的结果结构并且返回它释放的消息的类型。
七、 Calls for error handling
下面的调用用来解释其它LDAP API 返回的错误信息:
int ldap_result2error(
LDAP *ld,
LDAPMessage *res,
int freeit
);
char *ldap_err2string( int err );
void ldap_perror( LDAP *ld, char *msg );
参数说明:
ld 连接手柄;
res LDAP 操作结果如ldap_result()或其它同步API操作调用
的返回结果;
freeit 布尔参数,用以确定参数res 是否被释放(如果没有则
它的值为0);
err LDAP的错误代码,如ldap_result2error() 或其它同步API
的返回;
msg 显示在LDAP错误信息之前的信息。
ldap_result2error()用来把 LDAP 结果信息转换为数字的
LDAP 错误代码,LDAP结果信息可能来自ldap_result(), 也可能
是某个同步API操作调用返回的信息 res。它也用来分析结果信息
中的 ld_matched 和ld_error 两部分,进而把它们组成连接手柄信
息。所有的同步操作在返回之前都要调用ldap_result2error(),从而
确保这些域被正确地设置。在连接结构中相关的域有:
ld_matched 在LDAP_NO_SUCH_OBJECT错误返回事件中,这个参数包含DN匹配的程度;
ld_error 这个参数包含了被LDAP 服务器返回的错误信息;
ld_errno LDAP 错误代码,指示操作结果,它是下列常量内容之一:
LDAP_SUCCESS
LDAP_OPERATIONS_ERROR
LDAP_PROTOCOL_ERROR
LDAP_TIMELIMIT_EXCEEDED
LDAP_SIZELIMIT_EXCEEDED
LDAP_COMPARE_FALSE
LDAP_COMPARE_TRUE
LDAP_STRONG_AUTH_NOT_SUPPORTED
LDAP_STRONG_AUTH_REQUIRED
LDAP_NO_SUCH_ATTRIBUTE
LDAP_UNDEFINED_TYPE
LDAP_INAPPROPRIATE_MATCHING
LDAP_CONSTRAINT_VIOLATION
LDAP_TYPE_OR_VALUE_EXISTS
LDAP_INVALID_SYNTAX
LDAP_NO_SUCH_OBJECT
LDAP_ALIAS_PROBLEM
LDAP_INVALID_DN_SYNTAX
LDAP_IS_LEAF
LDAP_ALIAS_DEREF_PROBLEM
LDAP_INAPPROPRIATE_AUTH
LDAP_INVALID_CREDENTIALS
LDAP_INSUFFICIENT_ACCESS
LDAP_BUSY
LDAP_UNAVAILABLE
LDAP_UNWILLING_TO_PERFORM
LDAP_LOOP_DETECT
LDAP_NAMING_VIOLATION
LDAP_OBJECT_CLASS_VIOLATION
LDAP_NOT_ALLOWED_ON_NONLEAF
LDAP_NOT_ALLOWED_ON_RDN
LDAP_ALREADY_EXISTS
LDAP_NO_OBJECT_CLASS_MODS
LDAP_RESULTS_TOO_LARGE
LDAP_OTHER
LDAP_SERVER_DOWN
LDAP_LOCAL_ERROR
LDAP_ENCODING_ERROR
LDAP_DECODING_ERROR
LDAP_TIMEOUT
LDAP_AUTH_UNKNOWN
LDAP_FILTER_ERROR
LDAP_USER_CANCELLED
LDAP_PARAM_ERROR
LDAP_NO_MEMORY
ldap_err2string() 用来转换数字的 LDAP 错误代码为常用的
描述错误的NULL-terminated 字符串,数字的 LDAP 错误代码可能
来自ldap_result2error() ,或者某个同步的API操作调用。它返回一个指向静态数据的指针。
ldap_perror()用来输出由msg提供的信息, followed
by an indication of the error contained in the ld_errno field of the
ld connection handle, to standard error.
八、 Calls for parsing(分析) search entries
下面的 调用是用来分析由ldap_search()及其友函数返回的实体的。这些实体是不透明的,只有通过调用下面描述的函数才能访问。
1. Stepping through a set of entries
ldap_first_entry() 和 ldap_next_entry() 用来步查搜寻结果中的实体序列。 ldap_count_entries() 用来统计返回的实体个数。
LDAPMesage *ldap_first_entry( LDAP *ld, LDAPMessage *res );
LDAPMesage *ldap_next_entry( LDAP *ld, LDAPMessage *entry );
int ldap_count_entries( LDAP *ld, LDAPMessage *res );
参数说明:
ld 连接手柄;
res LDAP 操作结果如ldap_result()或其它同步API操作调用
的返回结果;
entry 在ldap_first_entry() 或ldap_next_entry()之前的调用的返
回实体。
ldap_first_entry() 和 ldap_next_entry()如果没有实体返回,就
返回为NULL 。而在出现错误时,也返回NULL ,这时就要设
置连接手柄中的ld_errno 来指示这个错误。
ldap_count_entries() 返回包含在实体链中的实体个数,也用来
统计调用ldap_first_entry() 或 ldap_next_entry()之后,链中剩余的
实体个数。
2. Stepping through the attributes of an entry
ldap_first_attribute() 和 ldap_next_attribute() 用来步查实体
返回的属性类型表。
char *ldap_first_attribute(
LDAP *ld,
LDAPMessage *entry,
void **ptr
);
char *ldap_next_attribute(
LDAP *ld,
LDAPMessage *entry,
void *ptr
);
参数说明:
ld 连接手柄;
entry 被步查属性的实体 ,如ldap_first_entry() 或ldap_next
_entry()返回的;
ptr 在 ldap_first_attribute()中,它是内部跟踪实体当前位置的
指针地址;在ldap_next_attribute()中,它是在ldap_first_
attribute()之前的调用返回的指针。
如果到达属性表的底端时,或有错误时,ldap_first_attribute()和
ldap_next_attribute() 都返回NULL,而在出现错误时就要设置连接
手柄中的ld_errno 来指示这个错误。
3. Retrieving the values of an attribute
ldap_get_values() 和 ldap_get_values_len() 用来恢复给定的实体的属性值。ldap_count_values() 和 ldap_count_values_len() 用来统计返回的值。ldap_value_free() 和 ldap_value_free_len() 用来释放属性值。
typedef struct berval {
unsigned long bv_len;
char *bv_val;
};
char **ldap_get_values(
LDAP *ld,
LDAPMessage *entry,
char *attr
);
struct berval **ldap_get_values_len(
LDAP *ld,
LDAPMessage *entry,
char *attr
);
int ldap_count_values( char **vals );
int ldap_count_values_len( struct berval **vals );
int ldap_value_free( char **vals );
int ldap_value_free_len( struct berval **vals );
参数说明:
ld 连接手柄;
entry 被恢复属性值的实体 ,如ldap_first_entry() 或ldap_next
_entry()返回的;
attr 被恢复值的属性 ,如ldap_first_attribute() 或 ldap_next
_attribute()返回的; or a caller- supplied string (e.g., "mail");
vals 在ldap_get_values() 或 ldap_get_values_len()之前的调用
返回值。
4. Retrieving the name of an entry
ldap_get_dn()用来恢复实体名。ldap_explode_dn()用来把名字
分开,形成若干部分。ldap_dn2ufn()用来把名字转换成 "user friendly"
格式。
char *ldap_get_dn( LDAP *ld, LDAPMessage *entry );
char **ldap_explode_dn( char *dn, int notypes );
char *ldap_dn2ufn( char *dn );
参数说明:
ld 连接手柄;
entry 被恢复名字的实体 ,如ldap_first_entry() 或ldap_next
_entry()返回的;
dn 要被分开的 dn ,如 ldap_get_dn()的返回;
notypes 布尔参数,如果非零,则dn的各成分应该使它们的类
型信息成条带状(如: "cn=Babs" 应变为 "Babs")。
五、举例使用API 及部分样本代码
#include <ldap.h>
main()
{
LDAP *ld;
LDAPMessage *res, *e;
int i;
char *a, *dn;
void *ptr;
char **vals;
/* open a connection */
if ( (ld = ldap_open( "dotted.host.name", LDAP_PORT ))
== NULL )
exit( 1 );
/* authenticate as nobody */
if ( ldap_simple_bind_s( ld, NULL, NULL ) != LDAP_SUCCESS ) {
ldap_perror( ld, "ldap_simple_bind_s" );
exit( 1 );
}
/* search for entries with cn of "Babs Jensen",
return all attrs */
if ( ldap_search_s( ld, "o=University of Michigan, c=US",
LDAP_SCOPE_SUBTREE, "(cn=Babs Jensen)", NULL, 0, &res )
!= LDAP_SUCCESS ) {
ldap_perror( ld, "ldap_search_s" );
exit( 1 );
}
/* step through each entry returned */
for ( e = ldap_first_entry( ld, res ); e != NULL;
e = ldap_next_entry( ld, e ) ) {
/* print its name */
dn = ldap_get_dn( ld, e );
printf( "dn: %s0, dn );
free( dn );
/* print each attribute */
for ( a = ldap_first_attribute( ld, e, &ptr );
a != NULL;
a = ldap_next_attribute( ld, e, ptr ) ) {
printf( "attribute: %s0, a );
/* print each value */
vals = ldap_get_values( ld, e, a );
for ( i = 0; vals[i] != NULL; i++ ) {
printf( "value: %s0, vals[i] );
}
ldap_value_free( vals );
}
}
/* free the search results */
ldap_msgfree( res );
/* close and free connection resources */
ldap_unbind( ld );
}