Stopping a windows service via program - c#

Programmatically stopping a windows service in c# generates below listed System.InvalidOperationException
{Access is denied}
If i start/stop through windows interface then everything works fine!
I'm an Admin user and running the service under Windows 7

I'm not sure how you are trying to stop it, but I tried this on my system now, and this approach at least works fine:
var p = Process.Start(new ProcessStartInfo
{
FileName = "net",
Arguments = "stop NameOfService",
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
});
p.WaitForExit(); //add this line if you want to make sure that the service is stopped before your program execution continues

This sounds like a UAC issue - try running the application which shall control the service as admin (right-click, "Run as Administrator").
Note that even an administrator account does not have full privileges unless you explicitly run applications in administrator mode - this was introduced for protecting the user from malicious software in Windows Vista and has been there since then.

If authentication is the problem ...
You can programatically control the user from which you're doing stuff with, using
WindowsIdentity
and
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool LogonUser(...)
I don't know if that would work, but it could be worth a shot.
IntPtr token IntPtr.Zero;
LogonUser(username, password ..., ref token) //and some other parameters
var identity = WindowsIdentity(token);
using(identity.Impersonate())
{
//do stuff here using another identity
//find service and stop it
}
edit: This can be used for authentication on remote servers.

Related

How to force Windows to create the running user's profile directory

I am having an issue with a process being run, where the profile directory of the process' user has not yet been created.
To explain, here are the details of how this is happening:
We run a large distributed server grid, and are using (parts of) DataSynapse to execute processes on this grid. For those familiar with DataSynapse, the Engine is configured to run the particular service with "RunAs", where we use a certain AD domain service account for the service processes. I believe the problem is that DataSynapse, when running the process under "runas", does not set the LoadUserProfile flag (nor should it). Whatever the precise reason, if the "runas" service account (and AD domain account) has never logged on to some of the grid machines, then those machines will not have the user profile directory for the account.
For those not familiar with DataSynapse, here is a more generic explanation. On each machine on the grid, there is a process running, I'll call it dsService, and it runs under the credentials of the local machine's system account (or some similar account with elevated credentials). The process dsService will spawn a child process, say childProcess, but it runs childProcess under the credentials of our AD domain account, which I'll call serviceUser. There are thousands of machines on the grid, and typically they are never logged on to manually. In particular, the profile directory C:\users\serviceUser may not initially exist. Once it is created once, there are not further issues. But if new nodes are added to the grid, typically they will not have the C:\users\serviceUser initially. The problem is that when the dsService spawns childProcess, C:\users\serviceUser does not get created, and we need it.
I believe that this is because dsService does not set the LoadUserProfile flag to true when spawning childProcess, though I am not certain.
In any event, childProcess is a (.net) process (running as serviceUser) under our control, and I would like to know if there is a way (in C#) that childProcess can force the OS to create the running user's profile directory C:\users\serviceUser when it determines that it does not yet exist?
Edit:
Experimentation has confirmed that if one starts a process under another user ID, and the user's profile directory is not there (more specifically, if the user's local profile has not been created yet - merely deleting a pre-existing profile directory creates a different situation, one we're not interested in anyway), then (1) the profile directory (and one presumes, the local profile) gets created if the process is started with the LoadUserProfile set to true; and (2) the profile directory (and one presumes, the local profile) does NOT get created if the process is started with the LoadUserProfile set to false. This makes sense, and is as one would expect.
Related post: stackoverflow.com/q/9008742/1082063
If the running account has admin privileges, the following code will cause the creation of the running account's profile, including its UserProfile directory. Without admin, I don't know if it is possible:
using System.Runtime.InteropServices;
...
[DllImport("userenv.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int CreateProfile(
[In] string pszUserSid,
[In] string pszUserName,
System.Text.StringBuilder pszProfilePath,
int cchProfilePath);
....
public static string getUserProfilePath()
{
string userProfilePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
if(string.IsNullOrWhiteSpace(userProfilePath) || !Directory.Exists(userProfilePath))
{ //This will only work if we have admin...
var pathBuf = new System.Text.StringBuilder(240);
var Up = System.DirectoryServices.AccountManagement.UserPrincipal.Current;
if( 0 == CreateProfile(Up.Sid.ToString(), Up.SamAccountName, pathBuf, pathBuf.Capacity) }
{
userProfilePath = pathBuf.ToString();
}
}
return userProfilePath;
}
If anyone can tell me how to do this when the account is not admin, they will get their answer accepted as the correct answer. Until then, this at least gives others some idea.

Reboot machine from C#/Forms App while session is locked

Referring to this question: .net - Reboot machine from a C#/WPF App
I am attempting to create a c#/.net app that can restart the machine even if the session is locked (i.e., user is logged in, this app is running, but session is locked).
I tried this from the question: System.Diagnostics.Process.Start("shutdown.exe", "-r -t 0");
but apparently that only works if the session is unlocked. Additionally, after reading this: MSDN - InitiateSystemShutdown Function it seems the InitiateSystemShutdown function will display the System Shutdown dialog box, which doesn't seem like it will suite my purposes.
Are there any other methods of doing this?
The ExitWindowsEx function accomplished what I was trying to do.
Using:
[DllImport("user32.dll", SetLastError = true)]
public static extern int ExitWindowsEx(ExitWindows uFlags, ShutdownReason dwReason);
after adjusting token privileges, and using uFlags 0x06 (reboot / force). I used dwReason 0 as well. This function will restart the machine whether or not the session is locked.
here

WinSpool OpenPrinter Access Denied

This is 335th time this question being asked, by I've found no answer.
I'm trying to send raw data directly to printer via WinSpool api from ASP.net C# application.
My code is just a copy from here.
Error goes here
if( OpenPrinter( szPrinterName.Normalize(), out hPrinter, IntPtr.Zero ) )
It works fine for local printer but for shared network printer the result of OpenPrinter (result of GetLastError actually) is always 5 - Access Denied.
I've tried
different values for PRINTER_DEFAULTS with different combinations of DesiredAccess
give administrator privileges to user
setup printer like this
I must note that I just want to print, not change printer config or something that require administrative rights.
I can print to this shared printer from server using Printer Option page and test tool embedded in it. So printer works. How to gain access to it via API?
Update: looks like this code is working fine if called from Windows Application or Console Application. Then why access denied in Web Application?
Update 2: problem may be caused by the fact that printer installed on host PC and shared with virtual PC (or in production: printer is installed inside domain and shared to PC in DMZ) and there is no proper way to grant rights to this printer to users of virtual PC (or users outside of domain)
Update 3: and here is one more fact. If I browse to host PC from virtual in explorer (like this \\host_pc\C$) I get notified to enter user name and password to access host PC. If I check "save password" after that the whole "access denied"-problem will go away until I change password on host PC.
By default, your ASP.Net website running under IIS runs under a low-privilege local user account IIS_APPPOOL\mysite. In order for you to allow clients to access domain resources from the website, you'll need to change the user that IIS runs under (known as the application pool identity) to a domain user that has the correct rights to everything (both the network printer, and IIS itself).
The simplest solution (there may be more secure ones) is to change the IIS APPPool to use the built-in NetworkService account. This account is automatically added to your domain as MyDomain\MyHostName$, so you can use that to grant printer permissions (or whatever else is needed).
To change the app pool identity, just open the IIS manager, select the right application pool and then hit "Advanced Settings", and look for the setting "Identity".
More info here:
http://www.iis.net/learn/manage/configuring-security/application-pool-identities
Adding my answer since the accepted answer didn't solve my issue, to any who may get this error this might be useful to you if:
You get an error building for AnyCPU or x64.
You don't get an error building for x86.
The issue seems to be in GC recycling the PRINTER_DEFAULTS struct and than the OpenPrinter tries writing to that location. The suggested solution is to use a class which will stay on the stack.
public class PrinterSettings
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal class PRINTERDEFAULTSClass
{
public IntPtr pDatatype;
public IntPtr pDevMode;
public int DesiredAccess;
}
[DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, PRINTERDEFAULTSClass pdc);
public DEVMODE GetPrinterSettings(string PrinterName)
{
DEVMODE dm;
var pdc = new PRINTERDEFAULTSClass
{
pDatatype = new IntPtr(0),
pDevMode = new IntPtr(0),
DesiredAccess = PRINTER_ALL_ACCESS
};
var nRet = Convert.ToInt32(OpenPrinter(PrinterName,
out hPrinter, pdc));
}
}

How to start a process from an IIS hosted WCF service?

I would like to run a process from an intranet client on the WCF service side. In my case a client asks a server to create a new process on the server's machine under the provided credentials. WCF service is hosted on IIS 7.5 and I start a process using this code
var processInfo = new ProcessStartInfo("C:\\Windows\\System32\\notepad.exe")
{
UserName = "some user",
Password = MakeSecureString("some password"),
UseShellExecute = false,
LoadUserProfile = true
};
Process process = Process.Start(processInfo);
This code works if I host WCF service as a self-hosted console application running under admin user and I see the notepad started under another user. It fails on IIS with no exception, but process is immediately terminated
process.HasExited = true;
process.ExitCode = -1073741502;
On IIS WCF application is running under the user with admin rights and has got full trust defined in web.config. I cannot use self hosted application as it doesn't support easy continuous delivery (like WebDeploy with IIS web farms).
Q: How can I start a process on a server side from WCF service hosted on IIS?
EDIT:
I stumbled upon this post, with similar issues and I tried all the methods there, including all possible variations for Process.Start and P/Invoke with CreateProcessWithLogonW and CreateProcessAsUser I also tried granting additional permissions to users. Non of this would work with the error messages identical to the ones the guy had posted.
Oleksii, the point is that if you host the WCF service in a console application, there is a windows session (a user logged in and Windows Explorer loaded) for that user and the notepad is opened and shown for that user, so you see it in the UI.
when you host your WCF service in IIS, being a server, IIS requires and allows no user interaction and works also if no user is logged in; in that context there is no UI to host your notepad or other UI enabled applications, you could execute a process for elaboration or other batch jobs but not render a windows UI application, because Windows Explorer is not loaded for you and there is no place to render your process's UI.
here is what I use to call GnuPGP to do encryption. How does your setup compare?
private int ExecuteCommand(string arguments, string passPhrase, int timeout)
{
Process processObject;
ProcessStartInfo pInfo = new ProcessStartInfo(_executablePath, arguments);
pInfo.CreateNoWindow = true;
pInfo.UseShellExecute = false;
pInfo.RedirectStandardInput = true;
pInfo.RedirectStandardOutput = true;
pInfo.RedirectStandardError = true;
processObject = Process.Start(pInfo);
if (!string.IsNullOrEmpty(passPhrase))
{
processObject.StandardInput.WriteLine(passPhrase);
processObject.StandardInput.Flush();
}
string result = processObject.StandardOutput.ReadToEnd();
string error = processObject.StandardError.ReadToEnd();
if (!processObject.WaitForExit(timeout))
{
throw new TimeoutException("GnuPG operation timeout. Waited " + timeout + " milliseconds ");
}
int exitcode = processObject.ExitCode;
Error = error;
Output = result;
return exitcode;
}
There's an apppool setting to make sure it loads the user profile.
loadUserProfile Optional Boolean attribute.
Specifies whether IIS loads the user profile for the application pool identity. Setting
this value to false causes IIS to revert to IIS 6.0 behavior. IIS 6.0 does not load the
user profile for an application pool identity.
The default value is false.
That along with being a domain user as the identity with enough permissions might work?? I know that at a minimum the user will need a user profile.
That said, it's a little bit of an odd architecture. It seems like a better arch would be to have a persistent process like a windows service that the site communicates with but I'm not sure what your constraints are.
Hope that helps.

Cannot run as a non-admin impersonated user - access is denied

I do impersonation of a non-admin user account in an app that is running as an admin user (using LogonUser(), DuplicateToken() and WindowsIdentity.Impersonate() functions). Since this user account is temporary, I also need to load a user profile (using LoadUserProfile() native function). All methods execute successfully (no last error is set) and the current identity is the impesonated non-admin user as expected. However, when I try to run a new process with System.Diagnostics.Process.Start(), I get an error:
Access is denied.
When I try to manually execute the same scenario with runas /profile /user:mynonadmin user, everything works fine.
What am I missing here?
Ran into this a while back.
The impersonated user did not have access to the CWD which was set on the Process object. Create a ProcessStartInfo object and set the working directory to a location the impersonated user has access to.
I had a very similar situation with a service project. Here is some over-simplified pseudo code to give you an idea of what I was doing:
uint ConsoleSessionID = WTSGetActiveConsoleSessionId()
WTSQueryUserToken(ConsoleSessionID, out hToken)
IsUserInAdminGroup(hToken)
DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, ref sa, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out IntPtr DuplicateToken)
WindowsIdentity.RunImpersonated(new SafeAccessTokenHandle(DuplicateToken), () =>
{
Process p = new Process();
p.StartInfo.FileName = ...
p.StartInfo.Arguments = ...
p.WorkingDirectory = ...
p.StartInfo.UseShellExecute = true;
p.StartInfo.Verb = "runas"; // Elevated!
p.Start();
}
This worked ABSOLUTELY FINE when logging into Windows using the local Administrator account.
However, if I created a "Test_User" account that was a member of the administrators group, I was getting ACCESS DENIED OR a 0xc0000142 exception from Process.Start().
Allow service to interact with desktop
After checking "Allow service to interact with desktop", now whether I used the actual local Administrator account, or any other account that is a member of the local administrators group, my service can now start an elevated application in the context of the logged-on user.
Of course I went back and updated my code for installing the service, to ensure the SERVICE_INTERACTIVE_PROCESS flag was set, so this option was set programmatically.
Hope this helps someone...

Categories

Resources