Search code examples
c++winapicredential-providers

Access violation when reading buffer of UNICODE_STRING


I have written a custom credential provider that uses a KERB_CERTIFICATE_LOGON structure. Now that everything works fine I tried to write some unittests (I know that tests should be written before coding, but in this case I had to figure out how everything works first ;-)).

When I try to assert the contents of the KERB_CERTIFICATE_LOGON structure by using the solution to this SO-question with wcsncmp like this:

KERB_CERTIFICATE_LOGON* kerbCertificateLogon = reinterpret_cast<KERB_CERTIFICATE_LOGON*>(serializedCredentials->rgbSerialization);
wcsncmp(domainName, kerbCertificateLogon->DomainName.Buffer, kerbCertificateLogon->DomainName.Length / sizeof(wchar_t));

I get an access violation in wcsncmp at this point:

    extern "C" int __cdecl wcsncmp(
    wchar_t const* a,
    wchar_t const* b,
    size_t         count
    )
{
    if (count == 0)
        return 0;

    while (--count != 0 && *a && *a == *b) // <== Here comes "read access violation b was 0x48"
    {
        ++a;
        ++b;
    }

    return static_cast<int>(*a - *b);
}

Furthermore when debugging the function filling the buffer I can see in the "Locals"-view of visual studio directly after filling the buffer with this code:

kerbCertificateLogon->DomainName.Buffer = reinterpret_cast<PWSTR>(domainBuffer - authInfo);

this result:

0x0000000000000048 <Error reading characters of string.>

WTF??? The same code works fine when used by LSA, so I think that everythings alright, but why can't I read the value in a simple unittest?


Solution

  • OK, as usual I found the solution shortly after asking. Contrary to one of the comments above the error had to to with the certificate structures! So just using two UNICODE_STRING would not have worked to reproduce the issue.

    Inside of these structures relative pointers are used to store information as could have been seen from the link to the older question.

    I just didn't see the wood for the trees.

    So the solution is quite simple: instead of directly using the pointer to the buffer in the UNICODE_STRING I had to recalculate the absolute pointer. I extracted this to a method because I need this comparison quite often in my tests:

    static void AssertRelativeUnicodeStringEquals(const wchar_t* expected, UNICODE_STRING actual, LPBYTE basePointer)
    {
        const int result = wcscmp(expected, reinterpret_cast<PWSTR>(basePointer + reinterpret_cast<int>(actual.Buffer)));
        EXPECT_EQ(0, result);
    }
    

    This I can now call with:

    AssertRelativeUnicodeStringEquals(domainName, kerbCertificateLogon->DomainName, (LPBYTE)kerbCertificateLogon);