rgw::auth::Engine::result_t
rgw::auth::Strategy::authenticate(const req_state* const s) const
{
+ int previous_error = 0;
for (const stack_item_t& kv : auth_stack) {
const rgw::auth::Engine& engine = kv.first;
const auto& policy = kv.second;
rgw::auth::Engine::result_t res;
try {
res = engine.authenticate(s);
- } catch (int err) {
- /* NOP */
+ } catch (const int err) {
+ previous_error = err;
}
const auto& applier = res.first;
case Control::SUFFICIENT:
/* Just try next. */
continue;
+ case Control::FALLBACK:
+ throw previous_error;
default:
/* Huh, memory corruption? */
abort();
class Strategy : public Engine {
public:
/* Specifiers controlling what happens when an associated engine fails.
- * The names and semantic has been borrowed from libpam. */
+ * The names and semantic has been borrowed mostly from libpam. */
enum class Control {
/* Failure of an engine injected with the REQUISITE specifier aborts
* the whole authentication process immediately. No other engine will
* doesn't abort it - there will be fall-back to following engine
* it the one that failed wasn't the last. */
SUFFICIENT,
+
+ /* Like SUFFICIENT with the exception that on failure the reason code
+ * is not overridden. Instead, it's taken directly from the last tried
+ * non-FALLBACK engine. If there was no previous non-FALLBACK engine
+ * in a Strategy, then the result_t::deny(reason = -EACCES) is used. */
+ FALLBACK,
};
Engine::result_t authenticate(const req_state* s) const override final;