Welcome to Snooda's Blog
ppp/pppd/pptp插件编写手册/指南
[| 2012/07/01 15:27]
最近研究了一下pptp vpn的插件编写。刚开始的时候以为身份认证等信息是pptpd服务进程做的,后来看了代码后发现并非如此,是在pppd那里做的,也就是ppp软件包。
下载了ppp源码看了下,当前版本是2.4.5,项目居然在samba域名下,不知道这个跟samba有啥联系。
在代码的pppd/plugins里面有一些插件,比如radius的,看了下源码。大致了解了下思路:
一个插件即是一个标准的动态链接库。pppd使用dlopen()库调用来加载。pppd使用plugin选项来加载插件,只有一个参数:插件文件名。如果指定的文件名没有斜线,pppd会到/usr/lib/pppd/目前下面查找该文件,这里面version是pppd的版本,比如2.4.5。也可以设置全路径,在使用自己编写的插件时比较方便。
插件可以做的事:
使用add_options(),传入option_t 。最后一项是NULL,跟lighttpd配置是一样的。这个作用应该是时ppp支持相应配置。
指定hook。pppd会在处理过程中调用各个钩子。插件可以任意设置自己的过程到这些hook。
插件可以用add_notifier消息回调。
#include "pppd.h"
#include "chap-new.h"
char pppd_version[] = VERSION;
static int pppd_chap_check(void) {
return 1;
}
static int pppd_chap_verify(char *user, char *ourname, int id, struct chap_digest_type *digest, unsigned char *challenge, unsigned char *response, char *message, int message_space) {
if(digest->verify_response(id, user, "a", strlen("a"), challenge, response, message, message_space)) {
return 1;
} else {
return 0;
}
}
static int check_address_allowed(int addr) {
return 1;
}
void plugin_init(void) {
chap_check_hook = pppd_chap_check;
chap_verify_hook = pppd_chap_verify;
allowed_address_hook = check_address_allowed;
}
这是一个简单的插件源码,如果用户密码是a就通过。可以进行扩充就可以让pppd支持多种不同的权限认证,并且可以支持各种计数操作。该插件支持chap认证,包括mschap,mschap-v2,chap方式,至于pap,由于安全性不行,不支持也无所谓。
这里需要注意allowed_address_hook 需要实现,表示同意使用分配的该ip,没有实现时会在注册网络时失败。
char pppd_version[] = VERSION;
表示支持的ppp版本。
plugin_init,就是插件被载入时执行的操作,执行hook注册等等操作。
chap_check_hook 表示该插件是否可以进行chap认证。
chap_verify_hook 是身份认证的过程,一般使用digest->verify_response来进行校验。
以下是一篇资料里贴出的函数及消息列表:
插件钩子函数列表
int (*idle_time_hook)(struct ppp_idle *idlep);
idle_time_hook在这个连接第一次开始的时候被调用(比如,第一个网络协议开始的时候),那之后被定时调用。在第一次调用的时候,idlep参数是NULL,返回值则是pppd检查连接活跃前的秒数,或者0表示没有超时。
在后来的调用中,idlep指令一个指向最包被发送、接收秒数的结构体。如果返回的值大于0,pppd会在再次检查之前等一些的秒数。如果小于等于0,就是说该连接在不活跃的情况下应该被终结。
int (*holdoff_hook)(void);
当尝试拌连接失败或是连接被终结的时候,holdoff_hook被调用,persist或是demand选项被使用。它返回PPTP重新建立连接应该等待的秒数(0表示立即)。
int (*chap_check_hook)(void);
int (*chap_passwd_hook)(char *user, char *passwd);
int (*chap_auth_hook)(char *user, u_char *remmd, int remmd_len, chap_state *cstate);
这些hook被设计用来在插件里面替换常规的CHAP密码处理过程(比如外部服务器的认证)。
chap_check_hook被调用来检查对端是否有必要向我们认证其自身。如果返回1,pppd将询问询问其自身,否则返回0(如果该认证被要求了,在网络协议协商之前pppd会退出或是终结当前连接)。如果返回-1,pppd将查找chap-secrets文件使用常规方式处理。
chap_passwd_hook决定pppd应该使用什么密码来使用CHAP来向对端认证自身。user字符串使用’user’选项或者’name’选项、主机名进行初始化,有必须可以进行修改。这个钩子只有当ppdp是客户客户端而非服务的时候被调用。passwd可以容纳最多MAXSECRETLEN 字节。如果钩子返回0,PPPD使用 *passwd,如果返回 -1,pppd认证失败。
chap_auth_hook 钩子确定由对应提供的CHAP挑战响应是否有效。user指向一个包含对端提供用户名的非NULL结束的字符串。remmd向向对端提供的响应,由remmd_len表示其长度 。cstate是PPTP维护的内部CHAP状态结构体。chap_auth_hook应该返回CHAP_SUCCESS 或者CHAP_FAILURE。
int (*null_auth_hook)(struct wordlist **paddrs, struct wordlist **popts);
这个钩子允许插件确定当请求的认证被对端拒绝的时候应该采取的策略。 如果返回0,连接被终结;1,连接被允许处理,这种情况下 *paddrs和*popts可像pap_auth_hook一样被设置,以指定被允许的IP地址列表和任意扩展属性。如果返回-1,pppd查找pap-secret文件按常规处理。
void (*ip_choose_hook)(u_int32_t *addrp);
该钩子在IPCP协商开始的时候调用。它使得插件有机会设置对端的IP地址。地方应该保存在*addrp。如果*addrp里什么也没有存储的庆,pppd使用常规方式决定对端的地址。
int (*allowed_address_hook)(u_int32_t addr)
这个钩子确认对端是否可以使用指定的IP地址。如果钩子返回1,地址可以接受,返回0为拒绝。如果返回-1,将使用常规方式查找适当的选项和secrets文件来决定。
void (*snoop_recv_hook)(unsigned char *p, int len)
void (*snoop_send_hook)(unsigned char *p, int len)
这些钩子在接受或是发送数据包的时候被调用。数据包在p里同,长度由 len表式。使得插件可以检查pppd的会话。这些钩子在实现L2TP的时候将会起到很大的作用。
可注册的消息列表
插件可以使用notifier注册自身,通过申明一个下面形式的过程:
void my_notify_proc(void *opaque, int arg);
然后使用适当的notifier,以下面形式调用来注册这个过程。
add_notifier(&interesting_notifier, my_notify_proc, opaque);
add_notifier 中的’opaque’参数每次调用notifier的时候传递给my_notify_proc 。传递的’arg’参数决定于notifier一个nofify过程可以使用下面的方式从当前的notifier列表中被移除。
remove_notifier(&interesting_notifier, my_notify_proc, opaque);
下面是目前pppd实现的notifier列表。
pidchange 由其父进程在pppd已经forked并且子进程在继续pppd’s处理时调用,比如pppd从其控制终端中分离出来的时候。参数就是这个子进程的pid。
phasechange 当pppd从一个阶段操作转移到另一个的时候调用。参数是新阶段的编号。
exitnotify 在pppd退出前调用。参数是pppd退出的状态。(比如exit()的参数)。
sigreceived 在收到信号的时候调用,存在于signal handle里面。参数是signal的编号。
ip_up_notifier 在IPCP发生的时候调用。
ip_down_notifier 在IPCP关闭的时候调用。
auth_up_notifier 在对端认证自身成功的时候调用。
link_down_notifier 在连接关闭的时候调用。
参考资料:http://liaoweiqiang.info/?p=411
下载了ppp源码看了下,当前版本是2.4.5,项目居然在samba域名下,不知道这个跟samba有啥联系。
在代码的pppd/plugins里面有一些插件,比如radius的,看了下源码。大致了解了下思路:
一个插件即是一个标准的动态链接库。pppd使用dlopen()库调用来加载。pppd使用plugin选项来加载插件,只有一个参数:插件文件名。如果指定的文件名没有斜线,pppd会到/usr/lib/pppd/
插件可以做的事:
使用add_options(),传入option_t 。最后一项是NULL,跟lighttpd配置是一样的。这个作用应该是时ppp支持相应配置。
指定hook。pppd会在处理过程中调用各个钩子。插件可以任意设置自己的过程到这些hook。
插件可以用add_notifier消息回调。
引用
#include "pppd.h"
#include "chap-new.h"
char pppd_version[] = VERSION;
static int pppd_chap_check(void) {
return 1;
}
static int pppd_chap_verify(char *user, char *ourname, int id, struct chap_digest_type *digest, unsigned char *challenge, unsigned char *response, char *message, int message_space) {
if(digest->verify_response(id, user, "a", strlen("a"), challenge, response, message, message_space)) {
return 1;
} else {
return 0;
}
}
static int check_address_allowed(int addr) {
return 1;
}
void plugin_init(void) {
chap_check_hook = pppd_chap_check;
chap_verify_hook = pppd_chap_verify;
allowed_address_hook = check_address_allowed;
}
这是一个简单的插件源码,如果用户密码是a就通过。可以进行扩充就可以让pppd支持多种不同的权限认证,并且可以支持各种计数操作。该插件支持chap认证,包括mschap,mschap-v2,chap方式,至于pap,由于安全性不行,不支持也无所谓。
这里需要注意allowed_address_hook 需要实现,表示同意使用分配的该ip,没有实现时会在注册网络时失败。
char pppd_version[] = VERSION;
表示支持的ppp版本。
plugin_init,就是插件被载入时执行的操作,执行hook注册等等操作。
chap_check_hook 表示该插件是否可以进行chap认证。
chap_verify_hook 是身份认证的过程,一般使用digest->verify_response来进行校验。
以下是一篇资料里贴出的函数及消息列表:
插件钩子函数列表
int (*idle_time_hook)(struct ppp_idle *idlep);
idle_time_hook在这个连接第一次开始的时候被调用(比如,第一个网络协议开始的时候),那之后被定时调用。在第一次调用的时候,idlep参数是NULL,返回值则是pppd检查连接活跃前的秒数,或者0表示没有超时。
在后来的调用中,idlep指令一个指向最包被发送、接收秒数的结构体。如果返回的值大于0,pppd会在再次检查之前等一些的秒数。如果小于等于0,就是说该连接在不活跃的情况下应该被终结。
int (*holdoff_hook)(void);
当尝试拌连接失败或是连接被终结的时候,holdoff_hook被调用,persist或是demand选项被使用。它返回PPTP重新建立连接应该等待的秒数(0表示立即)。
int (*chap_check_hook)(void);
int (*chap_passwd_hook)(char *user, char *passwd);
int (*chap_auth_hook)(char *user, u_char *remmd, int remmd_len, chap_state *cstate);
这些hook被设计用来在插件里面替换常规的CHAP密码处理过程(比如外部服务器的认证)。
chap_check_hook被调用来检查对端是否有必要向我们认证其自身。如果返回1,pppd将询问询问其自身,否则返回0(如果该认证被要求了,在网络协议协商之前pppd会退出或是终结当前连接)。如果返回-1,pppd将查找chap-secrets文件使用常规方式处理。
chap_passwd_hook决定pppd应该使用什么密码来使用CHAP来向对端认证自身。user字符串使用’user’选项或者’name’选项、主机名进行初始化,有必须可以进行修改。这个钩子只有当ppdp是客户客户端而非服务的时候被调用。passwd可以容纳最多MAXSECRETLEN 字节。如果钩子返回0,PPPD使用 *passwd,如果返回 -1,pppd认证失败。
chap_auth_hook 钩子确定由对应提供的CHAP挑战响应是否有效。user指向一个包含对端提供用户名的非NULL结束的字符串。remmd向向对端提供的响应,由remmd_len表示其长度 。cstate是PPTP维护的内部CHAP状态结构体。chap_auth_hook应该返回CHAP_SUCCESS 或者CHAP_FAILURE。
int (*null_auth_hook)(struct wordlist **paddrs, struct wordlist **popts);
这个钩子允许插件确定当请求的认证被对端拒绝的时候应该采取的策略。 如果返回0,连接被终结;1,连接被允许处理,这种情况下 *paddrs和*popts可像pap_auth_hook一样被设置,以指定被允许的IP地址列表和任意扩展属性。如果返回-1,pppd查找pap-secret文件按常规处理。
void (*ip_choose_hook)(u_int32_t *addrp);
该钩子在IPCP协商开始的时候调用。它使得插件有机会设置对端的IP地址。地方应该保存在*addrp。如果*addrp里什么也没有存储的庆,pppd使用常规方式决定对端的地址。
int (*allowed_address_hook)(u_int32_t addr)
这个钩子确认对端是否可以使用指定的IP地址。如果钩子返回1,地址可以接受,返回0为拒绝。如果返回-1,将使用常规方式查找适当的选项和secrets文件来决定。
void (*snoop_recv_hook)(unsigned char *p, int len)
void (*snoop_send_hook)(unsigned char *p, int len)
这些钩子在接受或是发送数据包的时候被调用。数据包在p里同,长度由 len表式。使得插件可以检查pppd的会话。这些钩子在实现L2TP的时候将会起到很大的作用。
可注册的消息列表
插件可以使用notifier注册自身,通过申明一个下面形式的过程:
void my_notify_proc(void *opaque, int arg);
然后使用适当的notifier,以下面形式调用来注册这个过程。
add_notifier(&interesting_notifier, my_notify_proc, opaque);
add_notifier 中的’opaque’参数每次调用notifier的时候传递给my_notify_proc 。传递的’arg’参数决定于notifier一个nofify过程可以使用下面的方式从当前的notifier列表中被移除。
remove_notifier(&interesting_notifier, my_notify_proc, opaque);
下面是目前pppd实现的notifier列表。
pidchange 由其父进程在pppd已经forked并且子进程在继续pppd’s处理时调用,比如pppd从其控制终端中分离出来的时候。参数就是这个子进程的pid。
phasechange 当pppd从一个阶段操作转移到另一个的时候调用。参数是新阶段的编号。
exitnotify 在pppd退出前调用。参数是pppd退出的状态。(比如exit()的参数)。
sigreceived 在收到信号的时候调用,存在于signal handle里面。参数是signal的编号。
ip_up_notifier 在IPCP发生的时候调用。
ip_down_notifier 在IPCP关闭的时候调用。
auth_up_notifier 在对端认证自身成功的时候调用。
link_down_notifier 在连接关闭的时候调用。
参考资料:http://liaoweiqiang.info/?p=411