From 45fc23704c8373d4b8458394aab7e1fbc3846d15 Mon Sep 17 00:00:00 2001 From: Michael Albinus Date: Tue, 26 Nov 2024 12:45:06 +0100 Subject: [PATCH] Support fingerprint readers in Tramp * doc/misc/tramp.texi (Frequently Asked Questions): Speak about fingerprint readers. * lisp/net/tramp-sh.el (tramp-actions-before-shell): Use `tramp-fingerprint-prompt-regexp'. * lisp/net/tramp.el (tramp-wrong-passwd-regexp): Add fingerprint messages. (tramp-fingerprint-prompt-regexp, tramp-use-fingerprint): New defcustoms. (tramp-action-fingerprint, tramp-action-show-message): New defuns. (tramp-action-show-and-confirm-message): Start check at (point-min). * test/lisp/net/tramp-tests.el (tramp-test47-read-fingerprint): New test. (cherry picked from commit 3fe787ad4d5894df5b540dbd195128118c949c7c) --- doc/misc/tramp.texi | 17 ++++++++ lisp/net/tramp-sh.el | 1 + lisp/net/tramp.el | 79 +++++++++++++++++++++++++++++++++++- test/lisp/net/tramp-tests.el | 43 ++++++++++++++++++++ 4 files changed, 139 insertions(+), 1 deletion(-) diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi index 98e52ce5be1..5851ad3abaf 100644 --- a/doc/misc/tramp.texi +++ b/doc/misc/tramp.texi @@ -5577,6 +5577,23 @@ nitrokey, or titankey. (residential) keys by @command{ssh-agent}. As workaround, you might disable @command{ssh-agent} for such keys. + +@item +Does @value{tramp} support a fingerprint reader? + +Yes. A fingerprint reader could be used as additional authentication +method for @option{sudo}-based logins. @value{tramp} supports the +additional handshaking messages for it@footnote{It supports +fingerprint readers driven by @command{fprintd}.}. If the fingerprint +isn't recognized by the fingerprint reader in time, the authentication +falls back to request a password. + +@vindex tramp-use-fingerprint +If the user option @code{tramp-use-fingerprint} is @code{nil}, +@value{tramp} interrupts the fingerprint request, falling back to +password authentication immediately. + + @item @value{tramp} does not connect to Samba or MS Windows hosts running SMB1 connection protocol diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index e8fd251457c..205b0d6304c 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -601,6 +601,7 @@ shell from reading its init file." '((tramp-login-prompt-regexp tramp-action-login) (tramp-password-prompt-regexp tramp-action-password) (tramp-otp-password-prompt-regexp tramp-action-otp-password) + (tramp-fingerprint-prompt-regexp tramp-action-fingerprint) (tramp-wrong-passwd-regexp tramp-action-permission-denied) (shell-prompt-pattern tramp-action-succeed) (tramp-shell-prompt-pattern tramp-action-succeed) diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index 6347286b715..2b64c386e5c 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -723,13 +723,52 @@ The regexp should match at end of buffer." "No supported authentication methods left to try!" (: "Login " (| "Incorrect" "incorrect")) (: "Connection " (| "refused" "closed")) - (: "Received signal " (+ digit))) + (: "Received signal " (+ digit)) + ;; Fingerprint. + "Verification timed out" + "Failed to match fingerprint" + "An unknown error occurred") (* nonl)) "Regexp matching a `login failed' message. The regexp should match at end of buffer." :type 'regexp :link '(tramp-info-link :tag "Tramp manual" tramp-wrong-passwd-regexp)) +;; +(defcustom tramp-fingerprint-prompt-regexp + (rx (| "Place your finger on" + "Swipe your finger across" + "Place your left thumb on" + "Swipe your left thumb across" + "Place your left index finger on" + "Swipe your left index finger across" + "Place your left middle finger on" + "Swipe your left middle finger across" + "Place your left ring finger on" + "Swipe your left ring finger across" + "Place your left little finger on" + "Swipe your left little finger across" + "Place your right thumb on" + "Swipe your right thumb across" + "Place your right index finger on" + "Swipe your right index finger across" + "Place your right middle finger on" + "Swipe your right middle finger across" + "Place your right ring finger on" + "Swipe your right ring finger across" + "Place your right little finger on" + "Swipe your right little finger across" + "Place your finger on the reader again" + "Swipe your finger again" + "Swipe was too short, try again" + "Your finger was not centred, try swiping your finger again" + "Remove your finger, and try swiping your finger again") + (* nonl) (* (any "\r\n"))) + "Regexp matching fingerprint prompts. +The regexp should match at end of buffer." + :version "30.2" + :type 'regexp) + (defcustom tramp-yesno-prompt-regexp (rx "Are you sure you want to continue connecting (yes/no" (? "/[fingerprint]") ")?" @@ -5737,6 +5776,23 @@ of." (narrow-to-region (point-max) (point-max)))) t) +(defcustom tramp-use-fingerprint t + "Whether fingerprint prompts shall be used for authentication." + :version "30.2" + :type 'boolean + :link '(tramp-info-link :tag "Tramp manual" tramp-use-fingerprint)) + +(defun tramp-action-fingerprint (proc vec) + "Query the user for a fingerprint verification. +Interrupt the query if `tramp-use-fingerprint' is nil." + (with-current-buffer (process-buffer proc) + (if tramp-use-fingerprint + (tramp-action-show-message proc vec) + (interrupt-process proc) + ;; Hide message. + (narrow-to-region (point-max) (point-max)))) + t) + (defun tramp-action-succeed (_proc _vec) "Signal success in finding shell prompt." (throw 'tramp-action 'ok)) @@ -5783,6 +5839,26 @@ The terminal type can be configured with `tramp-terminal-type'." (tramp-send-string vec (concat tramp-terminal-type tramp-local-end-of-line)) t) +(defun tramp-action-show-message (proc vec) + "Show the user a message for confirmation. +Wait, until the connection buffer changes." + (with-current-buffer (process-buffer proc) + (let ((cursor-in-echo-area t) + set-message-function clear-message-function tramp-dont-suspend-timers) + (with-tramp-suspended-timers + ;; Silence byte compiler. + (ignore set-message-function clear-message-function) + (tramp-message vec 6 "\n%s" (buffer-string)) + (goto-char (point-min)) + (tramp-check-for-regexp proc tramp-process-action-regexp) + (with-temp-message (concat (string-trim (match-string 0)) " ") + ;; Hide message in buffer. + (narrow-to-region (point-max) (point-max)) + ;; Wait for new output. + (while (length= (buffer-string) 0) + (tramp-accept-process-output proc)))))) + t) + (defun tramp-action-confirm-message (_proc vec) "Return RET in order to confirm the message." (tramp-message @@ -5800,6 +5876,7 @@ Wait, until the connection buffer changes." ;; Silence byte compiler. (ignore set-message-function clear-message-function) (tramp-message vec 6 "\n%s" (buffer-string)) + (goto-char (point-min)) (tramp-check-for-regexp proc tramp-process-action-regexp) (with-temp-message (concat (string-trim (match-string 0)) " ") ;; Hide message in buffer. diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index 3a770648b25..96dbdc53af0 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -7812,6 +7812,49 @@ process sentinels. They shall not disturb each other." (should-error (file-exists-p ert-remote-temporary-file-directory))))))))) +(ert-deftest tramp-test47-read-fingerprint () + "Check Tramp fingerprint handling." + :tags '(:expensive-test) + (skip-unless (tramp--test-mock-p)) + + (let (;; Suppress "exec". + (tramp-restricted-shell-hosts-alist `(,tramp-system-name))) + + ;; Reading fingerprint works. + (tramp-cleanup-connection tramp-test-vec 'keep-debug) + (let ((tramp-connection-properties + `((nil "login-args" + (("-c") + (,(tramp-shell-quote-argument + "echo Place your finger on the fingerprint reader")) + (";") ("sleep" "1") + (";") ("sh" "-i")))))) + (should (file-exists-p ert-remote-temporary-file-directory))) + + ;; Falling back after a timeout works. + (tramp-cleanup-connection tramp-test-vec 'keep-debug) + (let ((tramp-connection-properties + `((nil "login-args" + (("-c") + (,(tramp-shell-quote-argument + "echo Place your finger on the fingerprint reader")) + (";") ("sleep" "1") + (";") ("echo" "Failed to match fingerprint") + (";") ("sh" "-i")))))) + (should (file-exists-p ert-remote-temporary-file-directory))) + + ;; Interrupting the fingerprint handshaking works. + (tramp-cleanup-connection tramp-test-vec 'keep-debug) + (let ((tramp-connection-properties + `((nil "login-args" + (("-c") + (,(tramp-shell-quote-argument + "echo Place your finger on the fingerprint reader")) + (";") ("sleep" "1") + (";") ("sh" "-i"))))) + tramp-use-fingerprint) + (should (file-exists-p ert-remote-temporary-file-directory))))) + ;; This test is inspired by Bug#29163. (ert-deftest tramp-test48-auto-load () "Check that Tramp autoloads properly." -- 2.39.5