Tuesday, 22 October 2013

How to use AppSense Environment Manager to reverse user restrictions for support purposes...and to generate executables to use as Triggers....and to make a shortcut run only with elevated rights

That's a hell of a title right there...but this little problem I've just been trying to rectify sent me down a path that I've wanted to cover for some time. Namely, how to create yourself "empty" executables that you can use as Process Started triggers - whether initiated post-logon, or even by user interaction. If you haven't come across this requirement yet, I will try and explain why it is a good idea, but first, let's look at the actual client issue that set me off down this route - and that also, like some sort of bonus feature, introduced a way to "force" shortcuts to run with elevated rights.

Supporting users mid-session

Most of you will be familiar with using Group Policy Objects (within Environment Manager, or without) to secure and tie down the applications and interface in your enterprise. However, one PITA part of this is trying to support logged-in users with the restrictions in place. I can remember this conundrum when I worked in support, and I bet most readers out there are familiar with it. How do you, as a support person, access the C: drive, for instance, when the user is restricted from getting into it via a GPO?

You could just get them to log out and log in as yourself, if the problem is specific to the machine, but sometimes the user might be midway through running some vital report, or it may just be too time-consuming to actually wait for them to log off or reboot - and you might just need to recreate and/or test the problem/solution as the user themselves, rather than running as an admin. What I used to do, back in my support days, was actually connect remotely to the Registry and delete the offending GPO entries, do my support work, and then run a gpupdate. However, this relies on a few things - a) the Remote Registry service, b) being sad enough to remember where the GPOs reside in the Registry, c) not coming across the GPO "tattooing" issue, and d) the assumption that a gpupdate would be enough to reinstate those restrictions (if you're deploying GPOs in EM, gpupdate won't help you put them back).


No local drives available - a real pain for a remote support person trying to troubleshoot a user session

The idea we had seemed elegant and easy enough to begin with - simply provide a shortcut that the user can initiate which allows the connecting administrator to reverse the restrictions in question. Simple?

Triggers, triggers, triggers

This is where it starts getting a bit interesting. As we all should know by now, AppSense Environment Manager has a wide variety of Triggers - Logon, Logoff, Process Started, Process Stopped, Network Connected/Disconnected, Session Locked/Unlocked, etc. and I even hear rumours there may be some additions to this in the future. If we are going to set up some sort of user- or admin-initiated Trigger for custom use, it appears quite clear that the most suitable one for usage is Process Started.


Process Started nodes - always handy

Now herein lies the issue. You could just use something extraneous like WordPad, but that has an associated user interface so would look pretty unprofessional and rubbish - even if you used EM to terminate it straight after launching. You could pick something from the OS that doesn't get used much and that won't display an interface (not for long, anyway) - maybe something legacy like nltest.exe - and use that, but that throws up some other problems. For starters, you'd have to whitelist the application through Application Manager (or other product), and do you really want users possibly inadvertently trying to reset secure channels, whether it is secured by privilege or not - and anyways, would your security team sit for it? And if Microsoft suddenly decided to deprecate nltest.exe, you'd end up back at square one anyway. You could maybe use a script and match the parameters for your Process Started node - but again, this feels messy and open to abuse. What we want is something that is smooth and compliant, and none of the above ideas really feels anything like this. Something that, maybe, generates a custom executable on the AppSense-enabled endpoint that doesn't actually do anything (besides compile and pause for a second or two), which we can then use as a Process Started Trigger?

Compiling executables on-the-fly?

Some of you out there are probably thinking that you've seen a solution like this in action from AppSense Professional Services, and you'd be right. In fact, without seeing this myself, I never would have thought of this as a solution, and would have ended up using some dire fudge like the methods I just discussed, so deserved credit has to go to the AppSense technical teams for bringing this concept to the table. The APS guys have some excellent code written by Jorrit van Eijk which does this job perfectly through PowerShell, no less, but that's his own work and I'm not going to stand on the shoulders of giants by stealing it - but big credit is due to him for coming up with this idea and successfully implementing it, so thanks for the inspiration Jorrit :-)

So, the idea is, we will create two custom executables on the endpoint that we can leverage in for support purposes. Why two? Well, once a support person has accessed the user endpoint, remotely or locally, they will launch one executable to instigate "admin mode", and a second to put the user restrictions back once finished. But obviously, before we can do this, we need to generate the two executables themselves, so that when we create shortcuts to them, the shortcuts will be valid.

Sidebar - other uses of this concept

Just taking a lateral step for a second, another excellent use of this concept is as a "Post-logon offload trigger". Sometimes you have Nodes within your Environment Manager configuration that take a long time to process, and you want these Actions processed at logon time - but they slow the logon down too much so it is unacceptable to the user. By creating a blank executable in this way, and putting a link to it in HKCU\Software\Microsoft\Windows\CurrentVersion\Run, you can then use a Process Stopped trigger to run the slow-processing Actions "after" logon has finished, as far as the user is concerned (i.e. after the icons have appeared), but still as part of the entire logon process. As we've already crammed three subjects into the title for this post, I will visit this concept in a later article, because as it stands I think I'm putting too much information into this one for starters :-)

Update - the full post on creating this new trigger is now written

Creating the blank executables

This is the really challenging bit! I'm no coder, not by a long stretch of the imagination, but this is where the power of community really comes to the fore. I will have to say thanks to Michael B Smith, Ken Schaefer, Manuel Santos and Aakash Shah over on the MyITForum NTSysAdm mailing list for the pointers and help they provided in this section.

Now, I had various suggestions about how to do this, but the most promising seemed to be to use either VBC.exe or CSC.exe (either the Visual Basic or C# .NET compilers), mostly because they seemed the simplest. I then made the informed choice to use C# because I was under the impression Visual Basic died years ago :-) So what I now required was a C# source file to go with the compiler, which could then spawn an executable onto the endpoint.

However, my idea with this was to make it have no dependence on the copying across of network-based files, so that it would be equally at home on a remote endpoint with the required configuration on it. At this point, though, I really need to point out that I've only tested this on x64 Windows 7 and Windows 2008 R2, so if you're trying to put this out to endpoints beyond that scope you will need to check and test thoroughly. And naturally, you should be checking and testing thoroughly in any case - I'm not going to be responsible for any screw-ups as a result of this and you can be sure ignoring due diligence will get you a frosty response from support, should you break something severely enough to invoke them. There, disclaimers are done, let's crack on ;-)

What we need to do is create a node to run at Computer Startup that will create the source file and then compile the executable. So, to begin, we will create a Node in this Trigger and perform a simple File Condition check - that is, to see if the file already exists or not




If it doesn't, what we need to do now is create the C# source file locally to the endpoint. How can we do this? Well, essentially the source file is a text file, and we need it to contain the following text, so that our executable simply pauses (for two seconds, but you can configure longer or shorter as necessary) and then exits

class rte
{
   static void Main() {
System.Threading.Thread.Sleep(2000);
   }
}

The trick is, creating the source file with the correct formatting - text is easy, and could have been done with some simple command echoing. But as is becoming something of a motif lately on this blog, it's PowerShell to the rescue. I've sometimes despaired of the decisions Microsoft make with regard to their products, but PowerShell brought some much-needed standardization and unification to command-line management of a vast array of software. We're going to leverage it again here, creating a Custom Action to create the source file for us (.cs extension) - note the text below has wrapped for readability, and the text after "$env:programfiles" is an entire line (see the image underneath if you don't understand what I mean)

$ProgramFilesPath = $env:programfiles
"class rte`r`n{`r`n`tstatic void Main() {`r`n`t`tSystem.Threading.Thread.Sleep(2000);`r`n`t}`r`n}" | Out-File$ProgramFilesPath\LaunchSupport.cs”

Or, alternatively, for those of you who like this less complex than I originally have put it together, you could use this bit of code from Michael B Smith, who has simplified this part quite a bit

$ProgramFilesPath = $env:programfiles
@”
class rte
{
                static void Main() {
                                System.Threading.Thread.Sleep(2000);
                }
}
”@ | Out-File "$ProgramFilesPath\LaunchSupport.cs"

Here's the screenshot, just to keep you right if you're using my first script


Also note that you will need to set the Options for the Custom Action as so


and also don't forget as we are writing to %ProgramFiles% which is a high-privilege operation, you will need to set the context to SYSTEM for the Action to execute in


So, now you have an Action that creates the source file, so now we need to call another Action - an Execute Action this time - that compiles the executable from the source file. Obviously, this will be dependent on the source file, so it should be nested inside the preceding Custom Action. The full path we are using is

%SystemRoot%\Microsoft.NET\Framework64\v4.0.30319\csc.exe /out:"%ProgramFiles%\AppSense\LaunchSupport.exe" /t:winexe "%ProgramFiles%\launchsupport.cs"

This may need changing slightly on different systems, but you get the gist, you call CSC from the .NET folders as relevant for your OS. You could stick it into the PATH variable if you want it to be really slick, but we haven't bothered to move it.



Note the parameters we are using for the Execute Action - /out tells us where to put the file, /t:winexe creates a Windows executable rather than a console executable, and the final one specifies the source file. Again, you will need to ensure that the Run As is set to SYSTEM


There's one final thing to do after this to tidy up, and that would be to remove the source file from the endpoint, keeping things neat and simple. Again, this Action should be nested under the preceding one to create a dependency



This would be as good a time as any to do a bit of testing, so we will save the configuration, deploy it to a test endpoint and then restart the machine (because we need to invoke the Computer Startup trigger)

And hey presto! We now have a new program in the AppSense folder. Just to note, there is a reason why I've chosen to dump these files in the %ProgramFiles%\AppSense folder, and that's because that location is allowed by default in Application Manager configurations, meaning there are no whitelist updates required.


Now, we did say we needed two executables - so let's repeat this process to create EndSupport.exe as well. Note that this is the same executable, it runs for two seconds then exits, just we are creating it with a different name.


Phew....that's this bit done then, the executables are created on-the-fly on our AppSense-enabled endpoints when they start up, there is no dependency on maintaining files in a file share anywhere, and they are allowed to execute by Application Manager without issue. What's next?

Creating shortcuts to the files (and making one Run As Admin)

Now for a much easier bit...creating shortcuts to these files for the user. Really I should have left this bit blank as an admin exercise for all of you out there ;-)

So some nice familiar Action creation, very straightforward - or is it?

Actually no. What we want is to create a shortcut, first of all, to turn on our "admin mode". But if this admin mode lets GPO restrictions be reversed, then surely we need some sort of protection to stop the user invoking it unsupervised? Kind of like - well, UAC. In fact that's exactly what we'll use.

If you're not using UAC, you will have to think of another way around this, but I simply can't see any reason not to use it these days, even on shared platforms. I often turn off the elevation for printer drivers, but that's about it - I think User Account Control is a great feature.

There are options for Run As Administrator on shortcut Properties, both on the Shortcut | Advanced tab and Compatibility tab


but there is a problem with this...they affect only the shortcut, leaving the underlying application free to be abused. What we need to do is adjust the program itself so that it always runs with UAC elevation on. The way to do this is through the Registry

First we create the shortcut itself on the Support Tools area of the Start Menu



and then we set a Registry value to ensure it always runs with the UAC elevation prompt



Note that this value is for Windows 7/2008 R2 only - on Windows 8 the value is now ~RUNASADMIN (thanks to Harry Mavromatidis for that tip)

We don't need UAC for the "End admin mode" shortcut, so we can create that normally



Reversing the restrictions

Next we need to use a Process Started trigger to reverse the restrictions that we've put in place. For purposes of this demonstration, we are reversing only a single Group Policy Object - the restriction on the C: drive (User Configuration | Admin Templates | Windows Components | Windows Explorer | Hide these specified drives in My Computer). However, in the real world, you will need to reverse each policy object that restricts your administrators. You can make this simpler by keeping all your applied GPOs in one node and then copy them to another, before editing them all to do the opposite of what you originally intended.

First we set up the Process Started node



And then simply create a dependent Action that reverses our target Group Policy Object



After this Action, we will need to "refresh" the desktop to force the new Group Policy settings to apply to the shell process. There's probably a better, more subtle way of doing this in code - please can someone point it out to me - but for the moment, I've resorted to using taskkill.exe to terminate and restart the explorer.exe process. This will necessitate two Actions (one to terminate, one to restart) and they need to be nested. Also, the Execute Action that runs taskkill.exe (the first one below) will need to Run As SYSTEM.





You will also need to whitelist taskkill.exe for your users from within Application Manager, if you're using it, after setting this up.

Also, MAKE SURE the second Execute Action (the one that relaunches Explorer) DOESN'T have "Do not execute children of this action until the process has exited" selected. This smells slightly bug-like, but if you select that I found that a Windows 7 machine wouldn't log off until I killed the process, so handle it with care.

After these Actions are set up, our whole configuration example should now look something like this



Are any of you wondering, as I was, whether the fact that the executable will be initially launched as a different user will affect how the Group Policy is applied in the first child Action? After all, the Group Policy object refers to an HKEY_CURRENT_USER setting, doesn't it? Well, I thought it might, but my testing reveals that the ADMX Action applies in the context of the logged-on user, so all is well and good. Whether this is a feature of Environment Manager or Group Policy or even UAC, it's certainly handy - I was envisaging having to drop a shortcut onto the Public desktop area, lock it down with NTFS, and then reverse the NTFS permissions to allow the user to see it. However, as it works in the current configuration, I can swiftly move on :-) (although if anyone from AppSense can explain why this behaviour is so, I'd certainly like to know)

Reinstating the restrictions

When the support person has finished doing whatever they need to do to support the user, obviously these restrictions need to go back. So we will need to create another Process Started node, this time pointing to our second executable that we dropped on the endpoint



Then we create a dependent Action that puts our original Group Policy Object(s) back




And then we do the same trick with explorer.exe to terminate it, which I won't bother showing the screenshots for, given that they are identical to the example in "Reversing the restrictions" above. But don't forget to run the first taskkill.exe Action as SYSTEM.

And now the configuration example should look like this



Now a few may have had the thought that these sets of Actions - "reverse restrictions" and "apply restrictions" - might be good to put in Reusable Nodes, so you don't have to keep calling them....but hold that thought. It's OK to do it like that - as long as you don't mix Computer and User Configuration settings in the same Reusable Node. I know this specifically deals with User settings - but I just thought I'd mention that in case anyone trips themselves up with it.

So let's recap what we've done so far. We've created our "empty" executables at Startup, we've created shortcuts on the Start Menu for them at logon time, set the first one to run with UAC elevation only, and then we have set up Process Started triggers for both of them to reverse and reinstate the restrictions of our GPOs when run (although this doesn't specifically have to be restricted to GPOs - you can override any settings, including permissions and Lockdown, with this method). Time to test?

Testing time!

Let's take the AppSense Bigot blog up a notch - let's do some video and bring ourselves with a bang into the 21st century :-) Actually this is just a quick and dirty video without commentary, but it's better than screenshots (at least it would be, if I could get my passwords right first time). Basically it demonstrates a user logging on to an RDS server who can't access the C: drive through My Computer - but then we reverse and then reinstate the C: drive restriction using our shortcuts and executables we covered above.





As you can (hopefully) see, it works a treat - although the termination of Explorer makes it a little choppy, but that's something a support user can easily put up with.

Summary

Wow....a lot of information covered in this post. Maybe a little too much...but I just wanted to get these interesting functions out there.

I will do a follow-up article - hopefully by next week - on the Post-logon Trigger I talked about, as that's a really useful function with quite a bit of mileage that a lot of you may find useful.

So, I hope you've learned some handy tricks here. Certainly forcing a shortcut to run UAC elevated and creating your own executables to use as Process Started/Stopped Triggers are two highly useful things. As to reversing the GPO restrictions - it was certainly a requirement at my current project but I'm not sure whether it will have a great amount of application at other places. However, it's another good demonstration of how to overcome specific client requirements using AppSense DesktopNow.

Credits

As I mentioned in the body of the post, massive credit has to go to Jorrit van Eijk of AppSense for coming up with the idea of spawning the blank executables, and for having code to do it that it is vastly better than my cack-handed implementation :-) And thanks to Michael B Smith, Ken Schaefer, Aakash Shah and Manuel Santos for offering me help with this whole solution and helping me with my rudimentary PowerShell!

2 comments:

  1. The compiler doesn't care about returns and new lines so you can drop the `r`n bits from the text making up the .cs code file.

    ReplyDelete
    Replies
    1. Thanks for pointing that out, the second example I used doesn't have the `r `n code in, which is the one I recommend people use if they wish to do this.

      Cheers,



      JR

      Delete