gpt4 book ai didi

java - 使用 SSPI : no errors, 进行 Kerberos 模拟但不起作用

转载 作者:行者123 更新时间:2023-12-02 11:42:22 25 4
gpt4 key购买 nike

我需要在 Java 应用程序服务器中模拟用户,并使用该用户的权限对 IIS 中的 ASP 应用程序执行 http 请求。为此,我正在尝试调整 Apache HttpClient 的 WindowsNegotiateScheme 类。这使用 JNA 直接访问 Windows SSPI 身份验证功能,因此问题可能不是 Java 特有的。由于我需要“协议(protocol)转换”,因此我没有可以模拟的用户密码,只有名称。但使用 S4U 功能,这应该是可能的。

所有 SSPI 函数调用要么返回 0(成功),要么返回 590610 (SEC_I_CONTINUE_NEEDED),永远不会返回错误代码,并且代码会运行,但 http 请求会随着用户运行应用程序服务器而到达 ASP 端,无论我是否在我的工作站上以我的用户 ID 本地运行它,或者在应用程序服务器(恰好是也运行 IIS/ASP 应用程序的计算机)中运行它,该服务器作为本地系统运行。

相关部分代码如下:

public Header authenticate(Credentials credentials, HttpRequest request, HttpContext context) throws AuthenticationException {
final String response;
if (clientCred == null) { // first time
boolean impersonate = ...; // logic not relevant here

try {
final String username = impersonate ? credentials.getUserPrincipal().getName() : CurrentWindowsCredentials.getCurrentUsername();
final Sspi.TimeStamp lifetime = new Sspi.TimeStamp();
this.clientCred = new Sspi.CredHandle();
int credUse = impersonate ? (Sspi.SECPKG_CRED_OUTBOUND | Sspi.SECPKG_CRED_INBOUND) : Sspi.SECPKG_CRED_OUTBOUND;

int rc = Secur32.INSTANCE.AcquireCredentialsHandle(username,
scheme, credUse, null, null, null, null,
clientCred, lifetime);
if (WinError.SEC_E_OK != rc) {
throw new Win32Exception(rc);
}

if(impersonate) {
impersonationContext = getLocalContext(clientCred);
rc = Secur32.INSTANCE.ImpersonateSecurityContext(impersonationContext);
} else {
impersonationContext = null;
}

String targetSpn = getServicePrincipalName(context);
response = getToken(null, null, targetSpn);
} catch (RuntimeException ex) {
...
}
} else {
... // second round
}
... // build header form response and return it
}

方法getLocalContext()旨在为模拟用户获取服务本身的上下文。它看起来如下:

private static Sspi.CtxtHandle getLocalContext(final Sspi.CredHandle initialCredentials) {
final IntByReference attr = new IntByReference();

String localSpn = "HOST/" + Kernel32Util.getComputerName().toLowerCase();

Sspi.CtxtHandle clientContext = null;
Sspi.CtxtHandle serverContext = null;

Sspi.SecBufferDesc clientToServerToken;
Sspi.SecBufferDesc serverToClientToken = null;
while(true) {
// get clientContext and clientToServerToken:
Sspi.CtxtHandle outClientContext = (clientContext != null) ? clientContext : new Sspi.CtxtHandle();
clientToServerToken = new Sspi.SecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE);
int rc = Secur32.INSTANCE.InitializeSecurityContext(initialCredentials,
clientContext, localSpn, Sspi.ISC_REQ_DELEGATE | Sspi.ISC_REQ_MUTUAL_AUTH, 0,
Sspi.SECURITY_NATIVE_DREP, serverToClientToken, 0, outClientContext, clientToServerToken,
attr, null);
clientContext = outClientContext; // make them same for next round
switch (rc) {
case WinError.SEC_I_CONTINUE_NEEDED:
break;
case WinError.SEC_E_OK:
return serverContext != null ? serverContext : clientContext;
default:
freeContexts(clientContext, serverContext);
throw new Win32Exception(rc);
}

// get serverContext and serverToClientToken:
Sspi.CtxtHandle outServerContext = (serverContext != null) ? serverContext : new Sspi.CtxtHandle();
serverToClientToken = new Sspi.SecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE);
rc = Secur32.INSTANCE.AcceptSecurityContext(initialCredentials,
serverContext, clientToServerToken, Sspi.ISC_REQ_DELEGATE | Sspi.ISC_REQ_MUTUAL_AUTH,
Sspi.SECURITY_NATIVE_DREP, outServerContext, serverToClientToken, attr, null);
serverContext = outServerContext; // make them same for next round
switch (rc) {
case WinError.SEC_I_CONTINUE_NEEDED:
break;
case WinError.SEC_E_OK:
return serverContext;
default:
freeContexts(clientContext, serverContext);
throw new Win32Exception(rc);
}
}
}

我发现它在返回之前运行了完整的两轮( InitializeSecurityContextAcceptSecurityContextInitializeSecurityContextAcceptSecurityContext )。

作为引用,getToken方法只是从原始WinHttpClient WindowsNegotiateScheme复制而来类:

String getToken(
final CtxtHandle continueCtx,
final SecBufferDesc continueToken,
final String targetName) {
final IntByReference attr = new IntByReference();
final SecBufferDesc token = new SecBufferDesc(
Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE);

sspiContext = new CtxtHandle();
final int rc = Secur32.INSTANCE.InitializeSecurityContext(clientCred,
continueCtx, targetName, Sspi.ISC_REQ_DELEGATE | Sspi.ISC_REQ_MUTUAL_AUTH, 0,
Sspi.SECURITY_NATIVE_DREP, continueToken, 0, sspiContext, token,
attr, null);
switch (rc) {
case WinError.SEC_I_CONTINUE_NEEDED:
continueNeeded = true;
break;
case WinError.SEC_E_OK:
dispose(); // Don't keep the context
continueNeeded = false;
break;
default:
dispose();
throw new Win32Exception(rc);
}
return Base64.encodeBase64String(token.getBytes());
}

我在调试器中检查了用于模拟的用户名实际上类似于 DOMAINNAME\johndoe ,我意识到即使我使用不存在的用户也没关系,行为是一样的: AcquireCredentialsHandle返回 0 (= SEC_E_OK)。这告诉我,此方法不会根据域 Controller 检查用户名,而只会检查其参数的语法,但是为什么初始调用 InitializeSecurityContext如果第一个参数是不存在的用户的凭据,不会提示吗?或者该方法以某种方式完全忽略了它的第一个参数,但为什么呢?

假设我已经有一个供用户模拟的上下文 AcquireCredentialsHandle调用并省略对 getLocalContext 的调用和ImpersonateSecurityContext也没有用。

我发现没有太多关于使用纯 SSPI 进行模拟的文档。我发现的大多数示例(例如 this )依赖于 .net 类 WindowsIdentity ,这在纯 SSPI 接口(interface)中是不可用的。还有source code该类的内容看起来并不只是由几个 SSPI 调用组成,而是引用了许多其他 .net 基础结构。

我认为 Windows 操作系统模拟可能无法与 Java 正确协作,但根据 this question ,Windows 模拟是针对每个线程的,并且根据 this question ,Java 使用操作系统线程。

我怎样才能让模拟工作,或者你至少有一些尝试的提示?

最佳答案

您的代码永远不会执行您所描述的操作。您的代码片段仅执行凭证委派(无约束委派),其中转发的 TGT 嵌入到安全上下文中。由于您需要进行协议(protocol)转换(S4U2Self),因此需要调用LsaLogonUserKERB_S4U_LOGON .

阅读this博客文章。在 C 中这将是一个很大的痛苦。从来不明白为什么这对于 GSS-API 来说如此简单,但在 Windows 上却如此痛苦。

祝你好运。

关于java - 使用 SSPI : no errors, 进行 Kerberos 模拟但不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48460008/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com