我正在尝试使用JNA在Windows上调用Secur32.dll中的QueryContextAttributes函数。我无法使SECPKG_ATTR_SIZES调用的调用正确。我有一个适用于SECPKG_ATTR_NAMES和SECPKG_ATTR_PACKAGE_INFO的实现。因此,我推测(着名的最后一句话)问题在于结构的定义,或者关于调用的事情。
QueryContextAttributes的Microsoft函数定义是:
SECURITY_STATUS SEC_Entry QueryContextAttributes(
_In_ PCtxtHandle phContext,
_In_ ULONG ulAttribute,
_Out_ PVOID pBuffer
);
SecPkgContext_Sizes的Microsoft结构定义是:
typedef struct _SecPkgContext_Sizes {
ULONG cbMaxToken;
ULONG cbMaxSignature;
ULONG cbBlockSize;
ULONG cbSecurityTrailer;
} SecPkgContext_Sizes, *PSecPkgContext_Sizes;
JNA库(我使用jna-4.2.2和jna-platform-4.2.2)在Secur32中为该.dll中的一些函数提供了一个实现。结构的定义如下: SecPkgContext_Names structure, SecPkgInfo structure,和 SecPkgContext_Sizes structure
因此我定义了以下内容:
public interface ISecur32 extends Secur32 {
// get own copy of the INSTANCE variable
ISecur32 INSTANCE = (ISecur32) Native.loadLibrary("Secur32",
ISecur32.class,
W32APIOptions.UNICODE_OPTIONS);
// method definition to match
public int QueryContextAttributes(CtxtHandle phContext,
int SECPKG_ATTR,
PointerByReference pp);
//
// for the SECPKG_ATTR_NAMES call
// NOTE: this definition and invocation is working
//
public static class SecPkgContext_Names extends Structure {
public Pointer pName;
public SecPkgContext_Names(Pointer p)
{ super(p); }
@Override
protected List<?> getFieldOrder()
{ return Arrays.asList(new String[] { "pName" }); }
}
//
// for the SECPKG_ATTR_SIZES call
// NOTE: This invocation is NOT working
//
public static class SecPkgContext_SizesBis extends Structure {
public NativeLong cbMaxToken;
public NativeLong cbMaxSignature;
public NativeLong cbBlockSize;
public NativeLong cbSecurityTrailer;
public SecPkgContext_SizesBis(Pointer p)
{ super(p); }
@Override
protected List<?> getFieldOrder() {
return Arrays.asList(new String[] { "cbMaxToken", "cbMaxSignature",
"cbBlockSize", "cbSecurityTrailer"});
}
} //interface
姓名(正在运作)的调用是:
public static void querySecPkgAttr_Names(CtxtHandle phContext) {
final int SECPKG_ATTR_NAMES = 1;
PointerByReference pp = new PointerByReference();
int rc = ISecur32.INSTANCE.QueryContextAttributes(phContext,
SECPKG_ATTR_NAMES,
pp);
if (rc != 0) {
_log.error("Error in QueryContextAttributes: {}", rc);
return;
}
Pointer p = pp.getPointer();
ISecur32.SecPkgContext_Names names = new ISecur32.SecPkgContext_Names(p);
names.read();
String name = names.pName.getWideString(0);
rc = ISecur32.INSTANCE.FreeContextBuffer(p);
_log.debug("FreeContextBuffer: {}", rc);
}
当我尝试获取大小(具体来说,我在cbMaxSignature值之后)时,我使用以下调用:
public static int querySecPkgAttr_Sizes(CtxtHandle phContext) {
final int SECPKG_ATTR_SIZES = 0; // SECPKG_ATTR_SIZES is 0
PointerByReference pp = new PointerByReference();
int res = ISecur32.INSTANCE.QueryContextAttributes(phContext,
SECPKG_ATTR_SIZES,
pp);
// NOTE: the call is succeeding, so this line is not invoked
if (res != 0) {
return new NativeLong(0);
}
// NOTE: I have also used pp.getPointer()
Pointer p = pp.getValue();
ISecur32.SecPkgContext_Sizes sizes =
new ISecur32.SecPkgContext_Sizes(p);
// THIS LINE THROWS THE Invalid Memory Access Error
sizes.read();
NativeLong maxSig = sizes.cbMaxSignature;
rc = ISecur32.INSTANCE.FreeContextBuffer(p);
_log.debug("FreeContextBuffer: {}", rc);
return maxSig.intValue();
}
使用上面的调用,我收到了以下的异常堆栈跟踪:
Exception in thread "main" java.lang.Error: Invalid memory access
at com.sun.jna.Native.getInt(Native Method)
at com.sun.jna.Pointer.getInt(Pointer.java:601)
at com.sun.jna.Pointer.getValue(Pointer.java:389)
at com.sun.jna.Structure.readField(Structure.java:705)
at com.sun.jna.Structure.read(Structure.java:565)
at gov.sandia.dart.sspi.Utils.querySecPkgAttr_Sizes(Utils.java:145)
如果不是pp.getValue()
调用,我使用pp.getPointer()
,我收到(当我尝试实例化对象时,我相信):
java.lang.IllegalArgumentException: Structure exceeds provided memory bounds
我对如何解决这个问题感到很茫然。
我为没有一个完整的程序而道歉,但是为了达到需要CtxtHandle的程度,需要通过AcquireCredentialsHandle和InitializeSecurityContext进行嬉戏。我相信这些工作正常,因为Kerberos票证在InitializeSecurityContext
完成后显示在MSLSA缓存中(可通过klist查看)。
我还查看了解决方案Waffle,但它没有在初始化循环中设置正确的标志,也没有实现QueryContextAttributes,或者MakeSignature
中的所有这一切的最终目标功能
我为帖子的长度道歉。如果我遗漏了任何信息,请告诉我。
谢谢!
答案 0 :(得分:0)
我能够解决我遇到的问题,虽然不是很优雅。我没有尝试使用问题中建议的PointerByReference
中的QueryContextAttributes
,而是创建了多个方法定义,每个结构一个。所以我在界面中采用了以下方法:
public int QueryContextAttributes(CtxtHandle phContext,
int SECPKG_ATTR,
SecPkgContext_NegotiationInfo negoInfo);
public static class SecPkgContext_NegotiationInfo extends Structure
{
public Pointer pPackageInfo;
public int negotiationState;
public SecPkgContext_NegotiationInfo() {
super();
}
public SecPkgContext_NegotiationInfo(Pointer p) {
super(p);
}
@Override
protected List<?> getFieldOrder() {
return Arrays.asList(new String[] { "pPackageInfo", "negotiationState"
});
}
}
public int QueryContextAttributes(CtxtHandle phContext,
int SECPKG_ATTR,
SecPkgContext_Sizes sizes);
public static class SecPkgContext_Sizes extends Structure
{
public int cbMaxToken;
public int cbMaxSignature;
public int cbBlockSize;
public int cbSecurityTrailer;
public SecPkgContext_Sizes() {
super();
}
public SecPkgContext_Sizes(Pointer p) {
super(p);
}
@Override
protected List<?> getFieldOrder() {
return Arrays.asList(new String[] { "cbMaxToken",
"cbMaxSignature",
"cbBlockSize",
"cbSecurityTrailer"
});
}
}
等我需要的几个定义。
最终目标是让MakeSignature
调用工作(QueryContextAttributes(...)
是一个前兆),而我在利用默认的JNA SecBufferDesc
结构时遇到了麻烦。我找到了指向JSch SSPI by Joe Khoobyar解决方案的指针,并利用该网站的基本定义,将NativeLong
对象更改为int
,并添加了新的必需方法getFieldOrder()
。
MakeSignature
方法遵循Microsoft的定义,在接口中定义为:
public int MakeSignature(CtxtHandle phContext,
int fQOP,
ISecur32.SecBufferDesc pMessage,
int messageSeqNo);
使用QueryContextAttributes
的上述方法和SecBufferDesc
的修改结构后,我得到了一个有效的解决方案。 Java客户端现在可以利用Microsoft SSPI系统进行远程Linux机器的SSO。