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