CreateProcessAsUser() windowstations and desktops (165194)



The information in this article applies to:

  • Microsoft Win32 Application Programming Interface (API), when used with:
    • the operating system: Microsoft Windows NT 3.51
    • the operating system: Microsoft Windows NT 4.0
    • the operating system: Microsoft Windows 2000
    • the operating system: Microsoft Windows XP

This article was previously published under Q165194

SUMMARY

When a process is started by means of the CreateProcessAsUser function, the process will be started into a windowstation and desktop combination based on the value of lpDesktop in the STARTUPINFO structure parameter:
  • If a windowstation and desktop combination is specified in the lpDesktop member, the system will try to start the process into that windowstation and desktop.
  • If the lpDesktop member is initialized to NULL, the system will try to use the same windowstation and desktop as the calling process if the system is associated with the interactive windowstation.
  • If the lpDesktop member is not initialized to NULL, the system will create a new windowstation and desktop that you cannot see.
  • If the system is initialized with the empty string, "", it will either create a new windowstation and desktop that you cannot see, or if one has been created by means of a prior call by using the same access token, the existing windowstation and desktop will be used.

MORE INFORMATION

Sometimes the process may fail to start, and one of the following error messages may appear:
Error message 1
Initialization of the dynamic library <system>\system32\user32.dll failed. The process is terminating abnormally.
Error message 2
Initialization of the dynamic library <system>\system32\kernel32.dll failed. The process is terminating abnormally.
The error message occurs when the process that is started causes the initialization code in either the User32.dll or the Kernel32.dll file to fail because of an API call from the started process that does not have correct security access to either the targeted windowstation or desktop. For example, if the process that was started was trying to create a window, the process would have to have DESKTOP_CREATEWINDOW access to the desktop object. If the process has not been granted this access right, an error would occur in the User32.dll file, which would cause the system error box to appear and the process would fail to start.

Note Sometimes the process may start, but fail to draw its GUI correctly. The best method to resolve these and other potential access related problems is to grant the user full access to both the targeted windowstation and desktop. For example, if you want the process that is started by the CreateProcessAsUser function to be interactive, specify the following windowstation and desktop combination:

winsta0\default

Windows 2000 and Windows XP

A new API was introduced beginning with Windows 2000, CreateProcessWithLogonW(). If the lpDesktop member of the STARTUPINFO structure is initialized to either NULL or "", CreateProcessWithLogonW() implementation adds permissions for the specified user account to the inherited window station and desktop. If the application specifies a desktop in the lpDesktop member, it is the responsibility of the application to add permission for the specified user account to the specified window station and desktop.

The following sample code grants the user named franki access to the interactive windowstation and desktop, "winsta0\\default". Access is granted based on the logon security ID (SID) of the user franki.

For more information about windowstations and desktops, see the Win32 SDK documentation.

For more information, click the following article number to view the article in the Microsoft Knowledge Base:

327618 Security, services and the interactive desktop

Sample code

The following sample code gives the user named "franki" full access to the interactive windowstation and desktop, "winsta0\\default". The access control entry (ACE) for each object is based on franki's logon SID. The code executes the Cmd.exe file.

Note An application that runs many processes such as a scheduler service may want to remove the new ACE after the process has completed because the ACEs accumulate on the DACL of both the windowstation and desktop object.

For more information, click the following article number to view the article in the Microsoft Knowledge Base:

185292 SetUserObjectSecurity returns ERROR_NOT_ENOUGH_QUOTA

   #define RTN_OK     0
   #define RTN_ERROR 13

   #define WINSTA_ALL (WINSTA_ACCESSCLIPBOARD  | WINSTA_ACCESSGLOBALATOMS |
   WINSTA_CREATEDESKTOP    | WINSTA_ENUMDESKTOPS      |
   WINSTA_ENUMERATE        | WINSTA_EXITWINDOWS       |
   WINSTA_READATTRIBUTES   | WINSTA_READSCREEN        |
   WINSTA_WRITEATTRIBUTES  | DELETE                   |
   READ_CONTROL            | WRITE_DAC                |
   WRITE_OWNER)

   #define DESKTOP_ALL (DESKTOP_CREATEMENU      | DESKTOP_CREATEWINDOW  |
   DESKTOP_ENUMERATE       | DESKTOP_HOOKCONTROL   |
   DESKTOP_JOURNALPLAYBACK | DESKTOP_JOURNALRECORD |
   DESKTOP_READOBJECTS     | DESKTOP_SWITCHDESKTOP |
   DESKTOP_WRITEOBJECTS    | DELETE                |
   READ_CONTROL            | WRITE_DAC             |
   WRITE_OWNER)

   #define GENERIC_ACCESS (GENERIC_READ    | GENERIC_WRITE |
   GENERIC_EXECUTE | GENERIC_ALL)

   #include <windows.h>
   #include <stdio.h>

   BOOL ObtainSid(

        HANDLE hToken,           // Handle to an process access token.
        PSID   *psid             // ptr to the buffer of the logon sid
        );

   void RemoveSid(
        PSID *psid               // ptr to the buffer of the logon sid
        );

   BOOL AddTheAceWindowStation(

        HWINSTA hwinsta,         // handle to a windowstation
        PSID    psid             // logon sid of the process
        );

   BOOL AddTheAceDesktop(

        HDESK hdesk,             // handle to a desktop
        PSID  psid               // logon sid of the process
        );

   int main(void)
   {
        HANDLE              hToken;
        HDESK               hdesk;
        HWINSTA             hwinsta;
        PROCESS_INFORMATION pi;
        PSID                psid;
        STARTUPINFO         si;

        // 
        // obtain an access token for the user fester
        // 
        if (!LogonUser(
             "franki",
             NULL,
             "franki",
             LOGON32_LOGON_INTERACTIVE,
             LOGON32_PROVIDER_DEFAULT,
             &hToken
             ))
             return RTN_ERROR;

        // 
        // obtain a handle to the interactive windowstation
        // 
        hwinsta = OpenWindowStation(
             "winsta0",
             FALSE,
             READ_CONTROL | WRITE_DAC
             );
        if (hwinsta == NULL)
             return RTN_ERROR;

        HWINSTA hwinstaold = GetProcessWindowStation();

        // 
        // set the windowstation to winsta0 so that you obtain the
        // correct default desktop
        // 
        if (!SetProcessWindowStation(hwinsta))
             return RTN_ERROR;

        // 
        // obtain a handle to the "default" desktop
        // 
        hdesk = OpenDesktop(
             "default",
             0,
             FALSE,
             READ_CONTROL | WRITE_DAC |
             DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS
             );
        if (hdesk == NULL)
             return RTN_ERROR;

        // 
        // obtain the logon sid of the user fester
        // 
        if (!ObtainSid(hToken, &psid))
             return RTN_ERROR;

        // 
        // add the user to interactive windowstation
        // 
        if (!AddTheAceWindowStation(hwinsta, psid))
             return RTN_ERROR;

        // 
        // add user to "default" desktop
        // 
        if (!AddTheAceDesktop(hdesk, psid))
             return RTN_ERROR;

        // 
        // free the buffer for the logon sid
        // 
        RemoveSid(&psid);

        // 
        // close the handles to the interactive windowstation and desktop
        // 
        CloseWindowStation(hwinsta);

        CloseDesktop(hdesk);

        // 
        // initialize STARTUPINFO structure
        // 
        ZeroMemory(&si, sizeof(STARTUPINFO));
        si.cb        = sizeof(STARTUPINFO);
        si.lpDesktop = "winsta0\\default";

        // 
        // start the process
        // 
        if (!CreateProcessAsUser(
             hToken,
             NULL,
             "cmd.exe",
             NULL,
             NULL,
             FALSE,
             NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE,
             NULL,
             NULL,
             &si,
             &pi
             ))
             return RTN_ERROR;

        SetProcessWindowStation(hwinstaold); //set it back

        // 
        // close the handles
        // 
        CloseHandle(pi.hProcess);

        CloseHandle(pi.hThread);

        return RTN_OK;
   }


   BOOL ObtainSid(HANDLE hToken, PSID *psid)

      {
        BOOL                    bSuccess = FALSE; // assume function will
                                                  // fail
        DWORD                   dwIndex;
        DWORD                   dwLength = 0;
        TOKEN_INFORMATION_CLASS tic      = TokenGroups;
        PTOKEN_GROUPS           ptg      = NULL;

        __try
             {
             // 
             // determine the size of the buffer
        // 
             if (!GetTokenInformation(
             hToken,
             tic,
             (LPVOID)ptg,
             0,
             &dwLength
             ))
                  {
                  if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
                       {
                       ptg = (PTOKEN_GROUPS)HeapAlloc(
                            GetProcessHeap(),
                  HEAP_ZERO_MEMORY,
                  dwLength
                  );
                       if (ptg == NULL)
                            __leave;
                       }
                   else
                       __leave;
         }

             // 
             // obtain the groups the access token belongs to
             // 
             if (!GetTokenInformation(
                  hToken,
             tic,
             (LPVOID)ptg,
             dwLength,
             &dwLength
             ))
                  __leave;

             // 
             // determine which group is the logon sid
             // 
             for (dwIndex = 0; dwIndex < ptg->GroupCount; dwIndex++)
                  {
             if ((ptg->Groups[dwIndex].Attributes & SE_GROUP_LOGON_ID)
                  ==  SE_GROUP_LOGON_ID)
                       {
                       // 
                       // determine the length of the sid
                       // 
                       dwLength = GetLengthSid(ptg->Groups[dwIndex].Sid);

                       // 
                       // allocate a buffer for the logon sid
                       // 
                       *psid = (PSID)HeapAlloc(
                            GetProcessHeap(),
                  HEAP_ZERO_MEMORY,
                  dwLength
                  );
                  if (*psid == NULL)
                       __leave;

                  // 
                  // obtain a copy of the logon sid
                  // 
                  if (!CopySid(dwLength, *psid, ptg->Groups[dwIndex].Sid))
                       __leave;

                  // 
                  // break out of the loop because the logon sid has been
                  // found
                  // 
                  break;
                  }
             }

             // 
             // indicate success
             // 
             bSuccess = TRUE;
             }
        __finally
             {
             // 
        // free the buffer for the token group
        // 
             if (ptg != NULL)
                  HeapFree(GetProcessHeap(), 0, (LPVOID)ptg);
             }

        return bSuccess;

   }

   void RemoveSid(PSID *psid)
   {
        HeapFree(GetProcessHeap(), 0, (LPVOID)*psid);
   }

   BOOL AddTheAceWindowStation(HWINSTA hwinsta, PSID psid)
   {

        ACCESS_ALLOWED_ACE   *pace;
        ACL_SIZE_INFORMATION aclSizeInfo;
        BOOL                 bDaclExist;
        BOOL                 bDaclPresent;
        BOOL                 bSuccess  = FALSE; // assume function will
                                                //fail
        DWORD                dwNewAclSize;
        DWORD                dwSidSize = 0;
        DWORD                dwSdSizeNeeded;
        PACL                 pacl;
        PACL                 pNewAcl;
        PSECURITY_DESCRIPTOR psd       = NULL;
        PSECURITY_DESCRIPTOR psdNew    = NULL;
        PVOID                pTempAce;
        SECURITY_INFORMATION si        = DACL_SECURITY_INFORMATION;
        unsigned int         i;

        __try
             {
             // 
             // obtain the dacl for the windowstation
             // 
             if (!GetUserObjectSecurity(
                  hwinsta,
             &si,
             psd,
                  dwSidSize,
             &dwSdSizeNeeded
                  ))
             if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
                  {
                  psd = (PSECURITY_DESCRIPTOR)HeapAlloc(
                       GetProcessHeap(),
                       HEAP_ZERO_MEMORY,
                       dwSdSizeNeeded
             );
                  if (psd == NULL)
                       __leave;

                  psdNew = (PSECURITY_DESCRIPTOR)HeapAlloc(
                       GetProcessHeap(),
                       HEAP_ZERO_MEMORY,
                       dwSdSizeNeeded
                       );
                  if (psdNew == NULL)
                       __leave;

                  dwSidSize = dwSdSizeNeeded;

                  if (!GetUserObjectSecurity(
                       hwinsta,
                       &si,
                       psd,
                       dwSidSize,
                       &dwSdSizeNeeded
                       ))
                       __leave;
         }
             else
                   __leave;

             // 
             // create a new dacl
        // 
             if (!InitializeSecurityDescriptor(
                  psdNew,
                  SECURITY_DESCRIPTOR_REVISION
                  ))
                  __leave;

             // 
        // get dacl from the security descriptor
             // 
             if (!GetSecurityDescriptorDacl(
                  psd,
                  &bDaclPresent,
                  &pacl,
                  &bDaclExist
                  ))
                  __leave;

             // 
             // initialize
             // 
             ZeroMemory(&aclSizeInfo, sizeof(ACL_SIZE_INFORMATION));
             aclSizeInfo.AclBytesInUse = sizeof(ACL);

             // 
             // call only if the dacl is not NULL
             // 
             if (pacl != NULL)
                  {
                  // get the file ACL size info
                  if (!GetAclInformation(
                       pacl,
                       (LPVOID)&aclSizeInfo,
                       sizeof(ACL_SIZE_INFORMATION),
                       AclSizeInformation
                       ))
                       __leave;
                   }

             // 
             // compute the size of the new acl
             // 
             dwNewAclSize = aclSizeInfo.AclBytesInUse + (2 *
             sizeof(ACCESS_ALLOWED_ACE)) + (2 * GetLengthSid(psid)) - (2 *
             sizeof(DWORD));

             // 
             // allocate memory for the new acl
             // 
             pNewAcl = (PACL)HeapAlloc(
                  GetProcessHeap(),
                  HEAP_ZERO_MEMORY,
                  dwNewAclSize
                  );
             if (pNewAcl == NULL)
                  __leave;

             // 
             // initialize the new dacl
             // 
             if (!InitializeAcl(pNewAcl, dwNewAclSize, ACL_REVISION))
                  __leave;

             // 
             // if DACL is present, copy it to a new DACL
             // 
             if (bDaclPresent) // only copy if DACL was present
                  {
                  // copy the ACEs to our new ACL
                  if (aclSizeInfo.AceCount)
                       {
                       for (i=0; i < aclSizeInfo.AceCount; i++)
                            {
                            // get an ACE
                            if (!GetAce(pacl, i, &pTempAce))
                                 __leave;

                            // add the ACE to the new ACL
                            if (!AddAce(
                  pNewAcl,
                                 ACL_REVISION,
                                 MAXDWORD,
                                 pTempAce,
                  ((PACE_HEADER)pTempAce)->AceSize
                                 ))
                                 __leave;
                             }
                        }
                  }

             // 
             // add the first ACE to the windowstation
             // 
             pace = (ACCESS_ALLOWED_ACE *)HeapAlloc(
                  GetProcessHeap(),
                  HEAP_ZERO_MEMORY,
             sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) -
                  sizeof(DWORD
                  ));
             if (pace == NULL)
                  __leave;

             pace->Header.AceType  = ACCESS_ALLOWED_ACE_TYPE;
             pace->Header.AceFlags = CONTAINER_INHERIT_ACE |
                                     INHERIT_ONLY_ACE      |

                                     OBJECT_INHERIT_ACE;
             pace->Header.AceSize  = sizeof(ACCESS_ALLOWED_ACE) +

                                     GetLengthSid(psid) - sizeof(DWORD);
             pace->Mask            = GENERIC_ACCESS;

             if (!CopySid(GetLengthSid(psid), &pace->SidStart, psid))
                  __leave;

             if (!AddAce(
                  pNewAcl,
                  ACL_REVISION,
             MAXDWORD,
                  (LPVOID)pace,
                  pace->Header.AceSize
                  ))
                  __leave;

             // 
             // add the second ACE to the windowstation
             // 
             pace->Header.AceFlags = NO_PROPAGATE_INHERIT_ACE;
             pace->Mask            = WINSTA_ALL;

             if (!AddAce(
                  pNewAcl,
                  ACL_REVISION,
                  MAXDWORD,
                  (LPVOID)pace,
                  pace->Header.AceSize
                  ))
                  __leave;

                  // 
                  // set new dacl for the security descriptor
                  // 
                  if (!SetSecurityDescriptorDacl(
                       psdNew,
                       TRUE,
                       pNewAcl,
                       FALSE
                       ))
                       __leave;

                   // 
         // set the new security descriptor for the windowstation
         // 
         if (!SetUserObjectSecurity(hwinsta, &si, psdNew))
            __leave;

         // 
         // indicate success
         // 
         bSuccess = TRUE;
             }
        __finally
             {
             // 
             // free the allocated buffers
             // 
             if (pace != NULL)
                  HeapFree(GetProcessHeap(), 0, (LPVOID)pace);

             if (pNewAcl != NULL)
                  HeapFree(GetProcessHeap(), 0, (LPVOID)pNewAcl);

             if (psd != NULL)
                  HeapFree(GetProcessHeap(), 0, (LPVOID)psd);

             if (psdNew != NULL)
                  HeapFree(GetProcessHeap(), 0, (LPVOID)psdNew);
             }

        return bSuccess;

   }

   BOOL AddTheAceDesktop(HDESK hdesk, PSID psid)
   {

        ACL_SIZE_INFORMATION aclSizeInfo;
        BOOL                 bDaclExist;
        BOOL                 bDaclPresent;
        BOOL                 bSuccess  = FALSE; // assume function will
                                                // fail
        DWORD                dwNewAclSize;
        DWORD                dwSidSize = 0;
        DWORD                dwSdSizeNeeded;
        PACL                 pacl;
        PACL                 pNewAcl;
        PSECURITY_DESCRIPTOR psd       = NULL;
        PSECURITY_DESCRIPTOR psdNew    = NULL;
        PVOID                pTempAce;
        SECURITY_INFORMATION si        = DACL_SECURITY_INFORMATION;
        unsigned int         i;

        __try
             {
             // 
             // obtain the security descriptor for the desktop object
             // 
             if (!GetUserObjectSecurity(
                  hdesk,
                  &si,
                  psd,
                  dwSidSize,
                  &dwSdSizeNeeded
                  ))
                  {
                  if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
                       {
                       psd = (PSECURITY_DESCRIPTOR)HeapAlloc(
                            GetProcessHeap(),
                            HEAP_ZERO_MEMORY,
             dwSdSizeNeeded
             );
                       if (psd == NULL)
                            __leave;

                       psdNew = (PSECURITY_DESCRIPTOR)HeapAlloc(
                            GetProcessHeap(),
                            HEAP_ZERO_MEMORY,
                            dwSdSizeNeeded
             );
                       if (psdNew == NULL)
                            __leave;

                       dwSidSize = dwSdSizeNeeded;

                       if (!GetUserObjectSecurity(
                            hdesk,
                            &si,
                            psd,
                            dwSidSize,
                            &dwSdSizeNeeded
                            ))
                            __leave;
                       }
                  else
                       __leave;
                  }

             // 
             // create a new security descriptor
             // 
             if (!InitializeSecurityDescriptor(
                  psdNew,
                  SECURITY_DESCRIPTOR_REVISION
                  ))
               _   _leave;

             // 
             // obtain the dacl from the security descriptor
             // 
             if (!GetSecurityDescriptorDacl(
                  psd,
                  &bDaclPresent,
                  &pacl,
                  &bDaclExist
                  ))
                  __leave;

             // 
             // initialize
             // 
             ZeroMemory(&aclSizeInfo, sizeof(ACL_SIZE_INFORMATION));
             aclSizeInfo.AclBytesInUse = sizeof(ACL);

             // 
             // call only if NULL dacl
             // 
             if (pacl != NULL)
                  {
                  // 
                  // determine the size of the ACL info
                  // 
                  if (!GetAclInformation(
                       pacl,
                       (LPVOID)&aclSizeInfo,
                       sizeof(ACL_SIZE_INFORMATION),
                       AclSizeInformation
                       ))
                       __leave;
                   }

             // 
             // compute the size of the new acl
             // 
        dwNewAclSize = aclSizeInfo.AclBytesInUse +
                            sizeof(ACCESS_ALLOWED_ACE) +
                            GetLengthSid(psid) - sizeof(DWORD);

             // 
             // allocate buffer for the new acl
             // 
             pNewAcl = (PACL)HeapAlloc(
                  GetProcessHeap(),
                  HEAP_ZERO_MEMORY,
                  dwNewAclSize
                  );
             if (pNewAcl == NULL)
                  __leave;

             // 
             // initialize the new acl
             // 
             if (!InitializeAcl(pNewAcl, dwNewAclSize, ACL_REVISION))
                  __leave;

             // 
             // if DACL is present, copy it to a new DACL
             // 
             if (bDaclPresent) // only copy if DACL was present
                  {
                  // copy the ACEs to our new ACL
                  if (aclSizeInfo.AceCount)
                       {
                       for (i=0; i < aclSizeInfo.AceCount; i++)
                            {
                            // get an ACE
                            if (!GetAce(pacl, i, &pTempAce))
                                 __leave;

                            // add the ACE to the new ACL
                            if (!AddAce(
                                 pNewAcl,
                                 ACL_REVISION,
                                 MAXDWORD,
                                 pTempAce,
                                 ((PACE_HEADER)pTempAce)->AceSize
                                 ))
                                 __leave;
                             }
                        }
                  }

             // 
             // add ace to the dacl
             // 
             if (!AddAccessAllowedAce(
                  pNewAcl,
                  ACL_REVISION,
                  DESKTOP_ALL,
                  psid
                  ))
                  __leave;

             // 
             // set new dacl to the new security descriptor
             // 
             if (!SetSecurityDescriptorDacl(
                       psdNew,
                       TRUE,
                       pNewAcl,
                       FALSE
                       ))
                  __leave;

             // 
             // set the new security descriptor for the desktop object
             // 
             if (!SetUserObjectSecurity(hdesk, &si, psdNew))
                  __leave;

             // 
             // indicate success
             // 
             bSuccess = TRUE;
             }
        __finally
            {
            // 
            // free buffers
            // 
            if (pNewAcl != NULL)
                 HeapFree(GetProcessHeap(), 0, (LPVOID)pNewAcl);

             if (psd != NULL)
                  HeapFree(GetProcessHeap(), 0, (LPVOID)psd);

             if (psdNew != NULL)
                  HeapFree(GetProcessHeap(), 0, (LPVOID)psdNew);
             }

        return bSuccess;
   }
				

Modification Type:MajorLast Reviewed:7/19/2005
Keywords:kbAPI kbcode kberrmsg kbFAQ kbinfo kbKernBase kbSecurity kbThread KB165194 kbAudDeveloper