Skip to content

Commit 7cfa95b

Browse files
authored
Shim gss api on Linux to delay loading libgssapi_krb5.so (#55037)
* 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
1 parent 784ddf8 commit 7cfa95b

File tree

5 files changed

+149
-1
lines changed

5 files changed

+149
-1
lines changed

src/libraries/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.IsNtlmInstalled.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,29 @@ internal static partial class NetSecurityNative
1010
{
1111
[DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_IsNtlmInstalled")]
1212
internal static extern bool IsNtlmInstalled();
13+
14+
[DllImport(Interop.Libraries.NetSecurityNative, EntryPoint = "NetSecurityNative_EnsureGssInitialized")]
15+
private static extern int EnsureGssInitialized();
16+
17+
static NetSecurityNative()
18+
{
19+
GssInitializer.Initialize();
20+
}
21+
22+
internal static class GssInitializer
23+
{
24+
static GssInitializer()
25+
{
26+
if (EnsureGssInitialized() != 0)
27+
{
28+
throw new InvalidOperationException();
29+
}
30+
}
31+
32+
internal static void Initialize()
33+
{
34+
// No-op that exists to provide a hook for other static constructors.
35+
}
36+
}
1337
}
1438
}

src/libraries/Native/Unix/System.Net.Security.Native/entrypoints.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ static const Entry s_securityNative[] =
1313
DllImportEntry(NetSecurityNative_DeleteSecContext)
1414
DllImportEntry(NetSecurityNative_DisplayMajorStatus)
1515
DllImportEntry(NetSecurityNative_DisplayMinorStatus)
16+
DllImportEntry(NetSecurityNative_EnsureGssInitialized)
1617
DllImportEntry(NetSecurityNative_GetUser)
1718
DllImportEntry(NetSecurityNative_ImportPrincipalName)
1819
DllImportEntry(NetSecurityNative_ImportUserName)

src/libraries/Native/Unix/System.Net.Security.Native/extra_libs.cmake

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,11 @@ macro(append_extra_security_libs NativeLibsExtra)
1919
endif()
2020
endif()
2121

22-
list(APPEND ${NativeLibsExtra} ${LIBGSS})
22+
if(CLR_CMAKE_TARGET_LINUX)
23+
# On Linux libgssapi_krb5.so is loaded on demand to tolerate its absense in singlefile apps that do not use it
24+
list(APPEND ${NativeLibsExtra} dl)
25+
add_definitions(-DGSS_SHIM)
26+
else()
27+
list(APPEND ${NativeLibsExtra} ${LIBGSS})
28+
endif()
2329
endmacro()

src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.c

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
#include <assert.h>
2121
#include <string.h>
2222

23+
#if defined(GSS_SHIM)
24+
#include <dlfcn.h>
25+
#include "pal_atomic.h"
26+
#endif
27+
2328
c_static_assert(PAL_GSS_C_DELEG_FLAG == GSS_C_DELEG_FLAG);
2429
c_static_assert(PAL_GSS_C_MUTUAL_FLAG == GSS_C_MUTUAL_FLAG);
2530
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_
4853
.elements = gss_ntlm_oid_value};
4954
#endif
5055

56+
#if defined(GSS_SHIM)
57+
58+
#define FOR_ALL_GSS_FUNCTIONS \
59+
PER_FUNCTION_BLOCK(gss_accept_sec_context) \
60+
PER_FUNCTION_BLOCK(gss_acquire_cred) \
61+
PER_FUNCTION_BLOCK(gss_acquire_cred_with_password) \
62+
PER_FUNCTION_BLOCK(gss_delete_sec_context) \
63+
PER_FUNCTION_BLOCK(gss_display_name) \
64+
PER_FUNCTION_BLOCK(gss_display_status) \
65+
PER_FUNCTION_BLOCK(gss_import_name) \
66+
PER_FUNCTION_BLOCK(gss_indicate_mechs) \
67+
PER_FUNCTION_BLOCK(gss_init_sec_context) \
68+
PER_FUNCTION_BLOCK(gss_inquire_context) \
69+
PER_FUNCTION_BLOCK(gss_mech_krb5) \
70+
PER_FUNCTION_BLOCK(gss_oid_equal) \
71+
PER_FUNCTION_BLOCK(gss_release_buffer) \
72+
PER_FUNCTION_BLOCK(gss_release_cred) \
73+
PER_FUNCTION_BLOCK(gss_release_name) \
74+
PER_FUNCTION_BLOCK(gss_release_oid_set) \
75+
PER_FUNCTION_BLOCK(gss_unwrap) \
76+
PER_FUNCTION_BLOCK(gss_wrap) \
77+
PER_FUNCTION_BLOCK(GSS_C_NT_USER_NAME) \
78+
PER_FUNCTION_BLOCK(GSS_C_NT_HOSTBASED_SERVICE)
79+
80+
#if HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
81+
82+
#define FOR_ALL_GSS_FUNCTIONS FOR_ALL_GSS_FUNCTIONS \
83+
PER_FUNCTION_BLOCK(gss_set_cred_option)
84+
85+
#endif //HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
86+
87+
// define indirection pointers for all functions, like
88+
// static TYPEOF(gss_accept_sec_context)* gss_accept_sec_context_ptr;
89+
#define PER_FUNCTION_BLOCK(fn) \
90+
static TYPEOF(fn)* fn##_ptr;
91+
92+
FOR_ALL_GSS_FUNCTIONS
93+
#undef PER_FUNCTION_BLOCK
94+
95+
static void* volatile s_gssLib = NULL;
96+
97+
// remap gss function use to use indirection pointers
98+
#define gss_accept_sec_context(...) gss_accept_sec_context_ptr(__VA_ARGS__)
99+
#define gss_acquire_cred(...) gss_acquire_cred_ptr(__VA_ARGS__)
100+
#define gss_acquire_cred_with_password(...) gss_acquire_cred_with_password_ptr(__VA_ARGS__)
101+
#define gss_delete_sec_context(...) gss_delete_sec_context_ptr(__VA_ARGS__)
102+
#define gss_display_name(...) gss_display_name_ptr(__VA_ARGS__)
103+
#define gss_display_status(...) gss_display_status_ptr(__VA_ARGS__)
104+
#define gss_import_name(...) gss_import_name_ptr(__VA_ARGS__)
105+
#define gss_indicate_mechs(...) gss_indicate_mechs_ptr(__VA_ARGS__)
106+
#define gss_init_sec_context(...) gss_init_sec_context_ptr(__VA_ARGS__)
107+
#define gss_inquire_context(...) gss_inquire_context_ptr(__VA_ARGS__)
108+
#define gss_oid_equal(...) gss_oid_equal_ptr(__VA_ARGS__)
109+
#define gss_release_buffer(...) gss_release_buffer_ptr(__VA_ARGS__)
110+
#define gss_release_cred(...) gss_release_cred_ptr(__VA_ARGS__)
111+
#define gss_release_name(...) gss_release_name_ptr(__VA_ARGS__)
112+
#define gss_release_oid_set(...) gss_release_oid_set_ptr(__VA_ARGS__)
113+
#define gss_unwrap(...) gss_unwrap_ptr(__VA_ARGS__)
114+
#define gss_wrap(...) gss_wrap_ptr(__VA_ARGS__)
115+
116+
#if HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
117+
#define gss_set_cred_option(...) gss_set_cred_option_ptr(__VA_ARGS__)
118+
#endif //HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
119+
120+
121+
#define GSS_C_NT_USER_NAME (*GSS_C_NT_USER_NAME_ptr)
122+
#define GSS_C_NT_HOSTBASED_SERVICE (*GSS_C_NT_HOSTBASED_SERVICE_ptr)
123+
#define gss_mech_krb5 (*gss_mech_krb5_ptr)
124+
125+
#define gss_lib_name "libgssapi_krb5.so"
126+
127+
static int32_t ensure_gss_shim_initialized()
128+
{
129+
void* lib = dlopen(gss_lib_name, RTLD_LAZY);
130+
if (lib == NULL) { fprintf(stderr, "Cannot load library %s \nError: %s\n", gss_lib_name, dlerror()); return -1; }
131+
132+
// check is someone else has opened and published s_gssLib already
133+
if (!pal_atomic_cas_ptr(&s_gssLib, lib, NULL))
134+
{
135+
dlclose(lib);
136+
}
137+
138+
// initialize indirection pointers for all functions, like:
139+
// gss_accept_sec_context_ptr = (TYPEOF(gss_accept_sec_context)*)dlsym(s_gssLib, "gss_accept_sec_context");
140+
// 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; }
141+
#define PER_FUNCTION_BLOCK(fn) \
142+
fn##_ptr = (TYPEOF(fn)*)dlsym(s_gssLib, #fn); \
143+
if (fn##_ptr == NULL) { fprintf(stderr, "Cannot get symbol " #fn " from %s \nError: %s\n", gss_lib_name, dlerror()); return -1; }
144+
145+
FOR_ALL_GSS_FUNCTIONS
146+
#undef PER_FUNCTION_BLOCK
147+
148+
return 0;
149+
}
150+
151+
#endif // GSS_SHIM
152+
51153
// transfers ownership of the underlying data from gssBuffer to PAL_GssBuffer
52154
static void NetSecurityNative_MoveBuffer(gss_buffer_t gssBuffer, PAL_GssBuffer* targetBuffer)
53155
{
@@ -567,3 +669,12 @@ uint32_t NetSecurityNative_IsNtlmInstalled()
567669

568670
return foundNtlm;
569671
}
672+
673+
int32_t NetSecurityNative_EnsureGssInitialized()
674+
{
675+
#if defined(GSS_SHIM)
676+
return ensure_gss_shim_initialized();
677+
#else
678+
return 0;
679+
#endif
680+
}

src/libraries/Native/Unix/System.Net.Security.Native/pal_gssapi.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,9 @@ Shims gss_inquire_context and gss_display_name to get the remote user principal
193193
PALEXPORT uint32_t NetSecurityNative_GetUser(uint32_t* minorStatus,
194194
GssCtxId* contextHandle,
195195
PAL_GssBuffer* outBuffer);
196+
197+
/*
198+
Performs initialization of GSS shim, if necessary.
199+
Return value 0 indicates a success.
200+
*/
201+
PALEXPORT int32_t NetSecurityNative_EnsureGssInitialized(void);

0 commit comments

Comments
 (0)