From: Dmitry Shulga Date: February 16 2011 10:36am Subject: bzr commit into mysql-5.1-bugteam branch (Dmitry.Shulga:3537) Bug#11764168 List-Archive: http://lists.mysql.com/commits/131401 X-Bug: 11764168 Message-Id: <201102161036.p1GAaf7J032388@acsinet15.oracle.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============3103483441091005501==" --===============3103483441091005501== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///Users/shulga/projects/mysql/5.1-bugteam-bug56976/ based on revid:dao-gang.qu@stripped 3537 Dmitry Shulga 2011-02-16 Fixed Bug#11764168 "56976: SEVERE DENIAL OF SERVICE IN PREPARED STATEMENTS". The problem was that server didn't check resulting size of prepared statement argument which was set using mysql_send_long_data() API. By calling mysql_send_long_data() several times it was possible to create overly big string and thus force server to allocate memory for it. There was no way to limit this allocation. The solution is to add check for size of result string against value of max_long_data_size start-up parameter. When intermediate string exceeds max_long_data_size value an appropriate error message is emitted. We can't use existing max_allowed_packet parameter for this purpose since its value is limited by 1GB and therefore using it as a limit for data set through mysql_send_long_data() API would have been an incompatible change. Newly introduced max_long_data_size parameter gets value from max_allowed_packet parameter unless its value is specified explicitly. This new parameter is marked as deprecated and will be eventually replaced by max_allowed_packet parameter. @ mysql-test/t/variables.test Added checking for new start-up parameter max_long_data_size. @ sql/item.cc Added call to my_message() when accumulated string exceeds max_long_data_size value. my_message() calls error handler that was installed in mysql_stmt_get_longdata before call to Item_param::set_longdata. The error handler then sets state, last_error and last_errno fields for current statement to values which correspond to error which was caught. @ sql/mysql_priv.h Added max_long_data_size variable declaration. @ sql/mysqld.cc Added support for start-up parameter 'max_long_data_size'. This parameter limits size of data which can be sent from client to server using mysql_send_long_data() API. @ sql/set_var.cc Added variable 'max_long_data_size' into list of variables displayed by command 'show variables'. @ sql/sql_prepare.cc Added error handler class Set_longdata_error_handler. This handler is used to catch any errors that can be generated during execution of Item_param::set_longdata(). Sourcecode snippet that makes checking for statement's state during statement execution is moved from Prepared_statement::execute() to Prepared_statement::execute_loop() in order not to call set_parameters() when statement has failed during set_long_data() execution. If this hadn't been done the call to set_parameters() would have failed. @ tests/mysql_client_test.c A testcase for the bug #56976 was added. modified: mysql-test/r/variables.result mysql-test/t/variables.test sql/item.cc sql/mysql_priv.h sql/mysqld.cc sql/set_var.cc sql/sql_prepare.cc tests/mysql_client_test.c === modified file 'mysql-test/r/variables.result' --- a/mysql-test/r/variables.result 2010-11-25 03:11:05 +0000 +++ b/mysql-test/r/variables.result 2011-02-16 10:36:19 +0000 @@ -1540,6 +1540,9 @@ ERROR HY000: Cannot drop default keycach SET @@global.key_cache_block_size=0; Warnings: Warning 1292 Truncated incorrect key_cache_block_size value: '0' +select @@max_long_data_size; +@@max_long_data_size +1048576 SET @@global.max_binlog_cache_size=DEFAULT; SET @@global.max_join_size=DEFAULT; SET @@global.key_buffer_size=@kbs; === modified file 'mysql-test/t/variables.test' --- a/mysql-test/t/variables.test 2010-11-25 03:11:05 +0000 +++ b/mysql-test/t/variables.test 2011-02-16 10:36:19 +0000 @@ -1293,6 +1293,11 @@ SET @@global.max_join_size=0; SET @@global.key_buffer_size=0; SET @@global.key_cache_block_size=0; +# +# Bug#56976: added new start-up paramaeter +# +select @@max_long_data_size; + # cleanup SET @@global.max_binlog_cache_size=DEFAULT; SET @@global.max_join_size=DEFAULT; === modified file 'sql/item.cc' --- a/sql/item.cc 2010-12-21 11:34:11 +0000 +++ b/sql/item.cc 2011-02-16 10:36:19 +0000 @@ -2742,6 +2742,16 @@ bool Item_param::set_longdata(const char (here), and first have to concatenate all pieces together, write query to the binary log and only then perform conversion. */ + if (str_value.length() + length > max_long_data_size) + { + my_message(ER_UNKNOWN_ERROR, + "Parameter of prepared statement which is set through " + "mysql_send_long_data() is longer than " + "'max_long_data_size' bytes", + MYF(0)); + DBUG_RETURN(true); + } + if (str_value.append(str, length, &my_charset_bin)) DBUG_RETURN(TRUE); state= LONG_DATA_VALUE; === modified file 'sql/mysql_priv.h' --- a/sql/mysql_priv.h 2010-12-16 19:11:08 +0000 +++ b/sql/mysql_priv.h 2011-02-16 10:36:19 +0000 @@ -1969,6 +1969,7 @@ extern MYSQL_PLUGIN_IMPORT uint lower_ca extern bool opt_endinfo, using_udf_functions; extern my_bool locked_in_memory; extern bool opt_using_transactions; +extern ulong max_long_data_size; #endif /* MYSQL_SERVER */ #if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS extern MYSQL_PLUGIN_IMPORT bool mysqld_embedded; === modified file 'sql/mysqld.cc' --- a/sql/mysqld.cc 2010-12-28 23:47:05 +0000 +++ b/sql/mysqld.cc 2011-02-16 10:36:19 +0000 @@ -409,6 +409,7 @@ TYPELIB log_output_typelib= {array_eleme /* the default log output is log tables */ static bool lower_case_table_names_used= 0; +static bool max_long_data_size_used= false; static bool volatile select_thread_in_use, signal_thread_in_use; static bool volatile ready_to_exit; static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0; @@ -574,6 +575,11 @@ ulong delayed_insert_errors,flush_time; ulong specialflag=0; ulong binlog_cache_use= 0, binlog_cache_disk_use= 0; ulong max_connections, max_connect_errors; +/* + Maximum length of parameter value which can be set through + mysql_send_long_data() call. +*/ +ulong max_long_data_size; uint max_user_connections= 0; /** Limit of the total number of prepared statements in the server. @@ -5782,7 +5788,8 @@ enum options_mysqld OPT_SLOW_QUERY_LOG_FILE, OPT_IGNORE_BUILTIN_INNODB, OPT_BINLOG_DIRECT_NON_TRANS_UPDATE, - OPT_DEFAULT_CHARACTER_SET_OLD + OPT_DEFAULT_CHARACTER_SET_OLD, + OPT_MAX_LONG_DATA_SIZE }; @@ -6862,6 +6869,13 @@ thread is in the relay logs.", &global_system_variables.max_length_for_sort_data, &max_system_variables.max_length_for_sort_data, 0, GET_ULONG, REQUIRED_ARG, 1024, 4, 8192*1024L, 0, 1, 0}, + {"max_long_data_size", OPT_MAX_LONG_DATA_SIZE, + "The maximum size of prepared statement parameter which can be provided " + "through mysql_send_long_data() API call. " + "Deprecated option; use max_allowed_packet instead.", + &max_long_data_size, + &max_long_data_size, 0, GET_ULONG, + REQUIRED_ARG, 1024*1024L, 1024, UINT_MAX32, MALLOC_OVERHEAD, 1, 0}, {"max_prepared_stmt_count", OPT_MAX_PREPARED_STMT_COUNT, "Maximum number of prepared statements in the server.", &max_prepared_stmt_count, &max_prepared_stmt_count, @@ -8670,6 +8684,10 @@ mysqld_get_one_option(int optid, } break; #endif /* defined(ENABLED_DEBUG_SYNC) */ + case OPT_MAX_LONG_DATA_SIZE: + max_long_data_size_used= true; + WARN_DEPRECATED(NULL, VER_CELOSIA, "--max_long_data_size", "--max_allowed_packet"); + break; } return 0; } @@ -8831,6 +8849,14 @@ static int get_options(int *argc,char ** else pool_of_threads_scheduler(&thread_scheduler); /* purecov: tested */ #endif + + /* + If max_long_data_size is not specified explicitly use + value of max_allowed_packet. + */ + if (!max_long_data_size_used) + max_long_data_size= global_system_variables.max_allowed_packet; + return 0; } === modified file 'sql/set_var.cc' --- a/sql/set_var.cc 2010-12-28 23:47:05 +0000 +++ b/sql/set_var.cc 2011-02-16 10:36:19 +0000 @@ -394,6 +394,12 @@ static sys_var_thd_ulong sys_max_seeks_f &SV::max_seeks_for_key); static sys_var_thd_ulong sys_max_length_for_sort_data(&vars, "max_length_for_sort_data", &SV::max_length_for_sort_data); +static sys_var_const sys_max_long_data_size(&vars, + "max_long_data_size", + OPT_GLOBAL, SHOW_LONG, + (uchar*) + &max_long_data_size); + #ifndef TO_BE_DELETED /* Alias for max_join_size */ static sys_var_thd_ha_rows sys_sql_max_join_size(&vars, "sql_max_join_size", &SV::max_join_size, === modified file 'sql/sql_prepare.cc' --- a/sql/sql_prepare.cc 2010-12-28 23:47:05 +0000 +++ b/sql/sql_prepare.cc 2011-02-16 10:36:19 +0000 @@ -2730,6 +2730,32 @@ void mysql_sql_stmt_close(THD *thd) } } + +class Set_longdata_error_handler : public Internal_error_handler +{ +public: + Set_longdata_error_handler(Prepared_statement *statement) + :stmt(statement) + { } + +public: + bool handle_error(uint sql_errno, + const char *message, + MYSQL_ERROR::enum_warning_level level, + THD *thd) + { + stmt->state= Query_arena::ERROR; + stmt->last_errno= sql_errno; + strncpy(stmt->last_error, message, MYSQL_ERRMSG_SIZE); + + return TRUE; + } + +private: + Prepared_statement *stmt; +}; + + /** Handle long data in pieces from client. @@ -2786,16 +2812,19 @@ void mysql_stmt_get_longdata(THD *thd, c param= stmt->param_array[param_number]; + Set_longdata_error_handler err_handler(stmt); + /* + Install handler that will catch any errors that can be generated + during execution of Item_param::set_longdata() and propagate + them to Statement::last_error. + */ + thd->push_internal_handler(&err_handler); #ifndef EMBEDDED_LIBRARY - if (param->set_longdata(packet, (ulong) (packet_end - packet))) + param->set_longdata(packet, (ulong) (packet_end - packet)); #else - if (param->set_longdata(thd->extra_data, thd->extra_length)) + param->set_longdata(thd->extra_data, thd->extra_length); #endif - { - stmt->state= Query_arena::ERROR; - stmt->last_errno= ER_OUTOFMEMORY; - sprintf(stmt->last_error, ER(ER_OUTOFMEMORY), 0); - } + thd->pop_internal_handler(); general_log_print(thd, thd->command, NullS); @@ -3257,6 +3286,13 @@ Prepared_statement::execute_loop(String bool error; int reprepare_attempt= 0; + /* Check if we got an error when sending long data */ + if (state == Query_arena::ERROR) + { + my_message(last_errno, last_error, MYF(0)); + return TRUE; + } + if (set_parameters(expanded_query, packet, packet_end)) return TRUE; @@ -3497,12 +3533,6 @@ bool Prepared_statement::execute(String status_var_increment(thd->status_var.com_stmt_execute); - /* Check if we got an error when sending long data */ - if (state == Query_arena::ERROR) - { - my_message(last_errno, last_error, MYF(0)); - return TRUE; - } if (flags & (uint) IS_IN_USE) { my_error(ER_PS_NO_RECURSION, MYF(0)); === modified file 'tests/mysql_client_test.c' --- a/tests/mysql_client_test.c 2010-12-28 23:47:05 +0000 +++ b/tests/mysql_client_test.c 2011-02-16 10:36:19 +0000 @@ -18399,6 +18399,54 @@ static void test_bug47485() /* + Bug #56976: Severe Denial Of Service in prepared statements +*/ + +static void test_bug56976() +{ + MYSQL_STMT *stmt; + MYSQL_BIND bind[1]; + int rc; + const char* query = "SELECT LENGTH(?)"; + char *long_buffer; + unsigned long i, packet_len = 256 * 1024L; + unsigned long dos_len = 2 * 1024 * 1024L; + + DBUG_ENTER("test_bug56976"); + myheader("test_bug56976"); + + stmt= mysql_stmt_init(mysql); + check_stmt(stmt); + + rc= mysql_stmt_prepare(stmt, query, strlen(query)); + check_execute(stmt, rc); + + memset(bind, 0, sizeof(bind)); + bind[0].buffer_type = MYSQL_TYPE_TINY_BLOB; + + rc= mysql_stmt_bind_param(stmt, bind); + check_execute(stmt, rc); + + long_buffer= malloc(packet_len); + memset(long_buffer, 'a', packet_len); + + for (i= 0; i < dos_len / packet_len; i++) + { + rc= mysql_stmt_send_long_data(stmt, 0, long_buffer, packet_len); + check_execute(stmt, rc); + } + + rc= mysql_stmt_execute(stmt); + + DIE_UNLESS(rc && mysql_stmt_errno(stmt) == ER_UNKNOWN_ERROR); + + mysql_stmt_close(stmt); + + DBUG_VOID_RETURN; +} + + +/* Read and parse arguments and MySQL options from my.cnf */ @@ -18724,6 +18772,7 @@ static struct my_tests_st my_tests[]= { { "test_bug42373", test_bug42373 }, { "test_bug54041", test_bug54041 }, { "test_bug47485", test_bug47485 }, + { "test_bug56976", test_bug56976 }, { 0, 0 } }; --===============3103483441091005501== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/dmitry.shulga@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: dmitry.shulga@stripped\ # dw6fyg5fiw5d841m # target_branch: file:///Users/shulga/projects/mysql/5.1-bugteam-\ # bug56976/ # testament_sha1: 206fd9a46eb4523513338b86a8b21c3b642a92e9 # timestamp: 2011-02-16 16:36:31 +0600 # base_revision_id: dao-gang.qu@stripped\ # 32f4p072xij1zkmj # # Begin bundle IyBCYXphYXIgcmV2aXNpb24gYnVuZGxlIHY0CiMKQlpoOTFBWSZTWch4uTcACb//gECYQAB5//// /+//+r////pgFU5m1c10u+7vednz3vunU21sGiS6wvX2+nl9vu5r3u1dXdofbHmWlMbSSsqlprYt kp1us0gDTSTICZTaMmTRojSabSNNBmp5R6jTRpoaAeiDT1BKIAJoBMkZFPU2hMgAA0AYgAA0Gg1P QRoknpqNE09GkPUegjIABk00NAAAABIiIQyaE0mE8TQE1PUyaZJ6nlPapjRGRhiIwTagikmgRppP TSbU1PQlP8mKbKU/UTNEekGg/VNAGgAASKCaNAg0BJ6aCJ6m0RpPJqMhoAA0AADQAwjpIRFTJtJB S0gBSGUIKq68mS3HkzRKtmO40gxndVFHNIs9/3f69x/H44e2Py0a+I7ezTrGsJ7vgqa3D3HVxNot ZDbW0ts5d0qcl+N19zsfxwi7E3TIzkjmGCvcYg1ZtDX+fS/rSj+DSiBLJlOXc2bO986govFUEM1R E61RDDth2XrfdgxMDzZ6ySMcZOMHuvcb1kK2A3lMK5GMkhuquMxVXNna/BguSRNnL9S2UMbYK0RE cUCALuBh6mhXMbAPtqEc3PKJGhoTY2hDKIA/ltn3Rif5TYahsUOu3oexa6WB2PLZFsMIqU1xMAk4 YXYBoA6QG22NjbbaTbaFnVdcdolq59AW+rzbMgIpqzEEJRN0q2wVDnWIT+3aUyjyGvVAKlEbcYmg dA1rTucKgeVKxU0DVnDMSYIqakOQkwGRpXA2qWfDUbaAwCqvgwMHGYKq3stlo4d9whJgNOVA1t70 Nv2qF8nTruX8EwTlrlGAgGatVd5UbDLtbHJGYjA5s2UJu6sccjphNPhya1cPHNlS9H0lfm50CisR js7NWraZ1rQuo/HcHcpMTuCUGIi9ZiiZSAL1in4qk7pvOdZipnhAREnMdgWulcwxWtE9LJfMUxOu oi4rIXrwPl6suO0pspMnjDLVlo3ZbFFgz37RMJ3DrS54P0JjFxj+8a2vMBkxvgdTBZWS+Cys0+jF ejA/ax5P5vmeFkZwJSWFMX4TrgDXC3pVBW7B7nXm4z9EeHZuzuKnDSbaBFskGh1FdbHOicumABFh BSdQgo+RUkuY4Q7twg1VywhQg/snlBzGo1mtWW0z97nSCsynLaqrvK03LZe1+6iDzy9DANGjTo0s Y/Xr5tela55xhAYYy1HEabABA9Lq/fv2Hs1YvZt8rJ5D3daRD3aZysokvPzQUaQcxUg+heOR+eK3 QRgaXop42anvT6/c7TXvyil+ZkZiLP7H3V4W379IOghdGPZV+CgSsbLAdejcWvRo2+SuvRn4NgZi pEk6lzh0zYvY4X6BcZbd37YlegVxkPK82SPUBHQirmYh4VCCnjX2ieOO5jzfJtJczeZq3KXGnkfg 6MXNDDG2gxvTQP3+dXAT8c3HKR7XvGtIQ5Kv0O1Pg5FRBSSy9lEujUCejra8HhLZW/WQNx8QwzvS l2VJJwlqamwzhUkjdyd/jvjSca/E5W9KuhnHB7ZWtpGk4Rm8PMoN41gxFSVKiZml5Jck0Iu/lEcv jTVAQxeRG7uY2gtnQC6jWADvNnb4aXiQ2hsBsSb0al7SDxY+qSeCFSSQseEFCFCliEAkBJIA6onO SCQhNXkItBAsvURI4u6bCBUdCgupCKivgoRGfWSxjdUlcgxlaEmoDRYpZf6qY3kC4iP4HfLGUYVY qCXqha50IDyCjXvS/B7fEzuUBH5ZgiS6NSQbjAEdLFirtdsuRjkeuWvY28gQYeTdbOJxdM+uSPr4 azyRUE3KlcDg/DjO6lqeA0q3WyJhsTSQg455w5HIpZLknmhmNSJvu1iCCmmd1lJndhdunWStCZDZ yxBLWOqFLdRPQsHYBT21189wHMiezDedLEkcCQI/EqRwcrxyk4ZSjxsqOFTNBEN3oGd/jnOAYRBC rzqVgBWNBiMC6Ky7aFhJc7KTKWhLlG12rWW9JGAwKZogMGdpdsNtLFBojc7pKVE4NRUd3Eydm7WZ HbsMDQ/9ejzS9IAeBOSsqbbqUV+reHcIoGJuMKpCaZcx0LHLzSwSnYk6G9DBSwX5mDir6p1tUYOS TAQYmJpgPEiq7qZnOKjJQvCyFcRxwMwkHdSRZDVDi2/Cui34Pcl6WIjhYjgl1sEcmawnHGYIRqMH bZDSRQbpQfOfEU3tIwcwxqCj9B7H8MUgY8q0tkte78jGzNBKqrWp53FTQwLiu41Gzisv+o7E1JdV ju2TaXGvQ2Lcc2TynNkXvEicH5FB2duY150nFyI+pBLLRM2ZoZORRZzkQeXZnnTotjVCx4esRb16 bgikszNlBXLluRydBN9yU9UDYU1dMoeTYqVGJqnA8oIZRGo2kN4Ioq0LgxXNal5r5Vpfs23XboSl NNhotG9dxsIoRBTamCI67Kl24uLyKanKqtqO2TtZE71SaiOJFw0FGRfUcSEKhcytBDCnQRIxHjCQ z0TI2BBgcEOkwyDRlcmMthe1NUsgUkFImREhC5Ra5k8TzRJVuLCOjiNjkYxwLzAZN5uVDC4zpSmF 5FHQh0t7r6S3ZkuJktyj3pZeenTEnqrARjUopkTiZnEhryHtkVIHBVOa70zTtRzpI9Gg5OScuOeZ kMthjnFm8dUGampxFJOV4DybCo8gCHIYkbi6VxK8pEnPRYRfSQ0uKEAUoXttQ4XmBdYoRPXCxzqz NCS0mOL2JZEQUSGMrmrBGkGMLWOoiXYnQ8tUdeGpF4wlgWRmZoXIKlxBmoFqFBhbhS5TaZIpEwvP G3SrwElY5KMIGcDTPhEaX70t5BWNVqeJHqZ70ZwNziNUc+qoaMmnKxKVHSgU5RsAmgdrERShZGFi kgnHlfFBE1pX3UlIJFycWTv4HwESzkMQCO0iGWrspQ88yy6FEWJcRMXPDXaLR5kAOepekmHgNzcE ZE7WNS0ggoW4CcihJAENpj+UCXD6EPqwmfyfuvn5onzB1eLXlxrvbJAOnt293uTt/PXCAqShOrEB gQlAQCQf2IxEl2ZGrdaF+L7uKdo8Ln0dnKklsK7s2zbXSqdwlvNmNBv/aFz3Y7xar/+H3qiza7Ht pIh0Lz16POb26Sfhti4Ni5cdVk68FhnfpX0K0WQcnqoTOfiXZhXSEJxrtPN6pJnnQheAenm7Gp3F K2gzB8oYk2hQ9YXb4glQ4JWIGdhRyE01m+ubd4AidCOnGVyorA6osjZGmK3lK5+AmESJsKgtLpSX sW5oXoSc1UT9O8yPZSyGjefTSEiFR220k1Iw1Mo29h0Ueltc2tCulLp9ZK+dobS5orwB4xpzZr+H ciqQxK1QWZfwioLHQwTMIgoL7QxvWidY0fcDR/sffKDztIUqjbPI1FAj2J2Kgwql66AQA/b7pSPU URky+FEtYJCgRZZgFQO9VWAhzvilFBUBMBqwMpIJCyqnBFtxDB9Wfbu/7KSic4iGB+ASj2493pwx Kn3kDPUEmTjNCEatIRCj6/XefYjQ+fbl3gFqphh+0qZH2OX8Xd4ewjWiIlUQ8rzW6eUOCbt0NlBV hY9tEb47vIIVS5QwAYlW7a7kNyviHvmks04nJaoRNGYi0QvvNdsC4kKHdZUjo+2WAToIzSTAzIVz GLxBSM3vuHpC97Zt1ikskquClPhDriUEB9L8l1WvC8+PDyN/2nE40Um8sIP54FPuCktNLCYlpm5m 8pkeBzcmtXHvN6C0zl5Psbff09GLeDEu1ditPpDFdQbM+bOFVck8iQY+atcIlYdgWdeytmhNhyPE 7zYSbRnGQeCCwoXmVHh3LSkuxmN44uMDEEYc7vTtYV/IzDsJzImWJVEqt4UUNlTVMx4t6jFCCSZs T32SSRDjxTJ6gAs5Yz/HIi7DasVfpOYjOwtGLgaXUMlibRSFA4untomHSBJldEV4HPa+l+FTp4D9 JL274JaxGYs44iPE9R1MgRbmazIvKoCYiCTYZ7TJb/YzmEGDG20NIW0roaUt+INj7dC/PmBDwsOS NcvUg28rFuikv3GpAwNU8bqDKFexz3rWWeDx8lVeIIu9Xo8+GEpHHjQ6qcx9zq6GPDASl0UcAJDY bai9HqrAyqVoJnNNL2nbCVuIZIq1QrWBzmilKookiWBqK/k+xfzPkI+RIyPGRTP2Po4Oczu8H2LB S8SwxMzI4HwWvyLuHA4vg4tB9AGAampMhxALUiViyPMjuOcHkaEwidSxB2bcD2CKlJx0l9NJlJQ8 jvnPxITLKqJZREqqJXVBScFrDLYyd3KsnGPCPIxTupKLcNL4Ypfd74u3P/KMtZ1fFy23gPTQ0M7S AkmB6ZkLSYd9wUZZfAo4ghQBKNSmREdmmUltyfGtJeNlzgbKvCSktlJR1DknJFFXjHpOvSTYjX+2 tKSe2SEiZxrf02ppy2UVzE7colSrNAiAoSoLrggxiHo0VBpcrRKEqFxhKkkjYvyTOWoIkt6BhuLt GD6HrB6wMRRHNU6nM5kHMy8kpZsqja8zq2yKn3BrYOJnPky9NBpDkLqQ4JwTmonZ9oFYANLMDeCO iuxCqaO4sLWBkMuhjwa3J8HIE9Hvc+ghEwkSCx3UDUCTQTkKd3RigTcyWUJxgXm6tdPGVWlruTKz 7/TUeCcVdHpMmISsPMi5ajkymfqSgiCcZGphLcuGWIMaM4RtpioiJExpewLg49tn1tLNb5/Q4eSL 5SglPxLdXMctQnUtXySCHSi5ew36N4JcMTaiRnp+ic/v9ELq9rzu1Ugma6pWRLb/F7+7olFoC7GX VXDdgFKGB0wPa2ABxLy1zBSZg3NmzM0KQyx2pSJ5VgVhPbrywXi4j4P1+GdQwabaCtnGbIA2xquB vSM89vy0bq8I+ei+rkSSRYM+KxFzBH8NLiAmmZ1d8rW1odPjoTi98gpb7E2hTSr6Plt2vipSTdMn re7ml16y6DyiqVFBhOBkQsgvalF6njMX10C1NAn82S5+HwgPTSxlqI19QdiCqSGBi0lIeTnvXFhi e2FolNI8IzxEiQsjAZhnjBuITjAN7FsedbNraXGX+I0CpEZ6xMDvhfcGhGERGmPPu5V9d0iSas95 Qq3sIoL5JJNSiQN5KpQKzpOd4Jh2PZ74DjfbV/UuOnqTtY+j4TDwAv1X0NAGhpbpvDbHKBTKJPmT XrOkEQu3+5ehHI+KJ4XywQbALLDokL17MSpuH6a8Ts+d9FFiJaBypZ5dOdvM9AyRNvSUYyFSvCWU CwM0np4n2BbVVPs+oMQvqIZNagKYuMJMJuQBIDcJLAp1joWqxEjE2mNDsTARDEQ6Iuu8lmSbmhmR EIkpcvXCyJNoScNQmNCas1Kq+51MT4qfb09zEmE1tl3IrKZYtvs2sXySOGxTGt+V+XIzWNqgbFDX 4cb0FpcxKZIK7qBBY+5aRY02xNI8EkZpEZtFC+wtIhuoryWD56FMQuDuPu92Gc43pdQu96pvSSiF vmh+UBwm5mBpgmcol4pAaAtxBoOjIXcFUaV5lzV9Pgdq1kbV8Km7V1od+tHAxNBUDZBHzxN2EK96 pFFRF/Jpfwu537XxTW+54a3qfTsm532CD3vl2PQMkWpkBpPQuHpexa00eDaRDY00xsYyIUDbYNto bgcD2xClD6RnQDJoHGOc6gQEUQisEX5sy+DXy3b3cx3CybdRIHEqwZ2kFBPUM9E5YsXHDfQhaVix br04hD0JjVPkV6mOCLUQiNTszFrRdI81hFTEHUIV8lUhfABMZmhhEIykIcEDhCHBDz+LnLCHRx4Y rdQmtkqrKMbHb6XgTQlWqiiAlojUEk5jbV9fVektVPEzuxg3DVAOJoBxOQ0sSUTyHCXv9y98TyVa JTZUDs5efeCN3cZVXNIuMQaAH7ewO1ijV6gNT0RERERERERERDcTmy8mzgQ1YhlhbRXUSFyKAHLc tgXySU0mRHVHk+O0ziL6ZyIyVlHhXVS1I9LdSsgJpE4qrsuS+hLvJU7lZG7msGXyw3RkV9pxMI3S Ixu6BStFGdoMmYQs3YJOjsRPvIjiwbFMmoK+dQspwkFBccV6faqWK29Yu5DmIYmUAciBy7ACKIoi ogUgL5WiSHBIapUcplsO+cNCfO0TVtRpzWw0HjwDFOfj6E+IdabJAKeRa9QYpVRvfR0QE80JYM+Z Fu9h1lkcM+YWN97+a70HVWpCrmNDfNh715KaRJWbl8ql2jFM33XiRF9oyZpIRHYnmo2HSwNvVdFa kNrBzZ0uovyW2CPhW/uJ9ye+PVgdQbpZqjfSesSPdx7ackUOXo6SS+JauIRDPHeGxcTjXXUOxSgp JbWuxZozWi7EfHO4PTwgYtiYP07hte3667yu93m1jewT7dU6JqbGrTtbX5qmu4E0lIJ2uq0DMO5l YsCUQWyJFw1vvZO16vMxvlrEDc2txNgTDtbXodofBzFcMHtZMgSx83j7Ds5UbCZOiLI1zZRbCJ7n QzZPYzOQWVOIJRkWPZytNxUgEX6Wozml7W0KLnFUIYNjkX/F0tmva5mxvya3E7nF3B5EMQP55f/F 3JFOFCQyHi5NwA== --===============3103483441091005501==--