Solved

Error message while trying to impersonate.

Posted on 2004-10-18
11
1,395 Views
Last Modified: 2010-05-18
Hi,

I have a program that I wrote a couple of years ago and I user it extensively. This program makes use of SSPI to perform impersonation, granting me admin access to a thread which I use to create a service. The service then performs work for end users that do not have admin access to their machines.

I have customized it for a new delivery system, and it works on the majority of test machines. One one though, I sucessfully perform the impersonation, test the token for admin access (which I have) and yet when I try to create the service, my first call, which is to OpenSCManager, returns a '0' (fails) and GetLastError () provides me with this info.
"Either a required impersonation level was not provided, or the provided impersonation level is invalid".

I could accept this more readily if it failed on all systems, but this has been a rock solid service launcher for me the last couple of years. Could there be something in the local policy that would cause this, or maybe I just need to clean up a call or two.

The program is so long, I don't really know what to include or what to leave out, so I'll start with these segments...setting up the impersonation and the call to create the service.

Thanks for any and all help,
Jeff

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
      char                        szCompName[17];
      DWORD                        dwNameSize = 17;
      DWORD                        dwLevel = 102;
      LPWKSTA_INFO_102      pBuf = NULL;
      char                        szLogonError[] = "The system was unable to authenticate administrative logon credentials. Please contact the Verizon Solution Center on 1-813-978-2828 for further assistance.";
      char                        szErrBuf[BUFSIZE];
      HANDLE                        hToken = NULL;
      HKEY                        hKey = 0;
      static char                  lpDelSvcKey[] = "SYSTEM\\CurrentControlSet\\Services\\Profile Translator Service";
      long                        lResult = 0;
      int                              i = 0;
      BOOL                        bFound = FALSE;

      /*-->

            since this logon token is for the administrator
          or lanadmin of the local PC, we must call  
        GetComputerName. This will provide the NetBios
        name of the PC with which we then populate the
            domain field

            since this logon token is for the administrator
            or lanadmin of the local PC, we must call  
            GetComputerName. This will provide the NetBios
            name of the PC with which we then populate the
            domain field

            In this modified version, we need to get the user's
            logon domain. It may be either ba-ny, or ba-ma. We will
            use the NetWkstaGetInfo API to get this info.
      */


      //GetComputerName (szCompName, &dwNameSize);

      hToken = SSPLogonUser (szDomain, szUser, szPwd);
      if (hToken == NULL)
      {
            wsprintf (szErrBuf, "No valid accounts were found for this computer.\n");
            nResult = MessageBox (NULL, szErrBuf,
                              "Authentication Failure", MB_ICONWARNING);
      }            
      else
      {
            ImpersonateLoggedOnUser (hToken);
            // Just tracing
            {
                  char name[256];
                  DWORD dwSize;
                  name[0] = 0;
                  dwSize = sizeof(name);
                  GetUserNameA(name, &dwSize);
            }
            if (!IsAdmin())
            {
                  RevertToSelf();
                  hToken = NULL;
            }
            else
            {
                  CmdInstallService();
            }
      }

      return 0;
}
********************************************************
//////////////////////////////////////////////////////////////////////////////
void CmdInstallService()
{
      SC_HANDLE      hService;
      SC_HANDLE      hSCM;
      SC_HANDLE      hSCManager;
      SC_HANDLE      schService;
      DWORD            dwError;
      TCHAR            szErr[256];

      // Open the SCM on this machine.
      hSCM = OpenSCManager(      NULL,
                        NULL,
                        SC_MANAGER_CREATE_SERVICE);

      //have another handle so if the service already exists, we can simply start it.
      hSCManager = OpenSCManager(      NULL,
                        NULL,
                        SC_MANAGER_ALL_ACCESS);

      if ( hSCM )
      {
            hService = CreateService(hSCM,
                        TEXT(SZSERVICENAME),
                        TEXT(SZSERVICEDISPLAYNAME),
                        SERVICE_ALL_ACCESS,
                        SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS ,
                        SERVICE_DEMAND_START,
                        SERVICE_ERROR_IGNORE,
                        g_szAppPath,
                        NULL,
                        NULL,
                        TEXT(SZDEPENDENCIES),
                        NULL,
                        NULL);

            if (!hService )
            {
                  dwError = GetLastError();
                  if (dwError == ERROR_SERVICE_EXISTS)
                  {
                        schService = OpenService (
                                    hSCManager,
                                    "Profile Translator Service",
                                    SERVICE_ALL_ACCESS);
                        if (schService == NULL)
                        {
                              nResult = MessageBox (NULL, "Could not Open Service",
                                          "Service Open Failure", MB_ICONWARNING);
                        }
                        if (!StartService (
                                          schService,
                                          0,
                                          NULL))
                        {
                              nResult = MessageBox (NULL, "Could not Start Service",
                                          "Service Start Failure", MB_ICONWARNING);
                        }
                  }
                  else
                  {
                        switch (dwError)
                        {
                        case ERROR_ACCESS_DENIED:
                              nResult = MessageBox (NULL, "CreateService failed -> Access denied",
                                          "Authentication Failure", MB_ICONWARNING);
                              //fprintf (fp,"CreateService failed -> Access denied\n");
                              break;
                        case ERROR_CIRCULAR_DEPENDENCY:
                              nResult = MessageBox (NULL, "CreateService failed -> Circular dependency",
                                          "Authentication Failure", MB_ICONWARNING);
                              //fprintf (fp,"CreateService failed -> Circular dependency\n");
                              break;
                        case ERROR_DUP_NAME:
                              nResult = MessageBox (NULL, "CreateService failed -> duplicate name",
                                          "Authentication Failure", MB_ICONWARNING);
                              //fprintf (fp,"CreateService failed -> duplicate name\n");
                              break;
                        case ERROR_INVALID_HANDLE:
                              //fprintf (fp,"CreateService failed -> invalid handle\n");
                              nResult = MessageBox (NULL, "CreateService failed -> invalid handle",
                                          "Authentication Failure", MB_ICONWARNING);
                              break;
                        case ERROR_INVALID_NAME:
                              nResult = MessageBox (NULL, "CreateService failed -> invalid name",
                                          "Authentication Failure", MB_ICONWARNING);
                              //fprintf (fp,"CreateService failed -> invalid name\n");
                              break;
                        case ERROR_INVALID_PARAMETER:
                              //fprintf (fp,"CreateService failed -> invalid parameter\n");
                              nResult = MessageBox (NULL, "CreateService failed -> invalid parameter",
                                          "Authentication Failure", MB_ICONWARNING);
                              break;
                        case ERROR_INVALID_SERVICE_ACCOUNT:
                              //fprintf (fp,"CreateService failed -> invalid service account\n");
                              nResult = MessageBox (NULL, "CreateService failed -> invalid service account",
                                          "Authentication Failure", MB_ICONWARNING);
                              break;
                        case ERROR_SERVICE_EXISTS:
                              //fprintf (fp,"CreateService failed -> service exists\n");
                              nResult = MessageBox (NULL, "CreateService failed -> service exists",
                                          "Authentication Failure", MB_ICONWARNING);
                              break;
                        default:
                              //fprintf (fp,"CreateService failed -> none of the above\n");
                              nResult = MessageBox (NULL, "CreateService failed -> none of the above",
                                          "Authentication Failure", MB_ICONWARNING);
                              break;
                        }
                  }
            }
            else
            {
                  //fprintf (fp, "Service created..\n");
                  StartService (hService, NULL, NULL);
            }

            CloseServiceHandle(hSCM);
      }
      else
              _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
}
0
Comment
Question by:jpetter
  • 5
  • 3
  • 3
11 Comments
 
LVL 86

Expert Comment

by:jkr
Comment Utility
One thing - you are opening two SCM handles in the above and you are only closing one of them...
0
 

Author Comment

by:jpetter
Comment Utility
Ah, right you are. I'll make the change, though unfortunately I odn't think it has anything to do with me not being able to open it.

Thanks,
Jeff
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
BTW: Does 'OpenSCManager()' fail for the 1st or the 2nd handle?
0
 

Author Comment

by:jpetter
Comment Utility
It fails for both, and both return the same error.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
What is the implementation of IsAdmin() ?

If it fails, RevertToSelf terminates the impersonation and OpenSCManager fails also.

Regards, Alex
0
Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

 

Author Comment

by:jpetter
Comment Utility
Here is the IsAdmin function, which does succeed, as does the impersonation. If the impersonation fails, the call to create the service never occurs....or it shouldn't.

Here is the code from IsAdmin, and it's pretty much straight out of MSDN

Thanks,
Jeff

BOOL IsAdmin(void) {

   HANDLE hToken;
   DWORD  dwStatus;
   DWORD  dwAccessMask;
   DWORD  dwAccessDesired;
   DWORD  dwACLSize;
   DWORD  dwStructureSize = sizeof(PRIVILEGE_SET);
   PACL   pACL            = NULL;
   PSID   psidAdmin       = NULL;
   BOOL   bReturn         = FALSE;

   PRIVILEGE_SET   ps;
   GENERIC_MAPPING GenericMapping;

   PSECURITY_DESCRIPTOR     psdAdmin           = NULL;
   SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY;
   
   __try {

      // AccessCheck() requires an impersonation token.
      ImpersonateSelf(SecurityImpersonation);

      if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE,
            &hToken)) {

         if (GetLastError() != ERROR_NO_TOKEN)
            __leave;

         // If the thread does not have an access token, we'll
         // examine the access token associated with the process.
         if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY,
               &hToken))
            __leave;
      }

      if (!AllocateAndInitializeSid(&SystemSidAuthority, 2,
            SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
            0, 0, 0, 0, 0, 0, &psidAdmin))
         __leave;

      psdAdmin = LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
      if (psdAdmin == NULL)
         __leave;

      if (!InitializeSecurityDescriptor(psdAdmin,
            SECURITY_DESCRIPTOR_REVISION))
         __leave;
 
      // Compute size needed for the ACL.
      dwACLSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) +
            GetLengthSid(psidAdmin) - sizeof(DWORD);

      // Allocate memory for ACL.
      pACL = (PACL)LocalAlloc(LPTR, dwACLSize);
      if (pACL == NULL)
         __leave;

      // Initialize the new ACL.
      if (!InitializeAcl(pACL, dwACLSize, ACL_REVISION2))
         __leave;

      dwAccessMask= ACCESS_READ | ACCESS_WRITE;
     
      // Add the access-allowed ACE to the DACL.
      if (!AddAccessAllowedAce(pACL, ACL_REVISION2,
            dwAccessMask, psidAdmin))
         __leave;

      // Set the DACL to the SD.
      if (!SetSecurityDescriptorDacl(psdAdmin, TRUE, pACL, FALSE))
         __leave;

      // AccessCheck is sensitive about what is in the SD; set
      // the group and owner.
      SetSecurityDescriptorGroup(psdAdmin, psidAdmin, FALSE);
      SetSecurityDescriptorOwner(psdAdmin, psidAdmin, FALSE);

      if (!IsValidSecurityDescriptor(psdAdmin))
         __leave;

      dwAccessDesired = ACCESS_READ;

      //
      // Initialize GenericMapping structure even though you
      // do not use generic rights.
      //
      GenericMapping.GenericRead    = ACCESS_READ;
      GenericMapping.GenericWrite   = ACCESS_WRITE;
      GenericMapping.GenericExecute = 0;
      GenericMapping.GenericAll     = ACCESS_READ | ACCESS_WRITE;

      if (!AccessCheck(psdAdmin, hToken, dwAccessDesired,
            &GenericMapping, &ps, &dwStructureSize, &dwStatus,
            &bReturn)) {
         printf("AccessCheck() failed with error %lu\n", GetLastError());
         __leave;
      }

      //RevertToSelf();
   
   } __finally {

      // Clean up.
      if (pACL) LocalFree(pACL);
      if (psdAdmin) LocalFree(psdAdmin);  
      if (psidAdmin) FreeSid(psidAdmin);
   }

   return bReturn;
}
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
If found a sample that uses the following prototype of SSPLogonUser

BOOL SSPLogonUser(
      LPTSTR DomainName,
      LPTSTR UserName,
      LPTSTR Password
      );

You see, it returns a BOOL rather than a hToken.  

Did you use LoginUser in a previous implementation? Maybe it works if the Handle is 1 (TRUE) and it fails if not.

Regards, Alex
0
 
LVL 39

Accepted Solution

by:
itsmeandnobodyelse earned 150 total points
Comment Utility
I found that link at Google:

     http://www.mcse.ms/archive108-2004-2-263824.html

It tells, that you may get a valid token after SSPLogonUser by calling QuerySecurityContextToken.

Regards, Alex
0
 

Author Comment

by:jpetter
Comment Utility
Alex,

A few years ago, I was trying to rewrite that routine so that I could loop through accounts that are known to have an administrative context on some machines, but there is not one account that works on all. I started the rewrite and had a few problems, so I used the last of my two free tech support calls that come with the product. One of Microsoft's tech support engineers helped me out. The resulting function is listed below.

Thanks,
Jeff
BTW, the call the SSPLogonUser succeeds, and I did not make previous calls to LogonUser as it requires three special privileges:"Act as part of the operating system", "Replace a process level token" and "Increase quotas". That was my motivation for writing the service, which by default running within the LSA context, has those privileges.

Here it is:
HANDLE WINAPI SSPLogonUser(LPTSTR szDomain, LPTSTR szUser, LPTSTR szPassword) {

   AUTH_SEQ    asServer   = {0};
   AUTH_SEQ    asClient   = {0};
   BOOL        fDone      = FALSE;
   BOOL        fResult    = FALSE;
   DWORD       cbOut      = 0;
   DWORD       cbIn       = 0;
   DWORD       cbMaxToken = 0;
   PVOID       pClientBuf = NULL;
   PVOID       pServerBuf = NULL;
   PSecPkgInfo pSPI       = NULL;
   HMODULE     hModule    = NULL;
   SECURITY_STATUS ss     = 0;
   HANDLE      hToken     = NULL;

   SEC_WINNT_AUTH_IDENTITY ai;

   __try {

      hModule = LoadSecurityDll();
      if (!hModule)
         __leave;

      // Get max token size
      _QuerySecurityPackageInfo(_T("NTLM"), &pSPI);
      cbMaxToken = pSPI->cbMaxToken;
      _FreeContextBuffer(pSPI);

      // Allocate buffers for client and server messages
      pClientBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbMaxToken);
      pServerBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbMaxToken);

      // Initialize auth identity structure
      ZeroMemory(&ai, sizeof(ai));
#if defined(UNICODE) || defined(_UNICODE)
      ai.Domain = szDomain;
      ai.DomainLength = lstrlen(szDomain);
      ai.User = szUser;
      ai.UserLength = lstrlen(szUser);
      ai.Password = szPassword;
      ai.PasswordLength = lstrlen(szPassword);
      ai.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
#else      
      ai.Domain = (unsigned char *)szDomain;
      ai.DomainLength = lstrlen(szDomain);
      ai.User = (unsigned char *)szUser;
      ai.UserLength = lstrlen(szUser);
      ai.Password = (unsigned char *)szPassword;
      ai.PasswordLength = lstrlen(szPassword);
      ai.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
#endif

      // Prepare client message (negotiate) .
      cbOut = cbMaxToken;
      if (!GenClientContext(&asClient, &ai, NULL, 0, pClientBuf, &cbOut, &fDone))
         __leave;

      // Prepare server message (challenge) .
      cbIn = cbOut;
      cbOut = cbMaxToken;
      if (!GenServerContext(&asServer, pClientBuf, cbIn, pServerBuf, &cbOut,
            &fDone))
         __leave;
         // Most likely failure: AcceptServerContext fails with SEC_E_LOGON_DENIED
         // in the case of bad szUser or szPassword.
         // Unexpected Result: Logon will succeed if you pass in a bad szUser and
         // the guest account is enabled in the specified domain.

      // Prepare client message (authenticate) .
      cbIn = cbOut;
      cbOut = cbMaxToken;
      if (!GenClientContext(&asClient, &ai, pServerBuf, cbIn, pClientBuf, &cbOut,
            &fDone))
         __leave;

      // Prepare server message (authentication) .
      cbIn = cbOut;
      cbOut = cbMaxToken;
      if (!GenServerContext(&asServer, pClientBuf, cbIn, pServerBuf, &cbOut,
            &fDone))
         __leave;

      ss = _ImpersonateSecurityContext(&asServer.hctxt);
      if (!SEC_SUCCESS(ss))
      {
         //fprintf( fp, "ImpersonateSecurityContext failed with %08X\n", ss);
         __leave;
      }

      if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY|TOKEN_IMPERSONATE, TRUE, &hToken))
      {
         //fprintf( fp, "OpenThreadToken failed with %d\n", GetLastError());
         __leave;
      }

      ss = _RevertSecurityContext(&asServer.hctxt);
      if (!SEC_SUCCESS(ss))
      {
         //fprintf( fp, "RevertSecurityContext failed with %08X\n", ss);
         __leave;
      }

      fResult = TRUE;

   } __finally {

      // Clean up resources
      if (asClient.fHaveCtxtHandle)
         _DeleteSecurityContext(&asClient.hctxt);

      if (asClient.fHaveCredHandle)
         _FreeCredentialsHandle(&asClient.hcred);

      if (asServer.fHaveCtxtHandle)
         _DeleteSecurityContext(&asServer.hctxt);

      if (asServer.fHaveCredHandle)
         _FreeCredentialsHandle(&asServer.hcred);

      if (hModule)
         UnloadSecurityDll(hModule);

      HeapFree(GetProcessHeap(), 0, pClientBuf);
      HeapFree(GetProcessHeap(), 0, pServerBuf);

      if (fResult == FALSE)
      {
         CloseHandle(hToken);
         hToken = NULL;
      }
   }

   return hToken;
}
0
 
LVL 86

Assisted Solution

by:jkr
jkr earned 350 total points
Comment Utility
This might be a shot in the dark, but test this code to check for admin privileges:

/*------------------------------------------------------------------
| Name: RunningAsAdministrator
| Desc: checks if user has administrator privileges
| Notes: This function returns TRUE if the user identifier associated with
|   this process is a member of the the Administrators group.
------------------------------------------------------------------*/
BOOL RunningAsAdministrator ( VOID)
{
 BOOL   fAdmin;
 HANDLE  hThread;
 TOKEN_GROUPS *ptg = NULL;
 DWORD  cbTokenGroups;
 DWORD  dwGroup;
 PSID   psidAdmin;

 SID_IDENTIFIER_AUTHORITY SystemSidAuthority= SECURITY_NT_AUTHORITY;

 // First we must open a handle to the access token for this thread.

 if ( !OpenThreadToken ( GetCurrentThread(), TOKEN_QUERY, FALSE, &hThread))
 {
  if ( GetLastError() == ERROR_NO_TOKEN)
  {
   // If the thread does not have an access token, we'll examine the
   // access token associated with the process.

   if (! OpenProcessToken ( GetCurrentProcess(), TOKEN_QUERY,
                 &hThread))
   return ( FALSE);
  }
  else
   return ( FALSE);
 }

 // Then we must query the size of the group information associated with
 // the token. Note that we expect a FALSE result from GetTokenInformation
 // because we've given it a NULL buffer. On exit cbTokenGroups will tell
 // the size of the group information.

 if ( GetTokenInformation ( hThread, TokenGroups, NULL, 0, &cbTokenGroups))
  return ( FALSE);

 // Here we verify that GetTokenInformation failed for lack of a large
 // enough buffer.

 if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER)
  return ( FALSE);

 // Now we allocate a buffer for the group information.
 // Since _alloca allocates on the stack, we don't have
 // to explicitly deallocate it. That happens automatically
 // when we exit this function.

 if ( ! ( ptg= _alloca ( cbTokenGroups)))
  return ( FALSE);

 // Now we ask for the group information again.
 // This may fail if an administrator has added this account
 // to an additional group between our first call to
 // GetTokenInformation and this one.

 if ( !GetTokenInformation ( hThread, TokenGroups, ptg, cbTokenGroups,
          &cbTokenGroups) )
  return ( FALSE);

 // Now we must create a System Identifier for the Admin group.

 if ( ! AllocateAndInitializeSid ( &SystemSidAuthority, 2,
            SECURITY_BUILTIN_DOMAIN_RID,
            DOMAIN_ALIAS_RID_ADMINS,
            0, 0, 0, 0, 0, 0, &psidAdmin) )
  return ( FALSE);

 // Finally we'll iterate through the list of groups for this access
 // token looking for a match against the SID we created above.

 fAdmin= FALSE;

 for ( dwGroup= 0; dwGroup < ptg->GroupCount; dwGroup++)
 {
  if ( EqualSid ( ptg->Groups[dwGroup].Sid, psidAdmin))
  {
   fAdmin = TRUE;

   break;
  }
 }

 // Before we exit we must explicity deallocate the SID we created.

 FreeSid ( psidAdmin);

 return ( fAdmin);
}
/* eof - RunningAsAdministrator */


Taken from http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/msdn_ntprog.asp ("Windows NT Security")
0
 

Author Comment

by:jpetter
Comment Utility
Thanks guys for all the suggestions, but I believe that this is a Microsoft issue. I found an article on MSKB that describes changes the impersonation process in Win2K with SP4, and from the results I've seen from testing, WinXP with SP2. Now, in order to impersonate, it looks like a user, or global group must be added to the User Rights Assignment of the Local Policy through the policy "Impersonate a client after authentication". So, even after successful impersonation, with an admin token, the impersonation token is of no use unless the security context of the calling process is included in the above mentioned policy. Errr...I think I'm going to look into and play around a bit with CreateProcessWithLogonW.

Thanks, and I'll split up the points, since this is an oddball case. If I'm missing something, please help me see the light.

Jeff

BTW, here is a link  http://support.microsoft.com/default.aspx?scid=kb;en-us;821546
0

Featured Post

Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

Join & Write a Comment

When writing generic code, using template meta-programming techniques, it is sometimes useful to know if a type is convertible to another type. A good example of when this might be is if you are writing diagnostic instrumentation for code to generat…
This article shows you how to optimize memory allocations in C++ using placement new. Applicable especially to usecases dealing with creation of large number of objects. A brief on problem: Lets take example problem for simplicity: - I have a G…
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.

772 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now