Skip to content

Commit

Permalink
Shim gss api on Linux to delay loading libgssapi_krb5.so (#55037)
Browse files Browse the repository at this point in the history
* shim one function for starters

* do all functions

* drop static dependency on libgssapi_krb5.so

* remap all gss method calls

* keep one lib instance open.

* tolerate absence of API in `gss_indicate_mechs`. It may be used for API probing.

* init from a static constructor

* move the lib name definition to the shim

* no indirection

* do not try optimizing multiple initialization attempts
  • Loading branch information
VSadov authored Jul 9, 2021
1 parent 784ddf8 commit 7cfa95b
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,29 @@ internal static partial class NetSecurityNative
{
[DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_IsNtlmInstalled")]
internal static extern bool IsNtlmInstalled();

[DllImport(Interop.Libraries.NetSecurityNative, EntryPoint = "NetSecurityNative_EnsureGssInitialized")]
private static extern int EnsureGssInitialized();

static NetSecurityNative()
{
GssInitializer.Initialize();
}

internal static class GssInitializer
{
static GssInitializer()
{
if (EnsureGssInitialized() != 0)
{
throw new InvalidOperationException();
}
}

internal static void Initialize()
{
// No-op that exists to provide a hook for other static constructors.
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ static const Entry s_securityNative[] =
DllImportEntry(NetSecurityNative_DeleteSecContext)
DllImportEntry(NetSecurityNative_DisplayMajorStatus)
DllImportEntry(NetSecurityNative_DisplayMinorStatus)
DllImportEntry(NetSecurityNative_EnsureGssInitialized)
DllImportEntry(NetSecurityNative_GetUser)
DllImportEntry(NetSecurityNative_ImportPrincipalName)
DllImportEntry(NetSecurityNative_ImportUserName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,11 @@ macro(append_extra_security_libs NativeLibsExtra)
endif()
endif()

list(APPEND ${NativeLibsExtra} ${LIBGSS})
if(CLR_CMAKE_TARGET_LINUX)
# On Linux libgssapi_krb5.so is loaded on demand to tolerate its absense in singlefile apps that do not use it
list(APPEND ${NativeLibsExtra} dl)
add_definitions(-DGSS_SHIM)
else()
list(APPEND ${NativeLibsExtra} ${LIBGSS})
endif()
endmacro()
111 changes: 111 additions & 0 deletions src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
#include <assert.h>
#include <string.h>

#if defined(GSS_SHIM)
#include <dlfcn.h>
#include "pal_atomic.h"
#endif

c_static_assert(PAL_GSS_C_DELEG_FLAG == GSS_C_DELEG_FLAG);
c_static_assert(PAL_GSS_C_MUTUAL_FLAG == GSS_C_MUTUAL_FLAG);
c_static_assert(PAL_GSS_C_REPLAY_FLAG == GSS_C_REPLAY_FLAG);
Expand Down Expand Up @@ -48,6 +53,103 @@ static gss_OID_desc gss_mech_ntlm_OID_desc = {.length = ARRAY_SIZE(gss_ntlm_oid_
.elements = gss_ntlm_oid_value};
#endif

#if defined(GSS_SHIM)

#define FOR_ALL_GSS_FUNCTIONS \
PER_FUNCTION_BLOCK(gss_accept_sec_context) \
PER_FUNCTION_BLOCK(gss_acquire_cred) \
PER_FUNCTION_BLOCK(gss_acquire_cred_with_password) \
PER_FUNCTION_BLOCK(gss_delete_sec_context) \
PER_FUNCTION_BLOCK(gss_display_name) \
PER_FUNCTION_BLOCK(gss_display_status) \
PER_FUNCTION_BLOCK(gss_import_name) \
PER_FUNCTION_BLOCK(gss_indicate_mechs) \
PER_FUNCTION_BLOCK(gss_init_sec_context) \
PER_FUNCTION_BLOCK(gss_inquire_context) \
PER_FUNCTION_BLOCK(gss_mech_krb5) \
PER_FUNCTION_BLOCK(gss_oid_equal) \
PER_FUNCTION_BLOCK(gss_release_buffer) \
PER_FUNCTION_BLOCK(gss_release_cred) \
PER_FUNCTION_BLOCK(gss_release_name) \
PER_FUNCTION_BLOCK(gss_release_oid_set) \
PER_FUNCTION_BLOCK(gss_unwrap) \
PER_FUNCTION_BLOCK(gss_wrap) \
PER_FUNCTION_BLOCK(GSS_C_NT_USER_NAME) \
PER_FUNCTION_BLOCK(GSS_C_NT_HOSTBASED_SERVICE)

#if HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X

#define FOR_ALL_GSS_FUNCTIONS FOR_ALL_GSS_FUNCTIONS \
PER_FUNCTION_BLOCK(gss_set_cred_option)

#endif //HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X

// define indirection pointers for all functions, like
// static TYPEOF(gss_accept_sec_context)* gss_accept_sec_context_ptr;
#define PER_FUNCTION_BLOCK(fn) \
static TYPEOF(fn)* fn##_ptr;

FOR_ALL_GSS_FUNCTIONS
#undef PER_FUNCTION_BLOCK

static void* volatile s_gssLib = NULL;

// remap gss function use to use indirection pointers
#define gss_accept_sec_context(...) gss_accept_sec_context_ptr(__VA_ARGS__)
#define gss_acquire_cred(...) gss_acquire_cred_ptr(__VA_ARGS__)
#define gss_acquire_cred_with_password(...) gss_acquire_cred_with_password_ptr(__VA_ARGS__)
#define gss_delete_sec_context(...) gss_delete_sec_context_ptr(__VA_ARGS__)
#define gss_display_name(...) gss_display_name_ptr(__VA_ARGS__)
#define gss_display_status(...) gss_display_status_ptr(__VA_ARGS__)
#define gss_import_name(...) gss_import_name_ptr(__VA_ARGS__)
#define gss_indicate_mechs(...) gss_indicate_mechs_ptr(__VA_ARGS__)
#define gss_init_sec_context(...) gss_init_sec_context_ptr(__VA_ARGS__)
#define gss_inquire_context(...) gss_inquire_context_ptr(__VA_ARGS__)
#define gss_oid_equal(...) gss_oid_equal_ptr(__VA_ARGS__)
#define gss_release_buffer(...) gss_release_buffer_ptr(__VA_ARGS__)
#define gss_release_cred(...) gss_release_cred_ptr(__VA_ARGS__)
#define gss_release_name(...) gss_release_name_ptr(__VA_ARGS__)
#define gss_release_oid_set(...) gss_release_oid_set_ptr(__VA_ARGS__)
#define gss_unwrap(...) gss_unwrap_ptr(__VA_ARGS__)
#define gss_wrap(...) gss_wrap_ptr(__VA_ARGS__)

#if HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
#define gss_set_cred_option(...) gss_set_cred_option_ptr(__VA_ARGS__)
#endif //HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X


#define GSS_C_NT_USER_NAME (*GSS_C_NT_USER_NAME_ptr)
#define GSS_C_NT_HOSTBASED_SERVICE (*GSS_C_NT_HOSTBASED_SERVICE_ptr)
#define gss_mech_krb5 (*gss_mech_krb5_ptr)

#define gss_lib_name "libgssapi_krb5.so"

static int32_t ensure_gss_shim_initialized()
{
void* lib = dlopen(gss_lib_name, RTLD_LAZY);
if (lib == NULL) { fprintf(stderr, "Cannot load library %s \nError: %s\n", gss_lib_name, dlerror()); return -1; }

// check is someone else has opened and published s_gssLib already
if (!pal_atomic_cas_ptr(&s_gssLib, lib, NULL))
{
dlclose(lib);
}

// initialize indirection pointers for all functions, like:
// gss_accept_sec_context_ptr = (TYPEOF(gss_accept_sec_context)*)dlsym(s_gssLib, "gss_accept_sec_context");
// if (gss_accept_sec_context_ptr == NULL) { fprintf(stderr, "Cannot get symbol %s from %s \nError: %s\n", "gss_accept_sec_context", gss_lib_name, dlerror()); return -1; }
#define PER_FUNCTION_BLOCK(fn) \
fn##_ptr = (TYPEOF(fn)*)dlsym(s_gssLib, #fn); \
if (fn##_ptr == NULL) { fprintf(stderr, "Cannot get symbol " #fn " from %s \nError: %s\n", gss_lib_name, dlerror()); return -1; }

FOR_ALL_GSS_FUNCTIONS
#undef PER_FUNCTION_BLOCK

return 0;
}

#endif // GSS_SHIM

// transfers ownership of the underlying data from gssBuffer to PAL_GssBuffer
static void NetSecurityNative_MoveBuffer(gss_buffer_t gssBuffer, PAL_GssBuffer* targetBuffer)
{
Expand Down Expand Up @@ -567,3 +669,12 @@ uint32_t NetSecurityNative_IsNtlmInstalled()

return foundNtlm;
}

int32_t NetSecurityNative_EnsureGssInitialized()
{
#if defined(GSS_SHIM)
return ensure_gss_shim_initialized();
#else
return 0;
#endif
}
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,9 @@ Shims gss_inquire_context and gss_display_name to get the remote user principal
PALEXPORT uint32_t NetSecurityNative_GetUser(uint32_t* minorStatus,
GssCtxId* contextHandle,
PAL_GssBuffer* outBuffer);

/*
Performs initialization of GSS shim, if necessary.
Return value 0 indicates a success.
*/
PALEXPORT int32_t NetSecurityNative_EnsureGssInitialized(void);

0 comments on commit 7cfa95b

Please sign in to comment.