tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

handler_misbehaving.h (11892B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #ifndef CONTENT_ANALYSIS_DEMO_HANDLER_MISBEHAVING_H_
      6 #define CONTENT_ANALYSIS_DEMO_HANDLER_MISBEHAVING_H_
      7 
      8 #include <time.h>
      9 
     10 #include <algorithm>
     11 #include <chrono>
     12 #include <fstream>
     13 #include <map>
     14 #include <iostream>
     15 #include <utility>
     16 #include <vector>
     17 #include <regex>
     18 #include <windows.h>
     19 
     20 #include "content_analysis/sdk/analysis.pb.h"
     21 #include "content_analysis/sdk/analysis_agent.h"
     22 #include "agent/src/event_win.h"
     23 #include "handler.h"
     24 
     25 enum class Mode {
     26 // Have to use a "Mode_" prefix to avoid preprocessing problems in StringToMode
     27 #define AGENT_MODE(name) Mode_##name,
     28 #include "modes.h"
     29 #undef AGENT_MODE
     30 };
     31 
     32 extern std::map<std::string, Mode> sStringToMode;
     33 extern std::map<Mode, std::string> sModeToString;
     34 
     35 // Writes a string to the pipe. Returns ERROR_SUCCESS if successful, else
     36 // returns GetLastError() of the write.  This function does not return until
     37 // the entire message has been sent (or an error occurs).
     38 static DWORD WriteBigMessageToPipe(HANDLE pipe, const std::string& message) {
     39  std::cout << "[demo] WriteBigMessageToPipe top, message size is "
     40            << message.size() << std::endl;
     41  if (message.empty()) {
     42    return ERROR_SUCCESS;
     43  }
     44 
     45  OVERLAPPED overlapped;
     46  memset(&overlapped, 0, sizeof(overlapped));
     47  overlapped.hEvent = CreateEvent(/*securityAttr=*/nullptr,
     48                                  /*manualReset=*/TRUE,
     49                                  /*initialState=*/FALSE,
     50                                  /*name=*/nullptr);
     51  if (overlapped.hEvent == nullptr) {
     52    return GetLastError();
     53  }
     54 
     55  DWORD err = ERROR_SUCCESS;
     56  const char* cursor = message.data();
     57  for (DWORD size = message.length(); size > 0;) {
     58    std::cout << "[demo] WriteBigMessageToPipe top of loop, remaining size "
     59              << size << std::endl;
     60    if (WriteFile(pipe, cursor, size, /*written=*/nullptr, &overlapped)) {
     61      std::cout << "[demo] WriteBigMessageToPipe: success" << std::endl;
     62      err = ERROR_SUCCESS;
     63      break;
     64    }
     65 
     66    // If an I/O is not pending, return the error.
     67    err = GetLastError();
     68    if (err != ERROR_IO_PENDING) {
     69      std::cout
     70          << "[demo] WriteBigMessageToPipe: returning error from WriteFile "
     71          << err << std::endl;
     72      break;
     73    }
     74 
     75    DWORD written;
     76    if (!GetOverlappedResult(pipe, &overlapped, &written, /*wait=*/TRUE)) {
     77      err = GetLastError();
     78      std::cout << "[demo] WriteBigMessageToPipe: returning error from "
     79                   "GetOverlappedREsult "
     80                << err << std::endl;
     81      break;
     82    }
     83 
     84    // reset err for the next loop iteration
     85    err = ERROR_SUCCESS;
     86    std::cout << "[demo] WriteBigMessageToPipe: bottom of loop, wrote "
     87              << written << std::endl;
     88    cursor += written;
     89    size -= written;
     90  }
     91 
     92  CloseHandle(overlapped.hEvent);
     93  return err;
     94 }
     95 
     96 // An AgentEventHandler that does various misbehaving things
     97 class MisbehavingHandler final : public Handler {
     98 public:
     99  using Event = content_analysis::sdk::ContentAnalysisEvent;
    100 
    101  static
    102  std::unique_ptr<AgentEventHandler> Create(
    103                                  const std::string& modeStr,
    104                                  std::vector<unsigned long>&& delays,
    105                                  const std::string& print_data_file_path,
    106                                  RegexArray&& toBlock = RegexArray(),
    107                                  RegexArray&& toWarn = RegexArray(),
    108                                  RegexArray&& toReport = RegexArray()) {
    109    auto it = sStringToMode.find(modeStr);
    110    if (it == sStringToMode.end()) {
    111      std::cout << "\"" << modeStr << "\""
    112                << " is not a valid mode!" << std::endl;
    113      return nullptr;
    114    }
    115 
    116    return std::unique_ptr<AgentEventHandler>(new MisbehavingHandler(it->second, std::move(delays), print_data_file_path, std::move(toBlock), std::move(toWarn), std::move(toReport)));
    117  }
    118 
    119 private:
    120  MisbehavingHandler(Mode mode, std::vector<unsigned long>&& delays, const std::string& print_data_file_path,
    121          RegexArray&& toBlock = RegexArray(),
    122          RegexArray&& toWarn = RegexArray(),
    123          RegexArray&& toReport = RegexArray()) :
    124           Handler(std::move(delays), print_data_file_path, std::move(toBlock), std::move(toWarn), std::move(toReport)),
    125           mode_(mode) {}
    126 
    127 
    128  template <size_t N>
    129  DWORD SendBytesOverPipe(const unsigned char (&bytes)[N],
    130                          const std::unique_ptr<Event>& event) {
    131    content_analysis::sdk::ContentAnalysisEventWin* eventWin =
    132        static_cast<content_analysis::sdk::ContentAnalysisEventWin*>(
    133            event.get());
    134    HANDLE pipe = eventWin->Pipe();
    135    std::string s(reinterpret_cast<const char*>(bytes), N);
    136    return WriteBigMessageToPipe(pipe, s);
    137  }
    138 
    139  bool SetCustomResponse(AtomicCout& aout, std::unique_ptr<Event>& event) override {
    140    std::cout << std::endl << "----------" << std::endl << std::endl;
    141    std::cout << "Mode is " << sModeToString[mode_] << std::endl;
    142 
    143    bool handled = true;
    144    if (mode_ == Mode::Mode_largeResponse) {
    145      for (size_t i = 0; i < 1000; ++i) {
    146        content_analysis::sdk::ContentAnalysisResponse_Result* result =
    147            event->GetResponse().add_results();
    148        result->set_tag("someTag");
    149        content_analysis::sdk::ContentAnalysisResponse_Result_TriggeredRule*
    150            triggeredRule = result->add_triggered_rules();
    151        triggeredRule->set_rule_id("some_id");
    152        triggeredRule->set_rule_name("some_name");
    153      }
    154    } else if (mode_ ==
    155               Mode::Mode_invalidUtf8StringStartByteIsContinuationByte) {
    156      // protobuf docs say
    157      // "A string must always contain UTF-8 encoded text."
    158      // So let's try something invalid
    159      // Anything with bits 10xxxxxx is only a continuation code point
    160      event->GetResponse().set_request_token("\x80\x41\x41\x41");
    161    } else if (mode_ ==
    162               Mode::Mode_invalidUtf8StringEndsInMiddleOfMultibyteSequence) {
    163      // f0 byte indicates there should be 3 bytes following it, but here
    164      // there are only 2
    165      event->GetResponse().set_request_token("\x41\xf0\x90\x8d");
    166    } else if (mode_ == Mode::Mode_invalidUtf8StringOverlongEncoding) {
    167      // codepoint U+20AC, should be encoded in 3 bytes (E2 82 AC)
    168      // instead of 4
    169      event->GetResponse().set_request_token("\xf0\x82\x82\xac");
    170    } else if (mode_ == Mode::Mode_invalidUtf8StringMultibyteSequenceTooShort) {
    171      // f0 byte indicates there should be 3 bytes following it, but here
    172      // there are only 2 (\x41 is not a continuation byte)
    173      event->GetResponse().set_request_token("\xf0\x90\x8d\x41");
    174    } else if (mode_ == Mode::Mode_invalidUtf8StringDecodesToInvalidCodePoint) {
    175      // decodes to U+1FFFFF, but only up to U+10FFFF is a valid code point
    176      event->GetResponse().set_request_token("\xf7\xbf\xbf\xbf");
    177    } else if (mode_ == Mode::Mode_stringWithEmbeddedNull) {
    178      event->GetResponse().set_request_token("\x41\x00\x41");
    179    } else if (mode_ == Mode::Mode_zeroResults) {
    180      event->GetResponse().clear_results();
    181    } else if (mode_ == Mode::Mode_resultWithInvalidStatus) {
    182      // This causes an assertion failure and the process exits
    183      // So we just serialize this ourselves in SendCustomResponse()
    184      /*content_analysis::sdk::ContentAnalysisResponse_Result* result =
    185          event->GetResponse().mutable_results(0);
    186      result->set_status(
    187          static_cast<
    188              ::content_analysis::sdk::ContentAnalysisResponse_Result_Status>(
    189              100));*/
    190    } else {
    191      handled = false;
    192    }
    193    return handled;
    194  }
    195 
    196  bool SendCustomResponse(std::unique_ptr<Event>& event) override {
    197    if (mode_ == Mode::Mode_largeResponse) {
    198      content_analysis::sdk::ContentAnalysisEventWin* eventWin =
    199          static_cast<content_analysis::sdk::ContentAnalysisEventWin*>(
    200              event.get());
    201      HANDLE pipe = eventWin->Pipe();
    202      std::cout << "largeResponse about to write" << std::endl;
    203      DWORD result = WriteBigMessageToPipe(
    204          pipe, eventWin->SerializeStringToSendToBrowser());
    205      std::cout << "largeResponse done writing with error " << result
    206                << std::endl;
    207      eventWin->SetResponseSent();
    208    } else if (mode_ == Mode::Mode_resultWithInvalidStatus) {
    209      content_analysis::sdk::ContentAnalysisEventWin* eventWin =
    210          static_cast<content_analysis::sdk::ContentAnalysisEventWin*>(
    211              event.get());
    212      HANDLE pipe = eventWin->Pipe();
    213      std::string serializedString = eventWin->SerializeStringToSendToBrowser();
    214      // The last byte is the status value. Set it to 100
    215      serializedString[serializedString.length() - 1] = 100;
    216      WriteBigMessageToPipe(pipe, serializedString);
    217    } else if (mode_ == Mode::Mode_messageTruncatedInMiddleOfString) {
    218      unsigned char bytes[5];
    219      bytes[0] = 10;  // field 1 (request_token), LEN encoding
    220      bytes[1] = 13;  // length 13
    221      bytes[2] = 65;  // "A"
    222      bytes[3] = 66;  // "B"
    223      bytes[4] = 67;  // "C"
    224      SendBytesOverPipe(bytes, event);
    225    } else if (mode_ == Mode::Mode_messageWithInvalidWireType) {
    226      unsigned char bytes[5];
    227      bytes[0] = 15;  // field 1 (request_token), "7" encoding (invalid value)
    228      bytes[1] = 3;   // length 3
    229      bytes[2] = 65;  // "A"
    230      bytes[3] = 66;  // "B"
    231      bytes[4] = 67;  // "C"
    232      SendBytesOverPipe(bytes, event);
    233    } else if (mode_ == Mode::Mode_messageWithUnusedFieldNumber) {
    234      unsigned char bytes[5];
    235      bytes[0] = 82;  // field 10 (this is invalid), LEN encoding
    236      bytes[1] = 3;   // length 3
    237      bytes[2] = 65;  // "A"
    238      bytes[3] = 66;  // "B"
    239      bytes[4] = 67;  // "C"
    240      SendBytesOverPipe(bytes, event);
    241    } else if (mode_ == Mode::Mode_messageWithWrongStringWireType) {
    242      unsigned char bytes[2];
    243      bytes[0] = 10;  // field 1 (request_token), VARINT encoding (but should be
    244                      // a string/LEN)
    245      bytes[1] = 42;  // value 42
    246      SendBytesOverPipe(bytes, event);
    247    } else if (mode_ == Mode::Mode_messageWithZeroTag) {
    248      unsigned char bytes[1];
    249      // The protobuf deserialization code seems to handle this
    250      // in a special case.
    251      bytes[0] = 0;
    252      SendBytesOverPipe(bytes, event);
    253    } else if (mode_ == Mode::Mode_messageWithZeroFieldButNonzeroWireType) {
    254      // The protobuf deserialization code seems to handle this
    255      // in a special case.
    256      unsigned char bytes[5];
    257      bytes[0] = 2;   // field 0 (invalid), LEN encoding
    258      bytes[1] = 3;   // length 13
    259      bytes[2] = 65;  // "A"
    260      bytes[3] = 66;  // "B"
    261      bytes[4] = 67;  // "C"
    262      SendBytesOverPipe(bytes, event);
    263    } else if (mode_ == Mode::Mode_messageWithGroupEnd) {
    264      // GROUP_ENDs are obsolete and the deserialization code
    265      // handles them in a special case.
    266      unsigned char bytes[1];
    267      bytes[0] = 12;  // field 1 (request_token), GROUP_END encoding
    268      SendBytesOverPipe(bytes, event);
    269    } else if (mode_ == Mode::Mode_messageTruncatedInMiddleOfVarint) {
    270      unsigned char bytes[2];
    271      bytes[0] = 16;   // field 2 (status), VARINT encoding
    272      bytes[1] = 128;  // high bit is set, indicating there
    273                       // should be a byte after this
    274      SendBytesOverPipe(bytes, event);
    275    } else if (mode_ == Mode::Mode_messageTruncatedInMiddleOfTag) {
    276      unsigned char bytes[1];
    277      bytes[0] = 128;  // tag is actually encoded as a VARINT, so set the high
    278                       // bit, indicating there should be a byte after this
    279      SendBytesOverPipe(bytes, event);
    280    } else {
    281      return false;
    282    }
    283    return true;
    284  }
    285 
    286 private:
    287  Mode mode_;
    288 };
    289 
    290 #endif  // CONTENT_ANALYSIS_DEMO_HANDLER_MISBEHAVING_H_