Skip to content

USERDOMAIN Environmental Variable Returns COMPUTERDOMAIN Value #2407

Closed
PowerShell/openssh-portable
#813

Description

@RamblingPSTech

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:


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 Entries

Environment 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.0

Version

OpenSSH_for_Windows_9.8p2 Win32-OpenSSH-GitHub, LibreSSL 4.0.0

Visuals

No response

Activity

  1. StevenBucher98 commented on Oct 27, 2025

    @StevenBucher98
    Collaborator

    Tess Gauthier (@tgauth) will take a look and investigate further, thanks for linking the previous issues that revive some of our old investigations 😄

  2. mgkuhn commented on Oct 27, 2025

    @mgkuhn
  3. mgkuhn commented on Oct 27, 2025

    @mgkuhn
  4. o-sdn-o commented on Oct 27, 2025

    @o-sdn-o

    USERDOMAIN=workgroup is specific to the Local System account.

  5. o-sdn-o commented on Oct 27, 2025

    @o-sdn-o

    I can't find in the source code

    It appears that the user's environment is not being constructed properly.

  6. o-sdn-o commented on Oct 28, 2025

    @o-sdn-o

    There's a clear understanding of what's happening:

    • The user profile is loaded correctly in 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:

      A pointer to an environment block for the new process. If this parameter is NULL, the new process uses the environment of the calling process.

    • 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.
  7. mgkuhn commented on Oct 28, 2025

    @mgkuhn
  8. o-sdn-o commented on Oct 28, 2025

    @o-sdn-o

    I can see code calling LoadUserProfile in contrib/win32/win32compat/win32_usertoken_utils.c, but only for user "sshd", and nothing calling UnloadUserProfile. So is this really being used?

    Are these sufficiently light-weight functions that should be called for each SSH login, or is it a too heavy/slow operation that might be appropriate for a GUI login, but not for a terminal session? What if there are multiple, concurrent SSH logins?

    LoadUserProfile is called in the sshd-session.exe child process for each connecting user.

  9. o-sdn-o commented on Oct 28, 2025

    @o-sdn-o

    To fix the issue under discussion, the following is required:

    • Use CreateEnvironmentBlock with the connected user's context token (we have one) to obtain the user's environment block (the LocalSystem context's environment block is currently being updated, which is incorrect).
    • Ensure that the updated block is passed to CreateProcessW, which launches the resulting user process, such as cmd.exe.

    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):

    • CreateEnvironmentBlock

      User-specific environment variables such as %USERPROFILE% are set only when the user's profile is loaded. To load a user's profile, call the LoadUserProfile function.

  10. o-sdn-o commented on Oct 28, 2025

    @o-sdn-o

    Markus Kuhn (@mgkuhn) However, LoadUserProfile is called for each user (except sshd)

    • std::strcmp:

      Return value

      • Negative value if lhs appears before rhs in lexicographical order.
      • Zero if lhs and rhs compare equal.
      • Positive value if lhs appears after rhs in lexicographical order.
    Image
  11. mgkuhn commented on Oct 28, 2025

    @mgkuhn

    LoadUserProfile is called for each user (except sshd)

    Ah, I misread.

  12. o-sdn-o commented on Oct 28, 2025

    @o-sdn-o
  13. o-sdn-o commented on Oct 29, 2025

    @o-sdn-o

    An open question is whether CreateEnvironmentBlock should inherit the environment block from the LocalSystem context when generating the user block, or whether the user environment block should be untouched before updating.

    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):

    Image
    • sshd.exe (LocalSystem) -> 1.sshd-session.exe (LocalSystem):
      • Add c:\program files\openssh; to PATH
    • 1.sshd-session.exe (LocalSystem) -> 2.sshd-session.exe (MEG\test):
      • Changed:
        EnvVar Old value New value
        APPDATA C:\WINDOWS\system32\config\systemprofile\AppData\Roaming C:\Users\test\AppData\Roaming
        LOCALAPPDATA C:\WINDOWS\system32\config\systemprofile\AppData\Local C:\Users\test\AppData\Local
        PATH ... ...
        USERNAME MEG$ test
        USERPROFILE C:\WINDOWS\system32\config\systemprofile C:\Users\test
      • Added:
        EnvVar Value
        HOME C:\Users\test
        HOMEDRIVE C:
        HOMEPATH \Users\test
        LOGNAME test
        PROMPT test@MEG $P$G
        SHELL c:\windows\system32\cmd.exe
        SSH_CLIENT ::1 52938 22
        SSH_CONNECTION ::1 52938 ::1 22
        TEMP C:\Users\test\AppData\Local\Temp
        TMP C:\Users\test\AppData\Local\Temp
        USER test
    • 2.sshd-session.exe (MEG\test) -> cmd.exe (MEG\test):
      • Added:
        EnvVar Value
        c28fc6f98a2c44abbbd89d6a3037d0d9_POSIX_FD_STATE AAAAAAICAs0=
  14. added a commit that references this issue on Oct 29, 2025
    13b1747
  15. RamblingPSTech commented on Oct 29, 2025

    @RamblingPSTech
    Author

    @RamblingPSTech Are you sure you mean USERPROFILE, which should be the path to my home directory, e.g.

    >echo %USERPROFILE%
    C:\Users\mgk25
    

    Do you not mean just USERDOMAIN in your examples? (Or perhaps USER or LOGNAME, which are formed of USERDOMAIN\USERNAME?)

    Yes, sorry, I did mean USERDOMAIN instead of USERPROFILE.


    Likewise, do you mean COMPUTERNAME instead of COMPUTERDOMAIN?

    https://ss64.com/nt/syntax-variables.html

    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.

  16. o-sdn-o commented on Oct 30, 2025

    @o-sdn-o

    After the fix in the PR I created, the USERDOMAIN environment variable is assigned the correct value:

    • 1.sshd-session.exe (LocalSystem) -> 2.sshd-session.exe (MEG\test):
      EnvVar Old value New value
      USERDOMAIN WORKGROUP MEG

    The behavior for all other environment variables remains the same as before applying the PR.

  17. o-sdn-o commented on Oct 30, 2025

    @o-sdn-o

    The following environment variables are set by CreateEnvironmentBlock:

    • "APPDATA"
    • "LOCALAPPDATA"
    • "USERPROFILE"
    • "USERNAME"
    • Variables from the user's registry subsection 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.

  18. o-sdn-o commented on Oct 30, 2025

    @o-sdn-o

    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.

  19. o-sdn-o commented on Oct 30, 2025

    @o-sdn-o

    The PR also fixes the following environment variables:

    • LOGONSERVER
    • USERDOMAIN_ROAMINGPROFILE
  20. mgkuhn commented on Oct 30, 2025

    @mgkuhn

    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.)

  21. o-sdn-o commented on Oct 30, 2025

    @o-sdn-o

    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.)

    If I remove (comment out in source code) the addition of the HOMEDRIVE and HOMEPATH variables, they are missing. Only HOME=C:\Users\test is present in the user session.

  22. mgkuhn commented on Oct 30, 2025

    @mgkuhn

    According to this Stackexchange answer, the variables HOMEDRIVE etc. are set by an undocumented function shell32!RegenerateUserEnvironment during Explorer initialization (which presumably happens after userinit.exe has loaded the user's profile and started the shell, following a GUI login).

    There are also functions CreateProcessWithTokenW and CreateProcessWithLogonW which “If the HOMEDRIVE and HOMEPATH variables 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.)

  23. o-sdn-o commented on Oct 30, 2025

    @o-sdn-o

    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.

  24. o-sdn-o commented on Oct 30, 2025

    @o-sdn-o

    I made (PowerShell/openssh-portable@7ab1648) the variables HOMEPATH and HOMEDRIVE to be set only if they are not assigned.

  25. added a commit that references this issue on Nov 4, 2025
    18133aa
  26. added a commit that references this issue on May 6, 2026
    2ce9a02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    ⌂ 换网址