参考:http://blog.163.com/nonnie@126/blog/static/57630308201091275449969/

1.    openldap简介

openldap是在TCP上实现目录服务的系统,一种源代码开放的ldap版本。相对OSI的X.500服务,它使用了较少的代价(10%?)实现了目录服务大部分功能(90%?)。因为它基于TCP网络,且实现的功能没有X.500规定那么复杂,所以成为“轻量级”的目录服务。

目录服务器可以想象为树型结构。一个结点元素称为对象,一个对象可以拥有多个属性(例如某人身兼数值),多个对象拥有相同属性也是可能的(许多人属于某个公司)。objectclass是对属性类型的描述,多个objectclass可以构成一个schema,用于约束加入目录服务器的数据对象。

我们当前的测试基于RedHat Linux 9.0,openldap-2.0.27-8,后台基于dbm数据库(gdbm兼容dbm)。

我们可以把ldap构筑的数据存储系统,理解为数据库结构中的层次模型。

ldap概念

识别名dn:目录中每个对象唯一名字,是这个对象之上的所有对象单一属性加上自身附加属性构成;

相对识别名rdn:以目录中某个结点作为根节点命名其下的对象;

LDIF文件:LDAP数据交换格式。它是一个纯文本文件,每个对象包括许多行,对象定义的第一行以dn开头。它主要用于ldap服务器对数据的导入、导出、交换等。


安装openldap

l安装系统时选择安装openldap

这是最简单的方式。键入命令:

service ldap start

如果系统提示没有错误,则说明ldap服务器已经正确启动。我们目前使用这种方式。

l下载openldap代码,编译安装
一般经过如下步骤:
1.     # tar xzvf openldap-version.tgz
2.     # cd openldap-version
3.     # [env setting]./configure [OPTIONS]
4.     #make depend
5.     #make
6.     # make test
7.     # make install
详细资料可以查看openldap的INSTALL文件,也可以查看在线手册http://www.openldap.org
如果安装最新的openldap版本2.2.24,会遇到许多问题:
       其一是在配置(执行configure脚本)时选择后台数据库的问题,我们必须加入选项设置--disable-bdb屏蔽BDB数据库(Berkeley DataBase);
       其二是系统上原来安装的Cyrus SASL版本过老,必须下载安装新版本。openldap-2.2.24要求SASL版本在2.1.18以上。目前我们没有继续试验源码方式的安装。

4.    配置
服务器端需要配置/etc/openldap/slapd.conf文件。
加入某个schema文件,例如:
include   /etc/openldap/openldap.schema
加入数据库支持:
database       ldbm
声明本地节点后缀:
suffix     “o=aitiso.com,c=cn”
管理员识别名:
rootdn    “cn=root,o=aitiso.com,c=cn”
管理员密码:
rootpw   secret
# rootpw       {CRYPT}B7fF/KIqexnDs
# rootpw       {MD5}iWPLuV9TDL/GdW1ZRcX3vA==
# rootpw       {SMD5}tMQdIbEBsjG9JJTiDgk5A7wnu/o=
# rootpw       {SHA}GQoNhXp4y6KsQMbOZBVaaoSzqsU=
指定数据库文件存放位置:
directory/var/lib/ldap
其他配置项直接使用系统配置即可。

需要介绍的是slappasswd命令,使用下面命令得到加密后的口令,拷贝到配置文件中:
slappasswd-h {加密方法}
New Password:
Re-enter new password:
       {加密方法}密文

5.    访问
5.1.  启动ldap服务器:
手工方式启动:
# /usr/sbin/slapd –f /etc/openldap/slapd.conf –d 256
其中-f指出配置文件的路径名,-d指出debug级别。如果打开调试选项,则slapd把调试信息输出至stdout,如果不使用调试选项,slapd以后台进程方式启动。
以服务方式启动:
# service ldap start

5.2.  关闭ldap服务器
手工关闭:使用kill命令杀死slapd进程。
以服务方式关闭:
# service ldap stop

注意:我们可以使用手工方式启动,然后用服务方式关闭;反之亦然。

5.3.  脚本方式访问ldap
l ldapadd:      用于对ldap目录服务器添加数据,格式为LDIF;
例子:
$    ldapadd - D “cn=root,o=aitiso.com,c=cn” –x –w passwd –f test.ldif
若不给出ldif文件,命令执行成功并不退出,而是等待用户输入下一条修改数据。输入^D结束
选项说明:
-D后给出的是登录DN(此时为管理员DN);
-x说明使用简单认证(明码);
-w后给出密码正文;
-f后给出需要插入的数据文件。
l ldapmodify: 用于修改目录中的对象;
例子:
$    ldapmodify - D “cn=root,o=aitiso.com,c=cn” –x –w passwd
若不给出ldif文件,命令执行成功并不退出,而是等待用户输入下一条修改数据。输入^D结束
选项同上,说明:
-a,表示新加入数据,效果同ldapadd命令。这两个命令底层都使用ldap_add()函数完成。
l ldapdelete:   用于删除目录中的对象;
例子:
$    ldapdelete “ou=communications,o=ait.com,c=cn” –D”cn=root,o=aitiso.com,c=cn” –x –w root
选项同上,说明:
双引号中给出的是需要删除的结点。注意,当结点有非空子结点时(即当前要删除结点不是叶子结点),删除操作失败。如果加上-r选项,则命令将递归删除所有子结点,且不会提示任何确认信息。使用时需要特别小心。
l ldapmodrdn:修改rdn;
例子:
$    ldapmodrdn  - D “cn=root,o=aitiso.com,c=cn” –x –w passwd
选项同上,说明:
-r表示删除原有rdn。
在命令行结束后,用户需要输入原有dn,然后输入修改dn,最后输入^D结束
l ldapsearch:  查找对象;
例子:
$    ldapsearch –D “cn=root,o=aitiso.com,c=cn” –b “o=aitiso.com,c=cn” –x –w passwd ’objectClass=person’
选项同上,说明:
-b后给出搜索的起始结点DN(base DN),它必须给出,否则无法查询出结果;
单引号(双引号也可以)中给出的objectClass用于过滤输出结果(即搜索条件),此处查找的对象类型仅限于person对象,也就是查找aitiso.com,c=cn结点下的所有人员记录。

注意:
1.查询中经常使用的是简单认证,应给出-x选项,使用-w给出密码(或者-W在命令行后输入);
2. ldif文件中某些objectClass必须拥有一些属性(查看schema),在写ldif文件时应注意;
3. 应保证/etc/openldap/slapd.conf能够被ldap组的成员读写,否则无法正确读入配置信息;

5.4.  ldap的C接口
l 需要包含的头文件:
#include 
#include 
l 需要包含的库文件:
-lldap -llber
l 初始化,打开ldap服务器的socket连接
LDAP * ldap_init(ldap_host, port)
char *ldap_host;            /*ldap主机名字*/
int    port                       /*ldap监听的端口号*/
返回值为LDAP结构。
              typedef struct ldap {
                 /* ... other stuff you should not mess with ... */
                 char      ld_lberoptions;
                 int        ld_deref;
            #define LDAP_DEREF_NEVER 0
            #define LDAP_DEREF_SEARCHING 1
            #define LDAP_DEREF_FINDING   2
            #define LDAP_DEREF_ALWAYS    3
                 int       ld_timelimit;
                 int       ld_sizelimit;
            #define LDAP_NO_LIMIT        0
                 int       ld_errno;
                 char      *ld_error;
                 char      *ld_matched;
                 int       ld_refhoplimit;
                 unsigned long  ld_options;
            #define LDAP_OPT_REFERRALS     0x00000002   /* set by default */
            #define LDAP_OPT_RESTART 0x00000004
                 /* ... other stuff you should not mess with ... */
            } LDAP;
l 绑定LDAP服务器
int ldap_bind(ld, who, cred, method)
       LDAP *ld;
       char *who, *cred;
       int method;

int ldap_bind_s(ld, who, cred, method)
       LDAP *ld;
       char *who, *cred;
       int method;
这是打开ldap服务器之后,在访问之前的认证过程。它需要传入ldap初始化结构ld,需要输入DN作为连接访问者who,输入密码cred作为认证信息,并设置认证方式method。
返回值可以使用ldap_error(3)读取,输出。

l 错误输出:ldap_error(3)
       struct ldaperror {
           int e_code;
           char *e_reason;
       };

       struct ldaperror ldap_errlist[];

       char *ldap_err2string(err)
       int err;

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

       int ldap_result2error(ld, res, freeit)
       LDAP *ld;
       LDAPMessage *res;
       int freeit;

如果某步骤出现错误,可以使用ldap_error()捕获错误代码,并输出信息;

l 解除与LDAP服务器的绑定
       int ldap_unbind(ld)
       LDAP *ld;
返回值可以使用ldap_error(3)读取,输出。

l 查询ldap目录服务器的数据
int ldap_search_s(ld, base, scope, filter, attrs, attrsonly, res)
       LDAP *ld;
       char *base;
       int scope;
       char *filter, *attrs[]
       int attrsonly;
       LDAPMessage **res;
选择范围:
1. 设置起始点DN
2. 设置搜索深度(搜索范围)
过滤结果:

l 处理返回的结果数据
int ldap_msgfree(LDAPMessage *msg);      /*释放结果*/
int ldap_count_entries(LDAP *ld,LDAPMessage *result);  /**/
LDAPMessage *ldap_first_entry(LDAP *ld,LDAPMessage *result); /*获取第一个数据结点*/
LDAPMessage *ldap_next_entry(LDAP *ld,LDAPMessage *result); /*获取下一个结点*/

l 获取实际属性和值
char *ldap_first_attribute(LDAP *ld,LDAPMessage *entry,BerElement **ber_element);
char *ldap_next_attribute(LDAP *ld,LDAPMessage *entry,BerElement **ber_element);
char *ldap_get_dn(LDAP *ld, LDAPMessage *entry);
char **ldap_get_value(LDAP *ld,LDAPMessage *entry,char *attribute);
void ldap_value_free(char**value);

 
#include <ldap.h>

int ldap_get_option(LDAP *ld, int option, void *outvalue);

int ldap_set_option(LDAP *ld, int option, const void *invalue);




6.    数据备份和恢复

l 数据备份:


使用命令 slapcat 。缺省 slapcat 把数据输出至标准输出 stdout ,我们可以使用重定向,把输出保存到某个 ldif 文件