Monthly Archives: April 2011

The power of stupidity

I just spent a couple of days trying to figure out why logon-related code that worked in Windows XP failed in Windows Vista and Windows 7.

hToken = NULL;
if ( LogonUser( g_sUser, bIsUPN ? NULL : g_sDomain, g_sPass, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &hToken ) )
{
    // Re-populate the g_sUser and g_sDomain values from the token!
    TOKEN_USER tUser;
    DWORD nLength;
    // Get the user / domain information from the token.
    if (GetTokenInformation(hToken,TokenUser,&tUser,sizeof tUser,&nLength))
    {
        SID_NAME_USE eUse;
        DWORD dwUserSize = _countof(g_sUser);
        DWORD dwDomainSize = _countof(g_sDomain);
        LookupAccountSid(NULL,&tUser.User.Sid,
            g_sUser, &dwUserSize,
            g_sDomain, &dwDomainSize,
            &eUse);
    }
    CloseHandle(hToken);
}




.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

[Note that some error handling has been removed for clarity and brevity.]



So what was going wrong? This totally used to work – it’s designed to validate the username and password, as well as to provide me with the canonical form of the user name.



I had a look through the APIs, and sure enough, there was a more up-to-date version of one of them – LogonUser has a colleague, LogonUserEx, and that function returns the Logon SID as well as verifying the logon works. Cool, I thought, I can get rid of GetTokenInformation, which seems to be failing anyway, and use LogonUserEx.



No dice.



LogonUserEx claimed to be working, and yet LookupAccountSid returned an error, signifying ERROR_NONE_MAPPED (1332 decimal, 0x534 in hex, “No mapping between account names and security IDs was done.”)



A little searching on ERROR_NONE_MAPPED led to a blog post by David LeBlanc, indicating that logon SIDs will cause this error, because they are entirely ephemeral SIDs, used mainly to protect securable items that should not be available to processes running outside of this logon session (and, by the same measure, allowing access to be provided, where appropriate, across different processes in the same logon session).



And then I realised, after a few hours of experimentation, the answer was staring me in the face, in the documentation – LogonUserEx returns the Logon SID in ppLogonSid, which is a Logon SID. Does not have a name, only a SID.



So, that explained the failure of LookupAccountSid with LogonUserEx, and I returned to using LogonUser – which left me with the conundrum of what was failing there.



It often turns out to be the simplest of things.



TOKEN_USER is a structure containing a pointer to the SID. As such, GetTokenInformation has to put the SID somewhere. Cleverly, it asks you to build your TOKEN_USER structure a little bit long, and places the SID at the end of the structure, before setting the pointer in the structure to point to the SID. So, sizeof(TOKEN_USER) is not big enough to pass to a GetTokenInformation call requesting a TokenUser.



The big question is, not why this failed, but why it worked ever at all! I’m not too fussed in finding that answer, because I’ve now changed my code to do it properly, and everything still works – on Windows Vista and XP. But I do feel stupid that debugging this code took me part of Sunday and a little of Monday evening.



Now the code looks like this (again, some error handling has been removed for brevity – don’t skimp in your production code!)



hToken = NULL;
if ( LogonUser( g_sUser, bIsUPN ? NULL : g_sDomain, g_sPass, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &hToken ) )
{
    SecureZeroMemory(g_sPass,sizeof g_sPass);
    TOKEN_USER *ptUser;
    DWORD nLength;
    GetTokenInformation(hToken, TokenUser, NULL, 0, &nLength);
    ptUser = (TOKEN_USER*)new char[nLength];
    // Get the user / domain information from the token.
    if (GetTokenInformation(hToken,TokenUser,ptUser,nLength,&nLength))
    {
        SID_NAME_USE eUse;
        DWORD dwUserSize = _countof(g_sUser);
        DWORD dwDomainSize = _countof(g_sDomain);
        LookupAccountSid(NULL,ptUser->User.Sid,
            g_sUser, &dwUserSize,
            g_sDomain, &dwDomainSize,
            &eUse);
    }
    delete [] (char *)ptUser;
    CloseHandle(hToken);
}



.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Note that the fix is to request the length of the TOKEN_USER structure with an initial call to GetTokenInformation, followed by a second call to fill it in.