Question

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?

Was it helpful?

Solution 2

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

OTHER TIPS

Preface: Please use for one question one thread...

PortSharing: WHERE have you enabled port sharing? We cannot see this in your configuration file. For more infos see: How to: Configure a Windows Communication Foundation Service to Use Port Sharing

Have you installed the "Application Server" role on your server? See also: Checklist: Use TCP Port Sharing to Allow Multiple WCF Applications to Use the Same TCP Port

Is port sharing enabled on the system? See: How to: Enable the Net.TCP Port Sharing Service

Also, have you restarted your server? Sometimes this is needed (or at least all services which uses this port), see: http://blogs.msdn.com/b/joncole/archive/2010/06/10/tcp-port-sharing-access-is-denied.aspx

A comprehensive description about port-sharing is: http://blogs.msdn.com/b/andreal/archive/2009/04/05/net-tcp-ip-port-sharing.aspx

Also be aware that you must add some accounts for activation, if this is needed: Configuring the Net.TCP Port Sharing Service

Are you sure that the SmSvcHost.exe.config allows access from your account, your process is running under?

<configuration>
  <system.serviceModel.activation>
   <net.tcp listenBacklog="16" <!—16 * # of processors -->
      maxPendingAccepts="4"<!— 4 * # of processors -->
      maxPendingConnections="100"
      receiveTimeout="00:00:30" <!—30 seconds -->
      teredoEnabled="false">
      <allowAccounts>
         <!-- LocalSystem account -->
         <add securityIdentifier="S-1-5-18"/>
         <!-- LocalService account -->
         <add securityIdentifier="S-1-5-19"/>
         <!-- Administrators account -->
         <add securityIdentifier="S-1-5-20"/>
         <!-- Network Service account -->
         <add securityIdentifier="S-1-5-32-544" />
         <!-- IIS_IUSRS account (Vista only) -->
         <add securityIdentifier="S-1-5-32-568"/>
       </allowAccounts>
   </net.tcp>
 </system.serviceModel.activation>

(Just another answer that may help someone)

Turns out that in my case, logged in user did not have Administrative privilege. Changing the account type to Administrator solved the problem. I also did not change anything in SmSvcHost.exe.config

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top