1 ;;; starttls.el --- STARTTLS support via wrapper around GNU TLS
3 ;; Copyright (C) 2003 Free Software Foundation, Inc.
5 ;; Author: Simon Josefsson <simon@josefsson.org>
6 ;; Keywords: comm, tls, gnutls, ssl
8 ;; This file is part of GNU Emacs.
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING. If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
27 ;; This package implements a simple wrapper around the GNU TLS command
28 ;; line application "gnutls-cli" to make Emacs support STARTTLS.
30 ;; Usage is similar to `open-network-stream', i.e.:
32 ;; (setq tmp (open-starttls-stream "test" (current-buffer) "cyrus.andrew.cmu.edu" 143))
34 ;; (process-send-string tmp ". starttls\n")
36 ;; (negotiate-starttls tmp)
37 ;; "*** Starting TLS handshake
38 ;; - Certificate type: X.509
39 ;; - Certificate info:
40 ;; # Certificate is valid since: Thu Jun 26 19:00:00 CEST 2003
41 ;; # Certificate expires: Sat Jun 26 19:00:00 CEST 2004
42 ;; # Certificate fingerprint: 8d 59 d6 e1 c9 91 dc 5a bb 38 47 8c ec 85 1b 99
43 ;; # Certificate serial number: 3e fb 52 ce
44 ;; # Certificate version: #3
45 ;; # Certificate public key algorithm: RSA
46 ;; # Modulus: 1024 bits
47 ;; # CN=cyrus.andrew.cmu.edu,OU=Computing Services,O=Carnegie Mellon University,L=Pittsburgh,ST=Pennsylvania,C=US
48 ;; # Certificate Issuer's info:
49 ;; # CN=CMU CA mail 1,OU=Computing Services,O=Carnegie Mellon University,L=Pittsburgh,ST=Pennsylvania,C=US
51 ;; - Peer's certificate is NOT trusted
53 ;; - Key Exchange: RSA
54 ;; - Cipher: ARCFOUR 128
56 ;; - Compression: NULL
58 ;; (process-send-string tmp ". capability\n")
60 ;; (process-send-string tmp ". logout\n")
63 ;; Resolving 'cyrus.andrew.cmu.edu'...
64 ;; Connecting to '128.2.10.174:143'...
66 ;; - Simple Client Mode:
68 ;; * OK mail-fe4.andrew.cmu.edu Cyrus IMAP4 Murder v2.1.15-077 server ready
\r
69 ;; . OK Begin TLS negotiation now
\r
70 ;; * CAPABILITY IMAP4 IMAP4rev1 ACL QUOTA LITERAL+ MAILBOX-REFERRALS NAMESPACE UIDPLUS ID NO_ATOMIC_RENAME UNSELECT CHILDREN MULTIAPPEND SORT THREAD=ORDEREDSUBJECT THREAD=REFERENCES AUTH=PLAIN AUTH=KERBEROS_V4 AUTH=GSSAPI AUTH=ANONYMOUS ANNOTATEMORE
\r
72 ;; * BYE LOGOUT received
\r
74 ;; *** Received corrupted data(-9) - server has terminated the connection abnormally
76 ;; Process test<9> finished
81 (autoload 'format-spec "format-spec")
82 (autoload 'format-spec-make "format-spec"))
84 (defgroup starttls nil
85 "Negotiated Transport Layer Security (STARTTLS) parameters."
88 (defcustom starttls-programs '("gnutls-cli -s -p %p %h"
89 "gnutls-cli -s -p %p %h --protocols ssl3")
90 "List of strings containing commands to open STARTTLS stream to a host.
91 Each entry in the list is tried until a connection is successful.
92 %s is replaced with server hostname, %p with port to connect to.
93 The program should read input on stdin and write output to
94 stdout. Also see `starttls-connect' and `starttls-success' for
95 what the program should output after initial connection and
96 successful negotiation respectively."
97 :type '(repeat string)
100 (defcustom starttls-process-connection-type t
101 "*Value for `process-connection-type' to use when starting STARTTLS process.
102 Note that setting this to nil likely does not work, as
103 `process-send-eof' used in `negotiate-starttls' behave
104 differently depending on this setting, and it closes the
105 sub-process if this variable is set to nil."
109 (defcustom starttls-connect "- Simple Client Mode:"
110 "*Regular expression indicating successful connection.
111 The default is what GNUTLS's \"gnutls-cli\" outputs."
112 ;; cli.c:main() print this string when it is starting to run in the
113 ;; application read/write phase. If the logic, or the string
114 ;; itself, is modified, this have to be updated.
118 (defcustom starttls-failure "*** Handshake has failed"
119 "*Regular expression indicating failed TLS handshake.
120 The default is what GNUTLS's \"gnutls-cli\" outputs."
121 ;; cli.c:do_handshake() print this string on failure. If the logic,
122 ;; or the string itself, is modified, this have to be updated.
126 (defcustom starttls-success "- Compression: "
127 "*Regular expression indicating completed TLS handshakes.
128 The default is what GNUTLS's \"gnutls-cli\" outputs."
129 ;; cli.c:do_handshake() calls, on success, common.c:print_info(),
130 ;; that unconditionally print this string last. If that logic, or
131 ;; the string itself, is modified, this have to be updated.
135 (defun negotiate-starttls (process)
136 "Negotiate TLS on process opened by `open-starttls-stream'.
137 This should typically only be done once. It typically return a
138 multi-line informational message with information about the
139 handshake, or NIL on failure."
140 (let (buffer response old-max done-ok done-bad)
141 (if (null (setq buffer (process-buffer process)))
142 ;; XXX how to remove/extract the TLS negotiation junk?
143 (process-send-eof process)
144 (with-current-buffer buffer
146 (goto-char (point-max))
147 (setq old-max (point))
148 ;; `process-send-eof' closes sub-process unless we force
149 ;; `process-connection-type' to non-nil. A cleaner solution
151 ;; (process-send-string process (string-as-unibyte (format "%c" 4)))
152 ;; or something, but I could not get that to work.
153 (process-send-eof process)
155 (memq (process-status process) '(open run))
157 (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
161 done-ok (re-search-forward starttls-success nil t)
162 done-bad (re-search-forward starttls-failure nil t))
163 (not (or done-ok done-bad)))))
164 (accept-process-output process 1 100)
166 (setq info (buffer-substring-no-properties old-max (point-max)))
167 (delete-region old-max (point-max))
168 (if (or (and done-ok (not done-bad))
169 ;; prevent mitm that fake success msg after failure msg.
170 (and done-ok done-bad (< done-ok done-bad)))
172 (message "STARTTLS negotiation failed: %s" info)
175 (defun open-starttls-stream (name buffer host service)
176 "Open a TLS connection for a service to a host.
177 Returns a subprocess-object to represent the connection.
178 Input and output work as for subprocesses; `delete-process' closes it.
179 Args are NAME BUFFER HOST SERVICE.
180 NAME is name for process. It is modified if necessary to make it unique.
181 BUFFER is the buffer (or buffer-name) to associate with the process.
182 Process output goes at end of that buffer, unless you specify
183 an output stream or filter function to handle the output.
184 Third arg is name of the host to connect to, or its IP address.
185 Fourth arg SERVICE is name of the service desired, or an integer
186 specifying a port number to connect to."
187 (let ((cmds starttls-programs) cmd done old-max)
188 (message "Opening STARTTLS connection to `%s'..." host)
189 (with-current-buffer buffer
190 (setq old-max (point-max)))
191 (while (and (not done) (setq cmd (pop cmds)))
192 (message "Opening STARTTLS connection with `%s'..." cmd)
193 (let* ((process-connection-type starttls-process-connection-type)
194 (process (start-process
195 name buffer shell-file-name shell-command-switch
200 ?p (if (integerp service)
201 (int-to-string service)
205 (memq (process-status process) '(open run))
207 (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
208 (goto-char (point-min))
209 (not (setq done (re-search-forward
210 starttls-connect nil t)))))
211 (accept-process-output process 0 100)
213 (message "Opening STARTTLS connection with `%s'...%s" cmd
214 (if done "done" "failed"))
217 (with-current-buffer buffer
218 (delete-region old-max (point-max)))
220 (delete-process process))))
221 (message "Opening STARTTLS connection to `%s'...%s"
222 host (if done "done" "failed"))
225 ;; Compatibility with starttls.el by Daiki Ueno <ueno@unixuser.org>:
226 (defalias 'starttls-open-stream 'open-starttls-stream)
227 (defalias 'starttls-negotiate 'negotiate-starttls)
231 ;;; starttls.el ends here