I'm testing a small utility program. It behaves differently depending on whether it's being run with administrative rights or not. Therefore, my test framework needs to invoke the program both with and without admin rights, to check that it behaves appropriately.
The test framework is written in C#. Our CI server will run the test at midnight each day, so it's no good popping up access confirmations or similar; there won't be a human being there to press the button or type in credentials. I would prefer to avoid having to store the administrator's password if possible.
Where do I start with this? It looks like ProcessStartInfo can take a username, domain and password - but that would require the admin password to be compiled into the test framework. (Like that will work on more than one PC!) I've also seen answers that say something about WindowsIdentity and impersonation - but that seems to be to change the user ID of the current process, not the external process that I'm trying to invoke.
What about this:
Your test framework expects the username/password for the admin rights via command line parameters
your CI server calls this with the parameters - at least there you have to store the password. Any non-interactive solution demands the storage of the password somewhere
Your test framework will spawn the application then with ProcessStartInfo.
At least the password is not stored in code anymore, which makes it better to manage and not to leak it beyond AdminĀ“s scope ;)
BR Florian
Related
I'm writing an installer wrapper for a Windows C# Service. The installer is written in C# but needs to execute Powershell commands. As part of the installation I need to either get the current user's credentials, or have the user provide credentials. I need to use a PowerShell for the service installation (using New-Service), but I need to provide the credentials as a PSCredential object.
Such an object can be returned by Get-Credentials, but I can't invoke this command without accessing the PS Host UI, which I don't seem to be able to do.
I can do this the lazy way, and throw up a custom dialog asking for a username and password, and cram those in to a new PSCredential, but that is very bad practice.
I can also get these credentials by PInvoking my way through CredUIPromptForWindowsCredentials, but again, this is extremely kludgy and eventually results in a plain text password in memory which I'd like to avoid.
Surely there is some intermediate? I'm running this interaction from a WPF form, so I can happily present the user with a credentials dialog, but I don't know how to do this in a sensible way.
It would also be nice if there was a secure way to create a service and pass through the currently logged in user as the "run as" user.
Simply put, I want to create a Windows service that runs as a specified user. How do I do this programmatically in C#?
Any advice would be appreciated.
It is a bit unclear of what you want to do.
Are you using powershell inside c# code or powershell only, to install the c# code?
Are you searching for a solution in powershell or c#?
Are you asking for a solution on password security or on secure service creation?
This (https://learn.microsoft.com/en-us/dotnet/api/system.security.securestring?view=net-7.0) c# class: "Represents text that should be kept confidential, such as by deleting it from computer memory when no longer needed. This class cannot be inherited."
I would use this (https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/convertto-securestring?view=powershell-7.3) for powershell.
I know this is a bit basic, but I'll be eternally grateful if someone could set me straight on this.
I have a bunch of Windows 2012 R2 servers running Hyper-V.
An XML-RPC web service runs on these servers. There is some hosted Powershell behind this. It works perfectly.
The hosted Powershell code runs via impersonation. Again, works great.
Even though these machines have always been in a domain, XML-RPC authentication has worked like this:
I have used a period (".") for domain.
I have used the username "Administrator".
I have used a distinct password per 'local' administrator account.
I have recently added clustering functionality to my application, this is just in the form of some of wrappers for Microsoft's Powershell cmdlets, plus my own stuff.
If I read the dcos correctly, clustering can only be administered with domain credentials. I want my web service to run under a single set of credentials (to simplify the details the calling application needs to store).
I assumed I would wiggle the authentication model like this:
Have a domain user for each Hyper-V host.
Ensure that this user was a member of the local admin and hyper-v administrator groups.
Add these users to a 'clustered hyper-v hosts' group, give this group "clustering permissions' through the clustering administration tools.
My problems:
When running the tweaked model my code has exploded and no longer works, lots of authentication errors, I can sit and see things hitting the registry and being denied.
If I pick a host at random, log on to it with this new domain user (which - to be clear- is a member of the local admin group) and run my code without the xml-rpc wrapper, I seen lots of authentication error. It is as if this user is not running with administrative credentials, even though it has these permissions.
These users also seem to have restricted clustering permissions, even though they should have full access.
If I run the 'test-cluster' command, it will tell me I do not have permissions on the host I am running it on.
I am willing to admit I have an broken directory, or genuine access problems here, but perhaps I have an inherent misunderstanding of how this is supposed to work.
I am a Linux engineer and programmer, I know a bit of Windows. I am a little confused as how UAC is working in this context, in a domain environment, when I am not logged on as the true administrator account...
If I right click a Powershell window and 'run as' administrator, my code all appears to work, even the clustering stuff (which I am lead to believe, through docs, should not run as a local admin).
Am I hitting some kind of elevation issue here? If a domain user has 'effective permissions' (hopefully that term is not confusing things) to do some stuff, does it still have to be elevated in some way?
Thanks very much.
Change UAC to the lowest level(Never Notify), reboot and try again.
I faced similar issue with runing powershell from imperosnated C# application.
I used PowerShell Community Extensions https://pscx.codeplex.com/ to list active privileges inside PowerShell script.
Add this snipet to your PS script:
Import-Module Pscx
Get-Privilege
Let it run with working and non-working credentials, compare those lists of privileges.
If you find some privileges disabled (but not missing), try to enable them in such way:
Import-Module Pscx
$setPriv = new-object Pscx.Interop.TokenPrivilegeCollection
$setPriv.Enable('SeTcbPrivilege')
$setPriv.Enable('SeDebugPrivilege')
Set-Privilege -Privileges $setPriv
I have built an application using C# which accesses the registry and installation folder for read/write information. A normal user having limited privileges is getting an error while accessing/writing the information (in registry or installation folder).
Is there a way in which all types of users are able to run that application smoothly?
It is possible, although it's not completely straightforward.
You're gonna have to impersonate another user (who, in turn, has to have all the required privileges).
Check this question for details: Windows Impersonation from C#
There are two ways, the simple and complex. First - install the program per user rather than per computer. Second - to write a service that will operate under the privileged user and perform the necessary procedures (accessing/writing the information) for your application.
I am using C# and .Net Framework 4.
I am looking for a foolproof method to get the login id of the currently logged in windows user that is not susceptible to impersonation or hacking. I am looking for this in the form of: DOMAINNAME\USERNAME
e.g. SOMEDOMAIN\JohnDoe
Currently the best I have is:
var identity = System.Security.Principal.WindowsIdentity.GetCurrent();
var currentLoginId = identity.Name;
Is this open to impersonation (outside of someone knowing both the username and password) and if so is there a better way of doing this?
There can be at least four different identities involved at this point:
The identity assigned to the thread
The identity assigned to the process
The account of the user who started the process
The account of the user who logged onto the PC
In your code you're getting (1). This is normally fine, and is usually the same as (2).
To retrieve (2), you could:
Call WindowsIdentity.GetCurrent to get the impersonated identity
Undo impersonation by calling the Win32 RevertToSelf function
Look at WindowsIdentity.GetCurrent to get the underlying process identity
Reset the thread identity by impersonating the identity from step (1)
(2) and (3) will be the same unless you've written unmanaged code that changes the process identity. As #Daniel points out, (3) and (4) could legitimately be different in the presence of the Windows "run as" command.
Edit: You can trust that the current WindowsIdentity is who it says it is, insofar as you can trust any given piece of data in your application.
You could spoof it by attaching a debugger to your process and faking the return value from this call, but if you're going to do that, you might as well fake the piece of code where you pass the user name to the database.
The 100% safe way to do this is to use integrated authentication/SSPI to connect to the database.
Only a partial answer:
I believe System.Security.Principal.WindowsIdentity.GetCurrent(); will return the windows user account associaed with the currently executing thread. If the thread or application was started under a different user account from the logged in user, you won't get what you're after using this.
If you can be sure that your aplication was not started using the "run-as" feature (or programatic equialent) and you're not doing anything internally with respect to the thread's identity, you can probably be sure this is the logged in user's account but I'm not 100% on this.
It may be possible to find the user account associated with the widows "session" within which the application is running by using ADSI (see System.DirectoryServices).
I have what the UAC Development Guides refer to as a "Administrative Choice Application." If you're familiar with what this is skip to the next section.
Background:
I want to let a "Standard" user have the ability to select/deselect a Run On Startup option in the preferences for my application.
Since my application is per machine (not per user), what needs to happen is it will either need to Delete or Copy a shortcut file into the Start Menu/Programs/Startup folder which will require administrative access to perform this operation.
So, what i'd like is for the "User Account Control credential prompt" to appear and that way if the user has an admin account also they can put in the credentials. This is apparently how applications are supposed to be designed to keep the user from having to switch to another account each time they need to do something administrative.
Excerpt from MSDN documentation:
An Administrative Choice Application
An Elevated Process or COM Object
The initial application launches without requiring elevation. Those items in the user interface that would require an administrative access token are decorated with a shield icon as identification. This decoration indicates to the user that using that feature will require administrator approval. When the application detects that one of these buttons has been selected, it has the following two choices.
The application launches a second program using ShellExecute() to perform the administrative task. This second program would be marked with a requestedExecutionLevel of requireAdministrator, thus causing the user to be prompted for approval. This second program would be running with a full administrative access token and would be able to perform the desired task.
-OR-
The application launches a COM object using CreateElevatedComObject(). This API would launch the COM object with a full administrative access token following approval and this COM object would be able to perform the desired task.
I just need to copy a file... seems excessive to fork a new process using ShellExecute() and I don't know enough about COM to know if I could use it to copy a file. I am hoping someone can post some code which provides a way to copy the file and ideally also explain how to decorate a MenuItem with the "sheild decorator".
Notes:
I have looked at the UAC Demo provided by microsoft which is referenced in several StackOverflow posts such as (Request Windows Vista UAC elevation if path is protected?) on topics related to permissions. The code only has an example of the calling a separate process.
Though it still appears to involve at least restarting or spawning a process, you can find some help here:
UAC Shield for Elevation at CodeProject.com
I ended up going in a different direction. I had my installer create a startup shortcut in the All Users/Startup folder that passes an argument to the application "startup".
When the application started I would check for the existence of the arg[0].equals("startup") and then check if Settings1.Default.RunOnStartup == true.
If both conditions were true I'd exit the application immediately. When the application is started without that argument (ie the Start Menu Program Group), the application loaded normally.
The RunOnStartup setting is a user scoped setting so each user can change without effecting others.