6.使用SASL
6.1 概述
XMPP包含一个认证流的方法,此方法依靠一个简单认证与安全层(SASL)协议[SASL]的XMPP-specific profile。SASL提供一个一般化方法,用于给基于连接的协议加认证支持,并且,XMPP使用一个一般化XML命名空间profile,用于SASL,遵从[SASL]的profiling需求。
以下规则应用:
1) 如果两个服务器间发生SASL协商,直到由服务器宣称的域名系统(DNS)主机名被解析了(参考服务器到服务器通信(14.4)),通信才可处理。
2) 如果实始实体能够进行SASL协商,,它必须在初始流头中包含值至少为“1.0”的版本属性。
3) 如果接收实体能够进行SASL协商,它必须在一个<mechanisms/>元素中广告一个或多个认证机制,此元素靠'urn:ietf:params:xml:ns:xmpp-sasl'命名空间响应从初始实体(如果开放流标记包含所设值至少为“1.0”的版本属性)接收的开放流标记认证。
4) 在SASL协商期间,实体不准在根流元素中发送任何空白字符(匹配[XML]内容,产品[3])作为元素间(任何在SASL例子中的空白字符都只是为了便于阅读)的分隔符;这种限制有助于确保合适的安全层字节精度。
5) 任何包含在XML元素中的XML字符数据,在SASL协商期间使用,必须使用base64编码,编码在RFC3548第三节有定义。
6) 如果所提供的一个“简单用户名”能够被选定SASL机制(例:由DIGEST-MD5与CRAM-MD5机制所支持,但不靠EXTERNAL与GSSAPI机制所支持)所支持,在认证期间,初始实体应当作为简单用户名提供它的发送域(IP地址或包含在域标识符中的全认证域名)在服务器对服务器的通信情况下,或是它的已注册帐户名(包含在XMPP结点标识符中的用户或结点名)在客户到服务器的通信情况下。
7) 如果初始实体希望代表其它实体与支持授权身份传输的被选SASL机制来行动,初始实体在SASL协商期间必须提供一个授权身份。如果初始实体不希望代表另一个实体行动,它不准提供一个授权身份。正如[SASL]中指定的,初始实体不准提供一个授权身份,除非一个授权身份不同于缺省授权身份,此缺省授权身份派生于描述在[SASL]中的认证身份。如果提供了,授权身份值对服务器来说必须是<domain>值形式(例:只有一个域标识符),对客户端来说,必须是<node@domain>值形式(例:结点标识符与域标识符)。
8) 靠涉及到安全层协商的SASL协商的成功,接收实体必须抛弃来自本身没有获得SASL协商的初始实体的任何知识。
9) 靠涉及到安全层协商的SASL协商的成功,初始实体必须抛弃来自本身没有获得SASL协商的接收实体的任何知识。
10) 参考必须被支持的相关机制的强制实施技术(14.7)。
6.2叙述
当初始实体使用SASL认证接收实体时,步骤如下:
1) 初始实体请求SASL认证,通过在开放XML流头中包含版本属性,并将其发送给接收实体,属性值设为“1.0”。
2) 发送一个XML流头作为回应后,接收实体广告一个可利用的SASL认证机制列表;列表中每一项都是一个<mechanism/>元素,作为<mechanism/>容器元素的子元素,由'urn:ietf:params:xml:ns:xmpp-sasl'命名空间认证,在流命名空间中,依次是<features/>元素的子元素。如果使用TLS(5)需要在一个特别认证机制可能使用之间建立,接收实体不准提供在TLS协商之前的可利用SASL认证机制列表中的机制。如果初始实体在TLS协商之前出示了有效证书,接收实体应当在SASL协商(参考[SASL])之间,提供SASL EXTERNAL机制给初始实体,虽然EXTERNAL机制可能在其它环境下被提供了。
3) 初始实体选择一个机制,靠发送一个已被'urn:ietf:params:xml:ns:xmpp-sasl'命名空间认定为合格的<auth/>元素给接收实体,并为‘mechanism’属性包含一个合适的值。如果此机制需要XML字符数据,此元素可能包含XML字符数据(在SASL术语中,“初始响应”);如果初始实体需要发送一个0长度的初始响应,它必须按一个单等号符号(“=”)传输此响应,意味着响应出现,但不包含数据。
4) 如果需要,接收实体靠发送一个<chanllenge/>元素来挑战实始实体,此元素由给初始实体的'urn:ietf:params:xml:ns:xmpp-sasl'命名空间来限定;此元素可能包含XML字符数据(必须根据由初始实体选择的SASL机制的定义一致的来计算)。
5) 初始实体响应此挑战,靠发送由'urn:ietf:params:xml:ns:xmpp-sasl'命名空间限定的<response/>元素给接收实体;此元素可能包含XML字符数据(必须根据由初始实体选择的SASL机制的定义一致的来计算)。
6) 如果需要,接收实体发送更多的挑战,初始实体发送更多的响应。
Challenge/response序列对继续,直到以下三种事情之一发生:
1) 初始实体终止握手,靠发送一个由'urn:ietf:params:xml:ns:xmpp-sasl'命名空间限定的<abort/>元素给接收实体。根据接收一个<abort/>元素,接收实体应当允许一个可配置的但合理的重试号(至少2),之后,必须终止TCP连接;这使初始实体(例:一个终端用户客户端)能够忍受已提供的不正确的信任(例:一个错误类型的password)而不用被迫重连。
2) 接收实体报告握手失败,靠发送一个由'urn:ietf:params:xml:ns:xmpp-sasl'命名空间限定的<failure/>元素给初始实体(失败的特殊原因应当以<failure/>元素的子元素进行通信,<failure/>元素定义在SASL错误中(6.4))。如果错误情况发生,接收实体应当允许一个可配置的,但合理的重试号(至少2),之后,它必须终止TCP连接;这使初始实体(例:一个终端用户客户端)能够忍受已提供的不正确的信任(例:一个错误类型的password)而不用被迫重连。
3) 接收实体报告握手成功,靠发送一个由'urn:ietf:params:xml:ns:xmpp-sasl'命名空间限定的<success/>元素给初始实体;此元素可能包含XML字符数据(在SASL术语中,“additional data with success”),如果需要靠选定的SASL机制。根据接收的<success/>元素,初始实体必须靠发送一个开放的XML流头去初始化一个新流给接收实体(它不必事先发送一个关闭</stream>标记,因为接收实体与初始实体必须考虑源流根据发送或接收<success/>元素而将被关闭)。根据从初始实体接收的新流头,接收实体必须发送一个新XML流头给初始实体作为响应,并带有任何可利用的特征(但并不包含STARTTLS与SASL特征)或一个空<features/>元素(重要表示没有其它特征可利用);任何那种其它在此未定义的特征必须由XMPP的相关扩展来定义。
6.3 SASL定义
[SASL]的profiling需求要求协议定义提供以下信息:
服务名:“xmpp”
初始序列:初始实体提供一个开放XML流头后,并且接收实体按此响应后,接收实体提供一个可接收的认证方法列表。初始实体从列表中选择一个方法并作为‘machanism’属性值发送给接收实体,此属性被<auth/>元素拥有,随意的包括一个初始响应以避免环路。
交换序列:挑战与响应通过由接收实体到初始实体<challenge/>元素的交换与由初始实体到接收实体的<response/>元素的交换而执行。接收实体靠发送一个<failure/>元素报告错误,发送一个<success/>元素报告成功;初始实体靠发送<abort/>元素终止交换。根据成功协商,两端都认为源XML流将被关并且新流头由两端实体发送。
安全层协商:安全层在为接收实体发送<success/>元素的关闭“>”字符后立即有效,安全层在为初始实体发送<success/>元素的关闭“>”字符后立即有效。层顺序为:首先是[TCP],然后是[TLS],然后是[SASL],然后是XMPP。
使用授权身份:授权身份可以被XMPP用于指示客户端非缺省<node@domain>或服务器发送<domain>。
6.4 SASL错误
以下是SASL相关错误条件的定义:(略)
1)<aborted/>--
2)<incorrect-encoding/>--
3)<invalid-authzid/>--
4)<invalid-mechanism/>--
5)<mechanism-too-weak/>--
6)<not-authorized/>--
7)<temporary-auth-failure/>--
6.5 客户端到服务器的例子
以下例子显示了使用SASL授权的客户端与服务器端的数据流,正常情况下,是在TLS协商(注:显示在下面的替换步骤用于显示错误情况的协议;他们并不详尽也不是必要的由本例中数据发送而触发。)成功之后。
步1:客户端初始流给服务器:
<stream:stream
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
to='example.com'
version='1.0'>
步2:服务器使用一个流标记作为响应发送给客户端:
<stream:stream
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
id='c2s_234'
from='example.com'
version='1.0'>
步3:服务器通知客户端可利用的认证机制:
<stream:features>
<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<mechanism>DIGEST-MD5</mechanism>
<mechanism>PLAIN</mechanism>
</mechanisms>
</stream:features>
步4:客户端选择一个认证机制:
<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl'
mechanism='DIGEST-MD5'/>
步5:服务器发送一个[BASE64]编码挑战给客户端:
<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> cmVhbG09InNvbWVyZWFsbSIsbm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixxb3A9ImF1dGgiLGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNzCg==
</challenge>
解码挑战是:
realm="somerealm",nonce="OA6MG9tEQGm2hh",/
qop="auth",charset=utf-8,algorithm=md5-sess
步5(替换):服务器返回错误给客户端:
<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<incorrect-encoding/>
</failure>
</stream:stream>
步6:客户端发送一个[BASE64]编码响应挑战:
<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
dXNlcm5hbWU9InNvbWVub2RlIixyZWFsbT0ic29tZXJlYWxtIixub25jZT0i
T0E2TUc5dEVRR20yaGgiLGNub25jZT0iT0E2TUhYaDZWcVRyUmsiLG5jPTAw
MDAwMDAxLHFvcD1hdXRoLGRpZ2VzdC11cmk9InhtcHAvZXhhbXBsZS5jb20i LHJlc3BvbnNlPWQzODhkYWQ5MGQ0YmJkNzYwYTE1MjMyMWYyMTQzYWY3LGNoYXJzZXQ9dXRmLTgK
</response>
步7:服务器发送另一个[BASE64]编码挑战给客户端:
<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZAo=
</challenge>
解码挑战是:
rspauth=ea40f60335c427b5527b84dbabcdfffd
步7(替换):服务器返回错误给客户端:
<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<temporary-auth-failure/>
</failure>
</stream:stream>
步8:客户端响应挑战:
<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
步9:服务器通知客户端认证成功:
<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
步9(替换):服务器通知客户端认证失败:
<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<temporary-auth-failure/>
</failure>
</stream:stream>
步10:客户端初始化一个新流给服务器:
<stream:stream
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
to='example.com'
version='1.0'>
步11:服务器通过发送流头来响应客户端,伴随有任意另外的特征(或空特征元素):
<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>
6.6服务器到服务器的例子
以下例子显示服务器与服务器使用SASL认证的数据流,正常情况下,是在TLS协商之后(注:以下可替换步骤是由失败情况提供的;他们不是详尽的也不是必要的由数据发送而触发)。
步1:Server1初始化流给Server2:
<stream:stream
xmlns='jabber:server'
xmlns:stream='http://etherx.jabber.org/streams'
to='example.com'
version='1.0'>
步2:Server2发送一个流标记响应Server1:
<stream:stream
xmlns='jabber:server'
xmlns:stream='http://etherx.jabber.org/streams'
from='example.com'
id='s2s_234'
version='1.0'>
步3:Server2通知Server1可利用的认证机制:
<stream:features>
<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<mechanism>DIGEST-MD5</mechanism>
<mechanism>KERBEROS_V4</mechanism>
</mechanisms>
</stream:features>
步4:Server1选择一个认证机制:
<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl'
mechanism='DIGEST-MD5'/>
步5:Server2发送一个[BASE64]编码挑战给Server1:
<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
cmVhbG09InNvbWVyZWFsbSIsbm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixxb3A9
ImF1dGgiLGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNz
</challenge>
编码挑战是:
realm="somerealm",nonce="OA6MG9tEQGm2hh",/
qop="auth",charset=utf-8,algorithm=md5-sess
步5(替换):Server2返回错误给Server1
<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<incorrect-encoding/>
</failure>
</stream:stream>
步6:Server1发送[BASE64]编码响应挑战:
<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
dXNlcm5hbWU9ImV4YW1wbGUub3JnIixyZWFsbT0ic29tZXJlYWxtIixub25j
ZT0iT0E2TUc5dEVRR20yaGgiLGNub25jZT0iT0E2TUhYaDZWcVRyUmsiLG5j
PTAwMDAwMDAxLHFvcD1hdXRoLGRpZ2VzdC11cmk9InhtcHAvZXhhbXBsZS5vcmciLHJlc3BvbnNlPWQzODhkYWQ5MGQ0YmJkNzYwYTE1MjMyMWYyMTQzYWY3LGNoYXJzZXQ9dXRmLTgK
</response>
解码响应是:
username="example.org",realm="somerealm",/
nonce="OA6MG9tEQGm2hh",cnonce="OA6MHXh6VqTrRk",/
nc=00000001,qop=auth,digest-uri="xmpp/example.org",/
response=d388dad90d4bbd760a152321f2143af7,charset=utf-8
步7:Server2发送另一个[BASE64]编码挑战给Server1:
<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZAo=
</challenge>
解码挑战是:
rspauth=ea40f60335c427b5527b84dbabcdfffd
步7(替换):Server2返回错误给Server1:
<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<invalid-authzid/>
</failure>
</stream:stream>
步8:Server1响应挑战:
<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
步8(替换):Server1终止协商:
<abort xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
步9:Server2通知Server1成功认证:
<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
步9(替换):Server2通知Server1认证失败:
<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<aborted/>
</failure>
</stream:stream>
步10:Server1初始化一个新流给Server2:
<stream:stream
xmlns='jabber:server'
xmlns:stream='http://etherx.jabber.org/streams'
to='example.com'
version='1.0'>
步11:Server2通过发送一个流头响应Server1,并伴随着其它特征(或空特征元素):
<stream:stream
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
from='example.com'
id='s2s_345'
version='1.0'>
<stream:features/>