Creating user's home folder before account creation [duplicate] - c#

We are trying to build an installer, in Wix, for a product. Part of this product requires the installation of elasticsearch as a service, and getting it running as a service. The service should run under a separate user account.
The first step, getting the user account set up, has been successful. However, in order to allow elasticsearch to run correctly, we need to set up certain Environment variables. In order to reduce the chance of inadvertent user error, we will have to have these variables set under the elasticsearch user, not as machine-wide.
To this end, we need a way to create the variables under the specified user only. However, we have not yet been able to work out a way to do this, either using Wix or a C# extension.
The closest we have come is to query the ManagementObject in C# and discover the SID for our ELASTICUSER. We should, in theory, be able to write another environment variable to the Registry, under "HKEY_USERS\<SID>\Environment", as shown.
var query = new SelectQuery("Win32_UserAccount");
var manObjSearch = new ManagementObjectSearcher(query);
var stringToWrite = string.Empty;
foreach (var envVar in manObjSearch.Get())
{
if (envVar["Name"].ToString().ToUpperInvariant() == "ELASTICUSER")
{
elasticUserSid = envVar["SID"].ToString();
}
}
using (var key = Registry.Users.OpenSubKey(elasticUserSid + "\\Environment"))
{
if (key == null)
{
return;
}
key.SetValue("OurVariable", "Value");
}
However, it appears that the SID is not created in the Registry until the user first logs in.
So, is there any way to use either Wix, or in C# to create an environment variable for a newly created user that has never logged in?

Yes, from C# you can P/Invoke the LoadUserProfile Win32 API function. This will create the user's profile if it does not already exist, and load the user's registry hive.
I would not recommend this approach for an account that will be used for interactive logons, but it should be safe enough for a service account.

Related

How to specify AWS credentials in C# .NET core console program

I am trying to test a .NET core console program to publish a message to SNS. As I had issues trying to get it to work in Lambda, I want to try it in a non-Lambda environment. In Lambda, security is covered by the role, but in a console program, I presume that I have to specify my access key and secret somehow.
I've read this page: http://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-config-creds.html#net-dg-config-creds-sdk-store, but still totally confused.
I'm running on my local development computer, not an EC2 instance. No intent to go to production with this, just trying to test some code.
I'm on Visual Studio 2015, .NET Core 1.0. I've used Nuget to get the following:
"AWSSDK.Extensions.NETCore.Setup": "3.3.3",
"AWSSDK.SimpleNotificationService": "3.3.0.23",
Based on the answer to How to set credentials on AWS SDK on NET Core? I created the /user/.aws/credentials file (assuming credentials was the file name and not the directory name).
But that question/answer doesn't address how to actually use this file. The code I'm running is below.
public static void Main(string[] args)
{
Console.WriteLine("Started");
//var awsCredentials = new Amazon.Runtime.AWSCredentials()
var client = new Amazon.SimpleNotificationService.AmazonSimpleNotificationServiceClient(Amazon.RegionEndpoint.EUWest2);
//var client = new Amazon.SimpleNotificationService.AmazonSimpleNotificationServiceClient(awsCredentials, Amazon.RegionEndpoint.EUWest2);
//Amazon.SimpleNotificationService.Model.PublishResponse publishResp = null;
SendMessage(client).Wait();
Console.WriteLine("Completed call to SendMessage: Press enter to end:");
Console.ReadLine();
}
The error I'm getting on the new client is:
An unhandled exception of type 'Amazon.Runtime.AmazonServiceException' occurred in AWSSDK.Core.dll
Additional information: Unable to find credentials
I see there is a way to pass an AWSCredentials object to that constructor, but I don't understand how to build it. Amazon.Runtime.AWSCredentials is an abstract class, so I can't use it in a "new" statement.
Based on Dan Pantry's answer, here is a simple short answer with code highlighted (note the region enum in the second line):
var awsCredentials = new Amazon.Runtime.BasicAWSCredentials("myaccesskey", "mysecretkey");
var client = new Amazon.SimpleNotificationService.AmazonSimpleNotificationSer‌​viceClient(
awsCreden‌​tials, Amazon.RegionEndpoint.EUWest2);
Use a role if possible, but above works when needed. Then the question is where to store the access key/secret key; could be environment variable, config file, prompt the user, or any of the usual suspects.
AWS-CLI and Python use credentials from here: c:\Users\username\.aws\credentials, so the C# could just read that file so as not to put the codes in the C# program itself. But then each user/developer that runs the program would need to set their credentials there.
There is also now a concept of running Lambda on your local machine, but I haven't tried it yet:
https://dzone.com/articles/run-aws-lambda-functions-locally-on-windows-machin#:~:text=Step%201%3A%20Download%20SAM%20local,version%20with%20the%20command%20below.&text=Step%203%3A%20Write%20your%20lambda,yaml%20on%20the%20root%20level.
So the point is that if you are going to do Lambda, but you need to test locally first, this would probably be worth trying.
You'll want to construct one of its child classes instead of the abstract one. You can take a look at the class hierarchy here.
For posterity, the options are:
AnonymousAWSCredentials - Authenticates as an anonymous user.
BasicAWSCredentials - You provide your credentials to the class constructor directly.
EnvironmentAWSCredentials - Credentials are pulled from the environment variables of the running executable.
InstanceProfileAWSCredentials - Pulls credentials from the Instance Profile of the EC2 instance running the executable. This, obviously, only works on EC2.
SessionAWSCredentials - Similar to BasicAWSCredentials, except utilises an AWS Session using a temporary session token from AWS STS.
RefreshingSessionAWSCredentials - Similar to SessionAWSCredentials, but refreshes when the STS token expires.
Note that the default strategy in the absence of a credentials object involves checking the Environment Variables and then the instance profile.
If you want to have the program pull credentials from ~/.aws/credentials, you'll need to do some legwork. There used to be a StoredProfileAWSCredentials class, but that appears to have been removed - you can find more information by looking at this github issue. This is only useful, really, in development as you won't be using ~/.aws/credentials in production but probably instance profiles - I'd suggest instead using the default strategy and using Environment AWS credentials in test or development environments.
I take this approach at work since we use a command line tool to grab us limited time tokens from AWS STS and plunk them into the current shell for use for the next hour.
EDIT: It appears you're using AWS Lambda. These have federated access to AWS resources based on the roles assigned to them, so this should work using the default credential strategy in the aws-sdk library which uses instance profiles. So this is only really necessary for development/testing, in which case I would again recommend just using environment variables.
This is a really old question, and the existing answers work, but I really don't like hard-coding my Access Key Id and Secret Key values directly into source code, even for throw-away projects I'm doing on my local machine. For one thing, I might revoke those keys in the future, so I want to leverage the credentials in my .aws\credentials file.
To do that for my .NET core apps (including console apps, etc), I first add two NuGet packages:
Microsoft.Extensions.Configuration.Json
AWSSDK.Extensions.NETCore.Setup
Then, I add an applications.json file to my project, which contains the following (note - you need to right-click the file, and set "Copy to output" as either "copy if newer" or "always"):
{
"AWS": {
"Profile": "default",
"ProfilesLocation": "C:\\Users\\my-user-profile-folder\\.aws\\credentials",
"Region": "us-west-2"
}
}
Finally, I create an instance of the AWS SDK client using the following:
var builder = new ConfigurationBuilder().AddJsonFile("appsettings.Development.json", optional: false, reloadOnChange: true);
var options = builder.Build().GetAWSOptions();
var s3client = options.CreateServiceClient<IAmazonS3>();
This way, if I update my credentials file, I'm fine. Or if my code gets zipped up and emailed to a friend or co-worker, I don't accidentally send them my credentials also.
There is another way to do this, without needing to add the NuGet packages also, which many people might prefer. You can use the new SharedCredentialsFile class and AWSCredentialsFactory, like this (using the "default" profile here, and assumes your credential file is in the default location, same as the other method):
var sharedFile = new SharedCredentialsFile();
sharedFile.TryGetProfile("default", out var profile);
AWSCredentialsFactory.TryGetAWSCredentials(profile, sharedFile, out var credentials);
var s3Client = new AmazonS3Client(credentials);
Note - I'm not checking that the two Try* methods are succeeding here, which you probably should do. Details on using these classes are here: https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-config-creds.html#how-to-create-an-amazons3client-using-the-sharedcredentialsfile-class
While keeping your credentials in the shared "credentials" file, you can redefine the ProfilesLocation when creating the CredentialProfileStoreChain
//define your file location here:
var chain = new CredentialProfileStoreChain(#"C:\aws\credentials");
// input the name of your credentials here:
if (chain.TryGetAWSCredentials("nameofprofile", out AWSCredentials awsCredentials))
{
//executes if the credentials were found and inserted into awsCredentials
}
else
{
// executes if the credentials were not found
}
Taken from here: https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/creds-locate.html
For those struggling with profile names, here is where you can find it.
Contents of your ~/.aws/credentials:
[YOUR_PROFILE_NAME]
aws_access_key_id = ***
aws_secret_access_key = ***
aws_security_token = ***
aws_session_expiration = ***
aws_session_token = ***
So then in your application you access the credentials like this:
var chain = new CredentialProfileStoreChain();
var result = chain.TryGetAWSCredentials("YOUR_PROFILE_NAME", out var credentials);
Resources:
accessing credentials and profiles: https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-config-creds.html#creds-locate
named profiles: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html

Determining if a user is logged onto a VM

I am currently writing a C# service to basically make sure a group of exes are running and if they aren't then relaunch them.
The service starts up automatically so I run in to the problem that it attempts to launch the exe when no one is logged in. I have a solution that works for non-VM systems as shown below:
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT UserName FROM Win32_ComputerSystem");
ManagementObjectCollection collection = searcher.Get();
string loggedIn = (string)collection.Cast<ManagementBaseObject>().First()["UserName"];
I then check this against null or whitespace and then just don't check the process list if no user is logged in.
However, while running on a VM this section of code returns null when a user is logged in. So I need a way of checking for a logged in user on both a standard machine and VM. I also need it to work for XP and windows 7, and both 32bit and 64bit. I am using .Net 4.
Official documentation to list sessions prescribes to use LsaLogOnSessions to enumerate them, refer to MSDN or to this CodeProject article for more examples.
That said if you just need to be sure there is at least one logged in user then you can simply check for default shell process. Default shell will always be loaded for each session:
bool loggedInUserExist = Process.GetProcessesByName("explorer").Any();
In this example I hard-coded value but you must read it from string value Shell in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon\, in case default shell has been overridden (or for Server Core installation when GUI isn't present).
If you don't need anything else then you're done. If you have to filter specific users then you have to get process' owner. It's not such easy in C# unless you use WMI to query Win32_Process, search for explorer in Name property and then call GetOwner() method of the ManagementObject (see this post here on SO). Note that you may use same query both to determine if there is any logged in user and who he is. As alternative you may P/Invoke for Windows API functions as described in this post (but in this case I'd go back to LsaLogOnSessions).

Why does my ASP.NET application need Administrator rights to read from the registry?

I've looked at a couple of StackOverflow threads now. This comes closest.
I'm stuck using VS2005, and .NET 2.0. The server is Win2008. Not R2.
I'm building a C# ASP.NET web application that reads information from a database that's modified by a VB6 application. The database configuration settings are stored in the registry.
I'm using the Registry.GetValue() function and it's returning a null value.
If I make the Application Pool run as Administrator, the code returns the expected value. With any other user, the ToString throws a System.NullReferenceException. I have tried the following:
Making the AppPool use and assigning Full Control to the following users does not work:
NT AUTHORITY\NETWORK SERVICE
NT AUTHORITY\SYSTEM
NT AUTHORITY\LOCAL SERVICE
A custom user in the Administrators group.
I cannot apply permissions to IIS APPPOOL\DefaultAppPool because the user doesn't seem to exist. This is a documented fault of Server 2008.
If I make the AppPool use the Administrator account, the system works.
Attached is the relevant code:
string root = "HKEY_CURRENT_USER";
string keyName = #"Software\Some\Key\Somewhere\";
string valueName = "someValue";
string fullKey = root + #"\" + keyName;
object keyValue;
try
{
keyValue = Registry.GetValue(fullKey, valueName, "Value not found.");
string val = keyValue.ToString();
return val;
}
catch (Exception ex)
{
return ex.GetType().ToString();
}
Unless it's running as Administrator, the above code always returns a System.NullReferenceException when running keyValue.ToString(). It never throws the System.Security.SecurityException.
I'm not eager to make my web app require Admin access.
You are searching under the key for the current user. Clearly the data exists under the key for the Administrator but not for the other users.
For the service accounts that don't have a profile, HKEY_CURRENT_USER resolves to HKEY_USERS.Default.
So to make it work with service accounts, you need to add the value to
HKEY_USERS\.Default\Software\Some\Key\Somewhere\
To make it work for the custom user in the Administrators group, you need to add it to
HKEY_USERS\<sid>\Software\Some\Key\Somewhere\
where is the SID of the custom user.
This isn't related to needing Admin access, you're just looking in the wrong place!
If the data isn't user-specific, try putting it in:
HKEY_LOCAL_MACHINE\Software\Some\Key\Somewhere\
then all the users will normally be able to read the same value.

TFS 2010 - Why am I getting "TF30063 You are not authorized to access.." error when impersonating?

I am attempting to create a bug in TFS2010 by impersonating a user but always get
"TF30063 You are not authorized to access.."
I first authenticate using a service account and then attempt to impersonate a separate user account. I can successfully create Work Items using either account both programmatically and in the web UI. However, when I try to create the Work Item used an impersonated account (either way around) I always get this error. My code is:
public int Save(List<KeyValuePair<string, string>> values, ticketType type,string user)
{
// get the Uri to the project collection to use
Uri tfsuri = new Uri("http://94.23.12.119:8085/tfs");
// get a reference to the team project collection (authenticate as generic service account)
using (var tfs = new TfsTeamProjectCollection(tfsuri, new System.Net.NetworkCredential("username", "password", "servername")))
{
tfs.EnsureAuthenticated();
//Now get the details of the user we want to impersonate
TeamFoundationIdentity identity = GetImpersonatedIdentity(tfsuri,tfs,user);
//Now connect as the impersonated user
using (TfsTeamProjectCollection ImpersonatedTFS = new TfsTeamProjectCollection(tfsuri, identity.Descriptor))
{
ImpersonatedTFS.EnsureAuthenticated();
var workItemStore = GetWorkItemStore(ImpersonatedTFS);
// create a new work item
WorkItem wi = new WorkItem(GetWorkItemType(type, workItemStore));
{
//Values are supplied as a KVP - Field Name/Value
foreach (KeyValuePair<string,string> kvp in values)
{
if (wi.Fields.Contains(kvp.Key))
{
wi.Fields[kvp.Key].Value = kvp.Value;
}
}
ValidationResult = wi.Validate();
}
if (ValidationResult.Count == 0)
{
wi.Save();
return wi.Id;
}
else
{
return 0;
}
}
}
}
It successfully gets the impersonated identity but falls over on
ImpersonatedTFS.EnsureAuthenticated();
Both accounts have the 'Make requests on behalf of others' permission set.
First let me clarify one thing first. It seems your application is a server application, in which case there is no value in using EnsureAuthenticated(). It is just a performance tuning trick to help UI/desktop clients.
Now back to your main issue:
- If your application works as expected when you access locally but fails when you access remotely, then please read on, otherwise this is not the solution for you.
The reason it is failing is because the SPN needs to be added to the service account on the active directory. It is necessary for Kerberos authentication to take place.
This is something that TFS team needs to explain because many developers will forget about it while focusing at the job it hand. Hope this helps.
To learn more about SPN's and Kerberos fundamentals, check out these resources:
Kerberos for the busy admin.
Introduction to Kerberos SPN
I hope this helps.
Thanks!
Where do your users have the Make requests on behalf of others permission set? Is it at the Project Collection level (accessed via Team > Team Project Collection Settings > Security..) or at the TFS server level (accessed via Team Foundation Administration Console > Application Tier > Security..) ?
I think your problem is that you only have permission to impersonate at the 'Server' level, but you're trying to impersonate in a collection.
This is what Taylor has to say in his Introducing TFS Impersonation blog post:
This permission is encapsulated within each Team Project Collection
and within the Configuration Server. This means that if User A has
this permission on TPC1 he will not be allowed to impersonate users
when talking to TPC2 or the Configuration Server. Similarly, if User
B has this permission on the Configuration Server she will not be able
impersonate users when talking to any of the Team Project Collections.

How does RegistryPermission works?

I am trying to check if I have write access to a specific key in the registry before displaying a form that allow the user to change some settings that are written in that key.
code sanitized for clarity
public bool CanWrite()
{
string key = #"HKEY_LOCAL_MACHINE\SOFTWARE\MyHaccpPlan, Inc.\2.0";
try
{
RegistryPermission permission = new RegistryPermission(RegistryPermissionAccess.Write, key);
permission.Demand();
return true;
}
catch(SecurityException)
{
return false;
}
}
I am running the application using a user that has read access only. The problem is that this function return true, even if the user don't have write access.
Later, a call like
Registry.SetValue(#"HKEY_LOCAL_MACHINE\SOFTWARE\MyHaccpPlan, Inc.\2.0", "Language", "fr-CA");
will fail with UnauthorizedAccessException.
How do I properly check for registry rights before attempting to write to it?
Edit
I don't want the current user to be able to write there. I want to use this registry entry as a flag that a feature in the software should be disabled. But if the user is an administrator, I want the software to allow the feature. The goal is that a network administrator could be able to preset the settings and that the users will be unable to change them.
But beside actually writing and waiting for it to crash, I want to check the security using the permission system offered in .NET if that is possible.
You shouldn't rely on .NET code access security for managing access control to the registry; let alone should you have explicit checks in your code. With that approach, the user can still use the registry editor and bypass all your access checks.
Instead, you should use proper ACLs to restrict what users can write to a key.
If you want to test at run-time whether you have access to a key, you should try to open the key for writing, and catch SecurityException (in which case the user running the application has no permission to modify the key).
mmm you can try using a tool like Lutz Roeder's Reflector for viewing the content of the Registry.SetValue Method.
Looking a bit to it, it seems to do it with next line of code:
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();

Categories

Resources