2016-12-05 79 views
0

我正在嘗試使用JNA調用Windows上的Secur32.dll中的QueryContextAttributes函數。我無法爲SECPKG_ATTR_SIZES調用獲取正確的調用。我有一個實現SECPKG_ATTR_NAMES和SECPKG_ATTR_PACKAGE_INFO的工作。因此,我認爲(着名的遺言)問題出現在結構的定義中,或者是關於調用的問題。使用JNA在QueryContextAttributes上無效的內存訪問

爲QueryContextAttributes微軟函數的定義是:

SECURITY_STATUS SEC_Entry QueryContextAttributes(
    _In_ PCtxtHandle phContext, 
    _In_ ULONG  ulAttribute, 
    _Out_ PVOID  pBuffer 
); 

SecPkgContext_Sizes微軟結構的定義是:

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 structureSecPkgInfo 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進行romp。我相信這些工作正常,因爲在InitializeSecurityContext完成後,Kerberos票證顯示在MSLSA緩存中(可通過klist查看)。

我也看了一下解決方案Waffle,但是它並沒有在初始化循環中設置正確的標誌,也沒有實現QueryContextAttributes,也沒有實現這個函數的最終目標。

我對帖子的長度表示歉意。如果我遺漏了任何信息,請告訴我。

謝謝!

+0

'PointerByReference.getValue()'是_always_如何檢索傳入引用中返回的結果。 'getPointer()'給你持有該值的內存的地址。 – technomage

+0

你很可能有一個不正確的結構映射(你不提供'CtxtHandle'或'SecPkgContext_Sizes'的映射,所以我打賭錯誤在那裏)。用'-Djna.dump_memory = true'運行你的JVM,並打印結構;這將表明JNA的佈局。將其與類似的結構大小/字段對齊的本地轉儲相匹配。 – technomage

+0

@technomage,感謝您的意見。 CtxtHandle是一個標準的JNA結構。在複製代碼時,SecPkgContext_Sizes最後在上面的代碼中以「Bis」結尾;我道歉。我找到了我在下面提出的一個解決方案,但我認爲應該有一種更優雅的方式來實現結果。感謝您花時間閱讀並評論我的問題! – KevinO

回答

0

我能夠解決我遇到的問題,雖然不是非常優雅的方式。按照問題中的建議,我不是試圖使用QueryContextAttributes中的a PointerByReference,而是最終創建了多個方法定義,每個結構都有一個定義。所以我在接口中的方法如下:

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方法,以下從微軟的定義,是在界面定義爲:

public int MakeSignature(CtxtHandle phContext, 
         int fQOP, 
         ISecur32.SecBufferDesc pMessage, 
         int messageSeqNo); 

使用QueryContextAttributes上述方法和用於SecBufferDesc改性結構之後,我是能夠得到的工作溶液。 Java客戶端現在可以利用Microsoft SSPI系統將SSO用於遠程Linux機器。

+0

這實際上是映射採用多用途參數的函數的首選方法,因爲它提供比本機函數本身更多的類型安全性。 – technomage