Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Investigate] - Detecting elevated processes. #1015

Closed
crutkas opened this issue Dec 26, 2019 · 22 comments
Closed

[Investigate] - Detecting elevated processes. #1015

crutkas opened this issue Dec 26, 2019 · 22 comments
Assignees
Labels
Area-Runner The PowerToys main executable Cost-Medium Medium work item - 1-3 Days worth of work. Resolution-Fix Committed Fix is checked in, but it might be 3-4 weeks until a release.

Comments

@crutkas
Copy link
Member

crutkas commented Dec 26, 2019

Can we detect if applications are running elevated and suggest PT needs to be relaunched elevated if they want to interact? This would keep us in more secure state.

Background:
Since PowerToys itself is a process, it can only do things at or below the same 'privilege'. If PowerShell or Terminal is launched as an admin and PT isn't, PT can't interact with those windows.

Why:

@crutkas crutkas added Needs-Community Feedback We need additional help with how something should act / look Area-Runner The PowerToys main executable labels Dec 26, 2019
@crutkas crutkas added this to the 20.02 release milestone Dec 26, 2019
@ghost
Copy link

ghost commented Dec 26, 2019

As a suggestion, its possible by using PowerShell script. Source: Detecting if a certain process is elevated

I have tested this script on local machine and it seems like it reports everything correctly, but only from non-elevated PowerShell (I tried PowerShell Core as well as Native version).

Script itself is pretty short:
Get-Process | Add-Member -Name IsElevated -MemberType ScriptProperty -Value {if ($this.Name -in @('Idle','System')) {$null} else {-not $this.Path -and -not $this.Handle} } -PassThru | Format-Table Name, IsElevated

@crutkas
Copy link
Member Author

crutkas commented Dec 26, 2019

Now the question becomes how do we do this programmatically. :)

@ghost
Copy link

ghost commented Dec 26, 2019

Now the question becomes how do we do this programmatically. :)

Actually, its not complicated and if using c# we can go with something like in this one:
Executing PowerShell scripts from C#

@enricogior
Copy link
Contributor

enricogior commented Dec 26, 2019

Running a PowerShell script every time the current active window changes is something I would consider unacceptable.
Also the script doesn't use an API designed to return in a reliable manner if the process is elevated, it's a hack that relies on a guess.

@crutkas
Copy link
Member Author

crutkas commented Dec 26, 2019

just brainstorming, hence this is an investigation step. How do we monitor and how do we not spam the OS to see if something is in an elevated state (aka polling every 10 seconds or something)

to me, i think the big thing we need to detect is the user's environment in a state we can't work properly in. If it is, we should prompt them and go "hey, we see some stuff is running as admin, some stuff may not work correctly until that process is ended or we are also elevated"

And if then detect no more processes aren't elevated, we should say "hey friend, we detected there is no longer a reason for us to run as admin, we're going to drop down to a normal process".

This could be a setting, auto detect and raise/lower your permission as needed. This could also be a setting of something like

  • "Detect and prompt"
  • "Detect and automatically relaunch as admin"
  • "Run in user mode"
  • "Always run elevated"

@enricogior
Copy link
Contributor

enricogior commented Dec 26, 2019

Detect and automatically relaunch as admin

I suspect this will open the door to scenarios where the execution time of PowerToys actions may be unacceptable, for example:

  • User enables Move newly created windows to their last known zone
  • user lunches Terminal and moves it to a zone
  • user closes Terminal and relaunches it as Admin
  • FZ detects a new app is running elevated and so it has to quit and restart
  • while FZ is restarting it can't move the Terminal window to its last known zone, it has to wait until it's fully restarted.

@ghost
Copy link

ghost commented Dec 26, 2019

I think we should not relaunch anything, #1016, but instead create some kind of grabber that runs elevated and manages apps that runs on high level, it will be used just in case if PT is not running as admin.

I bet its possible to run processes as elevated from non-elevated app.
That's where we should warn users that something may not work if they wont run this grabber.

As result we don't need to relaunch PT, we have grabber that deals with elevated apps, we ask users if they want to run that grabber. And we don't ask anything if PT itself is running elevated.

@enricogior
Copy link
Contributor

create some kind of grabber

Can you explain what the grabber functionalities are?
What does it mean "manages apps that runs on high level"?
"we have grabber that deals with elevated apps": what is the things that it "deals"?

@yuyoyuppe
Copy link
Collaborator

yuyoyuppe commented Dec 27, 2019

I would imagine that dragging an elevated window should trigger a one-time (or with a couple of days cooldown) notification to the user inviting her to read a detailed explanation why she might want to run PT elevated. An average user imho won't care when exactly in time we've decided to drop the privileges, so as long as she understands that we do the right thing the dropping privileges notification might be unnecessary.

Regarding the isolation discussion in #942, it'd be great to run each PT in a separate process, which we might refactor for in the future starting from #1016. As @enricogior stated, that would be a pretty fundamental change.

For now, as @alexr3 proposed, we could extract the part which does the actual window moving/resizing into a separate elevated process and send the commands to it. I've also traced which of the API functions fail during the elevated window moving/resizing, and it looks like the only one that fails with ERROR_ACCESS_DENIED is the actual SetWindowPlacement, so we could IPC the func name and its call params to that elevated process ("grabber"/"placer"). That way surely minimizes the amount of code running in an elevated context according to secure development lifecycle (SDL) but has an added complexity for IPC ceremony (perhaps we could just use local COM RPC), maybe @henrygab can weight-in on that.

Implementation-wise, we can determine if a process is elevated using the usual GetTokenInformation from a non-elevated context like this:

#define PSAPI_VERSION 1

#include <Windows.h>
#include <shlwapi.h>

#pragma comment( lib, "Psapi.lib" )

#include <stdio.h>
#include <tchar.h>
#include <psapi.h>

// To obtain a HWND's PID GetWindowThreadProcessId should suffice.
void PrintPidAndElevation(DWORD processID)
{
  HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
    FALSE, processID);

  HANDLE token = nullptr;
  bool elevated = false;

  if(OpenProcessToken(hProcess, TOKEN_QUERY, &token)) {
    TOKEN_ELEVATION elevation;
    DWORD size;
    if(GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &size)) 
      elevated = (elevation.TokenIsElevated != 0);
  }
  _tprintf(TEXT("PID: %u %s\n"), processID, elevated? L"elevated" : L"normal");

  CloseHandle(token);
  CloseHandle(hProcess);
}

int main(void)
{
  DWORD aProcesses[1024], cbNeeded, cProcesses;
  unsigned int i;

  if(!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
    return 1;

  cProcesses = cbNeeded / sizeof(DWORD);

  for(i = 0; i < cProcesses; i++)
  {
    if(aProcesses[i] != 0)
      PrintPidAndElevation(aProcesses[i]);
  }

  return 0;
}

Finally, we're not showing the zones when shift-dragging an elevated window when the fancyzones_shiftDrag is on due to UIPI, which prevents us obtaining the shift state here. That could also be solved by detecting whether the dragged window is elevated.

@enricogior
Copy link
Contributor

@yuyoyuppe these are all very useful points when evaluating changes to the current design vs complete new architecture.
It'a also important to not focus just on FancyZones requirements with regard to moving/resizing window, but add the lowlevel keyboard handler in the picture because it has its own set of requirements that may need a different approach compared to moving/resizing elevated windows.

@yuyoyuppe
Copy link
Collaborator

@enricogior agree, we might investigate whether it's possible to also integrate lowlevel keyboard handler with this imagined fine-grained elevated local COM server process which will communicate via COM RPC with all the different non-elevated toys.

@bzoz
Copy link
Contributor

bzoz commented Dec 27, 2019

Adding a separate elevated process to pull the strings sounds unnecessary complicated. Having each powertoy run in a separate process has its own benefits and would be simpler to implement.

@yuyoyuppe
Copy link
Collaborator

yuyoyuppe commented Dec 27, 2019

@bzoz I agree, though we'll have to send all events to subscribers using IPC anyway in case of separate processes if we still want to keep the "hooks architecture". Abandoning it will result in a simplest architecture and probably won't affect performance that much.

@ghost
Copy link

ghost commented Dec 27, 2019

Can you explain what the grabber functionalities are?

As @yuyoyuppe explained its some kind of module or part of a PT. While I am not good at COM and slightly understand possible implications using such technology I imagined this module as the one that calls functions or is executing commands which involve elevated processes. In case the target process is not elevated existing code would be used.

Primary function of this module would be execution of code which involves interaction with elevated processes.

That's was my initial vision, but maybe there is better approach to this.

@enricogior
Copy link
Contributor

@alexr3
thanks for the explanation.

@enricogior
Copy link
Contributor

I agree with @bzoz and @yuyoyuppe, I'd rather have fully separate processes with complete autonomy from each other.
The runner, as it is now will basically be replaced by the Settings and the Settings will run non elevated (there will be another discussion on how to actually implement the Settings if we adopt WinUI).

@crutkas
Copy link
Member Author

crutkas commented Dec 27, 2019

Remember, this is an investigation, I think there are a lot of things we may want to adjust. I was thinking about a lot of different ways we COULD do things.

I actually think #938 (documenting what needs elevated priv, when, and why) needs to happen before this as we can start talking about this and the upcoming utilities for what makes them need to run as admin and in what scenarios. @enricogior, we have lots of stuff that will share components and need think how those interact with one another along with if one runs at admin and the other doesn't, what happens. That is why i think #938 is a early item to knock out

We are still early days for this project. This is something to think about for next release and see what the impacts will be. Breaking changes could happen. Architectural changes may happen once we start seeing new scenarios.

@enricogior
Copy link
Contributor

@crutkas
Bartosz has already a PR ready with the new architecture!

JK ;)

@henrygab
Copy link

henrygab commented Jan 6, 2020

I agree with @crutkas … the first step is documenting what needs elevation, when, and why.

Once that is agreed upon, it will provide the common foundation for discussions on solutions, such as determining if a solution would work for each item or not.

[[ Note: I am participating in my free time, in a personal capacity only ]]

@enricogior
Copy link
Contributor

@henrygab
the issue to track each module requirements #938

@enricogior enricogior added Cost-Medium Medium work item - 1-3 Days worth of work. and removed Needs-Community Feedback We need additional help with how something should act / look labels Mar 17, 2020
@yuyoyuppe yuyoyuppe added the Status-In progress This issue or work-item is under development label Mar 23, 2020
@yuyoyuppe
Copy link
Collaborator

yuyoyuppe commented Mar 23, 2020

@crutkas How do we want to show a warning that a window can't be dragged because its process is elevated? To avoid notification spam, we could show a toast notification e.g. once a day. We could also have a "Never show again" button on it.

@enricogior
Copy link
Contributor

The fix is now available in 0.16 https://github.com/microsoft/PowerToys/releases

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Runner The PowerToys main executable Cost-Medium Medium work item - 1-3 Days worth of work. Resolution-Fix Committed Fix is checked in, but it might be 3-4 weeks until a release.
Projects
None yet
Development

No branches or pull requests

5 participants