可扩展的消息和出席信息协议 (XMPP): 核心协议
关于本文的说明
本文为互联网社区定义了一个互联网标准跟踪协议,并且申请讨论协议和提出了改进的建议。请参照“互联网官方协议标准”的最新版本(STD 1)获得这个协议的标准化进程和状态。本文可以不受限制的分发。
版权声明
本文版权属于互联网社区 (C) The Internet Society (2004).
摘要
本文定义了可扩展消息和出席信息协议(XMPP)的核心功能,这个协议采用XML流实现在任意两个网络终端接近实时的交换结构化信息。XMPP提供一个通用的可扩展的框架来交换XML数据,它主要用来建立即时消息和出席信息应用以实现 RFC 2779 的需求。
目录
- 绪论
- 通用的架构
- 地址空间
- XML流
- TLS的使用
- SASL的使用
- 资源绑定
- 服务器回拨
- XML节
- 服务器处理XML节的规则
- XMPP中的XML用法
- 核心的兼容性要求
- 国际化事项
- 安全性事项
- IANA事项
- 参考
1.1. 概览
XMPP是一个开放式的XML协议,设计用于准实时消息和出席信息以及请求-响应服务。其基本的语法和语义最初主要是由Jabber开放源代码社区于1999年开发的。2002年,XMPP工作组被授权接手开发和改编Jabber协议以适应IETF的消息和出席信息技术。作为XMPP工作组的成果,本文定义了 XMPP 1.0 的核心功能;在 RFC 2779 [IMP-REQS] 中指定的提供即时消息和出席信息功能的扩展,定义在 XMPP-IM 协议 [the Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence] 中。
1.2. 术语
本文中大写的关键字 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", 和 "OPTIONAL" 的确切含义符合 BCP 14, RFC 2119 [TERMS].
2.1. 概览
尽管XMPP没有结合任何特定的网络结构,通常认为它是 客户-服务器架构的一种实现,在这里客户端用XMPP的方式访问服务器采用的是TCP连接,服务器之间的通信也是TCP连接。
以下是这一架构的抽象的示意图 (这里 "-" 表示使用 XMPP 通讯, "=" 表示可使用任何协议通讯)。
C1----S1---S2---C3
|
C2----+---G1===FN1===FC1
符号代表的意思如下:
- C1, C2, C3 = XMPP 客户端
- S1, S2 = XMPP 服务器
- G1 = 一个XMPP和外部(非XMPP)消息网络之间进行“翻译”的网关
- FN1 = 一个外部消息网络
- FC1 = 外部消息网络上的一个客户端
2.2. 服务器
服务器充当XMPP通信的一个智能抽象层,它主要负责:
- 管理发出的连接或其他实体的会话,在XML流(第四章)的表单中接收和发送给授权的客户端,服务器和其他实体。
- 用XML流通过实体转发特定地址的XML消息(第九章)
大部分 XMPP 兼容的服务器也负责存储客户端使用的数据 (比如基于XMPP应用的联系人名单); 在这种情况下, XML 数据直接由服务器代替客户端处理而不需要转发到其他实体。
2.3. 客户端
大部分客户端通过 TCP 连接直接连到服务器,并通过XMPP获得由服务器和任何相关的服务所提供的全部功能。多个不同资源(比如不同的设备和地点)的客户端可以同时登陆并且并发的连接到一个服务器,每个不同资源的客户端通过XMPP地址的资源标识符来区分(比如<node@domain/ home> 和 <node@domain/work>),参见地址空间(第三章)。__建议__的客户端和服务器连接的端口是 5222 ,这个端口已经在 IANA(在第十五章第九节查阅端口号码) 注册了。.
2.4. 网关
网关是一个特殊用途的服务器端的服务,主要功能是把 XMPP 翻译成外部(非XMPP)消息系统,并把返回的消息翻译成 XMPP 。例如到 email(参见 [SMTP] ),IRC(参见 [IRC] ),SIMPLE(参见 [SIMPLE] ),SMS 的网关;还有和别的消息服务的网关,比如AIM,ICQ,MSN Messenger,Yahoo! Instant Messenger。网关和服务器之间的通信,网关和外部消息系统的通信,不在本文描述范围之内。
2.5. 网络
因为每个服务器都是由一个网络地址来标识的并且服务器之间的通信是 客户-服务器协议的直接扩展,实际上整个系统是由很多互通的服务器构成的。例如, <juliet@example.com> 可以和 <romeo@example.net> 交换消息,出席信息和其他信息。这种模式常见于那些需要使网络地址标准化的协议(比如 SMTP )。任意两个服务器之间的通信是可选(OPTIONAL)的。如果被激活,那么这种通信应该(SHOULD)通过 XML 流绑定到 TCP 连接上进行。建议的(RECOMMENDED)服务器之间的连接端口为 5269 ,这个端口号已经在 IANA (在第十五章第九节查阅端口号码)注册了。
3.1. 概览
一个实体可以是任何一个被认为是一个网络端点的东西(例如网络上的一个 ID ),而且它是通过XMPP进行通信的。所有这些实体都有一个具有唯一性的地址,并符合 RFC 2396 [URI]规范要求的格式。由于历史原因,一个 XMPP 实体的地址被称为 Jabber Identifier 或 JID 。一个合法的 JID 包括一组排列好的元素,包括域名(domain identifier),节点名(node identifier),和资源名(resource identifier)。
JID的语法定义,使用 [ABNF] 中的 Augmented Backus-Naur 格式。(IPv4 地址和 IPv6地址规则在 附录B 中的 [IPv6] 中定义;确定节点规则的合法字符顺序由 附录A 的 [STRINGPREP] 的 Nodeprep 部分来定义;确定资源规则的合法字符顺序由 附录B 的 [STRINGPREP] 的Resourceprep 部分来定义;子域名规则参考 [IDNA] 中关于国际域名标签的描述。)。
jid = [ node "@" ] domain [ "/" resource ]
domain = fqdn / address-literal
fqdn = (sub-domain 1*("." sub-domain))
sub-domain = (internationalized domain label)
address-literal = IPv4address / IPv6address
所有 JID 都是基于上述的结构。类似 <user@host/resource> 这种结构,最常用来标识一个即时消息用户,这个用户所连接的服务器,以及这个用户用于连接的资源(比如特定类型的客户端软件)。不过,节点类型不是客户端也是有可能的,比如一个用来提供多用户聊天服务的特定的聊天室,地址可以是 <room@service> (这里 “room“ 是聊天室的名字而 ”service“ 是多用户聊天服务的主机名),而加入了这个聊天室的某个特定的用户的地址则是 <room@service/nick> (这里 ”nick“ 是用户在聊天室的昵称)。许多其他的 JID 类型都是可能的(例如 <domain/resource> 可能是一个服务器端的脚本或服务)。
一个 JID 的每个合法部分(节点名,域名,资源名)的长度不能(MUST NOT)超过 1023 字节。也就是整体长度(包括 '@' 和 '/' )不能超过 3071 字节。
3.2. 域名
域名是一个主要的ID并且是 JID 中唯一必需(REQUIRED)的元素(一个纯粹的域名也是一个合法的 JID)。它通常代表网络的网关或者“主”服务器,其他实体通过连接它来实现 XML 转发和数据管理功能。然而,由一个域名标识引用的实体,并非总是一个服务器,它也可能是一个服务器的子域地址,提供额外的功能(比如多用户聊天服务,用户目录,或一个到外部消息系统的网关)。
每个服务器或者服务的域名,可以(MAY)是一个 IP 地址,但应该(SHOULD)是一个完全合法的域名(参见 [DNS]).一个域名ID必须(MUST)是 [IANA] 里定义的“国际化域名”,并且按 [STRINGPREP]中的 [NAMEPREP] profile进行成功的字符转换。在比较两个域名ID之前,服务器必须(MUST),客户端应该(SHOULD)首先按照 Nameprep profile(定义在[IANA]中) 来转换每个域名的字符。
3.3. 节点名
节点名是一个可选(OPTIONAL)的第二 ID,放在域名之前并用符号"@"分开.它通常表示一个向服务器或网关请求和使用网络服务的实体(比如一个客户端),当然它也能够表示其他的实体(比如在多用户聊天系统中的一个房间). 节点名所代表的实体,它的地址依赖于一个特定的域名;在 XMPP 的即时消息和出席信息应用系统中,这个地址是“纯 JID” <node@domain> 中的一部分。
一个节点名必须按 [STRINGPREP] 中的 Nodeprep profile 进行成功的字符转换。在比较两个节点ID之前,服务器必须(MUST),客户端应该(SHOULD)首先按 Nodeprep profile 转换每个ID的字符。
3.4. 资源名
资源名是一个可选的第三 ID,它放在域名的后面并由符号"/"分开。资源名可以跟在 <node@domain>后面也可以跟在 <domain> 后面。它通常表示一个特定的会话,连接(比如设备或者所在位置),或者一个附属于某个节点ID实体相关实体的对象(比如多用户聊天室中的一个参加者)。对于服务器和和其他客户端来说,资源名是不透明的。当它提供必需的信息以完成资源绑定(参见第七章)的时候,通常是由客户端来实现的(尽管可以由客户端向服务器请求完成),然后显示为“已连接的资源”。一个实体可以(MAY)并发维护多个已连接的资源。每个已连接的资源由不同的资源ID来区分。
一个资源名必须(MUST)按 [STRINGPREP] 中的 Resourceprep profile 进行成功的格式化。在比较两个资源ID之前,服务器必须(MUST),客户端应该(SHOULD)首先按 Resourceprep profile 转换每个ID的字符。
3.5. 地址的确认
在 SASL (见第六章)握手之后(如果必要的话,也在资源绑定(见第七章)之后),正在接收流信息的实体必须(MUST)确认初始实体的 ID 。
对于服务器间的通信,在 SASL 握手时,如果没有指明授权的ID,这个初始的实体应该(SHOULD)是经过认证实体(参见 简单认证和安全层协议 [SASL] 中的定义)授权的ID(见第六章)。
对于客户端和服务器的通信,在 SASL 握手时,如果没有指明授权的ID,“纯 JID” (<node@domain>)应该(SHOULD)是经过认证实体(参见 [SASL] 中的定义)授权的ID,“全 JID” (<node@domain/resource>)的资源ID部分应该(SHOULD)是由客户端和服务器在资源绑定的时候商定的(参见第七章)。
接收的实体必须(MUST)确保结果JID(包括节点名,域名,资源名以及分隔符)与本章前面部分描述的规则和格式相一致;为了满足这些约束条件,接收实体可能(MAY)需要把初始实体的发送方 JID 替换成接收实体认可的规范 JID。
4.1. 概览
两个基本概念,XML流和XML节,使得在出席信息已知的实体之间,异步交换低负载的结构化信息成为可能。这两个术语定义如下:
XML流的定义:一个XML流是一个容器,包含了两个实体之间通过网络交换的XML元素。一个XML流是由一个XML打开标签 <stream> (包含适当的属性和名字空间声明)开始的,流的结尾则是一个XML关闭L标签 </stream> 。在流的整个生命周期,初始化它的实体可以通过流发送大量的XML元素,用于流的握手(例如 TLS 握手(第五章) 或 SASL 握手(第六章))或XML节(在这里指符合缺省名字空间的元素,包括<message/>,<presence/>, 或 <iq/> 元素)。“初始的流”由初始化实体(通常是一个客户端或服务器)和接收实体(通常是一个服务器)握手,从接收实体来看,它就是那个初始实体的"会话".初始化流允许从初始化实体到接收实体的单向通信;为了使接收实体能够和初始实体交换信息,接收实体必须发起一个反向的握手(应答流).
XML节的定义: 一个XML节是一个实体通过 XML 流向另一个实体发送的结构化信息中的一个离散的语义单位。一个XML直接存在于根元素<stream/>的下一级,并且如果这样就能够满足[XML]内容的production 43,那么它被认为是均衡的.任何XML节都是从一个XML流的下一级的某个打开标签(如 <presence>)开始,到相应的关闭标签(如 </presence>)。一个XML节可以(MAY)包含子元素(相关的属性,元素, 和 XML 字符数据等) 以表达完整的信息.在这里定义的XML节仅限于<message/>, <presence/>, 和 <iq/> 元素,具体描述间见 XML Stanzas(第九章);为TLS握手(第五章)、SASL握手(第六章)、服务器回拨(第八章)的需要而发送的XML元素,不被认为是一个XML节。
设想一个客户端和服务器会话的例子。为了连接一个服务器,一个客户端必须(MUST)发送一个打开标签<stream>给服务器,初始化一个XML流,也可选择(OPTIONAL)在这之前发送一段文本声明XML版本和支持的字符集(参见文本声明的内容(第十一章第四节); 也可看字符编码(第十一章第五节))。视本地化策略和提供的服务而定,服务器应该(SHOULD)回复一个XML流给客户端,同样的,也可选择在这之前发送一段文本声明。一旦客户端完成了SASL握手(第六章),客户端可以(MAY)通过流发送不限量的XML节给网络中的任何接收者。当客户端想关闭这个流,它只需要简单的发送一个关闭标签</stream>给服务器(或者作为另一个选择,可能由服务器关闭这个流)。然后,客户端和服务器都应该(SHOULD)彻底地终止这个连接(通常是一个TCP连接)。
那些习惯认为XML是一个以文本为中心风格的人可能希望看看一个与服务器连接的客户端会话,包含两个 打开-关闭 XML文档:一个是从客户端到服务器,一个是从服务器到客户端。下图中,根元素<stream/> 可以被认为是每个“文档”的文档实体,这两个“文档”通过累积那些在XML上传输的XML节来搭建的。无论如何,下图只是方便理解;实际上XMPP并不处理文档而是处理XML流和XML节。
基本上,一个XML流相当于一个会话期间所有XML节的一个信封。我们可以简单的把它描述成下图:
|----------------------------
| <stream>
|----------------------------
| <presence>
| <show/>
| </presence>
|----------------------------
| <message to='foo'>
| <body/>
| </message>
|----------------------------
| <iq to='bar'>
| <query/>
| </iq>
|----------------------------
| …
|----------------------------
| </stream>
|----------------------------
4.2. 绑定到TCP
虽然有很多非必需的连接使用XML流来绑定[TCP]连接(两个实体可以通过别的机制来互联,比如通过[HTPP]连接轮询),本规范只定义了 XMPP 到 TCP 的绑定。在客户和服务器通信的过程中,服务器必须(MUST)允许客户端共享一个TCP连接来传输XML节,包括从客户端传到服务器和从服务器传到客户端。在服务器之间的通信过程中,服务器必须(MUST)用一个 TCP连接 向对方发送 XML节,另一个 TCP连接(由对方初始化)接收对方的XML节,一共两个 TCP连接。
4.3. 流的安全
在XMPP 1.0中,当XML流开始握手时,TLS应该(SHOULD)按第五章:TLS的使用 中的规定来使用,SASL必须(MUST)按 第六章:SASL的使用中的规定来使用。尽管可能(MAY)存在某种共有的机制能够保证双向安全,但是“初始化流”(比如从初始化实体发给接收实体的流)和“应答流”(比如从接收实体发给初始化实体的流)还是必须(MUST)安全的分开。在流被验证之间,实体不应该(SHOULD NOT) 尝试通过流发送XML节(第九章);就算它这样做了,对方的实体也不能(MUST NOT)接受这些XML节,并且应该(SHOULD)返回一个 <not-authorized/> 的流错误信息并且终止当前TCP连接上双方的XML流;注意,这仅仅是针对XML节(包含在缺省命名空间中的 <message/>, <presence/>, 和 <iq/> 元素),而不是指那些用于 TLS握手(第五章)、SASL握手(第六章)握手的流。
4.4. 流属性
流元素的属性如下:
- to -- 'to'属性应该(SHOULD)仅用于从初始化实体到接收实体的 XML流的头,并且必须(MUST)设成为接收实体提供服务的主机名。注意,不应该(SHOULD NOT)有 'to'属性出现在接收实体应答初始实体的 XML流的头中;无论如何,如果'to'属性出现在应答流中,初始化实体应该(SHOULD)忽略它。
- from -- 'from'属性应该(SHOULD)仅用于接收实体应答初始化实体的 XML流的头,并且必须(MUST)设成为接收实体(正在给初始实体授权)提供服务的主机名。注意,不应该(SHOULD NOT)有 'from'属性出现在初始实体发送给接收实体的 XML流的头中;无论如何,如果'from'属性出现在初始化流中,接收实体应该(SHOULD)忽略它。
- id -- 'id'属性应该(SHOULD)仅用于接收实体发送给初始化实体 XML流的头。这个属性是一个由接收实体创建的具有唯一性的ID,一个初始实体和接收实体之间的会话ID,并且它在接收方的应用程序中(通常是一个服务器)必须(MUST)是唯一的。注意,这个流 ID 必须是足够安全的,所以它必须是不可预知的和不可重复的(参见 RANDOM 了解如何获得随机性以保证安全性)。不应该(SHOULD NOT)有 'id'属性出现在初始实体发送给接收实体的 XML流的头中;无论如何,如果'id'属性出现在初始化流中,接收实体应该(SHOULD)忽略它。
- xml:lang -- 'xml:lang'属性(定义在[XML]中的第二章第十二节)应该(SHOULD)包含在初始化实体发给接收实体的 XML流的头中,以指定在流中传输的可读XML字符所使用的缺省语言。如果这个属性出现了,接收实体应该(SHOULD)记住它的值,作为初始化流和应答流的缺省属性;如果这个属性没有出现,接收实体应该(SHOULD)用一个可配置的缺省值用于双方的流,这个属性值必须(MUST)在应答流的头中传达。对于所有初始化流中传输的节,如果初始实体没有提供'xml:lang'属性,接收实体应该(SHOULD)应用缺省值;如果初始实体提供了'xml:lang'属性,接收实体不能(MUST NOT)修改或删除它(参见第九章第一节第五小节 xml:lang)。'xml:lang'属性的值必须(MUST)是一个 NMTOKEN (定义在[XML]的第二章第三节) 并且必须(MUST)遵守 RFC 3066 [LANGTAGS] 规定的格式。
- version -- version属性(最少需要"1.0")为本规范中和流相关的协议提供了支持。关于这个属性的生成和处理的详细规则将在下文中定义。
我们现在可以总结如下:
初始化方发给接收方 | 接收方发给初始化方 | |
---|---|---|
to | 接收方的主机名 | 忽略 |
from | 忽略 | 接收方的主机名 |
id | 忽略 | 会话键值 |
xml:lang | 缺省语言 | 缺省语言 |
version | 支持XMPP 1.0 | 支持XMPP 1.0 |
4.4.1. 版本支持
在这里XMPP的版本是"1.0";准确地说,这里囊括了和流相关的所有协议(TLS的使用 (第五章),SASL的使用(第六章), 和流错误(第四章第七节)),以及三个定义好的XML节类型(<message/>,<presence/>,和 <iq/>)。XMPP版本号的编号顺序是“<主版本号>.<副版本号>”。主版本和副版本号必须(MUST)是独立的整数并且每个号码可以(MAY)单独以阿拉伯数字增长。这样,"XMPP 2.4"的版本将比"XMPP 2.13"更低。号码前面 的“0”(比如XMPP 6.01)必须(MUST)被接收方忽略并且不能(MUST NOT)被发送出去.
如果流和节的格式或者必需的处理方式有了显著的改变,以至于老版本的实体如果只是简单的忽略它不理解的节和属性并且继续像老版本一样的处理方式,会使得老版本的实体不能够和新版本的实体交互,只有在这时候主版本号才应该(SHOULD)增加。副版本号显示新的性能,它必须(MUST)被副版本号更低的尸体忽略,但被高(副)版本号的实体用于了解信息。例如,一个副版本号显示处理某种新定义的"type"属性的值(用于message,presence或IQ节)的能力;副版本号高的实体将会了解到与之通信的对方不能够理解这个"type"属性的值,所以将不会发送它。
以下规则是用于'版本'属性在实现流的头信息时如何生成和处理:
- 初始化实体必须(MUST)在初始化流的头信息中把'版本'的值设置成它所支持的最高版本。(比如,如果最高版本支持就是本规范,那么它必须(MUST)设置成"1.0").
- 接收实体必须(MUST)在应答流的头信息中把'版本'的值设置成初始化实体所提供的版本或它所支持的最高版本,取其中版本号较低的那一个。接收实体必须(MUST)把主版本号和副版本号作为数字来比较,而不是对"主版本号.副版本号"这个字符串进行比较.
- 如果在应答流的头信息的版本号中至少有一个主版本号低于初始化流的头信息的版本号,并且如前所述,新版本的实体不能够和旧版本实体交互,初始化实体应该(SHUOULD)生成一个<unsupported-version/>的流错误信息并终止XML流和它的TCP连接。
- 如果一个实体收到一个头信息中没有'version'属性的流,这个实体必须(MUST)把对方实体的'version'当成'0.0'并且在它发送的应答流的头中也不应该(SHOULD NOT)包含'version'属性.
4.5. 名字空间声明
流的元素必须(MUST)同时满足一个流名字空间声明和一个缺省名字空间声明("名字空间声明"定义在 XML 名字空间定义 [XML-NAMES]中).关于流名字空间和缺省名字空间的详细信息,参考 名字空间的名字和前缀(第十一章第二节).
4.6. 流的特性
如果初始化的实体在初始化流的头信息中设置'version'属性的信息为"1.0",接收实体必须(MUST)向初始化实体发送一个 <features/> 子元素以声明任何可供协商的流一级的特性(或者其他需要声明的能力).目前,这仅用于声明本文中定义的 TLS的使用(第五章),SASL的使用(第六章)和资源绑定(第七章),以及 XMPP-IM 中定义的会话的建立;无论如何,流特性这一功能将来可以用于声明任何可协商的特性.如果一个实体不理解或支持安全特性,它应该(SHOULD)忽略它.如果要在一个非安全相关的特性(比如资源绑定)被提议之前,完成一个或多个安全特性(比如TLS和SASL)的协商,这个非安全相关的特性不应该(SHOULD NOT)在相应的安全特性协商完毕之前被声明.
4.7. 流错误
流的根元素可以(MAY)包含一个 <error/> 子元素,由流的名字空间前缀作为它的前缀。这个"错误"子元素必须(MUST)由感知到发生了流级别错误的实体发送(通常是一个服务器而不是一个客户端)。
4.7.1. 规则
以下规则适用于流级别的错误:
- 它假定所有流级别的错误都是不可恢复的;所以,如果一个错误发生在流级别,发现这个错误的实体必须(MUST)发送一个流错误信息给另一个实体,发送一个关闭标签 </stream>,并终止这个流所在的TCP连接。
- 如果这个错误发生在流刚开始设置的时候,接收实体必须(MUST)仍然发送一个开放标签 <stream> ,并在流元素中包含一个<error/>的子元素,然后发送一个关闭标签 </stream>,最后终止相应的TCP连接。在这种情况下,如果初始化实体在 'to' 属性中提供了一个未知的主机名,服务器应该(SHOULD)在终止之前,先在流的头信息的 'from' 属性中提供一个服务器认证的主机名.
4.7.2. 语法
流错误的语法如下:
<stream:error>
<defined-condition xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>
<text xmlns='urn:ietf:params:xml:ns:xmpp-streams'
xml:lang='langcode'>
OPTIONAL descriptive text
</text>
[OPTIONAL application-specific condition element]
</stream:error>
<error/>元素:
- 必须(MUST)包含一个子元素以描述一个下文定义的节错误条件;这个子元素必须(MUST)符合'urn:ietf:params:xml:ns:xmpp-streams'名字空间.
- 可以(MAY)包含一个 <text/> 子元素,用XML字符数据描述错误的细节;这个元素必须(MUST)符合'urn:ietf:params:xml:ns:xmpp-streams'名字空间并且应该(SHOULD)拥有一个'xml:lang'属性表明XML字符数据的自然语言。
- 可以(MAY)包含一个子元素用于描述一个明确的应用程序错误条件;这个元素必须(MUST)符合一个应用程序定义的名字空间,并且它的结构是由那个名字空间定义的。
<text/> 元素是可选的(OPTIONAL)。如果有这个元素,它应该(SHOULD)仅用于提供描述或调试信息以补充一个已定义的条件或应用程序定义的条件。它不应该(SHOULD NOT)被一个应用程序当成一个可编程的信息。它不应该(SHOULD NOT)被用于向用户表达错误信息,但是可以(MAY)作为和条件元素相关的错误信息之外的附加说明。
4.7.3. 已定义的条件
以下流级别的错误条件是已定义的:
- <bad-format/> -- 实体已经发送XML但是不能被处理;这个错误可以(可以)被更多特定的XML相关的错误替换,比如 <bad-namespace-prefix/>, <invalid-xml/>, <restricted-xml/>, <unsupported-encoding/>, 以及 <xml-not-well-formed/>,尽管更多特定的错误是首选的。
- <bad-namespace-prefix/> -- 实体发送的名字空间前缀不被支持,或者在一个需要某种前缀的元素中没有发送一个名字空间前缀(参见 XML Namespace Names and Prefixes (第十一章第二节)).
- <conflict/> -- 服务器正在关闭为这个实体激活的流,因为一个和已经存在的流有冲突的新的流已经被初始化。
- <connection-timeout/> -- 实体已经很长时间没有通过这个流发生任何通信流量(可由一个本地服务策略来配置).
- <host-gone/> -- 初始化实体在流的头信息中提供的'to'属性的值所指定的主机已经不再由这台服务器提供
- <host-unknown/> -- 由初始化实体在流的头信息中提供的 'to' 属性的值和由服务器提供的主机名不一致.
- <improper-addressing/> -- 一个在两台服务器之间传送的节缺少 'to' 或 'from' 属性(或者这个属性没有值).
- <internal-server-error/> -- 服务器配置错误或者其他未定义的内部错误,使得服务器无法提供流服务.
- <invalid-from/> -- 在'from'属性中提供的 JID 或 主机名地址,和认证的 JID不匹配 或服务器之间无法通过SASL(或回拨)协商出合法的域名,或客户端和服务器之间无法通过它进行认证和资源绑定。
- <invalid-id/> -- 流 ID 或回拨 ID 是非法的或和以前提供的 ID 不一致.
- <invalid-namespace/> -- 流名字空间和 "http://etherx.jabber.org/streams" 不相同或回拨名字空间和 "jabber:server:dialback" 不相同.(参考 XML Namespace Names and Prefixes (第十一章第二节)).
- <invalid-xml/> -- 实体通过流发送了一个非法的XML给执行验证的服务器 (参考 Validation (第十一章第三节)).
- <not-authorized/> -- 实体试图在流被验证之前发送数据或不被许可执行一个和流协商有关的动作,接收实体在发送错误信息之前不允许(MUST NOT)处理厌恶的节。
- <policy-violation/> -- 实体违反了某些本地服务策略;服务器可以(MAY)选择在 <text/> 元素或应用程序定义的错误条件(元素)中详细说明策略。
- <remote-connection-failed/> -- 服务器无法正确连接到用于验证或授权的远程实体。
- <resource-constraint/> -- 服务器缺乏必要的系统资源为流服务。
- <restricted-xml/> -- 实体试图发送受限的XML特性,比如一个注释,处理指示,DTD,实体参考,或保留的字符(参考 Restrictions (第十一章第一节)).
- <see-other-host/> -- 服务器将不提供服务给初始化实体但是把它重定向到另一台主机;服务器应该(SHOULD)在<see-other-host/>元素的XML字符数据中指明替代服务器名或IP地址(它必须(必须)是合法的域名标识)。
- <system-shutdown/> -- 服务器正在关机并且所有激活的流正在被关闭。
- <undefined-condition/> -- 错误条件不在本文已定义的错误条件列表之中;这个错误条件应该(SHOULD)仅用于"应用程序定义条件"元素.
- <unsupported-encoding/> -- 初始化实体以一个服务器不不支持的编码方式编码了一个流(参照 Character Encoding (第十一章第五节)).
- <unsupported-stanza-type/> -- 初始化实体发送了一个流的一级子元素但是服务器不支持.
- <unsupported-version/> -- 由初始化实体在流的头信息中指定的'version'属性的值所指定的版本不被服务器支持;服务器可以(MAY)在<text/>元素中指定一个它支持的版本号.
- <xml-not-well-formed/> -- 初始化实体发送了一个不规范的XML(参考[XML]).
4.7.4. 应用程序定义条件
大家知道,应用程序可以(MAY)在error元素中包含一个适当名字空间的子元素来提供一个应用程序定义流错误信息."应用程序定义"元素应该(SHOULD)补充或甚至限定一个已定义的元素.所以 <error/> 元素将包含两个或三个子元素:
<stream:error>
<xml-not-well-formed xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>
<text xml:lang='en' xmlns='urn:ietf:params:xml:ns:xmpp-streams'>
Some special application diagnostic information!
</text>
<escape-your-data xmlns='application-ns'/>
</stream:error>
</stream:stream>
4.8. 简化的流示例
这里包含两个简化的例子,描述了基于流的客户端在服务器上的“会话”(这里"C"表示从客户端发给服务器,"S"表示从服务器发给客户端);这些例子只是用于举例说明原理.
一个基本的 "会话":
C: <?xml version='1.0'?>
<stream:stream
to='example.com'
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
version='1.0'>
S: <?xml version='1.0'?>
<stream:stream
from='example.com'
id='someid'
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
version='1.0'>
… encryption, authentication, and resource binding ...
C: <message from='juliet@example.com'
to='romeo@example.net'
xml:lang='en'>
C: <body>Art thou not Romeo, and a Montague?</body>
C: </message>
S: <message from='romeo@example.net'
to='juliet@example.com'
xml:lang='en'>
S: <body>Neither, fair saint, if either thee dislike.</body>
S: </message>
C: </stream:stream>
S: </stream:stream>
一个不成功的 "会话" :
C: <?xml version='1.0'?>
<stream:stream
to='example.com'
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
version='1.0'>
S: <?xml version='1.0'?>
<stream:stream
from='example.com'
id='someid'
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
version='1.0'>
… encryption, authentication, and resource binding ...
C: <message xml:lang='en'>
<body>Bad XML, no closing body tag!
</message>
S: <stream:error>
<xml-not-well-formed
xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>
</stream:error>
S: </stream:stream>
5.1. 概览
XMPP包含的一个保证流安全的方法来防止篡改和偷听.这个传输层安全协议[TLS]的频道加密方法, 模拟了类似的其他"STARTTLS"(见RFC 2595 [USINGTLS])的扩展,如 IMAP [IMAP], POP3 [POP3], and ACAP [ACAP]."STARTTLS"的扩展名字空间是'urn:ietf:params:xml:ns:xmpp-tls'.
一个给定域的管理员可以(MAY)要求客户端和服务器通信以及服务器之间通信时使用TLS,或者两者都要求。客户端应该(SHOULD)在尝试完成 SASL (第六章)握手之前使用 TLS,服务器应该(SHOULD)在两个域之间使用 TLS 以保证服务器间通信的安全。
以下是使用规则:
- 一个遵守本协议的初始化实体必须(MUST)在初始化流的头信息中包含一个'version'属性并把值设为“1.0”。
- 如果TLS握手发生在两个服务器之间,除非服务器声称的DNS主机名已经被解析(见第十四章第四节 Server-to-Server Communications),通信不能(MUST NOT)继续进行。
- 当一个遵守本协议的接收实体接收了一个初始化流(它的头信息中包含一个'version'属性并且值设为“1.0”),在发送应答流的的头信息(其中包含版本标记)之后,它必须发送(MUST)<starttls/>元素(名字空间为 'urn:ietf:params:xml:ns:xmpp-tls')以及其他它支持的流特性 。
- 如果初始化实体选择使用TLS,TLS握手必须在SASL握手之前完成;这个顺序用于帮助保护SASL握手时发送的认证信息的安全,同时可以在必要的时候在TLS握手之前为SASL外部机制提供证书。
- TLS握手期间,一个实体不能(MUST NOT)在流的根元素中发送任何空格符号作为元素的分隔符(在下面的TLS示例中的任何空格符都仅仅是为了便于阅读);这个禁令用来帮助确保安全层字节精度。
- 接收实体必须(MUST)在发送<proceed/> 元素的关闭符号">" 之后立刻开始TLS协商。初始化实体必须(MUST)在从接收实体接收到<proceed/> 元素的关闭符号">" 之后立刻开始TLS协商。
- 初始化实体必须(MUST)验证接收实体出示的证书;关于证书验证流程参见Certificate Validation ( 第十四章第二节)。
- 证书必须(MUST)检查初始化实体(比如一个用户)提供的主机名;而不是通过DNS系统解析出来的主机名;例如,如果用户指定一个主机名"example.com"而一个DNS SRV [SRV]查询返回"im.example.com",证书必须(MUST)检查"example.com".如果任何种类的XMPP实体(例如客户端或服务器)的JID出现在一个证书里,它必须(MUST)表现为一个别名实体里面的UTF8字符串,存在于subjectAltName之中。如何使用 [ASN.1] 对象标识符 "id-on-xmppAddr" 定义在本文的第五章第一节第一小节。
- 如果 TLS 握手成功了,接收实体必须(MUST) 丢弃TLS 生效之前从初始化实体得到的任何不可靠的信息.
- 如果 TLS 握手成功了,初始化实体必须(MUST) 丢弃TLS 生效之前从接收实体得到的任何不可靠的信息.
- 如果 TLS 握手成功了,接收实体不能(MUST NOT)在流重新开始的时候通过提供其他的流特性来向初始化实体提供 STARTTLS 扩展.
- 如果 TLS 握手成功了,初始化实体必须(MUST)继续进行SASL握手。
- 如果 TLS 握手失败了,接收实体必须(MUST)终止XML流和相应的TCP连接。
- 关于必须(MUST)支持的机制,参照 Mandatory-to-Implement Technologies (第十四章第七节) 。
5.1.1. 用于 XMPP 地址的 ASN.1 对象标识符
上文提到的[ASN.1] 对象标识符 "id-on-xmppAddr"定义如下:
id-pkix OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) pkix(7) }
id-on OBJECT IDENTIFIER ::= { id-pkix 8 } -- other name forms
id-on-xmppAddr OBJECT IDENTIFIER ::= { id-on 5 }
XmppAddr ::= UTF8String
对象标识符也可以(MAY)使用点分隔的格式,如 "1.3.6.1.5.5.7.8.5".
5.2. 叙述
当一个初始化实体用TLS保护一个和接收实体之间的流,其步骤如下:
- 初始化实体打开一个TCP连接,发送一个打开的XML流头信息(其'version'属性设置为"1.0")给接收实体以初始化一个流。
- 接收实体打开一个TCP连接,发送一个XML流头信息(其'version'属性设置为"1.0")给初始化实体作为应答。
- 接收实体向初始化实体提议STARTTLS范围(包括其他支持的流特性),如果TLS对于和接收实体交互是必需的,它应该(SHOULD)在<starttls/>元素中包含子元素<required/>.
- 初始化实体发出STARTTLS命令(例如, 一个符合'urn:ietf:params:xml:ns:xmpp-tls'名字空间的 <starttls/> 元素) 以通知接收实体它希望开始一个TLS握手来保护流。
- 接收实体必须(MUST)以'urn:ietf:params:xml:ns:xmpp-tls'名字空间中的<proceed/>元素或<failure/>元素应答。如果失败,接收实体必须(MUST)终止XML流和相应的TCP连接。如果继续进行,接收实体必须(MUST)尝试通过TCP连接完成TLS握手并且在TLS握手完成之前不能(MUST NOT)发送任何其他XML数据。
- 初始化实体和接收实体尝试完成TLS握手。(要符合[TLS]规范)
- 如果 TLS 握手不成功, 接收实体必须(MUST)终止 TCP 连接. 如果 TLS 握手成功, 初始化实体必须(MUST)发送给接收实体一个打开的XML流头信息来初始化一个新的流(先发送一个关闭标签</stream>是不必要的,因为接收实体和初始化实体必须(MUST)确保原来的流在TLS握手成功之后被关闭)。
- 在从初始化实体收到新的流头信息之后,接收实体必须(MUST)发送一个新的XML流头信息给初始化实体作为应答,其中应包含可用的特性但不包含STATRTTLS特性。
5.3. 客户端-服务器 示例
以下例子展示一个客户端使用STARTTLS保护数据流 (注意: 以下步骤举例说明协议中的失败案例;在这个例子中它们并不详尽并且也不是必须被数据传输触发的).
步骤 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_123'
from='example.com'
version='1.0'>
步骤 3: 服务器发送 STARTTLS 范围给客户端(包括验证机制和任何其他流特性):
<stream:features>
<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'>
<required/>
</starttls>
<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<mechanism>DIGEST-MD5</mechanism>
<mechanism>PLAIN</mechanism>
</mechanisms>
</stream:features>
步骤 4: 客户端发送 STARTTLS 命令给服务器:
<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
步骤 5: 服务器通知客户端可以继续进行:
<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
步骤 5 (或者): 服务器通知客户端 TLS 握手失败并关闭流和TCP连接:
<failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
</stream:stream>
步骤 6: 客户端和服务器尝试通过已有的TCP连接完成 TLS 握手.
步骤 7: 如果 TLS 握手成功, 客户端初始化一个新的流给服务器:
<stream:stream
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
to='example.com'
version='1.0'>
步骤 7 (或者): 如果 TLS 握手不成功, 服务器关闭 TCP 连接.
步骤 8: 服务器发送一个流头信息应答客户端,其中包括任何可用的流特性:
<stream:stream
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
from='example.com'
id='c2s_234'
version='1.0'>
<stream:features>
<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<mechanism>DIGEST-MD5</mechanism>
<mechanism>PLAIN</mechanism>
<mechanism>EXTERNAL</mechanism>
</mechanisms>
</stream:features>
步骤 9: 客户端继续 SASL 握手 (第六章).
5.4. 服务器-服务器示例
以下例子展示两个服务器之间使用STARTTLS保护数据流 (注意: 以下步骤举例说明协议中的失败案例;在这个例子中它们并不详尽并且也不是必须被数据传输触发的).
步骤 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_123'
version='1.0'>
步骤 3: Server2 发送 STARTTLS 范围给 Server1 ,包括验证机制和任何其他流特性:
<stream:features>
<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'>
<required/>
</starttls>
<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<mechanism>DIGEST-MD5</mechanism>
<mechanism>KERBEROS_V4</mechanism>
</mechanisms>
</stream:features>
步骤 4: Server1 发送 STARTTLS 命令给 Server2:
<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
步骤 5: Server2 通知 Server1 允许继续进行:
<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
步骤 5 (或者): Server2 通知 Server1 TLS握手失败并关闭流:
<failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
</stream:stream>
步骤 6: Server1 和 Server2 尝试通过 TCP 完成 TLS 握手.
步骤 7: 如果 TLS 握手成功, Server1 初始化一个新的流给 Server2:
<stream:stream
xmlns='jabber:server'
xmlns:stream='http://etherx.jabber.org/streams'
to='example.com'
version='1.0'>
步骤 7 (或者): 如果 TLS 握手不成功, Server2 关闭 TCP 连接.
步骤 8: Server2 发送一个包含任何可用流特性的流头信息给 Server1 :
<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>DIGEST-MD5</mechanism>
<mechanism>KERBEROS_V4</mechanism>
<mechanism>EXTERNAL</mechanism>
</mechanisms>
</stream:features>
步骤 9: Server1 继续进行 SASL 握手(第六章).
6.1. 概览
XMPP 有一个验证流的方法,即XMPP特定的SASL(简单验证和安全层)[SASL]。SASL提供了一个通用的方法为基于连接的协议增加验证支持,而XMPP使用了一个普通的XML名字空间来满足SASL的需要。
以下规则应用于:
- 如果SASL协商发生在两台服务器之间,除非服务器宣称的DNS主机名得到解析,不能(MUST NOT)进行通信。(参见服务器间的通信(第十四章第四节)).
- 如果初始化实体有能力使用 SASL 协商, 它必须(MUST)在初始化流的头信息中包含一个值为"1.0"的属性'version'。
- 如果接收实体有能力使用 SASL 协商, 它必须(MUST)在应答从初始化实体收到的打开流标签时(如果打开的流标签包含一个值为"1.0"的'version'属性),通过'urn:ietf:params:xml:ns:xmpp-sasl'名字空间中的<mechanisms/>元素声明一个或多个验证机制.
- 当 SASL 协商时, 一个实体不能(MUST NOT)在流的根元素中发送任何空格符号(匹配 production [3] content of [XML])作为元素之间的分隔符(在以下的SASL例子中任何空格符号的出现仅仅是为了增加可读性); 这条禁令帮助确保安全层字节的精确度。
- 当SASL握手时,在XML元素中使用的任何 XML 字符数据必须被编码成 base64, 编码遵循 RFC 3548 第三章的规定。
- 如果一个 简单名字"simple username" 规范被选定的SASL机制所支持,(比如, 这被 DIGEST-MD5 和 CRAM-MD5 机制支持但不被 EXTERNAL 和 GSSAPI 机制支持), 验证的时候初始化实体应该(SHOULD)在服务器间通信时提供 简单名字自身的发送域(IP地址或包含在一个域标识符中的域名全称),在客户端与服务器之间通信时提供注册用户名(包含在一个XMPP节点标识符中的用户或节点名)。
- 如果初始化实体希望以另一个实体的身份出现并且SASL机制支持授权ID的传输,初始化实体在SASL握手时必须(MUST)提供一个授权ID。如果初始化实体不希望以另一个实体的身份出现,初始化实体在SASL握手时不能(MUST NOT)提供一个授权ID。在 [SASL] 的定义中,除非授权ID不同于从验证ID(详见[SASL])中得到的缺省的授权ID,初始化实体不能(MUST NOT)提供授权ID。如果提供了,这个授权ID的值必须(MUST)是<domain>的格式(对于服务器来说)或<node@domain>的格式(对于客户端来说).
- 在成功进行包括安全层的SASL握手之后,接收实体必须(MUST)丢弃任何从初始化实体得到的而不是从SASL协商本身获得的信息。
- 在成功进行包括安全层的SASL握手之后,初始化实体必须(MUST)丢弃任何从接收实体得到的而不是从SASL协商本身获得的信息。
- 参看 强制执行的技术 (第十四章第七届) ,了解关于必须(MUST)支持的机制.
6.2. 叙述
一个初始化实体使用SASL和接收实体做验证的步骤如下:
- 初始化实体请求SASL验证,它发送一个打开的XML流头信息给接收实体,其'version'属性的值为"1.0".
- 在发送一个XML流头回复之后,接收实体声明一个可用的SASL验证机制清单;每个机制作为一个<mechanism/>元素,作为子元素包含在<mechanisms/>容器元素(其名字空间为'urn:ietf:params:xml:ns:xmpp-sasl')中,而<mechanisms/>则包含在流名字空间中的<features/>元素中。如果在使用任何验证机制之前需要使用TLS(见第五章),接收实体不能(MUST NOT)在TLS握手之前提供可用的SASL验证机制清单。如果初始化实体在优先的TLS协商过程中呈现了一个合法的证书,接收实体应该(SHOULD)在SASL握手中提出一个SASL外部机制给初始化实体,尽管这个外部机制可以(MAY)在其它环境下提供。
- 初始化实体发送一个符合'urn:ietf:params:xml:ns:xmpp-sasl'名字空间的<auth/>元素(其中包含了适当的'mechanism'属性值)给接收实体,以选择一个机制。如果这个机制支持或需要,这个元素可以(MAY)包含XML字符数据(在SASL术语中,即“初始化应答”);如果初始化实体需要发送一个零字节的初始化应答,它必须(MUST)传输一个单独的等号作为应答,这表示应答有效但不包含数据。
- 如果必要,接收实体向初始化实体发送一个符合'urn:ietf:params:xml:ns:xmpp-sasl'名字空间的<challenge/>元素来发出挑战;这个元素可以(MAY)包含XML字符数据(必须按照初始化实体选择的SASL机制进行一致性运算)。
- 初始化实体向接收实体发送符合'urn:ietf:params:xml:ns:xmpp-sasl'名字空间的<response/>元素作为应答;这个元素可以(MAY)包含XML字符数据(必须按照初始化实体选择的SASL机制进行一致性运算)。
- 如果必要,接收实体发送更多的挑战给初始化实体,初始化实体发送更多的回应。
这一系列的 挑战/应答 组,持续进行直到发生以下三件事中的一件为止:
- 初始化实体向接收实体发送符合'urn:ietf:params:xml:ns:xmpp-sasl'名字空间的<abort/>元素以中止握手。在接收到<abort/>元素之后,接收实体应该(SHOULD)允许一个可配置的但是合理的重试次数(至少2次),然后它必须(MUST)终止TCP连接;这使得初始化实体(如一个最终用户客户端)能够容忍可能不正确的credentials(如密码输入错误)而不用强制重新连接。
- 接收实体向初始化实体发送符合'urn:ietf:params:xml:ns:xmpp-sasl'名字空间的<failure/>元素以报告握手失败(详细的失败原因应该在<failure/>的一个适当的子元素中沟通,在第六章第四节中的SASL Errors中定义)。如果失败的情况发生了,接收实体应该(SHOULD)允许一个可配置的但是合理的重试次数(至少2次),然后它必须(MUST)终止TCP连接;这使得初始化实体(如一个最终用户客户端)能够容忍可能不正确的credentials(如密码输入错误)而不用强制重新连接。
- 接收实体向初始化实体发送符合'urn:ietf:params:xml:ns:xmpp-sasl'名字空间的<success/>元素以报告握手成功;如果所选择的SASL机制要求,这个元素可以(MAY)包含XML字符数据(见SASL术语,“成功的额外数据”)。接收到<success/> 元素之后,初始化实体必须(MUST)发送一个打开的XML流头信息给接收实体以发起一个新的的流(不需要先发送一个 </stream>标签,因为在发送和接收到<success/>元素之后,接收实体和初始化实体必须确认原来的流被关闭了)。从初始化实体接收到新的流头信息之后,接收实体必须(MUST)发送一个新的流头信息给初始化实体作为回应,附上任何可用的特性(但不包括 STARTTLS 和 SASL 特性)或一个空的 <features/> 元素(这表示没有更多的特性可用);任何没有在本文定义的附加特性必须(MUST)在XMPP的相关扩展中定义。
6.3. SASL 定义
[SASL]的必要条件要求通过协议定义来提供以下信息:
service name(服务名): "xmpp"
initiation sequence(开始序列): 当初始化实体提供一个打开的XML流头信息并且接收实体善意回应之后,接收实体提供一个可接受的验证方法清单。初始化实体从这个清单中选择一个方法,把它作为 <auth/> 元素的 'mechanism' 属性的值发送给接收实体,也可以选择发送一个初始化应答以避免循环。
exchange sequence(交换序列): 挑战和回应的交换,从接收实体发送给初始化实体的 <challenge/> 元素和从初始化实体发送给接收实体的 <response/> 元素信息。接收实体通过发送 <failure/>元素报告失败,发送<success/>元素报告成功;初始化实体通过发送<abort/> 元素中止交换。成功的协商之后,两边都认为原来的XML流已经关闭并且都开始发送新的流头信息。
security layer negotiation(安全层协商): 安全层在接收实体发送 <success/> 元素的关闭字符">"之后立刻生效,在初始化实体发送 <success/> 元素的关闭字符">"之后也立刻生效。层的顺序是 [TCP],[TLS],然后是 [SASL],然后是 [XMPP]。
use of the authorization identity(授权ID的使用): 授权ID可在xmpp中用于表示一个客户端的非缺省的<node@domain>,或一个服务器的<domain> 。
6.4. SASL 错误
SASL相关的错误条件定义如下:
- <aborted/> -- 接收实体认可由初始化实体发送的<abort/>元素;在回应一个<abort/>元素时发送。
- <incorrect-encoding/> -- 由初始化实体提供的数据无法处理,因为[BASE64]编码不正确(例如,因为编码不符合[BASE64]的第三章); 在回应一个包含初始化响应数据的<response/> 元素或<auth/>元素时发送.
- <invalid-authzid/> -- 由初始化实体提供的授权id是非法的,因为它的格式不正确或初始化实体无权给那个ID授权;在回应一个包含初始化响应数据的<response/> 元素或<auth/>元素时发送。
- <invalid-mechanism/> -- 初始化实体不能提供一个机制活、或请求一个不被接受实体支持的机制;在回应一个<auth/>元素时发送。
- <mechanism-too-weak/> -- 初始化实体请求的机制比服务器策略对它的要求弱;在回应一个包含初始化响应数据的<response/> 元素或<auth/>元素时发送。
- <not-authorized/> -- 验证失败,因为初始化实体没有提供合法的credentials(这包括但不限于未知用户名等情形);在回应一个包含初始化响应数据的<response/> 元素或<auth/>元素时发送。
- <temporary-auth-failure/> -- 验证失败,因为接收实体出现了临时的错误;在回应一个<response/> 元素或<auth/>元素时发送。
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'> cmVhbG09InNvbWVyZWFsbSIsbm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixxb3A9ImF1dGgi LGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNzCg== </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 LHJlc3BvbnNlPWQzODhkYWQ5MGQ0YmJkNzYwYTE1MjMyMWYyMTQzYWY3LGNo YXJzZXQ9dXRmLTgK </response>
解码后的回应信息是:
username="somenode",realm="somerealm", nonce="OA6MG9tEQGm2hh",cnonce="OA6MHXh6VqTrRk", nc=00000001,qop=auth,digest-uri="xmpp/example.com", response=d388dad90d4bbd760a152321f2143af7,charset=utf-8
步骤 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>
步%