From 4090757035e37e9ee588c5a12925f32f8ff7b797 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Tue, 13 Jun 2023 16:49:15 +0200 Subject: [PATCH] Fix CVE-2023-27533 Merge: curl/curl@538b1e79a6e7b --- r5dev/thirdparty/curl/strcase.c | 82 ++++++++++++++++ r5dev/thirdparty/curl/strcase.h | 2 + r5dev/thirdparty/curl/telnet.c | 161 ++++++++++++++++++++------------ 3 files changed, 186 insertions(+), 59 deletions(-) diff --git a/r5dev/thirdparty/curl/strcase.c b/r5dev/thirdparty/curl/strcase.c index 6e581ce6..fb7a47e4 100644 --- a/r5dev/thirdparty/curl/strcase.c +++ b/r5dev/thirdparty/curl/strcase.c @@ -93,6 +93,73 @@ char Curl_raw_toupper(char in) return in; } +/* Portable, consistent tolower (remember EBCDIC). Do not use tolower() because + its behavior is altered by the current locale. */ +char Curl_raw_tolower(char in) +{ +#if !defined(CURL_DOES_CONVERSIONS) + if(in >= 'A' && in <= 'Z') + return (char)('a' + in - 'A'); +#else + switch(in) { + case 'A': + return 'a'; + case 'B': + return 'b'; + case 'C': + return 'c'; + case 'D': + return 'd'; + case 'E': + return 'e'; + case 'F': + return 'f'; + case 'G': + return 'g'; + case 'H': + return 'h'; + case 'I': + return 'i'; + case 'J': + return 'j'; + case 'K': + return 'k'; + case 'L': + return 'l'; + case 'M': + return 'm'; + case 'N': + return 'n'; + case 'O': + return 'o'; + case 'P': + return 'p'; + case 'Q': + return 'q'; + case 'R': + return 'r'; + case 'S': + return 's'; + case 'T': + return 't'; + case 'U': + return 'u'; + case 'V': + return 'v'; + case 'W': + return 'w'; + case 'X': + return 'x'; + case 'Y': + return 'y'; + case 'Z': + return 'z'; + } +#endif + + return in; +} + /* * Curl_raw_equal() is for doing "raw" case insensitive strings. This is meant * to be locale independent and only compare strings we know are safe for @@ -164,6 +231,21 @@ void Curl_strntoupper(char *dest, const char *src, size_t n) } while(*src++ && --n); } +/* Copy a lower case version of the string from src to dest. The + * strings may overlap. No more than n characters of the string are copied + * (including any NUL) and the destination string will NOT be + * NUL-terminated if that limit is reached. + */ +void Curl_strntolower(char *dest, const char *src, size_t n) +{ + if(n < 1) + return; + + do { + *dest++ = Curl_raw_tolower(*src); + } while(*src++ && --n); +} + /* Compare case-sensitive NUL-terminated strings, taking care of possible * null pointers. Return true if arguments match. */ diff --git a/r5dev/thirdparty/curl/strcase.h b/r5dev/thirdparty/curl/strcase.h index 7174aedb..dcbb6c7f 100644 --- a/r5dev/thirdparty/curl/strcase.h +++ b/r5dev/thirdparty/curl/strcase.h @@ -46,7 +46,9 @@ char Curl_raw_toupper(char in); #define checkprefix(a,b) curl_strnequal(a,b,strlen(a)) void Curl_strntoupper(char *dest, const char *src, size_t n); +void Curl_strntolower(char *dest, const char *src, size_t n); char Curl_raw_toupper(char in); +char Curl_raw_tolower(char in); bool Curl_safecmp(char *a, char *b); diff --git a/r5dev/thirdparty/curl/telnet.c b/r5dev/thirdparty/curl/telnet.c index 1d5c720a..992d26cb 100644 --- a/r5dev/thirdparty/curl/telnet.c +++ b/r5dev/thirdparty/curl/telnet.c @@ -815,22 +815,33 @@ static void printsub(struct Curl_easy *data, } } +static bool str_is_nonascii(const char *str) +{ + size_t len = strlen(str); + while(len--) { + if(*str & 0x80) + return TRUE; + str++; + } + return FALSE; +} + static CURLcode check_telnet_options(struct connectdata *conn) { struct curl_slist *head; struct curl_slist *beg; - char option_keyword[128] = ""; - char option_arg[256] = ""; struct Curl_easy *data = conn->data; struct TELNET *tn = (struct TELNET *)conn->data->req.protop; CURLcode result = CURLE_OK; - int binary_option; /* Add the user name as an environment variable if it was given on the command line */ if(conn->bits.user_passwd) { - snprintf(option_arg, sizeof(option_arg), "USER,%s", conn->user); - beg = curl_slist_append(tn->telnet_vars, option_arg); + char buffer[256]; + if(str_is_nonascii(conn->user)) + return CURLE_BAD_FUNCTION_ARGUMENT; + snprintf(buffer, sizeof(buffer), "USER,%s", conn->user); + beg = curl_slist_append(tn->telnet_vars, buffer); if(!beg) { curl_slist_free_all(tn->telnet_vars); tn->telnet_vars = NULL; @@ -840,68 +851,100 @@ static CURLcode check_telnet_options(struct connectdata *conn) tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES; } - for(head = data->set.telnet_options; head; head=head->next) { - if(sscanf(head->data, "%127[^= ]%*[ =]%255s", - option_keyword, option_arg) == 2) { - - /* Terminal type */ - if(strcasecompare(option_keyword, "TTYPE")) { - strncpy(tn->subopt_ttype, option_arg, 31); - tn->subopt_ttype[31] = 0; /* String termination */ - tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES; + for(head = data->set.telnet_options; head && !result; head = head->next) { + size_t olen; + char *option = head->data; + char *arg; + char *sep = strchr(option, '='); + if(sep) { + olen = sep - option; + arg = ++sep; + if(str_is_nonascii(arg)) continue; - } - - /* Display variable */ - if(strcasecompare(option_keyword, "XDISPLOC")) { - strncpy(tn->subopt_xdisploc, option_arg, 127); - tn->subopt_xdisploc[127] = 0; /* String termination */ - tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES; - continue; - } - - /* Environment variable */ - if(strcasecompare(option_keyword, "NEW_ENV")) { - beg = curl_slist_append(tn->telnet_vars, option_arg); - if(!beg) { - result = CURLE_OUT_OF_MEMORY; - break; + switch(olen) { + case 5: + /* Terminal type */ + if(strncasecompare(option, "TTYPE", 5)) { + strncpy(tn->subopt_ttype, arg, 31); + tn->subopt_ttype[31] = 0; /* String termination */ + tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES; } - tn->telnet_vars = beg; - tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES; - continue; - } + else + result = CURLE_UNKNOWN_OPTION; + break; - /* Window Size */ - if(strcasecompare(option_keyword, "WS")) { - if(sscanf(option_arg, "%hu%*[xX]%hu", - &tn->subopt_wsx, &tn->subopt_wsy) == 2) - tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES; - else { - failf(data, "Syntax error in telnet option: %s", head->data); - result = CURLE_TELNET_OPTION_SYNTAX; - break; + case 8: + /* Display variable */ + if(strncasecompare(option, "XDISPLOC", 8)) { + strncpy(tn->subopt_xdisploc, arg, 127); + tn->subopt_xdisploc[127] = 0; /* String termination */ + tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES; } - continue; - } + else + result = CURLE_UNKNOWN_OPTION; + break; - /* To take care or not of the 8th bit in data exchange */ - if(strcasecompare(option_keyword, "BINARY")) { - binary_option=atoi(option_arg); - if(binary_option!=1) { - tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO; - tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO; + case 7: + /* Environment variable */ + if(strncasecompare(option, "NEW_ENV", 7)) { + beg = curl_slist_append(tn->telnet_vars, arg); + if(!beg) { + result = CURLE_OUT_OF_MEMORY; + break; + } + tn->telnet_vars = beg; + tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES; } - continue; - } + else + result = CURLE_UNKNOWN_OPTION; + break; - failf(data, "Unknown telnet option %s", head->data); - result = CURLE_UNKNOWN_TELNET_OPTION; - break; + case 2: + /* Window Size */ + if(strncasecompare(option, "WS", 2)) { + char *p; + unsigned long x = strtoul(arg, &p, 10); + unsigned long y = 0; + if(x && (x <= 0xffff) && Curl_raw_tolower(*p) == 'x') { + p++; + y = strtoul(p, NULL, 10); + if(y && (y <= 0xffff)) { + tn->subopt_wsx = (unsigned short)x; + tn->subopt_wsy = (unsigned short)y; + tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES; + } + } + if(!y) { + failf(data, "Syntax error in telnet option: %s", head->data); + result = CURLE_TELNET_OPTION_SYNTAX; + } + } + else + result = CURLE_UNKNOWN_OPTION; + break; + + case 6: + /* To take care or not of the 8th bit in data exchange */ + if(strncasecompare(option, "BINARY", 6)) { + int binary_option = atoi(arg); + if(binary_option != 1) { + tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO; + tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO; + } + } + else + result = CURLE_UNKNOWN_OPTION; + break; + default: + failf(data, "Unknown telnet option %s", head->data); + result = CURLE_UNKNOWN_OPTION; + break; + } + } + else { + failf(data, "Syntax error in telnet option: %s", head->data); + result = CURLE_TELNET_OPTION_SYNTAX; } - failf(data, "Syntax error in telnet option: %s", head->data); - result = CURLE_TELNET_OPTION_SYNTAX; - break; } if(result) {