Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shim gss api on Linux to delay loading libgssapi_krb5.so #55037

Merged
merged 10 commits into from
Jul 9, 2021
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);