cvsdist c08065
;;; ssl.el,v --- ssl functions for emacsen without them builtin
cvsdist c08065
;; Author: wmperry
cvsdist c08065
;; Created: 1999/10/14 12:44:18
cvsdist c08065
;; Version: 1.2
cvsdist c08065
;; Keywords: comm
cvsdist c08065
cvsdist c08065
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
cvsdist c08065
;;; Copyright (c) 1995, 1996 by William M. Perry <wmperry@cs.indiana.edu>
cvsdist c08065
;;; Copyright (c) 1996 - 1999 Free Software Foundation, Inc.
cvsdist c08065
;;;
cvsdist c08065
;;; This file is part of GNU Emacs.
cvsdist c08065
;;;
cvsdist c08065
;;; GNU Emacs is free software; you can redistribute it and/or modify
cvsdist c08065
;;; it under the terms of the GNU General Public License as published by
cvsdist c08065
;;; the Free Software Foundation; either version 2, or (at your option)
cvsdist c08065
;;; any later version.
cvsdist c08065
;;;
cvsdist c08065
;;; GNU Emacs is distributed in the hope that it will be useful,
cvsdist c08065
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
cvsdist c08065
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
cvsdist c08065
;;; GNU General Public License for more details.
cvsdist c08065
;;;
cvsdist c08065
;;; You should have received a copy of the GNU General Public License
cvsdist c08065
;;; along with GNU Emacs; see the file COPYING.  If not, write to the
cvsdist c08065
;;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
cvsdist c08065
;;; Boston, MA 02111-1307, USA.
cvsdist c08065
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
cvsdist c08065
cvsdist c08065
(require 'cl)
cvsdist c08065
(require 'base64)
cvsdist c08065
cvsdist c08065
(eval-and-compile
cvsdist c08065
  (condition-case ()
cvsdist c08065
      (require 'custom)
cvsdist c08065
    (error nil))
cvsdist c08065
  (if (and (featurep 'custom) (fboundp 'custom-declare-variable))
cvsdist c08065
      nil ;; We've got what we needed
cvsdist c08065
    ;; We have the old custom-library, hack around it!
cvsdist c08065
    (defmacro defgroup (&rest args)
cvsdist c08065
      nil)
cvsdist c08065
    (defmacro defcustom (var value doc &rest args) 
cvsdist c08065
      (` (defvar (, var) (, value) (, doc))))))
cvsdist c08065
cvsdist c08065
(defgroup ssl nil
cvsdist c08065
  "Support for `Secure Sockets Layer' encryption."
cvsdist c08065
  :group 'comm)
cvsdist c08065
  
cvsdist c08065
(defcustom ssl-certificate-directory "~/.w3/certs/"
cvsdist c08065
  "*Directory to store CA certificates in"
cvsdist c08065
  :group 'ssl
cvsdist c08065
  :type 'directory)
cvsdist c08065
cvsdist c08065
(defcustom ssl-rehash-program-name "c_rehash"
cvsdist c08065
  "*Program to run after adding a cert to a directory .
cvsdist c08065
Run with one argument, the directory name."
cvsdist c08065
  :group 'ssl
cvsdist c08065
  :type 'string)
cvsdist c08065
cvsdist c08065
(defcustom ssl-view-certificate-program-name "x509"
cvsdist c08065
  "*The program to run to provide a human-readable view of a certificate."
cvsdist c08065
  :group 'ssl
cvsdist c08065
  :type 'string)
cvsdist c08065
cvsdist c08065
(defcustom ssl-view-certificate-program-arguments '("-text" "-inform" "DER")
cvsdist c08065
  "*Arguments that should be passed to the certificate viewing program.
cvsdist c08065
The certificate is piped to it.
cvsdist c08065
Maybe a way of passing a file should be implemented"
cvsdist c08065
  :group 'ssl
cvsdist c08065
  :type 'list)
cvsdist c08065
cvsdist c08065
(defcustom ssl-certificate-directory-style 'ssleay
cvsdist c08065
  "*Style of cert database to use, the only valid value right now is `ssleay'.
cvsdist c08065
This means a directory of pem encoded certificates with hash symlinks."
cvsdist c08065
  :group 'ssl
cvsdist c08065
  :type '(choice (const :tag "SSLeay" :value ssleay)
cvsdist c08065
		 (const :tag "OpenSSL" :value openssl)))
cvsdist c08065
cvsdist c08065
(defcustom ssl-certificate-verification-policy 0
cvsdist c08065
  "*How far up the certificate chain we should verify."
cvsdist c08065
  :group 'ssl
cvsdist c08065
  :type '(choice (const :tag "No verification" :value 0)
cvsdist c08065
		 (const :tag "Verification required" :value 1)
cvsdist c08065
		 (const :tag "Reject connection if verification fails" :value 3)
cvsdist c08065
		 (const :tag "SSL_VERIFY_CLIENT_ONCE" :value 5)))
cvsdist c08065
cvsdist c08065
(defcustom ssl-program-name "openssl"
cvsdist c08065
  "*The program to run in a subprocess to open an SSL connection."
cvsdist c08065
  :group 'ssl
cvsdist c08065
  :type 'string)
cvsdist c08065
cvsdist c08065
(defcustom ssl-program-arguments
cvsdist c08065
  '("s_client"
cvsdist c08065
    "-quiet"
cvsdist c08065
    "-host" host
cvsdist c08065
    "-port" service
cvsdist c08065
    "-verify" (int-to-string ssl-certificate-verification-policy)
cvsdist c08065
    "-CApath" ssl-certificate-directory
cvsdist c08065
    )
cvsdist c08065
  "*Arguments that should be passed to the program `ssl-program-name'.
cvsdist c08065
This should be used if your SSL program needs command line switches to
cvsdist c08065
specify any behaviour (certificate file locations, etc).
cvsdist c08065
The special symbols 'host and 'port may be used in the list of arguments
cvsdist c08065
and will be replaced with the hostname and service/port that will be connected
cvsdist c08065
to."
cvsdist c08065
  :group 'ssl
cvsdist c08065
  :type 'list)
cvsdist c08065
cvsdist c08065
(defun ssl-certificate-information (der)
cvsdist c08065
  "Return an assoc list of information about a certificate in DER format."
cvsdist c08065
  (let ((certificate (concat "-----BEGIN CERTIFICATE-----\n"
cvsdist c08065
			     (base64-encode-string der)
cvsdist c08065
			     "\n-----END CERTIFICATE-----\n"))
cvsdist c08065
	(exit-code 0))
cvsdist c08065
    (save-excursion
cvsdist c08065
      (set-buffer (get-buffer-create " *openssl*"))
cvsdist c08065
      (erase-buffer)
cvsdist c08065
      (insert certificate)
cvsdist c08065
      (setq exit-code (condition-case ()
cvsdist c08065
			  (call-process-region (point-min) (point-max)
cvsdist c08065
					       ssl-program-name
cvsdist c08065
					       t (list (current-buffer) nil) t
cvsdist c08065
					       "x509"
cvsdist c08065
					       "-subject" ; Print the subject DN
cvsdist c08065
					       "-issuer" ; Print the issuer DN
cvsdist c08065
					       "-dates" ; Both before and after dates
cvsdist c08065
					       "-serial" ; print out serial number
cvsdist c08065
					       "-noout" ; Don't spit out the certificate
cvsdist c08065
					       )
cvsdist c08065
			(error -1)))
cvsdist c08065
      (if (/= exit-code 0)
cvsdist c08065
	  nil
cvsdist c08065
	(let ((vals nil))
cvsdist c08065
	  (goto-char (point-min))
cvsdist c08065
	  (while (re-search-forward "^\\([^=\n\r]+\\)\\s *=\\s *\\(.*\\)" nil t)
cvsdist c08065
	    (push (cons (match-string 1) (match-string 2)) vals))
cvsdist c08065
	  vals)))))
cvsdist c08065
  
cvsdist c08065
(defun ssl-accept-ca-certificate ()
cvsdist c08065
  "Ask if the user is willing to accept a new CA certificate. The buffer-name
cvsdist c08065
should be the intended name of the certificate, and the buffer should probably
cvsdist c08065
be in DER encoding"
cvsdist c08065
  ;; TODO, check if it is really new or if we already know it
cvsdist c08065
  (let* ((process-connection-type nil)
cvsdist c08065
	 (tmpbuf (generate-new-buffer "X509 CA Certificate Information"))
cvsdist c08065
	 (response (save-excursion
cvsdist c08065
		     (and (eq 0 
cvsdist c08065
			      (apply 'call-process-region
cvsdist c08065
				     (point-min) (point-max) 
cvsdist c08065
				     ssl-view-certificate-program-name 
cvsdist c08065
				     nil tmpbuf t
cvsdist c08065
				     ssl-view-certificate-program-arguments))
cvsdist c08065
			  (switch-to-buffer tmpbuf)
cvsdist c08065
			  (goto-char (point-min))
cvsdist c08065
			  (or (recenter) t)
cvsdist c08065
			  (yes-or-no-p
cvsdist c08065
			   "Accept this CA to vouch for secure server identities? ")
cvsdist c08065
			  (kill-buffer tmpbuf)))))
cvsdist c08065
    (if (not response)
cvsdist c08065
	nil
cvsdist c08065
      (if (not (file-directory-p ssl-certificate-directory))
cvsdist c08065
	  (make-directory ssl-certificate-directory))
cvsdist c08065
      (case ssl-certificate-directory-style
cvsdist c08065
	(ssleay
cvsdist c08065
	 (base64-encode-region (point-min) (point-max))
cvsdist c08065
	 (goto-char (point-min))
cvsdist c08065
	 (insert "-----BEGIN CERTIFICATE-----\n")
cvsdist c08065
	 (goto-char (point-max))
cvsdist c08065
	 (insert "-----END CERTIFICATE-----\n")
cvsdist c08065
	 (let ((f (expand-file-name
cvsdist c08065
		   (concat (file-name-sans-extension (buffer-name)) ".pem")
cvsdist c08065
		   ssl-certificate-directory)))
cvsdist c08065
	   (write-file f)
cvsdist c08065
	   (call-process ssl-rehash-program-name
cvsdist c08065
			 nil nil nil
cvsdist c08065
			 (expand-file-name ssl-certificate-directory))))))))
cvsdist c08065
cvsdist c08065
(defun open-ssl-stream (name buffer host service)
cvsdist c08065
  "Open a SSL connection for a service to a host.
cvsdist c08065
Returns a subprocess-object to represent the connection.
cvsdist c08065
Input and output work as for subprocesses; `delete-process' closes it.
cvsdist c08065
Args are NAME BUFFER HOST SERVICE.
cvsdist c08065
NAME is name for process.  It is modified if necessary to make it unique.
cvsdist c08065
BUFFER is the buffer (or buffer-name) to associate with the process.
cvsdist c08065
 Process output goes at end of that buffer, unless you specify
cvsdist c08065
 an output stream or filter function to handle the output.
cvsdist c08065
 BUFFER may be also nil, meaning that this process is not associated
cvsdist c08065
 with any buffer
cvsdist c08065
Third arg is name of the host to connect to, or its IP address.
cvsdist c08065
Fourth arg SERVICE is name of the service desired, or an integer
cvsdist c08065
specifying a port number to connect to."
cvsdist c08065
  (if (integerp service) (setq service (int-to-string service)))
cvsdist c08065
  (let* ((process-connection-type nil)
cvsdist c08065
	 (port service)
cvsdist c08065
	 (proc (eval
cvsdist c08065
		(`
cvsdist c08065
		 (start-process name buffer ssl-program-name
cvsdist c08065
				(,@ ssl-program-arguments))))))
cvsdist c08065
    (process-kill-without-query proc)
cvsdist c08065
    proc))
cvsdist c08065
cvsdist c08065
(provide 'ssl)