// file : openssl/agent/pkcs11/url.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2019 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #include #include #include // strtoull() #include // 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:: translate_scheme (const string_type& /* url */, string_type&& scheme, optional& authority, optional& path, optional& /* query */, optional& 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 */, const optional& /* path */, const optional& /* query */, const optional& /* 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 (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>, // but this would uglify a client code quite a bit and make it less // efficient. // static pair 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& 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 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 ( 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 (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& 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 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"); } } } }