Let the password prompt say why
panama-sudo is pkexec with a stated reason: the reason travels to the shell over the existing polkit IPC target, and the prompt renders it labeled "Stated reason (unverified)" beside polkitd's real action message -- beside, never instead of, because any process can claim any reason and the action text is the trust anchor. Reasons are single-shot and expire in ten seconds, so a stale one cannot dress up an unrelated prompt; without a reason, a running shell, or qs the wrapper is exactly pkexec. Built for agents, so the person typing their password learns what for. Verified live end to end -- reason shown, consumed once, expired when stale, cleared on dismissal -- and pinned by the polkit reason contract.
This commit is contained in:
@@ -94,6 +94,34 @@ PanelWindow {
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
// The caller's stated reason, when panama-sudo passed one.
|
||||
// Untrusted commentary from an unprivileged process, so it is
|
||||
// labeled as a claim and drawn beside polkitd's message above --
|
||||
// never in place of it. The real action text is the trust anchor.
|
||||
Column {
|
||||
width: parent.width
|
||||
visible: Polkit.statedReason !== ""
|
||||
spacing: 2
|
||||
|
||||
Text {
|
||||
text: "Stated reason (unverified)"
|
||||
color: Theme.fgMuted
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
text: Polkit.statedReason
|
||||
color: Theme.fgDim
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSize
|
||||
font.italic: true
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
visible: Polkit.users.length > 1
|
||||
|
||||
@@ -38,12 +38,31 @@ Singleton {
|
||||
// How many times a wrong password has been offered for this request.
|
||||
property int attempts: 0
|
||||
|
||||
// A caller's stated reason for the NEXT request, and the one attached to
|
||||
// the request on screen. Untrusted by design -- any process can state one
|
||||
// -- so the prompt shows it clearly labeled beside polkitd's real action
|
||||
// message, never in place of it. See stateReason().
|
||||
property var pendingReason: null
|
||||
property string statedReason: ""
|
||||
|
||||
readonly property bool active: root.request !== null
|
||||
|
||||
// Held only between pressing Enter and the helper accepting it on stdin.
|
||||
property string pendingSecret: ""
|
||||
|
||||
|
||||
// panama-sudo's side channel: state WHY the authentication request about
|
||||
// to arrive is being made, so the prompt can say more than the generic
|
||||
// action text. Single-shot and short-lived -- it attaches only to the next
|
||||
// request, and only if that request arrives within ten seconds -- so a
|
||||
// stale reason can never dress up an unrelated prompt.
|
||||
function stateReason(text: string): void {
|
||||
const trimmed = String(text).trim().slice(0, 200);
|
||||
if (trimmed === "")
|
||||
return;
|
||||
root.pendingReason = { text: trimmed, at: Date.now() };
|
||||
}
|
||||
|
||||
function begin(path: string): void {
|
||||
// A second request while one is open would leave the first
|
||||
// unanswerable; polkit serializes these in practice, and refusing is
|
||||
@@ -69,6 +88,13 @@ Singleton {
|
||||
: (root.users.length > 0 ? String(root.users[0]) : "");
|
||||
root.attempts = 0;
|
||||
root.failureText = "";
|
||||
// Consume the stated reason whether or not it is still fresh:
|
||||
// either way it must not survive to a later request.
|
||||
const pending = root.pendingReason;
|
||||
root.pendingReason = null;
|
||||
root.statedReason = (pending !== null && Date.now() - pending.at <= 10000)
|
||||
? pending.text
|
||||
: "";
|
||||
} catch (error) {
|
||||
console.warn("Polkit: could not read the request:", error);
|
||||
root.dismiss("failed");
|
||||
@@ -114,6 +140,7 @@ Singleton {
|
||||
root.failureText = "";
|
||||
root.pendingSecret = "";
|
||||
root.authenticating = false;
|
||||
root.statedReason = "";
|
||||
}
|
||||
|
||||
function responsePathFor(path: string): string {
|
||||
|
||||
@@ -424,8 +424,15 @@ ShellRoot {
|
||||
target: "polkit"
|
||||
function begin(path: string): void { Polkit.begin(path); }
|
||||
function cancel(): void { Polkit.cancel(); }
|
||||
// panama-sudo's side channel: the reason a privileged command is about
|
||||
// to run, shown labeled on the prompt beside polkitd's own message.
|
||||
function reason(text: string): void { Polkit.stateReason(text); }
|
||||
function status(): string {
|
||||
return JSON.stringify({ active: Polkit.active, action: Polkit.actionId });
|
||||
return JSON.stringify({
|
||||
active: Polkit.active,
|
||||
action: Polkit.actionId,
|
||||
statedReason: Polkit.statedReason
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user