openfire SASL的使用与SASL协商
<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='example.com' version='1.0'>
<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='c2s_234' from='example.com' version='1.0'>
<stream:features<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <mechanism</mechanism<mechanism</mechanism</mechanisms</stream:features<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>
<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'></challenge<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <incorrect-encoding/> </failure</stream:stream<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'></response<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'></challenge<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <temporary-auth-failure/> </failure</stream:stream<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <temporary-auth-failure/> </failure</stream:stream<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='example.com' version='1.0'>
<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='c2s_345' from='example.com' version='1.0'> <stream:features<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/> <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/> </stream:features服务器-服务器 示例
<stream:stream xmlns='jabber:server' xmlns:stream='http://etherx.jabber.org/streams' to='example.com' version='1.0'>
<stream:stream xmlns='jabber:server' xmlns:stream='http://etherx.jabber.org/streams' from='example.com' id='s2s_234' version='1.0'>
<stream:features<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <mechanism</mechanism<mechanism</mechanism</mechanisms</stream:features<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>
<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'></challenge<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <incorrect-encoding/> </failure</stream:stream<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'></response<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'></challenge<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <invalid-authzid/> </failure</stream:stream<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
<abort xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <aborted/> </failure</stream:stream<stream:stream xmlns='jabber:server' xmlns:stream='http://etherx.jabber.org/streams' to='example.com' version='1.0'>
<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' from='example.com' id='s2s_345' version='1.0'> <stream:features/>
XMPP包含了一个某种意义上XMPP特有的简单验证和安全层协议(见SASL)用于验证一个流. SASL提供一个一般化的方法来给基于连接的协议添加验证支持, 而XMPP遵照SASL的解析要求来使用SASL的XML命名空间解析. SASL扩展的XML命名空间名称是'urn:ietf:params:xml:ns:xmpp-sasl'.
XMPP客户端和服务器必须支持SASL协商.
一个流双方bxu确认SASL是强制协商的.
在SASL协商之后, 双方必须重启该流.
任何将要扮演SASL客户端或SASL服务器的实体必须对于该客户端或该服务器维护一个它推荐的SASL机制的有序列表, 这个列表的顺序是根据本地策略或用户配置来的(它的顺序应该是根据验证能力越强排在越靠前). 初始化实体必须独立于接收方实体的推荐顺序来维护它自己的推荐顺序. 客户端必须以它自己的推荐顺序来尝试SASL机制. 例如, 如果服务器提供的顺序列表是"PLAIN SCRAM-SHA-1 GSSAPI" 或 "SCRAM-SHA-1 GSSAPI PLAIN" 而客户端的顺序列表是 "GSSAPI SCRAM-SHA-1", 客户端必须首先尝试 GSSAPI 然后尝试 SCRAM-SHA-1 而不能(MUST NOT)尝试 PLAIN (因为 PLAIN 不在它的列表中).
如果接收方实体在它接受特定的SASL机制之前确定TLS协商是强制协商, 它不能(MUST NOT)在完成TLS协商之前在它的可用SASL机制列表中声明那个机制.
如果发生以下两种情况,接收方实体应该提供 SASL EXTERNAL 机制,:
无论如何, 接收方实体在其他情况下也一样可以提供 SASL EXTERNAL 机制.
当接收方实体提供 SASL EXTERNAL 机制, 接收方实体应该首先列出它提供的SASL机制的 EXTERNAL 机制列表,而初始化实体应该尝试首先使用EXTERNAL机制来进行SASL协商(这个选择往往会增加双方相互进行证书验证的可能性).
13.8定义了必须支持的SASL机制; 自然的, 也一样可以支持其他的SASL机制.
以下数据格式规则适用于SASL协商:
涉及安全层协商的SASL协商成功后, 初始化实体和接收方实体都必须丢弃任何应用层状态(即, 来自XMPP层的状态, 不包括来自TLS协商或SASL协商的状态).
一些SASL机制(例如, CRAM-MD5, DIGEST-MD5, 和 SCRAM) 指定了在这些机制的上下文中使用的验证身份是一个"简单用户名" (见 SASL 的第二章以及 SASLPREP). 在任何特定的机制或部署中简单用户名的准确格式都是一个本地事务, 并且简单用户名不需要映射到一个应用身份例如JID或JID部件(例如, 本地部分). 无论如何, 在缺乏由服务器提供的本地信息的情况下, 一个XMPP客户端应该假定一个SASL机制的验证身份是等于该用户的JID的本地部分的简单用户名.
授权身份是一个由初始化实体提供的可选的身份,用来定义它扮演的身份(见SASL第二章). 在客户端-服务器流中, 它大部分被管理员用于代表另一个用户来执行一些管理任务, 而在服务器-服务器流它大部分被用于在XMPP服务上指定一个特定的附加服务(例如, 一个多用户聊天服务器conference.example.com寄宿在example.com的XMPP服务上). 如果初始化实体希望代表另一个实体并且所选择的SASL机制支持授权身份的传输, 该初始化实体必须在SASL协商时提供一个授权身份. 如果初始化实体不希望代表另一个实体的身份, 它不能(MUST NOT)提供授权身份.
在客户端-服务器通讯的情况下, 授权身份的值必须是一个纯JID(<本地部分@域部分>) 而不是一个全JID(<本地部分@域部分/资源部分>).
在服务器-服务器通讯的情况下, 授权身份的值必须且只能是一个域部分(<域部分>).
如果初始化实体在SASL协商时提供一个授权身份, 接收方实体负责验证初始化实体是否事实上被允许承担指定的授权身份; 如果不是, 接收方实体必须返回一个<invalid-authzid/> SASL错误,如 6.5.6 所述.
在以特定SASL机制协商的时候接受方实体可以包含一个领域(例如, GSSAPI 和 DIGEST-MD5 机制都允许验证交换的信息中包含领域, 不过其他方式, 如 EXTERNAL, SCRAM, 和 PLAIN 机制不支持这个特性). 如果接受方实体不以一个领域来通讯, 则初始化实体不能(MUST NOT)假定任何领域的存在. 领域必须只被用于验证的目的; 特别是, 初始化实体不能(MUST NOT)尝试从接受方实体提供的领域信息来派生出一个XMPP域部分.
SASL 规定,一个使用中的协议(例如XMPP)可以定义两个方法,这样协议可以节省批准SASL机制的回合:
为了协议的效率, 要求客户端和服务器必须支持这些方法并且建议使用它们; 无论如何, 客户端和服务器也必须支持低效的模式.
SASL协商过程如下.
如果SASL协商紧跟在成功的STARTTLS协商之后, 那么SASL协商发生在已经协商过的受保护的流上. 否则, 初始化实体如第三章所述解析接受方实体的完整域名(FQDN), 打开一个到已解析的IP地址的已声明的端口的TCP连接, 并发送一个初始化流头给接受方实体. 在两种情况下, 接受方实体都将从初始化实体接收到一个初始化流.
<stream:stream from='juliet@im.example.com' to='im.example.com' version='1.0' xml:lang='en' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>
当接受方实体处理来自初始化实体的初始化流的时候, 它必须发送一个应答流头给初始化实体(为此它必须生成一个唯一的流ID. 如果TLS协商已经成功, 那么这个流ID必须不同于TLS协商成功之前发送的那个流ID).
<stream:stream from='im.example.com' id='vgKi/bkYME8OAj4rlXMkpucAqe4=' to='juliet@im.example.com' version='1.0' xml:lang='en' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>
接受方实体也必须发送流特性给初始化实体. 流特性应该包含一个声明用来支持SASL协商, 即, 一个由'urn:ietf:params:xml:ns:xmpp-sasl'命名空间限定的<mechanisms/>元素. 典型的,只有三种情况下对SASL协商的支持不需要在这里声明:
<mechanisms/>元素必须为接受方实体提供给初始化实体的每个验证机制包含一个<mechanism/>子元素. 大家知道, 在XML中的<mechanism/>元素的顺序表示来自接受方实体的SASL机制的优先顺序(它不一定是来自初始化实体的优先顺序).
<stream:features<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <mechanism</mechanism<mechanism</mechanism<mechanism</mechanism<mechanism</mechanism</mechanisms</stream:features初始化为了开始SASL协商, 初始化实体发送一个由'urn:ietf:params:xml:ns:xmpp-sasl'命名空间限定的<auth/>元素并在'mechanism'属性包含一个适当的值, 从而开始使用特定的验证机制进行握手. 这个元素可以包含XML字符串数据(用SASL术语来说, 就是"初始化应答"),如果这个机制支持或必须要它的话. 如果初始化实体需要发送一个长度为零的初始化应答, 它必须以单个等号字符("=")来传输这个应答, 这表示那个应答是当前的但是不包含数据.
<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'></auth挑战-应答序列如果必要, 接收方实体通过发送一个由'urn:ietf:params:xml:ns:xmpp-sasl'命名空间限定的<challenge/>元素来挑战初始化实体; 这个元素可以包含XML字符串数据(它必须根据被初始化实体选择的SASL机制的定义来生成).
初始化实体通过发送一个由'urn:ietf:params:xml:ns:xmpp-sasl'命名空间限定的<response/>元素来应答这个挑战; 这个元素可以包含XML字符串数据(它必须根据被初始化实体选择的SASL机制的定义来生成).
如果必要y, 接收方实体发送更多挑战而初始化实体发送更多应答.
这一系列的 挑战/应答 对 一直持续直到发生以下三件事情之一:
这些场景具体描述在接下来的章节.
初始化实体通过发送一个由'urn:ietf:params:xml:ns:xmpp-sasl'命名空间限定的<abort/>元素放弃为这个验证机制所做的握手.
<abort xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
在接收到一个<abort/>元素之后, 接收方实体必须返回一个由'urn:ietf:params:xml:ns:xmpp-sasl'命名空间限定的<failure/>元素并在其中包含一个<aborted/>子元素.
<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <aborted/> </failureSASL失败接收方实体通过发送一个由'urn:ietf:params:xml:ns:xmpp-sasl'命名空间限定的<failure/>元素来汇报这个验证机制握手失败(特定的失败原因必须放进<failure/>元素的适当子元素,如 6.5定义的).
<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <not-authorized/> </failureSASL成功在确定SASL握手成功之前, 如果初始化实体在一个其保密和诚信得到TLS或同等的安全层(例如SASL GSSAPI机制)保护的初始化流头提供了一个'from'属性,那么接收方实体应该把这个验证身份结果关联到来自SASL协商的'from'地址; 如果这两个身份不匹配,那么接收方实体应该终止连接尝试(然而, 接收方实体可以有合法的理由不终止这个连接尝试, 例如, 因为它覆盖了一个连接的客户端的地址来纠正JID格式或根据终端用户的证书授予一个JID).
接收方实体通过发送一个由'urn:ietf:params:xml:ns:xmpp-sasl'命名空间限定的<success/>元素来汇报握手成功; 这个元素可以包含XML字符串数据(在SASL 属于中, 是"成功的附加数据"), 如果选择的SASL机制支持或者要求它. 如果接收方实体需要发送零长度的附加数据, 它必须传送一个单独的等号字符("=")数据.
<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
一旦接收到<success/>元素, 初始化实体必须在现有的TCP连接上发送一个新的初始化流头到接收方实体来初始化一个新的流(如4.3.3所述, 在发送新的初始化流头之前,初始化实体不能(MUST NOT)发送一个关闭</stream>标签, 因为接收方实体和初始化实体必须确定原始的流被替换成SASL协商成功之后的流).
<stream:stream from='juliet@im.example.com' to='im.example.com' version='1.0' xml:lang='en' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>
一旦从初始化实体接收到新的初始化流头, 接收方实体必须发送一个新的流头给初始化实体来应答(为此它必须生成一个新的流ID而不是重用旧的流ID).
<stream:stream from='im.example.com' id='gPybzaOzBmaADgxKXu9UClbprp0=' to='juliet@im.example.com' version='1.0' xml:lang='en' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>
接收方实体也必须发送流特性, 包含任何更多的可用特性或不包含特性(通过一个空的<features/>元素).
<stream:features<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/> </stream:featuresSASL错误SASL错误的语法如下, 那些用方括号 '[' 和 ']' 括起来的XML数据是可选的.
<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <defined-condition/><text xml:lang='langcode'></text</failureaborted接收方实体确认验证握手已经被初始化实体放弃; 对<abort/>元素发送应答.
<abort xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/><failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <aborted/> </failureaccount-disabled初始化实体的帐号已经被暂时禁用; 对<auth/>元素或<response/>元素发送应答(可以包含或不包含初始化应答数据).
<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'></auth<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <account-disabled/> <text xml:lang='en'></text</failurecredentials-expired因为初始化实体提供的证书过期而验证失败; 对<response/>元素或<auth/>元素发送包含初始化应答数据的应答.
<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'></response<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <credentials-expired/> </failureencryption-required初始化实体请求的机制不能使用,出非当前的流的保密性和完整性收到保护(典型的是通过TLS); 对<auth/>元素发送应答(包含或不包含初始化应答数据).
<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'></auth<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <encryption-required/> </failureincorrect-encoding初始化实体提供的数据无法被处理,因为 base 64 编码不正确(例如, 因为编码没有遵循BASE64的第四章的定义); 对<response/>元素或<auth/>元素发送包含初始化应答数据的应答.
<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'></auth<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <incorrect-encoding/> </failureinvalid-authzid初始化实体提供的authzid是非法的, 要么因为它格式不正确要么因为初始化实体没有权限授权那个ID; 对<response/>元素或<auth/>元素发送包含初始化应答数据的应答.
<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'></response<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <invalid-authzid/> </failureinvalid-mechanism初始化实体没有指定一个机制, 或请求的机制不被接收方实体支持; 对<auth/>元素发送应答.
<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='CRAM-MD5'/><failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <invalid-mechanism/> </failuremalformed-request请求是不良的(例如, <auth/>元素包含了初始化应答数据但是机制不允许这个, 或被发送的数据违反了指定的SASL机制的语法); 对<abort/>, <auth/>, <challenge/>, 或 <response/> 元素发送应答.
(下例中, <auth/>元素的XML字符串数据包含了多于255个UTF-8编码的Unicode字符,所以违反了定义于ANONYMOUS的SASL ANONYMOUS的"token"生产.)
<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='ANONYMOUS'></auth<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <malformed-request/> </failuremechanism-too-weak初始化实体请求的机制弱于服务器策略允许初始化实体使用的机制; 对<auth/>元素发送应答(包含或不包含初始化应答数据).
<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'></auth<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <mechanism-too-weak/> </failurenot-authorized验证失败,因为初始化实体没有提供正确的证书, 或因为发生了一些普通的验证失败而接收方实体不希望泄露失败原因的特定信息; 对<response/>元素或<auth/>元素发送包含初始化应答数据的应答.
<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'></response<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <not-authorized/> </failuretemporary-auth-failure验证失败,因为接收方实体的临时性错误, 可以建议初始化实体晚点再试; 对<auth/>元素或<response/>元素发送应答.
<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'></response<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <temporary-auth-failure/> </failure>