From cf653dbf7e60d8be75dec5917f04d7941a53e675 Mon Sep 17 00:00:00 2001 From: Francois Kritzinger Date: Tue, 20 Feb 2024 10:43:10 +0200 Subject: JWT: OpenSSL error handling --- mod/jwt.cxx | 66 ++++++++++++++++++++++++++++++++------------------- mod/mod-ci-github.cxx | 12 ++-------- 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/mod/jwt.cxx b/mod/jwt.cxx index 962ed1e..e70752e 100644 --- a/mod/jwt.cxx +++ b/mod/jwt.cxx @@ -102,11 +102,7 @@ gen_jwt (const options::openssl_options& o, // Create the signature. // - - // The signature (base64url-encoded). Will be left empty if openssl exits - // with a non-zero status. @@ - // - string s; + string s; // Signature (base64url-encoded). try { // Sign the concatenated header and payload using openssl. @@ -118,48 +114,68 @@ gen_jwt (const options::openssl_options& o, // Note that here we assume both output and diagnostics will fit into pipe // buffers and don't poll both with fdselect(). // + fdpipe errp (fdopen_pipe ()); // stderr pipe. + openssl os (path ("-"), // Read message from openssl::out. path ("-"), // Write output to openssl::in. - 2, // Diagnostics to stderr. + process::pipe (errp.in.get (), move (errp.out)), process_env (o.openssl (), o.openssl_envvar ()), "dgst", o.openssl_option (), "-sha256", "-sign", pk); - // @@ TODO redirect stderr to pipe/ofdstream. - + vector bs; // Binary signature (openssl output). + string et; // Openssl stderr text. try { + // In case of exception, skip and close input after output. + // + ifdstream in (os.in.release (), fdstream_mode::skip); + ofdstream out (os.out.release ()); + ifdstream err (move (errp.in)); + // Write the concatenated header and payload to openssl's input. // - os.out << h << '.' << p; - os.out.close (); + out << h << '.' << p; + out.close (); // Read the binary signature from openssl's output. // - vector bs (os.in.read_binary ()); - os.in.close (); + bs = in.read_binary (); + in.close (); + + if (!os.wait ()) + et = err.read_text (); + + err.close (); } - catch (const io_error&) + catch (const io_error& e) { - if (!os.wait ()) - throw system_error (); // @@ + // IO failure, child exit status doesn't matter. Just wait for the + // process completion and throw. + // + os.wait (); - // Fall through. + throw_generic_error (e.code ().value (), + ("unable to communicate with " + + o.openssl ().string () + ": " + e.what ()) + .c_str ()); } if (!os.wait ()) - throw system_error (); // @@ + { + throw_generic_error ( + EINVAL, + (o.openssl ().string () + " failed: " + et).c_str ()); + } s = base64url_encode (bs); } - catch () + catch (const process_error& e) { - // @@ TODO: catch all possible errors and translate to suitable - // system_error. + throw_generic_error ( + e.code ().value (), + ("unable to execute " + o.openssl ().string () + ": " + e.what ()) + .c_str ()); } - // Return the token, or empty if openssl exited with a non-zero status. @@ - // - return !s.empty () - ? h + '.' + p + '.' + s - : ""; + return h + '.' + p + '.' + s; // Return the token. } diff --git a/mod/mod-ci-github.cxx b/mod/mod-ci-github.cxx index 53ec9a7..7a641a0 100644 --- a/mod/mod-ci-github.cxx +++ b/mod/mod-ci-github.cxx @@ -73,6 +73,7 @@ // the webhook request to restrict access, otherwise we get access to all // repos covered by the installation if installed on an organisation for // example. +// using namespace std; using namespace butl; @@ -245,20 +246,11 @@ handle (request& rq, response& rs) chrono::minutes (options_->ci_github_jwt_validity_period ()), chrono::seconds (60))); - if (jwt.empty ()) - fail << "unable to generate JWT: " << options_->openssl () - << " failed"; - cout << "JWT: " << jwt << endl; } catch (const system_error& e) { - fail << "unable to generate JWT: unable to execute " - << options_->openssl () << ": " << e.what (); - } - catch (const std::exception& e) - { - fail << "unable to generate JWT: " << e; + fail << "unable to generate JWT: [" << e.code () << "] " << e.what (); } return true; -- cgit v1.1