Reputation: 178
Scenario:
An administrator will install the application. The application has some kernel level operations so, it has to run with privileged mode. But the user does not have administrator credentials to run the application in elevated mode.
So, what are the best possibility to solve the above scenario.
Solution one (tried): While installing the application through administrator, we would create an admin where we know his user name and password. So, when the user tries to perform any operation, we will run the application as elevated mode using the functions processstartinfo() and process.start() in c#. But, the application runs in admin name, so the mapped drives for the user are not visible as this admin is not in the ACL list. So, this method is getting ruled out.
Can there be a solution where elevating the user it self as admin till the operation is completed using c# application?
I'm stuck and I don't find any articles to read upon on this.
Edit : At an enterprise level, the windows doesn't ask for admin creds for using bitlocker. So, we want to achieve the same type of functionality.
Is it possible?
Can anyone please help?
Thank you.
Upvotes: 10
Views: 3923
Reputation: 139276
What you can do is use COM+ Component Services. With .NET the easiest way is use Enterprise Services's ServicedComponent which has all sorts of wrappers and utility classes to interop with COM+ Component services.
So here are steps to do it:
1) Create a .NET Framework Class Library.
2) Add it a strong name and sign it with it
3) Add it a class like this for example (I've also put some utility method to diagnose things)
[ComVisible(true)]
public class AdminClass : ServicedComponent
{
public int DoSomethingAsAdmin()
{
// test something that a normal user shouldn't see
return Directory.GetFiles(Path.Combine(Environment.SystemDirectory, "config")).Length;
}
public string WindowsIdentityCurrentName => WindowsIdentity.GetCurrent().Name;
public string CurrentProcessFilePath => Process.GetCurrentProcess().MainModule.FileName;
// depending on how you call regsvcs, you can run as a 32 or 64 bit surrogate dllhost.exe
public bool Is64BitProcess => Environment.Is64BitProcess;
}
4) Add the following to AssemblyInfo.cs
[assembly: ApplicationName("AdminApp")]
[assembly: SecurityRole("AdminAppUser")]
[assembly: ApplicationActivation(ActivationOption.Server)]
What this does is define a COM+ application named "AdminApp", add a role named "AdminAppUser" to it, and declare the app will run as a "server" which means "out-of-process".
5) Compile that and run this command as admin
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\regsvcs.exe AdminApp.dll
or this command:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\regsvcs.exe AdminApp.dll
Both commands will create the the COM + application, and host the .NET library DLL in a surrogate .exe (dllhost.exe). If you choose the first, the hosted process will run as x64, and if you run the second, the hosted process will run as x86.
You can check the result of this registration if you run Component Services (from Windows/Run):
6) Right-click the app and you'll see a whole bunch of cool things you can configure. Note you can even run this as a service (in the 'Activation' tab), etc. What you must do is configure the identity which will run this process, something like this:
Here, I've used a custom admin account. You don't want to use any of the other builtin choices.
7) Now, since default security has been enabled, basically nobody can calls this component. So we just have to add a user to the role "AdminAppUser" we created earlier. You can of course do this using the UI as shown here:
but here is a piece of code that does this programmatically (we use the COM+ administration objects) :
AddUserInRole("AdminApp", "AdminAppUser", @"SMO01\simon");
....
static void AddUserInRole(string appName, string roleName, string userName)
{
dynamic catalog = Activator.CreateInstance(Type.GetTypeFromProgID("COMAdmin.COMAdminCatalog"));
// the list of collection hierarchy : https://learn.microsoft.com/en-us/windows/desktop/cossdk/com--administration-collections
var apps = catalog.GetCollection("Applications");
var app = GetCollectionItem(apps, appName);
if (app == null)
throw new Exception("Application '" + appName + "' was not found.");
var roles = apps.GetCollection("Roles", app.Key);
var role = GetCollectionItem(roles, roleName);
if (role == null)
throw new Exception("Role '" + roleName + "' was not found.");
// UsersInRole collection
// https://learn.microsoft.com/en-us/windows/desktop/cossdk/usersinrole
var users = roles.GetCollection("UsersInRole", role.Key);
var user = GetCollectionItem(users, userName);
if (user == null)
{
user = users.Add();
user.Value["User"] = userName;
users.SaveChanges();
}
}
static dynamic GetCollectionItem(dynamic collection, string name)
{
collection.Populate();
for (int i = 0; i < collection.Count; i++)
{
var item = collection.Item(i);
if (item.Name == name)
return item;
}
return null;
}
The result should be like this:
8) Now, for the client app, using the AdminApp facilities is easy. Don't reference the .DLL as a standard .NET reference, but use it as any other external COM component. You could reference the .TLB file that was created by regsvcs, or just use the magic dynamic keyword as I demonstrate here (the drawback is you don't get autocompletion):
using System;
using System.Security.Principal;
namespace UserApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Is64BitProcess " + Environment.Is64BitProcess);
Console.WriteLine("Running As " + WindowsIdentity.GetCurrent().Name);
var type = Type.GetTypeFromProgID("AdminApp.AdminClass");
dynamic trustedClass = Activator.CreateInstance(type);
Console.WriteLine("Admin App Process Path: " + trustedClass.CurrentProcessFilePath);
Console.WriteLine("Admin App Running As: " + trustedClass.WindowsIdentityCurrentName);
Console.WriteLine("Admin App Is64BitProcess: " + trustedClass.Is64BitProcess);
Console.WriteLine("Admin App DoSomethingAsAdmin: " + trustedClass.DoSomethingAsAdmin());
}
}
}
Now, when you run it for example as "simon", you should see something like this, it works:
Is64BitProcess False
Running As SMO01\simon
Admin App Process Path: C:\WINDOWS\system32\dllhost.exe
Admin App Running As: SMO01\myAdmin
Admin App Is64BitProcess: True
Admin App DoSomethingAsAdmin: 71
and when you run it for example as "bob" who's not configured in the role, you should see something like this with an access denied, this is expected:
Is64BitProcess False
Running As SMO01\bob
Unhandled Exception: System.UnauthorizedAccessException: Retrieving the COM class factory for component with CLSID {0DC1F11A-A187-3B6D-9888-17E635DB0974} failed due to the following error: 80070005 Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)).
at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
at System.Activator.CreateInstance(Type type, Boolean nonPublic)
at System.Activator.CreateInstance(Type type)
at UserApp.Program.Main(String[] args) in C:\Users\simon\source\repos\TrustedSystem\UserApp\Program.cs:line 14
Note we've created a trusted system without setting any password anywhere. And, I've only scratched the surface of what you can do with COM+ component. For example, you can export the app as an .MSI for easy deployment, etc.
Upvotes: 0
Reputation:
You could try creating a parallel thread that has admin privileges in order to execute the kernel operations. This way your whole program is contained in one executable.
Upvotes: 0
Reputation: 12898
I think you might be stuck because you are dealing with two functions that have permissions features, and both are required to properly run your application.
I have noted that you have these two:
To illustrate this...
| File System Path | Application Process
User | OK | --
Admin | -- | OK
The solution needs to look like this...
| File System Path | Application Process
Service | OK | OK
If possible, I would say this is the most straight-forward way, whereby you create a service account that has permissions to do both.
Upvotes: 0
Reputation: 804
The solution that I did was to separate the program into two parts. One part runs as a service that is run using admin privileges and the other part runs using normal privileges. The communication between the two programs can run via Ethernet or using shared memory.
Upvotes: 3