3

I am working on an application that needs to correctly detect all used drive letters on Windows. And I use the GetLogicalDrives() function for that. This function works just fine excepts when users launch my application with elevated privileges (i.e., the user logged in with an account belongs to 'Administrators' group and launch my application by choosing "Run as administrator" option). In this case, GetLogicalDrives() fails to detect mapped network drives on the machine.

It seems that the root cause of the problem is that in this scenario Windows runs 2 user sessions in parallel. My application runs in the "elevated privileges" session, while the drive is mapped in the "non-elevated" session:

https://support.microsoft.com/en-us/help/3035277/mapped-drives-are-not-available-from-an-elevated-prompt-when-uac-is-configured-to-prompt-for-credentials-in-windows

Is there any programmatic workaround for this problem? I tried to relaunch my application in the "non-elevated" session, but don't know how (or if it is even possible). What I tried already is relaunching my application with a restricted token (using CreateRestrictedTokenwith DISABLE_MAX_PRIVILEGE option), hoping that Windows will somehow figure out that it could now relaunch my application in a "non-elevated" session, but it did not work.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
trungdinhtrong
  • 2,604
  • 2
  • 17
  • 26
  • Can you explain why you need to know the mapped drive letters? – Jim Rhodes Mar 13 '17 at 15:41
  • I need to map a new drive letter, and as part of it, trying to figure out the already mapped one. In the above scenario that I described, my application failed to detect an already mapped drive letter (e.g., 'D'), and actually successfully mapped a new Drive to it ('D'). The problem is it would not show up in Explorer, which, I suppose, would run in the un-elevated privileges user session. – trungdinhtrong Mar 13 '17 at 16:01
  • 2
    You want your program (which is running elevated) to map a drive so that it shows up in non-elevated processes? It's going to be difficult at best (I certainly don't know how). Is setting `EnabledLinkedConnections` in the registry (as per the link you posted) an option? It would solve all your problems at once. – Michael Gunter Mar 13 '17 at 16:29
  • Thank you, Michael for the reply. Technically speaking, the problem was not that non-elevated processes cannot see my drive. If I map a drive correctly into an unused slot, then it would show up in Explorer. The problem here is my elevated process could not see some already mapped drive letter, and so it mapped to an used slot, and on Explorer we will only see the previously mapped drive. Also, I'm trying to avoid `EnabledLinkedConnections`, since that change will affect more than just my program. – trungdinhtrong Mar 13 '17 at 17:52
  • FYI, setting `EnabledLinkedConnections` works. But as I mentioned earlier, I'm trying to avoid it. – trungdinhtrong Mar 13 '17 at 18:11
  • 1
    You might want to have a look at this question http://stackoverflow.com/questions/42762595/mapping-network-drive-using-wnetaddconnection2-from-run-as-administrator-proce – Carey Gregory Mar 13 '17 at 18:40
  • @Carey I would love to see an answer with working code for that. – zett42 Mar 13 '17 at 18:45
  • 1
    `for relaunch my application in the "non-elevated" session` - need relaunch in specific logon session, where drive mapped. `CreateRestrictedToken` here not help because it not changed logon session in token. need open some token from this logon session and use it in call `CreateProcessAsUserW` – RbMm Mar 13 '17 at 19:06
  • @RbMm that makes sense. How do I open a token from the logged in session, though? – trungdinhtrong Mar 13 '17 at 19:12
  • first you need open own linked token - get `AuthenticationId` from here. then enumerate processes - search for token with same `AuthenticationId`. also for use `CreateProcessAsUserW` you need `SE_ASSIGNPRIMARYTOKEN_PRIVILEGE` - you can get it from some system process, while you enumerate processes. so code (for launch process in non elevated logon session to which your linked token refer) some complex ~150 lines) but working. but are this exactly what you need ? – RbMm Mar 13 '17 at 19:20
  • It works w/o `CreateProcessAsUserW`. I did a little test following steps 1..5 of [this sequence](https://blogs.msdn.microsoft.com/aaron_margosis/2009/06/06/faq-how-do-i-start-a-program-as-the-desktop-user-from-an-elevated-app/). Then `ImpersonateLoggedOnUser()` with the token of step 5. After that `GetLogicalDrives()` returned network drive that was mapped as standard user. – zett42 Mar 13 '17 at 19:57

1 Answers1

5

for this you can temporary impersonate with Linked token - so get own linked token, if it exist, set it to thread, call GetLogicalDrives() and return to process token (Linked token have SECURITY_IMPERSONATION_LEVEL == SecurityIdentification as result it can be used very restrict )

#define BOOL_TO_ERR(b) ((b) ? NOERROR : GetLastError())

ULONG GetLogicalDrivesEx(PULONG pDrives)
{
    HANDLE hToken;

    ULONG err = BOOL_TO_ERR(OpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken));

    if (err != NOERROR)
    {
        return err;
    }

    union {
        TOKEN_ELEVATION_TYPE tet;
        TOKEN_LINKED_TOKEN tlt;
    };

    ULONG rcb;

    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))))
                {
                    err = (rcb = GetLogicalDrives()) ? NOERROR : GetLastError();
                    SetThreadToken(0, 0);
                }

                CloseHandle(tlt.LinkedToken);
            }
        }
        else
        {
            err = (rcb = GetLogicalDrives()) ? NOERROR : GetLastError();
        }
    }

    *pDrives = rcb;
    return err;
}

void test()
{
    ULONG Drives, Drives0 = GetLogicalDrives();
    GetLogicalDrivesEx(&Drives);
    WCHAR sz[32];
    swprintf(sz, L"%08x %08x", Drives0, Drives);
    MessageBoxW(0, sz, L"", MB_OK);

}

if no errors (GetLogicalDrivesEx return NOERROR) the Drives is logical drives for not-elevated session, when Drives0 - for elevated (if of course you run as elevated)

RbMm
  • 31,280
  • 3
  • 35
  • 56