diff lisp/utils/passwd.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 0293115a14e9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/utils/passwd.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,386 @@
+;;; passwd.el --- Prompting for passwords semi-securely
+
+;; Copyright (C) 1994 Free Software Foundation, Inc.
+;; Keywords: comm, extensions
+
+;; Author: Jamie Zawinski <jwz@netscape.com>
+
+;; This file is part of XEmacs.
+
+;; XEmacs is free software; you can redistribute it and/or modify it
+;; under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; XEmacs is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;; General Public License for more details.
+
+;;; Synched up with: Not in FSF.
+
+;; You should have received a copy of the GNU General Public License
+;; along with XEmacs; see the file COPYING.  If not, write to the Free
+;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;;; Change Log:
+;;
+;;  Sun Jun 12 04:19:30 1994 by sandy on ibm550.sissa.it
+;;    Added support for password histories and (provide 'passwd)
+;;    (jwz says: this "history" thing is completely undocumented, you loser!)
+;; 2-Jan-95 (mon); 4:13 AM by jwz@netscape.com
+;;    Fixed Sandy's extreme keymap bogosity.  Made it invert the screen when
+;;    reading securely (this could be better; maybe use red text or something
+;;    instead...)
+;; 9-Jul-95 (fri); 4:55 AM by jwz@netscape.com
+;;    Made it work with XEmacs 19.12.
+;; 7-Jul-95 by cthomp@cs.uiuc.edu
+;;    Added variable to control inverting frame when keyboard grabbed
+
+;;; Code:
+
+(defvar passwd-invert-frame-when-keyboard-grabbed t
+  "*If non-nil swap the foreground and background colors of all faces.
+This is done while the keyboard is grabbed in order to give a visual
+clue that a grab is in effect.")
+
+(defvar passwd-echo ?.
+  "*The character which should be echoed when typing a password,
+or nil, meaning echo nothing.")
+
+(defvar read-passwd-map
+  (let ((i 0)
+	(s (make-string 1 0))
+	map)
+    (cond ((fboundp 'set-keymap-parent)
+	   (setq map (make-keymap))
+	   (set-keymap-parent map minibuffer-local-map))
+	  (t  ; v18/FSFmacs compatibility
+	   (setq map (copy-keymap minibuffer-local-map))))
+    (if (fboundp 'set-keymap-name)
+	(set-keymap-name map 'read-passwd-map))
+
+    (while (< i 127)
+      (aset s 0 i)
+      (or (and (boundp 'meta-prefix-char) (eq i meta-prefix-char))
+	  (define-key map s 'self-insert-command))
+      (setq i (1+ i)))
+
+    (define-key map "\C-g" 'keyboard-quit)
+    (define-key map "\C-h" 'delete-backward-char)
+    (define-key map "\r" 'exit-minibuffer)
+    (define-key map "\n" 'exit-minibuffer)
+    (define-key map "\C-u" 'passwd-erase-buffer)
+    (define-key map "\C-q" 'quoted-insert)
+    (define-key map "\177" 'delete-backward-char)
+    (define-key map "\M-n" 'passwd-next-history-element)
+    (define-key map "\M-p" 'passwd-previous-history-element)
+    map)
+  "Keymap used for reading passwords in the minibuffer.
+The \"bindings\" in this map are not real commands; only a limited
+number of commands are understood.  The important bindings are:
+\\<read-passwd-map>
+	\\[passwd-erase-buffer]	Erase all input.
+	\\[quoted-insert]	Insert the next character literally.
+	\\[delete-backward-char]	Delete the previous character.
+	\\[exit-minibuffer]	Accept what you have typed.
+	\\[keyboard-quit]	Abort the command.
+
+All other characters insert themselves (but do not echo.)")
+
+;;; internal variables
+
+(defvar passwd-history nil)
+(defvar passwd-history-posn 0)
+
+;;;###autoload
+(defun read-passwd (prompt &optional confirm default)
+  "Prompts for a password in the minibuffer, and returns it as a string.
+If PROMPT may be a prompt string or an alist of elements 
+'\(prompt . default\).
+If optional arg CONFIRM is true, then ask the user to type the password
+again to confirm that they typed it correctly.
+If optional arg DEFAULT is provided, then it is a string to insert as
+the default choice (it is not, of course, displayed.)
+
+If running under X, the keyboard will be grabbed (with XGrabKeyboard())
+to reduce the possibility that evesdropping is occuring.
+
+When reading a password, all keys self-insert, except for:
+\\<read-passwd-map>
+	\\[read-passwd-erase-line]	Erase the entire line.
+	\\[quoted-insert]	Insert the next character literally.
+	\\[delete-backward-char]	Delete the previous character.
+	\\[exit-minibuffer]	Accept what you have typed.
+	\\[keyboard-quit]	Abort the command.
+
+The returned value is always a newly-created string.  No additional copies
+of the password remain after this function has returned.
+
+NOTE: unless great care is taken, the typed password will exist in plaintext
+form in the running image for an arbitrarily long time.  Priveleged users may
+be able to extract it from memory.  If emacs crashes, it may appear in the
+resultant core file.
+
+Some steps you can take to prevent the password from being copied around:
+
+ - as soon as you are done with the returned string, destroy it with
+   (fillarray string 0).  The same goes for any default passwords
+   or password histories.
+
+ - do not copy the string, as with concat or substring - if you do, be
+   sure to keep track of and destroy all copies.
+
+ - do not insert the password into a buffer - if you do, be sure to 
+   overwrite the buffer text before killing it, as with the functions 
+   `passwd-erase-buffer' or `passwd-kill-buffer'.  Note that deleting
+   the text from the buffer does NOT necessarily remove the text from
+   memory.
+
+ - be careful of the undo history - if you insert the password into a 
+   buffer which has undo recording turned on, the password will be 
+   copied onto the undo list, and thus recoverable.
+
+ - do not pass it as an argument to a shell command - anyone will be
+   able to see it if they run `ps' at the right time.
+
+Note that the password will be temporarily recoverable with the `view-lossage'
+command.  This data will not be overwritten until another hundred or so 
+characters are typed.  There's not currently a way around this."
+
+  (save-excursion
+    (let ((input (get-buffer-create " *password*"))
+	  (passwd-history-posn 0)
+	  passwd-history)
+      (if (listp prompt)
+	  (setq passwd-history prompt
+		default (cdr (car passwd-history))))
+      (set-buffer input)
+      (buffer-disable-undo input)
+      (use-local-map read-passwd-map)
+      (unwind-protect
+	  (progn
+	    (if (passwd-grab-keyboard)
+		(passwd-secure-display))
+	    (read-passwd-1 input prompt nil default)
+	    (set-buffer input)
+
+	    (if (not confirm)
+		(buffer-string)
+	      (let ((ok nil)
+		    passwd)
+		(while (not ok)
+		  (set-buffer input)
+		  (setq passwd (buffer-string))
+		  (read-passwd-1 input prompt "[Retype to confirm]")
+		  (if (passwd-compare-string-to-buffer passwd input)
+		      (setq ok t)
+		    (fillarray passwd 0)
+		    (setq passwd nil)
+		    (beep)
+		    (read-passwd-1 input prompt "[Mismatch. Start over]")
+		    ))
+		passwd)))
+	;; protected
+	(passwd-ungrab-keyboard)
+	(passwd-insecure-display)
+	(passwd-kill-buffer input)
+	(if (fboundp 'clear-message) ;XEmacs
+	    (clear-message)
+	  (message ""))
+	))))
+
+
+(defun read-passwd-1 (buffer prompt &optional prompt2 default)
+  (set-buffer buffer)
+  (passwd-erase-buffer)
+  (if default (insert default))
+  (catch 'exit ; exit-minibuffer throws here
+    (while t
+      (set-buffer buffer)
+      (let* ((minibuffer-completion-table nil)
+	     (cursor-in-echo-area t)
+	     (echo-keystrokes 0)
+	     (key (passwd-read-key-sequence
+		   (concat (if (listp prompt)
+			       (car (nth passwd-history-posn passwd-history))
+			     prompt)
+			   prompt2
+			   (if passwd-echo
+			       (make-string (buffer-size) passwd-echo)))))
+	     (binding (key-binding key)))
+	(setq prompt2 nil)
+	(set-buffer buffer)		; just in case...
+	(if (fboundp 'event-to-character) ;; lemacs
+	    (setq last-command-event (aref key (1- (length key)))
+		  last-command-char (event-to-character last-command-event))
+	  ;; v18/FSFmacs compatibility
+	  (setq last-command-char (aref key (1- (length key)))))
+	(setq this-command binding)
+	(condition-case c
+	    (command-execute binding)
+	  (error
+	   (beep)
+	   (if (fboundp 'display-error)
+	       (display-error c t)
+	     ;; v18/FSFmacs compatibility
+	     (message (concat (or (get (car-safe c) 'error-message) "???")
+			      (if (cdr-safe c) ": ")
+			      (mapconcat 
+			       (function (lambda (x) (format "%s" x)))
+			       (cdr-safe c) ", "))))
+	   (sit-for 2)))
+	))))
+
+(defun passwd-previous-history-element (n)
+  (interactive "p")
+  (or passwd-history
+      (error "Password history is empty."))
+  (let ((l (length passwd-history)))
+    (setq passwd-history-posn
+	  (% (+ n passwd-history-posn) l))
+    (if (< passwd-history-posn 0)
+	(setq passwd-history-posn (+ passwd-history-posn l))))
+  (let ((obuff (current-buffer))) ; want to move point in passwd buffer
+    (unwind-protect
+	(progn
+	  (set-buffer " *password*")
+	  (passwd-erase-buffer)
+	  (insert (cdr (nth passwd-history-posn passwd-history))))
+      (set-buffer obuff))))
+
+(defun passwd-next-history-element (n)
+  (interactive "p")
+  (passwd-previous-history-element (- n)))
+
+(defun passwd-erase-buffer ()
+  ;; First erase the buffer, which will simply enlarge the gap.
+  ;; Then insert null characters until the gap is filled with them
+  ;; to prevent the old text from being visible in core files or kmem.
+  ;; (Actually use 3x the size of the buffer just to be safe - a longer
+  ;; passwd might have been typed and backspaced over.)
+  (interactive)
+  (widen)
+  (let ((s (* (buffer-size) 3)))
+    (erase-buffer)
+    (while (> s 0)
+      (insert ?\000)
+      (setq s (1- s)))
+    (erase-buffer)))
+
+(defun passwd-kill-buffer (buffer)
+  (save-excursion
+    (set-buffer buffer)
+    (buffer-disable-undo buffer)
+    (passwd-erase-buffer)
+    (set-buffer-modified-p nil))
+  (kill-buffer buffer))
+
+
+(defun passwd-compare-string-to-buffer (string buffer)
+  ;; same as (equal string (buffer-string)) but with no dangerous consing.
+  (save-excursion
+    (set-buffer buffer)
+    (goto-char (point-min))
+    (let ((L (length string))
+	  (i 0))
+      (if (/= L (- (point-max) (point-min)))
+	  nil
+	(while (not (eobp))
+	  (if (/= (following-char) (aref string i))
+	      (goto-char (point-max))
+	    (setq i (1+ i))
+	    (forward-char)))
+	(= (point) (+ i (point-min)))))))
+
+
+(defvar passwd-face-data nil)
+(defun passwd-secure-display ()
+  ;; Inverts the screen - used to indicate secure input, like xterm.
+  (cond
+   ((and passwd-invert-frame-when-keyboard-grabbed
+	 (fboundp 'set-face-foreground))
+    (setq passwd-face-data
+	  (delq nil (mapcar (function
+			     (lambda (face)
+			       (let ((fg (face-foreground face))
+				     (bg (face-background face)))
+				 (if (or fg bg)
+				     (if (fboundp 'color-name)
+					 (list face
+					       (color-name fg)
+					       (color-name bg))
+				       (list face fg bg))
+				   nil))))
+			    (if (fboundp 'list-faces)
+				(list-faces) ; lemacs
+			      (face-list)    ; FSFmacs
+			      ))))
+    (let ((rest passwd-face-data))
+      (while rest
+	(set-face-foreground (nth 0 (car rest)) (nth 2 (car rest)))
+	(set-face-background (nth 0 (car rest)) (nth 1 (car rest)))
+	(setq rest (cdr rest))))))
+  nil)
+
+(defun passwd-insecure-display ()
+  ;; Undoes the effect of `passwd-secure-display'.
+  (cond
+   (passwd-invert-frame-when-keyboard-grabbed
+    (while passwd-face-data
+      (set-face-foreground (nth 0 (car passwd-face-data))
+			   (nth 1 (car passwd-face-data)))
+      (set-face-background (nth 0 (car passwd-face-data))
+			   (nth 2 (car passwd-face-data)))
+      (setq passwd-face-data (cdr passwd-face-data)))
+    nil)))
+
+(defun passwd-grab-keyboard ()
+  (cond ((not (and (fboundp 'x-grab-keyboard) ; lemacs 19.10+
+		   (eq 'x (if (fboundp 'frame-type)
+			      (frame-type (selected-frame))
+			    (live-screen-p (selected-screen))))))
+	 nil)
+	((x-grab-keyboard)
+	 t)
+	(t
+	 (message "Unable to grab keyboard - waiting a second...")
+	 (sleep-for 1)
+	 (cond ((x-grab-keyboard)
+		(message "Keyboard grabbed on second try.")
+		t)
+	       (t
+		(beep)
+		(message "WARNING: keyboard is insecure (unable to grab!)")
+		(sleep-for 3)
+		nil)))))
+
+(defun passwd-ungrab-keyboard ()
+  (if (and (fboundp 'x-ungrab-keyboard) ; lemacs 19.10+
+	   (eq 'x (if (fboundp 'frame-type)
+		      (frame-type (selected-frame))
+		    (live-screen-p (selected-screen)))))
+      (x-ungrab-keyboard)))
+
+;; v18 compatibility
+(or (fboundp 'buffer-disable-undo)
+    (fset 'buffer-disable-undo 'buffer-flush-undo))
+
+;; read-key-sequence echoes the key sequence in Emacs 18.
+(defun passwd-read-key-sequence (prompt)
+  (let ((inhibit-quit t)
+	str)
+    (while (or (null str) (keymapp (key-binding str)))
+      (if (fboundp 'display-message)
+	  (display-message 'prompt prompt)
+	(message prompt))
+      (setq str (concat str (char-to-string (read-char)))))
+    (setq quit-flag nil)
+    str))
+
+(or (string-match "^18" emacs-version)
+    (fset 'passwd-read-key-sequence 'read-key-sequence))
+
+(provide 'passwd)
+
+;;; passwd.el ends here