// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved. // Licensed under the Apache License, Version 2.0 (the "License"); // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "PHPSerializedDataParser.h" #include "log_generator.h" #include USE_DEBUG_FLAG(D_WAAP_PARSER_PHPSERIALIZE); const std::string PHPSerializedDataParser::m_parserName = "PHPSerializedDataParser"; PHPSerializedDataParser::PHPSerializedDataParser(IParserStreamReceiver &outReceiver, size_t parser_depth) : m_state(), m_outReceiver(outReceiver), m_keyStack("php_serialized"), m_parser_depth(parser_depth) { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "parser_depth=" << parser_depth; } size_t PHPSerializedDataParser::push(const char* buf, size_t len) { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::push()"; size_t i = 0; char c; if (len == 0) { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::push(): len = 0 "; if(m_state.phase_state != s_start) { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::push(): len = 0 ;" "phase_state != s_start ; m_state.phase_state: " << m_state.phase_state; m_error = true; return -1; } switch (m_state.kv_state) { case (s_onKey): { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::push(): len = 0 ; s_onKey"; m_outReceiver.onKey(m_value.c_str(), m_value.length()); break; } case (s_onValue): { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::push(): len = 0 ; s_onValue"; m_outReceiver.onValue(m_value.c_str(), m_value.length()); m_outReceiver.onKvDone(); break; } case (s_clear_kv): { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::push(): len = 0 ; s_clear_kv;" "State Finished has expected"; // State Finished has expected. break; } } return 1; } while (i < len) { c = buf[i]; dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::push():while(i 1) { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::push(): s_boolean_OnValue" << " Error length is bigger than 1 : Boolean should be with 0 or 1"; m_error = true; return -1; } if ( c != ';' ) { m_value.push_back(c); break; } // boolean can be 0 or 1 only. if (m_value.compare("1") != 0 && m_value.compare("0") != 0) { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::push(): s_boolean_OnValue" << " Error Boolean value is not 0 or 1 : " << m_value; m_error = true; return -1; } if (handleStateAfterFinish("Boolean")) { break; } m_value.clear(); m_state.phase_state = s_start; break; } case s_null: { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::push(): s_null"; if (c != ';') { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::push(): s_null" << " Null should end with ';' not with : " << c; m_error = true; return -1; } if (handleStateAfterFinish("Null")) { break; } m_value.clear(); m_state.phase_state = s_start; break; } default: { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::push(): default" << " Unexpected Error."; m_error = true; return -1; } } return 0; } // Handle data end of an object and if he got the right number of values. // termChar = is char terminator (f.e }) // checkEndBlock enable check if last char is equal to } bool PHPSerializedDataParser::onDataEnd(char termChar, bool checkEndBlock) { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::onDataEnd (phase_state=" << m_state.phase_state << ", termChar='" << termChar << "')"; if (m_state.current_length != m_state.length) { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::push():" << "current_length " << m_state.current_length << "!=" << " m_state.length " << m_state.length; m_error = true; return false; } if (termChar != '}' && checkEndBlock) { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::push():" << "termChar is not }"; m_error = true; return false; } if (m_stack.empty()) { return true; } m_state.isObject = false; m_keyStack.pop(m_keyStack.first().c_str()); m_state = m_stack.top(); m_stack.pop(); m_state.phase_state = s_start; return true; } void PHPSerializedDataParser::onEmptyStack(std::string type) { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::onEmptyStack(): stack is empty."; m_outReceiver.onKey(type.c_str(), type.length()); m_outReceiver.onValue(m_value.c_str(), m_value.length()); m_outReceiver.onKvDone(); m_value.clear(); m_state.current_length = 0; m_state.phase_state = s_start; } void PHPSerializedDataParser::onStateKey() { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::onStateKey()"; if (m_keyStack.size() >= 1) { m_value = m_keyStack.str() + "." + m_value; } m_outReceiver.onKey(m_value.c_str(), m_value.length()); // clear current length m_state.current_length = 0; //clear value m_value.clear(); // change state from key to value. m_state.kv_state = s_onValue; m_state.phase_state = s_start; } void PHPSerializedDataParser::onStateValue() { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::onStateValue()"; // change state from value to key. m_state.kv_state = s_onKey; // Look at our last state and raise its member counter. State &stack_state = m_stack.top(); stack_state.current_length++; dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "stack_state.current_length: " << stack_state.current_length; // set Value and KvDone. m_outReceiver.onValue(m_value.c_str(), m_value.length()); m_outReceiver.onKvDone(); } // checking if current length is equal to the length the object got // and move it to s_data_end bool PHPSerializedDataParser::onCheckLength() { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::onCheckLength()"; State &stack_state = m_stack.top(); if (stack_state.current_length == stack_state.length) { m_state.current_length = 0; m_value.clear(); m_state = m_stack.top(); m_state.phase_state = s_data_end; m_state.kv_state = s_clear_kv; return true; } return false; } // Handle State after finishing reading data from a type. bool PHPSerializedDataParser::handleStateAfterFinish(const std::string &type) { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::handleStateAfterFinish()"; // If stack empty that means we don't have last state : Object || Custom || Array if (m_stack.empty()) { onEmptyStack(type); return true; } if (m_state.kv_state == s_onKey) { onStateKey(); return true; } // If stack is not empty check last state Object || Custom || Array // change state from value - if state on key should throw error on s_start // key must be value on boolean onStateValue(); if (onCheckLength()) { return true; } return false; } void PHPSerializedDataParser::finish() { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::finish()"; push(NULL, 0); } const std::string & PHPSerializedDataParser::name() const { return m_parserName; } bool PHPSerializedDataParser::error() const { if (m_error) { dbgTrace(D_WAAP_PARSER_PHPSERIALIZE) << "PHPSerializedDataParser::error(): parser returned with an error"; return true; } return false; }