]> git.eshelyaron.com Git - emacs.git/commitdiff
Added `comint-password-function' hook
authorMichael R. Mauger <michael@mauger.com>
Mon, 23 Dec 2019 04:56:05 +0000 (23:56 -0500)
committerMichael R. Mauger <michael@mauger.com>
Mon, 23 Dec 2019 04:56:05 +0000 (23:56 -0500)
* etc/NEWS:
* lisp/comint.el (comint-password-function): New variable.
  (comint-send-invisible): Use it.
* test/lisp/comint-tests.el (comint-test-no-password-function,
  comint-test-password-function-with-value,
  comint-test-password-function-with-nil): Test new variable.

etc/NEWS
lisp/comint.el
test/lisp/comint-tests.el

index 20288398bd3710be504b9050feb39f23f52b8d7b..2b0622e752f80def96c9a738c1a72bee90dec4db 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1131,6 +1131,19 @@ end.
 *** 'comint-run' can now accept a list of switches to pass to the program.
 'C-u M-x comint-run' will prompt for the switches interactively.
 
+*** Abnormal hook `comint-password-function' has been added.
+This hook permits a derived mode to supply a password for the
+underlying command interpreter without prompting the user.  For
+example, in sql-mode, the password for connecting to the database may
+be stored in the connection wallet and may be passed on the command
+line to start the SQL interpreter.  This is a potential security flaw
+that could expose user's database passwords on the command line
+through the use of a process list (Bug#8427).  With this hook, it is
+possible to not pass the password on the command line and wait for the
+program to prompt for the password.  When it does so, the password cam
+be supplied to the SQL interpreter without involving the user just as
+if it had been supplied on the command line.
+
 ** SQL
 
 *** SQL Indent Minor Mode
index 4bb4367035488a1aa5e87c589d71aeeb457963d2..c06a63fbd24abe15c8ec4556cf9ead3e69579a73 100644 (file)
@@ -2356,6 +2356,13 @@ a buffer local variable."
 ;; saved -- typically passwords to ftp, telnet, or somesuch.
 ;; Just enter m-x comint-send-invisible and type in your line.
 
+(defvar comint-password-function nil
+  "Abnormal hook run when prompted for a password.
+This function gets one argument, a string containing the prompt.
+It may return a string containing the password, or nil if normal
+password prompting should occur.")
+(make-variable-buffer-local 'comint-password-function)
+
 (defun comint-send-invisible (&optional prompt)
   "Read a string without echoing.
 Then send it to the process running in the current buffer.
@@ -2370,8 +2377,13 @@ Security bug: your string can still be temporarily recovered with
           (format "(In buffer %s) "
                   (current-buffer)))))
     (if proc
-       (let ((str (read-passwd (concat prefix
-                                       (or prompt "Non-echoed text: ")))))
+       (let ((prefix-prompt (concat prefix
+                                    (or prompt "Non-echoed text: ")))
+             str)
+         (when comint-password-function
+           (setq str (funcall comint-password-function prefix-prompt)))
+         (unless str
+           (setq str (read-passwd prefix-prompt)))
          (if (stringp str)
              (progn
                (comint-snapshot-last-prompt)
index 213a5c7c9e4369ca7816492ab1d8dfd383d8e78f..c04134599f6f87f4aa5ae66810412dc8ef03bbc7 100644 (file)
   (dolist (str comint-testsuite-password-strings)
     (should (string-match comint-password-prompt-regexp str))))
 
+(ert-deftest comint-test-no-password-function ()
+  "Test that `comint-password-function' not being set does not
+alter normal password flow."
+  (cl-letf
+      (((symbol-function 'read-passwd)
+        (lambda (_prompt &optional _confirm _default)
+          "PaSsWoRd123")))
+    (let ((cat (executable-find "cat")))
+      (when cat
+        (with-temp-buffer
+          (make-comint-in-buffer "test-comint-password" (current-buffer) cat)
+          (let ((proc (get-buffer-process (current-buffer))))
+            (comint-send-string proc "Password: ")
+            (accept-process-output proc 0 1 t)
+            (comint-send-eof)
+            (accept-process-output proc 0 1 t)
+            (should (string-equal (buffer-substring-no-properties (point-min) (point-max))
+                                  "Password: PaSsWoRd123\n"))
+            (when (process-live-p proc)
+              (kill-process proc))
+            (accept-process-output proc 0 1 t)))))))
+
+(ert-deftest comint-test-password-function-with-value ()
+  "Test that `comint-password-function' alters normal password
+flow.  Hook function returns alternative password."
+  (cl-letf
+      (((symbol-function 'read-passwd)
+        (lambda (_prompt &optional _confirm _default)
+          "PaSsWoRd123")))
+    (let ((cat (executable-find "cat"))
+          (comint-password-function (lambda (_prompt) "MaGiC-PaSsWoRd789")))
+      (when cat
+        (with-temp-buffer
+          (make-comint-in-buffer "test-comint-password" (current-buffer) cat)
+          (let ((proc (get-buffer-process (current-buffer))))
+            (comint-send-string proc "Password: ")
+            (accept-process-output proc 0 1 t)
+            (comint-send-eof)
+            (accept-process-output proc 0 1 t)
+            (should (string-equal (buffer-substring-no-properties (point-min) (point-max))
+                                  "Password: MaGiC-PaSsWoRd789\n"))
+            (when (process-live-p proc)
+              (kill-process proc))
+            (accept-process-output proc 0 1 t)))))))
+
+(ert-deftest comint-test-password-function-with-nil ()
+  "Test that `comint-password-function' does not alter the normal
+password flow if it returns a nil value."
+  (cl-letf
+      (((symbol-function 'read-passwd)
+        (lambda (_prompt &optional _confirm _default)
+          "PaSsWoRd456")))
+    (let ((cat (executable-find "cat"))
+          (comint-password-function (lambda (_prompt) nil)))
+      (when cat
+        (with-temp-buffer
+          (make-comint-in-buffer "test-comint-password" (current-buffer) cat)
+          (let ((proc (get-buffer-process (current-buffer))))
+            (comint-send-string proc "Password: ")
+            (accept-process-output proc 0 1 t)
+            (comint-send-eof)
+            (accept-process-output proc 0 1 t)
+            (should (string-equal (buffer-substring-no-properties (point-min) (point-max))
+                                  "Password: PaSsWoRd456\n"))
+            (when (process-live-p proc)
+              (kill-process proc))
+            (accept-process-output proc 0 1 t)))))))
+
 ;; Local Variables:
 ;; no-byte-compile: t
 ;; End: