--- parser3/src/main/untaint.C 2008/02/14 18:35:01 1.139 +++ parser3/src/main/untaint.C 2009/07/06 08:45:35 1.148 @@ -5,7 +5,7 @@ Author: Alexandr Petrosian (http://paf.design.ru) */ -static const char * const IDENT_UNTAINT_C="$Date: 2008/02/14 18:35:01 $"; +static const char * const IDENT_UNTAINT_C="$Date: 2009/07/06 08:45:35 $"; #include "pa_string.h" @@ -53,7 +53,7 @@ extern "C" { // author forgot to do that } -#define escape(action) \ +#define escape_fragment(action) \ for(; fragment_length--; CORD_next(info->pos)) { \ char c=CORD_pos_fetch(info->pos); \ action \ @@ -74,26 +74,29 @@ inline bool need_file_encode(unsigned ch // russian letters and space ENABLED // encoding only these... return strchr( - "*?'\"<>|" + "*?'\"<>|" #ifndef WIN32 - ":\\" + ":\\" #endif - , c)!=0; + , c)!=0; } + inline bool need_uri_encode(unsigned char c){ - if((c>='0') &&(c<='9') ||(c>='A') &&(c<='Z') ||(c>='a') &&(c<='z')) + if((c>='0') &&(c<='9') ||(c>='A') &&(c<='Z') ||(c>='a') &&(c<='z')) return false; - return !strchr("_-./", c); + return !strchr("_-./", c); } + inline bool need_http_header_encode(unsigned char c){ - if(strchr(" , :", c)) + if(strchr(" , :", c)) return false; return need_uri_encode(c); } + inline bool need_regex_escape(unsigned char c){ - return strchr("\\^$.[]|()?*+{}", c)!=0; + return strchr("\\^$.[]|()?*+{}-", c)!=0; } // String @@ -182,21 +185,37 @@ int append_fragment_nonoptimizing(char a or marking ALL pieces of it with a @a lang when @a forced to, and propagating OPTIMIZE language bit. */ -String& String::append_to(String& dest, Language lang, bool forced) const { +String& String::append_to(String& dest, Language ilang, bool forced) const { if(is_empty()) return dest; // first: fragment infos - if(lang==L_PASS_APPENDED) // without language-change? - dest.langs.append(dest.body, body.length(), langs); + if(ilang==L_PASS_APPENDED) // without language-change? + dest.langs.appendHelper(dest.body, langs, body); else if(forced) //forcing passed lang? - dest.langs.append(dest.body, lang, length()); - else { - Append_fragment_info info={lang, &dest.langs, dest.body.length()}; - langs.for_each(body, lang&L_OPTIMIZE_BIT? - append_fragment_optimizing - :append_fragment_nonoptimizing, &info); + dest.langs.appendHelper(dest.body, ilang, body); + else { + if(langs.opt.is_not_just_lang){ + Append_fragment_info info={ilang, &dest.langs, dest.body.length()}; + langs.for_each(body, ilang&L_OPTIMIZE_BIT? + append_fragment_optimizing + :append_fragment_nonoptimizing, &info); + } else { + Language lang=langs.opt.lang; + // see append_fragment_* for explanation + if(ilang&L_OPTIMIZE_BIT){ + dest.langs.appendHelper(dest.body, + lang==String::L_TAINTED? + ilang + :lang==String::L_CLEAN? + (String::Language)(String::L_CLEAN|String::L_OPTIMIZE_BIT) + :lang, + body); + } else { + dest.langs.appendHelper(dest.body, lang==String::L_TAINTED ? ilang:lang, body); + } + } } // next: letters @@ -298,14 +317,11 @@ struct Cstr_to_string_body_block_info { const char* exception; }; #endif -int cstr_to_string_body_block(char alang, size_t fragment_length, Cstr_to_string_body_block_info* info) { - const String::Language fragment_lang=(String::Language)(unsigned char)alang; +int cstr_to_string_body_block(String::Language to_lang, size_t fragment_length, Cstr_to_string_body_block_info* info) { bool& whitespace=info->whitespace; size_t fragment_end=info->fragment_begin+fragment_length; - //fprintf(stderr, "%d, %d =%s=\n", fragment_lang, fragment_length, info->body->cstr()); - + //fprintf(stderr, "%d, %d =%s=\n", to_lang, fragment_length, info->body->cstr()); - String::Language to_lang=info->lang==String::L_UNSPECIFIED?fragment_lang:info->lang; bool optimize=(to_lang & String::L_OPTIMIZE_BIT)!=0; if(!optimize) whitespace=false; @@ -325,14 +341,26 @@ int cstr_to_string_body_block(char alang break; case String::L_FILE_SPEC: // tainted, untaint language: file [name] - escape( - // Macintosh has problems with small Russian letter 'r' - if( c=='\xF0' && info->charsets && info->charsets->source().NAME()=="WINDOWS-1251" ) { - // fixing that letter for most common charset - to_char('p'); - } else // fallback to default - encode(need_file_encode, '_', c); - ); + { + bool is1251=(info->charsets && info->charsets->source().NAME()=="WINDOWS-1251"); + escape_fragment( + // Macintosh has problems with small Russian letter 'r' + if( is1251 && c=='\xF0' ) { + // fixing that letter for most common charset + to_char('p'); + } else // fallback to default + encode(need_file_encode, '_', c); + ); + } + break; + case String::L_FILE_POST: + { + escape_fragment(switch(c) { + case '\0': to_string("\\0"); break; + case '\\': to_string("\\\\"); break; + default: _default; break; + }); + } break; case String::L_URI: // tainted, untaint language: uri @@ -354,7 +382,7 @@ int cstr_to_string_body_block(char alang case String::L_HTTP_HEADER: // tainted, untaint language: http-field-content-text // the same as L_URI BUT not transcoded into $response:charset before encoding - escape( + escape_fragment( encode(need_uri_encode, '%', c); ); break; @@ -421,20 +449,22 @@ int cstr_to_string_body_block(char alang } break; case String::L_JS: - escape(switch(c) { + escape_fragment(switch(c) { + case '\n': to_string("\\n"); break; case '"': to_string("\\\""); break; case '\'': to_string("\\'"); break; - case '\n': to_string("\\n"); break; case '\\': to_string("\\\\"); break; case '\xFF': to_string("\\\xFF"); break; + case '\r': to_string("\\r"); break; default: _default; break; }); break; case String::L_XML: // [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] - escape(switch(c) { - case '\x9': - case '\xA': + escape_fragment(switch(c) { + case '\x20': + case '\x9': + case '\xA': case '\xD': // this is usually removed on input _default; break; @@ -463,7 +493,7 @@ int cstr_to_string_body_block(char alang }); break; case String::L_HTML: - escape(switch(c) { + escape_fragment(switch(c) { case '&': to_string("&"); break; case '>': to_string(">"); break; case '<': to_string("<"); break; @@ -473,12 +503,26 @@ int cstr_to_string_body_block(char alang break; case String::L_REGEX: // tainted, untaint language: regex - escape( + escape_fragment( if(need_regex_escape(c)) to_char('\\') _default; ); break; + case String::L_HTTP_COOKIE: + // tainted, untaint language: cookie (3.3.0 and higher: %uXXXX in UTF-8) + { + const char *fragment_str=info->body->mid(info->fragment_begin, fragment_length).cstr(); + // skip source [we use recoded version] + pa_CORD_pos_advance(info->pos, fragment_length); + String::C output(fragment_str, fragment_length); + + output=Charset::escape(output, info->charsets->source()); + //throw Exception(0, 0, output); + to_string(output); + + } + break; default: SAPI::abort("unknown untaint language #%d", static_cast(to_lang)); // should never @@ -491,9 +535,11 @@ int cstr_to_string_body_block(char alang } -String::Body String::cstr_to_string_body(Language lang, - SQL_Connection* connection, - const Request_charsets *charsets) const { +int cstr_to_string_body_block_default(char alang, size_t fragment_length, Cstr_to_string_body_block_info* info){ + return cstr_to_string_body_block(info->lang==String::L_UNSPECIFIED ? (String::Language)(unsigned char)alang : info->lang, fragment_length, info); +} + +String::Body String::cstr_to_string_body(Language lang, SQL_Connection* connection, const Request_charsets *charsets) const { if(is_empty()) return String::Body(); @@ -511,12 +557,81 @@ String::Body String::cstr_to_string_body info.exception=0; info.whitespace=true; - langs.for_each(body, cstr_to_string_body_block, &info); - if(info.exception){ + langs.for_each(body, cstr_to_string_body_block_default, &info); + if(info.exception) + throw Exception(0, + 0, + info.exception); + + return String::Body(CORD_ec_to_cord(info.result)); +} + +String::Body String::cstr_to_string_body_taint(Language lang, SQL_Connection* connection, const Request_charsets *charsets) const { + if(is_empty()) + return String::Body(); + + Cstr_to_string_body_block_info info; + // input + info.lang=lang; + info.connection=connection; + info.charsets=charsets; + info.body=&body; + // output + CORD_ec_init(info.result); + // private + body.set_pos(info.pos, 0); + info.fragment_begin=0; + info.exception=0; + info.whitespace=true; + + cstr_to_string_body_block(lang, length(), &info); + + if(info.exception) + throw Exception(0, + 0, + info.exception); + + return String::Body(CORD_ec_to_cord(info.result)); +} + +int cstr_to_string_body_block_untaint(char alang, size_t fragment_length, Cstr_to_string_body_block_info* info){ + const String::Language lang=(String::Language)(unsigned char)alang; + // see append_fragment_* for explanation + if(info->lang&String::L_OPTIMIZE_BIT) + return cstr_to_string_body_block( + lang==String::L_TAINTED? + info->lang + :lang==String::L_CLEAN? + (String::Language)(String::L_CLEAN|String::L_OPTIMIZE_BIT) + :lang, + fragment_length, info); + else + return cstr_to_string_body_block(lang==String::L_TAINTED ? info->lang:lang, fragment_length, info); +} + +String::Body String::cstr_to_string_body_untaint(Language lang, SQL_Connection* connection, const Request_charsets *charsets) const { + if(is_empty()) + return String::Body(); + + Cstr_to_string_body_block_info info; + // input + info.lang=lang; + info.connection=connection; + info.charsets=charsets; + info.body=&body; + // output + CORD_ec_init(info.result); + // private + body.set_pos(info.pos, 0); + info.fragment_begin=0; + info.exception=0; + info.whitespace=true; + + langs.for_each(body, cstr_to_string_body_block_untaint, &info); + if(info.exception) throw Exception(0, 0, info.exception); - } return String::Body(CORD_ec_to_cord(info.result)); }