LDAP API
介紹
這篇文檔定義了 LDAP API (C語言版)。它使用方便,功能強大,大致內容有以下幾個方面:
簡單瀏覽LDAP模型
應用程序怎樣使用API去獲取LDAP信息
詳細介紹API 調用函數
舉例使用API 及部分樣本代碼
下面我們分別介紹。
簡單瀏覽LDAP模型
LDAP 是以client-server 模型爲基礎的,在此模型中,客戶機可以建立與LDAP服務器的連接,從而發送請求,接收應答。
LDAP的信息模型是基於實體(entry)的,每個實體都代表着某種對象類型的特例。比如組織機構、用戶、網絡、計算機等。每一種實體又包含有許多屬性,每種屬性又由它的實體所代表的對象的類型決定。
實體用樹型結構組織再一起,通常根據政治上的,地理學的,和組織的關係進行分類。每一個實體都有唯一的名字通過它的RDN與它的同屬實體相連。
三、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 );
}