摘要

  • RFC 1408 在 Telnet 选项 36 中规定 VAR=0VALUE=1;RFC 1571 随后记录,BSD 参考实现对这两个值的解释正好相反。
  • RFC 1571 通过非法标记、先后次序、空标记、数量关系和已知变量名推测对端采用哪一本字典,但某些报文没有足够证据,只能采用默认假设。
  • RFC 1572 保留书面定义,却把它放到新的 Telnet 选项 39 NEW-ENVIRON 下。即便解码无歧义,服务器仍可忽略、替换或采用收到的变量。

错的不是一个字,而是一段正在运行的关系

1993 年 1 月发布的 RFC 1408,想让 Telnet 客户端在连接时把环境信息交给服务器。协议使用选项号 36,名称是 ENVIRON。子协商先有 IS=0SEND=1INFO=2,在承载的内容里又有 VAR=0VALUE=1ESC=2USERVAR=3

这几个数字负责把连续字节切成结构。VAR 开始一个公认变量名,USERVAR 开始用户自定义名称,VALUE 把名称与值分开,ESC 让保留字节能够作为普通内容出现。变量名后没有 VALUE,表示变量未定义;有 VALUE 却立刻遇到下一个类型或结束,表示变量已经定义,只是值为空。空环境本身也是合法答复。

因此,零和一的分歧不会只把标签贴错。它会改变接收方从哪里切开名称、哪里开始读值,以及整段报文是否合法。

一年后的 RFC 1571 公开了冲突。它说,RFC 1408 对 VARVALUE 的定义与 BSD Telnet Environment 实现相反;BSD 又是这份 RFC 原本要记录的参考实现,并且构成许多既有实现的基础。

这段话不是部署统计,不能据此声称某种实现占了多少比例。它证明的是更窄也更关键的事实:选项 36 已经携带两套历史。仅仅看到编号,无法知道发送端怎样理解零。

ENVIRON 运送的是启动输入

当时许多操作系统都有需要带到远端的启动信息。若每出现一种信息就增加一个 Telnet 选项,公共协议会不断吸收本地系统的特殊需求。RFC 1408 因而提供一个通用容器,而不是把 Telnet 变成环境变量的总目录。

规范列出六个公认名称:USERJOBACCTPRINTERSYSTEMTYPEDISPLAY。另外以 USERVAR 携带任意用户变量。这个分类保留来源性质,并不认证内容。RFC 甚至指出,谨慎的实现很可能同样不信任两类数据;公认名称与用户名称冲突时如何处理,也由实现决定。

默认状态是不交换:WONT ENVIRONDONT ENVIRON。愿意发送的一端说 WILL,愿意接收的一端说 DO。只有发出 DO 的一端能以 SEND 请求变量,只有发出 WILL 的一端能用 IS 回答,或用 INFO 报告后续变化。

方向性把几个事件分开了。愿意发送不等于已经发送;提出请求不等于变量存在;IS 是初始问答,INFO 可以在以后自发出现,却不能代替第一次交换。任何一个事件都不证明接收方已经应用内容。

接收不是命令,USER 也不是身份

环境信息通常要在连接早期到达,因为许多系统只在创建进程时传入环境。时间越靠近登录,接收方的本地判断越重要。

RFC 1408 明确允许服务器不把变量放进用户环境。它可以忽略不认识的名称,可以把 USERACCT 一类输入用于选择登录上下文而不复制给进程,也可以优先采用更准确的来源。

规范用 TERM 说明这种优先级:若 Telnet Terminal-Type 选项已经确定终端类型,服务器可以忽略客户端发送的 USERVAR TERM=xtermDISPLAY 与独立的 X-Display-Location 选项冲突时,规范建议使用最近收到的信息。两种规则不同,但权力位置相同——由接收者解决冲突。

所以 USER 只是客户端希望登录的账户名,不是人的身份凭据。PRINTER 的网络命名方式当时尚未统一,更不等于打印机存在、访问获准或纸张已经输出。DISPLAY 被正确解码,也不能证明 X 连接建立或用户看到窗口。

安全章节把边界推到登录之前:实现者必须分析哪些变量可以安全设置。若某个变量能改变登录或认证程序本身,攻击者可能借此绕过或破坏认证。RFC 没有报告一宗具体事件;它指出的是控制面——远端提供数据,本地程序决定数据是否进入高权限路径。

语法错误变成识别对端的证据

RFC 1571 面对一个现实限制:旧选项里没有“我使用哪套定义”的版本字段。它只能观察报文形状。

客户端解析 SEND 时,可以利用一个不可能事件。合法请求只应出现 VARUSERVAR,不应出现 VALUE。如果按本地字典看到了 VALUE,便可推断服务器交换了零和一;从这一点起,后续解析以及生成 ISINFO 时都要反转。若请求中根本没有零或一,就没有辨认材料,只能假设对方遵循书面定义。

服务器解析 ISINFO 更复杂,因为值本来就会出现。紧跟命令的是 VAR,倾向说明对方采用 RFC 次序;若是 VALUE,则说明采用反向次序。若首先出现 USERVAR,后面的零或一都有可能合法,服务器还要寻找连续标记、空标记,比较 VARUSERVARVALUE 的数量,再看某段内容是否像公认变量名。

这些方法不是在认证对端,而是在寻找哪套历史解释更能使字节符合语法。有的形状在一种解析下不可能,因此证据较强;有的只能提高概率。所有测试仍无法区分时,RFC 1571 要求默认采用书面定义。

默认值没有把未知变成已知。若系统只留下解码后的键和值,就会丢掉真正决定可审计性的材料:原始字节、选项号、协商方向、解析器版本、触发的启发式规则,以及当时是否仍有歧义。

新编号承认旧编号已经有历史

1994 年 1 月的 RFC 1572 选择了一条更干净的未来路径。它继续使用 RFC 写下的 VAR=0VALUE=1,但将协议命名为 NEW-ENVIRON,使用新的 Telnet 选项号 39。文件明确说,新编号让实现能够无歧义互通。

如果只是再次宣布选项 36 的正确表格,对端依然无法知道远端二进制来自 BSD 传统还是 RFC 传统。新文字能更新记录,不能倒写安装状态。协商 39 则同时选择了新的编号和唯一语法,不再需要猜测旧谱系。

今天的 IANA Telnet Options 登记表 仍保留两项:36 是由 RFC 1408 定义的 Environment Option,39 是由 RFC 1572 定义的 New Environment Option。登记表记录分叉,却不证明某台主机实现过、协商过或采用过任何变量。

新编号也没有把接收方的未来决定上收。NEW-ENVIRON 仍默认关闭,仍需双向协商,仍区分变量来源,也仍允许服务器用自己的更准确信息替换输入。公共层只负责让字节意思确定。

一串字节不能包办整条因果链

可靠证据要逐层保存:规范分配了什么;程序按哪套字典发出什么;对端怎样解释;哪条启发式规则选择了解释;解析得到什么候选变量;接收方如何验证、忽略或替换;登录或进程创建实际采用什么;应用看见什么;外部动作是否发生。

发布 RFC 没有重写运行代码。成功协商没有证明变量传输。解码 USER 没有认证用户。采用变量没有证明进程继承。进程继承也没有证明命令执行或结果出现。每一层都需要自己的记录。

来源

这些来源证明规范语法、RFC 对 BSD 冲突的同期记录、兼容启发式、新编号和登记结果;不证明具体实现、部署比例、攻击事件、真实身份、登录获准、环境生效、命令执行或当前风险。