diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 049d8ae2..ea422b70 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -85,7 +85,7 @@ jobs: run: | LOWER_JDK="${{ matrix.java }}" UPPER_JDK=$((LOWER_JDK+1)) - ./mvnw -B -V -e -Pcoverage verify -Dtoolchain.jdk.version="[$LOWER_JDK,$UPPER_JDK)" -Dmaven.resources.skip=true -Dflatten.skip=true -Dmaven.main.skip=true -Dbnd.skip=true -Dassembly.skipAssembly=true -Dmaven.javadoc.skip=true -Dcyclonedx.skip=true -Dspdx.skip=true -Dformatter.skip=true -Dforbiddenapis.skip=true -DskipTests=false -DskipITs=false + ./mvnw -B -V -e -Pcoverage verify -Dtoolchain.jdk.version="[$LOWER_JDK,$UPPER_JDK)" -Dwindowsapi.skip=true -Dmaven.resources.skip=true -Dflatten.skip=true -Dmaven.main.skip=true -Dbnd.skip=true -Dassembly.skipAssembly=true -Dmaven.javadoc.skip=true -Dcyclonedx.skip=true -Dspdx.skip=true -Dformatter.skip=true -Dforbiddenapis.skip=true -DskipTests=false -DskipITs=false - name: Upload test results uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: diff --git a/pom.xml b/pom.xml index ed1125d4..03113163 100644 --- a/pom.xml +++ b/pom.xml @@ -271,6 +271,58 @@ + + net.codecrete.windows-api + windowsapi-maven-plugin + 0.8.4 + + . + com.jcraft.jsch.windowsapi + false + + CloseHandle + CopySid + CreateFileMappingA + FindWindowA + GetCurrentProcessId + GetCurrentThreadId + GetLengthSid + GetTokenInformation + InitializeSecurityDescriptor + IsValidSid + MapViewOfFile + OpenProcess + OpenProcessToken + SendMessageA + SetSecurityDescriptorOwner + UnmapViewOfFile + + + COPYDATASTRUCT + SECURITY_ATTRIBUTES + SECURITY_DESCRIPTOR + SID_AND_ATTRIBUTES + TOKEN_USER + + + FILE_MAP + PAGE_PROTECTION_FLAGS + TOKEN_ACCESS_MASK + TOKEN_INFORMATION_CLASS + + + INVALID_HANDLE_VALUE + MAXIMUM_ALLOWED + SECURITY_DESCRIPTOR_REVISION + WM_COPYDATA + + + + + software.xdev + find-and-replace-maven-plugin + 1.0.5 + org.apache.maven.plugins maven-toolchains-plugin @@ -418,6 +470,36 @@ 16 + + default-compile-19 + + compile + + + + ${project.basedir}/src/main/java19 + + true + 19 + + + + default-compile-23 + + compile + + + + ${project.basedir}/src/main/java23 + ${project.build.directory}/generated-sources/windows-api + + true + 23 + + -Xlint:all,-processing,-classfile,-options,-restricted + + + default-compile-24 @@ -559,8 +641,8 @@ true none com.jcraft.jsch - com.jcraft.jsch.* - ${project.build.sourceDirectory}:${project.build.directory}/generated-sources/java-templates:${project.basedir}/src/main/java9:${project.basedir}/src/main/java10:${project.basedir}/src/main/java11:${project.basedir}/src/main/java15:${project.basedir}/src/main/java16:${project.basedir}/src/main/java24 + com.jcraft.jsch.*,com.jcraft.jsch.windowsapi.windows.win32.*,com.jcraft.jsch.windowsapi.windows.win32.system.*,com.jcraft.jsch.windowsapi.windows.win32.ui.* + ${project.build.sourceDirectory}:${project.build.directory}/generated-sources/java-templates:${project.basedir}/src/main/java9:${project.basedir}/src/main/java10:${project.basedir}/src/main/java11:${project.basedir}/src/main/java15:${project.basedir}/src/main/java16:${project.basedir}/src/main/java19:${project.basedir}/src/main/java23:${project.build.directory}/generated-sources/windows-api:${project.basedir}/src/main/java24 @@ -658,6 +740,8 @@ ${project.basedir}/src/main/java11 ${project.basedir}/src/main/java15 ${project.basedir}/src/main/java16 + ${project.basedir}/src/main/java19 + ${project.basedir}/src/main/java23 ${project.basedir}/src/main/java24 ${project.basedir}/src/main/java-templates @@ -679,6 +763,8 @@ ${project.basedir}/src/main/java11 ${project.basedir}/src/main/java15 ${project.basedir}/src/main/java16 + ${project.basedir}/src/main/java19 + ${project.basedir}/src/main/java23 ${project.basedir}/src/main/java24 ${project.basedir}/src/main/java-templates @@ -690,8 +776,10 @@ 0.8.14 + com/jcraft/jsch/JavaThreadId.class com/jcraft/jsch/JavaVersion.class com/jcraft/jsch/JplLogger.class + com/jcraft/jsch/PageantFFMConnector.class com/jcraft/jsch/UnixDomainSocketFactory.class com/jcraft/jsch/jce/KeyPairGenEdDSA.class com/jcraft/jsch/jce/MLKEM.class @@ -703,6 +791,7 @@ com/jcraft/jsch/jce/XDH.class META-INF/versions/9/com/jcraft/jsch/JavaVersion.class META-INF/versions/10/com/jcraft/jsch/JavaVersion.class + META-INF/versions/19/com/jcraft/jsch/JavaThreadId.class @@ -987,5 +1076,64 @@ + + windowsapi + + + windowsapi.skip + !true + + + + + + net.codecrete.windows-api + windowsapi-maven-plugin + + + + windows-api + + + + + + + software.xdev + find-and-replace-maven-plugin + + + fix-mapviewoffile-signature + process-sources + + file-contents + + + ${project.build.directory}/generated-sources/windows-api/com/jcraft/jsch/windowsapi/windows/win32/system/memory/ + Apis.java + UTF-8 + MapViewOfFile\(MemorySegment lastErrorState + MapViewOfFile(SegmentAllocator allocator, MemorySegment lastErrorState + + + + fix-mapviewoffile-downcall + process-sources + + file-contents + + + ${project.build.directory}/generated-sources/windows-api/com/jcraft/jsch/windowsapi/windows/win32/system/memory/ + Apis.java + UTF-8 + MapViewOfFile\$IMPL\.HANDLE\.invokeExact\(lastErrorState + MapViewOfFile\$IMPL.HANDLE.invokeExact(allocator, lastErrorState + + + + + + + diff --git a/src/assembly/sources.xml b/src/assembly/sources.xml index 51877a64..c9db1979 100644 --- a/src/assembly/sources.xml +++ b/src/assembly/sources.xml @@ -62,6 +62,27 @@ **/*.java + + ${project.basedir}/src/main/java19 + META-INF/versions/19 + + **/*.java + + + + ${project.basedir}/src/main/java23 + META-INF/versions/23 + + **/*.java + + + + ${project.build.directory}/generated-sources/windows-api + META-INF/versions/23 + + **/*.java + + ${project.basedir}/src/main/java24 META-INF/versions/24 diff --git a/src/main/java/com/jcraft/jsch/JavaThreadId.java b/src/main/java/com/jcraft/jsch/JavaThreadId.java new file mode 100644 index 00000000..07aadc75 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/JavaThreadId.java @@ -0,0 +1,11 @@ +package com.jcraft.jsch; + +import com.jcraft.jsch.annotations.SuppressForbiddenApi; + +final class JavaThreadId { + + @SuppressForbiddenApi("jdk-deprecated") + static long get() { + return Thread.currentThread().getId(); + } +} diff --git a/src/main/java/com/jcraft/jsch/PageantConnector.java b/src/main/java/com/jcraft/jsch/PageantConnector.java index 7b7cc31e..fd697ab8 100644 --- a/src/main/java/com/jcraft/jsch/PageantConnector.java +++ b/src/main/java/com/jcraft/jsch/PageantConnector.java @@ -36,6 +36,7 @@ import com.sun.jna.platform.win32.WinDef.HWND; import com.sun.jna.platform.win32.WinDef.LPARAM; import com.sun.jna.platform.win32.WinDef.LRESULT; +import com.sun.jna.platform.win32.WinError; import com.sun.jna.platform.win32.WinNT; import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.platform.win32.WinUser; @@ -85,8 +86,10 @@ public void query(Buffer buffer) throws AgentProxyException { throw new AgentProxyException("Pageant is not runnning."); } - String mapname = - String.format(Locale.ROOT, "PageantRequest%08x", kernel32.GetCurrentThreadId()); + String threadId = JavaVersion.getVersion() >= 19 + ? String.format(Locale.ROOT, "%08x%08x", kernel32.GetCurrentProcessId(), JavaThreadId.get()) + : String.format(Locale.ROOT, "%08x", kernel32.GetCurrentThreadId()); + String mapname = "JSchPageantRequest" + threadId; HANDLE sharedFile = null; Pointer sharedMemory = null; @@ -96,13 +99,19 @@ public void query(Buffer buffer) throws AgentProxyException { sharedFile = kernel32.CreateFileMapping(WinBase.INVALID_HANDLE_VALUE, psa, WinNT.PAGE_READWRITE, 0, AGENT_MAX_MSGLEN, mapname); - if (sharedFile == null || sharedFile == WinBase.INVALID_HANDLE_VALUE) { - throw new AgentProxyException("Unable to create shared file mapping."); + int lastError = kernel32.GetLastError(); + if (sharedFile == null) { + throw new AgentProxyException( + "Unable to CreateFileMapping(): GetLastError() = " + lastError); + } + if (lastError == WinError.ERROR_ALREADY_EXISTS) { + throw new AgentProxyException("Shared file mapping already exists"); } - sharedMemory = kernel32.MapViewOfFile(sharedFile, WinNT.SECTION_MAP_WRITE, 0, 0, 0); + sharedMemory = kernel32.MapViewOfFile(sharedFile, WinBase.FILE_MAP_WRITE, 0, 0, 0); if (sharedMemory == null) { - throw new AgentProxyException("Unable to create shared file mapping."); + throw new AgentProxyException( + "Unable to MapViewOfFile(): GetLastError() = " + kernel32.GetLastError()); } sharedMemory.write(0, buffer.buffer, 0, buffer.getLength()); @@ -124,13 +133,18 @@ public void query(Buffer buffer) throws AgentProxyException { sharedMemory.read(4, buffer.buffer, 0, i); } else { throw new AgentProxyException( - "User32.SendMessage() returned 0 with cds.dwData: " + Long.toHexString(foo)); + "SendMessage() returned 0 with cds.dwData: " + Long.toHexString(foo)); } } finally { - if (sharedMemory != null) - kernel32.UnmapViewOfFile(sharedMemory); - if (sharedFile != null) - kernel32.CloseHandle(sharedFile); + try { + if (sharedMemory != null) { + kernel32.UnmapViewOfFile(sharedMemory); + } + } finally { + if (sharedFile != null) { + kernel32.CloseHandle(sharedFile); + } + } } } diff --git a/src/main/java/com/jcraft/jsch/PageantFFMConnector.java b/src/main/java/com/jcraft/jsch/PageantFFMConnector.java new file mode 100644 index 00000000..ee10f022 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/PageantFFMConnector.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class PageantFFMConnector implements AgentConnector { + + public PageantFFMConnector() throws AgentProxyException { + throw new AgentProxyException("PageantFFMConnector requires Java23+."); + } + + @Override + public String getName() { + throw new UnsupportedOperationException("PageantFFMConnector requires Java23+."); + } + + @Override + public boolean isAvailable() { + throw new UnsupportedOperationException("PageantFFMConnector requires Java23+."); + } + + @Override + public void query(Buffer buffer) throws AgentProxyException { + throw new UnsupportedOperationException("PageantFFMConnector requires Java23+."); + } +} diff --git a/src/main/java19/com/jcraft/jsch/JavaThreadId.java b/src/main/java19/com/jcraft/jsch/JavaThreadId.java new file mode 100644 index 00000000..608652ab --- /dev/null +++ b/src/main/java19/com/jcraft/jsch/JavaThreadId.java @@ -0,0 +1,8 @@ +package com.jcraft.jsch; + +final class JavaThreadId { + + static long get() { + return Thread.currentThread().threadId(); + } +} diff --git a/src/main/java23/com/jcraft/jsch/PageantFFMConnector.java b/src/main/java23/com/jcraft/jsch/PageantFFMConnector.java new file mode 100644 index 00000000..e9fe2a3c --- /dev/null +++ b/src/main/java23/com/jcraft/jsch/PageantFFMConnector.java @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import com.jcraft.jsch.windowsapi.windows.win32.foundation.WIN32_ERROR; +import com.jcraft.jsch.windowsapi.windows.win32.security.SECURITY_ATTRIBUTES; +import com.jcraft.jsch.windowsapi.windows.win32.security.SECURITY_DESCRIPTOR; +import com.jcraft.jsch.windowsapi.windows.win32.security.SID_AND_ATTRIBUTES; +import com.jcraft.jsch.windowsapi.windows.win32.security.TOKEN_ACCESS_MASK; +import com.jcraft.jsch.windowsapi.windows.win32.security.TOKEN_INFORMATION_CLASS; +import com.jcraft.jsch.windowsapi.windows.win32.security.TOKEN_USER; +import com.jcraft.jsch.windowsapi.windows.win32.system.dataexchange.COPYDATASTRUCT; +import com.jcraft.jsch.windowsapi.windows.win32.system.memory.FILE_MAP; +import com.jcraft.jsch.windowsapi.windows.win32.system.memory.MEMORY_MAPPED_VIEW_ADDRESS; +import com.jcraft.jsch.windowsapi.windows.win32.system.memory.PAGE_PROTECTION_FLAGS; +import java.lang.foreign.Arena; +import java.lang.foreign.Linker; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.StructLayout; +import java.lang.foreign.ValueLayout; +import java.lang.invoke.VarHandle; +import java.nio.charset.StandardCharsets; +import java.util.Locale; + +import static com.jcraft.jsch.windowsapi.windows.win32.foundation.Constants.INVALID_HANDLE_VALUE; +import static com.jcraft.jsch.windowsapi.windows.win32.foundation.Apis.CloseHandle; +import static com.jcraft.jsch.windowsapi.windows.win32.security.Apis.CopySid; +import static com.jcraft.jsch.windowsapi.windows.win32.security.Apis.GetLengthSid; +import static com.jcraft.jsch.windowsapi.windows.win32.security.Apis.GetTokenInformation; +import static com.jcraft.jsch.windowsapi.windows.win32.security.Apis.InitializeSecurityDescriptor; +import static com.jcraft.jsch.windowsapi.windows.win32.security.Apis.IsValidSid; +import static com.jcraft.jsch.windowsapi.windows.win32.security.Apis.SetSecurityDescriptorOwner; +import static com.jcraft.jsch.windowsapi.windows.win32.system.memory.Apis.CreateFileMappingA; +import static com.jcraft.jsch.windowsapi.windows.win32.system.memory.Apis.MapViewOfFile; +import static com.jcraft.jsch.windowsapi.windows.win32.system.memory.Apis.UnmapViewOfFile; +import static com.jcraft.jsch.windowsapi.windows.win32.system.systemservices.Constants.MAXIMUM_ALLOWED; +import static com.jcraft.jsch.windowsapi.windows.win32.system.systemservices.Constants.SECURITY_DESCRIPTOR_REVISION; +import static com.jcraft.jsch.windowsapi.windows.win32.system.threading.Apis.GetCurrentProcessId; +import static com.jcraft.jsch.windowsapi.windows.win32.system.threading.Apis.GetCurrentThreadId; +import static com.jcraft.jsch.windowsapi.windows.win32.system.threading.Apis.OpenProcess; +import static com.jcraft.jsch.windowsapi.windows.win32.system.threading.Apis.OpenProcessToken; +import static com.jcraft.jsch.windowsapi.windows.win32.ui.windowsandmessaging.Apis.FindWindowA; +import static com.jcraft.jsch.windowsapi.windows.win32.ui.windowsandmessaging.Apis.SendMessageA; +import static com.jcraft.jsch.windowsapi.windows.win32.ui.windowsandmessaging.Constants.WM_COPYDATA; + +public class PageantFFMConnector implements AgentConnector { + + private static final int AGENT_MAX_MSGLEN = 262144; + private static final long AGENT_COPYDATA_ID = 0x804e50baL; + + private final StructLayout errorStateLayout; + private final VarHandle getLastErrorVarHandle; + + public PageantFFMConnector() throws AgentProxyException { + if (!Util.getSystemProperty("os.name", "").startsWith("Windows")) { + throw new AgentProxyException("PageantFFMConnector only available on Windows."); + } + + try { + errorStateLayout = Linker.Option.captureStateLayout(); + getLastErrorVarHandle = + errorStateLayout.varHandle(MemoryLayout.PathElement.groupElement("GetLastError")); + + // Force class initialization to catch UnsatisfiedLinkError + Object foo = com.jcraft.jsch.windowsapi.windows.win32.foundation.Apis.CloseHandle$handle(); + foo = com.jcraft.jsch.windowsapi.windows.win32.security.Apis.CopySid$handle(); + foo = com.jcraft.jsch.windowsapi.windows.win32.system.memory.Apis.CreateFileMappingA$handle(); + foo = com.jcraft.jsch.windowsapi.windows.win32.system.threading.Apis + .GetCurrentProcessId$handle(); + foo = + com.jcraft.jsch.windowsapi.windows.win32.ui.windowsandmessaging.Apis.FindWindowA$handle(); + } catch (IllegalArgumentException | LinkageError e) { + throw new AgentProxyException(e.toString(), e); + } + } + + @Override + public String getName() { + return "pageant_ffm"; + } + + @Override + public boolean isAvailable() { + try (Arena arena = Arena.ofConfined()) { + MemorySegment errorState = arena.allocate(errorStateLayout); + MemorySegment pageant = arena.allocateFrom("Pageant", StandardCharsets.US_ASCII); + return !FindWindowA(errorState, pageant, pageant).equals(MemorySegment.NULL); + } + } + + @Override + public void query(Buffer buffer) throws AgentProxyException { + if (buffer.getLength() > AGENT_MAX_MSGLEN) { + throw new AgentProxyException("Query too large."); + } + + try (Arena arena = Arena.ofConfined()) { + MemorySegment sharedFile = MemorySegment.NULL; + MemorySegment mmva = null; + MemorySegment sharedMemory = MemorySegment.NULL; + MemorySegment errorState = arena.allocate(errorStateLayout); + MemorySegment pageant = arena.allocateFrom("Pageant", StandardCharsets.US_ASCII); + + MemorySegment hwnd = FindWindowA(errorState, pageant, pageant); + + if (hwnd.equals(MemorySegment.NULL)) { + throw new AgentProxyException("Pageant is not runnning."); + } + + MemorySegment usersid = getUserSid(arena, errorState); + MemorySegment psd = SECURITY_DESCRIPTOR.allocate(arena); + if (InitializeSecurityDescriptor(errorState, psd, SECURITY_DESCRIPTOR_REVISION) == 0) { + throw new AgentProxyException("Unable to InitializeSecurityDescriptor(): GetLastError() = " + + GetLastError(errorState)); + } + if (SetSecurityDescriptorOwner(errorState, psd, usersid, 0) == 0) { + throw new AgentProxyException( + "Unable to SetSecurityDescriptorOwner(): GetLastError() = " + GetLastError(errorState)); + } + + MemorySegment psa = SECURITY_ATTRIBUTES.allocate(arena); + SECURITY_ATTRIBUTES.nLength(psa, (int) SECURITY_ATTRIBUTES.sizeof()); + SECURITY_ATTRIBUTES.bInheritHandle(psa, 1); + SECURITY_ATTRIBUTES.lpSecurityDescriptor(psa, psd); + + String threadId = String.format(Locale.ROOT, "%08x%08x", GetCurrentProcessId(), + Thread.currentThread().threadId()); + MemorySegment mapname = + arena.allocateFrom("JSchPageantRequest" + threadId, StandardCharsets.US_ASCII); + + try { + sharedFile = CreateFileMappingA(errorState, INVALID_HANDLE_VALUE, psa, + PAGE_PROTECTION_FLAGS.PAGE_READWRITE, 0, AGENT_MAX_MSGLEN, mapname); + int lastError = GetLastError(errorState); + if (sharedFile.equals(MemorySegment.NULL)) { + throw new AgentProxyException( + "Unable to CreateFileMapping(): GetLastError() = " + lastError); + } + if (lastError == WIN32_ERROR.ERROR_ALREADY_EXISTS) { + throw new AgentProxyException("Shared file mapping already exists"); + } + + mmva = MapViewOfFile(arena, errorState, sharedFile, FILE_MAP.WRITE, 0, 0, 0); + sharedMemory = MEMORY_MAPPED_VIEW_ADDRESS.Value(mmva); + if (sharedMemory.equals(MemorySegment.NULL)) { + throw new AgentProxyException( + "Unable to MapViewOfFile(): GetLastError() = " + GetLastError(errorState)); + } + sharedMemory = sharedMemory.reinterpret(AGENT_MAX_MSGLEN); + + MemorySegment buf = MemorySegment.ofArray(buffer.buffer); + MemorySegment.copy(buf, 0, sharedMemory, 0, buffer.getLength()); + + MemorySegment cds = COPYDATASTRUCT.allocate(arena); + COPYDATASTRUCT.dwData(cds, AGENT_COPYDATA_ID); + COPYDATASTRUCT.cbData(cds, (int) mapname.byteSize()); + COPYDATASTRUCT.lpData(cds, mapname); + + long rcode = SendMessageA(errorState, hwnd, WM_COPYDATA, MemorySegment.NULL.address(), + cds.address()); + // Dummy read to make sure COPYDATASTRUCT isn't GC'd early + long foo = COPYDATASTRUCT.dwData(cds); + + buffer.rewind(); + if (rcode != 0) { + MemorySegment.copy(sharedMemory, 0, buf, 0, 4); // length + int i = buffer.getInt(); + if (i <= 0 || i > AGENT_MAX_MSGLEN - 4) { + throw new AgentProxyException("Illegal length: " + i); + } + buffer.rewind(); + buffer.checkFreeSize(i); + // checkFreeSize may have created a new array + buf = MemorySegment.ofArray(buffer.buffer); + MemorySegment.copy(sharedMemory, 4, buf, 0, i); + } else { + throw new AgentProxyException( + "SendMessage() returned 0 with cds.dwData: " + Long.toHexString(foo)); + } + } finally { + try { + if (!sharedMemory.equals(MemorySegment.NULL)) { + UnmapViewOfFile(errorState, mmva); + } + } finally { + if (!sharedFile.equals(MemorySegment.NULL)) { + CloseHandle(errorState, sharedFile); + } + } + } + } + } + + private MemorySegment getUserSid(Arena arena, MemorySegment errorState) + throws AgentProxyException { + MemorySegment proc = MemorySegment.NULL; + MemorySegment tok = MemorySegment.NULL; + + try { + proc = OpenProcess(errorState, MAXIMUM_ALLOWED, 0, GetCurrentProcessId()); + if (proc.equals(MemorySegment.NULL)) { + throw new AgentProxyException( + "Unable to OpenProcess(): GetLastError() = " + GetLastError(errorState)); + } + + MemorySegment ptok = arena.allocate(ValueLayout.ADDRESS); + if (OpenProcessToken(errorState, proc, TOKEN_ACCESS_MASK.TOKEN_QUERY, ptok) == 0) { + throw new AgentProxyException( + "Unable to OpenProcessToken(): GetLastError() = " + GetLastError(errorState)); + } + + tok = ptok.get(ValueLayout.ADDRESS, 0); + if (tok.equals(MemorySegment.NULL)) { + throw new AgentProxyException("ProcessToken is NULL"); + } + + MemorySegment ptoklen = arena.allocate(ValueLayout.JAVA_INT); + if (GetTokenInformation(errorState, tok, TOKEN_INFORMATION_CLASS.TokenUser, + MemorySegment.NULL, 0, ptoklen) == 0) { + int lastError = GetLastError(errorState); + if (lastError != WIN32_ERROR.ERROR_INSUFFICIENT_BUFFER) { + throw new AgentProxyException( + "Unable to GetTokenInformation() toklen: GetLastError() = " + lastError); + } + } + + long toklen = ptoklen.get(ValueLayout.JAVA_INT, 0) & 0xffffffffL; + if (toklen < TOKEN_USER.sizeof()) { + throw new AgentProxyException(String.format(Locale.ROOT, + "toklen (%d) < sizeof(TOKEN_USER) (%d)", toklen, TOKEN_USER.sizeof())); + } + + MemorySegment user = arena.allocate(toklen, ValueLayout.ADDRESS.byteAlignment()); + if (GetTokenInformation(errorState, tok, TOKEN_INFORMATION_CLASS.TokenUser, user, + (int) toklen, ptoklen) == 0) { + throw new AgentProxyException( + "Unable to GetTokenInformation() user: GetLastError() = " + GetLastError(errorState)); + } + + MemorySegment psid = SID_AND_ATTRIBUTES.Sid(TOKEN_USER.User(user)); + if (IsValidSid(psid) == 0) { + throw new AgentProxyException("IsValidSid() failed"); + } + + long sidlen = GetLengthSid(psid) & 0xffffffffL; + MemorySegment usersid = arena.allocate(sidlen, ValueLayout.ADDRESS.byteAlignment()); + if (CopySid(errorState, (int) sidlen, usersid, psid) == 0) { + throw new AgentProxyException( + "Unable to CopySid(): GetLastError() = " + GetLastError(errorState)); + } + + return usersid; + } finally { + try { + if (!tok.equals(MemorySegment.NULL)) { + CloseHandle(errorState, tok); + } + } finally { + if (!proc.equals(MemorySegment.NULL)) { + CloseHandle(errorState, proc); + } + } + } + } + + private int GetLastError(MemorySegment errorState) { + return (int) getLastErrorVarHandle.get(errorState, 0); + } +}