这个项目来源于国外某运营商客户的需求,整个开发流程除了参考基本的tacacs+协议外,还借鉴了Github上一个类似项目的设计,下面介绍下协议的基本内容和开发注意事项以及对接测试环境的搭建。
目录
0. 协议简介
1. tacacs+消息头
2.1 认证报文:
2.2 认证响应报文:
3.1 授权请求报文:
3.2 授权响应报文:
4.1 计费请求报文:
4.2 计费响应报文:
5. Attribute-Value Pairs(参数对):
参考文档:
tacacs+协议全称 Terminal Access Controller Access Control System,终端访问控制器控制系统协议。
是在TACACS(RFC 1492)基础上进行了功能增强的安全协议。采用客户端/服务器模式实现,文章末尾会给出RFC文档链接。
tacacs+协议实现了3A功能(Authentication,Authorization and Accounting),基于TCP传输,服务器端默认监听端口49。认证、授权和计费流程可以看作是客户端和服务器之间的一次会话(session)。会话有一个唯一的会话ID,认证会话由多个报文交互组成,授权和计费只包含两个报文(request 和 reply),从这可以看出,会话流程还是相对简单的。
传输的时候需要建立tcp连接,tacacs+报文首部含有一个single connection mode标志位,用于表示是否支持单个连接上存在多个会话,如果不支持的话则每个会话则需要一个独立的连接。
tacacs+报文的消息体需要进行加密,加密的算法是从共享密钥、会话ID、版本和序列号反复计算MD5直到长度和tacacs+消息体相等,这时候再将该密钥和报文内容进行按位异或运算得到加密报文,服务器端收到报文后在进行相应的解密运算。加密流程如下:
//反复计算得到长度为报文长度的MD5值字符串
MD5_1 = MD5{session_id, key, version, seq_no}
MD5_2 = MD5{session_id, key, version, seq_no, MD5_1}
....
MD5_n = MD5{session_id, key, version, seq_no, MD5_n-1}
pseudo_pad = {MD5_1 [,MD5_2 [ ... ,MD5_n]]} truncated to len(data)
//将上述计算得到的值和消息体进行按位异或运算加密
data = ENCRYPTED {data} ^ pseudo_pad
tacacs+报文包含12个字节长度的消息头,格式如下:
版本字段占一个字节, 授权和计费报文版本字段值固定,认证报文的版本字段和认证流程有关,这一点协议已经明确指明了各个认证流程所需要使用的协议字段,下面会有介绍。
类型字段有三种取值:
TAC_PLUS_AUTHEN := 0x01 (Authentication)
TAC_PLUS_AUTHOR := 0x02 (Authorization)
TAC_PLUS_ACCT := 0x03 (Accounting)
seq_no序列号表示会话的报文序号,客户端从1开始发送,服务器端回复的时候则+1。最大不能超过255,超过则需要终止当前会话并重新开始。
标志位目前定义了两个,一个用于表示报文是否加密,另一个则用来表示是否支持单连接多会话。
TAC_PLUS_UNENCRYPTED_FLAG := 0x01
TAC_PLUS_SINGLE_CONNECT_FLAG := 0x04
session_id表示会话ID,在整个会话交互期间保持不变。
length表示tacacs+消息体的长度,注意这个长度不包括tacacs+消息头。
开发的时候需要注意,所有长度字段都需要按照网络字节序编码。
认证报文消息体里面的字段根据认证类型的不同而不同。先看下字段基本的含意和取值:
action表明认证的动作,
TAC_PLUS_AUTHEN_LOGIN := 0x01
TAC_PLUS_AUTHEN_CHPASS := 0x02
TAC_PLUS_AUTHEN_SENDAUTH := 0x04
priv_lvl表明用户的权限级别,协议定义了四个值,也可以自定义需要的值,但不要和已定义的冲突:
TAC_PLUS_PRIV_LVL_MAX := 0x0f
TAC_PLUS_PRIV_LVL_ROOT := 0x0f
TAC_PLUS_PRIV_LVL_USER := 0x01
TAC_PLUS_PRIV_LVL_MIN := 0x00
authen_type表示认证类型:
TAC_PLUS_AUTHEN_TYPE_ASCII := 0x01
TAC_PLUS_AUTHEN_TYPE_PAP := 0x02
TAC_PLUS_AUTHEN_TYPE_CHAP := 0x03
TAC_PLUS_AUTHEN_TYPE_ARAP := 0x04 (deprecated)
TAC_PLUS_AUTHEN_TYPE_MSCHAP := 0x05
TAC_PLUS_AUTHEN_TYPE_MSCHAPV2 := 0x06
authen_service,认证服务:
TAC_PLUS_AUTHEN_SVC_NONE := 0x00
TAC_PLUS_AUTHEN_SVC_LOGIN := 0x01
TAC_PLUS_AUTHEN_SVC_ENABLE := 0x02
TAC_PLUS_AUTHEN_SVC_PPP := 0x03
TAC_PLUS_AUTHEN_SVC_ARAP := 0x04
TAC_PLUS_AUTHEN_SVC_PT := 0x05
TAC_PLUS_AUTHEN_SVC_RCMD := 0x06
TAC_PLUS_AUTHEN_SVC_X25 := 0x07
TAC_PLUS_AUTHEN_SVC_NASI := 0x08
TAC_PLUS_AUTHEN_SVC_FWPROXY := 0x09
user_len表示用户名的字符长度,如果报文里面没有携带用户名的话,填写0
port_len表示端口字段的长度,这个端口即客户端的发送端口
rmt_addr_len表示地址的长度,没有设置的话写0.
data_len表示报文里面data的长度,data和认证类型有关,有些认证流程使用这个字段有些不用。待会讲到基本认证流程会给出说明。
status表示状态:
TAC_PLUS_AUTHEN_STATUS_PASS := 0x01
TAC_PLUS_AUTHEN_STATUS_FAIL := 0x02
TAC_PLUS_AUTHEN_STATUS_GETDATA := 0x03
TAC_PLUS_AUTHEN_STATUS_GETUSER := 0x04
TAC_PLUS_AUTHEN_STATUS_GETPASS := 0x05
TAC_PLUS_AUTHEN_STATUS_RESTART := 0x06
TAC_PLUS_AUTHEN_STATUS_ERROR := 0x07
TAC_PLUS_AUTHEN_STATUS_FOLLOW := 0x21
flag目前之定义了一个值,表明是否回显信息给终端用户:
TAC_PLUS_REPLY_FLAG_NOECHO := 0x01
server_msg是给用户看的信息,data和认证流程有关。
Tacacs+交互流程图:
协议定义了一些基本的认证类型:
1. ASCII Login
action = TAC_PLUS_AUTHEN_LOGIN
authen_type = TAC_PLUS_AUTHEN_TYPE_ASCII
minor_version = 0x0
这是一个标准的ASCII身份验证。Start报文可以包含用户名。如果用户不包含用户名,则
服务器必须使用CONTINUE从客户机获得它TAC_PLUS_AUTHEN_STATUS_GETUSER。如果用户
不提供然后服务器可以发送另一个用户名请求,但服务器必须限制允许重试的次数,建议限
制为3次尝试。当服务器具有用户名时,它将使用带TAC_PLUS_AUTHEN_STATUS_GETPASS的
continue作为密码。ASCII login 用户名和密码都使用user_msg字段。data字段没有使用。
2. PAP 认证
PAP Login
action = TAC_PLUS_AUTHEN_LOGIN
authen_type = TAC_PLUS_AUTHEN_TYPE_PAP
minor_version = 0x1
整个会话必须由一个单独的START报文和一个REPLY报文构成,START报文必须携带用户名,
并且data字段必须携带PAP ASCII密码。REPLY响应报文必须是PASS、FAIL或者ERROR
3. CHAP login
action = TAC_PLUS_AUTHEN_LOGIN
authen_type = TAC_PLUS_AUTHEN_TYPE_CHAP
minor_version = 0x1
The entire exchange MUST consist of a single START packet and a
single REPLY. The START packet MUST contain the username in the user
field and the data field is a concatenation of the PPP id, the
challenge and the response.
The length of the challenge value can be determined from the length
of the data field minus the length of the id (always 1 octet) and the
length of the response field (always 16 octets).
To perform the authentication, the server calculates the PPP hash as
defined in the PPP Authentication RFC RFC 1334 [RFC1334] and then
compare that value with the response. The MD5 algorithm option is
always used. The REPLY from the server MUST be a PASS, FAIL or
ERROR.
The selection of the challenge and its length are not an aspect of
the TACACS+ protocol. However, it is strongly recommended that the
client/endstation interaction is configured with a secure challenge.
The TACACS+ server can help by rejecting authentications where the
challenge is below a minimum length (Minimum recommended is 8 bytes).
4. MS-CHAP v1 login
action = TAC_PLUS_AUTHEN_LOGIN
authen_type = TAC_PLUS_AUTHEN_TYPE_MSCHAP
minor_version = 0x1
The entire exchange MUST consist of a single START packet and a
single REPLY. The START packet MUST contain the username in the user
field and the data field will be a concatenation of the PPP id, the
MS-CHAP challenge and the MS-CHAP response.
The length of the challenge value can be determined from the length
of the data field minus the length of the id (always 1 octet) and the
length of the response field (always 49 octets).
To perform the authentication, the server will use a combination of
MD4 and DES on the user’s secret and the challenge, as defined in RFC
2433 [RFC2433] and then compare the resulting value with the
response. The REPLY from the server MUST be a PASS or FAIL.
For best practices, please refer to RFC 2433 [RFC2433] . The TACACS+
server MUST reject authentications where the challenge deviates from
8 bytes as defined in the RFC.
5. MS-CHAP v2 login
action = TAC_PLUS_AUTHEN_LOGIN
authen_type = TAC_PLUS_AUTHEN_TYPE_MSCHAPV2
minor_version = 0x1
The entire exchange MUST consist of a single START packet and a
single REPLY. The START packet MUST contain the username in the user
field and the data field will be a concatenation of the PPP id, the
MS-CHAP challenge and the MS-CHAP response.
The length of the challenge value can be determined from the length
of the data field minus the length of the id (always 1 octet) and the
length of the response field (always 49 octets).
To perform the authentication, the server will use the algorithm
specified RFC 2759 [RFC2759] on the user’s secret and challenge and
then compare the resulting value with the response. The REPLY from
the server MUST be a PASS or FAIL.
For best practices for MS-CHAP v2, please refer to RFC2759 [RFC2759]
. The TACACS+ server MUST rejects authentications where the challenge
deviates from 16 bytes as defined in the RFC.
6. Enable Request
action = TAC_PLUS_AUTHEN_LOGIN
priv_lvl = implementation dependent
authen_type = not used
service = TAC_PLUS_AUTHEN_SVC_ENABLE
协议上说这是用来改变当前用户的权限的,整个交互流程可能包含多个报文,和ASCII login类似。
为了和其它请求区别,authen_service必须设置成 TAC_PLUS_AUTHEN_SVC_ENABLE。
7. ASCII change password request
action = TAC_PLUS_AUTHEN_CHPASS
authen_type = TAC_PLUS_AUTHEN_TYPE_ASCII
由多条消息组成,和ASCII login类似,用来改变密码的。
当status值为TAC_PLUS_AUTHEN_STATUS_GETPASS表示请求新密码,这条消息可以发送多次。
当status值为TAC_PLUS_AUTHEN_STATUS_GETDATA表示请求旧密码。
终止会话:
客户端可以主动终止会话,即在Continue报文中flag标识设置成TAC_PLUS_CONTINUE_FLAG_ABORT 。
如果设置该标识,data字段可能包含一段信息解释客户端为啥终止。
此外,当收到服务器回复的时候,对于客户端不支持的选项,比如REPLY报文里status字段是
TAC_PLUS_AUTHEN_STATUS_FOLLOW等,可以把它当作Fail来处理。
一次授权操作只有两个报文组成,分别是客户端的Request和服务器端的Reply。
authen_method:表明用户是如何认证的,可选值有如下,协议建议忽略这个字段:
TAC_PLUS_AUTHEN_METH_NOT_SET := 0x00
TAC_PLUS_AUTHEN_METH_NONE := 0x01
TAC_PLUS_AUTHEN_METH_KRB5 := 0x02
TAC_PLUS_AUTHEN_METH_LINE := 0x03
TAC_PLUS_AUTHEN_METH_ENABLE := 0x04
TAC_PLUS_AUTHEN_METH_LOCAL := 0x05
TAC_PLUS_AUTHEN_METH_TACACSPLUS := 0x06
TAC_PLUS_AUTHEN_METH_GUEST := 0x08
TAC_PLUS_AUTHEN_METH_RADIUS := 0x10
TAC_PLUS_AUTHEN_METH_KRB4 := 0x11
TAC_PLUS_AUTHEN_METH_RCMD := 0x20
priv_lvl:和认证报文里面字段一样,表明用户权限。
authen_service, 这个和认证字段一样,表明用户要求授权的服务。
user和user_len表明用户名和用户名长度。
port和port_len,表明本次客户端发送端口,长度为字节。
rem_addr和rem_addr_len和认证报文里面字段一样,表明客户端地址和地址长度,单位为字节。
arg_cnt表明参数个数。
arg_1 ... arg_N, arg_1_len .... arg_N_len:表明参数长度。
授权参数都是以属性-值这种方式呈现,两者中间可以是“=”或者是“*”,= 表示强制的,*表示可选的。协议已经定义了一些属性。
status表示授权状态,协议定义了如下值:
TAC_PLUS_AUTHOR_STATUS_PASS_ADD := 0x01 //授权成功,如果响应报文里面携带有参数,那么这些
参数也要应用。当然响应报文里面的参数也有可能是0
TAC_PLUS_AUTHOR_STATUS_PASS_REPL := 0x02 //授权成功,但是必须要使用响应报文里面的参数来替换。
TAC_PLUS_AUTHOR_STATUS_FAIL := 0x10 //授权失败
TAC_PLUS_AUTHOR_STATUS_ERROR := 0x11 //表明服务器遇到错误
TAC_PLUS_AUTHOR_STATUS_FOLLOW := 0x2 //这个和authen类似,如果客户端不支持的话可以当作失败处理
arg_cnt表示参数个数。
server_msg len和data_len分别表示server_msg和data部分的长度。
计费报文里面字段含义和授权一样。
status可选如下值:
TAC_PLUS_ACCT_FLAG_START := 0x02 //服务开始
TAC_PLUS_ACCT_FLAG_STOP := 0x04 //服务结束
TAC_PLUS_ACCT_FLAG_WATCHDOG := 0x08 //服务更新或者服务仍然在运行
响应报文就更简单了,基本上只需要关注status字段,已定义的值包括:
TAC_PLUS_ACCT_STATUS_SUCCESS := 0x01
TAC_PLUS_ACCT_STATUS_ERROR := 0x02
TAC_PLUS_ACCT_STATUS_FOLLOW := 0x21
计费请求报文里面的标志位可以有如下四种合法组合:
1. service首选项,用在授权和计费报文里面,表明操作的服务类型:
可选值包括:"shell", "tty-server", "connection", "system" and "firewall",这个选项必须要包含到报文里面去。
2. protocol, 可以用来表示服务的子集,可用可不用。
3. cmd, shell命令,如果service=shell的话必须定义cmd选项。这个是最常见的授权选项。
4. cmd-arg,表示shell 命令的参数
其它还有很多,这里只列出了最常见的几种,有需要的话可以去协议里查一下。
Attribute-Value Pairs(计费):
上面的参数主要用在授权里面,计费也有计费的参数。
1. task_id,start和stop同一个服务时必须填写一致的任务id,客户端必须确保taskid不会重复。
2. start_time,表明动作开始时间,这个时间从epoch(1970)开始到现在所经历的秒数。
3. stop_time, 和start_time类似。
4. elapsed_time:表明动作流逝的时间,单位为秒。
所有value的编码需要遵循一些规则:
1. 在attribute-value里的数字应该是无符号类型的ASCII十进制数字。
2. boolean应该是true或者false.
3. ......
以上基本上就是协议的全部内容,协议总共才40几页,内容比较简单,开发的时候有些细节再去协议里确定下就好了。
因为我开发的是tacacs客户端,所以需要和一个tacacs+服务器对接测试,这方面ubuntu已经有了这个软件包,只需要下载下来安装,然后配置数据就好了。具体的安装配置可以参考之前写的一篇简介(ubuntu tacacs+ 服务器安装启动)
使用这个软件可以测试AAA功能。
tacacs+ RFC文档下载连接:
1. https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-12
我自己使用Golang开发的的tacacs+客户端库,有兴趣的同学可以参考下,
1. [email protected]:FuYuanDe/goTacacsPlus.git
1. 【RFC】The TACACS+ Protocol draft-ietf-opsawg-tacacs-12
2. TACACS技术白皮书 http://www.h3c.com/cn/d_201706/1000387_30003_0.htm
=============================================================================================
Linux应用程序、内核、驱动、后台开发交流讨论群(745510310),感兴趣的同学可以加群讨论、交流、资料查找等,前进的道路上