locked
SingleSideLogin or reusing Token, CreateProcessAsUser, NamedPipe, ImpersonateNamedPipeClient, ACL/DACL, NT-Domain, Network drives RRS feed

  • Question

  • Hi there,

    Perhaps (shure) I have missed something in the Windows-Security-achitecture (NT4 to Win8.1).

    I try to implement a ("Server-Service/UserMode-Application", "Client-Application")  pair which solves this problem:
    The Server-application should be runnable on the server S as a Windows-Service or as a usermode application;
    the Client-Application could run on server S but mostly run on some other computer C.

    Requirement (in my UseCase):
    Person P1 and P2, P3 have valid accounts.
    P1, P2, P3 could login on Computer C and Server S.
    P1, P2 and P3 are not  Administrators.
    The accounts of P1 and P2 are part of the same NT-Domain-Group, i.e. "Cool".
    P3 belongs to another NT-Domain-Group, i.e. "Non-Cool".
    P1 is logged in on Computer C,  P1 is not logged in on Server S.
    P2, P3  is not logged in (not on C nor S). 
    Operationsystem: NT4 to Win8.1 

    UseCase:

    The Server-Application creates a NamedPipe and Person P1 uses the Client-Application to connect with this NamedPipe.
    After the NamedPipe has a client connection, the Server-Application should start on server S within security context of Person P1 an other Programm T, which must use settings from windows-profile on server S of user P1 because it needs access to networkdrives and environment variables etc.

    Problem1:
    NamedPipe ACL/DACL: I havn't figured out yet how to restrict read,write access (enable for NT-Domain-Group "Cool",  enable for NT-Domain-Administrator, enable for ServerLocal-Administrator, disable all other) correctly.

    Problem2:
    Can I reuse the current logintoken of person P1 from Computer C on server S?
    Or: How can I correctly interact ImpersonateNamedPipeClient with CreateProcessAsUser?

    Problem3:
    The ("Server-Service/UserMode-Application", "Client-Application") should run in a mixed os-environment: NT4, Win XP (32/64bit), Win 8.1 (32/64bit) .

    Best reagards,

    Robert

    • Moved by Shu 2017 Tuesday, December 30, 2014 9:03 AM no VC development issue
    • Moved by Dave PatrickMVP Tuesday, December 30, 2014 10:29 AM
    • Moved by Sheng Jiang 蒋晟 Wednesday, December 31, 2014 1:52 AM security dev
    Sunday, December 28, 2014 11:38 PM

All replies

  • Hi sporach,

    Thanks for posting in MSDN forum, but it seems that you have no VC++ develop issue in your post. So I will move this to where is forum for...  forum where someone will redirect you to a right forum. Thanks for your understanding.

    Best regards,

    Shu Hu


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Tuesday, December 30, 2014 9:02 AM
  • Problem1

    You can just setup the appropriate ACL when you create the named pipe.  I would suggest using SSDL to do this.

    Problem2

    I am assuming P1 is a domain user and Computer C & Server S belong to the same domain or a trusted domain.  ImpersonateNamedPipeClient() is going to return an impersonation token so you would have to convert it to a primary token with DuplicateTokenEx() to use it with CreateProcessAsUser(CPAU).  You'll need to setup the environment and load the profile if you are making the CPAU call on the server.

    Problem3

    Supporting all OS will be difficult since the architecture of Windows has changed between these versions.  The code for WIndows NT4 will be different from Windows 2000/Windows XP which will be different from Windows VISTA/Windows 7/Windows 8/8.1.  It is really going to be dependent on how you are using CreateProcessAsUser().  If it is just going to be a background process, this will be fairly straight forward but if the process needs to be INTERACTIVE, this is going to be a night mare.

    The basic concept of creating a client/server that communicates via a named pipe and the server impersonates the client won't be an issue.  The details of how you are going to use CreateProcessAsUser() and the impersonation token for CPAU is going to be the issue when going across all these different versions of Windows.

    thanks

    Frank K [MSFT]


    Wednesday, December 31, 2014 3:01 AM
  • Problem1

    You can just setup the appropriate ACL when you create the named pipe.  I would suggest using SSDL to do this.

    Well SDDL is working since Win2000. Because NT4 is in scope I have to construct the ACL in the old way / low level way: That means calling AllocateAndInitializeSid, using an Array for EXPLICIT_ACCESS , calling SetEntriesInAcl and SetSecurityDescriptorDacl, SetSecurityDescriptorSacl.

    I think now my Problem1 is reduced to the question:
    How I can implement my C++ helper functions, so I become able to  set the parameters for AllocateAndInitializeSid with correct values?

    PSID_IDENTIFIER_AUTHORITY myGetGroupAuthority(std::string domainName, std::string groupName) {

    return GetSidIdentifierAuthority(myGetGroupSid(domainName,groupName)));

    }

    PSID myGetGroupSid(std::string domainName, std::string groupName) {

    // any idea is welcome
    // can I use LookupAccountName

    }



    Problem2

    I am assuming P1 is a domain user and Computer C & Server S belong to the same domain or a trusted domain.  ImpersonateNamedPipeClient() is going to return an impersonation token so you would have to convert it to a primary token with DuplicateTokenEx() to use it with CreateProcessAsUser(CPAU).  You'll need to setup the environment and load the profile if you are making the CPAU call on the server.

    Thank you - yes everything is in the same domain :-)

    Problem3

    Supporting all OS will be difficult since the architecture of Windows has changed between these versions.  The code for WIndows NT4 will be different from Windows 2000/Windows XP which will be different from Windows VISTA/Windows 7/Windows 8/8.1.  It is really going to be dependent on how you are using CreateProcessAsUser().  If it is just going to be a background process, this will be fairly straight forward but if the process needs to be INTERACTIVE, this is going to be a night mare.

    The basic concept of creating a client/server that communicates via a named pipe and the server impersonates the client won't be an issue.  The details of how you are going to use CreateProcessAsUser() and the impersonation token for CPAU is going to be the issue when going across all these different versions of Windows.

    thanks

    Frank K [MSFT]

    Well my service doesn't get interactive input (except via the named pipe), but in "debuging-mode" I have to get some kind of console-output or something to catch with Sysinternals DebugView as normal user or a user viewable logfile.

    Best reagards,

    Robert



    • Edited by sporack Tuesday, January 13, 2015 4:11 PM missing something in codebock
    Tuesday, January 13, 2015 3:34 PM
  • I think now my Problem1 is reduced to the question:

    How I can implement my C++ helper functions, so I become able to  set the parameters for AllocateAndInitializeSid with correct values?

    PSID_IDENTIFIER_AUTHORITY myGetGroupAuthority(std::string domainName, std::string groupName) {

    return GetSidIdentifierAuthority(myGetGroupSid(domainName,groupName)));

    }

    PSID myGetGroupSid(std::string domainName, std::string groupName) {

    // any idea is welcome
    // can I use LookupAccountName

    }

    Yes LookupAccountName works:

    /**
     * C++ function to get PSID for a Domain and AccountName
     * just a proof of concept  
     * error handling could be optimizes 
     *
     * The User have to free the allocated memory for the return value with
     * ::HeapFree(GetProcessHeap(), 0, psid);
     * after the psid is not used anymore.
     *
     */
    inline PSID myGetSid(const std::string& domainName,
    		const std::string& accountName, const bool isGroupAccount = false) {
    	SID *sid;
    
    	SID_NAME_USE snu;
    
    	const std::string searchname = ((domainName.empty()||domainName.compare(".")==0)?accountName:(std::string(domainName)).append("\\").append(accountName));
    
    	char *name = const_cast<char*>(searchname.c_str());
    	char *found_domain = NULL;
    	int error;
    	unsigned long size, domainsize;
    
    	/* First find the size of buffers required for the SID and domain name */
    	sid = 0;
    	found_domain = 0;
    	size = domainsize = 0;
    
    	::SetLastError(0);
    	::LookupAccountNameA(0, name, sid, &size, found_domain, &domainsize, &snu);
    
    	/* Should have failed with ERROR_INSUFFICIENT_BUFFER */
    	error = ::GetLastError();
    	if (error != ERROR_INSUFFICIENT_BUFFER){ /* Error */
    		std::ostringstream s;
    		s << "API-Error: " << error;
    		throw(std::runtime_error(s.str().c_str()));
    
    	}
    
    	/* Allocate memory */
    	sid = (SID *) ::HeapAlloc(::GetProcessHeap(), 0, size);
    	if (!sid) /* Error */
    		throw(std::exception());
    
    	found_domain = (char *) ::HeapAlloc(::GetProcessHeap(), 0, domainsize);
    	if (!found_domain) /* Error */
    		throw(std::exception());
    
    	/* Get the SID */
    	::SetLastError(0);
    	if (!::LookupAccountNameA(0, name, sid, &size, found_domain, &domainsize, &snu)) /* Error */
    		throw(std::exception());
    
    
    	if ((isGroupAccount)&&((snu != SidTypeGroup)&&(snu !=SidTypeWellKnownGroup)&&(snu != SidTypeAlias))) {
    		std::ostringstream s;
    		s << "Wrong AccountType found - searched Group - found " <<snu;
    		throw(std::runtime_error(s.str().c_str()));
    	} else if ((!isGroupAccount)&&(snu != SidTypeUser)) {
    		std::ostringstream s;
    		s << "Wrong AccountType found - searched User - found " <<snu;
    		throw(std::runtime_error(s.str().c_str()));
    	}
    
    
    
    
    	if (::strcasecmp(domainName.c_str(),found_domain) != 0) /* Error */{
    		std::string errst = std::string("Found ").append(found_domain).append(" vs ").append(domainName);
    		const char* e = errst.c_str();
    		throw(std::runtime_error(e));
    	}
    
    	::HeapFree(::GetProcessHeap(), 0, found_domain);
    	return sid;
    
    }
    

    Now I can try to generate the ACEs and DACLs for the NamedPipe. Thank you.

    Best regards, Robert

    Thursday, January 15, 2015 10:53 AM
  • AllocateAndInitializeSid() is used to create a well known SID.

    https://msdn.microsoft.com/en-us/library/windows/desktop/aa379649(v=vs.85).aspx

    So if you are trying to grant access to the Everyone or Administrators Group, you could create the SID using AllocateAndInitializeSid().  You could also obtain the SID by calling LookupAccountName().

    thanks

    Frank K [MSFT]

    Wednesday, January 21, 2015 1:50 AM