From 89bbed83db22b8d63b3e6354963d8d595199304e Mon Sep 17 00:00:00 2001 From: akallabeth Date: Mon, 12 Jun 2023 12:06:38 +0200 Subject: [PATCH] [common,assistance] clean up assistance file parser --- libfreerdp/common/assistance.c | 1333 +++++++++-------- libfreerdp/common/test/TestCommonAssistance.c | 19 + 2 files changed, 723 insertions(+), 629 deletions(-) diff --git a/libfreerdp/common/assistance.c b/libfreerdp/common/assistance.c index 73abee8e9..66286e692 100644 --- a/libfreerdp/common/assistance.c +++ b/libfreerdp/common/assistance.c @@ -22,6 +22,8 @@ #include #include +#include +#include #include #include #include @@ -59,17 +61,121 @@ struct rdp_assistance_file BYTE* EncryptedLHTicket; size_t EncryptedLHTicketLength; - UINT32 MachineCount; - char** MachineAddresses; - UINT32* MachinePorts; + wArrayList* MachineAddresses; + wArrayList* MachinePorts; + wArrayList* MachineUris; char* RASessionId; char* RASpecificParams; + char* RASpecificParams2; char* filename; char* password; }; +static char* strrstr(const char* haystack, size_t len, const char* needle) +{ + if (*needle == '\0') + return (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 @@ -110,20 +216,19 @@ static BOOL freerdp_assistance_crypt_derive_key_sha1(BYTE* hash, size_t hashLeng size_t keyLength) { BOOL rc = FALSE; - size_t i; - BYTE* buffer; - BYTE pad1[64]; - BYTE pad2[64]; - memset(pad1, 0x36, 64); - memset(pad2, 0x5C, 64); + BYTE pad1[64] = { 0 }; + BYTE pad2[64] = { 0 }; - for (i = 0; i < hashLength; i++) + 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]; } - buffer = (BYTE*)calloc(hashLength, 2); + BYTE* buffer = (BYTE*)calloc(hashLength, 2); if (!buffer) goto fail; @@ -141,32 +246,12 @@ fail: return rc; } -static BOOL reallocate(rdpAssistanceFile* file, const char* host, UINT32 port) -{ - void *tmp1, *tmp2; - file->MachineCount++; - tmp1 = realloc(file->MachinePorts, sizeof(UINT32) * file->MachineCount); - tmp2 = realloc(file->MachineAddresses, sizeof(char*) * file->MachineCount); - - if (!tmp1 || !tmp2) - { - free(tmp1); - free(tmp2); - return FALSE; - } - - file->MachinePorts = tmp1; - file->MachineAddresses = tmp2; - file->MachinePorts[file->MachineCount - 1] = port; - file->MachineAddresses[file->MachineCount - 1] = _strdup(host); - return TRUE; -} - static BOOL append_address(rdpAssistanceFile* file, const char* host, const char* port) { - unsigned long p; + WINPR_ASSERT(file); + errno = 0; - p = strtoul(port, NULL, 0); + unsigned long p = strtoul(port, NULL, 0); if ((errno != 0) || (p == 0) || (p > UINT16_MAX)) { @@ -175,24 +260,27 @@ static BOOL append_address(rdpAssistanceFile* file, const char* host, const char return FALSE; } - return reallocate(file, host, (UINT16)p); + if (!ArrayList_Append(file->MachineAddresses, _strdup(host))) + return FALSE; + return ArrayList_Append(file->MachinePorts, 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 (!file || !list) + if (!list) return FALSE; char* strp = list; char* s = ";"; - char* token; // get the first token - token = strtok(strp, s); + char* token = strtok(strp, s); // walk through other tokens while (token != NULL) @@ -213,29 +301,27 @@ out: static BOOL freerdp_assistance_parse_connection_string1(rdpAssistanceFile* file) { - size_t i; - char* str; - int count; - size_t length; - char* tokens[8]; + char* tokens[8] = { 0 }; BOOL rc = FALSE; - if (!file || !file->RCTicket) + WINPR_ASSERT(file); + + if (!file->RCTicket) return FALSE; /** * ,,,, * ,,, */ - count = 1; - str = _strdup(file->RCTicket); + char* str = _strdup(file->RCTicket); if (!str) goto error; - length = strlen(str); + const size_t length = strlen(str); - for (i = 0; i < length; i++) + int count = 1; + for (size_t i = 0; i < length; i++) { if (str[i] == ',') count++; @@ -247,7 +333,7 @@ static BOOL freerdp_assistance_parse_connection_string1(rdpAssistanceFile* file) count = 0; tokens[count++] = str; - for (i = 0; i < length; i++) + for (size_t i = 0; i < length; i++) { if (str[i] == ',') { @@ -303,170 +389,339 @@ error: * */ +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)); + char* start = strstr(tag, ">"); + + if (!start || (start > input + ilen)) + { + WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 missing field %s", bkey); + return NULL; + } + + char* end = start; + 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 (!ArrayList_Append(file->MachineAddresses, strndup(n, nlen))) + return FALSE; + if (!ArrayList_Append(file->MachineAddresses, strndup(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) { - char* str; - char* tag; - char* end; - char* p; BOOL rc = FALSE; - if (!file || !file->ConnectionString2) + WINPR_ASSERT(file); + + if (!file->ConnectionString2) return FALSE; - str = file->ConnectionString2; - - if (!strstr(str, "")) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 missing field "); - return FALSE; - } - - if (!strstr(str, "")) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 missing field "); - return FALSE; - } - - str = _strdup(file->ConnectionString2); - + char* str = _strdup(file->ConnectionString2); if (!str) goto out_fail; - if (!(tag = strstr(str, ") */ - end = strstr(tag, "/>"); - - if (!end) + char* e = NULL; + size_t elen = 0; + if (!freerdp_assistance_consume_input_and_get_element(str, "E", &e, &elen)) goto out_fail; - *end = '\0'; - p = strstr(tag, "KH=\""); + if (!e || (elen == 0)) + goto out_fail; - if (p) - { - char* q; - size_t length; - p += sizeof("KH=\"") - 1; - q = strchr(p, '"'); + char* a = NULL; + size_t alen = 0; + if (!freerdp_assistance_get_element(e, elen, "A", &a, &alen)) + goto out_fail; - if (!q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 invalid field KH=%s", - q); - goto out_fail; - } + if (!a || (alen == 0)) + goto out_fail; - if (p > q) - { - WLog_ERR( - TAG, - "Failed to parse ASSISTANCE file: ConnectionString2 invalid field order for KH"); - goto out_fail; - } + if (!freerdp_assistance_parse_find_elements_of_c(file, e, elen)) + goto out_fail; - length = q - p; - free(file->RASpecificParams); - file->RASpecificParams = (char*)malloc(length + 1); + /* '\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 (!file->RASpecificParams) - goto out_fail; + if (!freerdp_assistance_parse_attr_str(&file->RASpecificParams2, "KH2", a)) + goto out_fail; - CopyMemory(file->RASpecificParams, p, length); - file->RASpecificParams[length] = '\0'; - } - - p = strstr(tag, "ID=\""); - - if (p) - { - char* q; - size_t length; - p += sizeof("ID=\"") - 1; - q = strchr(p, '"'); - - if (!q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 invalid field ID=%s", - q); - goto out_fail; - } - - if (p > q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: ConnectionString2 invalid field " - "order for ID"); - goto out_fail; - } - length = q - p; - free(file->RASessionId); - file->RASessionId = (char*)malloc(length + 1); - - if (!file->RASessionId) - goto out_fail; - - CopyMemory(file->RASessionId, p, length); - file->RASessionId[length] = '\0'; - } - - *end = '/'; - /* Parse 6) - { - if (!append_address(file, p, port)) - goto out_fail; - } - - p = strstr(q, "RASessionId, "ID", a)) + goto out_fail; rc = TRUE; out_fail: @@ -613,7 +868,7 @@ fail: return NULL; } -static BOOL freerdp_assistance_decrypt2(rdpAssistanceFile* file, const char* password) +static BOOL freerdp_assistance_decrypt2(rdpAssistanceFile* file) { BOOL rc = FALSE; int status = 0; @@ -628,10 +883,12 @@ static BOOL freerdp_assistance_decrypt2(rdpAssistanceFile* file, const char* pas BYTE InitializationVector[WINPR_AES_BLOCK_SIZE] = { 0 }; BYTE PasswordHash[WINPR_SHA1_DIGEST_LENGTH] = { 0 }; - if (!file || !password) + WINPR_ASSERT(file); + + if (!file->password) return FALSE; - PasswordW = ConvertUtf8ToWCharAlloc(password, &cbPasswordW); + PasswordW = ConvertUtf8ToWCharAlloc(file->password, &cbPasswordW); if (!PasswordW) { WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Conversion from UCS2 to UTF8 failed"); @@ -682,8 +939,8 @@ static BOOL freerdp_assistance_decrypt2(rdpAssistanceFile* file, const char* pas cnv.b = pbOut; cchOutW = cbOut / sizeof(WCHAR); - file->ConnectionString2 = ConvertWCharNToUtf8Alloc(cnv.wc, cchOutW, NULL); - if (!file->ConnectionString2) + + if (!update_connectionstring2_wchar(file, cnv.wc, cchOutW)) { WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Conversion from UCS2 to UTF8 failed"); goto fail; @@ -726,406 +983,183 @@ char* freerdp_assistance_bin_to_hex_string(const void* raw, size_t size) return winpr_BinToHexString(raw, size, FALSE); } -int freerdp_assistance_parse_file_buffer(rdpAssistanceFile* file, const char* buffer, size_t size, +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; + + 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) { - char* p; - char* q; - char* r; - char* amp; - int status; - size_t length; - - free(file->password); - file->password = _strdup(password); - - p = strstr(buffer, "UPLOADINFO"); - - if (p) + WINPR_ASSERT(file); + if (!password) { - p = strstr(p + sizeof("UPLOADINFO") - 1, "TYPE=\""); - - if (!p) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Missing UPLOADINFO TYPE"); - return -1; - } - - p = strstr(buffer, "UPLOADDATA"); - - if (!p) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Missing UPLOADDATA"); - return -1; - } - - /* Parse USERNAME */ - p = strstr(buffer, "USERNAME=\""); - - if (p) - { - p += sizeof("USERNAME=\"") - 1; - q = strchr(p, '"'); - - if (!q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid USERNAME=%s", p); - return -1; - } - - if (p > q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: invalid field " - "order for USERNAME"); - return -1; - } - - length = q - p; - file->Username = (char*)malloc(length + 1); - - if (!file->Username) - return -1; - - CopyMemory(file->Username, p, length); - file->Username[length] = '\0'; - } - - /* Parse LHTICKET */ - p = strstr(buffer, "LHTICKET=\""); - - if (p) - { - p += sizeof("LHTICKET=\"") - 1; - q = strchr(p, '"'); - - if (!q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid LHTICKET=%s", p); - return -1; - } - - if (p > q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: invalid field " - "order for LHTICKET"); - return -1; - } - - length = q - p; - file->LHTicket = (char*)malloc(length + 1); - - if (!file->LHTicket) - return -1; - - CopyMemory(file->LHTicket, p, length); - file->LHTicket[length] = '\0'; - } - - /* Parse RCTICKET */ - p = strstr(buffer, "RCTICKET=\""); - - if (p) - { - p += sizeof("RCTICKET=\"") - 1; - q = strchr(p, '"'); - - if (!q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid RCTICKET=%s", p); - return -1; - } - - if (p > q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: invalid field " - "order for RCTICKET"); - return -1; - } - - length = q - p; - file->RCTicket = (char*)malloc(length + 1); - - if (!file->RCTicket) - return -1; - - CopyMemory(file->RCTicket, p, length); - file->RCTicket[length] = '\0'; - } - - /* Parse RCTICKETENCRYPTED */ - p = strstr(buffer, "RCTICKETENCRYPTED=\""); - - if (p) - { - p += sizeof("RCTICKETENCRYPTED=\"") - 1; - q = strchr(p, '"'); - - if (!q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid RCTICKETENCRYPTED=%s", p); - return -1; - } - - if (p > q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: invalid field " - "order for RCTICKETENCRYPTED"); - return -1; - } - - length = q - p; - - if ((length == 1) && (p[0] == '1')) - file->RCTicketEncrypted = TRUE; - } - - /* Parse PassStub */ - p = strstr(buffer, "PassStub=\""); - - if (p) - { - p += sizeof("PassStub=\"") - 1; - - // needs to be unescaped (& => &) - amp = strstr(p, "&"); - - q = strchr(p, '"'); - - if (!q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid PassStub=%s", p); - return -1; - } - - if (p > q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: invalid field " - "order for PassStub"); - return -1; - } - - if (amp) - { - length = q - p - 4; - } - else - { - length = q - p; - } - - file->PassStub = (char*)malloc(length + 1); - - if (!file->PassStub) - return -1; - - if (amp) - { - // just skip over "amp;" leaving "&" - CopyMemory(file->PassStub, p, amp - p + 1); - CopyMemory(file->PassStub + (amp - p + 1), amp + 5, q - amp + 5); - } - else - { - CopyMemory(file->PassStub, p, length); - } - - file->PassStub[length] = '\0'; - } - - /* Parse DtStart */ - p = strstr(buffer, "DtStart=\""); - - if (p) - { - p += sizeof("DtStart=\"") - 1; - q = strchr(p, '"'); - - if (!q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid DtStart=%s", p); - return -1; - } - - if (p > q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: invalid field " - "order for DtStart"); - return -1; - } - - length = q - p; - r = (char*)malloc(length + 1); - - if (!r) - return -1; - - CopyMemory(r, p, length); - r[length] = '\0'; - errno = 0; - { - unsigned long val = strtoul(r, NULL, 0); - - if ((errno != 0) || (val > UINT32_MAX)) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid DtStart value %s", r); - free(r); - return -1; - } - - free(r); - file->DtStart = val; - } - } - - /* Parse DtLength */ - p = strstr(buffer, "DtLength=\""); - - if (p) - { - p += sizeof("DtLength=\"") - 1; - q = strchr(p, '"'); - - if (!q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid DtLength=%s", p); - return -1; - } - - if (p > q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: invalid field " - "order for DtLength"); - return -1; - } - - length = q - p; - r = (char*)malloc(length + 1); - - if (!r) - return -1; - - CopyMemory(r, p, length); - r[length] = '\0'; - errno = 0; - { - unsigned long val = strtoul(r, NULL, 0); - - if ((errno != 0) || (val > UINT32_MAX)) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid DtLength value %s", r); - free(r); - return -1; - } - - free(r); - file->DtLength = val; - } - } - - /* Parse L (LowSpeed) */ - p = strstr(buffer, " L=\""); - - if (p) - { - p += sizeof(" L=\"") - 1; - q = strchr(p, '"'); - - if (!q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Invalid L=%s", p); - return -1; - } - - if (p > q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: invalid field " - "order for L"); - return -1; - } - - length = q - p; - - if ((length == 1) && (p[0] == '1')) - file->LowSpeed = TRUE; - } - - file->Type = (file->LHTicket) ? 2 : 1; - status = 0; - - switch (file->Type) - { - case 2: - { - file->EncryptedLHTicket = freerdp_assistance_hex_string_to_bin( - file->LHTicket, &file->EncryptedLHTicketLength); - - if (!freerdp_assistance_decrypt2(file, password)) - 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( - password, file->PassStub, &file->EncryptedPassStubLength); - - if (!file->EncryptedPassStub) - return -1; - - return 1; + WLog_WARN(TAG, "empty password supplied"); } - p = strstr(buffer, ""); - - if (p) + if (!cbuffer || (size == 0)) { - q = strstr(buffer, ""); - - if (!q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Missing tag"); - return -1; - } - - if (p > q) - { - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: invalid field order for "); - return -1; - } - - q += sizeof("") - 1; - length = q - p; - file->ConnectionString2 = (char*)malloc(length + 1); - - if (!file->ConnectionString2) - return -1; - - CopyMemory(file->ConnectionString2, p, length); - file->ConnectionString2[length] = '\0'; - - if (!freerdp_assistance_parse_connection_string2(file)) - return -1; - - return 1; + WLog_WARN(TAG, "no data supplied [%p, %" PRIuz "]", cbuffer, size); + return -1; } - WLog_ERR(TAG, "Failed to parse ASSISTANCE file: Neither UPLOADINFO nor found"); - 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) @@ -1140,14 +1174,9 @@ int freerdp_assistance_parse_file(rdpAssistanceFile* file, const char* name, con size_t s; } fileSize; - if (!name) - { - WLog_ERR(TAG, "ASSISTANCE file %s invalid name", name); + if (!update_name(file, name)) return -1; - } - free(file->filename); - file->filename = _strdup(name); fp = winpr_fopen(name, "r"); if (!fp) @@ -1203,8 +1232,6 @@ int freerdp_assistance_parse_file(rdpAssistanceFile* file, const char* name, con BOOL freerdp_assistance_populate_settings_from_assistance_file(rdpAssistanceFile* file, rdpSettings* settings) { - UINT32 i; - if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceMode, TRUE)) return FALSE; @@ -1235,7 +1262,11 @@ BOOL freerdp_assistance_populate_settings_from_assistance_file(rdpAssistanceFile return FALSE; } - if (!freerdp_settings_set_string(settings, FreeRDP_ServerHostname, file->MachineAddresses[0])) + 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)) @@ -1253,65 +1284,95 @@ BOOL freerdp_assistance_populate_settings_from_assistance_file(rdpAssistanceFile if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceMode, TRUE)) return FALSE; - if (!freerdp_settings_set_uint32(settings, FreeRDP_ServerPort, file->MachinePorts[0])) + size_t ports = ArrayList_Count(file->MachinePorts); + if (ports < 1) return FALSE; - if (!freerdp_settings_set_pointer_len(settings, FreeRDP_TargetNetAddresses, NULL, - file->MachineCount)) + const UINT32 port = (UINT32)ArrayList_GetItem(file->MachinePorts, 0); + if (!freerdp_settings_set_uint32(settings, FreeRDP_ServerPort, port)) + return FALSE; + + if (!freerdp_settings_set_pointer_len(settings, FreeRDP_TargetNetAddresses, NULL, ports)) return FALSE; if (!freerdp_settings_set_pointer_len(settings, FreeRDP_TargetNetPorts, file->MachinePorts, - file->MachineCount)) + ports)) return FALSE; - for (i = 0; i < file->MachineCount; i++) + for (size_t i = 0; i < ArrayList_Count(file->MachineAddresses); i++) { - if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses, i, - file->MachineAddresses[i])) + const char* addr = ArrayList_GetItem(file->MachineAddresses, i); + if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses, i, addr)) 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 = _strdup; + return TRUE; +} + rdpAssistanceFile* freerdp_assistance_file_new(void) { winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT); - return (rdpAssistanceFile*)calloc(1, sizeof(rdpAssistanceFile)); + 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: + freerdp_assistance_file_free(file); + return NULL; } void freerdp_assistance_file_free(rdpAssistanceFile* file) { - UINT32 i; - if (!file) return; + update_password(file, NULL); + update_connectionstring2(file, NULL, 0); free(file->filename); - free(file->password); free(file->Username); free(file->LHTicket); free(file->RCTicket); free(file->PassStub); free(file->ConnectionString1); - free(file->ConnectionString2); free(file->EncryptedLHTicket); free(file->RASessionId); free(file->RASpecificParams); + free(file->RASpecificParams2); free(file->EncryptedPassStub); - for (i = 0; i < file->MachineCount; i++) - { - free(file->MachineAddresses[i]); - } - - free(file->MachineAddresses); - free(file->MachinePorts); + 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) { - size_t x; + 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); @@ -1322,11 +1383,21 @@ void freerdp_assistance_print_file(rdpAssistanceFile* file, wLog* log, DWORD lev 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 (x = 0; x < file->MachineCount; x++) + for (size_t x = 0; x < ArrayList_Count(file->MachineAddresses); x++) { - WLog_Print(log, level, "MachineAddress [%" PRIdz ": %s", x, file->MachineAddresses[x]); - WLog_Print(log, level, "MachinePort [%" PRIdz ": %" PRIu32, x, file->MachinePorts[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); } } @@ -1347,9 +1418,13 @@ int freerdp_assistance_set_connection_string2(rdpAssistanceFile* file, const cha if (!file || !string || !password) return -1; - free(file->ConnectionString2); - free(file->password); - file->ConnectionString2 = _strdup(string); - file->password = _strdup(password); + 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); } diff --git a/libfreerdp/common/test/TestCommonAssistance.c b/libfreerdp/common/test/TestCommonAssistance.c index 51d8390ce..aa5d87876 100644 --- a/libfreerdp/common/test/TestCommonAssistance.c +++ b/libfreerdp/common/test/TestCommonAssistance.c @@ -80,6 +80,18 @@ static const char TEST_MSRC_INCIDENT_FILE_TYPE2[] = * * */ +static const char connectionstr2[] = + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + ""; static BOOL test_msrsc_incident_file_type1(wLog* log) { @@ -137,6 +149,13 @@ static BOOL test_msrsc_incident_file_type2(wLog* log) if (!file) return -1; + status = freerdp_assistance_parse_file_buffer(file, connectionstr2, sizeof(connectionstr2), + TEST_MSRC_INCIDENT_PASSWORD_TYPE2); + printf("freerdp_assistance_parse_file_buffer: %d\n", status); + + if (status < 0) + goto fail; + status = freerdp_assistance_parse_file_buffer(file, TEST_MSRC_INCIDENT_FILE_TYPE2, sizeof(TEST_MSRC_INCIDENT_FILE_TYPE2), TEST_MSRC_INCIDENT_PASSWORD_TYPE2);