diff options
Diffstat (limited to 'openssl/agent/pkcs11/url.cxx')
-rw-r--r-- | openssl/agent/pkcs11/url.cxx | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/openssl/agent/pkcs11/url.cxx b/openssl/agent/pkcs11/url.cxx new file mode 100644 index 0000000..0b9c3ac --- /dev/null +++ b/openssl/agent/pkcs11/url.cxx @@ -0,0 +1,303 @@ +// file : openssl/agent/pkcs11/url.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <openssl/agent/pkcs11/url.hxx> + +#include <cerrno> +#include <cstdlib> // strtoull() +#include <iterator> // back_inserter() + +namespace openssl +{ + namespace agent + { + namespace pkcs11 + { + using namespace std; + + // Convenience functions. + // + static uint64_t + parse_uint64 (const string& s, + uint64_t min, + uint64_t max, + const char* what) + { + if (s[0] != '-' && s[0] != '+') // strtoull() allows these. + { + const char* b (s.c_str ()); + char* e (nullptr); + uint64_t v (strtoull (b, &e, 10)); // Can't throw. + + if (errno != ERANGE && e == b + s.size () && v >= min && v <= max) + return v; + } + + throw invalid_argument (string ("invalid ") + what + " '" + s + "'"); + } + + // url_traits + // + optional<url_traits::scheme_type> url_traits:: + translate_scheme (const string_type& /* url */, + string_type&& scheme, + optional<authority_type>& authority, + optional<path_type>& path, + optional<string_type>& /* query */, + optional<string_type>& fragment, + bool& rootless) + { + // If something is wrong with the URL leave the basic_url constructor + // to throw. + // + if (scheme.empty ()) + return nullopt; + + if (casecmp (scheme, "pkcs11") != 0) + throw invalid_argument ("invalid scheme"); + + if (authority) + throw invalid_argument ("unexpected authority"); + + if (path && (!rootless || path->find ('/') != string::npos)) + throw invalid_argument ("one-level path expected"); + + if (fragment) + throw invalid_argument ("unexpected fragment"); + + return move (scheme); + } + + url_traits::string_type url_traits:: + translate_scheme (string_type& /* url */, + const scheme_type& scheme, + const optional<authority_type>& /* authority */, + const optional<path_type>& /* path */, + const optional<string_type>& /* query */, + const optional<string_type>& /* fragment */, + bool /* rootless */) + { + return scheme; + } + + url_traits::path_type url_traits:: + translate_path (string_type&& path) + { + return move (path); + } + + url_traits::string_type url_traits:: + translate_path (const path_type& path) + { + return path; + } + + // library_version + // + library_version:: + library_version (const string& s) + { + auto num = [] (const string& s, const char* what) + { + return static_cast<unsigned char> (parse_uint64 (s, 0, 255, what)); + }; + + size_t p (s.find ('.')); + + if (p != string::npos) + { + major = num (string (s, 0, p), "library major version"); + minor = num (string (s, p + 1), "library minor version"); + } + else + { + major = num (s, "library major version"); + minor = 0; + } + } + + // Parse the attribute name=value representation. The value can contain + // binary data. + // + // It would probably be cleaner to return pair<string, vector<char>>, + // but this would uglify a client code quite a bit and make it less + // efficient. + // + static pair<string, string> + attribute (const string& s, size_t b, size_t n) + { + size_t i (b); + size_t e (b + n); + + for (; i != e && s[i] != '='; ++i) ; + + if (i == e) + throw invalid_argument ( + "no value for attribute '" + string (s, b, n) + "'"); + + string a; + url::decode (s.begin () + b, s.begin () + i, back_inserter (a)); + + string v; + url::decode (s.begin () + i + 1, s.begin () + e, back_inserter (v)); + + return make_pair (move (a), move (v)); + } + + // identity + // + identity:: + identity (const url& u) + { + const optional<string>& path (u.path); + + // If URL path component is absent then create the identity that + // matches all PKCS#11 entities in the system. + // + if (!path) + return; + + for (size_t b (0), e (0), n; (n = next_word (*path, b, e, ';')); ) + { + pair<string, string> a (attribute (*path, b, n)); + + const string& an (a.first); + string& av (a.second); + + auto set = [&an] (auto& attr, auto&& val) + { + if (attr) + throw invalid_argument ("duplicate attribute '" + an + "'"); + + attr = move (val); + }; + + // Module. + // + if (an == "library-manufacturer") + set (library_manufacturer, move (av)); + else if (an == "library-version") + set (library_version, library_version_type (av)); + else if (an == "library-description") + set (library_description, move (av)); + + // Slot. + // + else if (an == "slot-id") + set (slot_id, + static_cast<unsigned long> ( + parse_uint64 (av, 0, ~0UL, "slot-id attribute value"))); + else if (an == "slot-manufacturer") + set (slot_manufacturer, move (av)); + else if (an == "slot-description") + set (slot_description, move (av)); + + // Token. + // + else if (an == "serial") + set (serial, move (av)); + else if (an == "token") + set (token, move (av)); + else if (an == "model") + set (model, move (av)); + else if (an == "manufacturer") + set (manufacturer, move (av)); + + // Storage object. + // + else if (an == "id") + set (id, vector<unsigned char> (av.begin (), av.end ())); + else if (an == "object") + set (object, move (av)); + else if (an == "type") + set (type, move (av)); + else + throw invalid_argument ("unknown attribute '" + an + "'"); + } + } + + // access + // + access:: + access (const url& u) + { + const optional<string>& query (u.query); + + // If URL query component is absent then create an object that + // provides no access attributes. + // + if (!query) + return; + + for (size_t b (0), e (0), n; (n = next_word (*query, b, e, ';')); ) + { + pair<string, string> a (attribute (*query, b, n)); + + const string& an (a.first); + string& av (a.second); + + auto set = [&an] (auto& attr, auto&& val) + { + if (attr) + throw invalid_argument ("duplicate attribute '" + an + "'"); + + attr = move (val); + }; + + // Note that unrecognized attributes are ignored (see the traits + // class notes for details). + // + if (an == "pin-source") + set (pin_source, move (av)); + else if (an == "pin-value") + set (pin_value, move (av)); + else if (an == "module-name") + { + try + { + path p (av); + + if (!p.empty () && p.simple ()) + { + set (module_name, move (p)); + continue; + } + + // Fall through. + } + catch (const invalid_path& e) + { + // Fall through. + } + + throw invalid_argument ( + "invalid value '" + av + "' for module-name attribute"); + } + else if (an == "module-path") + { + try + { + path p (move (av)); + + if (p.relative ()) + throw invalid_argument ("relative path '" + p.string () + + "' for module-path attribute"); + + set (module_path, move (p)); + } + catch (const invalid_path& e) + { + throw invalid_argument ( + "invalid path '" + e.path + "' for module-path attribute"); + } + } + } + + if (pin_source && pin_value) + throw invalid_argument ( + "both pin-source and pin-value attributes specified"); + } + } + } +} |