Skip to content

pam: unlock existing keyrings on credential refresh - #592

Open
caniko wants to merge 3 commits into
linux-credentials:mainfrom
caniko:caniko/pam-lockscreen-20261007
Open

caniko wants to merge 3 commits into
linux-credentials:mainfrom
caniko:caniko/pam-lockscreen-20261007

Conversation

@caniko

@caniko caniko commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

This lets screen lockers unlock an existing oo7 daemon through
PAM_REINITIALIZE_CRED or PAM_REFRESH_CRED, without opening another session.
Addresses #587.

I traced GNOME Keyring's PAM module: it attempts the unlock in
pam_sm_authenticate
and leaves pam_sm_setcred as a no-op. This patch uses the credential-refresh
hooks requested in #587 so the caller can finish authentication and account
checks before delivering the password.

The refresh hook uses oo7's existing password message and waits for the daemon's
acknowledgement. It clears the stash after successful delivery, on credential
deletion, and before another authentication attempt. A passwordless retry cannot
reuse an earlier token. A new login still hands off through open_session;
refresh does not start a second daemon.

The sender now reports missing, malformed, oversized and negative
acknowledgements as failures. OS authentication remains non-fatal in the refresh
hook. This also avoids logging daemon-controlled rejection text.

Lockers must call credential refresh after both pam_authenticate() and
pam_acct_mgmt() succeed on the same handle. COSMIC's locker currently only
calls the authentication/account hooks, so it needs a corresponding caller
change. This contribution does not acquire TPM credentials or establish
privileged screen-unlock authorization; that belongs in the desktop/authentication
integration discussed in #506.

Verification

  • cargo test -p oo7-pam --locked -j 1: four tests passed, including exported
    PAM-hook calls, password cleanup, cold-login retention and acknowledgement
    refusal. The workflow now runs the PAM tests.
  • cargo clippy -p oo7-pam --all-targets --locked -j 1 -- -D warnings: passed.
  • Native oo7-pam cdylib build: passed.
  • A disposable real Linux-PAM stack loaded the built cdylib as an unprivileged
    user. Successful authentication delivered the synthetic password once.
    Failed authentication, failure of a later required factor, account rejection,
    and a missing password delivered nothing. The fixture used a private socket
    and did not modify host PAM files or existing keyrings.
  • Scoped treefmt and git diff --check: passed.

Live graphical-session and TPM acceptance remain separate qualification work.

Handle PAM_REINITIALIZE_CRED and PAM_REFRESH_CRED after the caller completes authentication and account checks. Preserve cold-login open_session behavior, clear obsolete and delivered tokens, and require a bounded positive daemon acknowledgement. Exercise the PAM hooks and transport in CI.

Addresses linux-credentials#587.
Comment thread .github/workflows/CI.yml Outdated
Comment thread pam/src/lib.rs
Comment thread pam/README.md
caniko added 2 commits October 8, 2026 11:27
Move PAM tests after component builds and shorten the end-user README. Extract the independent authentication retry change so it can be reintroduced in a focused commit without rewriting published history.
Keep authentication retry handling separate from credential refresh. Exercise both missing and empty passwords with a dedicated regression test.
Comment thread pam/src/protocol.rs
pub success: bool,
pub error_message: String,
#[serde(rename = "error_message")]
pub _error_message: String,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this change is unrelated, why?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if it is no longer being used, then just drop it.

Comment thread pam/src/lib.rs
{
return PAM_SUCCESS;
}
let Ok(username) = (unsafe { get_user(pamh) }) else {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this check if the socket in /run/user/ is the same as this one?

Also, this function duplicates some of the logic in open_session, so maybe de-duplicate parts of it?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually this might a bit more complex,

setcred sends the stashed password to the daemon of whichever user PAM_USER names at that point

pam_sm_setcred takes the target user from PAM_USER when setcred runs. That can be a different user from the one whose password was stashed during pam_sm_authenticate. sudo is one caller where this happens: in sudo_pam_begin_session() it sets PAM_USER to the run-as user and then calls pam_setcred(PAM_REINITIALIZE_CRED).

With pam_oo7.so in common-auth, as the README suggests, sudo -u bob sends alice's password to /run/user//oo7-pam.sock. The ownership check passes because bob owns that socket.

Suggested fix: store the username together with the password in pam_sm_authenticate, and have setcred skip the handoff when the current PAM_USER doesn't match it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can deduplicate, what do we do about the complexity? Are you suggesting:

Suggested fix: store the username together with the password in pam_sm_authenticate, and have setcred skip the handoff when the current PAM_USER doesn't match it.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants