0

Have written a DLL that attempts to convert a mapped drive to its equivalent network UNC. But it fails when DLL is called by setup program running as an elevated process. As a potential fix I modified the example source offered by @RbMn from an answer to the following question: How to correctly detect Network drive when running with elevated privileges

The call to GetLogicalDrives worked just like it was stated it would. However when it makes a call to WNetGetConnection with a mapped drive letter the error returned is 1222 (ERROR_NO_NETWORK) thus not giving the associated UNC. I believe the problem stems from how I go about impersonating a logon. Since my knowledge of UAC matters is very limited, I am not certain what I have to alter about impersonating a logon in order to correctly get the information I need.

Any help is greatly appreciated.

Below is the actual code:

BOOL ConvertToMappedFolder(LPSTR pUncPath, LPSTR pMappedDrive)
{
   BOOL bRet = 0; 

if (1) 
{
  HANDLE hToken = NULL;
  ULONG rcb = 0;
  TOKEN_ELEVATION_TYPE tet = 0;
  TOKEN_LINKED_TOKEN tlt = { 0 };
  ULONG err = BOOL_TO_ERR(OpenProcessToken(GetCurrentProcess() /* NtCurrentProcess() */, TOKEN_QUERY, &hToken));

if (err == NOERROR)
{
  err = BOOL_TO_ERR(GetTokenInformation(hToken, TokenElevationType, &tet, sizeof(tet), &rcb));

  if (err == NOERROR)
  {
   if (tet == TokenElevationTypeFull)
   {
     err = BOOL_TO_ERR(GetTokenInformation(hToken, TokenLinkedToken, &tlt, sizeof(tlt), &rcb));

    if (err == NOERROR)
    {
      if (NOERROR == (err = BOOL_TO_ERR(SetThreadToken(0, tlt.LinkedToken))))
      {
         bRet = ConvertToMappedFolderEx(pUncPath, pMappedDrive); 
         SetThreadToken(0, 0);
      }

      CloseHandle(tlt.LinkedToken);
    }
  }
}
}
}

BOOL ConvertToMappedFolderEx(LPSTR pUncPath, LPSTR pMappedDrive)
{
int  nPos  = 0;
UINT nType = 0; 
char strDrive[MAX_PATH+1] = "?:\\"; 
DWORD dwDriveList = GetLogicalDrives();
BOOL bRet = FALSE; 

(*pMappedDrive) = 0; 

// Check each drive letter determining if it is a mapped drive...
while (dwDriveList) 
{
if (dwDriveList & 1) 
{
strDrive[0] = 0x41 + nPos;

nType = GetDriveType(strDrive);

// If drive unknown do not attempt to determine if its UNC matches up...
if (DRIVE_UNKNOWN != nType) 
{
char  szDeviceName[MAX_PATH+1] = ""; 
char  szDriveLetter[4] = " :"; 
DWORD dwResult = 0;
DWORD cchBuff  = MAX_PATH; 

szDriveLetter[0] = strDrive[0]; 
dwResult = WNetGetConnection(szDriveLetter, (LPSTR) szDeviceName, &cchBuff); 


if (NO_ERROR == dwResult) 
{ 
LPSTR pPath = _stristr(pUncPath, szDeviceName); 

if (NULL != pPath)
{
strcpy(pMappedDrive, szDriveLetter); 
strcat(pMappedDrive, (pUncPath + strlen(szDeviceName))); 

bRet = TRUE; 
break; 
}
}
}
}

dwDriveList >>= 1;
nPos++;
}

return (bRet); 
}
  • 1
    @HarryJohnston - no, `WNetGetConnection` or better `NetUseGetInfo` full support impersonation. simply it not support `SecurityIdentification` impersonation level. almost nobody accept this level. the `GetLogicalDrives` very rarely exception here. but for `WNetGetConnection` need `SecurityImpersonation` level. so need more code for implement in process query – RbMm Aug 24 '17 at 23:31
  • 1
    Keep in mind that the elevated token may have its own set of drive mappings - if the user has opened an elevated command window and used net use, for example - so you need to be sure which of the two you want. (It's rather odd for a setup program to care about drive mappings in the first place.) – Harry Johnston Aug 24 '17 at 23:41

1 Answers1

1

connection made using Microsoft LAN Manager is per logon session. more exactly it associated with a logon session LUID. this is stored in token and can be read from TOKEN_STATISTICS.AuthenticationId. so result for any network drive functions depend from your current token - thread (if you impersonate) or process. use different tokens - can give different results. the elevated and not elevated processes always run in different logon sessions (have different AuthenticationId in process token).

so no sense speak about network drives in windows. need speak about network drives in logon session. and different logon session have different set of network drives. we can do next - enumerate all currently running processes, for every process open it token and query AuthenticationId - then we can once impersonate and query for every new AuthenticationId or say we can query AuthenticationId for not elevated token, linked to our elevated, and then try found process with exactly this token, impersonate it, and query with this token.

code example:

void CheckDrives()
{
    LONG dwDriveList = GetLogicalDrives();

    WCHAR Drive[] = L"Z:";

    ULONG n = 1 + 'Z' - 'A';

    do 
    {
        if (_bittest(&dwDriveList, --n))
        {
            if (DRIVE_REMOTE == GetDriveTypeW(Drive))
            {
                PUSE_INFO_0 pui;

                if (NET_API_STATUS err = NetUseGetInfo(0, Drive, 0, (BYTE**)&pui))
                {
                    DbgPrint("%S error=%u\n", Drive, err);
                }
                else
                {
                    DbgPrint("%S -> %S\n", pui->ui0_local, pui->ui0_remote);
                    NetApiBufferFree(pui);
                }
            }
        }

        --*Drive;

    } while (n);
}

BOOL ImpersonateNotElevated(LUID AuthenticationId)
{
    BOOL fOk = FALSE;

    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    if (hSnapshot != INVALID_HANDLE_VALUE)
    {
        PROCESSENTRY32W pe = { sizeof(pe) };

        ULONG rcb;

        BOOL fFound = FALSE;

        if (Process32First(hSnapshot, &pe))
        {
            do 
            {
                if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID))
                {
                    HANDLE hToken, hNewToken;                   

                    if (OpenProcessToken(hProcess, TOKEN_QUERY|TOKEN_DUPLICATE, &hToken))
                    {
                        TOKEN_STATISTICS ts;

                        if (GetTokenInformation(hToken, TokenStatistics, &ts, sizeof(ts), &rcb))
                        {
                            if (ts.AuthenticationId.LowPart == AuthenticationId.LowPart && ts.AuthenticationId.HighPart == AuthenticationId.HighPart)
                            {
                                fFound = TRUE;

                                if (DuplicateToken(hToken, SecurityImpersonation, &hNewToken))
                                {
                                    fOk = SetThreadToken(0, hNewToken);

                                    CloseHandle(hNewToken);
                                }
                            }
                        }
                        CloseHandle(hToken);
                    }

                    CloseHandle(hProcess);
                }

            } while (!fFound && Process32Next(hSnapshot, &pe));
        }

        CloseHandle(hSnapshot);
    }

    return fOk;
}

void CheckDrivesNotElevated()
{
    HANDLE hToken;

    if (OpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken))
    {
        union {
            TOKEN_ELEVATION_TYPE tet;
            TOKEN_LINKED_TOKEN tlt;
        };

        ULONG rcb;

        if (GetTokenInformation(hToken, TokenElevationType, &tet, sizeof(tet), &rcb))
        {
            if (tet == TokenElevationTypeFull)
            {
                if (GetTokenInformation(hToken, TokenLinkedToken, &tlt, sizeof(tlt), &rcb))
                {
                    TOKEN_STATISTICS ts;

                    BOOL fOk = GetTokenInformation(tlt.LinkedToken, ::TokenStatistics, &ts, sizeof(ts), &rcb);

                    CloseHandle(tlt.LinkedToken);

                    if (fOk)
                    {
                        if (ImpersonateNotElevated(ts.AuthenticationId))
                        {
                            CheckDrives();
                            SetThreadToken(0, 0);
                        }
                    }
                }
            }
            else
            {
                CheckDrives();
            }
        }
        CloseHandle(hToken);
    }
}
RbMm
  • 31,280
  • 3
  • 35
  • 56