Skip to content

Start-Process: Fix -Credential error as non-admin - #21393

Merged
Dongbo Wang (daxian-dbw) merged 2 commits into
PowerShell:masterfrom
jborean93:process-cred
Apr 15, 2024
Merged

Start-Process: Fix -Credential error as non-admin#21393
Dongbo Wang (daxian-dbw) merged 2 commits into
PowerShell:masterfrom
jborean93:process-cred

Conversation

@jborean93

Copy link
Copy Markdown
Collaborator

PR Summary

When using Start-Process -Credential in a non-administrator context with a different user's credentials the code currently throws an exception when attempting to get the new process' handle. This change treats this error as a warning only if -PassThru was specified as it only affects certain members on the returned object like ExitCode but others like WaitForExit() still work fine.

Fixes: #21073

PR Context

The code to retrieve the Handle property was added by #20749 which tries to set the internal state of the Process object. This works in most cases but can fail if the user is not running as admin and the credentials specified was for another user. This PR turns the exception it raises about being unable to retrieve the handle to now become a warning as some things can still be used with the returned Process object.

This isn't a change in the status quo as going back to .NET Framework the returned Process object in the above scenario still could not do things like get the ExitCode due to permissions problems. This just ensures that an error/exception is not raised and code that worked in the back will still continue to do so.

The ultimately fix here is to start the process through the .NET System.Diagnostics.Process class instead of using PInvoke with CreateProcess* but until they add an API to create a suspended process or build the Process object from a SafeHandle PowerShell cannot do so for all scenarios it deals with

The PR should be considered for a backport to 7.4.x as that was when the change was also introduced.

PR Checklist

@jborean93

Copy link
Copy Markdown
Collaborator Author

Just as an FYI I did not create a test in this as it would require being able to create another user (requires admin) then run as non-admin. The test runner doesn't seem to have an easy way to really do this so I skipped it.

When using Start-Process -Credential in a non-administrator context with
a different user's credentials the code currently throws an exception
when attempting to get the new process' handle. This change treats this
error as a warning only if -PassThru was specified as it only affects
certain members on the returned object like ExitCode but others like
WaitForExit() still work fine.
@iSazonov

Copy link
Copy Markdown
Collaborator

Just as an FYI I did not create a test in this as it would require being able to create another user (requires admin) then run as non-admin. The test runner doesn't seem to have an easy way to really do this so I skipped it.

We could try to create a test hook. Keyword is InternalTestHooks. I don't know how much complexity is warranted, but a regression defense would be desirable.

@iSazonov Ilya (iSazonov) added CL-General Indicates that a PR should be marked as a general cmdlet change in the Change Log 7.4-regression Regression in 7.4 Backport-7.4.x-Consider labels Mar 30, 2024
@jborean93

Copy link
Copy Markdown
Collaborator Author

We could try to create a test hook. Keyword is InternalTestHooks

Isn't that to just tell PowerShell to do something differently internally? I'm not sure how that would help here as the exception occurs in .NET code and not PowerShell. Unless I'm mistaken?

The problem that we need to test is doing Start-Process -Credential $cred where $cred is another user's credentials. The trouble with setting this up is the test needs to run as non-admin but we need admin rights to create the user.

@iSazonov

Copy link
Copy Markdown
Collaborator

Test hooks are definitely something we do internally. It's kind of like having our own test Web server. And that's not easy to implement for processes. This effort makes sense if we want to actively develop this cmdlet.

On the other hand, I suppose we could use the GiHub action config to create a regular user with a single-session password. Or try net user /add inside the test. That would be easier than the test hook.

@jborean93

Jordan Borean (jborean93) commented Mar 31, 2024

Copy link
Copy Markdown
Collaborator Author

Yea the proper method would be to configure CI to create a test user for this (and other tests) as an admin before it starts pester as the non-admin user. I’ve got no experience with the CI setup in this repository and I probably don’t want to go fiddling around with it without someone from that side advising what they want to do there.

As for the test hook I can see if being useful if we want to test out specific code branches but in this case it’s an exception being raised in code that we are calling rather than in code that we control. It wouldn’t make sense to raise an exception ourselves here to just catch it IMO.

@iSazonov

Copy link
Copy Markdown
Collaborator

Interesting, .Net uses WindowsTestAccount to create a test user.
We could try to add this to our test common module. Then it would be easier for us to create tests for the Start-Process

@jborean93

Copy link
Copy Markdown
Collaborator Author

Creating the test user is the simple part with builtin cmdlets New-LocalUser. The hard part is getting the rights to do so in a non-admin context or running the test as admin an de-elevating for this particular scenario.

@iSazonov

Copy link
Copy Markdown
Collaborator

Creating the test user is the simple part with builtin cmdlets New-LocalUser.

I forget that we have Windows PowerShell. 😄

We run unelevated tests with
Start-UnelevatedProcess

PowerShell/build.psm1

Lines 1664 to 1682 in b23607e

function script:Start-UnelevatedProcess
{
param(
[string]$process,
[string[]]$arguments
)
if (-not $environment.IsWindows)
{
throw "Start-UnelevatedProcess is currently not supported on non-Windows platforms"
}
if (-not $environment.OSArchitecture -eq 'arm64')
{
throw "Start-UnelevatedProcess is currently not supported on arm64 platforms"
}
runas.exe /trustlevel:0x20000 "$process $arguments"
}

So it seems it is easy to create a test user account.

@jborean93

Jordan Borean (jborean93) commented Apr 1, 2024

Copy link
Copy Markdown
Collaborator Author

So it seems it is easy to create a test user account.

The question is more when to integrate it. We would need to create the test user before running the Pester process and pass through the credentials somehow. It also would need to cleanup the user once the test is done. I don’t feel comfortable enough without the people who maintain the CI part of this repo to provide their input. I’m also somewhat concerned that the talk to add tests will bog down the fix especially since it should be backported to the next 7.4 release.

@iSazonov

Copy link
Copy Markdown
Collaborator

I think this PR will be reviewed quickly as it is a regression.

Also, I think we can easily add (and remove) a user to build.psm1 in the try-catch-finally block that is listed above. The only requirement is to generate a password dynamically. It will be in a pwsh variable for the duration of the test.
Travis Plunk (@TravisEz13) Is this acceptable?

@daxian-dbw Dongbo Wang (daxian-dbw) added the WG-Cmdlets general cmdlet issues label Apr 1, 2024
@SteveL-MSFT Steve Lee (SteveL-MSFT) added the WG-NeedsReview Needs a review by the labeled Working Group label Apr 1, 2024

@daxian-dbw Dongbo Wang (daxian-dbw) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Code changes look good but need Cmdlet WG to review the behavior.

@iSazonov

Copy link
Copy Markdown
Collaborator

For WG. It seems we never write "warnings" to avoid "noise". So we could remove the "warning" at all or replace it with "verbose" or "debug".

{
_ = process.Handle;
}
catch (Win32Exception e)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we check the HRESULT for the specific error rather than treating them all the same?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

As getting the Handle was an attempt to try and set the internal state and not something that is being used in the code I didn't think it was worth setting a specific error code. I can change it but I didn't think it warranted catching a specific error because we aren't doing anything to work around the error.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Steve Lee (@SteveL-MSFT) Here we should rather move in the other direction. Since the only operation we do is getting a handle, we rather need to catch and ignore all exceptions, especially since if there is a question about behavior, we can always display the debug message.
Although I think Win32Exception covers everything that is needed.

@SteveL-MSFT

Steve Lee (SteveL-MSFT) commented Apr 3, 2024

Copy link
Copy Markdown
Member

The @PowerShell/wg-powershell-cmdlets discussed this and compared the behavior to WinPS5.1. We recommend changing from Warning to a Debug message, then handle is null w/o any message so the behavior is consistent with 5.1.

@SteveL-MSFT Steve Lee (SteveL-MSFT) added WG-Reviewed A Working Group has reviewed this and made a recommendation and removed WG-NeedsReview Needs a review by the labeled Working Group labels Apr 3, 2024
@jborean93

Copy link
Copy Markdown
Collaborator Author

Updated to become WriteDebug.

@microsoft-github-policy-service microsoft-github-policy-service Bot added the Review - Needed The PR is being reviewed label Apr 12, 2024
@daxian-dbw
Dongbo Wang (daxian-dbw) merged commit 588dff6 into PowerShell:master Apr 15, 2024
@microsoft-github-policy-service microsoft-github-policy-service Bot removed the Review - Needed The PR is being reviewed label Apr 15, 2024
@daxian-dbw Dongbo Wang (daxian-dbw) removed the 7.4-regression Regression in 7.4 label Apr 15, 2024
@jborean93
Jordan Borean (jborean93) deleted the process-cred branch April 15, 2024 18:58
@jborean93

Copy link
Copy Markdown
Collaborator Author

Thanks for the reviews and merge!

@adityapatwardhan

Copy link
Copy Markdown
Member

We will wait on this change to be released in a preview before considering a backport to 7.4.

@jborean93

Copy link
Copy Markdown
Collaborator Author

Will that even make a difference. There are people already affected by this and the chances of someone actually using the preview for such use cases I see as rare.

Chris Dent (chrisdent-de) pushed a commit to chrisdent-de/PowerShell that referenced this pull request Sep 12, 2024
Thatgfsj (Thatgfsj) pushed a commit to Thatgfsj/PowerShell that referenced this pull request Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backport-7.4.x-Done CL-General Indicates that a PR should be marked as a general cmdlet change in the Change Log WG-Cmdlets general cmdlet issues WG-Reviewed A Working Group has reviewed this and made a recommendation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PS v7.4.1 breaks passing credentials to start-process

5 participants