copy_fail_family: skip DIRTYFAIL typed prompt under --i-know

The vendored DIRTYFAIL exploits call typed_confirm("DIRTYFAIL"), which
reads stdin interactively. SKELETONKEY already gates --exploit/--auto
behind --i-know, so the prompt is redundant and deadlocks non-interactive
runs like `skeletonkey --auto --i-know`.

Add a dirtyfail_assume_yes flag, forwarded from skeletonkey_ctx.authorized
by the bridge layer's apply_ctx(). When set, typed_confirm() auto-satisfies
its gate and logs that it did so.

The YES_BREAK_SSH self-lockout guard is exempt — it protects the
operator's own access rather than gating authorization, so it still
requires an interactive answer.

Standalone DIRTYFAIL builds are unchanged: the flag defaults false.
This commit is contained in:
2026-05-22 16:49:15 -04:00
parent 33f81aeb69
commit 3b287f84f0
3 changed files with 28 additions and 0 deletions
@@ -33,6 +33,12 @@ static void apply_ctx(const struct skeletonkey_ctx *ctx)
dirtyfail_use_color = !ctx->no_color; dirtyfail_use_color = !ctx->no_color;
dirtyfail_active_probes = ctx->active_probe; dirtyfail_active_probes = ctx->active_probe;
dirtyfail_json = ctx->json; dirtyfail_json = ctx->json;
/* Forward the --i-know authorization gate. SKELETONKEY already
* blocks --exploit/--auto unless --i-know is passed, so by the time
* a DIRTYFAIL exploit callback runs, authorization is established.
* This lets typed_confirm() skip its (now redundant) interactive
* prompt, which otherwise deadlocks `skeletonkey --auto --i-know`. */
dirtyfail_assume_yes = ctx->authorized;
/* dirtyfail_no_revert is intentionally not driven from ctx — /* dirtyfail_no_revert is intentionally not driven from ctx —
* it's a debug knob; default stays off. */ * it's a debug knob; default stays off. */
} }
+14
View File
@@ -31,6 +31,7 @@ bool dirtyfail_use_color = true;
bool dirtyfail_active_probes = false; bool dirtyfail_active_probes = false;
bool dirtyfail_no_revert = false; bool dirtyfail_no_revert = false;
bool dirtyfail_json = false; bool dirtyfail_json = false;
bool dirtyfail_assume_yes = false;
static void vlog(FILE *out, const char *prefix, const char *color, static void vlog(FILE *out, const char *prefix, const char *color,
const char *fmt, va_list ap) const char *fmt, va_list ap)
@@ -226,6 +227,19 @@ size_t build_authenc_keyblob(unsigned char *out,
bool typed_confirm(const char *expected) bool typed_confirm(const char *expected)
{ {
/* When the caller has already cleared an explicit authorization gate
* (SKELETONKEY's --i-know, forwarded via dirtyfail_assume_yes), the
* DIRTYFAIL typed prompt is redundant and would deadlock non-interactive
* runs like `skeletonkey --auto --i-know`. Auto-satisfy it.
*
* The SSH self-lockout guard (YES_BREAK_SSH) is deliberately exempt:
* it protects the operator's own access rather than gating
* authorization, so it always requires an interactive answer. */
if (dirtyfail_assume_yes && strcmp(expected, "YES_BREAK_SSH") != 0) {
log_step("confirmation gate '%s' auto-satisfied (--i-know)", expected);
return true;
}
char buf[128]; char buf[128];
printf(" Type \033[1;33m%s\033[0m and press enter to proceed: ", expected); printf(" Type \033[1;33m%s\033[0m and press enter to proceed: ", expected);
fflush(stdout); fflush(stdout);
+8
View File
@@ -86,6 +86,14 @@ extern bool dirtyfail_no_revert;
* is redirected to stderr. Set by --json. */ * is redirected to stderr. Set by --json. */
extern bool dirtyfail_json; extern bool dirtyfail_json;
/* When true, typed_confirm() auto-satisfies its gate instead of reading
* stdin — the caller has already cleared an explicit authorization gate.
* SKELETONKEY's bridge layer sets this from skeletonkey_ctx.authorized
* (i.e. the --i-know flag) so non-interactive runs like
* `skeletonkey --auto --i-know` don't deadlock on the DIRTYFAIL prompt.
* The YES_BREAK_SSH self-lockout guard is exempt — see typed_confirm(). */
extern bool dirtyfail_assume_yes;
void log_step (const char *fmt, ...) __attribute__((format(printf, 1, 2))); void log_step (const char *fmt, ...) __attribute__((format(printf, 1, 2)));
void log_ok (const char *fmt, ...) __attribute__((format(printf, 1, 2))); void log_ok (const char *fmt, ...) __attribute__((format(printf, 1, 2)));
void log_bad (const char *fmt, ...) __attribute__((format(printf, 1, 2))); void log_bad (const char *fmt, ...) __attribute__((format(printf, 1, 2)));