Skip to content
Draft
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
64 changes: 63 additions & 1 deletion protocols/docs/NOW-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
TOC is generated in [Obsidian](obsidian.md) via
[TOC plugin](https://github.com/hipstersmoothie/obsidian-plugin-toc)
-->
# NOW-PROTO 1.6
# NOW-PROTO 1.7
- [Messages](#messages)
- [Transport](#transport)
- [Message Syntax](#message-syntax)
Expand Down Expand Up @@ -46,6 +46,7 @@ TOC is generated in [Obsidian](obsidian.md) via
- [NOW_EXEC_BATCH_MSG](#now_exec_batch_msg)
- [NOW_EXEC_WINPS_MSG](#now_exec_winps_msg)
- [NOW_EXEC_PWSH_MSG](#now_exec_pwsh_msg)
- [Elevated Execution](#elevated-execution)
- [RDM Messages](#rdm-messages)
- [NOW_RDM_MSG](#now_rdm_msg)
- [NOW_RDM_CAPABILITIES_MSG](#now_rdm_capabilities_msg)
Expand Down Expand Up @@ -328,6 +329,7 @@ increment major version; Protocol implementations with different major version a
| NOW_CAP_EXEC_STYLE_PWSH<br>0x0020 | PowerShell 7 (.ps1) execution style. |
| NOW_CAP_EXEC_UNICODE_CONSOLE<br>0x0040 | Host supports encoding control flags (RAW_ENCODING, UNICODE_CONSOLE, and ENCODING_UTF8). |
| NOW_CAP_EXEC_IO_REDIRECTION<br>0x1000 | Set if host implements exec session IO redirection. |
| NOW_CAP_EXEC_ELEVATE_SHELL<br>0x0080 | Set if host can elevate an exec session using the platform shell. Elevation may prompt the interactive user for consent, and IO redirection is unavailable for elevated sessions. See [Elevated Execution](#elevated-execution). |

<!-- TODO: add AppleScript command -->

Expand Down Expand Up @@ -956,6 +958,7 @@ packet-beta
| Flag | Meaning |
|----------------------------------------|---------------------------|
| NOW_EXEC_FLAG_RUN_DIRECTORY_SET<br>0x0001 | `directory` field contains non-default value. |
| NOW_EXEC_FLAG_RUN_ELEVATED<br>0x0002 | Execute the command with elevated privileges. The elevation mechanism is selected by the server and advertised in `execCapset`; see [Elevated Execution](#elevated-execution). |

**sessionId (4 bytes)**: A 32-bit unsigned integer containing a unique remote execution session id.

Expand Down Expand Up @@ -993,6 +996,7 @@ packet-beta
| NOW_EXEC_FLAG_PROCESS_PARAMETERS_SET<br>0x0001 | `parameters` field contains non-default value. |
| NOW_EXEC_FLAG_PROCESS_DIRECTORY_SET<br>0x0002 | `directory` field contains non-default value.|
| NOW_EXEC_FLAG_PROCESS_ENCODING_UTF8<br>0x0004 | Enables OEM-to-UTF-8 transcoding for stdin, stdout, and stderr. Without this flag, data streams are passed through as raw bytes without encoding conversion. |
| NOW_EXEC_FLAG_PROCESS_ELEVATED<br>0x0008 | Execute the command with elevated privileges. The elevation mechanism is selected by the server and advertised in `execCapset`; see [Elevated Execution](#elevated-execution). |
| NOW_EXEC_FLAG_PROCESS_IO_REDIRECTION<br>0x1000 | Enable stdio (stdout, stderr, stdin) redirection. |
| NOW_EXEC_FLAG_PROCESS_DETACHED<br>0x8000 | Detached mode: the process is started without tracking execution or sending back output. |

Expand Down Expand Up @@ -1035,6 +1039,7 @@ packet-beta
|----------------------------------------|---------------------------|
| NOW_EXEC_FLAG_SHELL_SHELL_SET<br>0x0001 | `shell` field contains non-default value. |
| NOW_EXEC_FLAG_SHELL_DIRECTORY_SET<br>0x0002 | `directory` field contains non-default value. |
| NOW_EXEC_FLAG_SHELL_ELEVATED<br>0x0008 | Execute the command with elevated privileges. The elevation mechanism is selected by the server and advertised in `execCapset`; see [Elevated Execution](#elevated-execution). |
| NOW_EXEC_FLAG_SHELL_IO_REDIRECTION<br>0x1000 | Enable stdio (stdout, stderr, stdin) redirection. |
| NOW_EXEC_FLAG_SHELL_DETACHED<br>0x8000 | Detached mode: the shell is started without tracking execution or sending back output. |

Expand Down Expand Up @@ -1077,6 +1082,8 @@ packet-beta
| NOW_EXEC_FLAG_BATCH_DIRECTORY_SET<br>0x0001 | `directory` field contains non-default value. |
| NOW_EXEC_FLAG_BATCH_RAW_ENCODING<br>0x0002 | Disables the default OEM-to-UTF-8 transcoding: data streams are passed through as raw bytes without any encoding conversion. |
| NOW_EXEC_FLAG_BATCH_UNICODE_CONSOLE<br>0x0004 | Enables Unicode console: agent injects `@chcp 65001 > nul` and writes the script in BOM-less UTF-8. Implies UTF-8 stdout/stderr streams. |
| NOW_EXEC_FLAG_BATCH_ELEVATED<br>0x0008 | Execute the command with elevated privileges. The elevation mechanism is selected by the server and advertised in `execCapset`; see [Elevated Execution](#elevated-execution). |
| NOW_EXEC_FLAG_BATCH_NO_EXIT<br>0x0010 | Keeps the command interpreter running after the batch file completes (`cmd /K` rather than `/C`). MUST be ignored when the session redirects stdio, where the hidden interpreter would never exit. |
| NOW_EXEC_FLAG_BATCH_IO_REDIRECTION<br>0x1000 | Enable stdio (stdout, stderr, stdin) redirection. |
| NOW_EXEC_FLAG_BATCH_DETACHED<br>0x8000 | Detached mode: the batch is started without tracking execution or sending back output. |

Expand Down Expand Up @@ -1132,6 +1139,7 @@ packet-beta
| NOW_EXEC_FLAG_PS_DIRECTORY_SET<br>0x0100 | `directory` field contains non-default value and specifies command working directory |
| NOW_EXEC_FLAG_PS_RAW_ENCODING<br>0x0200 | Disables the default OEM-to-UTF-8 transcoding: data streams are passed through as raw bytes without any encoding conversion. |
| NOW_EXEC_FLAG_PS_UNICODE_CONSOLE<br>0x0400 | Enables Unicode console: agent injects `$OutputEncoding = [Console]::InputEncoding = [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()` at script start. Implies stdin/stdout/stderr streams are UTF-8. |
| NOW_EXEC_FLAG_PS_ELEVATED<br>0x0800 | Execute the command with elevated privileges. The elevation mechanism is selected by the server and advertised in `execCapset`; see [Elevated Execution](#elevated-execution). |
| NOW_EXEC_FLAG_PS_IO_REDIRECTION<br>0x1000 | Enable stdio (stdout, stderr, stdin) redirection. |
| NOW_EXEC_FLAG_PS_SERVER_MODE<br>0x2000 | Run PowerShell in server mode. |
| NOW_EXEC_FLAG_PS_DETACHED<br>0x8000 | Detached mode: PowerShell is started without tracking execution or sending back output. |
Expand Down Expand Up @@ -1196,6 +1204,56 @@ packet-beta

**configurationName (variable)**: A NOW_VARSTR structure, same as with NOW_EXEC_WINPS_MSG.

#### Elevated Execution

Any exec message may request elevated execution by setting its `NOW_EXEC_FLAG_*_ELEVATED` flag.
The flag expresses *intent* only: it never names a mechanism, and the client does not choose one.

`ELEVATED` raises the privileges of the identity that would otherwise run the command, which is the
session user. It does not select a different identity, and it carries no credentials. Running a
command as another user is a separate concern and would be expressed by a separate field.

**Requesting elevation.** A client SHOULD NOT set an `ELEVATED` flag unless the server advertised
at least one `NOW_CAP_EXEC_ELEVATE_*` capability. A server that receives an `ELEVATED` flag without
having advertised any elevation capability MUST fail the session with `NOW_CODE_NOT_IMPLEMENTED`.
A server predating this version ignores the flag entirely and executes without elevation, so a
client MUST NOT treat the absence of a failure as evidence that elevation took place: capability
negotiation is the only reliable signal, and a client is expected to refuse the request locally
when no elevation capability was advertised.
A server MUST NOT execute a command without elevation after being asked to elevate it: silently
downgrading the request denies the client any way to detect that privileges were not granted.

**Mechanism selection.** The server selects the mechanism and advertises it in `execCapset`. Only
`NOW_CAP_EXEC_ELEVATE_SHELL` is defined at present. A mechanism with different observable
properties, such as one that raises no consent prompt or preserves stdio redirection, is expected to
be advertised as an additional capability rather than by changing the meaning of an existing one, so
a client always learns what to expect before it sends a request.

**Capability scope.** An elevation capability states that the server implements elevation, not that
every execution style it advertises can be elevated. A server MAY support elevation for some styles
and not others, and MUST fail a request for a style it cannot elevate rather than executing that
request without elevation. A client MUST therefore be prepared for a per-style failure even when an
elevation capability was advertised.

**Semantics under NOW_CAP_EXEC_ELEVATE_SHELL.** The server elevates through the platform shell,
which on Windows raises a consent prompt on the interactive user's desktop:

- A client SHOULD omit `NOW_EXEC_FLAG_*_IO_REDIRECTION` when requesting elevation, and MUST NOT
assume the server honours it. The server MUST NOT open stdio channels for the session unless it
advertises a capability stating that elevated sessions keep their stdio.
- Unless the request uses `NOW_EXEC_RUN_MSG`, which is never tracked, or sets
`NOW_EXEC_FLAG_*_DETACHED`, which promises no tracking, the server SHOULD still track the
session and report the real exit code in `NOW_EXEC_RESULT_MSG`, so a client can distinguish
success from failure without stdio.
- `NOW_EXEC_ABORT_MSG` and `NOW_EXEC_CANCEL_REQ_MSG` MAY fail with `NOW_CODE_NOT_IMPLEMENTED`,
because the elevated child runs at a higher integrity level than the server's session process.
- Elevation MAY require interactive consent, so the request MAY fail when no user is present to
grant it, or when the user declines.

**Failure reporting.** A server that cannot grant elevation MUST report it distinctly rather than
succeeding: the account has no elevated token available, the user declined consent, or elevation is
not implemented for the requested exec style.

### RDM Messages

#### NOW_RDM_MSG
Expand Down Expand Up @@ -1484,3 +1542,7 @@ packet-beta
- Add `NOW_EXEC_FLAG_PROCESS_ENCODING_UTF8` flag for process exec commands.
- Add `NOW_EXEC_FLAG_*_UNICODE_CONSOLE` flags for batch (cmd), winps, and pwsh exec commands.
- Add `NOW_CAP_EXEC_UNICODE_CONSOLE` capability flag.
- 1.7
- Add `NOW_EXEC_FLAG_*_ELEVATED` flags for run, process, shell, batch, winps, and pwsh exec messages.
- Add `NOW_CAP_EXEC_ELEVATE_SHELL` capability flag.
- Add `NOW_EXEC_FLAG_BATCH_NO_EXIT` flag for batch exec commands.
6 changes: 6 additions & 0 deletions protocols/dotnet/Devolutions.NowClient/src/AExecParams.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ public StartedHandler OnStarted
}
}

/// <summary>
/// Whether the caller asked for elevated execution. Inspected by <see cref="NowClient"/>
/// so a request cannot be sent to a server that never advertised an elevation capability.
/// </summary>
internal bool IsElevated { get; set; }

internal ExecSession ToExecSession(uint sessionId, ChannelWriter<IClientCommand> commandWriter)
{
return new ExecSession(
Expand Down
31 changes: 31 additions & 0 deletions protocols/dotnet/Devolutions.NowClient/src/ExecBatchParams.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,26 @@ public ExecBatchParams UnicodeConsole(bool enable = true)
return this;
}

/// <summary>
/// Keep the command interpreter open after the batch file completes (`cmd /K` rather than
/// `/C`). Ignored by the server when the session redirects stdio.
/// </summary>
public ExecBatchParams NoExit(bool enable = true)
{
_noExit = enable;
return this;
Comment on lines +61 to +64
}

/// <summary>
/// Execute with elevated privileges. Requires the host to advertise an elevation
/// capability; under shell-based elevation the session has no IO redirection.
/// </summary>
public ExecBatchParams Elevated(bool enable = true)
{
IsElevated = enable;
return this;
}

internal NowMsgExecBatch ToNowMessage(uint sessionId)
{
var builder = new NowMsgExecBatch.Builder(sessionId, command);
Expand Down Expand Up @@ -83,6 +103,16 @@ internal NowMsgExecBatch ToNowMessage(uint sessionId)
builder.EnableUnicodeConsole();
}

if (IsElevated)
{
builder.EnableElevated();
}

if (_noExit)
{
builder.EnableNoExit();
}

return builder.Build();
}

Expand All @@ -91,5 +121,6 @@ internal NowMsgExecBatch ToNowMessage(uint sessionId)
private bool _detached = false;
private bool _rawEncoding = false;
private bool _unicodeConsole = false;
private bool _noExit = false;
}
}
17 changes: 16 additions & 1 deletion protocols/dotnet/Devolutions.NowClient/src/ExecProcessParams.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Devolutions.NowProto.Messages;
using Devolutions.NowProto.Messages;

namespace Devolutions.NowClient
{
Expand Down Expand Up @@ -54,6 +54,16 @@ public ExecProcessParams EncodingUtf8(bool enable = true)
return this;
}

/// <summary>
/// Execute with elevated privileges. Requires the host to advertise an elevation
/// capability; under shell-based elevation the session has no IO redirection.
/// </summary>
public ExecProcessParams Elevated(bool enable = true)
{
IsElevated = enable;
return this;
}

internal NowMsgExecProcess ToNowMessage(uint sessionId)
{
var builder = new NowMsgExecProcess.Builder(sessionId, filename);
Expand Down Expand Up @@ -83,6 +93,11 @@ internal NowMsgExecProcess ToNowMessage(uint sessionId)
builder.EnableEncodingUtf8();
}

if (IsElevated)
{
builder.EnableElevated();
}

return builder.Build();
}

Expand Down
15 changes: 15 additions & 0 deletions protocols/dotnet/Devolutions.NowClient/src/ExecPwshParams.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ public ExecPwshParams UnicodeConsole(bool enable = true)
return this;
}

/// <summary>
/// Execute with elevated privileges. Requires the host to advertise an elevation
/// capability; under shell-based elevation the session has no IO redirection.
/// </summary>
public ExecPwshParams Elevated(bool enable = true)
{
IsElevated = enable;
return this;
}

internal NowMsgExecPwsh ToNowMessage(uint sessionId)
{
var builder = _serverMode
Expand Down Expand Up @@ -193,6 +203,11 @@ internal NowMsgExecPwsh ToNowMessage(uint sessionId)
builder.EnableUnicodeConsole();
}

if (IsElevated)
{
builder.EnableElevated();
}

return builder.Build();
}

Expand Down
15 changes: 15 additions & 0 deletions protocols/dotnet/Devolutions.NowClient/src/ExecRunParams.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ public ExecRunParams Directory(string directory)
return this;
}

/// <summary>
/// Execute with elevated privileges. Requires the host to advertise an elevation
/// capability; under shell-based elevation the session has no IO redirection.
/// </summary>
public ExecRunParams Elevated(bool enable = true)
{
IsElevated = enable;
return this;
}

internal NowMsgExecRun ToNowMessage(uint sessionId)
{
var builder = new NowMsgExecRun.Builder(sessionId, command);
Expand All @@ -27,6 +37,11 @@ internal NowMsgExecRun ToNowMessage(uint sessionId)
builder.Directory(_directory);
}

if (IsElevated)
{
builder.EnableElevated();
}

return builder.Build();
}

Expand Down
15 changes: 15 additions & 0 deletions protocols/dotnet/Devolutions.NowClient/src/ExecShellParams.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ public ExecShellParams Detached(bool enable = true)
return this;
}

/// <summary>
/// Execute with elevated privileges. Requires the host to advertise an elevation
/// capability; under shell-based elevation the session has no IO redirection.
/// </summary>
public ExecShellParams Elevated(bool enable = true)
{
IsElevated = enable;
return this;
}

internal NowMsgExecShell ToNowMessage(uint sessionId)
{
var builder = new NowMsgExecShell.Builder(sessionId, command);
Expand All @@ -70,6 +80,11 @@ internal NowMsgExecShell ToNowMessage(uint sessionId)
builder.EnableDetached();
}

if (IsElevated)
{
builder.EnableElevated();
}

return builder.Build();
}

Expand Down
15 changes: 15 additions & 0 deletions protocols/dotnet/Devolutions.NowClient/src/ExecWinPsParams.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,16 @@ public ExecWinPsParams UnicodeConsole(bool enable = true)
return this;
}

/// <summary>
/// Execute with elevated privileges. Requires the host to advertise an elevation
/// capability; under shell-based elevation the session has no IO redirection.
/// </summary>
public ExecWinPsParams Elevated(bool enable = true)
{
IsElevated = enable;
return this;
}

internal NowMsgExecWinPs ToNowMessage(uint sessionId)
{
var builder = _serverMode
Expand Down Expand Up @@ -194,6 +204,11 @@ internal NowMsgExecWinPs ToNowMessage(uint sessionId)
builder.EnableUnicodeConsole();
}

if (IsElevated)
{
builder.EnableElevated();
}

return builder.Build();
}

Expand Down
Loading