/*++ Copyright (c) 2000 Microsoft Corporation Module Name: InetSess.cpp Abstract: Implements the Passport Session that uses WinInet as the underlying transport. Author: Biao Wang (biaow) 01-Oct-2000 --*/ #include "PPdefs.h" #include "session.h" // #include "inetsess.tmh" SESSION* CreateWinHttpSession(void); // ----------------------------------------------------------------------------- BOOL SESSION::CreateObject(PCWSTR pwszHttpStack, HINTERNET hSession, PCWSTR pwszProxyUser, PCWSTR pwszProxyPass, SESSION*& pSess) { PP_ASSERT(pwszHttpStack != NULL); pSess = NULL; if (!::_wcsicmp(pwszHttpStack, L"WinInet.dll") || !::_wcsicmp(pwszHttpStack, L"WinInet")) { PP_ASSERT(FALSE); pSess = NULL; // new WININET_SESSION(); } else { pSess = ::CreateWinHttpSession(); } if (pSess) { pSess->SetProxyCreds(pwszProxyUser, pwszProxyPass); return pSess->Open(pwszHttpStack, hSession); } else { DoTraceMessage(PP_LOG_ERROR, "CreateObject() failed; not enough memory"); return FALSE; } } // ----------------------------------------------------------------------------- SESSION::SESSION(void) { m_hHttpStack = 0; m_hCredUI = 0; m_RefCount = 0; m_pfnReadDomainCred = NULL; m_pfnCredFree = NULL; m_hKeyLM = NULL; m_fLogout = FALSE; m_wDefaultDAUrl[0] = 0; m_wCurrentDAUrl[0] = 0; m_wCurrentDAHost[0] = 0; m_wDARealm[0] = 0; m_LastNexusDownloadTime = 0xFFFFFFFF; m_pwszProxyUser = NULL; m_pwszProxyPass = NULL; m_dwVersion = 0; InitializeListHead(&m_DAMap); } // ----------------------------------------------------------------------------- SESSION::~SESSION(void) { if (m_pwszProxyUser) { SecureZeroMemory((void*)m_pwszProxyUser,sizeof(m_pwszProxyUser[0])*wcslen(m_pwszProxyUser)); delete [] m_pwszProxyUser; } if (m_pwszProxyPass) { SecureZeroMemory((void*)m_pwszProxyPass,sizeof(m_pwszProxyPass[0])*wcslen(m_pwszProxyPass)); delete [] m_pwszProxyPass; } } BOOL SESSION::GetDAInfoFromPPNexus( ) { BOOL fRetVal = FALSE; HINTERNET hRequest = NULL; HINTERNET hConnect = NULL; DWORD dwError; WCHAR wNexusHost[128] = L"nexus.passport.com"; DWORD dwHostLen = sizeof(wNexusHost); // note: size of the buffer, not # of UNICODE characters WCHAR wNexusObj[128] = L"rdr/pprdr.asp"; DWORD dwObjLen = sizeof(wNexusObj); PWSTR pwszPassportUrls = NULL; DWORD dwUrlsLen = 0; DWORD dwValueType; WCHAR Delimiters[] = L","; PWSTR Token = NULL; // we allow only one Nexus contact per session to avoid infinite loop due to Nexus misconfiguration DWORD dwCurrentTime = ::GetTickCount(); if ((dwCurrentTime >= m_LastNexusDownloadTime) && (dwCurrentTime - m_LastNexusDownloadTime < 5*60*1000)) // 5 minutes { DoTraceMessage(PP_LOG_WARNING, "SESSION::GetDAInfoFromPPNexus() failed: Nexus info already downloaded"); goto exit; } if (m_hKeyLM) { dwError = ::RegQueryValueExW(m_hKeyLM, L"NexusHost", 0, &dwValueType, reinterpret_cast(wNexusHost), &dwHostLen); PP_ASSERT(!(dwError == ERROR_MORE_DATA)); dwError = ::RegQueryValueExW(m_hKeyLM, L"NexusObj", 0, &dwValueType, reinterpret_cast(wNexusObj), &dwObjLen); PP_ASSERT(!(dwError == ERROR_MORE_DATA)); } hConnect = Connect(wNexusHost, INTERNET_DEFAULT_HTTPS_PORT ); if (hConnect == NULL) { DWORD dwErrorCode = ::GetLastError(); DoTraceMessage(PP_LOG_ERROR, "SESSION::GetDAInfoFromPPNexus(): failed to connect to %ws; Error = %d", wNexusHost, dwErrorCode); goto exit; } hRequest = OpenRequest(hConnect, NULL, wNexusObj, WINHTTP_FLAG_SECURE ); if (hRequest == NULL) { DWORD dwErrorCode = ::GetLastError(); DoTraceMessage(PP_LOG_ERROR, "SESSION::GetDAInfoFromPPNexus() failed; OpenRequest() to %ws failed, Error Code = %d", wNexusObj, dwErrorCode); goto exit; } if (m_pwszProxyUser && m_pwszProxyPass) { SetOption(hRequest, WINHTTP_OPTION_PROXY_USERNAME, (void*)m_pwszProxyUser, wcslen(m_pwszProxyUser) + 1); SetOption(hRequest, WINHTTP_OPTION_PROXY_PASSWORD, (void*)m_pwszProxyPass, wcslen(m_pwszProxyPass) + 1); } if (!SendRequest(hRequest, NULL, 0)) { DoTraceMessage(PP_LOG_ERROR, "SESSION::GetDAInfoFromPPNexus(): failed"); goto exit; } if (ReceiveResponse(hRequest) == FALSE) { DWORD dwErrorCode = ::GetLastError(); DoTraceMessage(PP_LOG_ERROR, "SESSION::GetDAInfoFromPPNexus() failed; ReceiveResponse() failed, Error Code = %d", dwErrorCode); goto exit; } if (QueryHeaders(hRequest, WINHTTP_QUERY_PASSPORT_URLS, 0, &dwUrlsLen) == FALSE) { if ((::GetLastError() != ERROR_INSUFFICIENT_BUFFER) || (dwUrlsLen == 0)) { DoTraceMessage(PP_LOG_ERROR, "SESSION::GetDAInfoFromPPNexus() failed; PassportUrls header not found"); goto exit; } } else { PP_ASSERT(FALSE); // should not reach here } pwszPassportUrls = new WCHAR[dwUrlsLen]; if (pwszPassportUrls == NULL) { DoTraceMessage(PP_LOG_ERROR, "SESSION::GetDAInfoFromPPNexus() failed; insufficient memory"); goto exit; } if (QueryHeaders(hRequest, WINHTTP_QUERY_PASSPORT_URLS, pwszPassportUrls, &dwUrlsLen) == FALSE) { DoTraceMessage(PP_LOG_ERROR, "SESSION::GetDAInfoFromPPNexus() failed; PassportUrls header not found"); goto exit; } Token = ::wcstok(pwszPassportUrls, Delimiters); while (Token != NULL) { // skip leading white spaces while (*Token == (L" ")[0]) { ++Token; } if (*Token == L'\0') { DoTraceMessage(PP_LOG_WARNING, "SESSION::GetDAInfoFromPPNexus() : no text in between commas"); goto next_token; } // find DALocation if (!::_wcsnicmp(Token, L"DALogin", ::wcslen(L"DALogin"))) { PWSTR pwszDAUrl = ::wcsstr(Token, L"="); if (pwszDAUrl == NULL) { DoTraceMessage(PP_LOG_WARNING, "SESSION::GetDAInfoFromPPNexus() : no = after DALocation"); goto exit; } pwszDAUrl++; // skip "=" while (*pwszDAUrl == (L" ")[0]) { ++pwszDAUrl; } // skip leading white spaces if (*pwszDAUrl == L'\0') { goto exit; } ::wcscpy(m_wDefaultDAUrl, L"https://"); ::wcsncat(m_wDefaultDAUrl, pwszDAUrl, MAX_PASSPORT_URL_LENGTH - 8); m_LastNexusDownloadTime = ::GetTickCount(); fRetVal = TRUE; DoTraceMessage(PP_LOG_INFO, "DALocation URL %ws found", m_wDefaultDAUrl); } else if (!::_wcsnicmp(Token, L"DARealm", ::wcslen(L"DARealm"))) { PWSTR pwszDARealm = ::wcsstr(Token, L"="); if (pwszDARealm == NULL) { DoTraceMessage(PP_LOG_WARNING, "SESSION::GetDAInfoFromPPNexus() : no = after DARealm"); goto exit; } pwszDARealm++; // skip "=" while (*pwszDARealm == (L" ")[0]) { ++pwszDARealm; } // skip leading white spaces if (*pwszDARealm == L'\0') { goto exit; } ::wcsncpy(m_wDARealm, pwszDARealm, MAX_PASSPORT_REALM_LENGTH); m_wDARealm[MAX_PASSPORT_REALM_LENGTH] = 0; DoTraceMessage(PP_LOG_INFO, "DARealm URL %ws found", m_wDefaultDAUrl); } else if (!::_wcsnicmp(Token, L"ConfigVersion", ::wcslen(L"ConfigVersion"))) { PWSTR pwszConfigVersion = ::wcsstr(Token, L"="); if (pwszConfigVersion == NULL) { DoTraceMessage(PP_LOG_WARNING, "SESSION::GetDAInfoFromPPNexus() : no = after ConfigVersion"); goto exit; } pwszConfigVersion++; // skip "=" while (*pwszConfigVersion == (L" ")[0]) { ++pwszConfigVersion; } // skip leading white spaces if (*pwszConfigVersion == L'\0') { goto exit; } m_dwVersion = _wtoi(pwszConfigVersion); DoTraceMessage(PP_LOG_INFO, "ConfigVersion URL %ws found", m_wDefaultDAUrl); } next_token: Token = ::wcstok(NULL, Delimiters); } exit: if (pwszPassportUrls) { delete [] pwszPassportUrls; } if (hRequest) { CloseHandle(hRequest); } if (hConnect) { CloseHandle(hConnect); } return fRetVal; } BOOL SESSION::GetRealm( PWSTR pwszRealm, // user supplied buffer ... PDWORD pdwRealmLen // ... and length (will be updated to actual length // on successful return) ) const { DWORD RealmLen = sizeof(m_wDARealm); DWORD dwValueType; PWSTR pwszDARealm = const_cast(&m_wDARealm[0]); if (m_hKeyLM && ::RegQueryValueExW(m_hKeyLM, L"LoginServerRealm", 0, &dwValueType, reinterpret_cast(pwszDARealm), &RealmLen) == ERROR_SUCCESS) { ; } if (!m_wDARealm[0]) { *pdwRealmLen = 0; return FALSE; } if (!pwszRealm) { *pdwRealmLen = ::wcslen(m_wDARealm) + 1; return FALSE; } if (*pdwRealmLen < (DWORD)::wcslen(m_wDARealm) + 1) { *pdwRealmLen = ::wcslen(m_wDARealm) + 1; return FALSE; } ::wcscpy(pwszRealm, m_wDARealm); *pdwRealmLen = ::wcslen(m_wDARealm) + 1; return TRUE; } DWORD SESSION::GetNexusVersion(void) { DWORD dwValueType; DWORD dwVerLen = sizeof(m_dwVersion); if (m_hKeyLM && ::RegQueryValueExW(m_hKeyLM, L"ConfigVersion", 0, &dwValueType, reinterpret_cast(&m_dwVersion), &dwVerLen) == ERROR_SUCCESS) { ; } return m_dwVersion; } BOOL SESSION::UpdateDAInfo( PCWSTR pwszSignIn, PCWSTR pwszDAUrl ) { BOOL fRet = FALSE; if (pwszSignIn) { LPCWSTR pwszDomain = ::wcsstr(pwszSignIn, L"@"); if (pwszDomain) { BOOL fFound = FALSE; for (PLIST_ENTRY entry = (&m_DAMap)->Flink; entry != (PLIST_ENTRY)&((&m_DAMap)->Flink); entry = entry->Flink) { P_DA_ENTRY pDAEntry = (P_DA_ENTRY)entry; if (!::_wcsicmp(pDAEntry->wDomain, pwszDomain)) { fFound = TRUE; ::wcsncpy(pDAEntry->wDA, pwszDAUrl, MAX_PASSPORT_URL_LENGTH); pDAEntry->wDA[MAX_PASSPORT_URL_LENGTH] = 0; fRet = TRUE; break; } } if (!fFound) { P_DA_ENTRY pDAEntry = new DA_ENTRY; if (pDAEntry) { ::wcsncpy(pDAEntry->wDomain, pwszDomain, MAX_PASSPORT_DOMAIN_LENGTH); pDAEntry->wDomain[MAX_PASSPORT_DOMAIN_LENGTH] = 0; ::wcsncpy(pDAEntry->wDA, pwszDAUrl, MAX_PASSPORT_URL_LENGTH); pDAEntry->wDA[MAX_PASSPORT_URL_LENGTH] = 0; InsertHeadList(&m_DAMap, (PLIST_ENTRY)pDAEntry); fRet = TRUE; } } } } return fRet; } BOOL SESSION::PurgeDAInfo(PCWSTR pwszSignIn) { if (pwszSignIn == NULL) { return TRUE; } LPCWSTR pwszDomain = ::wcsstr(pwszSignIn, L"@"); if (pwszDomain) { for (PLIST_ENTRY entry = (&m_DAMap)->Flink; entry != (PLIST_ENTRY)&((&m_DAMap)->Flink); entry = entry->Flink) { P_DA_ENTRY pDAEntry = (P_DA_ENTRY)entry; if (!::_wcsicmp(pDAEntry->wDomain, pwszDomain)) { RemoveEntryList(entry); delete pDAEntry; break; } } } return TRUE; } BOOL SESSION::GetDAInfo(PCWSTR pwszSignIn, LPWSTR pwszDAHostName, DWORD HostNameLen, LPWSTR pwszDAHostObj, DWORD HostObjLen) { LPCWSTR pwszDAUrl = m_wDefaultDAUrl; if (pwszSignIn) { LPCWSTR pwszDomain = ::wcsstr(pwszSignIn, L"@"); if (pwszDomain) { for (PLIST_ENTRY entry = (&m_DAMap)->Flink; entry != (PLIST_ENTRY)&((&m_DAMap)->Flink); entry = entry->Flink) { P_DA_ENTRY pDAEntry = (P_DA_ENTRY)entry; if (!::_wcsicmp(pDAEntry->wDomain, pwszDomain)) { pwszDAUrl = pDAEntry->wDA; break; } } } } ::wcsncpy(m_wCurrentDAUrl, pwszDAUrl, MAX_PASSPORT_URL_LENGTH); m_wCurrentDAUrl[MAX_PASSPORT_URL_LENGTH] = 0; URL_COMPONENTSW UrlComps; ::memset(&UrlComps, 0, sizeof(UrlComps)); UrlComps.dwStructSize = sizeof(UrlComps); UrlComps.lpszHostName = pwszDAHostName; UrlComps.dwHostNameLength = HostNameLen; UrlComps.lpszUrlPath = pwszDAHostObj; UrlComps.dwUrlPathLength = HostObjLen; if (CrackUrl(pwszDAUrl, 0, 0, &UrlComps) == FALSE) { DoTraceMessage(PP_LOG_ERROR, "WININET_SESSION::GetDAInfo() failed; can not crack the URL %ws", pwszDAUrl); return FALSE; } ::wcsncpy(m_wCurrentDAHost, UrlComps.lpszHostName, MAX_PASSPORT_HOST_LENGTH); m_wCurrentDAHost[MAX_PASSPORT_HOST_LENGTH] = 0; return TRUE; } BOOL SESSION::GetCachedCreds( PCWSTR pwszRealm, PCWSTR pwszTarget, PCREDENTIALW** pppCreds, DWORD* pdwCreds ) { *pppCreds = NULL; *pdwCreds = 0; if (m_pfnReadDomainCred == NULL) { return FALSE; } ULONG CredTypes = CRED_TYPE_DOMAIN_VISIBLE_PASSWORD; DWORD dwFlags = CRED_CACHE_TARGET_INFORMATION; CREDENTIAL_TARGET_INFORMATIONW TargetInfo; memset ( (void*)&TargetInfo, 0, sizeof(CREDENTIAL_TARGET_INFORMATIONW)); TargetInfo.TargetName = const_cast(pwszTarget); TargetInfo.DnsDomainName = const_cast(pwszRealm); TargetInfo.PackageName = L"Passport1.4"; TargetInfo.Flags = 0; TargetInfo.CredTypeCount = 1; TargetInfo.CredTypes = &CredTypes; if ((*m_pfnReadDomainCred)(&TargetInfo, dwFlags, pdwCreds, pppCreds ) != TRUE) { *pppCreds = NULL; *pdwCreds = 0; } else { if (IsLoggedOut()) { FILETIME LogoutTimestamp; ::SystemTimeToFileTime(GetLogoutTimeStamp(), &LogoutTimestamp); if (::CompareFileTime(&((**pppCreds)->LastWritten), &LogoutTimestamp) == -1) { // the cred is entered/created earlier (less) than the Logout request. It is no good. m_pfnCredFree(*pppCreds); *pppCreds = NULL; *pdwCreds = 0; } else { ResetLogoutFlag(); } } } return (*pppCreds != NULL ); } BOOL SESSION::Open(PCWSTR /*pwszHttpStack*/, HINTERNET) { BOOL fRetVal = FALSE; DWORD dwValueType; DWORD dwUrlLen = sizeof(m_wDefaultDAUrl); // note: size of the buffer, not # of UNICODE characters BOOL fDAInfoCached = FALSE; // assume NO DA info's cached locally ::RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\WinHttp\\Passport Test", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &m_hKeyLM, NULL); if (m_hKeyLM && ::RegQueryValueExW(m_hKeyLM, L"LoginServerUrl", 0, &dwValueType, reinterpret_cast(m_wDefaultDAUrl), &dwUrlLen) == ERROR_SUCCESS) { fDAInfoCached = TRUE; } if (!fDAInfoCached || (::wcslen(m_wDefaultDAUrl) == ::wcslen(L""))) { if (GetDAInfoFromPPNexus() == FALSE) { goto exit; } } m_hCredUI = ::LoadLibraryW(L"advapi32.dll"); if (m_hCredUI) { m_pfnReadDomainCred = reinterpret_cast(::GetProcAddress(m_hCredUI, "CredReadDomainCredentialsW")); if (m_pfnReadDomainCred == NULL) { DoTraceMessage(PP_LOG_WARNING, "failed to bind to CredReadDomainCredentialsW()"); } m_pfnCredFree = reinterpret_cast(::GetProcAddress(m_hCredUI, "CredFree")); if (m_pfnCredFree == NULL) { DoTraceMessage(PP_LOG_WARNING, "failed to bind to CredFree()"); } } fRetVal = TRUE; exit: return fRetVal; } void SESSION::Logout(void) { if (!m_fLogout) { m_fLogout = TRUE; ::GetSystemTime(&m_LogoutTimeStamp); SetOption(m_hInternet, WINHTTP_OPTION_PASSPORT_SIGN_OUT, m_wCurrentDAUrl, ::wcslen(m_wCurrentDAUrl) + 1); } } BOOL SESSION::IsLoggedOut(void) const { return m_fLogout; } void SESSION::ResetLogoutFlag(void) { m_fLogout = FALSE; } const SYSTEMTIME* SESSION::GetLogoutTimeStamp(void) const { return &m_LogoutTimeStamp; } void SESSION::Close(void) { if (m_hCredUI) { ::FreeLibrary(m_hCredUI); m_hCredUI = NULL; } if (m_hKeyLM) { ::RegCloseKey(m_hKeyLM); } PurgeAllDAInfo(); } BOOL SESSION::PurgeAllDAInfo(void) { while (!IsListEmpty(&m_DAMap)) { PLIST_ENTRY pEntry = RemoveHeadList(&m_DAMap); P_DA_ENTRY pDAEntry = (P_DA_ENTRY)pEntry; delete pDAEntry; } return TRUE; }