IIS Impersonation calling powershell thread? - powershell

I am newly learning IIS, and I am trying to use IIS to host an app to impersonate an Administrative user, then execute powershell command as Admin to add users to local groups.
Within in ASP.NET code, I am running the following:
private string RunScript()
{
StringBuilder stringBuilder = new StringBuilder();
PowerShell psExec = PowerShell.Create();
psExec.AddCommand(SCRIPT_LOCATION);
psExec.AddParameter("username",Username.Text);
psExec.AddParameter("computer", Computer.Text);
psExec.AddParameter("group", Group.Text);
Collection<PSObject> results;
stringBuilder.AppendLine(System.Security.Principal.WindowsIdentity.GetCurrent().Name);
results = psExec.Invoke();
//errors = psExec.Streams.Error.ReadAll();
foreach (PSObject result in results)
{
stringBuilder.AppendLine(result.ToString());
}
return stringBuilder.ToString();
}
In my web.config, I have the following added:
<authentication mode="Windows" />
<identity impersonate="True" userName="<My admin user name>" password="<My admin password>"/>
So my GetCurrent().Name seems to return that I am at least running the application as my admin username. But when calling my powershell script:
Write-Output whoami
Returns that I am NT Authority\System.
In my IIS:
Application pool Identity is "LocalSystem"
Authentication setting has ASP.NET Impersonation (where admin user is set as Specific user) is enabled.
Windows Authentication is enabled.
So when I try to add the user to group, I get "Access is denied". Just as a sanity check, I have confirmed that if I run the powershell script separately as Admin, the add works fine.
I am sure it's something basic I am missing, but if anyone would please provide any advice or guidance, I would greatly appreciate it.
Thank you for your time.

As far as I know, the psExec.Invoke() method creates a thread called “Pipeline Execution Thread” and the command/script is executed in this new thread. By default the impersonation token does not flow across threads, so “Pipeline Execution Thread” doesn’t get the impersonation token of the calling thread.
You can configure ASP.Net to flow the impersonation token to newly created threads by using “alwaysFlowImpersonationPolicy” and “legacyImpersonationPolicy” configuration elements in asp.config file like this:
<runtime>
<legacyImpersonationPolicy enabled=”false”/>
<alwaysFlowImpersonationPolicy enabled=”true”/>
</runtime>
More details about how to set it, you could refer to below article:
https://weblogs.asp.net/owscott/setting-an-aspnet-config-file-per-application-pool

Related

For NetSuite Map/Reduce script - Why is map stage failing when being called from Restlet?

In NetSuite, have a Restlet script that calls a deployed map/reduce script but the map stage shows as Failed when looking at details of status page (the getInputData stage does run and shows as Complete).
However, if I do a "Save and Execute" from the deployment of the map/reduce script, it works fine (Map stage does run).
Note that:
There is no error on execution log of either the restlet or the map/reduce scripts.
Have 'N/task' in define section of restlet as well as task in function parameters.
The map/reduce script has the Map checkbox checked. The map/reduce script deployment is not scheduled and has default values for other fields.
Using NetSuite help "See the quick brown fox jump" sample map/reduce script
Using Sandbox account
Using a custom role to post to restlet
Below is call to create.task code snippet from my Restlet. Don't know what is wrong, any help is appreciated.
var mrTask = task.create({
taskType: task.TaskType.MAP_REDUCE,
scriptId: 'customscript_test',
deploymentId: 'customdeploy_test'
});
var mrTaskId = mrTask.submit();
var taskStatus = task.checkStatus(mrTaskId);
log.debug('taskStatus', taskStatus);
You also need Documents and Files -View permission along with SuiteScript - View and SuiteScript Scheduling - Full permissions to access the Map/Reduce script.
The help for MapReduceScriptTask.submit() does not mention this but the help for ScheduledScriptTask.submit() does:
Only administrators can run scheduled scripts. If a user event script calls ScheduledScriptTask.submit(), the user event script has to be deployed with admin permissions.
I did a test of posting to my restlet using Administrator role credentials and it works fine as opposed to using my other custom role. Maybe just like ScheduledScriptTask, the MapReduceScriptTask can only be called by Administrator role ? My custom role does have SuiteScript - View and SuiteScript Scheduling - Full permissions. Thought that would do the trick but apparently not.
Anyone have any further insight on this ?

Connect to local SQL Server instance when running Service Fabric cluster in development environment

When opening a sql connection to a local database in a traditional console app I have no issues, however when I attempt to do the same thing within a stateless service running in Service Fabric I get a login error.
The error I receive is "Login failed for user 'WORKGROUP\\NICK$'."
And this is the code I'm using to connect
using (var con = new SqlConnection("Server=.;Trusted_Connection=True;Database=AddressBook"))
{
try
{
con.Open();
}
catch (Exception e)
{
}
}
When I try to add that user to sql server it tells me that the user cannot be found.
Based on the comments above I learned that Service Fabric is running under the NETWORK SERVICE account.
The solution is to update the User Mapping and role membership for the databases that you want to access within the SF cluster.
In SSMS expand Security, Logins, right click NETWORK SERVICE and then choose properties. Under User Mapping place a checkbox next to each Database that you want to expose access to and down below public, db_datareader/writer.
A comment to the accepted answer mentions running your service as a different user account. Here's how to do that. In your ApplicationManifest.xml file, insert this:
<Principals>
<Users>
<User Name="AcctToUse" AccountType="DomainUser"
AccountName="MyDomain\MyUsername" Password="MyPassword" />
</Users>
</Principals>
<Policies>
<DefaultRunAsPolicy UserRef="AcctToUse" />
</Policies>
Edit: I didn't make it clear, AcctToUse is just a string that you make up. It's a key that points to the User. The AccountName field on the other hand is the username.

Start a windows service without elevation

I have a windows service "Service1" configured to log on as "Local Service".
I built a console application to start it programmatically.
var service = new ServiceController("Service1");
service.Start();
I know that if I run the ConsoleApplication1 from an administrator command prompt it starts smoothly.
And if I run it without elevation I get an:
System error 5 has occurred.
Access is denied.
But, I need to start it without elevation.
Is it possible, or I have to change the way to achieve this?
You can set the ACL for the service itself to allow this. The SetACL.exe utility makes this (somewhat) straightforward; e.g.:
SetACL.exe -on "MyService" -ot srv -actn ace -ace "n:S-1-5-32-545;p:start_stop"
This allows members of the Users group (S-1-5-32-545) to start and stop MyService.
I followed torak link and I understand this key difference concerning rights in a service:
a service has rights concerning the "Run as" user
a service has different permission to control the service (i.e. to start/stop it)
So, to start the service I need to modify the service control permission.
Well, I have done a windows service called Service1 and I made an installer with WIX.
During setup I call ServiceInstall
<ServiceInstall Id="ServiceInstaller" Type="ownProcess" Vital="yes"
Name="Service1" DisplayName="Service1"
Description="Service1 description"Start="demand"
Account="NT AUTHORITY\LocalService"
ErrorControl="ignore" Interactive="no" >
</ServiceInstall>
Then I have a client program called TestProgram where I try to start the service:
var service = new ServiceController("Service1");
service.Start();
And obviously it doesn't start the service without elevation of TestProgram (that runs under a normal user account).
So the solution is to instruct WIX to allow members of the user group (for example) to start/stop the service, using the PermissionEx tag:
<util:PermissionEx User="Users" ServiceStart="yes" ServiceStop="yes">
</util:PermissionEx>
Hope this helps. Thank you all.
If i can add my 2 cents, here is my solution that wont require any complex App.Manifest or Windows Service modification.
The concept is simply to call "Net Start" through a process that is elevated :
public string StartServiceViaProcess(string param_strServiceName)
{
try
{
const int ERROR_CANCELLED = 1223; //The operation was canceled by the user.
Process process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.FileName = "cmd.exe";
startInfo.Verb = "runas";
startInfo.UseShellExecute = true;
startInfo.Arguments = "/C net start " + param_strServiceName;
process.StartInfo = startInfo;
try
{
Process.Start(startInfo);
}
catch (Win32Exception ex)
{
if (ex.NativeErrorCode == ERROR_CANCELLED)
return "L'usager a annulé la demande d'exécution avec privilège.";
else
throw;
}
}
catch (Exception ex)
{
return ex.SI_ConvertToString();
}
return "";
}
I don't think you can do it - It is my understanding that this is due to security reasons. Allowing malware to automatically elevate itself would be problematic.
This is the link that I followed and solved my issue.
How to Grant non-Administrators Rights to Manage Services
By default, common users with no system administrator privileges
cannot manage Windows services. It means that they cannot stop, start
or change the settings or permissions for such services. In some
cases, it is necessary that a user had the permissions to restart or
manage certain services. In this article, we’ll consider some ways to
manage permissions for Windows services. In particular, we’ll show how
to grant a standard user without administrative rights the permissions
to start and stop a specific service...
There is no simple and convenient integrated tool to manage services
permissions in Windows. We’ll consider some ways to grant a user
permissions to manage service...

Entity Framework Code First ignoring connection string, using IIS instead

I have a web app that I've created using Entity Framework Code First. In setting it up I have managed to match my DB connection string to my DBContext by specifying the full namespace and class of the DBContext as the name of the connection string.
<add name="MyClassProject.EfDbContext" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=MyDatabase;Integrated Security=true;User Id=MyUsername;Password=MyPassword;" providerName="System.Data.SqlClient"/>
Initially when I set up the project, I just had it created in c:\inetpub\wwwroot, and just ran it through Visual Studio. Everything worked fine.
Now I'm trying to have the code build to a separate website folder, and have the website run as it's own website and app pool in IIS. I've set up the website, and my hosts file but when I went to run it I received the following error.
Cannot open database "MyDatabase" requested by the login. The login failed.
Login failed for user 'IIS APPPOOL\MyAppPool'.
I'm wondering why this is happening, as I seem to be specifying the security username and password to use for the DB in my connection string....so why is it trying to connect as the app pool that my website is running in?
Also, how can I fix this, without having to give MyAppPool (or Network Service if I changed it to that) DB permissions in SQL Server?
Update: I should've mentioned that I initialise my DBContext class using:
namespace MyClassProject
{
public class EfDbContext : DbContext
{
public EfDbContext() : base ("MyDatabase")
{
}
}
}
I found the issue.
When I initialise my DBContext class with : base("MyDatabase"), it overrides the connection string specified in the web.config.
Removing that from my DBContext class, with the database already existing, the site now works in IIS.
However, if I don't have the database created already, (or if I have my database initialiser use DropCreateDatabaseWhenModelChanges or DropCreateDatabaseAlways so that it'll needs to recreate the DB), the initialiser will fail, as it'll try to use an SQL user that doesn't have permissions to create the DB.
My way around it is to use the : base("MyDatabase") and run from Visual Studio initially so the database is created.
Then remove it from code, add the specified user to the DB security in SQL Server, and it'll allow my site to run in IIS thereafter.
Remove Integrated Security=true;. That is the setting that passes the current user off.
When using Integrated Security, the DB is given a token from the user who is currently running the process. In all likelihood, you run Visual Studio from your user account, which likely has Admin permissions on your SQL Server instance.
When IIS runs your app, it uses something called an Application Pool (or App pool). You can have multiple apps in a single pool to be managed together. The app pool also runs under a special user account named for the pool. App pool users exist under a container called "IIS AppPool", so the local user for the DefaultAppPool is IIS AppPool\DefaultAppPool. If you want to grant access to a resource on your local system (including file permissions), you can also grant it to the app pool user or local group IIS_IUSRS to grant it to all app pools.
Remember that these are local accounts, so they will not cross network boundaries. To grant permissions on a different server, you'll need to either use a domain user (or even better, a domain Managed Service Account) or you can set the app pool user to NETWORK SERVICE and then you can grant permissions to MyDomain\MyWebServer$ (the dollar sign is important).
You can use Web.config Transform to have Local connection stirng different from Remote (say in Release mode). To start using it you need to publish your Web App using One-Click Publish from Visual Studio. That's really very handy way to publish web apps!
Looks like that's what you're looking for.
Or set connection string name base on a condition:
public EfDbContext() : base (GetConnectionStringName())
{
}
private static GetConnectionStringName()
{
return RunLocally() : "LocalDatabase" : "RemoteDatabase";
}
private static bool RunLocally()
{
// implement some how
}

Error when listing print queues from network server as impersonated user

I have been trying to set up a simple endpoint service that sits on a server, accepts a web request and then prints a job to a specific print queue on a specific server as a user, whose credentials are passed into the service. This is for automated testing so the users that we are using have limited permissions so security is not a concern.
When I attempt this code without impersonation:
PrintServer ps = new PrintServer(#"\\" + serverName, PrintSystemDesiredAccess.EnumerateServer);
response = "Created PrintServer object";
foreach (PrintQueue pq in ps.GetPrintQueues())
{
response = response + "~#~" + pq.Name;
}
It works properly and gives me a list of printers on the remote server, e.g:
Created PrintServer object~#~Sc-4 Pull Port~#~Microsoft XPS Document Writer
When I try it with impersonation I get this instead:
System.Printing.PrintServerException: An exception occurred while creating the PrintServer object. Win32 error: The printer name is invalid.
at System.Printing.PrintServer.Initialize(String path, String[] propertiesFilter, PrinterDefaults printerDefaults)
at System.Printing.PrintServer..ctor(String path, PrintSystemDesiredAccess desiredAccess)
at PrintService.PrintService.ImpersonatedInstalledPrinterList(String serverName, String userName, String userPassword, String domain)
I have been unable to figure out, or find online, why it is throwing the above error. I am using a valid domain account, and have even given the user I am using to test full permissions to the print queue on the server. I get the same error when I attempt to print as an impersonated user, having no issue with sending a print job when I am not performing impersonation.
Thanks for any help you can offer,
Tsuki
I found the issue with the code I was using, the problem was that I was using when I was using Impersonation, I was using SECURITY_IMPERSONATION_LEVEL of Impersonation when I needed to be using Delegation. Once I change that the error disappeared.