This question is related to Starting processes under specific credentials from a Windows service, but it's a different problem.
I've started a process from a Windows service in the System session (0) under specific credentials, but it is unable to listen to a port sharing URL. It's using a "Worker" domain account on a Windows Server 2008 machine.
My SMSvcHost.exe.config file: http://pastie.org/private/jxed8bdft0eir5uc371pq
I've restarted the services and the machine as well, but it's still giving me this exception:
System.ServiceModel.CommunicationException: The service endpoint failed to listen on the URI 'net.tcp://localhost:5400/Agent/384' because access was denied. Verify that the current user is granted access in the appropriate allowAccounts section of SMSvcHost.exe.config. ---> System.ComponentModel.Win32Exception: Access is denied
at System.ServiceModel.Activation.SharedMemory.Read(String name, String& content)
at System.ServiceModel.Channels.SharedConnectionListener.SharedListenerProxy.ReadEndpoint(String sharedMemoryName, String& listenerEndpoint)
My ProcessHelper code that starts the process: http://pastie.org/private/iytqehsdfujrgil1decda. I'm calling the StartAsUserFromService
method.
I suppose the link between the SID in the config and the account the process is running under is somehow not being made. But why?
EDIT:
I've double-checked that the config I'm editing is used by the service. I've tried adding the System account and Everyone explicitly, but it's still giving me an access denied error. It's like it's not looking at that config at all.
How can I find where the missing permission is?
EDIT:
I reinstalled .NET 4.5.1 on the machine and all the Windows updates, still no luck.
EDIT:
Is this the correct way of duplicating a user token to allow it to use port sharing? Specifically the SecurityDescriptor
bit?
private static ImpersonationResult ImpersonateUser(string domain, string username, string password)
{
IntPtr token = IntPtr.Zero;
IntPtr primaryToken = IntPtr.Zero;
try
{
// Get token
bool bImpersonated = LogonUser(
username,
domain,
password,
(int)LogonType.NetworkClearText,
(int)LogonProvider.Default,
ref token);
if (!bImpersonated)
{
throw new Exception(string.Format("Failed to impersonate identity. Error code: {0}", Marshal.GetLastWin32Error()));
}
SecurityDescriptor sd = new SecurityDescriptor();
IntPtr ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(sd));
Marshal.StructureToPtr(sd, ptr, false);
InitializeSecurityDescriptor(ptr, 1);
sd = (SecurityDescriptor)Marshal.PtrToStructure(ptr, typeof(SecurityDescriptor));
// Set up security
bool bDecriptorSet = SetSecurityDescriptorDacl(
ref sd,
true,
IntPtr.Zero,
false);
if (!bDecriptorSet)
{
throw new Exception(string.Format("Failed to set security descriptor. Error code: {0}", Marshal.GetLastWin32Error()));
}
SecurityAttributes processAttributes = new SecurityAttributes();
processAttributes.lpSecurityDescriptor = ptr;
processAttributes.nLength = (uint)Marshal.SizeOf(sd);
processAttributes.bInheritHandle = true;
// Duplicate token
bool bTokenDuplicated = DuplicateTokenEx(
token,
0,
ref processAttributes,
(int)SecurityImpersonationLevel.SecurityImpersonation,
(int)TokenType.TokenPrimary,
ref primaryToken);
if (!bTokenDuplicated)
{
throw new Exception(string.Format("Failed to duplicate identity token. Error code: {0}", Marshal.GetLastWin32Error()));
}
SecurityAttributes threadAttributes = new SecurityAttributes();
threadAttributes.lpSecurityDescriptor = IntPtr.Zero;
threadAttributes.nLength = 0;
threadAttributes.bInheritHandle = false;
// Got the token
return new ImpersonationResult()
{
Token = primaryToken,
ProcessAttributes = processAttributes,
ThreadAttributes = threadAttributes
};
}
finally
{
FreeToken(token);
}
}
private static void FreeToken(IntPtr token)
{
if (token != IntPtr.Zero)
{
CloseHandle(token);
}
}
EDIT:
Here's the app.config bit of my process that enables port sharing: http://pastie.org/private/8ekqeps4d7rmo7hnktsw
Here's an app.config bit of the service that starts the process: http://pastie.org/private/nqqcwz8bvjb5fzp48yavbw. It has no problems using Port Sharing because it's running under the System account.
The Port Sharing service itself is enabled, and I already mentioned that I've restarted the machine several times.
The "Application Server" role is not installed, but when I go to add it, the TCP Port Sharing Role is already ticked and greyed out, so something else must have installed it. Does it come with .NET 4.5.1?
It turns out that the logon type was causing the permissions to not work correctly with the Port Sharing Service. I changed it to LogonType.Batch
and it started working.
Full code:
ProcessHelper: http://pastie.org/private/dlkytj8rbigs8ixwtg
TokenImpersonationContext: http://pastie.org/private/nu3pvpghoea6pwwlvjuq