【问题标题】:Java/SPNEGO: Unwanted SPN canonicalization?Java/SPNEGO:不需要的 SPN 规范化?
【发布时间】:2012-09-01 16:57:36
【问题描述】:

我目前正在尝试使用来自 SourceForge 的 SPNEGO library 将 Java 客户端实现到受 SPNEGO 保护的 Web 服务(服务器使用相同的库)。我无法成功验证,我的请求总是以

HTTP/1.1 500 Failure unspecified at GSS-API level (Mechanism level: Checksum failed)

这与我从具有不适当主机名的浏览器访问 Web 服务时遇到的症状相似,并且确实在 Wireshark 中进行了一些调试显示客户端发送了错误的 SPN 请求 - 我发送到 service-test.client.com,它注册为 SPN 并在 DNS 中有 A 记录,但在 Windows 域中注册为 server-1234.client.corp。即使我将请求发送到 http://service-test.client.com(请参阅匹配的 Host 标头),Java 请求票证的 SPN 是“内部”Windows 名称:

从 Chrome 或 IE 发送的相同内容具有匹配的 Host 标头和 SPN:

由于在我的代码或 SPNEGO 库中没有发生这种转换,我认为它一定是在 JRE 中发生的。我一直在研究 JGSS 源代码,但有点难以理解。谁能告诉我如何跳过这个翻译并获得正确 SPN 的票?

客户端代码:

SpnegoHttpURLConnection con = new SpnegoHttpURLConnection("spnego-client", user, password);
con.connect(new URL("http://service-test.client.com:8083/service"));
int rc = con.getResponseCode();
String msg = con.getResponseMessage();

【问题讨论】:

  • 重新检查您的 DNS。进行反向查找。大多数问题是由不正确的反向 DNS 条目引起的。你的领域的名称是什么? RFC 中的第 85 页可能会对您有所帮助。 Windows 使用 SSPI,它实际上与 GSS-API 做同样的事情,但方式不同。另一个提示:您需要提供有关服务于CLIENT.COM 领域的 KDC 的信息,否则 Java 将无法获得票证。我们有一个类似的星座在工作。这被认为是失败的,因为 Kerberos 不知道领域 CLIENT.COM。
  • 您也可以使用 Wireshark 嗅探 KDC 流量。过滤kerberos。
  • @Michael-O:谢谢,伟大的 cmets。如果我正确地阅读了该 RFC,它不应该做任何反向查找恶作剧,但是......我将在星期一验证 DNS/Kerberos 流量发生了什么......
  • 实际上不是,它说“当解析对这种类型的名称的引用时,可以通过尝试 DNS 查找并使用完全-返回的限定域名,或者如果 DNS 查找失败,则使用提供的“主机名”。所以这取决于。完成反向查找是非常自然的。这就是 Kerberos 验证主机名的方式。如果您正在运行 DNS 循环,这实际上是至关重要的。没有它,它将永远无法构建真正的 SPN。周一告诉我你的结果。我很兴奋。
  • 谢谢 - 这一切都非常合理,但我不明白为什么 IE 的行为与 Java 不同。我一直认为循环设置必须使用共享的 SPN 运行。如果它按照您描述的方式工作,那么我就有麻烦了,因为我们使用多个 SPN 绑定到同一机器/IP 地址上的不同服务 - 如果您愿意,可以使用“Kerberos 虚拟主机”。到目前为止,仅使用浏览器客户端,它就可以工作,但我可能真的很幸运。

标签: java windows kerberos spnego spn


【解决方案1】:

以上cmets的总结:

重新检查您的 DNS。进行反向查找。大多数问题是由错误的反向 DNS 条目引起的。

RFC2713 中的第 85 页可能会对您有所帮助并检查 RFC4120 并搜索“canon”。

当使用 GSS-API 构建基于主机的服务的 SPN 时,您必须使用目标机制规范化该名称。 RFC 说

当解析对此类名称的引用时,可以通过尝试 DNS 查找并使用返回的完全限定域名或使用如果 DNS 提供了“主机名” 查找失败。规范化操作还将主机名映射为小写字符。

Kerberos 5 RFC 所说的:

服务器和传输时。因此,例如,不应依赖未受保护的 DNS 记录来将主机别名映射到主名称 服务器的名称,接受主要名称作为预期的一方 联系,因为攻击者可以修改映射并冒充 派对。

Kerberos 和基于 Kerberos 的协议的实现不得 使用不安全的 DNS 查询来规范化 服务主体名称(即,它们不得使用不安全的 DNS 将一个名称映射到另一个名称的查询以确定 要与之通信的主体名称)。在一个环境中 如果没有安全名称服务,应用程序作者可以附加一个 之前静态配置的域名到不合格的主机名 将名称传递给安全机制,但他们不应该这样做 比那更多的。安全名称服务设施(如果可用)可能 值得信任主机名规范化,但这样的规范化 KDC 实现不应该要求客户端提供。

实现说明:许多当前的实现在一定程度上做了 提供的服务名称的规范化,甚至经常使用 DNS 尽管它会产生安全问题。然而,没有 关于服务名称是否是实现之间的一致性 大小写折叠或是否使用反向分辨率。到 最大限度地提高互操作性和安全性,应用程序应提供 具有由折叠用户产生的名称的安全机制 将名称输入小写而不进行任何其他修改 或规范化。

似乎 GSS-API 实现可以规范化,但如果 DNS 不受信任,Kerberos 不应该这样做。 所以这取决于。完成反向查找是非常自然的。这就是 Kerberos 验证主机名的方式。如果您正在运行 DNS 循环,这实际上是至关重要的。没有它,它将永远无法构建真正的 SPN。

虽然我真的会在 Kerberos 邮件列表中这样做。这是一个非常有趣的观点。

我已经检查了 MIT Kerberos 实现,如果你检查 sn2princ.c 中的源代码,有一个方法 krb5_sname_to_principal 实际上会这样做:

if (type == KRB5_NT_SRV_HST) {
        struct addrinfo *ai = NULL, hints;
        int err;
        char hnamebuf[NI_MAXHOST];

        /* Note that the old code would accept numeric addresses,
           and if the gethostbyaddr step could convert them to
           real hostnames, you could actually get reasonable
           results.  If the mapping failed, you'd get dotted
           triples as realm names.  *sigh*

           The latter has been fixed in hst_realm.c, but we should
           keep supporting numeric addresses if they do have
           hostnames associated.  */

        memset(&hints, 0, sizeof(hints));
        hints.ai_flags = AI_CANONNAME;
        err = getaddrinfo(hostname, 0, &hints, &ai);
        if (err) {
#ifdef DEBUG_REFERRALS
            printf("sname_to_princ: failed to canonicalize %s; using as-is", hostname);
#endif
        }
        remote_host = strdup((ai && ai->ai_canonname) ? ai->ai_canonname : hostname);
        if (!remote_host) {
            if(ai)
                freeaddrinfo(ai);
            return ENOMEM;
        }

        if ((!err) && maybe_use_reverse_dns(context, DEFAULT_RDNS_LOOKUP)) {
            /*
             * Do a reverse resolution to get the full name, just in
             * case there's some funny business going on.  If there
             * isn't an in-addr record, give up.
             */
            /* XXX: This is *so* bogus.  There are several cases where
               this won't get us the canonical name of the host, but
               this is what we've trained people to expect.  We'll
               probably fix it at some point, but let's try to
               preserve the current behavior and only shake things up
               once when it comes time to fix this lossage.  */
            err = getnameinfo(ai->ai_addr, ai->ai_addrlen,
                              hnamebuf, sizeof(hnamebuf), 0, 0, NI_NAMEREQD);
            freeaddrinfo(ai);
            if (err == 0) {
                free(remote_host);
                remote_host = strdup(hnamebuf);
                if (!remote_host)
                    return ENOMEM;
            }
        } else
            freeaddrinfo(ai);
    }

所以,我想我们必须通过邮件列表询问。

【讨论】:

  • 我为此在 MIT Kerberos 邮件列表上创建了一个 thread。
猜你喜欢
  • 2011-01-11
  • 1970-01-01
  • 2023-01-20
  • 1970-01-01
  • 2020-07-07
  • 2016-08-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-29
相关资源
最近更新 更多