forked from dotnet/java-interop
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[java-interop, Java.Interop] Securely load native libs (dotnet#691)
Fixes: dotnet#676 Context: https://liquid.microsoft.com/Web/Object/Read/ms.security/Requirements/Microsoft.Security.SystemsADM.10039#guide The current security guidance is that the [`System.Runtime.InteropServices.DefaultDllImportSearchPathsAttribute`][0] attribute should be placed either on the assembly or on `[DllImport]` methods to control and constrain where [`LoadLibraryEx()`][1] will look for native libraries, in particular to *prevent* looking for native libraries within e.g. the current working directory or `%PATH%` or any other "attacker-controlled" location. Update `Java.Interop.dll` and `Java.Runtime.Environment.dll` so that the [`DllImportSearchPath`][2] values `AssemblyDirectory` and `SafeDirectories` are used: * `AssemblyDirectory`: "include the directory that contains the assembly itself, and search that directory first." * `SafeDirectories`: "Include the application directory, the `%WinDir%\System32` directory, and user directories in the DLL search path. Additionally, update `src/java-interop` so that instead of requiring the use of [**dlopen**(3)][3] on Windows, the following functions are added to support loading native libraries and resolving symbols from those native libraries: void* java_interop_lib_load (const char *path, unsigned int flags, char **error); void* java_interop_lib_symbol (void* library, const char *symbol, char **error); int java_interop_lib_close (void* library, char **error); (Previously, xamarin-android used the [dlfcn-win32/dlfcn-win32][4] library to implement **dlopen**(3), but dlfcn-win32/dlfcn-win32@ef7e412d calls `LoadLibraryEx()` with `LOAD_WITH_ALTERED_SEARCH_PATH`, which doesn't fulfill our internal requirements.) On Windows, `java_interop_lib_load()` will use [`LoadLibraryEx()`][5] to load libraries from a constrained set of directories: * `LOAD_LIBRARY_SEARCH_APPLICATION_DIR`: "the application's installation directory is searched for the DLL and its dependencies" * `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR`: "the directory that contains the DLL is temporarily added to the beginning of the list of directories that are searched for the DLL's dependencies." * `LOAD_LIBRARY_SEARCH_USER_DIRS`: "directories added using the `AddDllDirectory()` or the `SetDllDirectory()` function are searched for the DLL and its dependencies" In order to simplify the introduction of `java_interop_lib_load()`, start *requiring* the presence of the symbols `mono_thread_get_managed_id` and `mono_thread_get_name_utf8`. These symbols have been present within Mono for ages at this point, and requiring means we don't need to support `dlopen(NULL)` semantics. Update the `@(ClInclude)` item group and `BuildMac` and related targets so that we properly rebuild things when e.g. `java-interop-dlfcn.h` changes, as would "normally" be expected. Finally, the continued use of `MONO_API` and other macros causes "weird" compiler issues when integrating with xamarin-android. Replace `MONO_API`/etc. use with `JAVA_INTEROP_API`/etc. instead. [0]: https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.defaultdllimportsearchpathsattribute?view=netcore-3.1 [1]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexa?redirectedfrom=MSDN [2]: https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.dllimportsearchpath?view=netcore-3.1 [3]: https://linux.die.net/man/3/dlopen [4]: https://github.com/dlfcn-win32/dlfcn-win32 [5]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexa
- Loading branch information
Showing
13 changed files
with
342 additions
and
145 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
using System.Runtime.InteropServices; | ||
|
||
[assembly: DefaultDllImportSearchPathsAttribute (DllImportSearchPath.SafeDirectories | DllImportSearchPath.AssemblyDirectory)] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
#include "java-interop.h" | ||
#include "java-interop-dlfcn.h" | ||
#include "java-interop-util.h" | ||
|
||
#ifdef WINDOWS | ||
#include <libloaderapi.h> | ||
#include <winerror.h> | ||
#include <wtypes.h> | ||
#include <winbase.h> | ||
#else | ||
#include <dlfcn.h> | ||
#include <string.h> | ||
#endif | ||
|
||
namespace microsoft::java_interop { | ||
|
||
static char * | ||
_get_last_dlerror () | ||
{ | ||
#ifdef WINDOWS | ||
|
||
DWORD error = GetLastError (); | ||
if (error == ERROR_SUCCESS /* 0 */) { | ||
return nullptr; | ||
} | ||
|
||
wchar_t *buf = nullptr; | ||
|
||
DWORD size = FormatMessageW ( | ||
/* dwFlags */ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, | ||
/* lpSource */ NULL, | ||
/* dwMessageId */ error, | ||
/* dwLanguageId */ MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), | ||
/* lpBuffer */ (LPWSTR) &buf, | ||
/* nSize */ 0, | ||
/* Arguments */ NULL | ||
); | ||
if (size == 0) | ||
return nullptr; | ||
|
||
char *message = utf16_to_utf8 (buf); | ||
LocalFree (buf); | ||
|
||
return message; | ||
|
||
#else // ndef WINDOWS | ||
|
||
return java_interop_strdup (dlerror ()); | ||
|
||
#endif // ndef WINDOWS | ||
} | ||
|
||
static void | ||
_free_error (char **error) | ||
{ | ||
if (error == nullptr) | ||
return; | ||
java_interop_free (*error); | ||
*error = nullptr; | ||
} | ||
|
||
static void | ||
_set_error (char **error, const char *message) | ||
{ | ||
if (error == nullptr) | ||
return; | ||
*error = java_interop_strdup (message); | ||
} | ||
|
||
static void | ||
_set_error_to_last_error (char **error) | ||
{ | ||
if (error == nullptr) | ||
return; | ||
*error = _get_last_dlerror (); | ||
} | ||
|
||
void* | ||
java_interop_lib_load (const char *path, [[maybe_unused]] unsigned int flags, char **error) | ||
{ | ||
_free_error (error); | ||
if (path == nullptr) { | ||
_set_error (error, "path=nullptr is not supported"); | ||
return nullptr; | ||
} | ||
|
||
void *handle = nullptr; | ||
|
||
#ifdef WINDOWS | ||
|
||
wchar_t *wpath = utf8_to_utf16 (path); | ||
if (wpath == nullptr) { | ||
_set_error (error, "could not convert path to UTF-16"); | ||
return nullptr; | ||
} | ||
HMODULE module = LoadLibraryExW ( | ||
/* lpLibFileName */ wpath, | ||
/* hFile */ nullptr, | ||
/* dwFlags */ LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_USER_DIRS | ||
); | ||
java_interop_free (wpath); | ||
|
||
handle = reinterpret_cast<void*>(module); | ||
|
||
#else // ndef WINDOWS | ||
|
||
int mode = 0; | ||
if ((flags & JAVA_INTEROP_LIB_LOAD_GLOBALLY) == JAVA_INTEROP_LIB_LOAD_GLOBALLY) { | ||
mode = RTLD_GLOBAL; | ||
} | ||
if ((flags & JAVA_INTEROP_LIB_LOAD_LOCALLY) == JAVA_INTEROP_LIB_LOAD_LOCALLY) { | ||
mode = RTLD_LOCAL; | ||
} | ||
|
||
if (mode == 0) { | ||
mode = RTLD_LOCAL; | ||
} | ||
mode |= RTLD_NOW; | ||
|
||
handle = dlopen (path, mode); | ||
|
||
#endif // ndef WINDOWS | ||
|
||
if (handle == nullptr) { | ||
_set_error_to_last_error (error); | ||
} | ||
|
||
return handle; | ||
} | ||
|
||
void* | ||
java_interop_lib_symbol (void *library, const char *symbol, char **error) | ||
{ | ||
_free_error (error); | ||
|
||
if (library == nullptr) { | ||
_set_error (error, "library=nullptr"); | ||
return nullptr; | ||
} | ||
if (symbol == nullptr) { | ||
_set_error (error, "symbol=nullptr"); | ||
return nullptr; | ||
} | ||
|
||
void *address = nullptr; | ||
|
||
#ifdef WINDOWS | ||
|
||
HMODULE module = reinterpret_cast<HMODULE>(library); | ||
FARPROC a = GetProcAddress (module, symbol); | ||
address = reinterpret_cast<void*>(a); | ||
|
||
#else // ndef WINDOWS | ||
|
||
address = dlsym (library, symbol); | ||
|
||
#endif // ndef WINDOWS | ||
|
||
if (address == nullptr) { | ||
_set_error_to_last_error (error); | ||
} | ||
|
||
return address; | ||
} | ||
|
||
int | ||
java_interop_lib_close (void* library, char **error) | ||
{ | ||
_free_error (error); | ||
if (library == nullptr) { | ||
_set_error (error, "library=nullptr"); | ||
return JAVA_INTEROP_LIB_INVALID_PARAM; | ||
} | ||
|
||
int r = 0; | ||
|
||
#ifdef WINDOWS | ||
HMODULE h = reinterpret_cast<HMODULE>(library); | ||
BOOL v = FreeLibrary (h); | ||
if (!v) { | ||
r = JAVA_INTEROP_LIB_CLOSE_FAILED; | ||
} | ||
#else // ndef WINDOWS | ||
r = dlclose (library); | ||
if (r != 0) { | ||
r = JAVA_INTEROP_LIB_CLOSE_FAILED; | ||
} | ||
#endif // ndef WINDOWS | ||
|
||
if (r != 0) { | ||
_set_error_to_last_error (error); | ||
} | ||
|
||
return r; | ||
} | ||
|
||
} // namespace microsoft::java_interop |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#ifndef INC_JAVA_INTEROP_DLFCN_H | ||
#define INC_JAVA_INTEROP_DLFCN_H | ||
|
||
#include "java-interop.h" | ||
|
||
namespace microsoft::java_interop { | ||
|
||
// Possible flags values for java_interop_lib_load | ||
constexpr unsigned int JAVA_INTEROP_LIB_LOAD_GLOBALLY = (1 << 0); | ||
constexpr unsigned int JAVA_INTEROP_LIB_LOAD_LOCALLY = (1 << 1); | ||
|
||
|
||
// Possible error codes from java_interop_lib_close | ||
constexpr int JAVA_INTEROP_LIB_FAILED = -1000; | ||
constexpr int JAVA_INTEROP_LIB_CLOSE_FAILED = JAVA_INTEROP_LIB_FAILED-1; | ||
constexpr int JAVA_INTEROP_LIB_INVALID_PARAM = JAVA_INTEROP_LIB_FAILED-2; | ||
|
||
JAVA_INTEROP_BEGIN_DECLS | ||
|
||
JAVA_INTEROP_API void* java_interop_lib_load (const char *path, unsigned int flags, char **error); | ||
JAVA_INTEROP_API void* java_interop_lib_symbol (void* library, const char *symbol, char **error); | ||
JAVA_INTEROP_API int java_interop_lib_close (void* library, char **error); | ||
|
||
JAVA_INTEROP_END_DECLS | ||
|
||
} | ||
|
||
#endif /* INC_JAVA_INTEROP_DLFCN_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.