2023年6月21日发(作者:)

1 简介

本文定义了C语言应用程序编程接口。LDAP API设计成既强大又简单易用。定义了LDAP的兼容的同步和同步接口,以适应大量不同应用。此文档给出简要LDAP模型概要,可以引导用户如何通过这些API编程取得LDAP信息。

LDAP模型预览

LDAP是轻量级目录访问协议,提供了轻量级的X.500访问前端,或者作为一个独立服务。在两种模式下,LDAP基于C/S模式客户通过TCP连接到Server,通过此连接发送请求和接收响应。

LDAP模型基于条目,条目包含一些对象信息。条目由属性组成,属性拥有一个类型和一个或多个值,每个属性有决定那种值允许出现在属性中的语法(例如:ASCII字符,jepg图片„),并且决定这些值在目录操作中怎样表现其行为。

条目组织为树形结构,通常按政治,地理,组织划分。每个条目相对于它的相邻条目的命名是唯一的,由一个或多个相区别的属性值(RDN)组成。每个属性中至多一个有一个值可以在RDN中使用,例如条目Person Babs Jensen可能将名为“Barbara Jensen”的值作为commonName属性,一个全局条目的唯一名称称为DN,由连接从树的根到条目的一系列RDN构造的。例如,如果Babs在University of Michigan工作,他的DN U-M条目可能是“cn=BarbarsJensen ,o=University of Michigan ,c=US”。DN的格式参照RFC 1779。

操作提供了身份认证,查询并取得信息,修改信息,添加和删除条目。下一节将介绍API概要,包括如何使用这些API并且详细描述这些API的调用实现。

LDAP API 使用概要

应用程序通常按以下四个步骤使用LDAP API

 打开一个到LDAP Server的连接,ldap_open()返回连接句柄,允许多个连接同时打开。

 同LDAP Server验证且/或同X.500 DSA认证。Ldap_bind()及其他相关函数支持不同的认证方法。

 执行ldap操作获取结果。Ldap_search()及相关函数返回的结果可以由ldap_result2error(),ldap_first_entry(),ldap_next_entry()解析。

 关闭连接。由Ldap_next_entry()调用实现。

操作能够同步或异步地执行。同步调用以_s结尾,所以同步搜索的函数为ldap_search_s()。所有同步程序返回一个代表操作结果的指示符。(例如:常量LDAP_SUCCESS或其他错误码)。异步程序返回操作初始化的消息ID。此ID可以被随后的ldap_result()调用取得操作结果(结果集)。一个异步操作可以被ldap_abandon()函数取消。

结果和错误返回到一个非透明的结构LDAPMessage中。程序提供解析此结构,进入条目和返回的属性,等等功能。程序同样提供错误解释工作。下一节详细描述这些程序例程。

调用LDAP操作

本节描述每一LDAP操作调用细节。所有调用使用一个“连接句柄”—一个指向LDAP结构的指针,此结构包含了每一个连接的信息。许多程序返回LDAPMessage结构。这些结构和其他相关结构将在下面需要时详细描述。

2

3

4 4.1 打开一个连接

ldap_open()打开一个连到LDAP Server的连接。

typedef struct ldap {

/* ... opaque parameters ... */

int ld_deref;

int ld_timelimit;

int ld_sizelimit;

int ld_errno;

char *ld_matched;

char *ld_error;

/* ... opaque parameters ... */

} LDAP;

LDAP* ldap_open(char* hostname, int protno);

参数:

hostname:

要连接的运行LDAP Server的服务器的以空格分隔的主机名或点分十进制表示的IP地址列表。这些主机名开始被顺序地尝试连接,并且在第一个连接成功时停止。

Portno:

包含要连接的TCP端口号,缺省的LDAP端口由常量LDAP_PORT提供。

Ldap_open()返回一个“连接句柄”,——指向LDAP结构的指针,它是应该在后继调用保留这个连接。如果连结不能打开将返回NULL。Ldap_bind的一个调用应该在连接之前执行完成其他操作。

调用程序应对LDAP结构的各个字段赋空值。有许多结构中的其它字段为内部库使用的。上面的对这些字段的描述也显示在其他调用描述中。

4.2 目录验证

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 );

Parameters are:

Ld:

连接句柄

Dn:

绑定的条目名称

cred:

验证的信任方式LDAP_AUTH_SIMPLE,LDAP_AUTH_KRBV41,LDAP_AUTH_KRBV42 中的一个,指明验证方法。

Passwd:

Ldap_simple_bind()的密码,它将会和是条目的userPassword属性比较。 有三种类型的绑定调用,提供简单认证,kerberos认证,和两者都使用的通用例程。在kerberos第四版,验证使用通用ldap_bind()例程,cred将被忽略,例程将会赋予一个合法票证以授权可被取得正确的服务票证的票证已经存在。

同步版本的例程以_s结尾。这些例程返回绑定操作的结果,如果操作成功返回LDAP_SUCCESS,失败了返回错误码。在下面的错误捕获一节将给出关于可能的错误和如何捕获的更多信息。

异步版本调用返回绑定初始化消息ID,随后调用ldap_result()取得绑定结果。当出现错误时,程序返回-1,并相应设置LDAP结构的ld_error字段。

需注意的是在绑定调用成功完成以前,不应有其他的试图跨越除了连接的操作。随后的绑定调用可以用来进行在同一连接上的重新验证。

4.3 关闭连接

ldap_unbind()用来与一个目录解绑定并关闭连接。

Int ldap_unbind(LDAP* ld);

参数:

ld:

连接句柄

ldap_unbind()同步状态工作,同目录解绑定,关闭连接,在返回前释放ld结构空间。Ldap_unbing()返回LDAP_SUCCESS(或其他请求不能送到LDAP Server的LDAP错误码)。调用ldap_unbind()后,ld连接句柄将不可用。

4.4 查询

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:

RFC 1558定义的字符串,表示搜索条件。

attr:

指明匹配条目的哪些属性将要返回的NULL结尾的字符串,传NULL串将返回所有可用的属性。

attronly:

boolean值,为0时指明返回属性的类型和属性值,非0值只返回所需类型。

timeout:

在调用ldap_search_st()时,定义本地查询的超时时间。

res:

在同步调用时使用,作为返回结果参数,包含查询调用完成的结果。

Ld连接句柄的三个字段控制查询如何执行。它们是:

Ld_sizelimit:

限制查询返回的条目数量,0代表无限制。

Ld_timelimit:

限制查询时间,以秒为单位,0代表无限制。

Ld_deref:

常量LDAP_DERF_NEVER,LDAP_DEREF_SEARCHING,LDAP_DEREF_FINDING,LDAP_DEREF_ALWAYS之一。描述了在查询过程中如何处理别名。

LDAP_DEREF_SEARCHING值意为在查询中别名被解除,但不会在定位于查询的基对象时解除引用。

LDAP_DERED_FINGINFG意为别名在定位基对象但不是在查询过程中解除引用。 异步查询由ldap_search()初始化。函数返回查询初始化消息ID。查询结果将被随后的ldap_result()调用取得。结果由结果解释程序解释,在下面将详细介绍。如果出错,返回-1,同时ld连接句柄的ld_errno字段将会相应设置。

同步查询通过调用ldap_search_s()和ldap_search_st()执行。这些函数除了ldap_search_st()多一个附加参数以定义查询超时外,都是相同的。两个函数返回查询结果的标识,LDAP_SUCCESS或者错误标识(参阅错误捕获一节)。查询条目返回结果包含在res 参数中。此参数对于调用者是非透明的。条目,属性,值,等等,应被后面说明的函数解析出来。包含在res中的结果应调用ldap_msgfree()释放空间,后面将予以说明。

4.5 读一个条目

LDAP不支持直接的读操作,替代以查询来模拟,设置base参数为要读的条目DN,scope设置为LDAP_SCOPE_BASE,filter设置为“(objectclass=*)”。attrs包含返回的属性列表。

4.6 列出一个条目的子条目

LDAP不支持直接列表操作,替代以查询来模拟,设置base为要列表的条目DN,scope设为LDAP_SCOPE_ONELEVEL,filter设为“(objectclass=*)”。attrs包含返回的每一个子条目的属性列表。

4.7 修改条目

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__strvals

#define mod_bvalues mod__bvals

int ldap_modify( LDAP *ld, char *dn, LDAPMod *mods[] );

int ldap_modify_s( LDAP *ld, char *dn, LDAPMod *mods[] );

参数:

ld:连接句柄

dn:要修改的条目名

mods:

设置修改条目的空结尾的数组。

此参数在LDAPMod结构中有如下意义:

mod_op:

执行的修改操作。为LDAP_MOD_ADD,LDAP_MOD_DELETE,LDAP_MOD_REPLACE之一。此字段也指明在mod_vals联合中的值的类型。此字段同LDAP_MOD_BVALUES进行或操作,以形成mod_bvalues形式。另外,mod_values形式也被使用。

Mod_type:要修改的类型

mod_vals: 此值(如果有的话)将add,delete,replace。仅变量mod_values或mod_bvalues之一可以使用,选择此值同mod_op和LDAP_MOD_BVALUES常量进行或操作。Mod_values为以NULL结尾的NULL结尾的字符串数组,mod_bvalues为NULL结尾的berval结构数组,可以用来传送图像一类的二进制值。

对于LDAP_MOD_ADD修改操作,给定的值是要添加的条目,根据需要创建属性。

对于LDAP_MOD_DELETE修改操作,给定的值是要从条目中被删除的部分,如果没有保留值删除属性。如果条目属性被删除,mod_vals字段应设为NULL。

对于LDA_REPLACE修改操作,属性会在修改后有可列表的值,根据需要被创建。所有的修改操作按列出的顺序执行。

Ldap_modify_s()根据修改操作结果返回LDAP错误码。此代码由ldap_perror()及相关函数解释。

ldap_modify()返回请求初始化消息ID,或出错时返回-1。操作结果由ldap_result()获得。

4.8 修改条目的RDN

ldap_modrdn和ldap_modrn_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:要修改的条目名

newrdn:条目的新的RDN

deleteoldrdn:

布尔值,非0值指明删除旧的RDN,0值指明旧的RDN应保留为条目的非区别值。

ldap_modrdn_s()函数为同步调用,返回指明操作输出的LDAP错误码。

ldap_modrdn()函数为异步调用,返回初始化操作的消息ID,或者出错返回-1。由ldap_result()取得返回结果。

4.9 添加条目

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[] ); 5

6

参数:

ld:连接句柄

dn:要添加的条目名

attrs:

条目属性,使用为ldap_modify()定义的LDAPMod结构。Mod_type和mod_vals字段需要填充,mod_op字段会被忽略,除非同LDAP_MOD_BVALUES位或后,用来选择mod_vals中的mod_bvalues。

注意:条目的父条目必须存在。

Ldap_add_s()为同步函数,返回指明操作输出的LDAP错误代码。

Ldap_add()为异步函数,返回操作初始化消息ID,或出错时返回-1,结果由ldap_result()函数取得。

4.10 删除条目

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_delete_s()为异步函数,返回指明操作输出的LDAP错误代码。

ldap_delete()为同步函数,返回操作初始化消息ID,或出错时返回-1,结果由ldap_result()函数取得。

操作放弃调用

ldap_abandon()用来放弃一个操作过程。

int ldap_abandon(LDAP* ld, int msgid);

ldap_abandon()放弃消息ID为msgid的操作。如果操作成功返回0,否则返回-1。成功调用ldap_abandon()后,给定消息ID的结果不会由ldap_result()调用返回。

取得结果的调用

ldap_result()用来取得先前同步初始化的结果。Ldap_msgfree()用来释放先前调用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:

布尔值,代表查询结果的含义,非0值指明在所有查询结果都应取得后才能返回。如为0,查询结果(条目)将会一次返回查到的一个。

timeout:

表示等待返回结果的超时时间。NULL值将造成ldap_result()阻塞等待,直到结果可用。Timeout值为0秒表示轮询状态。

res:

对于ldap_result(),是一个包含操作结果集的结果参数。

对于ldap_msgfree(),结果要被释放的结果链,从先前的ldap_result(),ldap_search_s或ldap_seatch_st()调用取得。

在成功完成后,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_err字段会相应设置。

Ldap_msgfree()释放指向res的结果结构并返回释放的消息类型。

错误处理调用

下面调用用来捕获其他LDAP API返回的错误。

int ldap_result2error(

LDAP *ld,

LDAPMessage *res,

int freeit

);

7

char* ldap_err2string(int err);

void ldap_perror(LDAP* ld, char* msg);

参数:

ld:

连接句柄

res:

ldap_result()返回的LDAP操作结果,或一个异步API操作调用的结果。

freeit:

布尔值,指明res参数是否被释放(非0值释放,0值不释放)。

err:

LDAP错误码,ldap_result2error()返回的结果或一个同步API操作调用的结果。

msg:在LDAP错误信息之前显示的信息。

Ldap_result2error()用来转换从ldap_result()取得的LDAP结果信息,或由一个异步API操作调用的res参数返回的结果,转换成数字形式的错误码。同时也将结果信息中的ld_matched和ld_error部分解析出来,并将它们放入连接句柄信息中。所有的同步操作函数在返回前调用ldap_result2error(),确保这些字段正确设置。

连接结构的相关字段是:

ld_matched:此参数包含了LDAP Server返回结果的错误信息。

Ld_errno:指明操作输出LDAP 错误码,它是下面常量之一:

LDAP_SUCCESS LDAP_BUSY

LDAP_OPERATIONS_ERROR LDAP_UNAVAILABLE

LDAP_PROTOCOL_ERROR LDAP_UNWILLING_TO_PERFORM

LDAP_TIMELIMIT_EXCEEDED LDAP_LOOP_DETECT

LDAP_SIZELIMIT_EXCEEDED LDAP_NAMING_VIOLATION

LDAP_COMPARE_FALSE LDAP_OBJECT_CLASS_VIOLATION

LDAP_COMPARE_TRUE LDAP_NOT_ALLOWED_ON_NONLEAF

LDAP_STRONG_AUTH_NOT_SUPPORTED LDAP_NOT_ALLOWED_ON_RDN

LDAP_STRONG_AUTH_REQUIRED LDAP_ALREADY_EXISTS

LDAP_NO_SUCH_ATTRIBUTE LDAP_NO_OBJECT_CLASS_MODS

LDAP_UNDEFINED_TYPE LDAP_RESULTS_TOO_LARGE

LDAP_INAPPROPRIATE_MATCHING LDAP_OTHER

LDAP_CONSTRAINT_VIOLATION LDAP_SERVER_DOWN

LDAP_TYPE_OR_VALUE_EXISTS LDAP_LOCAL_ERROR

LDAP_INVALID_SYNTAX LDAP_ENCODING_ERROR

LDAP_NO_SUCH_OBJECT LDAP_DECODING_ERROR

LDAP_ALIAS_PROBLEM LDAP_TIMEOUT

LDAP_INVALID_DN_SYNTAX LDAP_AUTH_UNKNOWN

LDAP_IS_LEAF LDAP_FILTER_ERROR

LDAP_ALIAS_DEREF_PROBLEM LDAP_USER_CANCELLED

LDAP_INAPPROPRIATE_AUTH LDAP_PARAM_ERROR

LDAP_INVALID_CREDENTIALS LDAP_NO_MEMORY

LDAP_INSUFFICIENT_ACCESS

Ldap_err2string()用来转换LDAP错误码数字,此错误码为ldap_result2error()或一个同步API操作调用的返回结果,转换成以NULL结尾的包含错误描述的字符串。它返回指向静态数据的指针。

解释查询条目的调用

以下调用用来解析ldap_search()及相关函数返回的条目。这些条目返回一个只应被下面函数访问的非透明的结构中。这些函数提供遍历所返回的条目,遍历条目的属性,取得条目名称,取得条目的给定属性的属性值。

8 8.1 遍历条目集

ldap_first_entry()和ldap_next_entry()函数用来遍历查询结果的条目集。

Ldap_count_entries()用来计算返回的条目数量。

LDAPMessage* ldap_first_entry(LDAP* ld, LDAPMessage* res);

LDAPMessage* ldap_next_entry(LDAP* ld, LDAPMessage* entry);

int ldap_count_entry(LDAP* ld, LDAPMessage* res);

参数:

ld:连接句柄

res:查询结果,通过调用一个同步查询函数或ldap_result()获得。

Entry:先前的ldap_first_entry()或ldap_next_entry()调用返回的条目。

Ldap_first_entry()和ldap_next_entry在返回结果中没有条目时返回NULL。当在遍历条目时发生错误也返回NULL,这种情况下,ld连接句柄的ld_errno字段会被设置为错误码。

Ldap_count_entries()返回条目链的条目数,它也用来计算调用以ldap_first_entry或ldap_next_entry()返回的条目链中的剩余条目数。

8.2 遍历条目属性

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连接句柄的ld_errno字段会被设置为错误码.

两个函数返回一个指向预先连接的包含当前属性名的缓冲区,。这应看作静态数据。Ldap_first_attribute()将会分配空间并返回指向用来跟踪当前位置的BerElement的指针。此指针应传给后面调用的ldap_next_attribute()以便历条目属性。

返回的属性名用来由ldap_get_values()及相关函数调用解析并取得相关联的值。

8.3 获取属性值

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()返回,或一个调用者提供的字符串(例如:“mail”)。

Vals:

由先前调用的ldap_get_values()或ldap_get_values_len()返回的值。

上面提供了两种形式的变量调用,第一种形式仅适于非二进制的字符串数据,第二种以_len结尾的形式可以使用任何类型的数据。

注意返回的属性值是以malloc分配空间,所以在不使用时,应调用ldap_value_free()或ldap_value_free_len()释放。

8.4 取得条目名称

ldap_get_dn()用来返回条目名称。

Ldap_explode_dn()用来拆分名称为部分名。

Ldap_dn2ufn()用来转换名称为更多的“用户友好”的格式。

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:

布尔型参数,如果非0表明dn部件应有它们自己的类型剥离信息。(例如:“cn=Babs”应成为“Babs”)。

Ldap_get_dn()在解析dn发生错误时返回NULL,设置ld连接句柄的ld_errno字段以指明错误。返回的指针以malloc分配空间,所以调用者在不用时应调free()释放。注意返回的DN的给定格式。

Ldap_explode_dn()返回包含DN提供的RDN部分字符数组,带有或不带类型由notypes参数指定。返回的数组在不再使用时应调Ldap_value_free()释放。

Ldap_dn2ufn()转换DN为用户友好的格式(参照RFC 1781)。UFN返回的是已配好的空间,应在不用时调free()释放。

9

附录:简单LDAP API代码

#include

main()

{

LDAP *ld;

LDAPMessage *res, *e;

int i;

char *a, *dn;

void *ptr;

char **vals;

/* open a connection */

if ( (ld = ldap_open( "", 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 );

}

2023年6月21日发(作者:)

1 简介

本文定义了C语言应用程序编程接口。LDAP API设计成既强大又简单易用。定义了LDAP的兼容的同步和同步接口,以适应大量不同应用。此文档给出简要LDAP模型概要,可以引导用户如何通过这些API编程取得LDAP信息。

LDAP模型预览

LDAP是轻量级目录访问协议,提供了轻量级的X.500访问前端,或者作为一个独立服务。在两种模式下,LDAP基于C/S模式客户通过TCP连接到Server,通过此连接发送请求和接收响应。

LDAP模型基于条目,条目包含一些对象信息。条目由属性组成,属性拥有一个类型和一个或多个值,每个属性有决定那种值允许出现在属性中的语法(例如:ASCII字符,jepg图片„),并且决定这些值在目录操作中怎样表现其行为。

条目组织为树形结构,通常按政治,地理,组织划分。每个条目相对于它的相邻条目的命名是唯一的,由一个或多个相区别的属性值(RDN)组成。每个属性中至多一个有一个值可以在RDN中使用,例如条目Person Babs Jensen可能将名为“Barbara Jensen”的值作为commonName属性,一个全局条目的唯一名称称为DN,由连接从树的根到条目的一系列RDN构造的。例如,如果Babs在University of Michigan工作,他的DN U-M条目可能是“cn=BarbarsJensen ,o=University of Michigan ,c=US”。DN的格式参照RFC 1779。

操作提供了身份认证,查询并取得信息,修改信息,添加和删除条目。下一节将介绍API概要,包括如何使用这些API并且详细描述这些API的调用实现。

LDAP API 使用概要

应用程序通常按以下四个步骤使用LDAP API

 打开一个到LDAP Server的连接,ldap_open()返回连接句柄,允许多个连接同时打开。

 同LDAP Server验证且/或同X.500 DSA认证。Ldap_bind()及其他相关函数支持不同的认证方法。

 执行ldap操作获取结果。Ldap_search()及相关函数返回的结果可以由ldap_result2error(),ldap_first_entry(),ldap_next_entry()解析。

 关闭连接。由Ldap_next_entry()调用实现。

操作能够同步或异步地执行。同步调用以_s结尾,所以同步搜索的函数为ldap_search_s()。所有同步程序返回一个代表操作结果的指示符。(例如:常量LDAP_SUCCESS或其他错误码)。异步程序返回操作初始化的消息ID。此ID可以被随后的ldap_result()调用取得操作结果(结果集)。一个异步操作可以被ldap_abandon()函数取消。

结果和错误返回到一个非透明的结构LDAPMessage中。程序提供解析此结构,进入条目和返回的属性,等等功能。程序同样提供错误解释工作。下一节详细描述这些程序例程。

调用LDAP操作

本节描述每一LDAP操作调用细节。所有调用使用一个“连接句柄”—一个指向LDAP结构的指针,此结构包含了每一个连接的信息。许多程序返回LDAPMessage结构。这些结构和其他相关结构将在下面需要时详细描述。

2

3

4 4.1 打开一个连接

ldap_open()打开一个连到LDAP Server的连接。

typedef struct ldap {

/* ... opaque parameters ... */

int ld_deref;

int ld_timelimit;

int ld_sizelimit;

int ld_errno;

char *ld_matched;

char *ld_error;

/* ... opaque parameters ... */

} LDAP;

LDAP* ldap_open(char* hostname, int protno);

参数:

hostname:

要连接的运行LDAP Server的服务器的以空格分隔的主机名或点分十进制表示的IP地址列表。这些主机名开始被顺序地尝试连接,并且在第一个连接成功时停止。

Portno:

包含要连接的TCP端口号,缺省的LDAP端口由常量LDAP_PORT提供。

Ldap_open()返回一个“连接句柄”,——指向LDAP结构的指针,它是应该在后继调用保留这个连接。如果连结不能打开将返回NULL。Ldap_bind的一个调用应该在连接之前执行完成其他操作。

调用程序应对LDAP结构的各个字段赋空值。有许多结构中的其它字段为内部库使用的。上面的对这些字段的描述也显示在其他调用描述中。

4.2 目录验证

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 );

Parameters are:

Ld:

连接句柄

Dn:

绑定的条目名称

cred:

验证的信任方式LDAP_AUTH_SIMPLE,LDAP_AUTH_KRBV41,LDAP_AUTH_KRBV42 中的一个,指明验证方法。

Passwd:

Ldap_simple_bind()的密码,它将会和是条目的userPassword属性比较。 有三种类型的绑定调用,提供简单认证,kerberos认证,和两者都使用的通用例程。在kerberos第四版,验证使用通用ldap_bind()例程,cred将被忽略,例程将会赋予一个合法票证以授权可被取得正确的服务票证的票证已经存在。

同步版本的例程以_s结尾。这些例程返回绑定操作的结果,如果操作成功返回LDAP_SUCCESS,失败了返回错误码。在下面的错误捕获一节将给出关于可能的错误和如何捕获的更多信息。

异步版本调用返回绑定初始化消息ID,随后调用ldap_result()取得绑定结果。当出现错误时,程序返回-1,并相应设置LDAP结构的ld_error字段。

需注意的是在绑定调用成功完成以前,不应有其他的试图跨越除了连接的操作。随后的绑定调用可以用来进行在同一连接上的重新验证。

4.3 关闭连接

ldap_unbind()用来与一个目录解绑定并关闭连接。

Int ldap_unbind(LDAP* ld);

参数:

ld:

连接句柄

ldap_unbind()同步状态工作,同目录解绑定,关闭连接,在返回前释放ld结构空间。Ldap_unbing()返回LDAP_SUCCESS(或其他请求不能送到LDAP Server的LDAP错误码)。调用ldap_unbind()后,ld连接句柄将不可用。

4.4 查询

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:

RFC 1558定义的字符串,表示搜索条件。

attr:

指明匹配条目的哪些属性将要返回的NULL结尾的字符串,传NULL串将返回所有可用的属性。

attronly:

boolean值,为0时指明返回属性的类型和属性值,非0值只返回所需类型。

timeout:

在调用ldap_search_st()时,定义本地查询的超时时间。

res:

在同步调用时使用,作为返回结果参数,包含查询调用完成的结果。

Ld连接句柄的三个字段控制查询如何执行。它们是:

Ld_sizelimit:

限制查询返回的条目数量,0代表无限制。

Ld_timelimit:

限制查询时间,以秒为单位,0代表无限制。

Ld_deref:

常量LDAP_DERF_NEVER,LDAP_DEREF_SEARCHING,LDAP_DEREF_FINDING,LDAP_DEREF_ALWAYS之一。描述了在查询过程中如何处理别名。

LDAP_DEREF_SEARCHING值意为在查询中别名被解除,但不会在定位于查询的基对象时解除引用。

LDAP_DERED_FINGINFG意为别名在定位基对象但不是在查询过程中解除引用。 异步查询由ldap_search()初始化。函数返回查询初始化消息ID。查询结果将被随后的ldap_result()调用取得。结果由结果解释程序解释,在下面将详细介绍。如果出错,返回-1,同时ld连接句柄的ld_errno字段将会相应设置。

同步查询通过调用ldap_search_s()和ldap_search_st()执行。这些函数除了ldap_search_st()多一个附加参数以定义查询超时外,都是相同的。两个函数返回查询结果的标识,LDAP_SUCCESS或者错误标识(参阅错误捕获一节)。查询条目返回结果包含在res 参数中。此参数对于调用者是非透明的。条目,属性,值,等等,应被后面说明的函数解析出来。包含在res中的结果应调用ldap_msgfree()释放空间,后面将予以说明。

4.5 读一个条目

LDAP不支持直接的读操作,替代以查询来模拟,设置base参数为要读的条目DN,scope设置为LDAP_SCOPE_BASE,filter设置为“(objectclass=*)”。attrs包含返回的属性列表。

4.6 列出一个条目的子条目

LDAP不支持直接列表操作,替代以查询来模拟,设置base为要列表的条目DN,scope设为LDAP_SCOPE_ONELEVEL,filter设为“(objectclass=*)”。attrs包含返回的每一个子条目的属性列表。

4.7 修改条目

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__strvals

#define mod_bvalues mod__bvals

int ldap_modify( LDAP *ld, char *dn, LDAPMod *mods[] );

int ldap_modify_s( LDAP *ld, char *dn, LDAPMod *mods[] );

参数:

ld:连接句柄

dn:要修改的条目名

mods:

设置修改条目的空结尾的数组。

此参数在LDAPMod结构中有如下意义:

mod_op:

执行的修改操作。为LDAP_MOD_ADD,LDAP_MOD_DELETE,LDAP_MOD_REPLACE之一。此字段也指明在mod_vals联合中的值的类型。此字段同LDAP_MOD_BVALUES进行或操作,以形成mod_bvalues形式。另外,mod_values形式也被使用。

Mod_type:要修改的类型

mod_vals: 此值(如果有的话)将add,delete,replace。仅变量mod_values或mod_bvalues之一可以使用,选择此值同mod_op和LDAP_MOD_BVALUES常量进行或操作。Mod_values为以NULL结尾的NULL结尾的字符串数组,mod_bvalues为NULL结尾的berval结构数组,可以用来传送图像一类的二进制值。

对于LDAP_MOD_ADD修改操作,给定的值是要添加的条目,根据需要创建属性。

对于LDAP_MOD_DELETE修改操作,给定的值是要从条目中被删除的部分,如果没有保留值删除属性。如果条目属性被删除,mod_vals字段应设为NULL。

对于LDA_REPLACE修改操作,属性会在修改后有可列表的值,根据需要被创建。所有的修改操作按列出的顺序执行。

Ldap_modify_s()根据修改操作结果返回LDAP错误码。此代码由ldap_perror()及相关函数解释。

ldap_modify()返回请求初始化消息ID,或出错时返回-1。操作结果由ldap_result()获得。

4.8 修改条目的RDN

ldap_modrdn和ldap_modrn_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:要修改的条目名

newrdn:条目的新的RDN

deleteoldrdn:

布尔值,非0值指明删除旧的RDN,0值指明旧的RDN应保留为条目的非区别值。

ldap_modrdn_s()函数为同步调用,返回指明操作输出的LDAP错误码。

ldap_modrdn()函数为异步调用,返回初始化操作的消息ID,或者出错返回-1。由ldap_result()取得返回结果。

4.9 添加条目

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[] ); 5

6

参数:

ld:连接句柄

dn:要添加的条目名

attrs:

条目属性,使用为ldap_modify()定义的LDAPMod结构。Mod_type和mod_vals字段需要填充,mod_op字段会被忽略,除非同LDAP_MOD_BVALUES位或后,用来选择mod_vals中的mod_bvalues。

注意:条目的父条目必须存在。

Ldap_add_s()为同步函数,返回指明操作输出的LDAP错误代码。

Ldap_add()为异步函数,返回操作初始化消息ID,或出错时返回-1,结果由ldap_result()函数取得。

4.10 删除条目

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_delete_s()为异步函数,返回指明操作输出的LDAP错误代码。

ldap_delete()为同步函数,返回操作初始化消息ID,或出错时返回-1,结果由ldap_result()函数取得。

操作放弃调用

ldap_abandon()用来放弃一个操作过程。

int ldap_abandon(LDAP* ld, int msgid);

ldap_abandon()放弃消息ID为msgid的操作。如果操作成功返回0,否则返回-1。成功调用ldap_abandon()后,给定消息ID的结果不会由ldap_result()调用返回。

取得结果的调用

ldap_result()用来取得先前同步初始化的结果。Ldap_msgfree()用来释放先前调用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:

布尔值,代表查询结果的含义,非0值指明在所有查询结果都应取得后才能返回。如为0,查询结果(条目)将会一次返回查到的一个。

timeout:

表示等待返回结果的超时时间。NULL值将造成ldap_result()阻塞等待,直到结果可用。Timeout值为0秒表示轮询状态。

res:

对于ldap_result(),是一个包含操作结果集的结果参数。

对于ldap_msgfree(),结果要被释放的结果链,从先前的ldap_result(),ldap_search_s或ldap_seatch_st()调用取得。

在成功完成后,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_err字段会相应设置。

Ldap_msgfree()释放指向res的结果结构并返回释放的消息类型。

错误处理调用

下面调用用来捕获其他LDAP API返回的错误。

int ldap_result2error(

LDAP *ld,

LDAPMessage *res,

int freeit

);

7

char* ldap_err2string(int err);

void ldap_perror(LDAP* ld, char* msg);

参数:

ld:

连接句柄

res:

ldap_result()返回的LDAP操作结果,或一个异步API操作调用的结果。

freeit:

布尔值,指明res参数是否被释放(非0值释放,0值不释放)。

err:

LDAP错误码,ldap_result2error()返回的结果或一个同步API操作调用的结果。

msg:在LDAP错误信息之前显示的信息。

Ldap_result2error()用来转换从ldap_result()取得的LDAP结果信息,或由一个异步API操作调用的res参数返回的结果,转换成数字形式的错误码。同时也将结果信息中的ld_matched和ld_error部分解析出来,并将它们放入连接句柄信息中。所有的同步操作函数在返回前调用ldap_result2error(),确保这些字段正确设置。

连接结构的相关字段是:

ld_matched:此参数包含了LDAP Server返回结果的错误信息。

Ld_errno:指明操作输出LDAP 错误码,它是下面常量之一:

LDAP_SUCCESS LDAP_BUSY

LDAP_OPERATIONS_ERROR LDAP_UNAVAILABLE

LDAP_PROTOCOL_ERROR LDAP_UNWILLING_TO_PERFORM

LDAP_TIMELIMIT_EXCEEDED LDAP_LOOP_DETECT

LDAP_SIZELIMIT_EXCEEDED LDAP_NAMING_VIOLATION

LDAP_COMPARE_FALSE LDAP_OBJECT_CLASS_VIOLATION

LDAP_COMPARE_TRUE LDAP_NOT_ALLOWED_ON_NONLEAF

LDAP_STRONG_AUTH_NOT_SUPPORTED LDAP_NOT_ALLOWED_ON_RDN

LDAP_STRONG_AUTH_REQUIRED LDAP_ALREADY_EXISTS

LDAP_NO_SUCH_ATTRIBUTE LDAP_NO_OBJECT_CLASS_MODS

LDAP_UNDEFINED_TYPE LDAP_RESULTS_TOO_LARGE

LDAP_INAPPROPRIATE_MATCHING LDAP_OTHER

LDAP_CONSTRAINT_VIOLATION LDAP_SERVER_DOWN

LDAP_TYPE_OR_VALUE_EXISTS LDAP_LOCAL_ERROR

LDAP_INVALID_SYNTAX LDAP_ENCODING_ERROR

LDAP_NO_SUCH_OBJECT LDAP_DECODING_ERROR

LDAP_ALIAS_PROBLEM LDAP_TIMEOUT

LDAP_INVALID_DN_SYNTAX LDAP_AUTH_UNKNOWN

LDAP_IS_LEAF LDAP_FILTER_ERROR

LDAP_ALIAS_DEREF_PROBLEM LDAP_USER_CANCELLED

LDAP_INAPPROPRIATE_AUTH LDAP_PARAM_ERROR

LDAP_INVALID_CREDENTIALS LDAP_NO_MEMORY

LDAP_INSUFFICIENT_ACCESS

Ldap_err2string()用来转换LDAP错误码数字,此错误码为ldap_result2error()或一个同步API操作调用的返回结果,转换成以NULL结尾的包含错误描述的字符串。它返回指向静态数据的指针。

解释查询条目的调用

以下调用用来解析ldap_search()及相关函数返回的条目。这些条目返回一个只应被下面函数访问的非透明的结构中。这些函数提供遍历所返回的条目,遍历条目的属性,取得条目名称,取得条目的给定属性的属性值。

8 8.1 遍历条目集

ldap_first_entry()和ldap_next_entry()函数用来遍历查询结果的条目集。

Ldap_count_entries()用来计算返回的条目数量。

LDAPMessage* ldap_first_entry(LDAP* ld, LDAPMessage* res);

LDAPMessage* ldap_next_entry(LDAP* ld, LDAPMessage* entry);

int ldap_count_entry(LDAP* ld, LDAPMessage* res);

参数:

ld:连接句柄

res:查询结果,通过调用一个同步查询函数或ldap_result()获得。

Entry:先前的ldap_first_entry()或ldap_next_entry()调用返回的条目。

Ldap_first_entry()和ldap_next_entry在返回结果中没有条目时返回NULL。当在遍历条目时发生错误也返回NULL,这种情况下,ld连接句柄的ld_errno字段会被设置为错误码。

Ldap_count_entries()返回条目链的条目数,它也用来计算调用以ldap_first_entry或ldap_next_entry()返回的条目链中的剩余条目数。

8.2 遍历条目属性

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连接句柄的ld_errno字段会被设置为错误码.

两个函数返回一个指向预先连接的包含当前属性名的缓冲区,。这应看作静态数据。Ldap_first_attribute()将会分配空间并返回指向用来跟踪当前位置的BerElement的指针。此指针应传给后面调用的ldap_next_attribute()以便历条目属性。

返回的属性名用来由ldap_get_values()及相关函数调用解析并取得相关联的值。

8.3 获取属性值

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()返回,或一个调用者提供的字符串(例如:“mail”)。

Vals:

由先前调用的ldap_get_values()或ldap_get_values_len()返回的值。

上面提供了两种形式的变量调用,第一种形式仅适于非二进制的字符串数据,第二种以_len结尾的形式可以使用任何类型的数据。

注意返回的属性值是以malloc分配空间,所以在不使用时,应调用ldap_value_free()或ldap_value_free_len()释放。

8.4 取得条目名称

ldap_get_dn()用来返回条目名称。

Ldap_explode_dn()用来拆分名称为部分名。

Ldap_dn2ufn()用来转换名称为更多的“用户友好”的格式。

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:

布尔型参数,如果非0表明dn部件应有它们自己的类型剥离信息。(例如:“cn=Babs”应成为“Babs”)。

Ldap_get_dn()在解析dn发生错误时返回NULL,设置ld连接句柄的ld_errno字段以指明错误。返回的指针以malloc分配空间,所以调用者在不用时应调free()释放。注意返回的DN的给定格式。

Ldap_explode_dn()返回包含DN提供的RDN部分字符数组,带有或不带类型由notypes参数指定。返回的数组在不再使用时应调Ldap_value_free()释放。

Ldap_dn2ufn()转换DN为用户友好的格式(参照RFC 1781)。UFN返回的是已配好的空间,应在不用时调free()释放。

9

附录:简单LDAP API代码

#include

main()

{

LDAP *ld;

LDAPMessage *res, *e;

int i;

char *a, *dn;

void *ptr;

char **vals;

/* open a connection */

if ( (ld = ldap_open( "", 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 );

}