aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rfc4251.C41
-rw-r--r--rfc4251.H116
-rw-r--r--rfc4251_gmp.C51
-rw-r--r--ssh-agent-filter.C14
4 files changed, 120 insertions, 102 deletions
diff --git a/rfc4251.C b/rfc4251.C
index 5ea0a93..05ef8b8 100644
--- a/rfc4251.C
+++ b/rfc4251.C
@@ -1,10 +1,10 @@
/*
* rfc4251.C -- support for name-list type from RFC 4251, section 5
*
- * These are the conversions between an rfc4251::string containing a name-list
- * and vector<string>.
+ * These are the conversions between an rfc4251::name_list and
+ * std::vector<std::string>.
*
- * Copyright (C) 2013,2015 Timo Weingärtner <timo@tiwe.de>
+ * Copyright (C) 2013,2015,2021 Timo Weingärtner <timo@tiwe.de>
*
* This file is part of ssh-agent-filter.
*
@@ -26,34 +26,33 @@
namespace rfc4251 {
-string::string (std::vector<std::string> const & v) {
+name_list::name_list (std::vector<std::string> const & v) {
for (auto it = v.begin(); it != v.end();) {
if (it->size() == 0)
throw std::length_error{"name of zero length"};
- if (value.size() + it->size() > std::numeric_limits<uint32_t>::max())
- throw std::length_error{"32-bit limit for rfc4251::string exceeded"};
- value.insert(value.end(), it->data(), it->data() + it->size());
+ check_length_against_limit(buf.size() + it->size());
+ buf.insert(buf.end(), it->data(), it->data() + it->size());
++it;
if (it == v.end())
break;
- value.push_back(',');
+ buf.push_back(',');
}
}
-string::operator std::vector<std::string> () const {
- std::vector<std::string> ret;
- auto name_start = value.begin();
- if (name_start != value.end())
- for (auto it = name_start; ; ++it) {
- if (it == value.end() or *it == ',') {
- if (it == name_start)
- throw std::length_error{"name of zero length"};
- ret.emplace_back(name_start, it);
- name_start = it + 1;
- }
- if (it == value.end())
- break;
+name_list::operator std::vector<std::string> () const {
+ std::vector<std::string> ret{};
+ if (buf.empty()) return ret;
+ auto name_start = buf.begin();
+ for (auto it = name_start; ; ++it) {
+ if (it == buf.end() or *it == ',') {
+ if (it == name_start)
+ throw std::length_error{"name of zero length"};
+ ret.emplace_back(name_start, it);
+ name_start = it + 1;
}
+ if (it == buf.end())
+ break;
+ }
return ret;
}
diff --git a/rfc4251.H b/rfc4251.H
index fe8ccd4..6647d15 100644
--- a/rfc4251.H
+++ b/rfc4251.H
@@ -5,7 +5,9 @@
* rfc4251::boolean boolean
* rfc4251::uint32 uint32
* rfc4251::uint64 uint64
- * rfc4251::string string, incl. mpint and name-list
+ * rfc4251::string string
+ * rfc4251::mpint mpint
+ * rfc4251::name_list name-list
*
* those structs contain the objects in their RFC 4251 representation,
* conversions are provided via constructors and cast operators
@@ -85,6 +87,48 @@ namespace internal {
return os.write(x.buf.data(), x.buf.size());
}
};
+
+ template<typename Length_Type>
+ struct variable_length : boost::totally_ordered<variable_length<Length_Type>> {
+ using length_type = Length_Type;
+ static_assert(std::is_unsigned_v<length_type>, "negative lengths are not supported");
+
+ std::vector<char> buf;
+
+ variable_length () = default;
+ explicit variable_length (std::istream & is) { is >> *this; }
+
+ size_t size () const noexcept { return buf.size(); }
+ char const * data () const noexcept { return buf.data(); }
+ char * data () noexcept { return buf.data(); }
+
+ friend std::istream & operator>> (std::istream & is, variable_length & v) {
+ if (length_type const len{fixed_length<length_type>{is}}; is) {
+ v.buf.clear();
+ v.buf.resize(len);
+ is.read(v.buf.data(), len);
+ }
+ return is;
+ }
+ friend std::ostream & operator<< (std::ostream & os, variable_length const & v) {
+ check_length_against_limit(v.buf.size());
+ if (os << fixed_length<length_type>{static_cast<length_type>(v.buf.size())})
+ os.write(v.buf.data(), v.buf.size());
+ return os;
+ }
+ friend bool operator== (variable_length const & l, variable_length const & r) {
+ return l.buf == r.buf;
+ }
+ friend bool operator< (variable_length const & l, variable_length const & r) {
+ return l.buf < r.buf;
+ }
+
+ constexpr static void check_length_against_limit(size_t const length) {
+ if (length > std::numeric_limits<length_type>::max())
+ throw std::length_error{"numeric limit for length field in rfc4251::variable_length type exceeded"};
+ }
+
+ };
}
using byte = internal::fixed_length<uint8_t>;
@@ -92,59 +136,33 @@ using boolean = internal::fixed_length<bool>;
using uint32 = internal::fixed_length<uint32_t>;
using uint64 = internal::fixed_length<uint64_t>;
-struct string : boost::totally_ordered<string> {
- std::vector<char> value;
-
- string () = default;
- inline explicit string (char const *, size_t);
+struct string : internal::variable_length<uint32_t> {
+ using variable_length::variable_length;
+
+ inline explicit string (char const * s, size_t l) {
+ check_length_against_limit(l);
+ buf.insert(buf.end(), s, s + l);
+ }
explicit string (std::string const & s) : string{s.data(), s.size()} {}
- explicit string (std::vector<std::string> const &);
- explicit string (mpz_srcptr);
- explicit string (mpz_class const & x) : string{x.get_mpz_t()} {}
- inline explicit string (std::istream &);
- size_t size () const noexcept { return value.size(); }
- char const * data () const noexcept { return value.data(); }
- char * data () noexcept { return value.data(); }
+ operator std::string () const { return {buf.cbegin(), buf.cend()}; }
+};
- operator std::string () const { return {value.begin(), value.end()}; }
+struct name_list : internal::variable_length<uint32_t> {
+ using variable_length::variable_length;
+
+ explicit name_list (std::vector<std::string> const &);
+
operator std::vector<std::string> () const;
- operator mpz_class () const;
};
-inline string::string (char const * s, size_t l) {
- if (l > std::numeric_limits<uint32_t>::max())
- throw std::length_error{"32-bit limit for rfc4251::string exceeded"};
- value.insert(value.end(), s, s + l);
-}
-
-inline std::istream & operator>> (std::istream & is, string & s) {
- s.value.clear();
- if (uint32_t const len{uint32{is}}; is) {
- s.value.resize(len);
- is.read(s.value.data(), len);
- }
- return is;
-}
-
-inline std::ostream & operator<< (std::ostream & os, string const & s) {
- if (s.value.size() > std::numeric_limits<uint32_t>::max())
- throw std::length_error{"32-bit limit for rfc4251::string exceeded"};
- if (os << uint32{static_cast<uint32_t>(s.value.size())})
- os.write(s.value.data(), s.value.size());
- return os;
-}
-
-inline string::string (std::istream & is) {
- is >> *this;
-}
-
-inline bool operator== (string const & l, string const & r) {
- return l.value == r.value;
-}
-
-inline bool operator< (string const & l, string const & r) {
- return l.value < r.value;
-}
+struct mpint : internal::variable_length<uint32_t> {
+ using variable_length::variable_length;
+
+ explicit mpint (mpz_srcptr);
+ explicit mpint (mpz_class const & x) : mpint{x.get_mpz_t()} {}
+
+ operator mpz_class () const;
+};
}
diff --git a/rfc4251_gmp.C b/rfc4251_gmp.C
index b4c369b..06b6cf6 100644
--- a/rfc4251_gmp.C
+++ b/rfc4251_gmp.C
@@ -1,9 +1,9 @@
/*
- * rfc4251_gmp.C -- implements mpint/gmp conversions for rfc4251::string
+ * rfc4251_gmp.C -- implements mpint/mpz conversions for rfc4251::mpint
*
* these functions need linking against libgmp
*
- * Copyright (C) 2013,2015 Timo Weingärtner <timo@tiwe.de>
+ * Copyright (C) 2013,2015,2021 Timo Weingärtner <timo@tiwe.de>
*
* This file is part of ssh-agent-filter.
*
@@ -25,36 +25,37 @@
namespace rfc4251 {
-string::string (mpz_srcptr x) {
- if (mpz_sgn(x) == 0)
- return;
-
- auto const import_positive = [] (mpz_srcptr x, std::vector<char> & value) {
+mpint::mpint (mpz_srcptr x) {
+ auto const import_positive = [] (mpz_srcptr x, std::vector<char> & buf) {
size_t bits{mpz_sizeinbase(x, 2)};
size_t bytes{(bits + 7) / 8};
size_t extrabyte{(bits % 8) == 0}; // need extra byte if MSB is 1 to keep it non-negative
- if (bytes + extrabyte > std::numeric_limits<uint32_t>::max())
- throw std::length_error{"32-bit limit for rfc4251::string exceeded"};
- value.resize(bytes + extrabyte);
- value[0] = 0;
- mpz_export(value.data() + extrabyte, nullptr, 1, 1, 1, 0, x);
+ check_length_against_limit(bytes + extrabyte);
+ buf.resize(bytes + extrabyte);
+ buf[0] = 0;
+ mpz_export(buf.data() + extrabyte, nullptr, 1, 1, 1, 0, x);
};
- if (mpz_sgn(x) == 1)
- import_positive(x, value);
- else {
- // handle two's complement: add 1, invert all bits
- mpz_class tmp{x};
- tmp += 1;
- import_positive(tmp.get_mpz_t(), value);
- for (auto & i : value)
- i ^= 0xff;
+
+ switch (mpz_sgn(x)) {
+ case 0:
+ return;
+ case 1:
+ import_positive(x, buf);
+ return;
+ default:
+ // handle two's complement: add 1, invert all bits
+ mpz_class tmp{x};
+ tmp += 1;
+ import_positive(tmp.get_mpz_t(), buf);
+ for (auto & i : buf)
+ i ^= 0xff;
}
}
-string::operator mpz_class () const {
- mpz_class ret;
- mpz_import(ret.get_mpz_t(), value.size(), 1, 1, 1, 0, value.data());
- if (mpz_sizeinbase(ret.get_mpz_t(), 2) == value.size() * 8) { // negative
+mpint::operator mpz_class () const {
+ mpz_class ret{};
+ mpz_import(ret.get_mpz_t(), buf.size(), 1, 1, 1, 0, buf.data());
+ if (mpz_sizeinbase(ret.get_mpz_t(), 2) == buf.size() * 8) { // negative
mpz_com(ret.get_mpz_t(), ret.get_mpz_t());
ret += 1;
mpz_neg(ret.get_mpz_t(), ret.get_mpz_t());
diff --git a/ssh-agent-filter.C b/ssh-agent-filter.C
index 639e55c..c658b62 100644
--- a/ssh-agent-filter.C
+++ b/ssh-agent-filter.C
@@ -347,14 +347,14 @@ std::optional<string> dissect_auth_data_ssh_cert (rfc4251::string const & data)
rfc4251::string nonce{datastream};
std::ostringstream key_to_be_signed{};
if (keytype_str == "ssh-rsa") {
- rfc4251::string e{datastream};
- rfc4251::string n{datastream};
+ rfc4251::mpint e{datastream};
+ rfc4251::mpint n{datastream};
key_to_be_signed << rfc4251::string{keytype_str} << e << n;
} else if (keytype_str == "ssh-dss") {
- rfc4251::string p{datastream};
- rfc4251::string q{datastream};
- rfc4251::string g{datastream};
- rfc4251::string y{datastream};
+ rfc4251::mpint p{datastream};
+ rfc4251::mpint q{datastream};
+ rfc4251::mpint g{datastream};
+ rfc4251::mpint y{datastream};
key_to_be_signed << rfc4251::string{keytype_str} << p << q << g << y;
} else if (keytype_str == "ecdsa-sha2-nistp256"
|| keytype_str == "ecdsa-sha2-nistp384"
@@ -474,7 +474,7 @@ string describe_sign_request (rfc4251::string const & data_to_be_signed) {
rfc4251::string handle_request (rfc4251::string const & r) {
io::stream<io::array_source> request{r.data(), r.size()};
rfc4251::string ret;
- io::stream<io::back_insert_device<vector<char>>> answer{ret.value};
+ io::stream<io::back_insert_device<vector<char>>> answer{ret.buf};
arm(request);
arm(answer);
rfc4251::byte request_code{request};