CNTLMAuthObject on Windows Mobile 5 bugs...
So I don't forget and have to go through this investigation again, and
to help anyone else who has to get CNTLMAuthObject working on Windows
Mobile 5, here are details of a couple of bugs I found...
The first one simply fixes a linker error. The second one fixes NTLM
authentication when using a named
domain/user/password, rather than simply letting it use the currently
logged on user.
Using these two fixes I've successfully managed to talk to a .net web
service on an IIS server from a C++ client program running on a
Windows Mobile 5 device.
1. If you use CNTLMAuthObject - to handle NTLM authentication from
your HTTP client or Web service client you'll receive a linker error :
LNK2019: unresolved external symbol CompleteAuthToken referenced in
function "protected: bool __cdecl
ATL::CNTLMAuthObject::DoNTLMAuthenticate(void)"...
That's because the link library (secure32.lib?) doesn't contain the
named entry point - even though the function is present in the Dll.
To fix this one, include the following code in your project...
PSecurityFunctionTable m_pSecurityFuncs;
HMODULE m_hLibSSPI = 0;
bool LoadSecurityLibrary (LPCTSTR dllName)
{
bool rv = false;
m_hLibSSPI = LoadLibrary (dllName);
if (m_hLibSSPI != 0);
{
FARPROC pInit = GetProcAddress (m_hLibSSPI,
SECURITY_ENTRYPOINT);
if (pInit != 0)
{
m_pSecurityFuncs = (PSecurityFunctionTable) (_int64) pInit
();
if (m_pSecurityFuncs != 0)
rv = true;
}
if (!rv)
{
FreeLibrary (m_hLibSSPI);
n_hLibSSPI = 0;
}
}
return rv;
}
SECURITY_STATUS CompleteAuthToken(
PCtxtHandle phContext,
PSecBufferDesc pToken
)
{
if (m_hLibSSPI == 0)
LoadSecurityLibrary (TEXT ("secur32.dll"));
if (m_hLibSSPI == 0)
return SEC_E_INTERNAL_ERROR;
else
return m_pSecurityFuncs->CompleteAuthToken (phContext,
pToken);
}
2. By default, CNTLMAuthObject authenticates using the credentials of
the currently logged-on user. That works fine. However, you are also
meant to be able to supply a domain/user/password combination by
implementing the IAuthInfo interface, from which it can receive these
credentials.
Unfortunately this doesn't work (at least on Windows Mobile 5) due to
a bug in the CNTLMAuthObject::AcquireCredHandle method.
AcquireCredHandle calls the AcquireCredentialsHandle API to do the
work, which takes a SEC_WINNT_AUTH_IDENTITY structure as the parameter
holding the domain/user/password. However, due to a static cast
error, AcquireCredHandle gets it wrong.
AcquireCredHandle isn't virtual, so you can't just override it with
your own version. Fortunately, it only gets called by the
CNTLMAuthObject.Authenticate method which *is* virtual, and only a
couple of lines long so you can override that, and call your own
CMyNTLMAuthObject::AcquireCredHandle1 method to fix the problem...
bool AcquireCredHandle1 ()
{
PSecPkgInfo pPackageInfo = NULL;
SECURITY_STATUS SecurityStatus = SEC_E_OK;
// Acquire a credentials handle on the NTLM security package
SecurityStatus = QuerySecurityPackageInfo(ATL_HTTP_AUTHTYPE_NTLM,
&pPackageInfo);
if (SecurityStatus != SEC_E_OK)
return false;
CSecAuthIdentity CA;
SEC_WINNT_AUTH_IDENTITY ai;
SEC_WINNT_AUTH_IDENTITY *pai = 0;
if (m_pAuthInfo)
{
// If m_pAuthInfo has been set then the caller wants us
// to get credentials from them. The original ATL version
f*cked
// this up, hence this fix...
if (CA.Init(m_pAuthInfo))
{
ai.Domain = CA.Domain;
ai.DomainLength = CA.DomainLength;
ai.User = CA.User;
ai.UserLength = CA.UserLength;
ai.Password = CA.Password;
ai.PasswordLength = CA.PasswordLength;
ai.Flags = CA.Flags;
// ... See - it wasn't too hard - was it! None of that
silly unsafe
// typecasting classes to structures that you get in
the original
pai = &ai;
}
}
SecurityStatus = AcquireCredentialsHandle(
0,
pPackageInfo->Name,
SECPKG_CRED_OUTBOUND,
0,
pai,
0,
0,
&m_hCredentials,
&m_ts
);
m_nMaxTokenSize = pPackageInfo->cbMaxToken;
FreeContextBuffer(pPackageInfo);
return SecurityStatus == SEC_E_OK ? true : false;
}
// Override the original virtual 'Authenicate' to call our own
AcquireCredHandle1 method
virtual bool Authenticate(LPCTSTR szAuthTypes, bool bProxy)
{
m_bProxy = bProxy;
if (AcquireCredHandle1())
return DoNTLMAuthenticate();
return false;
}
--
Colin