mirror of
https://github.com/OpenVPN/openvpn3.git
synced 2024-09-20 12:12:15 +02:00
f38e97e1c3
- [ipv4.hpp, ipv6.hpp] In both v4 and v6 headers it is safe to cast the hex so as to eliminate the spurious warnings. - [lz4.hpp] Apply value clamp to the hint that is sent to the compressor to prevent a potential conversion overflow. - [zlib.hpp] In compress_gzip, zs.s.avail_in and zs.s.avail_out are theoretically susceptable to overflow. To prevent this we use numeric_cast. In decompress_gzip we do a similar thing for zs.s.avail_in but only value clamp avail_out, since the read loop looks like it will compensate - [buffmt.hpp] It's safe to cast the result of the arithmentically caused promotion back down to char. - [base64.hpp] In Base64 CTOR, changed type of a couple variables to match the type of the table they generate. In decode, perform a static cast to the type of the template elements the function is instantiated for. - [core.hpp] Perform static cast long --> int on value representing number of cores. If we run on systems where there are more cores than int can represent this will behave oddly, but this circumstance seems unlikely at the present time. - [environ.hpp] The casts seem to be safe but I have added a todo ticket to evaluate this change further. - [hexstr.hpp] In render_hex_char there were two conversion warnings and a bug involving out of range input. Those are addressed. In dump_hex the result of some math and logic is now clamped to the range of acceptable input values for string::spaces In parse_hex the result of converting from a hex string to an integral value is cast to the template value_type - [hostport.hpp] The static_cast should be safe because the value produced by validate_port is range checked. - [split.hpp] Applied numeric cast to ensure output of lex.get stays within acceptable type limit. - [stop.hpp] In Stop::Scope It's extremely unlikely but was possible for the vector size to exceed the limit of int. The size now has a much lower limit applied and will throw if it is exceeded. - [string.hpp] Changed the call to toupper/tolower so they call the locale function template instead of the cctype C function. This eliminates the warning and the need for the cast. - [cliproto.hpp] The computation of mss_fix is stored in a size_t and then assigned to an unsigned short. We clamp this assignment to the range of unsigned short. - [tempfile.hpp] In TempFile CTOR suffixLen is computed as one type and consumed as another. Since the CTOR is already throwing for a couple other error conditions, I have added a numeric_cast to the conversion that also throws in case of a value overflow. - [unicode.hpp] In an 8 --> 16 bit string conversion we mask and assign in a way the compiler can't be certain is safe even though it is safe. Added static cast to let the compiler know it's safe. In the second case the class uses unsigned int to store a size, and then uses it in with size_t which generates conversion warnings. I have changed the type of size to size_t - [logperiod.hpp] in log_period changed return type specification to match the actual return type. - [usergroup_retain_cap.hpp] In the unlikely event the caps size (size_t) exceeds the range cap_set_flag can accept, an exception will be thrown. - [crypto_aead.hpp] StaticKey::size provides a size_t where unsigned int is required. We use numeric_cast to check the size() value in the extremely unlikely event it is manipulated to exceed the allowed value. - [packet_id.hpp] Code packs a time_t into a uint32_t for replay packet ID protection purposes. The warning is supressed by a mask and cast since the 32 bit limit is baked into the protocol and the overflow itself does not cause a severe breakage. - [headredact.hpp] Altered code such that the type that stores the find result is compatible with the result from find. Additionally used the npos constant instead of -1. There is a commented out code block that claims to be dropped due to requiring C++ '14 - consider just using that. - [csum.hpp] in csum fold and cfold one has a mask and cast, the second is just a cast to undo a promotion. Both appear safe. - [ipv4.hpp] Values are masked and shifted so the cast should be safe. Added cast. - [ping4.hpp] ICMP ID and sequence number function arguments are changed to the same type as needed by the structure. For IPv4 header version_len 2nd arg is int but sizeof is not, so we cast it. IPv4 tot_len is a uint16_t so we clamp to that value range and compute it once. - [ping6.hpp] Enforces a value constraint on the len argument to csum_icmp, then checks the result of some math to ensure the result will fit in the type it has to fit. In generate_echo_request the ICMP ID and sequence args are changed to match the type they are assigned to in the struct, and added numeric_cast to range check payload_len. - [remotelist.hpp] In get_endpoint, endpoint.port is called with an unsigned int where the function is expecting an unsigned short int. Since parse_number_throw is a function template, we just ask it to return the correct type now. - [compress.hpp] In v2_push we accept an int value that is assigned to an unsigned char we push to the buffer. I changed the function to accept an unsigned char directly. Added unit tests - thanks Mark Deric. Signed-off-by: Charlie Vigue <charlie.vigue@openvpn.net>
503 lines
15 KiB
C++
503 lines
15 KiB
C++
// OpenVPN -- An application to securely tunnel IP networks
|
|
// over a single port, with support for SSL/TLS-based
|
|
// session authentication and key exchange,
|
|
// packet encryption, packet authentication, and
|
|
// packet compression.
|
|
//
|
|
// Copyright (C) 2012-2022 OpenVPN Inc.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License Version 3
|
|
// as published by the Free Software Foundation.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program in the COPYING file.
|
|
// If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
// A collection of functions for rendering and parsing hexadecimal strings
|
|
|
|
#ifndef OPENVPN_COMMON_HEXSTR_H
|
|
#define OPENVPN_COMMON_HEXSTR_H
|
|
|
|
#include <string>
|
|
#include <iomanip>
|
|
#include <sstream>
|
|
#include <algorithm>
|
|
|
|
#include <openvpn/common/clamp_typerange.hpp>
|
|
#include <openvpn/common/exception.hpp>
|
|
#include <openvpn/common/string.hpp>
|
|
|
|
using namespace openvpn::numeric_util;
|
|
|
|
namespace openvpn {
|
|
|
|
/**
|
|
* Renders an integer value within the hexadecimal range (0-15)
|
|
* to a hexadecimal character.
|
|
*
|
|
* @param c Integer to render as a hexadecimal character.
|
|
* @param caps Boolean (default false) which sets the outout to
|
|
* be either lower case (false) or upper case (true).
|
|
*
|
|
* @return Returns a char with the hexadecimal representation of
|
|
* the input value. If the value is out-of-range (outside
|
|
* of 0-15), it will be replaced with a questionmark (?).
|
|
*/
|
|
inline char render_hex_char(const int c, const bool caps = false)
|
|
{
|
|
if (c < 0)
|
|
return '?';
|
|
if (c < 10)
|
|
return '0' + c;
|
|
else if (c < 16)
|
|
return (caps ? 'A' : 'a') - 10 + c;
|
|
else
|
|
return '?';
|
|
}
|
|
|
|
/**
|
|
* Parses a character in the range {0..9,A-F,a-f} to an
|
|
* integer value. Used to convert hexadecimal character to integer.
|
|
* Only a single character is parsed by this function.
|
|
*
|
|
* @param c Character to be be parsed.
|
|
*
|
|
* @return Returns an integer value of the hexadecimal input. If the
|
|
* input character is invalid, outside of {0..9,A-F,a-f}, it will
|
|
* return -1.
|
|
*/
|
|
inline int parse_hex_char(const char c)
|
|
{
|
|
if (c >= '0' && c <= '9')
|
|
return c - '0';
|
|
else if (c >= 'a' && c <= 'f')
|
|
return c - 'a' + 10;
|
|
else if (c >= 'A' && c <= 'F')
|
|
return c - 'A' + 10;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
|
|
/**
|
|
* Class which Renders a single byte as hexadecimal
|
|
*/
|
|
class RenderHexByte
|
|
{
|
|
public:
|
|
/**
|
|
* Initializes a new object
|
|
*
|
|
* @param byte Unsigned char (one byte) to be processed
|
|
* @param caps Boolean (default false) which sets the outout to
|
|
* be either lower case (false) or upper case (true).
|
|
*/
|
|
RenderHexByte(const unsigned char byte, const bool caps = false)
|
|
{
|
|
c[0] = render_hex_char(byte >> 4, caps);
|
|
c[1] = render_hex_char(byte & 0x0F, caps);
|
|
}
|
|
|
|
char char1() const
|
|
{
|
|
return c[0];
|
|
}
|
|
char char2() const
|
|
{
|
|
return c[1];
|
|
}
|
|
|
|
/**
|
|
* Retrieve the hexadecimal representation of the value.
|
|
* Warning: The result is a non-NULL terminated string.
|
|
*
|
|
* @return Returns a non-NULL terminated 2 byte string with the hexadecimal
|
|
* representation of the initial value. The return value is guaranteed
|
|
* to always be 2 bytes.
|
|
*/
|
|
const char *str2() const
|
|
{
|
|
return c;
|
|
} // Note: length=2, NOT null terminated
|
|
|
|
private:
|
|
char c[2];
|
|
};
|
|
|
|
|
|
/**
|
|
* Render a byte buffer (unsigned char *) as a hexadecimal string.
|
|
*
|
|
* @param data Unsigned char pointer to buffer to render.
|
|
* @param size size_t of the number of bytes to parse from the buffer.
|
|
* @param caps Boolean (default false) which sets the outout to
|
|
* be either lower case (false) or upper case (true).
|
|
*
|
|
* @return Returns a std::string of the complete hexadecimal representation
|
|
*/
|
|
inline std::string render_hex(const unsigned char *data, size_t size, const bool caps = false)
|
|
{
|
|
if (!data)
|
|
return "NULL";
|
|
std::string ret;
|
|
ret.reserve(size * 2 + 1);
|
|
while (size--)
|
|
{
|
|
const RenderHexByte b(*data++, caps);
|
|
ret += b.char1();
|
|
ret += b.char2();
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* Render a byte buffer (void *) as a hexadecimal string.
|
|
*
|
|
* @param data Void pointer to buffer to render.
|
|
* @param size size_t of the number of bytes to parse from the buffer.
|
|
* @param caps Boolean (default false) which sets the outout to
|
|
* be either lower case (false) or upper case (true).
|
|
*
|
|
* @return Returns a std::string of the complete hexadecimal representation.
|
|
*/
|
|
inline std::string render_hex(const void *data, const size_t size, const bool caps = false)
|
|
{
|
|
return render_hex((const unsigned char *)data, size, caps);
|
|
}
|
|
|
|
|
|
/**
|
|
* Variant of @render_hex(const unsiged char *,...) which adds a
|
|
* separator between each byte
|
|
*
|
|
* @param data Unsigned char pointer to buffer to render.
|
|
* @param size size_t of the number of bytes to parse from the buffer.
|
|
* @param sep A single character to use as the separator.
|
|
* @param caps Boolean (default false) which sets the outout to
|
|
* be either lower case (false) or upper case (true).
|
|
*
|
|
* @return Returns a std::string of the complete hexadecimal representation
|
|
* with each byte separated by a given character.
|
|
*/
|
|
inline std::string render_hex_sep(const unsigned char *data, size_t size, const char sep, const bool caps = false)
|
|
{
|
|
if (!data)
|
|
return "NULL";
|
|
std::string ret;
|
|
ret.reserve(size * 3);
|
|
bool prsep = false;
|
|
while (size--)
|
|
{
|
|
if (prsep)
|
|
ret += sep;
|
|
const RenderHexByte b(*data++, caps);
|
|
ret += b.char1();
|
|
ret += b.char2();
|
|
prsep = true;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Variant of @render_hex(const void *,...) which adds a
|
|
* separator between each byte
|
|
|
|
* @param data Void pointer to buffer to render.
|
|
* @param size size_t of the number of bytes to parse from the buffer.
|
|
* @param sep A single character to use as the separator.
|
|
* @param caps Boolean (default false) which sets the outout to
|
|
* be either lower case (false) or upper case (true).
|
|
*
|
|
* @return Returns a std::string of the complete hexadecimal representation
|
|
* with each byte separated by a given character.
|
|
*/
|
|
inline std::string render_hex_sep(const void *data, const size_t size, const char sep, const bool caps = false)
|
|
{
|
|
return render_hex_sep((const unsigned char *)data, size, sep, caps);
|
|
}
|
|
|
|
|
|
/**
|
|
* Render a std::vector<T> container as a hexadecimal string.
|
|
* T must be a data type compatible with
|
|
* RenderHexByte(const unsigned char,...)
|
|
*
|
|
* @param data std::vector<T> containing the data to render
|
|
* @param caps Boolean (default false) which sets the outout to
|
|
* be either lower case (false) or upper case (true).
|
|
*
|
|
* @return Returns a std::string of the complete hexadecimal representation.
|
|
*/
|
|
template <typename V>
|
|
inline std::string render_hex_generic(const V &data, const bool caps = false)
|
|
{
|
|
std::string ret;
|
|
ret.reserve(data.size() * 2 + 1);
|
|
for (size_t i = 0; i < data.size(); ++i)
|
|
{
|
|
const RenderHexByte b(data[i], caps);
|
|
ret += b.char1();
|
|
ret += b.char2();
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* Renders a combined hexadecimal and character dump of a buffer,
|
|
* with the typical 16 bytes split between hexadecimal and character
|
|
* separation per line.
|
|
*
|
|
* @param data Unsigned char pointer to the buffer to dump.
|
|
* @param size Size of the buffer to render.
|
|
*
|
|
* @return Returns a string containing a preformatted output of the
|
|
* hexadecimal dump.
|
|
*/
|
|
inline std::string dump_hex(const unsigned char *data, size_t size)
|
|
{
|
|
if (!data)
|
|
return "NULL\n";
|
|
const unsigned int mask = 0x0F; // N bytes per line - 1
|
|
std::ostringstream os;
|
|
os << std::hex;
|
|
std::string chars;
|
|
size_t i;
|
|
for (i = 0; i < size; ++i)
|
|
{
|
|
if (!(i & mask))
|
|
{
|
|
if (i)
|
|
{
|
|
os << " " << chars << std::endl;
|
|
chars.clear();
|
|
}
|
|
os << std::setfill(' ') << std::setw(8) << i << ":";
|
|
}
|
|
const unsigned char c = data[i];
|
|
os << ' ' << std::setfill('0') << std::setw(2) << (unsigned int)c;
|
|
if (string::is_printable(c))
|
|
chars += c;
|
|
else
|
|
chars += '.';
|
|
}
|
|
if (i)
|
|
os << string::spaces(clamp_to_typerange<int>(2 + (((i - 1) & mask) ^ mask) * 3)) << chars << std::endl;
|
|
return os.str();
|
|
}
|
|
|
|
/**
|
|
* Renders a combined hexadecimal and character dump of a buffer,
|
|
* with the typical 16 bytes split between hexadecimal and character
|
|
* separation per line.
|
|
*
|
|
* @param data Void pointer to the buffer to dump.
|
|
* @param size Size of the buffer to render.
|
|
*
|
|
* @return Returns a string containing a preformatted output of the
|
|
* hexadecimal dump.
|
|
*/
|
|
inline std::string dump_hex(void *data, size_t size)
|
|
{
|
|
return dump_hex((const unsigned char *)data, size);
|
|
}
|
|
|
|
/**
|
|
* Renders a combined hexadecimal and character dump of a std::string buffer,
|
|
* with the typical 16 bytes split between hexadecimal and character
|
|
* separation per line.
|
|
*
|
|
* @param data std::string containing the buffer to render
|
|
*
|
|
* @return Returns a string containing a preformatted output of the
|
|
* hexadecimal dump.
|
|
*/
|
|
inline std::string dump_hex(const std::string &str)
|
|
{
|
|
return dump_hex((const unsigned char *)str.c_str(), str.length());
|
|
}
|
|
|
|
|
|
/**
|
|
* Renders a combined hexadecimal and character dump of a std::vector<T>
|
|
* based buffer, with the typical 16 bytes split between hexadecimal and
|
|
* character separation per line.
|
|
*
|
|
* @param data std::vector<T> containing the buffer to render
|
|
*
|
|
* @return Returns a string containing a preformatted output of the
|
|
* hexadecimal dump.
|
|
*/
|
|
template <typename V>
|
|
inline std::string dump_hex(const V &data)
|
|
{
|
|
return dump_hex(data.c_data(), data.size());
|
|
}
|
|
|
|
/**
|
|
* Declaration of a hexadecimal parsing error exception class
|
|
*/
|
|
OPENVPN_SIMPLE_EXCEPTION(parse_hex_error);
|
|
|
|
|
|
/**
|
|
* Parses a std::string containing a hexadecimal value into
|
|
* a std::vector<T>.
|
|
*
|
|
* @param dest std::vector<T> destination buffer to use.
|
|
* @param str std::string& containing the hexadecimal string to parse.
|
|
*
|
|
* @return Returns nothing on success. Will throw a parse_hex_error
|
|
* exception if the input is invalid/not parseable as a hexadecimal
|
|
* number.
|
|
*/
|
|
template <typename V>
|
|
inline void parse_hex(V &dest, const std::string &str)
|
|
{
|
|
using vvalue_t = typename V::value_type;
|
|
const int len = int(str.length());
|
|
int i;
|
|
for (i = 0; i <= len - 2; i += 2)
|
|
{
|
|
const int high = parse_hex_char(str[i]);
|
|
const int low = parse_hex_char(str[i + 1]);
|
|
if (high == -1 || low == -1)
|
|
throw parse_hex_error();
|
|
dest.push_back(static_cast<vvalue_t>((high << 4) + low));
|
|
}
|
|
if (i != len)
|
|
throw parse_hex_error(); // straggler char
|
|
}
|
|
|
|
|
|
/**
|
|
* Parses a char buffer (C string) containing a hexadecimal
|
|
* string into a templated (T) variable. The input buffer
|
|
* MUST be NULL terminated.
|
|
*
|
|
* WARNING: There are _NO_ overflow checks.
|
|
*
|
|
* @param str Char pointer (char *) to the buffer to be parsed.
|
|
* @param retval Return buffer where the parsed value is stored.
|
|
*
|
|
* @return Returns true on successful parsing, otherwise false.
|
|
*/
|
|
template <typename T>
|
|
inline bool parse_hex_number(const char *str, T &retval)
|
|
{
|
|
if (!str[0])
|
|
return false; // empty string
|
|
size_t i = 0;
|
|
T ret = T(0);
|
|
while (true)
|
|
{
|
|
const char c = str[i++];
|
|
const int hd = parse_hex_char(c);
|
|
if (hd >= 0)
|
|
{
|
|
ret *= T(16);
|
|
ret += T(hd);
|
|
}
|
|
else if (!c)
|
|
{
|
|
retval = ret;
|
|
return true;
|
|
}
|
|
else
|
|
return false; // non-hex-digit
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Variant of @parse_hex_number(const char *, ...) which takes a std::string
|
|
* as the input.
|
|
*
|
|
* @param str std::string containing the hexadecimal string to be parsed.
|
|
* @param retval Return buffer where the parsed value is stored.
|
|
*
|
|
* @return Returns true on successful parsing, otherwise false.
|
|
*/
|
|
template <typename T>
|
|
inline bool parse_hex_number(const std::string &str, T &retval)
|
|
{
|
|
return parse_hex_number(str.c_str(), retval);
|
|
}
|
|
|
|
|
|
/**
|
|
* Parses a std::string containing a hexadecimal
|
|
* string into a templated (T) variable.
|
|
*
|
|
* NOTE: Currently doesn't detect overflow
|
|
*
|
|
* @param str std::string containing the hexadecimal
|
|
* string to be parsed.
|
|
*
|
|
* @return Returns a template T variable containing the
|
|
* parsed value on success. Will throw the parse_hex_error
|
|
* exception on parsing errors.
|
|
*
|
|
*/
|
|
template <typename T>
|
|
inline T parse_hex_number(const std::string &str)
|
|
{
|
|
T ret;
|
|
if (!parse_hex_number<T>(str.c_str(), ret))
|
|
throw parse_hex_error();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Renders a templated T variable containing a numeric value
|
|
* into a std::string containing a hexadecimal representation.
|
|
*
|
|
* @param value Numeric (T) value to represent as hexadecimal.
|
|
* @param caps Boolean (default false) which sets the outout to
|
|
* be either lower case (false) or upper case (true).
|
|
*
|
|
* @return Retuns a std::string containing the hexadecimal
|
|
* representation on succes. Will throw a parse_hex_error
|
|
* exception on parsing errors.
|
|
*/
|
|
template <typename T>
|
|
std::string render_hex_number(T value, const bool caps = false)
|
|
{
|
|
unsigned char buf[sizeof(T)];
|
|
for (size_t i = sizeof(T); i-- > 0;)
|
|
{
|
|
buf[i] = value & 0xFF;
|
|
value >>= 8;
|
|
}
|
|
return render_hex(buf, sizeof(T), caps);
|
|
}
|
|
|
|
|
|
/**
|
|
* Renders a single byte as a hexadecimal string
|
|
*
|
|
* @param value Unsigned char (byte) to be represented as hexadecimal.
|
|
* @param caps Boolean (default false) which sets the outout to
|
|
* be either lower case (false) or upper case (true).
|
|
*
|
|
* @return Returns a std::string with the hexadecimal representation
|
|
* of the input value. The result will always contain only
|
|
* two characters.
|
|
*/
|
|
inline std::string render_hex_number(unsigned char uc, const bool caps = false)
|
|
{
|
|
RenderHexByte b(uc, caps);
|
|
return std::string(b.str2(), 2);
|
|
}
|
|
|
|
} // namespace openvpn
|
|
|
|
#endif // OPENVPN_COMMON_HEXSTR_H
|