aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2018-10-08 23:01:16 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2018-10-10 23:54:18 +0300
commit61349dcf5fbfeab888ea345ebec3d887777a2782 (patch)
tree958d7f34185d987b21ae24ad137b7a2e0a1593ec
parenteb58dc7c9ec9877c645585b7fb163d2bcc251b5d (diff)
Add support for openssl qualified options
-rw-r--r--bpkg/auth.cxx47
-rw-r--r--bpkg/common.cli32
-rw-r--r--bpkg/options-types.hxx58
-rw-r--r--bpkg/repository-signing.cli11
-rw-r--r--bpkg/types-parsers.hxx9
-rw-r--r--bpkg/types-parsers.txx69
6 files changed, 200 insertions, 26 deletions
diff --git a/bpkg/auth.cxx b/bpkg/auth.cxx
index af49f37..48ba56f 100644
--- a/bpkg/auth.cxx
+++ b/bpkg/auth.cxx
@@ -26,6 +26,13 @@ using namespace butl;
namespace bpkg
{
+ static const string openssl_rsautl ("rsautl");
+ static const string openssl_x509 ("x509");
+
+ const char* openssl_commands[] = {openssl_rsautl.c_str (),
+ openssl_x509.c_str (),
+ nullptr};
+
// Print process command line.
//
static void
@@ -139,12 +146,15 @@ namespace bpkg
dr << ": " << *e;
};
+ const path& openssl_path (co.openssl ()[openssl_x509]);
+ const strings& openssl_opts (co.openssl_option ()[openssl_x509]);
+
try
{
openssl os (print_command,
fdstream_mode::text, fdstream_mode::text, 2,
- co.openssl (), "x509",
- co.openssl_option (), "-sha256", "-noout", "-fingerprint");
+ openssl_path, openssl_x509,
+ openssl_opts, "-sha256", "-noout", "-fingerprint");
os.out << pem;
os.out.close ();
@@ -175,7 +185,7 @@ namespace bpkg
}
catch (const process_error& e)
{
- error << "unable to execute " << co.openssl () << ": " << e;
+ error << "unable to execute " << openssl_path << ": " << e;
// Fall through.
}
@@ -223,6 +233,9 @@ namespace bpkg
dr << ": " << *e;
};
+ const path& openssl_path (co.openssl ()[openssl_x509]);
+ const strings& openssl_opts (co.openssl_option ()[openssl_x509]);
+
try
{
// The order of the options we pass to openssl determines the order in
@@ -247,12 +260,8 @@ namespace bpkg
openssl os (
print_command,
fdstream_mode::text, fdstream_mode::text, 2,
- co.openssl (), "x509",
- co.openssl_option (),
- "-noout",
- "-subject",
- "-dates",
- "-email",
+ openssl_path, openssl_x509,
+ openssl_opts, "-noout", "-subject", "-dates", "-email",
// Previously we have used "RFC2253,sep_multiline" format to display
// the requested fields, but that resulted in some undesirable
@@ -448,7 +457,7 @@ namespace bpkg
}
catch (const process_error& e)
{
- error << "unable to execute " << co.openssl () << ": " << e;
+ error << "unable to execute " << openssl_path << ": " << e;
// Fall through.
}
@@ -818,12 +827,15 @@ namespace bpkg
dr << ": " << *e;
};
+ const path& openssl_path (co.openssl ()[openssl_rsautl]);
+ const strings& openssl_opts (co.openssl_option ()[openssl_rsautl]);
+
try
{
openssl os (print_command,
path ("-"), fdstream_mode::text, 2,
- co.openssl (), "rsautl",
- co.openssl_option (), "-verify", "-certin", "-inkey", f);
+ openssl_path, openssl_rsautl,
+ openssl_opts, "-verify", "-certin", "-inkey", f);
for (const auto& c: sm.signature)
os.out.put (c); // Sets badbit on failure.
@@ -851,7 +863,7 @@ namespace bpkg
}
catch (const process_error& e)
{
- error << "unable to execute " << co.openssl () << ": " << e;
+ error << "unable to execute " << openssl_path << ": " << e;
// Fall through.
}
@@ -903,12 +915,15 @@ namespace bpkg
dr << ": " << *e;
};
+ const path& openssl_path (co.openssl ()[openssl_rsautl]);
+ const strings& openssl_opts (co.openssl_option ()[openssl_rsautl]);
+
try
{
openssl os (print_command,
fdstream_mode::text, path ("-"), 2,
- co.openssl (), "rsautl",
- co.openssl_option (), "-sign", "-inkey", key_name);
+ openssl_path, openssl_rsautl,
+ openssl_opts, "-sign", "-inkey", key_name);
os.out << sha256sum;
os.out.close ();
@@ -925,7 +940,7 @@ namespace bpkg
}
catch (const process_error& e)
{
- error << "unable to execute " << co.openssl () << ": " << e;
+ error << "unable to execute " << openssl_path << ": " << e;
// Fall through.
}
diff --git a/bpkg/common.cli b/bpkg/common.cli
index f001608..a8c363e 100644
--- a/bpkg/common.cli
+++ b/bpkg/common.cli
@@ -217,21 +217,43 @@ namespace bpkg
multiple tar options."
}
- path --openssl = "openssl"
+ // NOTE: when adding support for a new openssl command, don't forget to
+ // update the openssl_commands qualifier list.
+ //
+ qualified_option<openssl_commands, path> --openssl = "openssl"
{
"<path>",
"The openssl program to be used for crypto operations. You can also
specify additional options that should be passed to the openssl
program with \cb{--openssl-option}. If the openssl program is not
- explicitly specified, then \cb{bpkg} will use \cb{openssl} by default."
+ explicitly specified, then \cb{bpkg} will use \cb{openssl} by default.
+
+ The \cb{--openssl*} values can be optionally qualified with the openssl
+ command in the \c{\i{command}\b{:}\i{value}} form. This makes the value
+ only applicable to the specific command, for example:
+
+ \
+ bpkg rep-create \
+ --openssl rsautl:/path/to/openssl \
+ --openssl-option rsautl:-engine \
+ --openssl-option rsautl:pkcs11 \
+ ...
+ \
+
+ An unqualified value that contains a colon can be specified as
+ qualified with an empty command, for example, \cb{--openssl
+ :C:\\bin\\openssl}. To see openssl commands executed by \cb{bpkg}, use
+ the verbose mode (\cb{-v} option)."
}
- strings --openssl-option
+ qualified_option<openssl_commands, strings> --openssl-option
{
"<opt>",
"Additional option to be passed to the openssl program. See
- \cb{--openssl} for more information on the openssl program. Repeat this
- option to specify multiple openssl options."
+ \cb{--openssl} for more information on the openssl program.
+ The values can be optionally qualified with the openssl command,
+ as discussed in \cb{--openssl}. Repeat this option to specify
+ multiple openssl options."
}
bpkg::auth --auth = bpkg::auth::remote
diff --git a/bpkg/options-types.hxx b/bpkg/options-types.hxx
index 373ef5e..3438be1 100644
--- a/bpkg/options-types.hxx
+++ b/bpkg/options-types.hxx
@@ -5,6 +5,10 @@
#ifndef BPKG_OPTIONS_TYPES_HXX
#define BPKG_OPTIONS_TYPES_HXX
+#include <map>
+#include <cassert>
+#include <utility> // move()
+
namespace bpkg
{
enum class auth
@@ -13,6 +17,60 @@ namespace bpkg
remote,
all
};
+
+ // Qualified options.
+ //
+ // An option that uses this type can have its values qualified using the
+ // <qualifier>:<value> form, for example, '--option foo:bar' An unqualified
+ // value that contains a colon can be specified as qualified with an empty
+ // qualifier, for example, '--option :http://example.org'. Unqualified
+ // values apply to all the qualifiers in the order specified.
+ //
+ // The second template argument is a NULL-terminated list of valid qualifier
+ // strings, for example:
+ //
+ // const char* option_qualifiers[] = {"foo", "bar", nullptr};
+ //
+ template <const char* Q[], typename V>
+ class qualified_option: public std::map<string, V>
+ {
+ public:
+ using base_type = std::map<string, V>;
+
+ template <typename T>
+ explicit
+ qualified_option (T v) {this->emplace ("", std::move (v));}
+
+ qualified_option (): qualified_option (V ()) {}
+
+ using base_type::operator[];
+
+ const V&
+ operator[] (const string& q) const
+ {
+ auto verify = [&q] ()
+ {
+ for (const char** p (Q); *p != nullptr; ++p)
+ {
+ if (q == *p)
+ return true;
+ }
+ return q.empty ();
+ };
+
+ assert (verify ());
+
+ typename base_type::const_iterator i (this->find (q));
+
+ if (i == this->end ())
+ i = this->find ("");
+
+ assert (i != this->end ());
+ return i->second;
+ }
+ };
+
+ extern const char* openssl_commands[];
}
#endif // BPKG_OPTIONS_TYPES_HXX
diff --git a/bpkg/repository-signing.cli b/bpkg/repository-signing.cli
index 0fb59c9..776d82b 100644
--- a/bpkg/repository-signing.cli
+++ b/bpkg/repository-signing.cli
@@ -190,13 +190,14 @@ media, store it in a secure, offline location, and remove the key from the
build machine.
To sign the repository with Yubikey specify the following options instead of
-just \cb{--key} as at step 4 (\c{\"SIGN key\"} is the name for slot \c{9c}):
+just \cb{--key} as at step 4 (\c{\"SIGN key\"} is the label for the slot
+\c{9c} private key):
\
-bpkg rep-create \
- --openssl-option -engine --openssl-option pkcs11 \
- --openssl-option -keyform --openssl-option engine \
- --key \"label_SIGN key\" /path/to/repository
+bpkg rep-create \
+ --openssl-option rsautl:-engine --openssl-option rsautl:pkcs11 \
+ --openssl-option rsautl:-keyform --openssl-option rsautl:engine \
+ --key \"pkcs11:object=SIGN%20key\" /path/to/repository
\
||
diff --git a/bpkg/types-parsers.hxx b/bpkg/types-parsers.hxx
index 3425e9d..24cc92d 100644
--- a/bpkg/types-parsers.hxx
+++ b/bpkg/types-parsers.hxx
@@ -46,7 +46,16 @@ namespace bpkg
static void
parse (repository_type&, bool&, scanner&);
};
+
+ template <const char* Q[], typename V>
+ struct parser<qualified_option<Q, V>>
+ {
+ static void
+ parse (qualified_option<Q, V>&, bool&, scanner&);
+ };
}
}
+#include <bpkg/types-parsers.txx>
+
#endif // BPKG_TYPES_PARSERS_HXX
diff --git a/bpkg/types-parsers.txx b/bpkg/types-parsers.txx
new file mode 100644
index 0000000..7a40b0e
--- /dev/null
+++ b/bpkg/types-parsers.txx
@@ -0,0 +1,69 @@
+// file : bpkg/types-parsers.txx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+namespace bpkg
+{
+ namespace cli
+ {
+ template <const char* Q[], typename V>
+ void parser<qualified_option<Q, V>>::
+ parse (qualified_option<Q, V>& x, bool& xs, scanner& s)
+ {
+ xs = true;
+ const char* o (s.next ());
+
+ if (!s.more ())
+ throw missing_value (o);
+
+ string v (s.next ());
+
+ // Extract the qualifier from the option value.
+ //
+ string qv;
+ size_t n (v.find (':'));
+
+ if (n != string::npos)
+ {
+ const char** q (Q);
+ for (; *q != nullptr; ++q)
+ {
+ if (v.compare (0, n, *q) == 0)
+ {
+ qv = *q;
+ v = string (v, n + 1);
+ break;
+ }
+ }
+
+ // Fail it the qualifier is not recognized, unless it is a special
+ // empty qualifier.
+ //
+ if (*q == nullptr && n != 0)
+ throw invalid_value (o, v);
+ }
+
+ // Parse the value for the extracted (possibly empty) qualifier.
+ //
+ int ac (2);
+ char* av[] = {const_cast<char*> (o), const_cast<char*> (v.c_str ())};
+ bool dummy;
+
+ {
+ argv_scanner s (0, ac, av);
+ parser<V>::parse (x[qv], dummy, s);
+ }
+
+ // Parse an unqualified value for all qualifiers.
+ //
+ if (qv.empty ())
+ {
+ for (const char** q (Q); *q != nullptr; ++q)
+ {
+ argv_scanner s (0, ac, av);
+ parser<V>::parse (x[*q], dummy, s);
+ }
+ }
+ }
+ }
+}