Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ The Windows Remote Management (WinRM) Java Client is a library that enables to:
> `WqlQuery.getSelectedProperties()`/`getSubPropertiesMap()` are now **unmodifiable views**
> (and `WinRMWqlExecutor` copies the lists passed to its constructor): callers that mutated
> the returned collections must now copy them first.
> * The remote transfer directory used by `upload(...)`/`localFileToCopyList` is now
> **`<windir>\Temp\winrm-upload-<CLIENT-COMPUTER-NAME>`** instead of the
> `SEN_ShareFor_<CLIENT-COMPUTER-NAME>$` name kept from the old SMB implementation. Directories
> left behind by earlier versions are neither reused nor cleaned up (they can simply be
> deleted), and hosts hardened with per-directory ACLs must grant write access on the new path.
> For consumers of the public `WindowsTempShare.getOrCreateShare(...)` API, the created SMB
> share (and the returned UNC path) follows the same rename — and without the trailing `$` the
> share is **no longer hidden** from network browsing. The generated unique file names of
> `WindowsRemoteProcessUtils.buildNewOutputFileName()` changed from `SEN_...` to `winrm-...`
> accordingly.

## Prerequisites on the target host

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public static Charset getWindowsEncodingCharset(
*/
public static String buildNewOutputFileName() {
return String.format(
"SEN_%s_%d_%d",
"winrm-%s-%d-%d",
Utils.getComputerName(),
Utils.getCurrentTimeMillis(),
(long) (Math.random() * 1000000)
Expand Down
7 changes: 4 additions & 3 deletions src/main/java/org/metricshub/winrm/WindowsTempShare.java
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ static String buildCreateRemoteDirectoryCommand(final String remotePath) {
static String buildPathOnCluster(final String path) {
Utils.checkNonNull(path, "path");

return String.format("%s\\Temp\\SEN_TempFor_%s", path, Utils.getComputerName());
return String.format("%s\\Temp\\winrm-upload-%s", path, Utils.getComputerName());
}

/**
Expand All @@ -234,12 +234,13 @@ static String buildRemotePath(final String folder, final String shareName) {
}

/**
* Build the Share name with the computer name.
* Build the name of the per-client-machine transfer directory (also used as the share name
* in the legacy SMB flow) with the computer name.
*
* @return The share name.
*/
static String buildShareName() {
return String.format("SEN_ShareFor_%s$", Utils.getComputerName());
return String.format("winrm-upload-%s", Utils.getComputerName());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Document the public share-name change in README

For consumers of the public WindowsTempShare.getOrCreateShare(...) API, this changes both the created SMB share/returned UNC name and its visibility by dropping the trailing $; however, README.md was not updated, and the migration guide still promises that WindowsTempShare is unchanged. Add an upgrade notice explaining this compatibility change and the new visible share name, as required for end-user-facing changes.

AGENTS.md reference: AGENTS.md:L25-L27

Useful? React with 👍 / 👎.

}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ private WinRMCommandExecutor() {}
* "CSCRIPT c:\\MyScript.vbs", null, "remote-srv", null, null, null, 30000, Arrays.asList("c:\\MyScript.vbs"), false);
* </code>
* This will copy <b>c:\\MyScript.vbs</b> to <b>remote-srv</b>, typically in
* <b>C:\\Windows\\Temp\\SEN_ShareFor_MYHOST</b> and the command that is executed will therefore
* <b>C:\\Windows\\Temp\\winrm-upload-MYHOST</b> and the command that is executed will therefore
* become:
* <code>CSCRIPT "C:\\Windows\\Temp\\SEN_ShareFor_MYHOST\\MyScript.vbs"</code>
* <code>CSCRIPT "C:\\Windows\\Temp\\winrm-upload-MYHOST\\MyScript.vbs"</code>
*
* @param command The command to execute. (Mandatory)
* @param protocol The HTTP protocol (HTTP by default)
Expand Down
11 changes: 6 additions & 5 deletions src/site/markdown/file-transfers.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ With **transfer-and-run**, files are copied to a per-client-machine transfer dir
remote host:

```text
<windir>\Temp\SEN_ShareFor_<CLIENT-COMPUTER-NAME>$
<windir>\Temp\winrm-upload-<CLIENT-COMPUTER-NAME>
```

* `<windir>` is discovered on the remote host with the WQL query
Expand All @@ -50,15 +50,16 @@ remote host:
directory. Clients that report the same computer name (cloned machines, containers) share one —
which is safe, because the content-addressed file names below prevent them from ever
overwriting each other's payloads; they simply also share the cache and the 30-day cleanup.
The name and the trailing `$` are kept from the pre-2.0.0 SMB implementation, which used this
directory as a hidden share — no share is created anymore.
Versions before 2.0.0 used `<windir>\Temp\SEN_ShareFor_<CLIENT-COMPUTER-NAME>$`, exposed as a
hidden SMB share — no share is created anymore, and a directory left behind by an older
version is neither reused nor cleaned up: it can simply be deleted.
* The directory is created if missing (`IF NOT EXIST ... MKDIR ...`).

Inside that directory the remote file name is **content-addressed**: a 12-hex-digit fragment of
the file's SHA-256 digest is inserted before the extension:

```text
collect.vbs → <windir>\Temp\SEN_ShareFor_MYHOST$\collect.1a2b3c4d5e6f.vbs
collect.vbs → <windir>\Temp\winrm-upload-MYHOST\collect.1a2b3c4d5e6f.vbs
```

Because the name identifies the content, two files with the same name but different content get
Expand Down Expand Up @@ -143,7 +144,7 @@ path, and the result is executed through `CMD.EXE /C (...)`:
```text
given: CSCRIPT c:\scripts\collect.vbs /debug
uploads: c:\scripts\collect.vbs
executes: CMD.EXE /C (CSCRIPT C:\Windows\Temp\SEN_ShareFor_MYHOST$\collect.1a2b3c4d5e6f.vbs /debug)
executes: CMD.EXE /C (CSCRIPT C:\Windows\Temp\winrm-upload-MYHOST\collect.1a2b3c4d5e6f.vbs /debug)
```

Notes:
Expand Down
2 changes: 1 addition & 1 deletion src/site/markdown/preparing-the-host.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ separately:
| --- | --- |
| **WQL queries** (`client.wql(...)`) | Remote access to the listener, access to the **WMI plug-in**, and rights on the target **WMI namespace** (`ROOT\CIMV2` by default) — plus whatever the queried class itself demands. |
| **Remote commands** (`client.command(...)`) | Remote access to the listener, remote shell access on the host (`AllowRemoteShellAccess`, `True` by default), and whatever rights **the command itself** needs once it runs. |
| **Transfer-and-run** (`upload(...)`) | Both of the above, plus write access to `<windir>\Temp\SEN_ShareFor_<CLIENT>$`, and `certutil` and `forfiles` present on the host. See [File Transfers](file-transfers.html). |
| **Transfer-and-run** (`upload(...)`) | Both of the above, plus write access to `<windir>\Temp\winrm-upload-<CLIENT>`, and `certutil` and `forfiles` present on the host. See [File Transfers](file-transfers.html). |
| **`uploadFile(...)`** to an explicit path | Remote shell access, write access to the destination directory, and `certutil` on the host (the same transfer engine, minus the transfer directory and its `forfiles` housekeeping). |

So an account can perfectly well run WQL queries and fail to run commands, or the reverse. When
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/org/metricshub/winrm/ShellFileCopyTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ void rejectsFileNamesUnsafeForTheCommandShell() {

@Test
void buildUploadCommandsChunksBelowTheCommandLineLimit() {
final String base64File = expectedRemoteDirectory() + "\\big.bin.SEN_X_1_2.b64";
final String base64File = expectedRemoteDirectory() + "\\big.bin.winrm-X-1-2.b64";
final byte[] content = new byte[15000];
for (int i = 0; i < content.length; i++) {
content[i] = (byte) i;
Expand Down Expand Up @@ -450,7 +450,7 @@ void buildsContentAddressedRemoteNames() {
// The explicit bound derived from the directory keeps the COMPLETE staging path (with
// the ".<unique>.part.b64" suffixes) within the traditional Windows MAX_PATH limit,
// even for the longest allowed (64-character) client computer name
final String longDirectory = "C:\\Windows\\Temp\\SEN_ShareFor_" + "h".repeat(64) + "$";
final String longDirectory = "C:\\Windows\\Temp\\winrm-upload-" + "h".repeat(64);
final int budget = ShellFileCopy.maxRemoteNameLength(longDirectory);
final String bounded = ShellFileCopy.contentAddressedName("x".repeat(300) + ".vbs", content, budget);
assertTrue(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ void executesTheQueryThroughTheRealProtocolStack() throws Exception {
enqueueEnumeration(
server,
instance("Win32_Share", "Name", "C$", "Path", "C:\\"),
instance("Win32_Share", "Name", "SEN_ShareFor_PC$", "Path", "C:\\Windows\\Temp\\SEN_ShareFor_PC$")
instance("Win32_Share", "Name", "winrm-upload-PC", "Path", "C:\\Windows\\Temp\\winrm-upload-PC")
);

final WinRMWqlExecutor actual = executeWql(
Expand All @@ -133,7 +133,7 @@ void executesTheQueryThroughTheRealProtocolStack() throws Exception {
// Headers keep the order of the WQL SELECT clause, and each row is mapped onto it
assertEquals(asList("Name", "Path"), actual.getHeaders());
assertEquals(
asList(asList("C$", "C:\\"), asList("SEN_ShareFor_PC$", "C:\\Windows\\Temp\\SEN_ShareFor_PC$")),
asList(asList("C$", "C:\\"), asList("winrm-upload-PC", "C:\\Windows\\Temp\\winrm-upload-PC")),
actual.getRows()
);
assertTrue(actual.getExecutionTime() >= 0);
Expand Down
Loading