6722928: Support SSPI as a native GSS-API provider
authorweijun
Thu, 13 Jun 2019 10:06:07 +0800
changeset 55354 74f0622db875
parent 55353 946f7f2d321c
child 55355 bd2f0954cbb9
6722928: Support SSPI as a native GSS-API provider Reviewed-by: erikj, nwilliams, valeriep
make/lib/Lib-java.security.jgss.gmk
src/java.security.jgss/share/classes/sun/security/jgss/spnego/NegTokenTarg.java
src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java
src/java.security.jgss/share/native/libj2gss/GSSLibStub.c
src/java.security.jgss/share/native/libj2gss/NativeFunc.h
src/java.security.jgss/share/native/libj2gss/NativeUtil.h
src/java.security.jgss/share/native/libj2gss/gssapi.h
src/java.security.jgss/windows/native/libsspi_bridge/sspi.cpp
--- a/make/lib/Lib-java.security.jgss.gmk	Wed Jun 12 18:58:00 2019 -0700
+++ b/make/lib/Lib-java.security.jgss.gmk	Thu Jun 13 10:06:07 2019 +0800
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -57,6 +57,17 @@
     ))
 
     TARGETS += $(BUILD_LIBW2K_LSA_AUTH)
+
+    $(eval $(call SetupJdkLibrary, BUILD_LIBSSPI_BRIDGE, \
+        NAME := sspi_bridge, \
+        OPTIMIZATION := LOW, \
+        CFLAGS := $(CFLAGS_JDKLIB) \
+            -I$(TOPDIR)/src/java.security.jgss/share/native/libj2gss, \
+        LDFLAGS := $(LDFLAGS_JDKLIB) \
+            $(call SET_SHARED_LIBRARY_ORIGIN) \
+    ))
+
+    TARGETS += $(BUILD_LIBSSPI_BRIDGE)
   endif
 
   ifeq ($(call isTargetOs, macosx), true)
--- a/src/java.security.jgss/share/classes/sun/security/jgss/spnego/NegTokenTarg.java	Wed Jun 12 18:58:00 2019 -0700
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/spnego/NegTokenTarg.java	Thu Jun 13 10:06:07 2019 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,6 @@
 package sun.security.jgss.spnego;
 
 import java.io.*;
-import java.util.*;
 import org.ietf.jgss.*;
 import sun.security.jgss.*;
 import sun.security.util.*;
@@ -113,18 +112,6 @@
                 mic.putOctetString(mechListMIC);
                 targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
                                         true, (byte) 0x03), mic);
-            } else if (GSSUtil.useMSInterop()) {
-                // required for MS-interoperability
-                if (responseToken != null) {
-                    if (DEBUG) {
-                        System.out.println("SpNegoToken NegTokenTarg: " +
-                                "sending additional token for MS Interop");
-                    }
-                    DerOutputStream rspToken = new DerOutputStream();
-                    rspToken.putOctetString(responseToken);
-                    targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT,
-                                                true, (byte) 0x03), rspToken);
-                }
             }
 
             // insert in a SEQUENCE
--- a/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java	Wed Jun 12 18:58:00 2019 -0700
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/wrapper/SunNativeProvider.java	Thu Jun 13 10:06:07 2019 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -66,7 +66,7 @@
     static {
         MECH_MAP =
             AccessController.doPrivileged(
-                new PrivilegedAction<HashMap<String, String>>() {
+                new PrivilegedAction<>() {
                     public HashMap<String, String> run() {
                         DEBUG = Boolean.parseBoolean(
                             System.getProperty("sun.security.nativegss.debug"));
@@ -77,7 +77,7 @@
                             if (DEBUG) err.printStackTrace();
                             return null;
                         }
-                        String[] gssLibs = new String[0];
+                        String[] gssLibs;
                         String defaultLib
                                 = System.getProperty("sun.security.jgss.lib");
                         if (defaultLib == null || defaultLib.trim().equals("")) {
@@ -95,6 +95,12 @@
                                     "libgssapi_krb5.dylib",
                                     "/usr/lib/sasl2/libgssapiv2.2.so",
                                };
+                            } else if (osname.contains("Windows")) {
+                                // Full path needed, DLL is in jre/bin
+                                gssLibs = new String[]{ System.getProperty("java.home")
+                                        + "\\bin\\sspi_bridge.dll" };
+                            } else {
+                                gssLibs = new String[0];
                             }
                         } else {
                             gssLibs = new String[]{ defaultLib };
@@ -103,8 +109,7 @@
                             if (GSSLibStub.init(libName, DEBUG)) {
                                 debug("Loaded GSS library: " + libName);
                                 Oid[] mechs = GSSLibStub.indicateMechs();
-                                HashMap<String, String> map =
-                                            new HashMap<String, String>();
+                                HashMap<String,String> map = new HashMap<>();
                                 for (int i = 0; i < mechs.length; i++) {
                                     debug("Native MF for " + mechs[i]);
                                     map.put("GssApiMechanism." + mechs[i],
--- a/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c	Wed Jun 12 18:58:00 2019 -0700
+++ b/src/java.security.jgss/share/native/libj2gss/GSSLibStub.c	Thu Jun 13 10:06:07 2019 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -982,7 +982,7 @@
   OM_uint32 aFlags;
   OM_uint32 aTime;
   gss_cred_id_t delCred;
-  jobject jsrcName=GSS_C_NO_NAME;
+  jobject jsrcName = NULL;
   jobject jdelCred;
   jobject jMech;
   jboolean setTarget;
--- a/src/java.security.jgss/share/native/libj2gss/NativeFunc.h	Wed Jun 12 18:58:00 2019 -0700
+++ b/src/java.security.jgss/share/native/libj2gss/NativeFunc.h	Thu Jun 13 10:06:07 2019 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -57,38 +57,38 @@
 
 typedef OM_uint32 (*IMPORT_NAME_FN_PTR)
                                 (OM_uint32 *minor_status,
-                                gss_buffer_t input_name_buffer,
-                                gss_OID input_name_type,
+                                const gss_buffer_t input_name_buffer,
+                                const gss_OID input_name_type,
                                 gss_name_t *output_name);
 
 typedef OM_uint32 (*COMPARE_NAME_FN_PTR)
                                 (OM_uint32 *minor_status,
-                                gss_name_t name1,
-                                gss_name_t name2,
+                                gss_const_name_t name1,
+                                gss_const_name_t name2,
                                 int *name_equal);
 
 typedef OM_uint32 (*CANONICALIZE_NAME_FN_PTR)
                                 (OM_uint32 *minor_status,
-                                gss_name_t input_name,
-                                gss_OID mech_type,
+                                gss_const_name_t input_name,
+                                const gss_OID mech_type,
                                 gss_name_t *output_name);
 
 typedef OM_uint32 (*EXPORT_NAME_FN_PTR)
                                 (OM_uint32 *minor_status,
-                                gss_name_t input_name,
+                                gss_const_name_t input_name,
                                 gss_buffer_t exported_name);
 
 typedef OM_uint32 (*DISPLAY_NAME_FN_PTR)
                                 (OM_uint32 *minor_status,
-                                gss_name_t input_name,
+                                gss_const_name_t input_name,
                                 gss_buffer_t output_name_buffer,
                                 gss_OID *output_name_type);
 
 typedef OM_uint32 (*ACQUIRE_CRED_FN_PTR)
                                 (OM_uint32 *minor_status,
-                                gss_name_t desired_name,
+                                gss_const_name_t desired_name,
                                 OM_uint32 time_req,
-                                gss_OID_set desired_mech,
+                                const gss_OID_set desired_mech,
                                 gss_cred_usage_t cred_usage,
                                 gss_cred_id_t *output_cred_handle,
                                 gss_OID_set *actual_mechs,
@@ -100,7 +100,7 @@
 
 typedef OM_uint32 (*INQUIRE_CRED_FN_PTR)
                                 (OM_uint32 *minor_status,
-                                gss_cred_id_t cred_handle,
+                                gss_const_cred_id_t cred_handle,
                                 gss_name_t *name,
                                 OM_uint32 *lifetime,
                                 gss_cred_usage_t *cred_usage,
@@ -108,19 +108,19 @@
 
 typedef OM_uint32 (*IMPORT_SEC_CONTEXT_FN_PTR)
                                 (OM_uint32 *minor_status,
-                                gss_buffer_t interprocess_token,
+                                const gss_buffer_t interprocess_token,
                                 gss_ctx_id_t *context_handle);
 
 typedef OM_uint32 (*INIT_SEC_CONTEXT_FN_PTR)
                                 (OM_uint32 *minor_status,
-                                gss_cred_id_t initiator_cred_handle,
+                                gss_const_cred_id_t initiator_cred_handle,
                                 gss_ctx_id_t *context_handle,
-                                gss_name_t *target_name,
-                                gss_OID mech_type,
+                                gss_const_name_t target_name,
+                                const gss_OID mech_type,
                                 OM_uint32 req_flags,
                                 OM_uint32 time_req,
-                                gss_channel_bindings_t input_chan_bindings,
-                                gss_buffer_t input_token,
+                                const gss_channel_bindings_t input_chan_bindings,
+                                const gss_buffer_t input_token,
                                 gss_OID *actual_mech_type,
                                 gss_buffer_t output_token,
                                 OM_uint32 *ret_flags,
@@ -129,9 +129,9 @@
 typedef OM_uint32 (*ACCEPT_SEC_CONTEXT_FN_PTR)
                                 (OM_uint32 *minor_status,
                                 gss_ctx_id_t *context_handle,
-                                gss_cred_id_t acceptor_cred_handle,
-                                gss_buffer_t input_token,
-                                gss_channel_bindings_t input_chan_bindings,
+                                gss_const_cred_id_t acceptor_cred_handle,
+                                const gss_buffer_t input_token,
+                                const gss_channel_bindings_t input_chan_bindings,
                                 gss_name_t *src_name,
                                 gss_OID *mech_type,
                                 gss_buffer_t output_token,
@@ -141,7 +141,7 @@
 
 typedef OM_uint32 (*INQUIRE_CONTEXT_FN_PTR)
                                 (OM_uint32 *minor_status,
-                                gss_ctx_id_t context_handle,
+                                gss_const_ctx_id_t context_handle,
                                 gss_name_t *src_name,
                                 gss_name_t *targ_name,
                                 OM_uint32 *lifetime_rec,
@@ -157,12 +157,12 @@
 
 typedef OM_uint32 (*CONTEXT_TIME_FN_PTR)
                                 (OM_uint32 *minor_status,
-                                gss_ctx_id_t *context_handle,
+                                gss_const_ctx_id_t context_handle,
                                 OM_uint32 *time_rec);
 
 typedef OM_uint32 (*WRAP_SIZE_LIMIT_FN_PTR)
                                 (OM_uint32 *minor_status,
-                                gss_ctx_id_t context_handle,
+                                gss_const_ctx_id_t context_handle,
                                 int conf_req_flag,
                                 gss_qop_t qop_req,
                                 OM_uint32 req_output_size,
@@ -175,31 +175,31 @@
 
 typedef OM_uint32 (*GET_MIC_FN_PTR)
                                 (OM_uint32 *minor_status,
-                                gss_ctx_id_t context_handle,
+                                gss_const_ctx_id_t context_handle,
                                 gss_qop_t qop_req,
-                                gss_buffer_t message_buffer,
+                                const gss_buffer_t message_buffer,
                                 gss_buffer_t msg_token);
 
 typedef OM_uint32 (*VERIFY_MIC_FN_PTR)
                                 (OM_uint32 *minor_status,
-                                gss_ctx_id_t context_handle,
-                                gss_buffer_t message_buffer,
-                                gss_buffer_t token_buffer,
+                                gss_const_ctx_id_t context_handle,
+                                const gss_buffer_t message_buffer,
+                                const gss_buffer_t token_buffer,
                                 gss_qop_t *qop_state);
 
 typedef OM_uint32 (*WRAP_FN_PTR)
                                 (OM_uint32 *minor_status,
-                                gss_ctx_id_t context_handle,
+                                gss_const_ctx_id_t context_handle,
                                 int conf_req_flag,
                                 gss_qop_t qop_req,
-                                gss_buffer_t input_message_buffer,
+                                const gss_buffer_t input_message_buffer,
                                 int *conf_state,
                                 gss_buffer_t output_message_buffer);
 
 typedef OM_uint32 (*UNWRAP_FN_PTR)
                                 (OM_uint32 *minor_status,
-                                gss_ctx_id_t context_handle,
-                                gss_buffer_t input_message_buffer,
+                                gss_const_ctx_id_t context_handle,
+                                const gss_buffer_t input_message_buffer,
                                 gss_buffer_t output_message_buffer,
                                 int *conf_state,
                                 gss_qop_t *qop_state);
@@ -215,14 +215,14 @@
 
 typedef OM_uint32 (*ADD_OID_SET_MEMBER_FN_PTR)
                                 (OM_uint32 *minor_status,
-                                gss_OID member_oid,
+                                const gss_OID member_oid,
                                 gss_OID_set *oid_set);
 
 typedef OM_uint32 (*DISPLAY_STATUS_FN_PTR)
                                 (OM_uint32 *minor_status,
                                 OM_uint32 status_value,
                                 int status_type,
-                                gss_OID mech_type,
+                                const gss_OID mech_type,
                                 OM_uint32 *message_context,
                                 gss_buffer_t status_string);
 
--- a/src/java.security.jgss/share/native/libj2gss/NativeUtil.h	Wed Jun 12 18:58:00 2019 -0700
+++ b/src/java.security.jgss/share/native/libj2gss/NativeUtil.h	Thu Jun 13 10:06:07 2019 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -83,10 +83,10 @@
   extern jfieldID FID_NativeGSSContext_flags;
   extern jfieldID FID_NativeGSSContext_lifetime;
   extern jfieldID FID_NativeGSSContext_actualMech;
-  #define TRACE0(s) { if (JGSS_DEBUG) { puts(s); fflush(stdout); }}
-  #define TRACE1(s, p1) { if (JGSS_DEBUG) { printf(s"\n", p1); fflush(stdout); }}
-  #define TRACE2(s, p1, p2) { if (JGSS_DEBUG) { printf(s"\n", p1, p2); fflush(stdout); }}
-  #define TRACE3(s, p1, p2, p3) { if (JGSS_DEBUG) { printf(s"\n", p1, p2, p3); fflush(stdout); }}
+  #define TRACE0(s) { if (JGSS_DEBUG) { printf("[GSSLibStub:%d] %s\n", __LINE__, s); fflush(stdout); }}
+  #define TRACE1(s, p1) { if (JGSS_DEBUG) { printf("[GSSLibStub:%d] "s"\n", __LINE__, p1); fflush(stdout); }}
+  #define TRACE2(s, p1, p2) { if (JGSS_DEBUG) { printf("[GSSLibStub:%d] "s"\n", __LINE__, p1, p2); fflush(stdout); }}
+  #define TRACE3(s, p1, p2, p3) { if (JGSS_DEBUG) { printf("[GSSLibStub:%d] "s"\n", __LINE__, p1, p2, p3); fflush(stdout); }}
 
 
 #ifdef __cplusplus
--- a/src/java.security.jgss/share/native/libj2gss/gssapi.h	Wed Jun 12 18:58:00 2019 -0700
+++ b/src/java.security.jgss/share/native/libj2gss/gssapi.h	Thu Jun 13 10:06:07 2019 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -57,9 +57,17 @@
  */
 #include <sys/types.h>
 
-typedef void * gss_name_t;
-typedef void * gss_cred_id_t;
-typedef void * gss_ctx_id_t;
+struct gss_name_struct;
+typedef struct gss_name_struct * gss_name_t;
+typedef const struct gss_name_struct *gss_const_name_t;
+
+struct gss_cred_id_struct;
+typedef struct gss_cred_id_struct * gss_cred_id_t;
+typedef const struct gss_cred_id_struct *gss_const_cred_id_t;
+
+struct gss_ctx_id_struct;
+typedef struct gss_ctx_id_struct * gss_ctx_id_t;
+typedef const struct gss_ctx_id_struct *gss_const_ctx_id_t;
 
 /*
  * The following type must be defined as the smallest natural unsigned integer
@@ -82,16 +90,19 @@
       OM_uint32 length;
       void *elements;
 } gss_OID_desc, *gss_OID;
+typedef const gss_OID_desc * gss_const_OID;
 
 typedef struct gss_OID_set_desc_struct  {
       size_t  count;
       gss_OID elements;
 } gss_OID_set_desc, *gss_OID_set;
+typedef const gss_OID_set_desc * gss_const_OID_set;
 
 typedef struct gss_buffer_desc_struct {
       size_t length;
       void *value;
 } gss_buffer_desc, *gss_buffer_t;
+typedef const gss_buffer_desc * gss_const_buffer_t;
 
 typedef struct gss_channel_bindings_struct {
       OM_uint32 initiator_addrtype;
@@ -100,6 +111,7 @@
       gss_buffer_desc acceptor_address;
       gss_buffer_desc application_data;
 } *gss_channel_bindings_t;
+typedef const struct gss_channel_bindings_struct *gss_const_channel_bindings_t;
 
 /*
  * For now, define a QOP-type as an OM_uint32
@@ -119,6 +131,7 @@
 #define GSS_C_ANON_FLAG 64
 #define GSS_C_PROT_READY_FLAG 128
 #define GSS_C_TRANS_FLAG 256
+#define GSS_C_DELEG_POLICY_FLAG 32768
 
 /*
  * Credential usage options
@@ -389,9 +402,9 @@
 
 GSS_DLLIMP OM_uint32 gss_acquire_cred(
         OM_uint32 *,            /* minor_status */
-        gss_name_t,             /* desired_name */
+        gss_const_name_t,       /* desired_name */
         OM_uint32,              /* time_req */
-        gss_OID_set,            /* desired_mechs */
+        const gss_OID_set,      /* desired_mechs */
         gss_cred_usage_t,       /* cred_usage */
         gss_cred_id_t *,        /* output_cred_handle */
         gss_OID_set *,          /* actual_mechs */
@@ -405,14 +418,14 @@
 
 GSS_DLLIMP OM_uint32 gss_init_sec_context(
         OM_uint32 *,            /* minor_status */
-        gss_cred_id_t,          /* claimant_cred_handle */
+        gss_const_cred_id_t,    /* claimant_cred_handle */
         gss_ctx_id_t *,         /* context_handle */
-        gss_name_t,             /* target_name */
-        gss_OID,                /* mech_type (used to be const) */
+        gss_const_name_t,       /* target_name */
+        const gss_OID,          /* mech_type */
         OM_uint32,              /* req_flags */
         OM_uint32,              /* time_req */
-        gss_channel_bindings_t, /* input_chan_bindings */
-        gss_buffer_t,           /* input_token */
+        const gss_channel_bindings_t, /* input_chan_bindings */
+        const gss_buffer_t,     /* input_token */
         gss_OID *,              /* actual_mech_type */
         gss_buffer_t,           /* output_token */
         OM_uint32 *,            /* ret_flags */
@@ -422,9 +435,9 @@
 GSS_DLLIMP OM_uint32 gss_accept_sec_context(
         OM_uint32 *,            /* minor_status */
         gss_ctx_id_t *,         /* context_handle */
-        gss_cred_id_t,          /* acceptor_cred_handle */
-        gss_buffer_t,           /* input_token_buffer */
-        gss_channel_bindings_t, /* input_chan_bindings */
+        gss_const_cred_id_t,    /* acceptor_cred_handle */
+        const gss_buffer_t,     /* input_token_buffer */
+        const gss_channel_bindings_t, /* input_chan_bindings */
         gss_name_t *,           /* src_name */
         gss_OID *,              /* mech_type */
         gss_buffer_t,           /* output_token */
@@ -435,8 +448,8 @@
 
 GSS_DLLIMP OM_uint32 gss_process_context_token(
         OM_uint32 *,            /* minor_status */
-        gss_ctx_id_t,           /* context_handle */
-        gss_buffer_t            /* token_buffer */
+        gss_const_ctx_id_t,     /* context_handle */
+        const gss_buffer_t      /* token_buffer */
 );
 
 GSS_DLLIMP OM_uint32 gss_delete_sec_context(
@@ -447,35 +460,35 @@
 
 GSS_DLLIMP OM_uint32 gss_context_time(
         OM_uint32 *,            /* minor_status */
-        gss_ctx_id_t,           /* context_handle */
+        gss_const_ctx_id_t,     /* context_handle */
         OM_uint32 *             /* time_rec */
 );
 
 /* New for V2 */
 GSS_DLLIMP OM_uint32 gss_get_mic(
         OM_uint32 *,            /* minor_status */
-        gss_ctx_id_t,           /* context_handle */
+        gss_const_ctx_id_t,     /* context_handle */
         gss_qop_t,              /* qop_req */
-        gss_buffer_t,           /* message_buffer */
+        const gss_buffer_t,     /* message_buffer */
         gss_buffer_t            /* message_token */
 );
 
 /* New for V2 */
 GSS_DLLIMP OM_uint32 gss_verify_mic(
         OM_uint32 *,            /* minor_status */
-        gss_ctx_id_t,           /* context_handle */
-        gss_buffer_t,           /* message_buffer */
-        gss_buffer_t,           /* message_token */
+        gss_const_ctx_id_t,     /* context_handle */
+        const gss_buffer_t,     /* message_buffer */
+        const gss_buffer_t,     /* message_token */
         gss_qop_t *             /* qop_state */
 );
 
 /* New for V2 */
 GSS_DLLIMP OM_uint32 gss_wrap(
         OM_uint32 *,            /* minor_status */
-        gss_ctx_id_t,           /* context_handle */
+        gss_const_ctx_id_t,     /* context_handle */
         int,                    /* conf_req_flag */
         gss_qop_t,              /* qop_req */
-        gss_buffer_t,           /* input_message_buffer */
+        const gss_buffer_t,     /* input_message_buffer */
         int *,                  /* conf_state */
         gss_buffer_t            /* output_message_buffer */
 );
@@ -483,8 +496,8 @@
 /* New for V2 */
 GSS_DLLIMP OM_uint32 gss_unwrap(
         OM_uint32 *,            /* minor_status */
-        gss_ctx_id_t,           /* context_handle */
-        gss_buffer_t,           /* input_message_buffer */
+        gss_const_ctx_id_t,     /* context_handle */
+        const gss_buffer_t,     /* input_message_buffer */
         gss_buffer_t,           /* output_message_buffer */
         int *,                  /* conf_state */
         gss_qop_t *             /* qop_state */
@@ -494,7 +507,7 @@
         OM_uint32 *,            /* minor_status */
         OM_uint32,              /* status_value */
         int,                    /* status_type */
-        gss_OID,                /* mech_type (used to be const) */
+        const gss_OID,          /* mech_type (used to be const) */
         OM_uint32 *,            /* message_context */
         gss_buffer_t            /* status_string */
 );
@@ -506,22 +519,22 @@
 
 GSS_DLLIMP OM_uint32 gss_compare_name(
         OM_uint32 *,            /* minor_status */
-        gss_name_t,             /* name1 */
-        gss_name_t,             /* name2 */
+        gss_const_name_t,       /* name1 */
+        gss_const_name_t,       /* name2 */
         int *                   /* name_equal */
 );
 
 GSS_DLLIMP OM_uint32 gss_display_name(
         OM_uint32 *,            /* minor_status */
-        gss_name_t,             /* input_name */
+        gss_const_name_t,       /* input_name */
         gss_buffer_t,           /* output_name_buffer */
         gss_OID *               /* output_name_type */
 );
 
 GSS_DLLIMP OM_uint32 gss_import_name(
         OM_uint32 *,            /* minor_status */
-        gss_buffer_t,           /* input_name_buffer */
-        gss_OID,                /* input_name_type(used to be const) */
+        const gss_buffer_t,     /* input_name_buffer */
+        const gss_OID,          /* input_name_type(used to be const) */
         gss_name_t *            /* output_name */
 );
 
@@ -542,7 +555,7 @@
 
 GSS_DLLIMP OM_uint32 gss_inquire_cred(
         OM_uint32 *,            /* minor_status */
-        gss_cred_id_t,          /* cred_handle */
+        gss_const_cred_id_t,    /* cred_handle */
         gss_name_t *,           /* name */
         OM_uint32 *,            /* lifetime */
         gss_cred_usage_t *,     /* cred_usage */
@@ -552,7 +565,7 @@
 /* Last argument new for V2 */
 GSS_DLLIMP OM_uint32 gss_inquire_context(
         OM_uint32 *,            /* minor_status */
-        gss_ctx_id_t,           /* context_handle */
+        gss_const_ctx_id_t,     /* context_handle */
         gss_name_t *,           /* src_name */
         gss_name_t *,           /* targ_name */
         OM_uint32 *,            /* lifetime_rec */
@@ -565,7 +578,7 @@
 /* New for V2 */
 GSS_DLLIMP OM_uint32 gss_wrap_size_limit(
         OM_uint32 *,            /* minor_status */
-        gss_ctx_id_t,           /* context_handle */
+        gss_const_ctx_id_t,     /* context_handle */
         int,                    /* conf_req_flag */
         gss_qop_t,              /* qop_req */
         OM_uint32,              /* req_output_size */
@@ -575,9 +588,9 @@
 /* New for V2 */
 GSS_DLLIMP OM_uint32 gss_add_cred(
         OM_uint32 *,            /* minor_status */
-        gss_cred_id_t,          /* input_cred_handle */
-        gss_name_t,             /* desired_name */
-        gss_OID,                /* desired_mech */
+        gss_const_cred_id_t,    /* input_cred_handle */
+        gss_const_name_t,       /* desired_name */
+        const gss_OID,          /* desired_mech */
         gss_cred_usage_t,       /* cred_usage */
         OM_uint32,              /* initiator_time_req */
         OM_uint32,              /* acceptor_time_req */
@@ -590,8 +603,8 @@
 /* New for V2 */
 GSS_DLLIMP OM_uint32 gss_inquire_cred_by_mech(
         OM_uint32 *,            /* minor_status */
-        gss_cred_id_t,          /* cred_handle */
-        gss_OID,                /* mech_type */
+        gss_const_cred_id_t,    /* cred_handle */
+        const gss_OID,          /* mech_type */
         gss_name_t *,           /* name */
         OM_uint32 *,            /* initiator_lifetime */
         OM_uint32 *,            /* acceptor_lifetime */
@@ -608,7 +621,7 @@
 /* New for V2 */
 GSS_DLLIMP OM_uint32 gss_import_sec_context(
         OM_uint32 *,            /* minor_status */
-        gss_buffer_t,           /* interprocess_token */
+        const gss_buffer_t,     /* interprocess_token */
         gss_ctx_id_t *          /* context_handle */
 );
 
@@ -627,22 +640,22 @@
 /* New for V2 */
 GSS_DLLIMP OM_uint32 gss_add_oid_set_member(
         OM_uint32 *,            /* minor_status */
-        gss_OID,                /* member_oid */
+        const gss_OID,          /* member_oid */
         gss_OID_set *           /* oid_set */
 );
 
 /* New for V2 */
 GSS_DLLIMP OM_uint32 gss_test_oid_set_member(
         OM_uint32 *,            /* minor_status */
-        gss_OID,                /* member */
-        gss_OID_set,            /* set */
+        const gss_OID,          /* member */
+        const gss_OID_set,      /* set */
         int *                   /* present */
 );
 
 /* New for V2 */
 GSS_DLLIMP OM_uint32 gss_str_to_oid(
         OM_uint32 *,            /* minor_status */
-        gss_buffer_t,           /* oid_str */
+        const gss_buffer_t,     /* oid_str */
         gss_OID *               /* oid */
 );
 
@@ -656,28 +669,28 @@
 /* New for V2 */
 GSS_DLLIMP OM_uint32 gss_inquire_names_for_mech(
         OM_uint32 *,            /* minor_status */
-        gss_OID,                /* mechanism */
+        const gss_OID,          /* mechanism */
         gss_OID_set *           /* name_types */
 );
 
 /* New for V2 */
 GSS_DLLIMP OM_uint32 gss_export_name(
         OM_uint32  *,           /* minor_status */
-        const gss_name_t,       /* input_name */
+        gss_const_name_t,       /* input_name */
         gss_buffer_t            /* exported_name */
 );
 
 /* New for V2 */
 GSS_DLLIMP OM_uint32 gss_duplicate_name(
         OM_uint32  *,           /* minor_status */
-        const gss_name_t,       /* input_name */
+        gss_const_name_t,       /* input_name */
         gss_name_t *            /* dest_name */
 );
 
 /* New for V2 */
 GSS_DLLIMP OM_uint32 gss_canonicalize_name(
         OM_uint32  *,           /* minor_status */
-        const gss_name_t,       /* input_name */
+        gss_const_name_t,       /* input_name */
         const gss_OID,          /* mech_type */
         gss_name_t *            /* output_name */
 );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.security.jgss/windows/native/libsspi_bridge/sspi.cpp	Thu Jun 13 10:06:07 2019 +0800
@@ -0,0 +1,1575 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// This library is client-side only, and only supports the default credentials.
+// It speaks krb5 and SPNEGO. NTLM is excluded from SPNEGO negotiation.
+//
+// This library can be built directly with the following command:
+//   cl -I %OPENJDK%\src\java.security.jgss\share\native\libj2gss\ sspi.cpp \
+//      -link -dll -out:sspi_bridge.dll
+
+#define UNICODE
+#define _UNICODE
+
+#include <windows.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <Strsafe.h>
+#include <ntsecapi.h>
+#include <new>
+
+#define GSS_DLL_FILE
+#include <gssapi.h>
+
+#define SECURITY_WIN32
+#include <sspi.h>
+
+#pragma comment(lib, "secur32.lib")
+
+// Otherwise an exception will be thrown
+#define new new (std::nothrow)
+
+// A debugging macro
+#define PP(fmt, ...) \
+        if (trace) { \
+            fprintf(stderr, "[SSPI:%ld] "fmt"\n", __LINE__, ##__VA_ARGS__); \
+            fflush(stderr); \
+        }
+#define SEC_SUCCESS(status) ((*minor_status = (status)), (status) >= SEC_E_OK)
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+// When SSPI_BRIDGE_TRACE is set, debug info goes to stderr. The value is ignored.
+char* trace = getenv("SSPI_BRIDGE_TRACE");
+
+void
+dump(const char* title, PBYTE data, size_t len)
+{
+    if (trace) {
+        fprintf(stderr, "==== %s ====\n", title);
+        for (size_t i = 0; i < len; i++) {
+            if (i != 0 && i % 16 == 0) {
+                fprintf(stderr, "\n");
+            }
+            fprintf(stderr, "%02X ", *(data + i) & 0xff);
+        }
+        fprintf(stderr, "\n");
+    }
+}
+
+gss_OID_desc KRB5_OID = {9, (void*)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
+gss_OID_desc SPNEGO_OID = {6, (void*)"\x2b\x06\x01\x05\x05\x02"};
+gss_OID_desc USER_NAME_OID = {10, (void*)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01"};
+gss_OID_desc KRB5_NAME_OID = {10, (void*)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01"};
+gss_OID_desc HOST_SERVICE_NAME_OID = {10, (void*)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"};
+gss_OID_desc EXPORT_NAME_OID = {6, (void*)"\x2b\x06\x01\x05\x06\x04"};
+
+struct gss_name_struct {
+    SEC_WCHAR* name;
+};
+
+struct gss_ctx_id_struct {
+    CredHandle* phCred;
+    CtxtHandle hCtxt;
+    SecPkgContext_Sizes SecPkgContextSizes;
+    SecPkgContext_NativeNames nnames;
+    BOOLEAN established;
+    BOOLEAN isSPNEGO;
+    BOOLEAN isLocalCred;
+    OM_uint32 flags;
+};
+
+struct gss_cred_id_struct {
+    CredHandle* phCredK;
+    CredHandle* phCredS;
+    long time;
+};
+
+/* This section holds supporting functions that are not exported */
+
+static OM_uint32
+seconds_until(int inputIsUTC, TimeStamp *time)
+{
+    // time is local time
+    LARGE_INTEGER uiLocal;
+    FILETIME now;
+    GetSystemTimeAsFileTime(&now);
+    if (!inputIsUTC) {
+        FILETIME nowLocal;
+        if (FileTimeToLocalFileTime(&now, &nowLocal) == 0) {
+            return -1;
+        }
+        now = nowLocal;
+    }
+    uiLocal.HighPart = now.dwHighDateTime;
+    uiLocal.LowPart = now.dwLowDateTime;
+    if (time->QuadPart < uiLocal.QuadPart) {
+        return 0;
+    }
+    ULONGLONG diff = (time->QuadPart - uiLocal.QuadPart) / 10000000;
+    if (diff > (ULONGLONG)~(OM_uint32)0)
+        return GSS_C_INDEFINITE;
+    return (OM_uint32)diff;
+}
+
+static void
+show_time(char* label, TimeStamp* ts)
+{
+    if (trace) {
+        SYSTEMTIME stLocal;
+        FileTimeToSystemTime((FILETIME*)ts, &stLocal);
+
+        // Build a string showing the date and time.
+        PP("%s: %02d/%02d/%d  %02d:%02d %uld", label,
+            stLocal.wMonth, stLocal.wDay, stLocal.wYear,
+            stLocal.wHour, stLocal.wMinute,
+            seconds_until(1, ts));
+    }
+}
+
+// isSPNEGO: true, SPNEGO. false, Kerberos.
+static gss_ctx_id_t
+new_context(BOOLEAN isSPNEGO)
+{
+    gss_ctx_id_t out = new gss_ctx_id_struct;
+    if (out == NULL) {
+        return NULL;
+    }
+    out->phCred = NULL;
+    out->hCtxt.dwLower = out->hCtxt.dwUpper = NULL;
+    out->established = FALSE;
+    out->SecPkgContextSizes.cbMaxSignature
+            = out->SecPkgContextSizes.cbBlockSize
+            = out->SecPkgContextSizes.cbSecurityTrailer
+            = 0;
+    out->nnames.sClientName = out->nnames.sServerName = NULL;
+    out->isSPNEGO = isSPNEGO;
+    out->isLocalCred = FALSE;
+    return out;
+}
+
+static gss_cred_id_t
+new_cred()
+{
+    gss_cred_id_t out = new gss_cred_id_struct;
+    out->phCredK = out->phCredS = NULL;
+    out->time = 0L;
+    return out;
+}
+
+static int
+flag_sspi_to_gss(int fin)
+{
+    int fout = 0;
+    if (fin & ISC_REQ_MUTUAL_AUTH) fout |= GSS_C_MUTUAL_FLAG;
+    if (fin & ISC_REQ_CONFIDENTIALITY) fout |= GSS_C_CONF_FLAG;
+    if (fin & ISC_REQ_DELEGATE) fout |= GSS_C_DELEG_FLAG;
+    if (fin & ISC_REQ_INTEGRITY) fout |= GSS_C_INTEG_FLAG;
+    if (fin & ISC_REQ_REPLAY_DETECT) fout |= GSS_C_REPLAY_FLAG;
+    if (fin & ISC_REQ_SEQUENCE_DETECT) fout |= GSS_C_SEQUENCE_FLAG;
+    return fout;
+}
+
+static int
+flag_gss_to_sspi(int fin)
+{
+    int fout = 0;
+    if (fin & GSS_C_MUTUAL_FLAG) fout |= ISC_RET_MUTUAL_AUTH;
+    if (fin & GSS_C_CONF_FLAG) fout |= ISC_RET_CONFIDENTIALITY;
+    if (fin & GSS_C_DELEG_FLAG) fout |= ISC_RET_DELEGATE;
+    if (fin & GSS_C_INTEG_FLAG) fout |= ISC_RET_INTEGRITY;
+    if (fin & GSS_C_REPLAY_FLAG) fout |= ISC_RET_REPLAY_DETECT;
+    if (fin & GSS_C_SEQUENCE_FLAG) fout |= ISC_RET_SEQUENCE_DETECT;
+    return fout;
+}
+
+static BOOLEAN
+is_same_oid(gss_OID o2, gss_OID o1)
+{
+    return o1 && o2 && o1->length == o2->length
+            && !memcmp(o1->elements, o2->elements, o2->length);
+}
+
+static BOOLEAN
+has_oid(gss_OID_set set, gss_OID oid)
+{
+    for (int i = 0; i < set->count; i++) {
+        if (is_same_oid(&set->elements[i], oid)) {
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+static void
+get_oid_desc(gss_OID mech)
+{
+    if (trace) {
+        if (is_same_oid(mech, &KRB5_OID)) {
+            PP("Kerberos mech");
+        } else if (is_same_oid(mech, &SPNEGO_OID)) {
+            PP("SPNEGO mech");
+        } else if (is_same_oid(mech, &USER_NAME_OID)) {
+            PP("NT_USER_NAME name-type");
+        } else if (is_same_oid(mech, &KRB5_NAME_OID)) {
+            PP("KRB5_NAME name-type");
+        } else if (is_same_oid(mech, &HOST_SERVICE_NAME_OID)) {
+            PP("NT_HOSTBASED_SERVICE name-type");
+        } else if (is_same_oid(mech, &EXPORT_NAME_OID)) {
+            PP("NT_EXPORT_NAME name-type");
+        } else {
+            dump("UNKNOWN OID", (PBYTE)mech->elements, mech->length);
+        }
+    }
+}
+
+static void
+get_oid_set_desc(gss_OID_set mechs)
+{
+    if (trace) {
+        if (mechs == NULL) {
+            PP("OID set is NULL");
+            return;
+        }
+        PP("gss_OID_set.count is %d", (int)mechs->count);
+        for (int i = 0; i < mechs->count; i++) {
+            get_oid_desc(&mechs->elements[i]);
+        }
+    }
+}
+
+// Add realm to a name if there was none.
+// Returns a newly allocated name.
+static WCHAR*
+get_full_name(WCHAR* input)
+{
+    // input has realm, no need to add one
+    for (int i = 0;; i++) {
+        if (!input[i]) { // the end
+            break;
+        }
+        if (input[i] == L'\\') { // escaped
+            i++;
+            continue;
+        }
+        if (input[i] == L'@') {
+            return _wcsdup(input);
+        }
+    }
+
+    // Always use the default domain
+    WCHAR* realm = _wgetenv(L"USERDNSDOMAIN");
+    if (realm == NULL) {
+        realm = L"";
+    }
+
+    size_t oldlen = wcslen(input);
+    size_t newlen = oldlen + 1 + wcslen(realm) + 1;
+
+    WCHAR* fullname = new WCHAR[newlen];
+    if (!fullname) {
+        return NULL;
+    }
+    wcscpy_s(fullname, newlen, input);
+    wcscat_s(fullname, newlen, L"@");
+    wcscat_s(fullname, newlen, realm);
+
+    PP("get_full_name returns %ls", fullname);
+    return fullname;
+}
+
+/* End support section */
+
+/* This section holds GSS-API exported functions */
+
+#define CHECK_OUTPUT(x)  if (!x) return GSS_S_CALL_INACCESSIBLE_WRITE;
+#define CHECK_BUFFER(b)  if (!b || !b->value) return GSS_S_CALL_INACCESSIBLE_READ;
+#define CHECK_OID(o)     if (!o || !o->elements) return GSS_S_CALL_INACCESSIBLE_READ;
+#define CHECK_NAME(n)    if (!n || !(n->name)) return GSS_S_BAD_NAME;
+#define CHECK_CONTEXT(c) if (!c) return GSS_S_NO_CONTEXT;
+#define CHECK_CRED(c)    if (!c || (!(cred_handle->phCredK) && !(cred_handle->phCredS))) \
+                                return GSS_S_NO_CRED;
+
+__declspec(dllexport) OM_uint32
+gss_release_name(OM_uint32 *minor_status,
+                 gss_name_t *name)
+{
+    PP(">>>> Calling gss_release_name %p...", *name);
+    if (name != NULL && *name != GSS_C_NO_NAME) {
+        if ((*name)->name != NULL) {
+            delete[] (*name)->name;
+        }
+        delete *name;
+        *name = GSS_C_NO_NAME;
+    }
+    return GSS_S_COMPLETE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_import_name(OM_uint32 *minor_status,
+                const gss_buffer_t input_name_buffer,
+                const gss_OID input_name_type,
+                gss_name_t *output_name)
+{
+    PP(">>>> Calling gss_import_name...");
+    CHECK_BUFFER(input_name_buffer)
+    CHECK_OUTPUT(output_name)
+
+    int len = (int)input_name_buffer->length;
+    LPSTR input = (LPSTR)input_name_buffer->value;
+    if (input_name_type != NULL
+            && is_same_oid(input_name_type, &EXPORT_NAME_OID)) {
+        int mechLen = (int)input[3]; /* including 06 len */
+        len -= mechLen + 8; /* 4 header bytes, and an int32 length after OID */
+        if (len <= 0) {
+            return GSS_S_FAILURE;
+        }
+        // Reject if mech is not krb5
+        if (mechLen - 2!= KRB5_OID.length ||
+                memcmp(input + 6, KRB5_OID.elements, mechLen - 2)) {
+            return GSS_S_FAILURE;;
+        }
+        input = input + mechLen + 8;
+    }
+
+    SEC_WCHAR* value = new SEC_WCHAR[len + 1];
+    if (value == NULL) {
+        goto err;
+    }
+
+    len = MultiByteToWideChar(CP_UTF8, 0, input, len, value, len+1);
+    if (len == 0) {
+        goto err;
+    }
+    value[len] = 0;
+
+    PP("import_name from %ls", value);
+
+    if (len > 33 && !wcscmp(value+len-33, L"@WELLKNOWN:ORG.H5L.REFERALS-REALM")) {
+        // Remove the wellknown referrals realms
+        value[len-33] = 0;
+        len -= 33;
+    } else if (value[len-1] == L'@') {
+        // Remove the empty realm. It might come from an NT_EXPORT_NAME.
+        value[len-1] = 0;
+        len--;
+    }
+    if (len == 0) {
+        goto err;
+    }
+
+    if (input_name_type != NULL
+            && is_same_oid(input_name_type, &HOST_SERVICE_NAME_OID)) {
+        // HOST_SERVICE_NAME_OID takes the form of service@host.
+        for (int i = 0; i < len; i++) {
+            if (value[i] == L'\\') {
+                i++;
+                continue;
+            }
+            if (value[i] == L'@') {
+                value[i] = L'/';
+                break;
+            }
+        }
+        PP("Host-based service now %ls", value);
+    }
+    PP("import_name to %ls", value);
+    gss_name_struct* name = new gss_name_struct;
+    if (name == NULL) {
+        goto err;
+    }
+    name->name = value;
+    *output_name = (gss_name_t) name;
+    return GSS_S_COMPLETE;
+err:
+    if (value != NULL) {
+        delete[] value;
+    }
+    return GSS_S_FAILURE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_compare_name(OM_uint32 *minor_status,
+                 gss_const_name_t name1,
+                 gss_const_name_t name2,
+                 int *name_equal)
+{
+    PP(">>>> Calling gss_compare_name...");
+    CHECK_NAME(name1)
+    CHECK_NAME(name2)
+    CHECK_OUTPUT(name_equal)
+
+    *name_equal = 0;
+
+    SEC_WCHAR* n1 = name1->name;
+    SEC_WCHAR* n2 = name2->name;
+    PP("Comparing %ls and %ls", n1, n2);
+    int l1 = lstrlen(n1);
+    int l2 = lstrlen(n2);
+    if (l1 < l2 && n2[l1] != L'@'
+            || l2 < l1 && n1[l2] != L'@') {
+        return GSS_S_COMPLETE; // different
+    }
+    if (l1 > l2) {
+        l1 = l2; // choose the smaller one. longer=smaller @ ...
+    }
+    if (CompareStringEx(LOCALE_NAME_SYSTEM_DEFAULT, NORM_IGNORECASE,
+            n1, l1, n2, l1, NULL, NULL, 0) == CSTR_EQUAL) {
+        *name_equal = 1;
+    }
+    return GSS_S_COMPLETE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_canonicalize_name(OM_uint32 *minor_status,
+                      gss_const_name_t input_name,
+                      const gss_OID mech_type,
+                      gss_name_t *output_name)
+{
+    PP(">>>> Calling gss_canonicalize_name...");
+    CHECK_NAME(input_name)
+    CHECK_OID(mech_type)
+    CHECK_OUTPUT(output_name)
+
+    gss_name_t names2 = new gss_name_struct;
+    if (names2 == NULL) {
+        return GSS_S_FAILURE;
+    }
+    names2->name = get_full_name(input_name->name);
+    if (names2->name == NULL) {
+        delete names2;
+        return GSS_S_FAILURE;
+    }
+    *output_name = names2;
+    return GSS_S_COMPLETE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_export_name(OM_uint32 *minor_status,
+                gss_const_name_t input_name,
+                gss_buffer_t exported_name)
+{
+    PP(">>>> Calling gss_export_name...");
+    CHECK_NAME(input_name)
+    CHECK_OUTPUT(exported_name)
+
+    OM_uint32 result = GSS_S_FAILURE;
+    SEC_WCHAR* name = input_name->name;
+    SEC_WCHAR* fullname = get_full_name(name);
+    if (!fullname) {
+        goto err;
+    }
+    PP("Make fullname: %ls -> %ls", name, fullname);
+    int len;
+    size_t namelen = wcslen(fullname);
+    if (namelen > 255) {
+        goto err;
+    }
+    len = (int)namelen;
+    // We only deal with not-so-long names.
+    // 04 01 00 ** 06 ** OID len:int32 name
+    int mechLen = KRB5_OID.length;
+    char* buffer = new char[10 + mechLen + len];
+    if (buffer == NULL) {
+        goto err;
+    }
+    buffer[0] = 4;
+    buffer[1] = 1;
+    buffer[2] = 0;
+    buffer[3] = 2 + mechLen;
+    buffer[4] = 6;
+    buffer[5] = mechLen;
+    memcpy_s(buffer + 6, mechLen, KRB5_OID.elements, mechLen);
+    buffer[6 + mechLen] = buffer[7 + mechLen] = buffer[8 + mechLen] = 0;
+    buffer[9 + mechLen] = (char)len;
+    len = WideCharToMultiByte(CP_UTF8, 0, fullname, len,
+                buffer+10+mechLen, len, NULL, NULL);
+    if (len == 0) {
+        delete[] buffer;
+        goto err;
+    }
+    exported_name->length = 10 + mechLen + len;
+    exported_name->value = buffer;
+    result = GSS_S_COMPLETE;
+err:
+    if (fullname != name) {
+        delete[] fullname;
+    }
+    return result;
+}
+
+__declspec(dllexport) OM_uint32
+gss_display_name(OM_uint32 *minor_status,
+                 gss_const_name_t input_name,
+                 gss_buffer_t output_name_buffer,
+                 gss_OID *output_name_type)
+{
+    PP(">>>> Calling gss_display_name...");
+    CHECK_NAME(input_name)
+    CHECK_OUTPUT(output_name_buffer)
+
+    SEC_WCHAR* names = input_name->name;
+    int len = (int)wcslen(names);
+    char* buffer = new char[4*len+1];
+    if (buffer == NULL) {
+        return GSS_S_FAILURE;
+    }
+    len = WideCharToMultiByte(CP_UTF8, 0, names, len, buffer, 4*len, NULL, NULL);
+    if (len == 0) {
+        delete[] buffer;
+        return GSS_S_FAILURE;
+    }
+    buffer[len] = 0;
+    output_name_buffer->length = len;
+    output_name_buffer->value = buffer;
+    PP("Name found: %ls -> %d [%s]", names, len, buffer);
+    if (output_name_type != NULL) {
+        *output_name_type = &KRB5_NAME_OID;
+    }
+    return GSS_S_COMPLETE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_acquire_cred(OM_uint32 *minor_status,
+                 gss_const_name_t desired_name,
+                 OM_uint32 time_req,
+                 const gss_OID_set desired_mechs,
+                 gss_cred_usage_t cred_usage,
+                 gss_cred_id_t *output_cred_handle,
+                 gss_OID_set *actual_mechs,
+                 OM_uint32 *time_rec)
+{
+    PP(">>>> Calling gss_acquire_cred...");
+    CHECK_OUTPUT(output_cred_handle)
+
+    SECURITY_STATUS ss;
+    TimeStamp ts;
+    ts.QuadPart = 0;
+    cred_usage = 0;
+    PP("AcquireCredentialsHandle with %d %p", cred_usage, desired_mechs);
+    get_oid_set_desc(desired_mechs);
+
+    BOOLEAN reqKerberos, reqSPNEGO;
+
+    if (!desired_mechs) {
+        reqKerberos = reqSPNEGO = TRUE;
+    } else {
+        if (has_oid(desired_mechs, &KRB5_OID)) {
+            PP("reqKerberos");
+            reqKerberos = TRUE;
+        }
+        if (has_oid(desired_mechs, &SPNEGO_OID)) {
+            PP("reqSPNEGO");
+            reqSPNEGO = TRUE;
+        }
+        if (!reqSPNEGO && !reqKerberos) {
+            return GSS_S_BAD_MECH;
+        }
+    }
+
+    if (actual_mechs) {
+        *actual_mechs = GSS_C_NO_OID_SET;
+    }
+
+    gss_cred_id_t cred = new_cred();
+    if (cred == NULL) {
+        goto err;
+    }
+
+    if (reqKerberos) {
+        cred->phCredK = new CredHandle;
+        if (cred->phCredK == NULL) {
+            goto err;
+        }
+        ss = AcquireCredentialsHandle(
+                NULL,
+                L"Kerberos",
+                cred_usage == 0 ? SECPKG_CRED_BOTH :
+                    (cred_usage == 1 ? SECPKG_CRED_OUTBOUND : SECPKG_CRED_INBOUND),
+                NULL,
+                NULL,
+                NULL,
+                NULL,
+                cred->phCredK,
+                &ts);
+        if (!(SEC_SUCCESS(ss))) {
+            delete cred->phCredK;
+            cred->phCredK = NULL;
+            goto err;
+        }
+    }
+
+    if (reqSPNEGO) {
+        cred->phCredS = new CredHandle;
+        if (cred->phCredS == NULL) {
+            goto err;
+        }
+        SEC_WINNT_AUTH_IDENTITY_EX auth;
+        ZeroMemory(&auth, sizeof(auth));
+        auth.Version = SEC_WINNT_AUTH_IDENTITY_VERSION;
+        auth.Length = sizeof(auth);
+        auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+        auth.PackageList = (unsigned short*)L"Kerberos";
+        auth.PackageListLength = 8;
+        ss = AcquireCredentialsHandle(
+                NULL,
+                L"Negotiate",
+                cred_usage == 0 ? SECPKG_CRED_BOTH :
+                    (cred_usage == 1 ? SECPKG_CRED_OUTBOUND : SECPKG_CRED_INBOUND),
+                NULL,
+                &auth,
+                NULL,
+                NULL,
+                cred->phCredS,
+                &ts);
+        if (!(SEC_SUCCESS(ss))) {
+            delete cred->phCredS;
+            cred->phCredS = NULL;
+            goto err;
+        }
+    }
+
+    if (actual_mechs) {
+        if (gss_create_empty_oid_set(minor_status, actual_mechs)) {
+            goto err;
+        }
+        if (reqKerberos) {
+            if (gss_add_oid_set_member(minor_status, &KRB5_OID, actual_mechs)) {
+                goto err;
+            }
+        }
+        if (reqSPNEGO) {
+            if (gss_add_oid_set_member(minor_status, &SPNEGO_OID, actual_mechs)) {
+                goto err;
+            }
+        }
+    }
+
+    *output_cred_handle = (gss_cred_id_t)cred;
+
+    // Note: ts here is weirdly huge, maybe because LSA retains the
+    // password and can re-acquire a TGT at anytime. It will be
+    // GSSCredential.INDEFINITE_LIFETIME.
+    show_time("cred expiration", &ts);
+    cred->time = seconds_until(1, &ts);
+    if (time_rec != NULL) {
+        *time_rec = cred->time;
+    }
+
+    // Since only default cred is supported, if there is a desired_name,
+    // we must make sure it is the same as the realname of the default cred.
+    if (desired_name != NULL) {
+        PP("Acquiring cred with a name. Check if it's me.");
+        gss_name_t realname;
+        if (gss_inquire_cred(minor_status, *output_cred_handle, &realname,
+                NULL, NULL, NULL) != GSS_S_COMPLETE) {
+            PP("Cannot get owner name of default creds");
+            goto err;
+        }
+        SEC_WCHAR* rnames = realname->name;
+        SEC_WCHAR* dnames = desired_name->name;
+        int equals = 0;
+        gss_compare_name(minor_status, realname, desired_name, &equals);
+        gss_release_name(minor_status, &realname);
+        PP("Comparing result: %d", equals);
+        if (!equals) {
+            goto err;
+        }
+    }
+
+    return GSS_S_COMPLETE;
+err:
+    if (cred) {
+        OM_uint32 dummy;
+        gss_release_cred(&dummy, &cred);
+    }
+    if (actual_mechs) {
+        OM_uint32 dummy;
+        gss_release_oid_set(&dummy, actual_mechs);
+    }
+    return GSS_S_FAILURE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_release_cred(OM_uint32 *minor_status,
+                 gss_cred_id_t *cred_handle)
+{
+    PP(">>>> Calling gss_release_cred...");
+    if (cred_handle && *cred_handle) {
+        if ((*cred_handle)->phCredK) {
+            FreeCredentialsHandle((*cred_handle)->phCredK);
+            delete (*cred_handle)->phCredK;
+        }
+        if ((*cred_handle)->phCredS) {
+            FreeCredentialsHandle((*cred_handle)->phCredS);
+            delete (*cred_handle)->phCredS;
+        }
+        delete *cred_handle;
+        *cred_handle = GSS_C_NO_CREDENTIAL;
+    }
+    return GSS_S_COMPLETE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_inquire_cred(OM_uint32 *minor_status,
+                 gss_const_cred_id_t cred_handle,
+                 gss_name_t *name,
+                 OM_uint32 *lifetime,
+                 gss_cred_usage_t *cred_usage,
+                 gss_OID_set *mechanisms)
+{
+    PP(">>>> Calling gss_inquire_cred...");
+    CHECK_CRED(cred_handle)
+
+    CredHandle* cred = cred_handle->phCredK
+            ? cred_handle->phCredK
+            : cred_handle->phCredS;
+    SECURITY_STATUS ss;
+    if (name) {
+        *name = GSS_C_NO_NAME;
+        SecPkgCredentials_Names snames;
+        ss = QueryCredentialsAttributes(cred, SECPKG_CRED_ATTR_NAMES, &snames);
+        if (!SEC_SUCCESS(ss)) {
+            return GSS_S_FAILURE;
+        }
+        SEC_WCHAR* names = new SEC_WCHAR[lstrlen(snames.sUserName) + 1];
+        if (names == NULL) {
+            return GSS_S_FAILURE;
+        }
+        StringCchCopy(names, lstrlen(snames.sUserName) + 1, snames.sUserName);
+        FreeContextBuffer(snames.sUserName);
+        PP("Allocate new name at %p", names);
+        gss_name_t name1 = new gss_name_struct;
+        if (name1 == NULL) {
+            delete[] names;
+            return GSS_S_FAILURE;
+        }
+        name1->name = names;
+        *name = (gss_name_t) name1;
+    }
+    if (lifetime) {
+        *lifetime = cred_handle->time;
+    }
+    if (cred_usage) {
+        *cred_usage = 1; // We only support INITIATE_ONLY now
+    }
+    if (mechanisms) {
+        // Useless for Java
+    }
+    // Others inquiries not supported yet
+    return GSS_S_COMPLETE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_import_sec_context(OM_uint32 *minor_status,
+                       const gss_buffer_t interprocess_token,
+                       gss_ctx_id_t *context_handle)
+{
+    // Not transferable, return FAILURE
+    PP(">>>> Calling UNIMPLEMENTED gss_import_sec_context...");
+    *minor_status = 0;
+    return GSS_S_FAILURE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_init_sec_context(OM_uint32 *minor_status,
+                     gss_const_cred_id_t initiator_cred_handle,
+                     gss_ctx_id_t *context_handle,
+                     gss_const_name_t target_name,
+                     const gss_OID mech_type,
+                     OM_uint32 req_flags,
+                     OM_uint32 time_req,
+                     const gss_channel_bindings_t input_chan_bindings,
+                     const gss_buffer_t input_token,
+                     gss_OID *actual_mech_type,
+                     gss_buffer_t output_token,
+                     OM_uint32 *ret_flags,
+                     OM_uint32 *time_rec)
+{
+    PP(">>>> Calling gss_init_sec_context...");
+    CHECK_NAME(target_name)
+    CHECK_OUTPUT(output_token)
+
+    SECURITY_STATUS ss;
+    TimeStamp lifeTime;
+    SecBufferDesc inBuffDesc;
+    SecBuffer inSecBuff;
+    SecBufferDesc outBuffDesc;
+    SecBuffer outSecBuff;
+    BOOLEAN isSPNEGO = is_same_oid(mech_type, &SPNEGO_OID);
+
+    gss_ctx_id_t pc;
+
+    output_token->length = 0;
+    output_token->value = NULL;
+
+    BOOLEAN firstTime = (*context_handle == GSS_C_NO_CONTEXT);
+    PP("First time? %d", firstTime);
+    if (firstTime) {
+        pc = new_context(isSPNEGO);
+        if (pc == NULL) {
+            return GSS_S_FAILURE;
+        }
+        *context_handle = (gss_ctx_id_t) pc;
+    } else {
+        pc = *context_handle;
+    }
+
+    if (pc == NULL) {
+        return GSS_S_NO_CONTEXT;
+    }
+
+    DWORD outFlag;
+    TCHAR outName[100];
+
+    OM_uint32 minor;
+    gss_buffer_desc tn;
+    gss_display_name(&minor, target_name, &tn, NULL);
+    int len = MultiByteToWideChar(CP_UTF8, 0, (LPCCH)tn.value, (int)tn.length,
+            outName, sizeof(outName) - 1);
+    if (len == 0) {
+        goto err;
+    }
+    outName[len] = 0;
+
+    int flag = flag_gss_to_sspi(req_flags) | ISC_REQ_ALLOCATE_MEMORY;
+
+    outBuffDesc.ulVersion = SECBUFFER_VERSION;
+    outBuffDesc.cBuffers = 1;
+    outBuffDesc.pBuffers = &outSecBuff;
+
+    outSecBuff.BufferType = SECBUFFER_TOKEN;
+
+    if (!firstTime) {
+        inBuffDesc.ulVersion = SECBUFFER_VERSION;
+        inBuffDesc.cBuffers = 1;
+        inBuffDesc.pBuffers = &inSecBuff;
+
+        inSecBuff.BufferType = SECBUFFER_TOKEN;
+        inSecBuff.cbBuffer = (ULONG)input_token->length;
+        inSecBuff.pvBuffer = input_token->value;
+    } else if (!pc->phCred) {
+        if (isSPNEGO && initiator_cred_handle
+                && initiator_cred_handle->phCredS) {
+            PP("Find SPNEGO credentials");
+            pc->phCred = initiator_cred_handle->phCredS;
+            pc->isLocalCred = FALSE;
+        } else if (!isSPNEGO && initiator_cred_handle
+                && initiator_cred_handle->phCredK) {
+            PP("Find Kerberos credentials");
+            pc->phCred = initiator_cred_handle->phCredK;
+            pc->isLocalCred = FALSE;
+        } else {
+            PP("No credentials provided, acquire myself");
+            CredHandle* newCred = new CredHandle;
+            SEC_WINNT_AUTH_IDENTITY_EX auth;
+            ZeroMemory(&auth, sizeof(auth));
+            auth.Version = SEC_WINNT_AUTH_IDENTITY_VERSION;
+            auth.Length = sizeof(auth);
+            auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+            auth.PackageList = (unsigned short*)L"Kerberos";
+            auth.PackageListLength = 8;
+            ss = AcquireCredentialsHandle(
+                    NULL,
+                    isSPNEGO ? L"Negotiate" : L"Kerberos",
+                    SECPKG_CRED_OUTBOUND,
+                    NULL,
+                    isSPNEGO ? &auth : NULL,
+                    NULL,
+                    NULL,
+                    newCred,
+                    &lifeTime);
+            if (!(SEC_SUCCESS(ss))) {
+                delete newCred;
+                goto err;
+            }
+            pc->phCred = newCred;
+            pc->isLocalCred = TRUE;
+        }
+    }
+    ss = InitializeSecurityContext(
+            pc->phCred,
+            firstTime ? NULL : &pc->hCtxt,
+            outName,
+            flag,
+            0,
+            SECURITY_NATIVE_DREP,
+            firstTime ? NULL : &inBuffDesc,
+            0,
+            &pc->hCtxt,
+            &outBuffDesc,
+            &outFlag,
+            &lifeTime);
+
+    if (!SEC_SUCCESS(ss)) {
+        // TODO: seems NativeGSSContext has not failed here.
+        PP("InitializeSecurityContext failed");
+        goto err;
+    }
+
+    pc->flags = *ret_flags = flag_sspi_to_gss(outFlag);
+
+    // Ignore the result of the next call. Might fail before context established.
+    QueryContextAttributes(
+            &pc->hCtxt, SECPKG_ATTR_SIZES, &pc->SecPkgContextSizes);
+    PP("cbMaxSignature: %ld. cbBlockSize: %ld. cbSecurityTrailer: %ld",
+            pc->SecPkgContextSizes.cbMaxSignature,
+            pc->SecPkgContextSizes.cbBlockSize,
+            pc->SecPkgContextSizes.cbSecurityTrailer);
+
+    output_token->length = outSecBuff.cbBuffer;
+    if (outSecBuff.cbBuffer) {
+        // No idea how user would free the data. Let's duplicate one.
+        output_token->value = new char[outSecBuff.cbBuffer];
+        if (!output_token->value) {
+            FreeContextBuffer(outSecBuff.pvBuffer);
+            output_token->length = 0;
+            goto err;
+        }
+        memcpy(output_token->value, outSecBuff.pvBuffer, outSecBuff.cbBuffer);
+        FreeContextBuffer(outSecBuff.pvBuffer);
+    }
+
+    if (ss == SEC_I_CONTINUE_NEEDED) {
+        return GSS_S_CONTINUE_NEEDED;
+    } else {
+        pc->established = true;
+        ss = QueryContextAttributes(&pc->hCtxt, SECPKG_ATTR_NATIVE_NAMES, &pc->nnames);
+        if (!SEC_SUCCESS(ss)) {
+            goto err;
+        }
+        PP("Names. %ls %ls", pc->nnames.sClientName, pc->nnames.sServerName);
+        *ret_flags |= GSS_C_PROT_READY_FLAG;
+        return GSS_S_COMPLETE;
+    }
+err:
+    if (firstTime) {
+        OM_uint32 dummy;
+        gss_delete_sec_context(&dummy, context_handle, GSS_C_NO_BUFFER);
+    }
+    if (output_token->value) {
+        gss_release_buffer(NULL, output_token);
+        output_token = GSS_C_NO_BUFFER;
+    }
+    return GSS_S_FAILURE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_accept_sec_context(OM_uint32 *minor_status,
+                       gss_ctx_id_t *context_handle,
+                       gss_const_cred_id_t acceptor_cred_handle,
+                       const gss_buffer_t input_token,
+                       const gss_channel_bindings_t input_chan_bindings,
+                       gss_name_t *src_name,
+                       gss_OID *mech_type,
+                       gss_buffer_t output_token,
+                       OM_uint32 *ret_flags,
+                       OM_uint32 *time_rec,
+                       gss_cred_id_t *delegated_cred_handle)
+{
+    PP(">>>> Calling UNIMPLEMENTED gss_accept_sec_context...");
+    PP("gss_accept_sec_context is not supported in this initiator-only library");
+    return GSS_S_FAILURE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_inquire_context(OM_uint32 *minor_status,
+                    gss_const_ctx_id_t context_handle,
+                    gss_name_t *src_name,
+                    gss_name_t *targ_name,
+                    OM_uint32 *lifetime_rec,
+                    gss_OID *mech_type,
+                    OM_uint32 *ctx_flags,
+                    int *locally_initiated,
+                    int *open)
+{
+    PP(">>>> Calling gss_inquire_context...");
+    CHECK_CONTEXT(context_handle)
+
+    gss_name_t n1 = NULL;
+    gss_name_t n2 = NULL;
+    if (!context_handle->established) {
+        return GSS_S_NO_CONTEXT;
+    }
+    if (src_name != NULL) {
+        n1 = new gss_name_struct;
+        if (n1 == NULL) {
+            goto err;
+        }
+        n1->name = new SEC_WCHAR[lstrlen(context_handle->nnames.sClientName) + 1];
+        if (n1->name == NULL) {
+            goto err;
+        }
+        PP("Allocate new name at %p", n1->name);
+        StringCchCopy(n1->name, lstrlen(context_handle->nnames.sClientName) + 1,
+                context_handle->nnames.sClientName);
+        *src_name = (gss_name_t) n1;
+    }
+    if (targ_name != NULL) {
+        n2 = new gss_name_struct;
+        if (n2 == NULL) {
+            goto err;
+        }
+        n2->name = new SEC_WCHAR[lstrlen(context_handle->nnames.sServerName) + 1];
+        if (n2->name == NULL) {
+            goto err;
+        }
+        PP("Allocate new name at %p", n2->name);
+        StringCchCopy(n2->name, lstrlen(context_handle->nnames.sServerName) + 1,
+                context_handle->nnames.sServerName);
+        *targ_name = (gss_name_t) n2;
+    }
+    if (lifetime_rec != NULL) {
+        SecPkgContext_Lifespan ls;
+        SECURITY_STATUS ss;
+        ss = QueryContextAttributes(
+                (PCtxtHandle)&context_handle->hCtxt,
+                SECPKG_ATTR_LIFESPAN,
+                &ls);
+        if (!SEC_SUCCESS(ss)) {
+            goto err;
+        }
+        *lifetime_rec = seconds_until(0, &ls.tsExpiry);
+    }
+    if (mech_type != NULL) {
+        *mech_type = context_handle->isSPNEGO
+                ? &SPNEGO_OID : &KRB5_OID;
+    }
+    if (ctx_flags != NULL) {
+        *ctx_flags = context_handle->flags;
+    }
+    if (locally_initiated != NULL) {
+        // We are always initiator
+        *locally_initiated = 1;
+    }
+    return GSS_S_COMPLETE;
+err:
+    if (n1 != NULL) {
+        if (n1->name != NULL) {
+            delete[] n1->name;
+        }
+        delete n1;
+        n1 = NULL;
+    }
+    if (n2 != NULL) {
+        if (n2->name != NULL) {
+            delete[] n2->name;
+        }
+        delete n2;
+        n2 = NULL;
+    }
+    return GSS_S_FAILURE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_delete_sec_context(OM_uint32 *minor_status,
+                       gss_ctx_id_t *context_handle,
+                       gss_buffer_t output_token)
+{
+    PP(">>>> Calling gss_delete_sec_context...");
+    CHECK_CONTEXT(context_handle)
+
+    DeleteSecurityContext(&(*context_handle)->hCtxt);
+    if ((*context_handle)->isLocalCred && (*context_handle)->phCred != NULL) {
+        FreeCredentialsHandle((*context_handle)->phCred);
+        (*context_handle)->phCred = NULL;
+    }
+    if ((*context_handle)->nnames.sClientName != NULL) {
+        FreeContextBuffer((*context_handle)->nnames.sClientName);
+        (*context_handle)->nnames.sClientName = NULL;
+    }
+    if ((*context_handle)->nnames.sServerName != NULL) {
+        FreeContextBuffer((*context_handle)->nnames.sServerName);
+        (*context_handle)->nnames.sServerName = NULL;
+    }
+    delete (*context_handle);
+    *context_handle = GSS_C_NO_CONTEXT;
+    return GSS_S_COMPLETE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_context_time(OM_uint32 *minor_status,
+                 gss_const_ctx_id_t context_handle,
+                 OM_uint32 *time_rec)
+{
+    PP(">>>> Calling IMPLEMENTED gss_context_time...");
+    CHECK_CONTEXT(context_handle)
+    CHECK_OUTPUT(time_rec)
+
+    SECURITY_STATUS ss;
+    SecPkgContext_Lifespan ls;
+    ss = QueryContextAttributes(
+            (PCtxtHandle)&context_handle->hCtxt,
+            SECPKG_ATTR_LIFESPAN,
+            &ls);
+    if (ss == SEC_E_OK) {
+        *time_rec = seconds_until(0, &ls.tsExpiry);
+        show_time("context start", &ls.tsStart);
+        show_time("context expiry", &ls.tsExpiry);
+        return *time_rec == 0 ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
+    } else {
+        return GSS_S_FAILURE;
+    }
+}
+
+__declspec(dllexport) OM_uint32
+gss_wrap_size_limit(OM_uint32 *minor_status,
+                    gss_const_ctx_id_t context_handle,
+                    int conf_req_flag,
+                    gss_qop_t qop_req,
+                    OM_uint32 req_output_size,
+                    OM_uint32 *max_input_size)
+{
+    PP(">>>> Calling gss_wrap_size_limit...");
+    CHECK_CONTEXT(context_handle)
+    CHECK_OUTPUT(max_input_size)
+
+    *max_input_size = req_output_size
+            - context_handle->SecPkgContextSizes.cbSecurityTrailer
+            - context_handle->SecPkgContextSizes.cbBlockSize;
+    return GSS_S_COMPLETE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_export_sec_context(OM_uint32 *minor_status,
+                       gss_ctx_id_t *context_handle,
+                       gss_buffer_t interprocess_token)
+{
+    PP(">>>> Calling UNIMPLEMENTED gss_export_sec_context...");
+    return GSS_S_FAILURE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_get_mic(OM_uint32 *minor_status,
+            gss_const_ctx_id_t context_handle,
+            gss_qop_t qop_req,
+            const gss_buffer_t message_buffer,
+            gss_buffer_t msg_token)
+{
+    PP(">>>> Calling gss_get_mic...");
+    CHECK_CONTEXT(context_handle);
+    CHECK_BUFFER(message_buffer);
+    CHECK_OUTPUT(msg_token);
+
+    SECURITY_STATUS ss;
+    SecBufferDesc buffDesc;
+    SecBuffer secBuff[2];
+
+    buffDesc.cBuffers = 2;
+    buffDesc.pBuffers = secBuff;
+    buffDesc.ulVersion = SECBUFFER_VERSION;
+
+    secBuff[0].BufferType = SECBUFFER_DATA;
+    secBuff[0].cbBuffer = (ULONG)message_buffer->length;
+    secBuff[0].pvBuffer = message_buffer->value;
+
+    secBuff[1].BufferType = SECBUFFER_TOKEN;
+    secBuff[1].cbBuffer = context_handle->SecPkgContextSizes.cbMaxSignature;
+    secBuff[1].pvBuffer = msg_token->value = new char[secBuff[1].cbBuffer];
+
+    ss = MakeSignature((PCtxtHandle)&context_handle->hCtxt, 0, &buffDesc, 0);
+
+    if (!SEC_SUCCESS(ss)) {
+        msg_token->length = 0;
+        msg_token->value = NULL;
+        delete[] secBuff[1].pvBuffer;
+        return GSS_S_FAILURE;
+    }
+
+    msg_token->length = secBuff[1].cbBuffer;
+    return GSS_S_COMPLETE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_verify_mic(OM_uint32 *minor_status,
+               gss_const_ctx_id_t context_handle,
+               const gss_buffer_t message_buffer,
+               const gss_buffer_t token_buffer,
+               gss_qop_t *qop_state)
+{
+    PP(">>>> Calling gss_verify_mic...");
+    CHECK_CONTEXT(context_handle);
+    CHECK_BUFFER(message_buffer);
+    CHECK_BUFFER(token_buffer);
+
+    SECURITY_STATUS ss;
+    SecBufferDesc buffDesc;
+    SecBuffer secBuff[2];
+    ULONG qop;
+
+    buffDesc.ulVersion = SECBUFFER_VERSION;
+    buffDesc.cBuffers = 2;
+    buffDesc.pBuffers = secBuff;
+
+    secBuff[0].BufferType = SECBUFFER_TOKEN;
+    secBuff[0].cbBuffer = (ULONG)token_buffer->length;
+    secBuff[0].pvBuffer = token_buffer->value;
+
+    secBuff[1].BufferType = SECBUFFER_DATA;
+    secBuff[1].cbBuffer = (ULONG)message_buffer->length;
+    secBuff[1].pvBuffer = message_buffer->value;
+
+    ss = VerifySignature((PCtxtHandle)&context_handle->hCtxt, &buffDesc, 0, &qop);
+    if (qop_state) {
+        *qop_state = qop;
+    }
+
+    if (ss == SEC_E_OK) {
+        return GSS_S_COMPLETE;
+    } else if (ss == SEC_E_OUT_OF_SEQUENCE) {
+        return GSS_S_UNSEQ_TOKEN;
+    } else {
+        return GSS_S_BAD_SIG;
+    }
+}
+
+__declspec(dllexport) OM_uint32
+gss_wrap(OM_uint32 *minor_status,
+         gss_const_ctx_id_t context_handle,
+         int conf_req_flag,
+         gss_qop_t qop_req,
+         const gss_buffer_t input_message_buffer,
+         int *conf_state,
+         gss_buffer_t output_message_buffer)
+{
+    PP(">>>> Calling gss_wrap...");
+    CHECK_CONTEXT(context_handle);
+    CHECK_BUFFER(input_message_buffer);
+    CHECK_OUTPUT(output_message_buffer);
+
+    SECURITY_STATUS ss;
+    SecBufferDesc buffDesc;
+    SecBuffer secBuff[3];
+
+    buffDesc.ulVersion = SECBUFFER_VERSION;
+    buffDesc.cBuffers = 3;
+    buffDesc.pBuffers = secBuff;
+
+    secBuff[0].BufferType = SECBUFFER_TOKEN;
+    secBuff[0].cbBuffer = context_handle->SecPkgContextSizes.cbSecurityTrailer;
+    output_message_buffer->value = secBuff[0].pvBuffer = malloc(
+            context_handle->SecPkgContextSizes.cbSecurityTrailer
+                    + input_message_buffer->length
+                    + context_handle->SecPkgContextSizes.cbBlockSize);;
+
+    secBuff[1].BufferType = SECBUFFER_DATA;
+    secBuff[1].cbBuffer = (ULONG)input_message_buffer->length;
+    secBuff[1].pvBuffer = malloc(secBuff[1].cbBuffer);
+    memcpy_s(secBuff[1].pvBuffer, secBuff[1].cbBuffer,
+            input_message_buffer->value, input_message_buffer->length);
+
+    secBuff[2].BufferType = SECBUFFER_PADDING;
+    secBuff[2].cbBuffer = context_handle->SecPkgContextSizes.cbBlockSize;
+    secBuff[2].pvBuffer = malloc(secBuff[2].cbBuffer);
+
+    ss = EncryptMessage((PCtxtHandle)&context_handle->hCtxt,
+            conf_req_flag ? 0 : SECQOP_WRAP_NO_ENCRYPT,
+            &buffDesc, 0);
+    if (conf_state) {
+        *conf_state = conf_req_flag;
+    }
+
+    if (!SEC_SUCCESS(ss)) {
+        free(secBuff[0].pvBuffer);
+        free(secBuff[1].pvBuffer);
+        free(secBuff[2].pvBuffer);
+        output_message_buffer->length = 0;
+        output_message_buffer->value = NULL;
+        return GSS_S_FAILURE;
+    }
+
+    memcpy_s((PBYTE)secBuff[0].pvBuffer + secBuff[0].cbBuffer,
+            input_message_buffer->length + context_handle->SecPkgContextSizes.cbBlockSize,
+            secBuff[1].pvBuffer,
+            secBuff[1].cbBuffer);
+    memcpy_s((PBYTE)secBuff[0].pvBuffer + secBuff[0].cbBuffer + secBuff[1].cbBuffer,
+            context_handle->SecPkgContextSizes.cbBlockSize,
+            secBuff[2].pvBuffer,
+            secBuff[2].cbBuffer);
+
+    output_message_buffer->length = secBuff[0].cbBuffer + secBuff[1].cbBuffer
+            + secBuff[2].cbBuffer;
+    free(secBuff[1].pvBuffer);
+    free(secBuff[2].pvBuffer);
+
+    return GSS_S_COMPLETE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_unwrap(OM_uint32 *minor_status,
+           gss_const_ctx_id_t context_handle,
+           const gss_buffer_t input_message_buffer,
+           gss_buffer_t output_message_buffer,
+           int *conf_state,
+           gss_qop_t *qop_state)
+{
+    PP(">>>> Calling gss_unwrap...");
+    CHECK_CONTEXT(context_handle);
+    CHECK_BUFFER(input_message_buffer);
+    CHECK_OUTPUT(output_message_buffer);
+
+    SECURITY_STATUS ss;
+    SecBufferDesc buffDesc;
+    SecBuffer secBuff[2];
+    ULONG ulQop = 0;
+
+    buffDesc.cBuffers = 2;
+    buffDesc.pBuffers = secBuff;
+    buffDesc.ulVersion = SECBUFFER_VERSION;
+
+    secBuff[0].BufferType = SECBUFFER_STREAM;
+    secBuff[0].cbBuffer = (ULONG)input_message_buffer->length;
+    secBuff[0].pvBuffer = malloc(input_message_buffer->length);
+    memcpy_s(secBuff[0].pvBuffer, input_message_buffer->length,
+            input_message_buffer->value, input_message_buffer->length);
+
+    secBuff[1].BufferType = SECBUFFER_DATA;
+    secBuff[1].cbBuffer = 0;
+    secBuff[1].pvBuffer = NULL;
+
+    ss = DecryptMessage((PCtxtHandle)&context_handle->hCtxt, &buffDesc, 0, &ulQop);
+    if (qop_state) {
+        *qop_state = ulQop;
+    }
+    if (!SEC_SUCCESS(ss)) {
+        free(secBuff[0].pvBuffer);
+        output_message_buffer->length = 0;
+        output_message_buffer->value = NULL;
+        return GSS_S_FAILURE;
+    }
+
+    // Must allocate a new memory block so client can release it correctly
+    output_message_buffer->length = secBuff[1].cbBuffer;
+    output_message_buffer->value = new char[secBuff[1].cbBuffer];
+    memcpy_s(output_message_buffer->value, secBuff[1].cbBuffer,
+            secBuff[1].pvBuffer, secBuff[1].cbBuffer);
+    *conf_state = ulQop == SECQOP_WRAP_NO_ENCRYPT ? 0 : 1;
+
+    free(secBuff[0].pvBuffer);
+    return GSS_S_COMPLETE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_indicate_mechs(OM_uint32 *minor_status,
+                   gss_OID_set *mech_set)
+{
+    PP(">>>> Calling gss_indicate_mechs...");
+    OM_uint32 major = GSS_S_COMPLETE;
+
+    ULONG ccPackages;
+    PSecPkgInfo packages;
+    EnumerateSecurityPackages(&ccPackages, &packages);
+    PP("EnumerateSecurityPackages returns %ld", ccPackages);
+    for (unsigned int i = 0; i < ccPackages; i++) {
+        PP("#%d: %ls, %ls\n", i, packages[i].Name, packages[i].Comment);
+    }
+    FreeContextBuffer(packages);
+
+    // Hardcode kerberos and SPNEGO support
+    major = gss_create_empty_oid_set(minor_status, mech_set);
+    if (major != GSS_S_COMPLETE) {
+        goto done;
+    }
+
+    major = gss_add_oid_set_member(minor_status, &KRB5_OID, mech_set);
+    if (major != GSS_S_COMPLETE) {
+        goto done;
+    }
+
+    major = gss_add_oid_set_member(minor_status, &SPNEGO_OID, mech_set);
+    if (major != GSS_S_COMPLETE) {
+        goto done;
+    }
+
+done:
+
+    if (major != GSS_S_COMPLETE) {
+        gss_release_oid_set(minor_status, mech_set);
+    }
+
+    return major;
+}
+
+__declspec(dllexport) OM_uint32
+gss_inquire_names_for_mech(OM_uint32 *minor_status,
+                           const gss_OID mechanism,
+                           gss_OID_set *name_types)
+{
+    PP(">>>> Calling gss_inquire_names_for_mech...");
+    CHECK_OID(mechanism);
+
+    if (gss_create_empty_oid_set(minor_status, name_types)) {
+        return GSS_S_FAILURE;
+    }
+    if (gss_add_oid_set_member(minor_status, &USER_NAME_OID, name_types)) {
+        goto err;
+    }
+    if (gss_add_oid_set_member(minor_status, &HOST_SERVICE_NAME_OID, name_types)) {
+        goto err;
+    }
+    if (!is_same_oid(mechanism, &SPNEGO_OID)) {
+        if (gss_add_oid_set_member(minor_status, &EXPORT_NAME_OID, name_types)) {
+            goto err;
+        }
+    }
+    return GSS_S_COMPLETE;
+err:
+    gss_release_oid_set(minor_status, name_types);
+    return GSS_S_FAILURE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_add_oid_set_member(OM_uint32 *minor_status,
+                       const gss_OID member_oid,
+                       gss_OID_set *oid_set)
+{
+    PP(">>>> Calling gss_add_oid_set_member...");
+    CHECK_OID(member_oid);
+    CHECK_OUTPUT(oid_set);
+
+
+    int count = (int)(*oid_set)->count;
+    for (int i = 0; i < count; i++) {
+        if (is_same_oid(&(*oid_set)->elements[i], member_oid)) {
+            // already there
+            return GSS_S_COMPLETE;
+        }
+    }
+    gss_OID existing = (*oid_set)->elements;
+    gss_OID newcopy = new gss_OID_desc[count + 1];
+    if (newcopy == NULL) {
+        return GSS_S_FAILURE;
+    }
+    if (existing) {
+        memcpy_s(newcopy, (count + 1) * sizeof(gss_OID_desc),
+                existing, count * sizeof(gss_OID_desc));
+    }
+    newcopy[count].length = member_oid->length;
+    newcopy[count].elements = new char[member_oid->length];
+    if (newcopy[count].elements == NULL) {
+        delete[] newcopy;
+        return GSS_S_FAILURE;
+    }
+    memcpy_s(newcopy[count].elements, member_oid->length,
+            member_oid->elements, member_oid->length);
+    (*oid_set)->elements = newcopy;
+    (*oid_set)->count++;
+    if (existing) {
+        delete[] existing;
+    }
+
+    return GSS_S_COMPLETE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_display_status(OM_uint32 *minor_status,
+                   OM_uint32 status_value,
+                   int status_type,
+                   const gss_OID mech_type,
+                   OM_uint32 *message_context,
+                   gss_buffer_t status_string)
+{
+    PP(">>>> Calling gss_display_status...");
+    TCHAR msg[256];
+    int len = FormatMessage(
+            FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+            0, status_value,
+            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+            msg, 256, 0);
+    if (len > 0) {
+        status_string->value = new char[len + 20];
+        status_string->length = sprintf_s(
+                (LPSTR)status_string->value, len + 19,
+                "(%lx) %ls", status_value, msg);
+    } else {
+        status_string->value = new char[33];
+        status_string->length = sprintf_s(
+                (LPSTR)status_string->value, 32,
+                "status is %lx", status_value);
+    }
+    if (status_string->length <= 0) {
+        gss_release_buffer(NULL, status_string);
+        status_string = GSS_C_NO_BUFFER;
+        return GSS_S_FAILURE;
+    } else {
+        return GSS_S_COMPLETE;
+    }
+}
+
+__declspec(dllexport) OM_uint32
+gss_create_empty_oid_set(OM_uint32 *minor_status,
+                         gss_OID_set *oid_set)
+{
+    PP(">>>> Calling gss_create_empty_oid_set...");
+    CHECK_OUTPUT(oid_set);
+
+    if (*oid_set = new gss_OID_set_desc) {
+        memset(*oid_set, 0, sizeof(gss_OID_set_desc));
+        return GSS_S_COMPLETE;
+    }
+    return GSS_S_FAILURE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_release_oid_set(OM_uint32 *minor_status,
+                    gss_OID_set *set)
+{
+    PP(">>>> Calling gss_release_oid_set...");
+    if (set == NULL || *set == GSS_C_NO_OID_SET) {
+        return GSS_S_COMPLETE;
+    }
+    for (int i = 0; i < (*set)->count; i++) {
+        delete[] (*set)->elements[i].elements;
+    }
+    delete[] (*set)->elements;
+    delete *set;
+    *set = GSS_C_NO_OID_SET;
+    return GSS_S_COMPLETE;
+}
+
+__declspec(dllexport) OM_uint32
+gss_release_buffer(OM_uint32 *minor_status,
+                   gss_buffer_t buffer)
+{
+    PP(">>>> Calling gss_release_buffer...");
+    if (buffer == NULL || buffer == GSS_C_NO_BUFFER) {
+        return GSS_S_COMPLETE;
+    }
+    if (buffer->value) {
+        delete[] buffer->value;
+        buffer->value = NULL;
+    }
+    buffer->length = 0;
+    return GSS_S_COMPLETE;
+}
+
+/* End implemented section */
+
+#ifdef __cplusplus
+}
+#endif