diff options
author | Karen Arutyunov <karen@codesynthesis.com> | 2017-05-25 21:12:03 +0300 |
---|---|---|
committer | Karen Arutyunov <karen@codesynthesis.com> | 2017-05-31 01:10:52 +0300 |
commit | d6a34b68d4667d4b99c1e76d63604a7bc1c9c3dd (patch) | |
tree | b3429ea208e804bdd4b7f80416510e509ff36181 /mod/mod-build-result.cxx | |
parent | 94b04d166c1041028571222b9931121b0f7dfded (diff) |
Add support for bbot agent authentication
Diffstat (limited to 'mod/mod-build-result.cxx')
-rw-r--r-- | mod/mod-build-result.cxx | 123 |
1 files changed, 103 insertions, 20 deletions
diff --git a/mod/mod-build-result.cxx b/mod/mod-build-result.cxx index ae6c5b1..41bfb2b 100644 --- a/mod/mod-build-result.cxx +++ b/mod/mod-build-result.cxx @@ -7,7 +7,9 @@ #include <odb/database.hxx> #include <odb/transaction.hxx> +#include <libbutl/openssl.hxx> #include <libbutl/sendmail.hxx> +#include <libbutl/fdstream.hxx> #include <libbutl/process-io.hxx> #include <libbutl/manifest-parser.hxx> #include <libbutl/manifest-serializer.hxx> @@ -226,6 +228,11 @@ handle (request& rq, response&) return true; } + auto print_args = [&trace, this] (const char* args[], size_t n) + { + l2 ([&]{trace << process_args {args, n};}); + }; + // Load and update the package build configuration (if present). // shared_ptr<build> b; @@ -245,28 +252,109 @@ handle (request& rq, response&) warn_expired ("non-matching timestamp"); else { - unforced = b->force == force_state::unforced; + // Check the challenge. + // + // If the challenge doesn't match expectations (probably due to the + // authentication settings change), then we log this case with the + // warning severity and respond with the 200 HTTP code as if the + // challenge is valid. The thinking is that we shouldn't alarm a + // law-abaiding agent and shouldn't provide any information to a + // malicious one. + // + auto warn_auth = [&rqm, &warn] (const string& d) + { + warn << "session '" << rqm.session << "' authentication failed: " << d; + }; + + bool auth (false); - // Don's send email for the success-to-success status change, unless the - // build was forced. + // Must both be present or absent. // - notify = !(rqm.result.status == result_status::success && - b->status && *b->status == rqm.result.status && unforced); + if (!b->agent_challenge != !rqm.challenge) + warn_auth (rqm.challenge + ? "unexpected challenge" + : "challenge is expected"); + else if (bot_agent_keys_ == nullptr) // Authentication is disabled. + auth = true; + else if (!b->agent_challenge) // Authentication is recently enabled. + warn_auth ("challenge is required now"); + else + { + assert (b->agent_fingerprint && rqm.challenge); + auto i (bot_agent_keys_->find (*b->agent_fingerprint)); + + // The agent's key is recently replaced. + // + if (i == bot_agent_keys_->end ()) + warn_auth ("agent's public key not found"); + else + { + try + { + openssl os (print_args, + path ("-"), fdstream_mode::text, 2, + options_->openssl (), "rsautl", + options_->openssl_option (), + "-verify", "-pubin", "-inkey", i->second); + + for (const auto& c: *rqm.challenge) + os.out.put (c); // Sets badbit on failure. + + os.out.close (); + + string s; + getline (os.in, s); + + bool v (os.in.eof ()); + os.in.close (); + + if (os.wait () && v) + { + auth = s == *b->agent_challenge; + + if (!auth) + warn_auth ("challenge mismatched"); + } + else // The signature is presumably meaningless. + warn_auth ("unable to verify challenge"); + } + catch (const system_error& e) + { + fail << "unable to verify challenge: " << e; + } + } + } - prev_status = move (b->status); + if (auth) + { + unforced = b->force == force_state::unforced; - b->state = build_state::built; - b->status = rqm.result.status; - b->force = force_state::unforced; + // Don's send email for the success-to-success status change, unless + // the build was forced. + // + notify = !(rqm.result.status == result_status::success && + b->status && *b->status == rqm.result.status && unforced); - // Mark the section as loaded, so results are updated. - // - b->results_section.load (); - b->results = move (rqm.result.results); + prev_status = move (b->status); + + b->state = build_state::built; + b->status = rqm.result.status; + b->force = force_state::unforced; + + // Cleanup the authentication data. + // + b->agent_fingerprint = nullopt; + b->agent_challenge = nullopt; + + // Mark the section as loaded, so results are updated. + // + b->results_section.load (); + b->results = move (rqm.result.results); - b->timestamp = timestamp::clock::now (); + b->timestamp = timestamp::clock::now (); - build_db_->update (b); + build_db_->update (b); + } } t.commit (); @@ -299,11 +387,6 @@ handle (request& rq, response&) ? *p->package_email : p->email); - auto print_args = [&trace, this] (const char* args[], size_t n) - { - l2 ([&]{trace << process_args {args, n};}); - }; - // Redirect the diagnostics to webserver error log. // // Note: if using this somewhere else, then need to factor out all this |