共享秘钥模式下XAUTH验证流程

以下根据strongswan代码中的testing/tests/ikev1/xauth-psk/中的测试环境,来看一下XAUTH验证流程。拓扑结构如下:

在这里插入图片描述

拓扑图中使用到的设备包括:虚拟网关moon和主机carol及dave。

moon网关配置

moon的配置文件:/etc/ipsec.conf,内容如下。注意此处keyexchange字段的值,使用ikev1版本协议。在名称为rw的连接配置中,leftauth和rightauth字段都指定了psk共享秘钥认证,另外rightauth2的值xauth,表明执行第二级的XAUTH认证。

conn %default
        keyexchange=ikev1

conn rw
        left=PH_IP_MOON
        leftid=@moon.strongswan.org
        leftsubnet=10.1.0.0/16
        leftauth=psk
        leftfirewall=yes
        right=%any
        rightauth=psk
        rightauth2=xauth

PSK模式和XAUTH认证的秘钥信息保存在文件/etc/ipsec.secrets中,内容如下。对于ID值为carol@strongswan.org和dave@strongswan.org的连接使用不同的XAUTH秘钥。

moon.strongswan.org %any : PSK 0sv+NkxY9LLZvwj4qCC2o/gGrWDF2d21jL

carol@strongswan.org : XAUTH "4iChxLT3"

dave@strongswan.org  : XAUTH "ryftzG4A"

配置文件/etc/strongswan.conf中的插件xauth-generic负责处理XAUTH相关认证工作。其相关代码位于目录:strongswan-5.8.1/src/libcharon/plugins/xauth_generic/。

charon {
  load = random nonce aes sha1 sha2 hmac curve25519 xauth-generic kernel-netlink socket-default updown stroke
}

主机配置

carol主机的配置文件:/etc/ipsec.conf,内容如下。其中与网关moon使用相同的ikev1版本协议以及共享秘钥psk认证。此外,leftauth2的值xauth表明本机将进行第二级的XAUTH认证。

conn %default
        keyexchange=ikev1

conn home
        left=PH_IP_CAROL
        leftid=carol@strongswan.org
        leftauth=psk
        leftauth2=xauth
        leftfirewall=yes
        right=PH_IP_MOON
        rightsubnet=10.1.0.0/16
        rightid=@moon.strongswan.org
        rightauth=psk
        auto=add

PSK秘钥和XAUTH秘钥保存在carol主机文件/etc/ipsec.secrets中,如下所示。可见其与moon网关的/etc/ipsec.secrets文件中指定的秘钥相同,否则无法完成认证。

: PSK 0sv+NkxY9LLZvwj4qCC2o/gGrWDF2d21jL

carol@strongswan.org : XAUTH "4iChxLT3" 

此外,carol主机的strongswan.conf文件中同样加载了xauth-generic插件。dave主机的配置与carol基本系统,区别在于一些主机相关选项和XAUTH秘钥的差别。

连接建立流程

分别在三台机器上启动strongswan进程,以及在carol和dave主机上启动home连接。

moon::ipsec start
carol::ipsec start
dave::ipsec start
moon::expect-connection rw
carol::expect-connection home
carol::ipsec up home
dave::expect-connection home
dave::ipsec up home

在ikev1的主模式第一报文中,carol主机通告了对XAUTH的支持,如下报文,

在这里插入图片描述

接下来在回复报文中,moon网关同样通告了对XAUTH的支持,如下。

在这里插入图片描述

之后XAUTH认证在第一阶段Main模式和第二阶段Quick模式之间开始进行,其增加了4个Config配置模式的报文来完成XAUTH认证。

在这里插入图片描述

第一个报文由moon网关发到carol的请求报文,类型为:ISAKMP_CFG_REQUEST(1),其请求两个属性:XAUTH_USER_NAME(16521)和XAUTH_USER_PASSWORD(16522)。

在这里插入图片描述

第二个报文为carol的回复报文,类型为:ISAKMP_CFG_REPLY(2),其在属性XAUTH_USER_NAME和XAUTH_USER_PASSWORD中分别携带自身的用户名(carol@strongswan.org)和秘钥(4iChxLT3)。此与moon网关配置的秘钥相同。
在这里插入图片描述

第三个报文为moon网关发送的验证结果报文,类型为:ISAKMP_CFG_SET(3),其中属性XAUTH_STATUS表明,此次认证成功(值为1)。

在这里插入图片描述

最好一个报文为carol对验证结果的确认,类型为:ISAKMP_CFG_ACK(4),其中属性XAUTH_STATUS带有长度为0的值(无值)。

在这里插入图片描述

状态信息

使用ip命令在moon和sun网关上查看创建的GRE隧道设备,ikey和okey两个方向的值都为42。

moon:~# ip -d link show gre-moon
24: gre-moon@NONE: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1472 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/gre 192.168.0.1 peer 192.168.0.2 promiscuity 0 
    gre remote 192.168.0.2 local 192.168.0.1 ttl inherit ikey 0.0.0.42 okey 0.0.0.42 addrgenmode eui64 numtxqueues 1 gso_max_size 65536 gso_max_segs 65535 
moon:~#

sun:~# ip -d link show gre-sun
26: gre-sun@NONE: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1472 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/gre 192.168.0.2 peer 192.168.0.1 promiscuity 0 
    gre remote 192.168.0.1 local 192.168.0.2 ttl inherit ikey 0.0.0.42 okey 0.0.0.42 addrgenmode eui64 numtxqueues 1 gso_max_size 65536 gso_max_segs 65535 
sun:~# 

使用ip route命令查看为gre-moon虚拟接口添加的路由信息,目的网段10.2.0.0/16(bob主机所在网段)的流量路由到gre-moon接口。

moon:~# ip route
default via 192.168.0.254 dev eth0 onlink 
10.1.0.0/16 dev eth1 proto kernel scope link src 10.1.0.1 
10.2.0.0/16 dev gre-moon scope link 
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.1 
moon:~# 

XAUTH通用插件

文件:strongswan-5.8.1/src/libcharon/plugins/xauth_generic/xauth_generic.c为此处使用的xauth_generic插件的实现代码。函数xauth_generic_create_server将创建一个xauth认证服务器,

xauth_generic_t *xauth_generic_create_server(identification_t *server, identification_t *peer, char *profile)
{       
    private_xauth_generic_t *this;
    
    INIT(this,
            .public = {
                    .xauth_method = { 
                            .initiate = _initiate_server,
                            .process = _process_server,
                            .get_identity = _get_identity,
                            .destroy = _destroy,
                    },
            },
            .server = server->clone(server),
            .peer = peer->clone(peer),
    );

初始化函数initiate_server如下,其将创建类型为CFG_REQUEST的报文(对应以上的ISAKMP_CFG_REQUEST报文),并且添加两个配置属性XAUTH_USER_NAME和XAUTH_USER_PASSWORD,但是这两个数据的值都为空(chunk_empty)。此即以上提到的moon网关发送的第一个XAUTH报文。

METHOD(xauth_method_t, initiate_server, status_t, private_xauth_generic_t *this, cp_payload_t **out)
{
    cp_payload_t *cp;

    cp = cp_payload_create_type(PLV1_CONFIGURATION, CFG_REQUEST);
    cp->add_attribute(cp, configuration_attribute_create_chunk(
                            PLV1_CONFIGURATION_ATTRIBUTE, XAUTH_USER_NAME, chunk_empty));
    cp->add_attribute(cp, configuration_attribute_create_chunk(
                            PLV1_CONFIGURATION_ATTRIBUTE, XAUTH_USER_PASSWORD, chunk_empty));
    *out = cp;
    return NEED_MORE;
}

函数process_server负责由认证客户端的回复消息中获取用户名和秘钥属性值,并与服务端的用户秘钥进行对比,验证是否相等。

METHOD(xauth_method_t, process_server, status_t, private_xauth_generic_t *this, cp_payload_t *in, cp_payload_t **out)
{
    enumerator = in->create_attribute_enumerator(in);
    while (enumerator->enumerate(enumerator, &attr))
    {
        switch (attr->get_type(attr))
        {
                case XAUTH_USER_NAME:
                        user = attr->get_chunk(attr);
                        break;
                case XAUTH_USER_PASSWORD:
                        pass = attr->get_chunk(attr);

    while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
    {
        if (chunk_equals_const(shared->get_key(shared), pass))
        {
                status = SUCCESS;

以下函数xauth_generic_create_peer用于创建一个认证客户端结构。与以上的服务端不同,此处的initiate_peer函数为空,客户端不需要进行初始化。

xauth_generic_t *xauth_generic_create_peer(identification_t *server, identification_t *peer, char *profile)
{
    private_xauth_generic_t *this;

    INIT(this,
        .public =  {
                .xauth_method = {
                        .initiate = _initiate_peer,
                        .process = _process_peer,
                        .get_identity = _get_identity,
                        .destroy = _destroy,
                },
        },
        .server = server->clone(server),
        .peer = peer->clone(peer),

客户端处理函数process_peer负责处理服务器的认证消息,这里创建CFG_REPLY类型的回复消息,增加XAUTH_USER_NAME和XAUTH_USER_PASSWORD两个属性字段,并且赋予相应的值。

METHOD(xauth_method_t, process_peer, status_t, private_xauth_generic_t *this, cp_payload_t *in, cp_payload_t **out)
{
    cp = cp_payload_create_type(PLV1_CONFIGURATION, CFG_REPLY);

    enumerator = in->create_attribute_enumerator(in);
    while (enumerator->enumerate(enumerator, &attr))
    {
        switch (attr->get_type(attr))
        {
            case XAUTH_USER_NAME:
                    cp->add_attribute(cp, configuration_attribute_create_chunk(
                                            PLV1_CONFIGURATION_ATTRIBUTE, XAUTH_USER_NAME,
                                            this->peer->get_encoding(this->peer)));
                    break;
            case XAUTH_USER_PASSWORD:
                    shared = lib->credmgr->get_shared(lib->credmgr, type, this->peer, this->server);

                    cp->add_attribute(cp, configuration_attribute_create_chunk(
                                            PLV1_CONFIGURATION_ATTRIBUTE, attr->get_type(attr),
                                                shared->get_key(shared)));

XAUTH任务

XAUTH任务处理代码位于:strongswan-5.8.1/src/libcharon/sa/ikev1/tasks/xauth.c文件中,与以上的xauth插件类似,函数xauth_create首先创建一个private_xauth_t结构,并根据第二个参数initiator的值,创建initiator或者responder任务。

xauth_t *xauth_create(ike_sa_t *ike_sa, bool initiator)
{
        private_xauth_t *this;

        INIT(this,
                .initiator = initiator,
                .ike_sa = ike_sa,
                .status = XAUTH_FAILED,
        );

        if (initiator) {
                this->public.task.build = _build_i;
                this->public.task.process = _process_i;
        } else {
                this->public.task.build = _build_r;
                this->public.task.process = _process_r;

函数build_i_status负责创建XAUTH的认证结果报文,类型为CFG_SET,增加属性XAUTH_STATUS。

METHOD(task_t, build_i_status, status_t, private_xauth_t *this, message_t *message)
{
        cp_payload_t *cp;

        cp = cp_payload_create_type(PLV1_CONFIGURATION, CFG_SET);
        cp->add_attribute(cp,
                        configuration_attribute_create_value(XAUTH_STATUS, this->status));

        message->add_payload(message, (payload_t *)cp);

函数build_r_ack负责创建XAUTH的认证结果却认报文(即XAUTH的第四个报文),类型为CFG_ACK,属性XAUTH_STATUS的值为空(chunk_empty)。

METHOD(task_t, build_r_ack, status_t, private_xauth_t *this, message_t *message)
{
        cp_payload_t *cp;

        cp = cp_payload_create_type(PLV1_CONFIGURATION, CFG_ACK);
        cp->set_identifier(cp, this->identifier);
        cp->add_attribute(cp,
                        configuration_attribute_create_chunk(
                                        PLV1_CONFIGURATION_ATTRIBUTE, XAUTH_STATUS, chunk_empty));

        message->add_payload(message, (payload_t *)cp);

以下build_i函数,如果当前xauth结构没有关联认证方法,使用load_method进行关联,即上一节介绍的xauth_generic通用方法。之后调用方法的initiate函数,对于认证服务器的角色,initiate函数为:initiate_server函数;但是对于认证客户端而言,其initiate函数initiate_peer实际上为空,参见以上的介绍。前者initiate_server函数返回值为NEED_MORE。

METHOD(task_t, build_i, status_t, private_xauth_t *this, message_t *message)
{
        if (!this->xauth)
        {
                cp_payload_t *cp = NULL;

                this->xauth = load_method(this);
                if (!this->xauth) {
                        return FAILED;
                }
                switch (this->xauth->initiate(this->xauth, &cp))
                {
                        case NEED_MORE:
                                break;
                        case SUCCESS:
                                DESTROY_IF(cp);
                                if (add_auth_cfg(this, NULL, FALSE) && allowed(this)) {
                                        this->status = XAUTH_OK;
                                }
                                this->public.task.process = _process_i_status;
                                return build_i_status(this, message);
                        default:
                                return FAILED;
                }
                message->add_payload(message, (payload_t *)cp);
                return NEED_MORE;

函数process_r为认证客户端的处理服务器报文所使用。根据认证服务器的报文类型CFG_REQUEST或者CFG_SET进行不同的处理。对于CFG_REQUEST报文,需要调用xauth_generic插件的process函数指针进行处理,即函数process_peer。最后启动build任务,即build_r_ack函数创建回复报文。

METHOD(task_t, process_r, status_t, private_xauth_t *this, message_t *message)
{
        if (cp->get_type(cp) == CFG_REQUEST) {
                switch (this->xauth->process(this->xauth, cp, &this->cp)) {
                        case NEED_MORE:
                                return NEED_MORE;
                        case SUCCESS:
                        case FAILED:
                        default:
                                break;
                }
                this->cp = NULL;
                return NEED_MORE;
        }
        if (cp->get_type(cp) == CFG_SET) {
                configuration_attribute_t *attribute;
                enumerator_t *enumerator;

                enumerator = cp->create_attribute_enumerator(cp);
                while (enumerator->enumerate(enumerator, &attribute)) {
                        if (attribute->get_type(attribute) == XAUTH_STATUS) {
                                this->status = attribute->get_value(attribute);
                        }
                }
                enumerator->destroy(enumerator);
                if (this->status == XAUTH_OK && add_auth_cfg(this, this->xauth->get_identity(this->xauth), TRUE)) {
                        DBG1(DBG_IKE, "XAuth authentication of '%Y' (myself) successful", this->xauth->get_identity(this->xauth));
                } else {
                        DBG1(DBG_IKE, "XAuth authentication of '%Y' (myself) failed", this->xauth->get_identity(this->xauth));
                }
        }
        this->identifier = cp->get_identifier(cp);
        this->public.task.build = _build_r_ack;

strongswan版本: 5.8.1
内核版本: 5.0

END

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页