0

I am attempting to use JNA to invoke the QueryContextAttributes function in the Secur32.dll on Windows. I am unable to get the invocation correct for the SECPKG_ATTR_SIZES call. I have an implementation working for SECPKG_ATTR_NAMES and SECPKG_ATTR_PACKAGE_INFO. Therefore, I presume (famous last words) the issue is in the definition of the structure, or something about the invocation.

The Microsoft function definition for QueryContextAttributes is:

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

The Microsoft structure definition for the SecPkgContext_Sizes is:

typedef struct _SecPkgContext_Sizes {
  ULONG cbMaxToken;
  ULONG cbMaxSignature;
  ULONG cbBlockSize;
  ULONG cbSecurityTrailer;
} SecPkgContext_Sizes, *PSecPkgContext_Sizes;

The JNA library (I'm using jna-4.2.2 and jna-platform-4.2.2) provides an implementation in the Secur32 for some of the functions in that .dll. The definitions for the structures are at: SecPkgContext_Names structure, SecPkgInfo structure, and SecPkgContext_Sizes structure

I therefore have defined the following:

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

The call for the Names (which is working) is:

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);    
  }

When I attempt to obtain the Sizes (specifically, I'm after the cbMaxSignature value), I use the following call:

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();
  }

Using the above call, I receive the Exception stack trace of:

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)

If instead of the pp.getValue() call, I use pp.getPointer(), I receive (when trying to instantiate the Object, I believe):

java.lang.IllegalArgumentException: Structure exceeds provided memory bounds

I am at a loss as to how to solve this issue.

I apologize for not having a complete program, but to get to the point of having the CtxtHandle needed requires a romp through AcquireCredentialsHandle and InitializeSecurityContext. I believe these are working appropriately, as the Kerberos ticket is showing in the MSLSA cache (viewable via klist) after the InitializeSecurityContext completes.

I also looked at the solution Waffle, but it does not set the correct flags for in the initialization loop, and also does not implement QueryContextAttributes, or the ultimate goal in all of this the MakeSignature function.

I apologize for the length of the post. If I have omitted any piece of information, please let me know.

Thank you!

KevinO
  • 4,303
  • 4
  • 27
  • 36
  • `PointerByReference.getValue()` is _always_ how you should retrieve a result returned in a passed-in reference. `getPointer()` gives you the address of the memory holding that value. – technomage Dec 06 '16 at 21:20
  • You most likely have an improper structure mapping (you don't provide your mapping of `CtxtHandle` or `SecPkgContext_Sizes`, so I'm betting the error is there). Run your JVM with `-Djna.dump_memory=true`, and print the structures; that will indicate JNA's layout. Match that up with a similar native dump of structure size/field alignment. – technomage Dec 06 '16 at 21:22
  • @technomage, thank you for the comments. The CtxtHandle is a standard JNA structure. In copying code, the SecPkgContext_Sizes ended up with a "Bis" on the end in the code above; I apologize. I found a solution I present below, though I think there should be a more elegant way of achieving the outcome. I appreciate you taking the time to read and comment upon my issue! – KevinO Dec 13 '16 at 19:02

1 Answers1

0

I was able to resolve the issue I was having, though not in a very elegant manner. Rather than attempting to use the a PointerByReference in the QueryContextAttributes as suggested in the question, I ended up creating multiple method definitions, one for each structure. So I have in the Interface, the approach of:

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" 
    });
  }
}

And so forth for the few definitions I need.

Ultimately the goal was to get the MakeSignature call to work (the QueryContextAttributes(...) being a precursor), and I had trouble leveraging the default JNA SecBufferDesc structure. I found a pointer to a solution from JSch SSPI by Joe Khoobyar, and leveraged the basic definition from that site, changing the NativeLong objects to int, and adding the new required method getFieldOrder().

The MakeSignature method, following definitions from Microsoft, was defined in the Interface as:

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

After using the above methods for QueryContextAttributes and the modified structure for SecBufferDesc, I was able to get a working solution. The Java clients may now leverage the Microsoft SSPI system for SSO to the remote Linux machines.

KevinO
  • 4,303
  • 4
  • 27
  • 36
  • This is actually the preferred method of mapping functions which take multi-purpose arguments, since it provides more type safety than the native function itself does. – technomage Dec 13 '16 at 20:37