Repository navigation
Description
Environment: Windows Server in workgroup mode (not domain joined)
When logged into a Windows OS via Console, Remote Desktop, WinRM session, the %USERPROFILE% ($env:USERPROFILE) %USERDOMAIN% ($env:USERDOMAIN) variable contains the value of the computername (i.e. WIN-1DVQ9PPCF1L) - CORRECT
When logged into a Windows OS via SSH (direct SSH & PowerShell session over SSH), the %USERPROFILE% ($env:USERPROFILE) %USERDOMAIN% ($env:USERDOMAIN) variable contains the value of the workgroup name (i.e. "WORKGROUP") - INCORRECT
Processes that use a syntax of ${env(USERDOMAIN)}\${system_username} to reference a security principal fail when using a remote SSH session, but work with WinRM sessions due to the inconsistent USERDOMAIN value
Related Issues:
- Permissions repair PowerShell scripts do not work in an ssh session. #1877
- installer fails during remote session EnterpriseDB/edb-installers#336
PowerShell Session Over WinRM
PS C:\> $pssessionoption = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
PS C:\> Enter-PSSession -SessionOption $PSSessionOption -ComputerName 192.168.0.10 -Credential (Get-Credential) -UseSSL
PowerShell credential request
Enter your credentials.
User: administrator
Password for user administrator: *********
[192.168.0.10]: PS C:\Users\Administrator\Documents> $env:userdomain
WIN-1DVQ9PPCF1L
PowerShell Session Over SSH
PS C:\> Enter-PSSession -HostName 192.168.0.10 -IdentityFilePath ~\.ssh\id_ecdsa -UserName administrator
[administrator@192.168.0.10]: PS C:\Users\Administrator\Documents> $env:userdomain
WORKGROUP
Expected behavior
Within Remote SSH session to Windows OS:
Command (cmd.exe): `echo %USERDOMAIN%`
Output: `WIN-1DVQ9PPCF1L` (current remote computer name)
Command (pwsh): `$env:userdomain`
Output: `WIN-1DVQ9PPCF1L` (current remote computer name)Actual behavior
Within Remote SSH session to Windows OS:
Command (cmd.exe): `echo %USERDOMAIN%`
Output: `WORKGROUP` (current NETBIOS workgroup name)
Command (pwsh): `$env:userdomain`
Output: `WORKGROUP` (current NETBIOS workgroup name)Error details
`${env(USERDOMAIN)}\${system_username}` is used by some processes to add the current user to the accesss control list after disabling inheritance and clearing existing access control entries.
WORKGROUP\USERNAME results in an invalid user SID mapping causing the Access Control List to have no Access Control EntriesEnvironment data
PSVersion 7.5.4
PSEdition Core
GitCommitId 7.5.4
OS Microsoft Windows 10.0.26100
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0Version
OpenSSH_for_Windows_9.8p2 Win32-OpenSSH-GitHub, LibreSSL 4.0.0
Visuals
No response
Activity
StevenBucher98 commentedon Oct 27, 2025
Tess Gauthier (@tgauth) will take a look and investigate further, thanks for linking the previous issues that revive some of our old investigations 😄
mgkuhn commentedon Oct 27, 2025
mgkuhn commentedon Oct 27, 2025
o-sdn-o commentedon Oct 27, 2025
USERDOMAIN=workgroup is specific to the Local System account.
o-sdn-o commentedon Oct 27, 2025
It appears that the user's environment is not being constructed properly.
o-sdn-o commentedon Oct 28, 2025
There's a clear understanding of what's happening:
openssh-portable\contrib\win32\win32compat\win32_usertoken_utils.c:818(the first child ssd-session.exe of sshd.exe)openssh-portable\contrib\win32\win32compat\w32fd.c:1108:CreateProcessAsUserW launches child processes (the second child ssd-session.exe) with a variable block from the LocalSystem context:
openssh-portable\contrib\win32\win32compat\w32-doexec.c:106:The LocalSystem envvar block is updated, instead of the user envvar block. It may be necessary to load the user environment block using CreateEnvironmentBlock and then update it.
openssh-portable\contrib\win32\win32compat\w32fd.c:1112:The updated user environment block should then be passed to CreateProcessW.
mgkuhn commentedon Oct 28, 2025
o-sdn-o commentedon Oct 28, 2025
LoadUserProfile is called in the sshd-session.exe child process for each connecting user.
o-sdn-o commentedon Oct 28, 2025
To fix the issue under discussion, the following is required:
It seems that LoadUserProfile is not necessary for each user, the only thing that is required is to ensure that variables such as %USERPROFILE% are updated (this is already done here):
o-sdn-o commentedon Oct 28, 2025
Markus Kuhn (@mgkuhn) However, LoadUserProfile is called for each user (except
sshd)std::strcmp:mgkuhn commentedon Oct 28, 2025
Ah, I misread.
o-sdn-o commentedon Oct 28, 2025
o-sdn-o commentedon Oct 29, 2025
Apparently, simple inheritance at the last moment before launching cmd.exe isn't suitable. Perhaps the user environment block needs to be loaded before launching the second instance of sshd-session.exe (
2.sshd-session.exe):sshd.exe (LocalSystem)->1.sshd-session.exe (LocalSystem):c:\program files\openssh;to PATH1.sshd-session.exe (LocalSystem)->2.sshd-session.exe (MEG\test):2.sshd-session.exe (MEG\test)->cmd.exe (MEG\test):Fix user environment block construction.
RamblingPSTech commentedon Oct 29, 2025
Yes, sorry, I did mean USERDOMAIN instead of USERPROFILE.
I did mean the NetBios domain name or netbios workgroup name of the computer when referencing "COMPUTERDOMAIN" in the issue title. I realize COMPUTERDOMAIN is not an environmental variable that is set.
o-sdn-o commentedon Oct 30, 2025
After the fix in the PR I created, the
USERDOMAINenvironment variable is assigned the correct value:1.sshd-session.exe (LocalSystem)->2.sshd-session.exe (MEG\test):The behavior for all other environment variables remains the same as before applying the PR.
o-sdn-o commentedon Oct 30, 2025
The following environment variables are set by CreateEnvironmentBlock:
HKEY_CURRENT_USER\Environment.Accordingly, I removed (in PowerShell/openssh-portable@6d83d89) their repeated assignment in the
setup_session_user_vars(pw_dir_w)function. I left only the"HOMEDRIVE","HOMEPATH"and"PATH"assignments. Constructing the"PATH"variable as before - loading it from the registry.o-sdn-o commentedon Oct 30, 2025
We construct the PATH variable from scratch because CreateEnvironmentBlock grabs its contents from the LocalSystem context due to inheritance. We have to use inheritance because using CreateEnvironmentBlock without inheritance breaks the transfer of the current session state to the child process.
o-sdn-o commentedon Oct 30, 2025
The PR also fixes the following environment variables:
LOGONSERVERUSERDOMAIN_ROAMINGPROFILEmgkuhn commentedon Oct 30, 2025
Why does sshd still need to assign HOMEDRIVE and HOMEPATH itself? They seem part of the normal user environment (outside sshd) when I start cmd.exe in the GUI. (I'm just trying to understand what is different now for environment variables between a GUI login and an SSH login.)
o-sdn-o commentedon Oct 30, 2025
If I remove (comment out in source code) the addition of the
HOMEDRIVEandHOMEPATHvariables, they are missing. OnlyHOME=C:\Users\testis present in the user session.mgkuhn commentedon Oct 30, 2025
According to this Stackexchange answer, the variables
HOMEDRIVEetc. are set by an undocumented functionshell32!RegenerateUserEnvironmentduring Explorer initialization (which presumably happens afteruserinit.exehas loaded the user's profile and started the shell, following a GUI login).There are also functions CreateProcessWithTokenW and CreateProcessWithLogonW which “If the
HOMEDRIVEandHOMEPATHvariables are not set, [will modify] the environment block to use the drive and path of the user's working directory”. (The latter sounds more like a fallback.)o-sdn-o commentedon Oct 30, 2025
I noticed that when connecting as a user who has the graphical shell (explorer.exe) running, the HOMEDRIVE and HOMEPATH variables are present by default. If you connect as a user on whose behalf there are no graphical shells running in the system, then these variables are absent.
o-sdn-o commentedon Oct 30, 2025
I made (PowerShell/openssh-portable@7ab1648) the variables HOMEPATH and HOMEDRIVE to be set only if they are not assigned.
Fix user environment block construction (#813)
Fix user environment block construction (PowerShell#813)