--- parser3/src/main/untaint.C 2004/07/01 07:20:10 1.133 +++ parser3/src/main/untaint.C 2008/07/16 17:06:44 1.141 @@ -1,11 +1,11 @@ /** @file Parser: String class part: untaint mechanizm. - Copyright(c) 2001-2004 ArtLebedev Group (http://www.artlebedev.com) + Copyright(c) 2001-2005 ArtLebedev Group (http://www.artlebedev.com) Author: Alexandr Petrosian (http://paf.design.ru) */ -static const char * const IDENT_UNTAINT_C="$Date: 2004/07/01 07:20:10 $"; +static const char * const IDENT_UNTAINT_C="$Date: 2008/07/16 17:06:44 $"; #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 \ @@ -92,6 +92,9 @@ inline bool need_http_header_encode(unsi return need_uri_encode(c); } +inline bool need_regex_escape(unsigned char c){ + return strchr("\\^$.[]|()?*+{}", c)!=0; +} // String @@ -292,14 +295,14 @@ struct Cstr_to_string_body_block_info { CORD_pos pos; size_t fragment_begin; bool whitespace; + 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; bool& whitespace=info->whitespace; size_t fragment_end=info->fragment_begin+fragment_length; - //fprintf(stderr, "%d, %d\n", fragment.lang, fragment.length); - + //fprintf(stderr, "%d, %d =%s=\n", fragment_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; @@ -321,14 +324,17 @@ 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_URI: // tainted, untaint language: uri @@ -349,7 +355,8 @@ int cstr_to_string_body_block(char alang break; case String::L_HTTP_HEADER: // tainted, untaint language: http-field-content-text - escape( + // the same as L_URI BUT not transcoded into $response:charset before encoding + escape_fragment( encode(need_uri_encode, '%', c); ); break; @@ -371,6 +378,8 @@ int cstr_to_string_body_block(char alang bool email=false; uchar c; for(const char* src=mail_ptr; (c=(uchar)*src++); ) { + if(c=='\r' || c=='\n') + c=' '; if(to_quoted_printable && (c==',' || c == '"' || addr_spec_soon(src-1/*position to 'c'*/))) { email=c=='<'; to_string("?="); @@ -407,16 +416,17 @@ int cstr_to_string_body_block(char alang pa_CORD_pos_advance(info->pos, fragment_length); to_string(info->connection->quote(fragment_str, fragment_length)); - } else - throw Exception(0, - 0, - "untaint in SQL language failed - no connection specified"); + } else { + info->exception="untaint in SQL language failed - no connection specified"; + info->fragment_begin=fragment_end; + return 1; // stop processing. can't throw exception here + } 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; default: _default; break; @@ -424,9 +434,10 @@ int cstr_to_string_body_block(char alang 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; @@ -455,7 +466,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; @@ -463,6 +474,28 @@ int cstr_to_string_body_block(char alang default: _default; break; }); break; + case String::L_REGEX: + // tainted, untaint language: regex + 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 @@ -478,6 +511,8 @@ 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 { + if(is_empty()) + return String::Body(); Cstr_to_string_body_block_info info; // input @@ -490,10 +525,15 @@ String::Body String::cstr_to_string_body // private body.set_pos(info.pos, 0); info.fragment_begin=0; + info.exception=0; info.whitespace=true; - if(!is_empty()) - langs.for_each(body, cstr_to_string_body_block, &info); + langs.for_each(body, cstr_to_string_body_block, &info); + if(info.exception){ + throw Exception(0, + 0, + info.exception); + } return String::Body(CORD_ec_to_cord(info.result)); }