// RASrv.cpp : Implementation of CRASrv #include "stdafx.h" #include "Raserver.h" #include "RASrv.h" #include #include #include #define SIZE_BUFF 256 /*extern "C" { DWORD APIENTRY SquishAddress(WCHAR *szIp, WCHAR *szCompIp); DWORD APIENTRY ExpandAddress(WCHAR *szIp, WCHAR *szCompIp); }; */ // **** This code was in the ICSHelper /**************************************************************************** ** ** Address String Compression routines ** ***************************************************************************** ** ** The following is a group of routines designed to compress and expand ** IPV4 addresses into the absolute minimum size possible. This is to ** provide a compressed ASCII string that can be parsed using standard ** shell routines for command line parsing. ** The compressed string has the following restrictions: ** -> Must not expand to more characters if UTF8 encoded. ** -> Must not contain the NULL character so that string libs work. ** -> Cannot contain double quote character, the shell needs that. ** -> Does not have to be human readable. ** ** Data Types: ** There are three data types used here: ** szAddr The orginal IPV4 string address ("X.X.X.X:port") ** blobAddr Six byte struct with 4 bytes of address, and 2 bytes of port ** szComp Eight byte ascii string of compressed IPV4 address ** ****************************************************************************/ #define COMP_OFFSET '#' #define COMP_SEPERATOR '!' #pragma pack(push,1) typedef struct _BLOB_ADDR { UCHAR addr_d; // highest order address byte UCHAR addr_c; UCHAR addr_b; UCHAR addr_a; // lowest order byte (last in IP string address) WORD port; } BLOB_ADDR, *PBLOB_ADDR; #pragma pack(pop) WCHAR b64Char[64]={ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', '0','1','2','3','4','5','6','7','8','9','+','/' }; /**************************************************************************** ** char * atob(char *szVal, UCHAR *result) ****************************************************************************/ WCHAR *atob(WCHAR *szVal, UCHAR *result) { WCHAR *lptr; WCHAR ucb; UCHAR foo; if (!result || !szVal) { OutputDebugString(L"ERROR: NULL ptr passed in atob"); return NULL; } // start ptr at the beginning of string lptr = szVal; foo = 0; ucb = *lptr++ - '0'; while (ucb >= 0 && ucb <= 9) { foo *= 10; foo += ucb; ucb = (*lptr++)-'0'; } *result = (UCHAR)foo; return lptr; } /**************************************************************************** ** ** CompressAddr(pszAddr, pblobAddr); ** Takes an ascii IP address (X.X.X.X:port) and converts it to a ** 6 byte binary blob. ** ** returns TRUE for success, FALSE for failure. ** ****************************************************************************/ BOOL CompressAddr(WCHAR *pszAddr, PBLOB_ADDR pblobAddr) { BLOB_ADDR lblob; WCHAR *lpsz; if (!pszAddr || !pblobAddr) { OutputDebugString(L"ERROR: NULL ptr passed in CompressAddr"); return FALSE; } lpsz = pszAddr; lpsz = atob(lpsz, &lblob.addr_d); if (*(lpsz-1) != '.') { OutputDebugString(L"ERROR: bad address[0] passed in CompressAddr"); return FALSE; } lpsz = atob(lpsz, &lblob.addr_c); if (*(lpsz-1) != '.') { OutputDebugString(L"ERROR: bad address[1] passed in CompressAddr"); return FALSE; } lpsz = atob(lpsz, &lblob.addr_b); if (*(lpsz-1) != '.') { OutputDebugString(L"ERROR: bad address[2] passed in CompressAddr"); return FALSE; } lpsz = atob(lpsz, &lblob.addr_a); // is there a port number here? if (*(lpsz-1) == ':') lblob.port = (WORD)_wtoi(lpsz); else lblob.port = 0; // copy back the result memcpy(pblobAddr, &lblob, sizeof(*pblobAddr)); return TRUE; } /**************************************************************************** ** ** ExpandAddr(pszAddr, pblobAddr); ** Takes 6 byte binary blob and converts it into an ascii IP ** address (X.X.X.X:port) ** ** returns TRUE for success, FALSE for failure. ** ****************************************************************************/ BOOL ExpandAddr(WCHAR *pszAddr, PBLOB_ADDR pba) { if (!pszAddr || !pba) { OutputDebugString(L"ERROR: NULL ptr passed in ExpandAddr"); return FALSE; } wsprintf(pszAddr, L"%d.%d.%d.%d", pba->addr_d, pba->addr_c, pba->addr_b, pba->addr_a); if (pba->port) { WCHAR scratch[8]; wsprintf(scratch, L":%d", pba->port); wcscat(pszAddr, scratch); } return TRUE; } /**************************************************************************** ** ** AsciifyAddr(pszAddr, pblobAddr); ** Takes 6 byte binary blob and converts it into compressed ascii ** will return either 6 or 8 bytes of string ** ** returns TRUE for success, FALSE for failure. ** ****************************************************************************/ BOOL AsciifyAddr(WCHAR *pszAddr, PBLOB_ADDR pba) { UCHAR tmp; DWORDLONG dwl; int i, iCnt; if (!pszAddr || !pba) { OutputDebugString(L"ERROR: NULL ptr passed in AsciifyAddr"); return FALSE; } iCnt = 6; if (pba->port) iCnt = 8; dwl = 0; memcpy(&dwl, pba, sizeof(*pba)); for (i = 0; i < iCnt; i++) { // get 6 bits of data tmp = (UCHAR)(dwl & 0x3f); // add the offset to asciify this // offset must be bigger the double-quote char. pszAddr[i] = b64Char[tmp]; // (WCHAR)(tmp + COMP_OFFSET); // Shift right 6 bits dwl = Int64ShrlMod32(dwl, 6); } // terminating NULL pszAddr[iCnt] = 0; return TRUE; } /**************************************************************************** ** ** DeAsciifyAddr(pszAddr, pblobAddr); ** Takes a compressed ascii string and converts it into a ** 6 or 8 byte binary blob ** ** returns TRUE for success, FALSE for failure. ** ****************************************************************************/ BOOL DeAsciifyAddr(WCHAR *pszAddr, PBLOB_ADDR pba) { UCHAR tmp; WCHAR wtmp; DWORDLONG dwl; int i; int iCnt; if (!pszAddr || !pba) { OutputDebugString(L"ERROR: NULL ptr passed in DeAsciifyAddr"); return FALSE; } /* how long is this string? * if it is 6 bytes, then there is no port * else it should be 8 bytes */ i = wcslen(pszAddr); if (i == 6 || i == 8) iCnt = i; else { iCnt = 8; OutputDebugString(L"Strlen is wrong in DeAsciifyAddr"); } dwl = 0; for (i = iCnt-1; i >= 0; i--) { wtmp = pszAddr[i]; if (wtmp >= L'A' && wtmp <= L'Z') tmp = wtmp - L'A'; else if (wtmp >= L'a' && wtmp <= L'z') tmp = wtmp - L'a' + 26; else if (wtmp >= L'0' && wtmp <= L'9') tmp = wtmp - L'0' + 52; else if (wtmp == L'+') tmp = 62; else if (wtmp == L'/') tmp = 63; else { tmp = 0; OutputDebugString(L"ERROR:found invalid character in decode stream"); } // tmp = (UCHAR)(pszAddr[i] - COMP_OFFSET); if (tmp > 63) { tmp = 0; OutputDebugString(L"ERROR:screwup in DeAsciify"); } dwl = Int64ShllMod32(dwl, 6); dwl |= tmp; } memcpy(pba, &dwl, sizeof(*pba)); return TRUE; } /**************************************************************************** ** ** SquishAddress(char *szIp, char *szCompIp) ** Takes one IP address and compresses it to minimum size ** ****************************************************************************/ DWORD APIENTRY SquishAddress(WCHAR *szIp, WCHAR *szCompIp) { WCHAR *thisAddr, *nextAddr; BLOB_ADDR ba; if (!szIp || !szCompIp) { OutputDebugString(L"SquishAddress called with NULL ptr"); return ERROR_INVALID_PARAMETER; } // TRIVIAL_MSG((L"SquishAddress(%s)", szIp)); thisAddr = szIp; szCompIp[0] = 0; while (thisAddr) { WCHAR scr[10]; nextAddr = wcschr(thisAddr, L';'); if (nextAddr && *(nextAddr+1)) { *nextAddr = 0; } else nextAddr=0; CompressAddr(thisAddr, &ba); AsciifyAddr(scr, &ba); wcscat(szCompIp, scr); if (nextAddr) { // restore seperator found earlier *nextAddr = ';'; nextAddr++; wcscat(szCompIp, L"!" /* COMP_SEPERATOR */); } thisAddr = nextAddr; } // TRIVIAL_MSG((L"SquishAddress returns [%s]", szCompIp)); return ERROR_SUCCESS; } /**************************************************************************** ** ** ExpandAddress(char *szIp, char *szCompIp) ** Takes a compressed IP address and returns it to ** "normal" ** ****************************************************************************/ DWORD APIENTRY ExpandAddress(WCHAR *szIp, WCHAR *szCompIp) { BLOB_ADDR ba; WCHAR *thisAddr, *nextAddr; if (!szIp || !szCompIp) { OutputDebugString(L"ExpandAddress called with NULL ptr"); return ERROR_INVALID_PARAMETER; } // TRIVIAL_MSG((L"ExpandAddress(%s)", szCompIp)); thisAddr = szCompIp; szIp[0] = 0; while (thisAddr) { WCHAR scr[32]; nextAddr = wcschr(thisAddr, COMP_SEPERATOR); if (nextAddr) *nextAddr = 0; DeAsciifyAddr(thisAddr, &ba); ExpandAddr(scr, &ba); wcscat(szIp, scr); if (nextAddr) { // restore seperator found earlier *nextAddr = COMP_SEPERATOR; nextAddr++; wcscat(szIp, L";"); } thisAddr = nextAddr; } // TRIVIAL_MSG((L"ExpandAddress returns [%s]", szIp)); return ERROR_SUCCESS; } // ***** END ICSHelper code bool CRASrv::InApprovedDomain() { // WCHAR ourUrl[INTERNET_MAX_URL_LENGTH]; CComBSTR cbOurURL; if (!GetOurUrl(cbOurURL)) { return false; } return IsApprovedDomain(cbOurURL); } bool CRASrv::GetOurUrl(CComBSTR & cbOurURL) { HRESULT hr; CComPtr spSrvProv; CComPtr spWebBrowser; // Get site pointer... CComPtr spClientSite; hr = GetClientSite((IOleClientSite**)&spClientSite); if (FAILED(hr)) { return false; } hr = spClientSite->QueryInterface(IID_IServiceProvider, (void **)&spSrvProv); if (FAILED(hr)) { return false; } hr = spSrvProv->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2, (void**)&spWebBrowser); if (FAILED(hr)) return false; CComBSTR bstrURL; if (FAILED(spWebBrowser->get_LocationURL(&bstrURL))) return false; cbOurURL = bstrURL.Copy(); return true; } bool CRASrv::IsApprovedDomain(CComBSTR & cbOurURL) { // Only allow http access. // You can change this to allow file:// access. // INTERNET_SCHEME isResult; isResult = GetScheme(cbOurURL); if ( (isResult != INTERNET_SCHEME_HTTPS) ) { return false; } //char ourDomain[256]; CComBSTR cbOurDomain; CComBSTR cbGoodDomain[] = {L"www.microsoft.com", L"microsoft.com", L"microsoft"}; if (!GetDomain(cbOurURL, cbOurDomain)) return false; // Go through all the domains that should work if (MatchDomains(cbGoodDomain[0], cbOurDomain) || MatchDomains(cbGoodDomain[1], cbOurDomain) || MatchDomains(cbGoodDomain[2], cbOurDomain) ) { return true; } return false; } INTERNET_SCHEME CRASrv::GetScheme(CComBSTR & cbUrl) { WCHAR buf[32]; URL_COMPONENTS uc; ZeroMemory(&uc, sizeof uc); uc.dwStructSize = sizeof(uc); uc.lpszScheme = buf; uc.dwSchemeLength = sizeof buf; if (InternetCrackUrl(cbUrl, cbUrl.Length() , ICU_DECODE, &uc)) return uc.nScheme; else return INTERNET_SCHEME_UNKNOWN; } bool CRASrv::GetDomain(CComBSTR & cbUrl, CComBSTR & cbBuf) { bool bRet = TRUE; URL_COMPONENTS uc; ZeroMemory(&uc, sizeof uc); WCHAR buf[INTERNET_MAX_URL_LENGTH]; int cbBuff = sizeof(WCHAR) * INTERNET_MAX_URL_LENGTH; uc.dwStructSize = sizeof uc; uc.lpszHostName = buf; uc.dwHostNameLength = cbBuff; if (!InternetCrackUrl(cbUrl, cbUrl.Length(), ICU_DECODE, &uc)) { bRet = FALSE; } else { cbBuf = buf; bRet = true; } return bRet; } // Return if ourDomain is within approvedDomain. // approvedDomain must either match ourDomain // or be a suffix preceded by a dot. // bool CRASrv::MatchDomains(CComBSTR& approvedDomain, CComBSTR& ourDomain) { /* int apDomLen = lstrlen(approvedDomain); int ourDomLen = lstrlen(ourDomain); if (apDomLen > ourDomLen) return false; if (lstrcmpi(ourDomain+ourDomLen-apDomLen, approvedDomain) != 0) return false; if (apDomLen == ourDomLen) return true; if (ourDomain[ourDomLen - apDomLen - 1] == '.') return true; */ return approvedDomain == ourDomain ? true : false; } ///////////////////////////////////////////////////////////////////////////// // CRASrv // CRAServer // Function: StartRA // // Parameter: BSTR strData - contains the following in a comma delimited string // ,, STDMETHODIMP CRASrv::StartRA(BSTR strData, BSTR strPassword) { HRESULT hr = S_OK; STARTUPINFO StartUp; PROCESS_INFORMATION p_i; BOOL result; if ((strData == NULL) || (*strData == L'\0')) { hr = E_INVALIDARG; return hr; } if (strData[1] == NULL) { hr = E_INVALIDARG; return hr; } // Note, there is not always a strPassword so don't fail if it's NULL if (!InApprovedDomain()) { hr = E_FAIL; return hr; } // **** Parse out the components of the string into CComBSTRs CComBSTR strIn; // Used as the local storage strIn = strData; // Copy the contents so that we can parse CComBSTR strVer; BOOL bPassword = FALSE; BOOL bModem = FALSE; CComBSTR strIPList; CComBSTR strSessionID; CComBSTR strPublicKey; WCHAR * tok; CComBSTR strSquishedIPList; CComBSTR strExpandedIPList; DWORD dwRet = 0x0; CComBSTR strExe( HC_ROOT_HELPSVC_BINARIES L"\\HelpCtr.exe" ); WCHAR * pStr; int iSizeStr = 128; // Expand environment variables in strExe pStr = new WCHAR[iSizeStr]; dwRet = ::ExpandEnvironmentStrings(strExe, pStr, iSizeStr); if (dwRet == 0) { return E_FAIL; } if (dwRet >= 128) // if the Buffer is too small { delete [] pStr; pStr = new WCHAR[dwRet]; dwRet = ::ExpandEnvironmentStrings(strExe, pStr, dwRet); if (dwRet == 0) { return E_FAIL; } } // Copy the new expanded buffer back to strExe strExe = pStr; // Release the memory allocated to expand the Env Vars delete [] pStr; // Parse the BSTR (strIn) to get the ticket info. // Grab the lower byte of the first WCHAR and then get the squished IP List tok = ::wcstok(strIn, L","); if (tok != NULL) { // Check the password flag (bit 0) if (tok[0] & 0x1) bPassword = TRUE; else bPassword = FALSE; // Check the Modem flag (bit 1) if (tok[0] & 0x2) bModem = TRUE; else bModem = FALSE; tok = &tok[1]; // Move to the next WCHAR to start the Squished IPLIST strSquishedIPList = tok; } else { hr = ERROR_INVALID_USER_BUFFER; return hr; } tok = ::wcstok(NULL, L","); if (tok != NULL) { strSessionID = tok; } else { hr = ERROR_INVALID_USER_BUFFER; return hr; } tok = ::wcstok(NULL, L","); if (tok != NULL) { strPublicKey = tok; } else { hr = ERROR_INVALID_USER_BUFFER; return hr; } // Now convert the SquishedIPList to an ExpandedIPList WCHAR szIP[50]; tok = ::wcstok(strSquishedIPList, L";"); while (tok != NULL) { // Expand the current SquishedIP we are looking at ::ZeroMemory(szIP,sizeof(szIP)); ::ExpandAddress(&szIP[0], (WCHAR*)tok); strExpandedIPList += szIP; // Grab Next Token tok = ::wcstok(NULL, L";"); // add a ; at the end of each IP in the ExpandedIPList // ONLY if it's not the last IP in the list. if (tok != NULL) strExpandedIPList += L";"; } strIPList = strExpandedIPList; // **** Create a temp file in the temp dir to store the ticket CComBSTR strTempPath; CComBSTR strTempFile; CComBSTR strPathFile; // Used as the full path and name of the file WCHAR buff[SIZE_BUFF]; WCHAR buff2[SIZE_BUFF]; // Clear out the memory in the buffer ZeroMemory(buff,sizeof(WCHAR)*SIZE_BUFF); if (!::GetTempPathW(SIZE_BUFF, (LPWSTR)&buff)) { OutputDebugStringW(L"GetTempPath failed!\r\n"); return E_FAIL; } strTempPath = buff; strTempPath += L"RA\\"; // Create this directory if (!CreateDirectory((LPCWSTR)strTempPath, NULL)) { if (ERROR_ALREADY_EXISTS != GetLastError()) { OutputDebugStringW(L"CreateDirectory failed!\r\n"); return E_FAIL; } } ZeroMemory(buff2,sizeof(WCHAR)*SIZE_BUFF); if (!::GetTempFileNameW((LPCWSTR)strTempPath, NULL, 0, (LPWSTR)&buff2)) { OutputDebugStringW(L"GetTempFileName failed!\r\n"); return E_FAIL; } strTempFile = buff2; // Write the ticket to the file CComBSTR strXMLTicket; strXMLTicket = L" spXMLDoc; VARIANT_BOOL vbSuccess; CComVariant cvStrTempFile; hr = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (void**)&spXMLDoc); if (FAILED(hr)) { return hr; } hr = spXMLDoc->loadXML(strXMLTicket, &vbSuccess); if (FAILED(hr) || (vbSuccess == VARIANT_FALSE)) { return E_FAIL; } cvStrTempFile = strTempFile; hr = spXMLDoc->save(cvStrTempFile); if (FAILED(hr)) { return hr; } // Start helpctr.exe with this ticket in order to start Remote Assistance // as an Expert strExe += L" -Mode \"hcp://system/Remote Assistance/RAHelpeeAcceptLayout.xml\" -url \"hcp://system/Remote Assistance/Interaction/Client/RcToolscreen1.htm\" -ExtraArgument \"IncidentFile="; strExe += strTempFile; strExe += L"\""; // initialize our structs ZeroMemory(&p_i, sizeof(p_i)); ZeroMemory(&StartUp, sizeof(StartUp)); StartUp.cb = sizeof(StartUp); StartUp.dwFlags = STARTF_USESHOWWINDOW; StartUp.wShowWindow = SW_SHOWNORMAL; result = CreateProcess(NULL, strExe, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS + CREATE_UNICODE_ENVIRONMENT , NULL, // Environment block (must use the CREATE_UNICODE_ENVIRONMENT flag) NULL, &StartUp, &p_i); if (!result) { // CreateProcess Failed! dwRet = GetLastError(); OutputDebugStringW(L"CreateProcessW failed!"); return E_FAIL; } return hr; } /* STDMETHODIMP CRASrv::QueryOS(long *pRes) { OSVERSIONINFOEX osinfo; HRESULT hr = S_OK; if (!pRes) { hr = E_INVALIDARG; return hr; } memset((OSVERSIONINFO *)&osinfo, 0, sizeof(osinfo)); osinfo.dwOSVersionInfoSize = sizeof(osinfo); if (!GetVersionEx((OSVERSIONINFO *)&osinfo)) { return E_FAIL; } // Return the correct value // Operating System Constant // Windows XP 0x01 // Windows 2000 0x02 // Windows NT 0x03 // Windows ME 0x04 // Windows 98 0x05 // Windows 95 0x06 // Windows 3.x 0x07 // Other 0x10 switch(osinfo.dwMajorVersion) { case 5: switch(osinfo.dwMinorVersion) { case 1: *pRes = 0x01; break; case 0: *pRes = 0x02; break; default: // Nothing break; } break; case 4: switch(osinfo.dwMinorVersion) { case 0: // Win95 or NT 4 *pRes = 0x03; break; case 10: // Win98 *pRes = 0x05; break; case 90: //WinME *pRes = 0x04; break; default: // Nothing break; } break; case 3: //WinNT3.51 *pRes = 0x07; default: *pRes = 0x10; break; } return hr ; } */ // // Function: Cleanup() // // Description: // // This function goes into the RA temporary files directory // and cleans up all the files that are older than 5 minutes. bool CRASrv::Cleanup() { bool bRet = TRUE; WCHAR buff[SIZE_BUFF]; CComBSTR strTempDir, strTempDirWild; CComBSTR strTempFile; HANDLE hFile; // *** Find out what the temp directory is // Clear out the memory in the buffer ZeroMemory(buff,sizeof(WCHAR)*SIZE_BUFF); if (!::GetTempPathW(SIZE_BUFF, (LPWSTR)&buff)) { OutputDebugStringW(L"GetTempPath failed!\r\n"); return false; } strTempDir = buff; strTempDir += L"RA\\"; strTempDirWild = strTempDir; strTempDirWild += L"*.*"; WIN32_FIND_DATA ffd; // then enumerate all the files in this directory hFile = ::FindFirstFileW((LPCWSTR)strTempDirWild, &ffd); if (hFile != INVALID_HANDLE_VALUE) { bool bCont = true; FILETIME ftSystemTime; SYSTEMTIME st; ::GetSystemTime(&st); ::SystemTimeToFileTime(&st, &ftSystemTime); int iTimeDelta, iTimeThreshold = 0x1; // The threshold is dec(0x1 0000 0000)*(1/60)*10^-9 // which is approximately 7-8 minutes while (bCont) { // get the timestamp on this file if it's not . or .. if (!( (::wcscmp(L".", ffd.cFileName) == 0) || (::wcscmp(L"..", ffd.cFileName) == 0) ) ) { iTimeDelta = ftSystemTime.dwHighDateTime - ffd.ftCreationTime.dwHighDateTime; if (iTimeDelta > iTimeThreshold) { // The file is older than the threshold, delete the file strTempFile = strTempDir; strTempFile += ffd.cFileName; ::DeleteFileW((LPCWSTR)strTempFile); } } // Grab the next file if (!::FindNextFileW(hFile, &ffd)) { if (ERROR_NO_MORE_FILES == GetLastError()) { bCont = false; } } } } return bRet; }