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);
+ }
+}