aboutsummaryrefslogtreecommitdiff
path: root/bpkg/auth.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'bpkg/auth.cxx')
-rw-r--r--bpkg/auth.cxx209
1 files changed, 156 insertions, 53 deletions
diff --git a/bpkg/auth.cxx b/bpkg/auth.cxx
index 0693abc..191da0a 100644
--- a/bpkg/auth.cxx
+++ b/bpkg/auth.cxx
@@ -7,11 +7,11 @@
#include <limits> // numeric_limits
#include <iterator> // ostreambuf_iterator
-#include <libbutl/sha256.mxx>
-#include <libbutl/base64.mxx>
-#include <libbutl/openssl.mxx>
-#include <libbutl/timestamp.mxx>
-#include <libbutl/filesystem.mxx>
+#include <libbutl/base64.hxx>
+#include <libbutl/openssl.hxx>
+#include <libbutl/timestamp.hxx>
+#include <libbutl/filesystem.hxx>
+#include <libbutl/semantic-version.hxx>
#include <bpkg/package.hxx>
#include <bpkg/package-odb.hxx>
@@ -23,11 +23,15 @@ using namespace butl;
namespace bpkg
{
- static const string openssl_rsautl ("rsautl");
- static const string openssl_x509 ("x509");
-
- const char* openssl_commands[3] = {openssl_rsautl.c_str (),
- openssl_x509.c_str (),
+ static const string openssl_version_cmd ("version");
+ static const string openssl_pkeyutl_cmd ("pkeyutl");
+ static const string openssl_rsautl_cmd ("rsautl");
+ static const string openssl_x509_cmd ("x509");
+
+ const char* openssl_commands[5] = {openssl_version_cmd.c_str (),
+ openssl_pkeyutl_cmd.c_str (),
+ openssl_rsautl_cmd.c_str (),
+ openssl_x509_cmd.c_str (),
nullptr};
// Print process command line.
@@ -39,6 +43,73 @@ namespace bpkg
print_process (args, n);
}
+ // Query the openssl information and return the openssl version. Cache the
+ // version on the first function call. Fail on the underlying process and IO
+ // error. Return the 0.0.0 version if unable to parse the openssl stdout.
+ //
+ static optional<semantic_version> openssl_ver;
+
+ static const semantic_version&
+ openssl_version (const common_options& co)
+ {
+ const path& openssl_path (co.openssl ()[openssl_version_cmd]);
+
+ if (!openssl_ver)
+ try
+ {
+ optional<openssl_info> oi (
+ openssl::info (print_command, 2, openssl_path));
+
+ openssl_ver = (oi && oi->name == "OpenSSL"
+ ? move (oi->version)
+ : semantic_version ());
+ }
+ catch (const process_error& e)
+ {
+ fail << "unable to execute " << openssl_path << ": " << e << endf;
+ }
+ catch (const io_error& e)
+ {
+ fail << "unable to read '" << openssl_path << "' output: " << e
+ << endf;
+ }
+
+ return *openssl_ver;
+ }
+
+ // Return true if the openssl version is greater or equal to 3.0.0 and so
+ // pkeyutl needs to be used instead of rsautl.
+ //
+ // Note that openssl 3.0.0 deprecates rsautl in favor of pkeyutl.
+ //
+ // Also note that pkeyutl is only implemented in openssl version 1.0.0 and
+ // its -verifyrecover mode is broken in the [1.1.1 1.1.1d] version range
+ // (see the 'pkeyutl -verifyrecover error "input data too long to be a
+ // hash"' issue report for details).
+ //
+ static inline bool
+ use_openssl_pkeyutl (const common_options& co)
+ {
+ return openssl_version (co) >= semantic_version {3, 0, 0};
+ }
+
+ // Return true if some openssl commands (openssl x509 -fingerprint, etc) may
+ // issue the 'Reading certificate from stdin since no -in or -new option is
+ // given' warning. This is the case for the openssl version in the [3.2.0
+ // 3.3.0) range (see GH issue #353 for details).
+ //
+ // Note that there is no easy way to suppress this warning on Windows and
+ // thus we don't define this function there.
+ //
+#ifndef _WIN32
+ static inline bool
+ openssl_warn_stdin (const common_options& co)
+ {
+ const semantic_version& v (openssl_version (co));
+ return v >= semantic_version {3, 2, 0} && v < semantic_version {3, 3, 0};
+ }
+#endif
+
// Find the repository location prefix that ends with the version component.
// We consider all repositories under this location to be related.
//
@@ -143,15 +214,25 @@ namespace bpkg
dr << ": " << *e;
};
- const path& openssl_path (co.openssl ()[openssl_x509]);
- const strings& openssl_opts (co.openssl_option ()[openssl_x509]);
+ const path& openssl_path (co.openssl ()[openssl_x509_cmd]);
+ const strings& openssl_opts (co.openssl_option ()[openssl_x509_cmd]);
try
{
openssl os (print_command,
fdstream_mode::text, fdstream_mode::text, 2,
- openssl_path, openssl_x509,
- openssl_opts, "-sha256", "-noout", "-fingerprint");
+ openssl_path, openssl_x509_cmd,
+ openssl_opts,
+ "-sha256",
+ "-noout",
+ "-fingerprint"
+#ifndef _WIN32
+ ,
+ (openssl_warn_stdin (co)
+ ? cstrings ({"-in", "/dev/stdin"})
+ : cstrings ())
+#endif
+ );
os.out << pem;
os.out.close ();
@@ -160,21 +241,32 @@ namespace bpkg
getline (os.in, s);
os.in.close ();
- try
+ if (os.wait ())
{
- const size_t n (19);
-
- if (os.wait () &&
- s.size () > n && s.compare (0, n, "SHA256 Fingerprint=") == 0)
+ // Normally the output is:
+ //
+ // SHA256 Fingerprint=<fingerprint>
+ //
+ // But it can be translated and SHA spelled in lower case (LC_ALL=C
+ // doesn't seem to help in some cases).
+ //
+ if (icasecmp (s, "SHA256", 6) == 0)
{
- string fp (s, n);
- string ab (fingerprint_to_sha256 (fp, 16));
- return {move (fp), move (ab)};
+ size_t p (s.find ('='));
+ if (p != string::npos)
+ {
+ try
+ {
+ string fp (s, p + 1);
+ string ab (fingerprint_to_sha256 (fp, 16));
+ return {move (fp), move (ab)};
+ }
+ catch (const invalid_argument&)
+ {
+ }
+ }
}
}
- catch (const invalid_argument&)
- {
- }
calc_failed ();
@@ -230,8 +322,8 @@ namespace bpkg
dr << ": " << *e;
};
- const path& openssl_path (co.openssl ()[openssl_x509]);
- const strings& openssl_opts (co.openssl_option ()[openssl_x509]);
+ const path& openssl_path (co.openssl ()[openssl_x509_cmd]);
+ const strings& openssl_opts (co.openssl_option ()[openssl_x509_cmd]);
try
{
@@ -257,7 +349,7 @@ namespace bpkg
openssl os (
print_command,
fdstream_mode::text, fdstream_mode::text, 2,
- openssl_path, openssl_x509,
+ openssl_path, openssl_x509_cmd,
openssl_opts, "-noout", "-subject", "-dates", "-email",
// Previously we have used "RFC2253,sep_multiline" format to display
@@ -289,6 +381,13 @@ namespace bpkg
// sep_multiline - display field per line.
//
"-nameopt", "utf8,esc_ctrl,dump_nostr,dump_der,sname,sep_multiline"
+
+#ifndef _WIN32
+ ,
+ (openssl_warn_stdin (co)
+ ? cstrings ({"-in", "/dev/stdin"})
+ : cstrings ())
+#endif
);
// We unset failbit to provide the detailed error description (which
@@ -561,7 +660,6 @@ namespace bpkg
//
static shared_ptr<certificate>
auth_cert (const common_options& co,
- const dir_path& conf,
database& db,
const optional<string>& pem,
const repository_location& rl,
@@ -603,7 +701,7 @@ namespace bpkg
//
if (pem)
{
- path f (conf / certs_dir / path (cert->id + ".pem"));
+ path f (db.config_orig / certs_dir / path (cert->id + ".pem"));
try
{
@@ -624,6 +722,7 @@ namespace bpkg
shared_ptr<const certificate>
authenticate_certificate (const common_options& co,
const dir_path* conf,
+ database* db,
const optional<string>& pem,
const repository_location& rl,
const optional<string>& dependent_trust)
@@ -633,15 +732,12 @@ namespace bpkg
if (co.trust_no () && co.trust_yes ())
fail << "--trust-yes and --trust-no are mutually exclusive";
- if (conf != nullptr && conf->empty ())
- conf = exists (bpkg_dir) ? &current_dir : nullptr;
-
- assert (conf == nullptr || !conf->empty ());
-
shared_ptr<certificate> r;
if (conf == nullptr)
{
+ assert (db == nullptr);
+
// If we have no configuration, go straight to authenticating a new
// certificate.
//
@@ -650,20 +746,21 @@ namespace bpkg
? auth_real (co, fp, *pem, rl, dependent_trust).cert
: auth_dummy (co, fp.abbreviated, rl);
}
- else if (transaction::has_current ())
+ else if (db != nullptr)
{
+ assert (transaction::has_current ());
+
r = auth_cert (co,
- *conf,
- transaction::current ().database (),
+ *db,
pem,
rl,
dependent_trust);
}
else
{
- database db (open (*conf, trace));
+ database db (*conf, trace, false /* pre_attach */);
transaction t (db);
- r = auth_cert (co, *conf, db, pem, rl, dependent_trust);
+ r = auth_cert (co, db, pem, rl, dependent_trust);
t.commit ();
}
@@ -680,11 +777,6 @@ namespace bpkg
{
tracer trace ("authenticate_repository");
- if (conf != nullptr && conf->empty ())
- conf = exists (bpkg_dir) ? &current_dir : nullptr;
-
- assert (conf == nullptr || !conf->empty ());
-
path f;
auto_rmfile rm;
@@ -699,7 +791,7 @@ namespace bpkg
try
{
- rm = tmp_file ("cert");
+ rm = tmp_file (conf != nullptr ? *conf : empty_dir_path, "cert");
f = rm.path;
ofdstream ofs (f);
@@ -825,15 +917,22 @@ namespace bpkg
dr << ": " << *e;
};
- const path& openssl_path (co.openssl ()[openssl_rsautl]);
- const strings& openssl_opts (co.openssl_option ()[openssl_rsautl]);
+ bool ku (use_openssl_pkeyutl (co));
+ const string& cmd (ku ? openssl_pkeyutl_cmd : openssl_rsautl_cmd);
+
+ const path& openssl_path (co.openssl ()[cmd]);
+ const strings& openssl_opts (co.openssl_option ()[cmd]);
try
{
openssl os (print_command,
path ("-"), fdstream_mode::text, 2,
- openssl_path, openssl_rsautl,
- openssl_opts, "-verify", "-certin", "-inkey", f);
+ openssl_path, cmd,
+ openssl_opts,
+ ku ? "-verifyrecover" : "-verify",
+ "-certin",
+ "-inkey",
+ f);
for (const auto& c: sm.signature)
os.out.put (c); // Sets badbit on failure.
@@ -914,14 +1013,18 @@ namespace bpkg
dr << ": " << *e;
};
- const path& openssl_path (co.openssl ()[openssl_rsautl]);
- const strings& openssl_opts (co.openssl_option ()[openssl_rsautl]);
+ const string& cmd (use_openssl_pkeyutl (co)
+ ? openssl_pkeyutl_cmd
+ : openssl_rsautl_cmd);
+
+ const path& openssl_path (co.openssl ()[cmd]);
+ const strings& openssl_opts (co.openssl_option ()[cmd]);
try
{
openssl os (print_command,
fdstream_mode::text, path ("-"), 2,
- openssl_path, openssl_rsautl,
+ openssl_path, cmd,
openssl_opts, "-sign", "-inkey", key_name);
os.out << sha256sum;