/** * FreeRDP: A Remote Desktop Protocol Implementation * Remote Assistance * * Copyright 2014 Marc-Andre Moreau * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../core/settings.h" #define TAG FREERDP_TAG("common") struct rdp_assistance_file { UINT32 Type; char* Username; char* LHTicket; char* RCTicket; char* PassStub; UINT32 DtStart; UINT32 DtLength; BOOL LowSpeed; BOOL RCTicketEncrypted; char* ConnectionString1; char* ConnectionString2; BYTE* EncryptedPassStub; size_t EncryptedPassStubLength; BYTE* EncryptedLHTicket; size_t EncryptedLHTicketLength; wArrayList* MachineAddresses; wArrayList* MachinePorts; wArrayList* MachineUris; char* RASessionId; char* RASpecificParams; char* RASpecificParams2; char* filename; char* password; }; static const char* strrstr(const char* haystack, size_t len, const char* needle) { if (*needle == '\0') return (const char*)haystack; char* result = NULL; for (;;) { char* p = strstr(haystack, needle); if (p == NULL) break; if (p > haystack + len) return NULL; result = p; haystack = p + 1; } return result; } static BOOL update_option(char** opt, const char* val, size_t len) { WINPR_ASSERT(opt); free(*opt); *opt = NULL; if (!val && (len != 0)) return FALSE; else if (!val && (len == 0)) return TRUE; *opt = strndup(val, len); return *opt != NULL; } static BOOL update_name(rdpAssistanceFile* file, const char* name) { WINPR_ASSERT(file); if (!name) { WLog_ERR(TAG, "ASSISTANCE file %s invalid name", name); return FALSE; } free(file->filename); file->filename = _strdup(name); return file->filename != NULL; } static BOOL update_password(rdpAssistanceFile* file, const char* password) { WINPR_ASSERT(file); free(file->password); file->password = NULL; if (!password) return TRUE; file->password = _strdup(password); return file->password != NULL; } static BOOL update_connectionstring2_nocopy(rdpAssistanceFile* file, char* str) { WINPR_ASSERT(file); free(file->ConnectionString2); file->ConnectionString2 = NULL; if (!str) return TRUE; file->ConnectionString2 = str; return file->ConnectionString2 != NULL; } static BOOL update_connectionstring2(rdpAssistanceFile* file, const char* str, size_t len) { char* strc = NULL; if (!str && (len != 0)) return FALSE; if (str && (len > 0)) { strc = strndup(str, len); if (!strc) return FALSE; } return update_connectionstring2_nocopy(file, strc); } static BOOL update_connectionstring2_wchar(rdpAssistanceFile* file, const WCHAR* str, size_t len) { char* strc = NULL; if (!str && (len != 0)) return FALSE; if (str && (len > 0)) { strc = ConvertWCharNToUtf8Alloc(str, len, NULL); if (!strc) return FALSE; } return update_connectionstring2_nocopy(file, strc); } /** * Password encryption in establishing a remote assistance session of type 1: * http://blogs.msdn.com/b/openspecification/archive/2011/10/31/password-encryption-in-establishing-a-remote-assistance-session-of-type-1.aspx * * Creation of PassStub for the Remote Assistance Ticket: * http://social.msdn.microsoft.com/Forums/en-US/6316c3f4-ea09-4343-a4a1-9cca46d70d28/creation-of-passstub-for-the-remote-assistance-ticket?forum=os_windowsprotocols */ /** * CryptDeriveKey Function: * http://msdn.microsoft.com/en-us/library/windows/desktop/aa379916/ * * Let n be the required derived key length, in bytes. * The derived key is the first n bytes of the hash value after the hash computation * has been completed by CryptDeriveKey. If the hash is not a member of the SHA-2 * family and the required key is for either 3DES or AES, the key is derived as follows: * * Form a 64-byte buffer by repeating the constant 0x36 64 times. * Let k be the length of the hash value that is represented by the input parameter hBaseData. * Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes * of the buffer with the hash value that is represented by the input parameter hBaseData. * * Form a 64-byte buffer by repeating the constant 0x5C 64 times. * Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes * of the buffer with the hash value that is represented by the input parameter hBaseData. * * Hash the result of step 1 by using the same hash algorithm as that used to compute the hash * value that is represented by the hBaseData parameter. * * Hash the result of step 2 by using the same hash algorithm as that used to compute the hash * value that is represented by the hBaseData parameter. * * Concatenate the result of step 3 with the result of step 4. * Use the first n bytes of the result of step 5 as the derived key. */ static BOOL freerdp_assistance_crypt_derive_key_sha1(BYTE* hash, size_t hashLength, BYTE* key, size_t keyLength) { BOOL rc = FALSE; BYTE pad1[64] = { 0 }; BYTE pad2[64] = { 0 }; memset(pad1, 0x36, sizeof(pad1)); memset(pad2, 0x5C, sizeof(pad2)); for (size_t i = 0; i < hashLength; i++) { pad1[i] ^= hash[i]; pad2[i] ^= hash[i]; } BYTE* buffer = (BYTE*)calloc(hashLength, 2); if (!buffer) goto fail; if (!winpr_Digest(WINPR_MD_SHA1, pad1, 64, buffer, hashLength)) goto fail; if (!winpr_Digest(WINPR_MD_SHA1, pad2, 64, &buffer[hashLength], hashLength)) goto fail; CopyMemory(key, buffer, keyLength); rc = TRUE; fail: free(buffer); return rc; } static BOOL append_address_to_list(wArrayList* MachineAddresses, const char* str, size_t len) { char* copy = NULL; if (len > 0) copy = strndup(str, len); if (!copy) return FALSE; const BOOL rc = ArrayList_Append(MachineAddresses, copy); if (!rc) free(copy); // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): ArrayList_Append takes ownership of copy return rc; } static BOOL append_address(rdpAssistanceFile* file, const char* host, const char* port) { WINPR_ASSERT(file); errno = 0; unsigned long p = strtoul(port, NULL, 0); if ((errno != 0) || (p == 0) || (p > UINT16_MAX)) { WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 invalid port value %s", port); return FALSE; } if (!append_address_to_list(file->MachineAddresses, host, host ? strlen(host) : 0)) return FALSE; return ArrayList_Append(file->MachinePorts, (void*)(uintptr_t)p); } static BOOL freerdp_assistance_parse_address_list(rdpAssistanceFile* file, char* list) { WINPR_ASSERT(file); WLog_DBG(TAG, "freerdp_assistance_parse_address_list list=%s", list); BOOL rc = FALSE; if (!list) return FALSE; char* strp = list; char* s = ";"; // get the first token char* token = strtok(strp, s); // walk through other tokens while (token != NULL) { char* port = strchr(token, ':'); if (!port) goto out; *port = '\0'; port++; if (!append_address(file, token, port)) goto out; token = strtok(NULL, s); } rc = TRUE; out: return rc; } static BOOL freerdp_assistance_parse_connection_string1(rdpAssistanceFile* file) { char* tokens[8] = { 0 }; BOOL rc = FALSE; WINPR_ASSERT(file); if (!file->RCTicket) return FALSE; /** * ,,,, * ,,, */ char* str = _strdup(file->RCTicket); if (!str) goto error; const size_t length = strlen(str); int count = 1; for (size_t i = 0; i < length; i++) { if (str[i] == ',') count++; } if (count != 8) goto error; count = 0; tokens[count++] = str; for (size_t i = 0; i < length; i++) { if (str[i] == ',') { str[i] = '\0'; tokens[count++] = &str[i + 1]; } } if (strcmp(tokens[0], "65538") != 0) goto error; if (strcmp(tokens[1], "1") != 0) goto error; if (strcmp(tokens[3], "*") != 0) goto error; if (strcmp(tokens[5], "*") != 0) goto error; if (strcmp(tokens[6], "*") != 0) goto error; file->RASessionId = _strdup(tokens[4]); if (!file->RASessionId) goto error; file->RASpecificParams = _strdup(tokens[7]); if (!file->RASpecificParams) goto error; if (!freerdp_assistance_parse_address_list(file, tokens[2])) goto error; rc = TRUE; error: free(str); return rc; } /** * Decrypted Connection String 2: * * * * * * */ static BOOL freerdp_assistance_parse_attr(const char** opt, size_t* plength, const char* key, const char* tag) { WINPR_ASSERT(opt); WINPR_ASSERT(plength); WINPR_ASSERT(key); *opt = NULL; *plength = 0; if (!tag) return FALSE; char bkey[128] = { 0 }; const int rc = _snprintf(bkey, sizeof(bkey), "%s=\"", key); WINPR_ASSERT(rc > 0); WINPR_ASSERT(rc < sizeof(bkey)); char* p = strstr(tag, bkey); if (!p) return TRUE; p += strlen(bkey); char* q = strchr(p, '"'); if (!q) { WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 invalid field '%s=%s'", key, p); return FALSE; } if (p > q) { WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 invalid field " "order for '%s'", key); return FALSE; } const size_t length = q - p; *opt = p; *plength = length; return TRUE; } static BOOL freerdp_assistance_parse_attr_str(char** opt, const char* key, const char* tag) { const char* copt = NULL; size_t size = 0; if (!freerdp_assistance_parse_attr(&copt, &size, key, tag)) return FALSE; return update_option(opt, copt, size); } static BOOL freerdp_assistance_parse_attr_bool(BOOL* opt, const char* key, const char* tag) { const char* copt = NULL; size_t size = 0; WINPR_ASSERT(opt); *opt = FALSE; if (!freerdp_assistance_parse_attr(&copt, &size, key, tag)) return FALSE; if (size != 1) return TRUE; *opt = (copt[0] == '1'); return TRUE; } static BOOL freerdp_assistance_parse_attr_uint32(UINT32* opt, const char* key, const char* tag) { const char* copt = NULL; size_t size = 0; WINPR_ASSERT(opt); *opt = 0; if (!freerdp_assistance_parse_attr(&copt, &size, key, tag)) return FALSE; char buffer[64] = { 0 }; if (size >= sizeof(buffer)) { WLog_WARN(TAG, "Invalid UINT32 string '%s' [%" PRIuz "]", copt, size); return FALSE; } strncpy(buffer, copt, size); errno = 0; unsigned long val = strtoul(buffer, NULL, 0); if ((errno != 0) || (val > UINT32_MAX)) { WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid value %s", buffer); return FALSE; } *opt = val; return TRUE; } static char* freerdp_assistance_contains_element(char* input, size_t ilen, const char* key, size_t* plen, char** pdata, size_t* pdlen) { WINPR_ASSERT(input); WINPR_ASSERT(key); WINPR_ASSERT(plen); char bkey[128] = { 0 }; const int rc = _snprintf(bkey, sizeof(bkey), "<%s", key); WINPR_ASSERT(rc > 0); WINPR_ASSERT(rc < sizeof(bkey)); char* tag = strstr(input, bkey); if (!tag || (tag > input + ilen)) return NULL; char* data = tag + strnlen(bkey, sizeof(bkey)); /* Ensure there is a valid delimiter following our token */ switch (data[0]) { case '>': case '/': case ' ': case '\t': break; default: WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 missing delimiter after " "field %s", bkey); return NULL; } char* start = strstr(tag, ">"); if (!start || (start > input + ilen)) { WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 missing field %s", bkey); return NULL; } const char* end = start; const char* dend = start - 1; if (*dend != '/') { char ekey[128] = { 0 }; const int erc = _snprintf(ekey, sizeof(ekey), "", key); WINPR_ASSERT(erc > 0); WINPR_ASSERT(erc < sizeof(ekey)); const size_t offset = start - tag; dend = end = strrstr(start, ilen - offset, ekey); if (end) end += strnlen(ekey, sizeof(ekey)); } if (!end) { WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 missing end tag for field %s", key); return NULL; } if (plen) *plen = end - tag; if (pdata) *pdata = data; if (pdlen) *pdlen = dend - data; return tag; } /**! \brief this function returns a XML element identified by \b key * The input string will be manipulated, so that the element found is '\0' terminated. * * This function can not find multiple elements on the same level as the input string is changed! */ static BOOL freerdp_assistance_consume_input_and_get_element(char* input, const char* key, char** element, size_t* elen) { WINPR_ASSERT(input); WINPR_ASSERT(key); WINPR_ASSERT(element); WINPR_ASSERT(elen); size_t len = 0; size_t dlen = 0; char* data = NULL; char* tag = freerdp_assistance_contains_element(input, strlen(input), key, &len, &data, &dlen); if (!tag) return FALSE; char* end = data + dlen; *tag = '\0'; *end = '\0'; *element = data; *elen = dlen + 1; return TRUE; } static BOOL freerdp_assistance_get_element(char* input, size_t ilen, const char* key, char** element, size_t* elen) { WINPR_ASSERT(input); WINPR_ASSERT(key); WINPR_ASSERT(element); WINPR_ASSERT(elen); size_t len = 0; size_t dlen = 0; char* data = NULL; char* tag = freerdp_assistance_contains_element(input, ilen, key, &len, &data, &dlen); if (!tag) return FALSE; if (tag + len > input + ilen) return FALSE; char* end = tag + len; *element = data; *elen = end - data + 1; return TRUE; } static BOOL freerdp_assistance_parse_all_elements_of(rdpAssistanceFile* file, char* data, size_t len, const char* key, BOOL (*fkt)(rdpAssistanceFile* file, char* data, size_t len)) { char* val = NULL; size_t vlen = 0; while (freerdp_assistance_get_element(data, len, key, &val, &vlen)) { data = val + vlen; len = strnlen(data, len); if (vlen > 0) { val[vlen - 1] = '\0'; if (!fkt(file, val, vlen)) return FALSE; } } return TRUE; } static BOOL freerdp_assistance_parse_all_elements_of_l(rdpAssistanceFile* file, char* data, size_t len) { UINT32 p = 0; const char* n = NULL; const char* u = NULL; size_t nlen = 0; size_t ulen = 0; if (!freerdp_assistance_parse_attr_uint32(&p, "P", data)) return FALSE; if (!freerdp_assistance_parse_attr(&n, &nlen, "N", data)) return FALSE; if (!freerdp_assistance_parse_attr(&u, &ulen, "U", data)) return FALSE; if (n && (nlen > 0)) { if (!append_address_to_list(file->MachineAddresses, n, nlen)) return FALSE; if (!ArrayList_Append(file->MachinePorts, (void*)(uintptr_t)p)) return FALSE; } if (u && (ulen > 0)) { if (!append_address_to_list(file->MachineAddresses, u, ulen)) return FALSE; if (!ArrayList_Append(file->MachinePorts, (void*)(uintptr_t)p)) return FALSE; } return TRUE; } static BOOL freerdp_assistance_parse_all_elements_of_t(rdpAssistanceFile* file, char* data, size_t len) { UINT32 id = 0; UINT32 sid = 0; if (!freerdp_assistance_parse_attr_uint32(&id, "ID", data)) return FALSE; if (!freerdp_assistance_parse_attr_uint32(&sid, "SID", data)) return FALSE; WLog_DBG(TAG, "transport id=%" PRIu32 ", sid=%" PRIu32, id, sid); return freerdp_assistance_parse_all_elements_of(file, data, len, "L", freerdp_assistance_parse_all_elements_of_l); } static BOOL freerdp_assistance_parse_all_elements_of_c(rdpAssistanceFile* file, char* data, size_t len) { return freerdp_assistance_parse_all_elements_of(file, data, len, "T", freerdp_assistance_parse_all_elements_of_t); } static BOOL freerdp_assistance_parse_find_elements_of_c(rdpAssistanceFile* file, char* data, size_t len) { return freerdp_assistance_parse_all_elements_of(file, data, len, "C", freerdp_assistance_parse_all_elements_of_c); } static BOOL freerdp_assistance_parse_connection_string2(rdpAssistanceFile* file) { BOOL rc = FALSE; WINPR_ASSERT(file); if (!file->ConnectionString2) return FALSE; char* str = _strdup(file->ConnectionString2); if (!str) goto out_fail; char* e = NULL; size_t elen = 0; if (!freerdp_assistance_consume_input_and_get_element(str, "E", &e, &elen)) goto out_fail; if (!e || (elen == 0)) goto out_fail; char* a = NULL; size_t alen = 0; if (!freerdp_assistance_get_element(e, elen, "A", &a, &alen)) goto out_fail; if (!a || (alen == 0)) goto out_fail; if (!freerdp_assistance_parse_find_elements_of_c(file, e, elen)) goto out_fail; /* '\0' terminate the detected XML elements so * the parser can continue with terminated strings */ a[alen] = '\0'; if (!freerdp_assistance_parse_attr_str(&file->RASpecificParams, "KH", a)) goto out_fail; if (!freerdp_assistance_parse_attr_str(&file->RASpecificParams2, "KH2", a)) goto out_fail; if (!freerdp_assistance_parse_attr_str(&file->RASessionId, "ID", a)) goto out_fail; rc = TRUE; out_fail: free(str); return rc; } char* freerdp_assistance_construct_expert_blob(const char* name, const char* pass) { size_t size = 0; size_t nameLength = 0; size_t passLength = 0; char* ExpertBlob = NULL; if (!name || !pass) return NULL; nameLength = strlen(name) + strlen("NAME="); passLength = strlen(pass) + strlen("PASS="); size = nameLength + passLength + 64; ExpertBlob = (char*)calloc(1, size); if (!ExpertBlob) return NULL; sprintf_s(ExpertBlob, size, "%" PRIdz ";NAME=%s%" PRIdz ";PASS=%s", nameLength, name, passLength, pass); return ExpertBlob; } char* freerdp_assistance_generate_pass_stub(DWORD flags) { UINT32 nums[14]; char* passStub = NULL; char set1[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789*_"; char set2[12] = "!@#$&^*()-+="; char set3[10] = "0123456789"; char set4[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; char set5[26] = "abcdefghijklmnopqrstuvwxyz"; passStub = (char*)malloc(15); if (!passStub) return NULL; /** * PassStub generation: * * Characters 0 and 5-13 are from the set A-Z a-z 0-9 * _ * Character 1 is from the set !@#$&^*()-+= * Character 2 is from the set 0-9 * Character 3 is from the set A-Z * Character 4 is from the set a-z * * Example: WB^6HsrIaFmEpi */ winpr_RAND(nums, sizeof(nums)); passStub[0] = set1[nums[0] % sizeof(set1)]; /* character 0 */ passStub[1] = set2[nums[1] % sizeof(set2)]; /* character 1 */ passStub[2] = set3[nums[2] % sizeof(set3)]; /* character 2 */ passStub[3] = set4[nums[3] % sizeof(set4)]; /* character 3 */ passStub[4] = set5[nums[4] % sizeof(set5)]; /* character 4 */ passStub[5] = set1[nums[5] % sizeof(set1)]; /* character 5 */ passStub[6] = set1[nums[6] % sizeof(set1)]; /* character 6 */ passStub[7] = set1[nums[7] % sizeof(set1)]; /* character 7 */ passStub[8] = set1[nums[8] % sizeof(set1)]; /* character 8 */ passStub[9] = set1[nums[9] % sizeof(set1)]; /* character 9 */ passStub[10] = set1[nums[10] % sizeof(set1)]; /* character 10 */ passStub[11] = set1[nums[11] % sizeof(set1)]; /* character 11 */ passStub[12] = set1[nums[12] % sizeof(set1)]; /* character 12 */ passStub[13] = set1[nums[13] % sizeof(set1)]; /* character 13 */ passStub[14] = '\0'; return passStub; } BYTE* freerdp_assistance_encrypt_pass_stub(const char* password, const char* passStub, size_t* pEncryptedSize) { BOOL rc = 0; size_t cbPasswordW = 0; size_t cbPassStubW = 0; size_t EncryptedSize = 0; BYTE PasswordHash[WINPR_MD5_DIGEST_LENGTH]; WINPR_CIPHER_CTX* rc4Ctx = NULL; BYTE* pbIn = NULL; BYTE* pbOut = NULL; size_t cbOut = 0; size_t cbIn = 0; size_t cbFinal = 0; WCHAR* PasswordW = ConvertUtf8ToWCharAlloc(password, &cbPasswordW); WCHAR* PassStubW = ConvertUtf8ToWCharAlloc(passStub, &cbPassStubW); if (!PasswordW || !PassStubW) goto fail; cbPasswordW = (cbPasswordW) * sizeof(WCHAR); cbPassStubW = (cbPassStubW) * sizeof(WCHAR); if (!winpr_Digest(WINPR_MD_MD5, (BYTE*)PasswordW, cbPasswordW, (BYTE*)PasswordHash, sizeof(PasswordHash))) goto fail; EncryptedSize = cbPassStubW + 4; pbIn = (BYTE*)calloc(1, EncryptedSize); pbOut = (BYTE*)calloc(1, EncryptedSize); if (!pbIn || !pbOut) goto fail; *((UINT32*)pbIn) = (UINT32)cbPassStubW; CopyMemory(&pbIn[4], PassStubW, cbPassStubW); rc4Ctx = winpr_Cipher_New(WINPR_CIPHER_ARC4_128, WINPR_ENCRYPT, PasswordHash, NULL); if (!rc4Ctx) { WLog_ERR(TAG, "winpr_Cipher_New failure"); goto fail; } cbOut = cbFinal = 0; cbIn = EncryptedSize; rc = winpr_Cipher_Update(rc4Ctx, pbIn, cbIn, pbOut, &cbOut); if (!rc) { WLog_ERR(TAG, "winpr_Cipher_Update failure"); goto fail; } if (!winpr_Cipher_Final(rc4Ctx, pbOut + cbOut, &cbFinal)) { WLog_ERR(TAG, "winpr_Cipher_Final failure"); goto fail; } winpr_Cipher_Free(rc4Ctx); free(pbIn); free(PasswordW); free(PassStubW); *pEncryptedSize = EncryptedSize; return pbOut; fail: winpr_Cipher_Free(rc4Ctx); free(PasswordW); free(PassStubW); free(pbIn); free(pbOut); return NULL; } static BOOL freerdp_assistance_decrypt2(rdpAssistanceFile* file) { BOOL rc = FALSE; int status = 0; size_t cbPasswordW = 0; size_t cchOutW = 0; WINPR_CIPHER_CTX* aesDec = NULL; WCHAR* PasswordW = NULL; BYTE* pbIn = NULL; BYTE* pbOut = NULL; size_t cbOut = 0; size_t cbIn = 0; size_t cbFinal = 0; BYTE DerivedKey[WINPR_AES_BLOCK_SIZE] = { 0 }; BYTE InitializationVector[WINPR_AES_BLOCK_SIZE] = { 0 }; BYTE PasswordHash[WINPR_SHA1_DIGEST_LENGTH] = { 0 }; WINPR_ASSERT(file); if (!file->password) return FALSE; PasswordW = ConvertUtf8ToWCharAlloc(file->password, &cbPasswordW); if (!PasswordW) { WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Conversion from UCS2 to UTF8 failed"); return FALSE; } cbPasswordW = (cbPasswordW) * sizeof(WCHAR); if (!winpr_Digest(WINPR_MD_SHA1, (BYTE*)PasswordW, cbPasswordW, PasswordHash, sizeof(PasswordHash))) goto fail; if (!freerdp_assistance_crypt_derive_key_sha1(PasswordHash, sizeof(PasswordHash), DerivedKey, sizeof(DerivedKey))) goto fail; aesDec = winpr_Cipher_New(WINPR_CIPHER_AES_128_CBC, WINPR_DECRYPT, DerivedKey, InitializationVector); if (!aesDec) goto fail; cbOut = cbFinal = 0; cbIn = (size_t)file->EncryptedLHTicketLength; pbIn = (BYTE*)file->EncryptedLHTicket; pbOut = (BYTE*)calloc(1, cbIn + WINPR_AES_BLOCK_SIZE + 2); if (!pbOut) goto fail; if (!winpr_Cipher_Update(aesDec, pbIn, cbIn, pbOut, &cbOut)) goto fail; if (!winpr_Cipher_Final(aesDec, pbOut + cbOut, &cbFinal)) { WLog_ERR(TAG, "winpr_Cipher_Final failure"); goto fail; } cbOut += cbFinal; cbFinal = 0; union { const WCHAR* wc; const BYTE* b; } cnv; cnv.b = pbOut; cchOutW = cbOut / sizeof(WCHAR); if (!update_connectionstring2_wchar(file, cnv.wc, cchOutW)) { WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Conversion from UCS2 to UTF8 failed"); goto fail; } if (!freerdp_assistance_parse_connection_string2(file)) goto fail; rc = TRUE; fail: winpr_Cipher_Free(aesDec); free(PasswordW); free(pbOut); WLog_DBG(TAG, "freerdp_assistance_parse_connection_string2: %d", status); return rc; } BYTE* freerdp_assistance_hex_string_to_bin(const void* raw, size_t* size) { BYTE* buffer = NULL; if (!raw || !size) return NULL; *size = 0; const size_t length = strlen(raw); buffer = calloc(length, sizeof(BYTE)); if (!buffer) return NULL; const size_t rc = winpr_HexStringToBinBuffer(raw, length, buffer, length); if (rc == 0) { free(buffer); return NULL; } *size = rc; return buffer; } char* freerdp_assistance_bin_to_hex_string(const void* raw, size_t size) { return winpr_BinToHexString(raw, size, FALSE); } static int freerdp_assistance_parse_uploadinfo(rdpAssistanceFile* file, char* uploadinfo, size_t uploadinfosize) { const char escalated[9] = "Escalated"; const size_t esclen = sizeof(escalated); const char* typestr = NULL; size_t typelen = 0; if (!uploadinfo || (uploadinfosize == 0)) return -1; if (strnlen(uploadinfo, uploadinfosize) == uploadinfosize) { WLog_WARN(TAG, "UPLOADINFOR string is not '\0' terminated"); return -1; } if (!freerdp_assistance_parse_attr(&typestr, &typelen, "TYPE", uploadinfo)) return -1; if ((typelen != esclen) || (strncmp(typestr, escalated, esclen) != 0)) { WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Missing or invalid UPLOADINFO TYPE '%s' [%" PRIuz "]", typestr, typelen); return -1; } char* uploaddata = NULL; size_t uploaddatasize = 0; if (!freerdp_assistance_consume_input_and_get_element(uploadinfo, "UPLOADDATA", &uploaddata, &uploaddatasize)) return -1; /* Parse USERNAME */ if (!freerdp_assistance_parse_attr_str(&file->Username, "USERNAME", uploaddata)) return -1; /* Parse LHTICKET */ if (!freerdp_assistance_parse_attr_str(&file->LHTicket, "LHTICKET", uploaddata)) return -1; /* Parse RCTICKET */ if (!freerdp_assistance_parse_attr_str(&file->RCTicket, "RCTICKET", uploaddata)) return -1; /* Parse RCTICKETENCRYPTED */ if (!freerdp_assistance_parse_attr_bool(&file->RCTicketEncrypted, "RCTICKETENCRYPTED", uploaddata)) return -1; /* Parse PassStub */ if (!freerdp_assistance_parse_attr_str(&file->PassStub, "PassStub", uploaddata)) return -1; if (file->PassStub) { const char* amp = "&"; char* passtub = strstr(file->PassStub, amp); while (passtub) { const char* end = passtub + 5; const size_t len = strlen(end); memmove(&passtub[1], end, len + 1); passtub = strstr(passtub, amp); } } /* Parse DtStart */ if (!freerdp_assistance_parse_attr_uint32(&file->DtStart, "DtStart", uploaddata)) return -1; /* Parse DtLength */ if (!freerdp_assistance_parse_attr_uint32(&file->DtLength, "DtLength", uploaddata)) return -1; /* Parse L (LowSpeed) */ if (!freerdp_assistance_parse_attr_bool(&file->LowSpeed, "L", uploaddata)) return -1; file->Type = (file->LHTicket) ? 2 : 1; int status = 0; switch (file->Type) { case 2: { file->EncryptedLHTicket = freerdp_assistance_hex_string_to_bin( file->LHTicket, &file->EncryptedLHTicketLength); if (!freerdp_assistance_decrypt2(file)) status = -1; } break; case 1: { if (!freerdp_assistance_parse_connection_string1(file)) status = -1; } break; default: return -1; } if (status < 0) { WLog_ERR(TAG, "freerdp_assistance_parse_connection_string1 failure: %d", status); return -1; } file->EncryptedPassStub = freerdp_assistance_encrypt_pass_stub(file->password, file->PassStub, &file->EncryptedPassStubLength); if (!file->EncryptedPassStub) return -1; return 1; } static int freerdp_assistance_parse_file_buffer_int(rdpAssistanceFile* file, char* buffer, size_t size, const char* password) { WINPR_ASSERT(file); WINPR_ASSERT(buffer); WINPR_ASSERT(size > 0); if (!update_password(file, password)) return -1; char* uploadinfo = NULL; size_t uploadinfosize = 0; if (freerdp_assistance_consume_input_and_get_element(buffer, "UPLOADINFO", &uploadinfo, &uploadinfosize)) return freerdp_assistance_parse_uploadinfo(file, uploadinfo, uploadinfosize); size_t elen = 0; const char* estr = freerdp_assistance_contains_element(buffer, size, "E", &elen, NULL, NULL); if (!estr || (elen == 0)) { WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Neither UPLOADINFO nor found"); return -1; } if (!update_connectionstring2(file, estr, elen)) return -1; if (!freerdp_assistance_parse_connection_string2(file)) return -1; return 1; } int freerdp_assistance_parse_file_buffer(rdpAssistanceFile* file, const char* cbuffer, size_t size, const char* password) { WINPR_ASSERT(file); if (!password) { WLog_WARN(TAG, "empty password supplied"); } if (!cbuffer || (size == 0)) { WLog_WARN(TAG, "no data supplied [%p, %" PRIuz "]", cbuffer, size); return -1; } char* abuffer = strndup(cbuffer, size); const size_t len = strnlen(cbuffer, size); if (len == size) WLog_WARN(TAG, "Input data not '\0' terminated"); if (!abuffer) return -1; const int rc = freerdp_assistance_parse_file_buffer_int(file, abuffer, len + 1, password); free(abuffer); return rc; } int freerdp_assistance_parse_file(rdpAssistanceFile* file, const char* name, const char* password) { int status = 0; BYTE* buffer = NULL; FILE* fp = NULL; size_t readSize = 0; union { INT64 i64; size_t s; } fileSize; if (!update_name(file, name)) return -1; fp = winpr_fopen(name, "r"); if (!fp) { WLog_ERR(TAG, "Failed to open ASSISTANCE file %s ", name); return -1; } _fseeki64(fp, 0, SEEK_END); fileSize.i64 = _ftelli64(fp); _fseeki64(fp, 0, SEEK_SET); if (fileSize.i64 < 1) { WLog_ERR(TAG, "Failed to read ASSISTANCE file %s ", name); fclose(fp); return -1; } buffer = (BYTE*)malloc(fileSize.s + 2); if (!buffer) { fclose(fp); return -1; } readSize = fread(buffer, fileSize.s, 1, fp); if (!readSize) { if (!ferror(fp)) readSize = fileSize.s; } fclose(fp); if (readSize < 1) { WLog_ERR(TAG, "Failed to read ASSISTANCE file %s ", name); free(buffer); buffer = NULL; return -1; } buffer[fileSize.s] = '\0'; buffer[fileSize.s + 1] = '\0'; status = freerdp_assistance_parse_file_buffer(file, (char*)buffer, fileSize.s, password); free(buffer); return status; } BOOL freerdp_assistance_populate_settings_from_assistance_file(rdpAssistanceFile* file, rdpSettings* settings) { if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceMode, TRUE)) return FALSE; if (!file->RASessionId || !file->MachineAddresses) return FALSE; if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceSessionId, file->RASessionId)) return FALSE; if (file->RCTicket) { if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceRCTicket, file->RCTicket)) return FALSE; } else { if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistanceRCTicket, file->ConnectionString2)) return FALSE; } if (file->PassStub) { if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistancePassStub, file->PassStub)) return FALSE; } if (ArrayList_Count(file->MachineAddresses) < 1) return FALSE; const char* addr = ArrayList_GetItem(file->MachineAddresses, 0); if (!freerdp_settings_set_string(settings, FreeRDP_ServerHostname, addr)) return FALSE; if (!freerdp_settings_set_string(settings, FreeRDP_AssistanceFile, file->filename)) return FALSE; if (!freerdp_settings_set_string(settings, FreeRDP_RemoteAssistancePassword, file->password)) return FALSE; if (file->Username) { if (!freerdp_settings_set_string(settings, FreeRDP_Username, file->Username)) return FALSE; } if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceMode, TRUE)) return FALSE; const size_t ports = ArrayList_Count(file->MachinePorts); const size_t addresses = ArrayList_Count(file->MachineAddresses); if (ports < 1) return FALSE; if (ports != addresses) return FALSE; const UINT32 port = (UINT32)ArrayList_GetItem(file->MachinePorts, 0); if (!freerdp_settings_set_uint32(settings, FreeRDP_ServerPort, port)) return FALSE; if (!freerdp_target_net_adresses_reset(settings, ports)) return FALSE; for (size_t x = 0; x < ports; x++) { const UINT32 port = (UINT32)ArrayList_GetItem(file->MachinePorts, x); if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetPorts, x, &port)) return FALSE; } for (size_t i = 0; i < addresses; i++) { const char* maddr = ArrayList_GetItem(file->MachineAddresses, i); if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses, i, maddr)) return FALSE; } return TRUE; } static BOOL setup_string(wArrayList* list) { WINPR_ASSERT(list); wObject* obj = ArrayList_Object(list); if (!obj) return FALSE; obj->fnObjectFree = free; // obj->fnObjectNew = wwinpr_ObjectStringClone; return TRUE; } rdpAssistanceFile* freerdp_assistance_file_new(void) { winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT); rdpAssistanceFile* file = calloc(1, sizeof(rdpAssistanceFile)); if (!file) return NULL; file->MachineAddresses = ArrayList_New(FALSE); file->MachinePorts = ArrayList_New(FALSE); file->MachineUris = ArrayList_New(FALSE); if (!file->MachineAddresses || !file->MachinePorts || !file->MachineUris) goto fail; if (!setup_string(file->MachineAddresses) || !setup_string(file->MachineUris)) goto fail; return file; fail: WINPR_PRAGMA_DIAG_PUSH WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC freerdp_assistance_file_free(file); WINPR_PRAGMA_DIAG_POP return NULL; } void freerdp_assistance_file_free(rdpAssistanceFile* file) { if (!file) return; update_password(file, NULL); update_connectionstring2(file, NULL, 0); free(file->filename); free(file->Username); free(file->LHTicket); free(file->RCTicket); free(file->PassStub); free(file->ConnectionString1); free(file->EncryptedLHTicket); free(file->RASessionId); free(file->RASpecificParams); free(file->RASpecificParams2); free(file->EncryptedPassStub); ArrayList_Free(file->MachineAddresses); ArrayList_Free(file->MachinePorts); ArrayList_Free(file->MachineUris); free(file); } void freerdp_assistance_print_file(rdpAssistanceFile* file, wLog* log, DWORD level) { WINPR_ASSERT(file); WLog_Print(log, level, "Username: %s", file->Username); WLog_Print(log, level, "LHTicket: %s", file->LHTicket); WLog_Print(log, level, "RCTicket: %s", file->RCTicket); WLog_Print(log, level, "RCTicketEncrypted: %" PRId32, file->RCTicketEncrypted); WLog_Print(log, level, "PassStub: %s", file->PassStub); WLog_Print(log, level, "DtStart: %" PRIu32, file->DtStart); WLog_Print(log, level, "DtLength: %" PRIu32, file->DtLength); WLog_Print(log, level, "LowSpeed: %" PRId32, file->LowSpeed); WLog_Print(log, level, "RASessionId: %s", file->RASessionId); WLog_Print(log, level, "RASpecificParams: %s", file->RASpecificParams); WLog_Print(log, level, "RASpecificParams2: %s", file->RASpecificParams2); for (size_t x = 0; x < ArrayList_Count(file->MachineAddresses); x++) { UINT32 port = 0; const char* uri = NULL; const char* addr = ArrayList_GetItem(file->MachineAddresses, x); if (x < ArrayList_Count(file->MachinePorts)) port = (UINT32)ArrayList_GetItem(file->MachinePorts, x); if (x < ArrayList_Count(file->MachineUris)) uri = ArrayList_GetItem(file->MachineUris, x); WLog_Print(log, level, "MachineAddress [%" PRIdz ": %s", x, addr); WLog_Print(log, level, "MachinePort [%" PRIdz ": %" PRIu32, x, port); WLog_Print(log, level, "MachineURI [%" PRIdz ": %s", x, uri); } } BOOL freerdp_assistance_get_encrypted_pass_stub(rdpAssistanceFile* file, const char** pwd, size_t* size) { if (!file || !pwd || !size) return FALSE; *pwd = (const char*)file->EncryptedPassStub; *size = file->EncryptedPassStubLength; return TRUE; } int freerdp_assistance_set_connection_string2(rdpAssistanceFile* file, const char* string, const char* password) { if (!file || !string || !password) return -1; char* str = _strdup(string); if (!str) return -1; if (!update_connectionstring2_nocopy(file, str)) return -1; if (!update_password(file, password)) return -1; return freerdp_assistance_parse_connection_string2(file); }