From 2f13e12882a32246d9b1d57e111ad17e0773ff54 Mon Sep 17 00:00:00 2001 From: Robert Pluim Date: Fri, 21 Dec 2018 11:58:00 +0100 Subject: [PATCH] Check for client certificates when using GnuTLS To: emacs-devel@gnu.org This fixes Bug#33780, and extends the documentation to describe how to enable use of client certificates. * lisp/net/network-stream.el (network-stream-certificate): Correct order of parameters to plist-get. (network-stream-open-tls): Pass all received parameters to open-gnutls-stream, not just :nowait. * lisp/net/gnutls.el (open-gnutls-stream): Add optional plist to arglist. Derive client certificate(s) and keys(s) from plist (maybe via auth-source) and pass to gnutls-boot-parameters and gnutls-negotiate. (network-stream-certificate): Add declare-function form for it. * doc/misc/auth.texi (Help for users): Describe format to use for client key/cert specification. * doc/misc/emacs-gnutls.texi (Help For Developers): Describe usage of new optional plist argument. Add crossref to description of .authinfo format for client key/cert specification. * etc/NEWS: Describe new client certificate functionality for 'open-network-stream' --- doc/misc/auth.texi | 9 +++++++++ doc/misc/emacs-gnutls.texi | 12 +++++++++++- etc/NEWS | 7 +++++++ lisp/net/gnutls.el | 31 +++++++++++++++++++++---------- lisp/net/network-stream.el | 5 +++-- 5 files changed, 51 insertions(+), 13 deletions(-) diff --git a/doc/misc/auth.texi b/doc/misc/auth.texi index fcbc83ead5..68b8553d58 100644 --- a/doc/misc/auth.texi +++ b/doc/misc/auth.texi @@ -109,6 +109,15 @@ Help for users @code{auth-source-search} queries. You can also use @code{login} and @code{account}. +You can also use this file to specify client certificates to use when +setting up TLS connections. The format is: +@example +machine @var{mymachine} port @var{myport} key "@var{key}" cert "@var{cert}" +@end example + +@var{key} and @var{cert} are filenames containing the key and +certificate to use respectively. + You can use spaces inside a password or other token by surrounding the token with either single or double quotes. diff --git a/doc/misc/emacs-gnutls.texi b/doc/misc/emacs-gnutls.texi index a690ccfcce..90c2d217e2 100644 --- a/doc/misc/emacs-gnutls.texi +++ b/doc/misc/emacs-gnutls.texi @@ -179,7 +179,7 @@ Help For Developers You should not have to use the @file{gnutls.el} functions directly. But you can test them with @code{open-gnutls-stream}. -@defun open-gnutls-stream name buffer host service &optional nowait +@defun open-gnutls-stream name buffer host service &optional nowait parameters This function creates a buffer connected to a specific @var{host} and @var{service} (port number or service name). The parameters and their syntax are the same as those given to @code{open-network-stream} @@ -191,6 +191,16 @@ Help For Developers asynchronous, and the connection process will be returned to the caller before TLS negotiation has happened. +@var{parameters} is a plist which is currently checked only for +@code{:client-certificate}. Any resulting client certificates are +passed down to the lower TLS layers. Set @code{:client certificate t} +to trigger looking up of the certificates using the auth-source +library. The format used by @file{.authinfo} to specify the +per-server keys is described in @xref{Help for users,,auth-source, +auth, Emacs auth-source Library}. + +Example calls: + @lisp ;; open a HTTPS connection (open-gnutls-stream "tls" "tls-buffer" "yourserver.com" "https") diff --git a/etc/NEWS b/etc/NEWS index 0624c5690b..74943fb2ff 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -199,6 +199,13 @@ issued), you can either set 'network-security-protocol-checks' to nil, or adjust the elements in that variable to only happen on the 'high' security level (assuming you use the 'medium' level). ++++ +** Native GnuTLS connections can now use client certificates. +Previously, this support was only available when using the external +gnutls-cli command. Call 'open-network-stream' with +':client-certificate t' to trigger looking up of per-server +certificates via 'auth-source'. + +++ ** New function 'fill-polish-nobreak-p', to be used in 'fill-nobreak-predicate'. It blocks line breaking after a one-letter word, also in the case when diff --git a/lisp/net/gnutls.el b/lisp/net/gnutls.el index 315932b7e6..30f933fa48 100644 --- a/lisp/net/gnutls.el +++ b/lisp/net/gnutls.el @@ -38,6 +38,9 @@ (require 'cl-lib) (require 'puny) +(declare-function network-stream-certificate "network-stream" + (host service parameters)) + (defgroup gnutls nil "Emacs interface to the GnuTLS library." :version "24.1" @@ -138,7 +141,7 @@ gnutls-min-prime-bits (integer :tag "Number of bits" 512)) :group 'gnutls) -(defun open-gnutls-stream (name buffer host service &optional nowait) +(defun open-gnutls-stream (name buffer host service &optional nowait parameters) "Open a SSL/TLS connection for a service to a host. Returns a subprocess-object to represent the connection. Input and output work as for subprocesses; `delete-process' closes it. @@ -155,6 +158,10 @@ open-gnutls-stream Fifth arg NOWAIT (which is optional) means that the socket should be opened asynchronously. The connection process will be returned to the caller before TLS negotiation has happened. +Sixth arg PARAMETERS is an optional property list. It is currently +checked for :client-certificate only. This allows specifying the +client certificates and keys used to set up the connection. +See `open-network-stream' for a complete description. Usage example: @@ -168,19 +175,23 @@ open-gnutls-stream documentation for the specific parameters you can use to open a GnuTLS connection, including specifying the credential type, trust and key files, and priority string." - (let ((process (open-network-stream - name buffer host service - :nowait nowait - :tls-parameters - (and nowait - (cons 'gnutls-x509pki - (gnutls-boot-parameters - :type 'gnutls-x509pki - :hostname (puny-encode-domain host))))))) + (let* ((cert (network-stream-certificate host service parameters)) + (keylist (and cert (list cert))) + (process (open-network-stream + name buffer host service + :nowait nowait + :tls-parameters + (and nowait + (cons 'gnutls-x509pki + (gnutls-boot-parameters + :type 'gnutls-x509pki + :keylist keylist + :hostname (puny-encode-domain host))))))) (if nowait process (gnutls-negotiate :process process :type 'gnutls-x509pki + :keylist keylist :hostname (puny-encode-domain host))))) (define-error 'gnutls-error "GnuTLS error") diff --git a/lisp/net/network-stream.el b/lisp/net/network-stream.el index a0589e25a4..26f92d5aa8 100644 --- a/lisp/net/network-stream.el +++ b/lisp/net/network-stream.el @@ -196,7 +196,7 @@ open-network-stream (car result)))))) (defun network-stream-certificate (host service parameters) - (let ((spec (plist-get :client-certificate parameters))) + (let ((spec (plist-get parameters :client-certificate))) (cond ((listp spec) ;; Either nil or a list with a key/certificate pair. @@ -389,7 +389,8 @@ network-stream-open-tls (stream (if (gnutls-available-p) (open-gnutls-stream name buffer host service - (plist-get parameters :nowait)) + (plist-get parameters :nowait) + parameters) (require 'tls) (open-tls-stream name buffer host service))) (eoc (plist-get parameters :end-of-command))) -- 2.19.1.816.gcd69ec8cde.dirty