diff --git a/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.IsNtlmInstalled.cs b/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.IsNtlmInstalled.cs index 79edbf6adad46..24b5b9ad5a78c 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.IsNtlmInstalled.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.IsNtlmInstalled.cs @@ -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. + } + } } } diff --git a/src/libraries/Native/Unix/System.Net.Security.Native/entrypoints.c b/src/libraries/Native/Unix/System.Net.Security.Native/entrypoints.c index ac702e9040090..56d2bde8ce6e0 100644 --- a/src/libraries/Native/Unix/System.Net.Security.Native/entrypoints.c +++ b/src/libraries/Native/Unix/System.Net.Security.Native/entrypoints.c @@ -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) diff --git a/src/libraries/Native/Unix/System.Net.Security.Native/extra_libs.cmake b/src/libraries/Native/Unix/System.Net.Security.Native/extra_libs.cmake index 49c6ff4961004..0744078474b39 100644 --- a/src/libraries/Native/Unix/System.Net.Security.Native/extra_libs.cmake +++ b/src/libraries/Native/Unix/System.Net.Security.Native/extra_libs.cmake @@ -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() diff --git a/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c b/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c index 9628318b773d6..2a37649f56c81 100644 --- a/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c +++ b/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c @@ -20,6 +20,11 @@ #include #include +#if defined(GSS_SHIM) +#include +#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); @@ -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) { @@ -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 +} diff --git a/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.h b/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.h index 5c1fe55163ec6..b270569c38890 100644 --- a/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.h +++ b/src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.h @@ -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);