emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 344f769: Add support for using a TLS client certificate with 'erc


From: Amin Bandali
Subject: master 344f769: Add support for using a TLS client certificate with 'erc-tls' (bug#47788)
Date: Thu, 22 Apr 2021 20:23:48 -0400 (EDT)

branch: master
commit 344f769491a84b6d47ee3722054b214167572219
Author: Amin Bandali <bandali@gnu.org>
Commit: Amin Bandali <bandali@gnu.org>

    Add support for using a TLS client certificate with 'erc-tls' (bug#47788)
    
    * lisp/erc/erc-backend.el (erc-session-client-certificate): New
    buffer-local variable storing the TLS client certificate used for the
    current connection.
    (erc-open-network-stream): Use open-network-stream instead of
    make-network-process, and pass any additional arguments to it.
    (erc-server-connect): Add an optional client-certificate argument
    that if present is passed with the :client-certificate keyword as part
    of the arguments to erc-server-connect-function.
    * lisp/erc/erc.el (erc-open): Add new optional client-certificate
    argument, set it as erc-session-client-certificate, and pass it along
    to erc-server-connect.
    (erc): Clarify documentation string with respect to the full-name
    argument.
    (erc-tls): Add new client-certificate keyword argument and pass it in
    the direct call to erc-open (instead of going through erc).
    (erc-open-tls-stream): Pass any additional arguments (such as
    :client-certificate) to open-network-stream.  Also allow overriding
    :nowait if desired.
    
    * doc/misc/erc.texi: Add documentation for erc-tls, including the new
    :client-certificate argument.
    
    * etc/NEWS: Announce the change.
---
 doc/misc/erc.texi       |  73 +++++++++++++++++++++++++++++++++--
 etc/NEWS                |  36 +++++++++++++++++
 lisp/erc/erc-backend.el |  30 ++++++++++-----
 lisp/erc/erc.el         | 100 +++++++++++++++++++++++++++++++++++++++---------
 4 files changed, 208 insertions(+), 31 deletions(-)

diff --git a/doc/misc/erc.texi b/doc/misc/erc.texi
index d635cac..45a753d 100644
--- a/doc/misc/erc.texi
+++ b/doc/misc/erc.texi
@@ -514,15 +514,82 @@ Non-interactively, it takes the following keyword 
arguments.
 
 That is, if called with the following arguments, @var{server} and
 @var{full-name} will be set to those values, whereas
-@code{erc-compute-port}, @code{erc-compute-nick} and
-@code{erc-compute-full-name} will be invoked for the values of the other
-parameters.
+@code{erc-compute-port} and @code{erc-compute-nick} will be invoked
+for the values of the other parameters.
 
 @example
 (erc :server "chat.freenode.net" :full-name "Harry S Truman")
 @end example
 @end defun
 
+To connect securely over an encrypted TLS connection, use @kbd{M-x
+erc-tls}.
+
+@defun erc-tls
+Select connection parameters and run ERC over TLS@.
+Non-interactively, it takes the following keyword arguments.
+
+@itemize @bullet
+@item @var{server}
+@item @var{port}
+@item @var{nick}
+@item @var{password}
+@item @var{full-name}
+@item @var{client-certificate}
+@end itemize
+
+That is, if called with the following arguments, @var{server} and
+@var{full-name} will be set to those values, whereas
+@code{erc-compute-port} and @code{erc-compute-nick} will be invoked
+for the values of the other parameters, and @code{client-certificate}
+will be @code{nil}.
+
+@example
+(erc-tls :server "chat.freenode.net" :full-name "Harry S Truman")
+@end example
+
+To use a certificate with @code{erc-tls}, specify the optional
+@var{client-certificate} keyword argument, whose value should be as
+described in the documentation of @code{open-network-stream}: if
+non-@code{nil}, it should either be a list where the first element is
+the file name of the private key corresponding to a client certificate
+and the second element is the file name of the client certificate
+itself to use when connecting over TLS, or @code{t}, which means that
+@code{auth-source} will be queried for the private key and the
+certificate.  Authenticating using a TLS client certificate is also
+refered to as ``CertFP'' (Certificate Fingerprint) authentication by
+various IRC networks.
+
+Examples of use:
+
+@example
+(erc-tls :server "chat.freenode.net" :port 6697
+         :client-certificate
+         '("/home/bandali/my-cert.key"
+           "/home/bandali/my-cert.crt"))
+@end example
+
+@example
+(erc-tls :server "chat.freenode.net" :port 6697
+         :client-certificate
+         `(,(expand-file-name "~/cert-freenode.key")
+           ,(expand-file-name "~/cert-freenode.crt")))
+@end example
+
+@example
+(erc-tls :server "chat.freenode.net" :port 6697
+         :client-certificate t)
+@end example
+
+In the case of @code{:client-certificate t}, you will need to add a
+line like the following to your authinfo file
+(e.g. @file{~/.authinfo.gpg}):
+
+@example
+machine chat.freenode.net key /home/bandali/my-cert.key cert 
/home/bandali/my-cert.crt
+@end example
+@end defun
+
 @subheading Server
 
 @defun erc-compute-server &optional server
diff --git a/etc/NEWS b/etc/NEWS
index 6fe4e98..34aeaf0 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1746,6 +1746,42 @@ type for highlighting the entire message but not the 
sender's nick.
 The 'erc-status-sidebar' package which provides a HexChat-like
 activity overview sidebar for joined IRC channels is now part of ERC.
 
++++
+*** erc-tls now supports specifying a TLS client certificate.
+The 'erc-tls' function has been updated to allow specifying a TLS
+client certificate for authentication, as an alternative to NickServ
+password-based authentication.  This is referred to as "CertFP" (short
+for Certificate Fingerprint) by several IRC networks.
+
+To use a certificate with 'erc-tls', specify the ':client-certificate'
+optional parameter, whose value should be as described in the
+documentation of 'open-network-stream': if non-nil, it should either
+be a list where the first element is the file name of the private key
+corresponding to a client certificate and the second element is the
+file name of the client certificate itself to use when connecting over
+TLS, or t, which means that 'auth-source' will be queried for the
+private key and the certificate.
+
+Examples of use:
+
+    (erc-tls :server "chat.freenode.net" :port 6697
+             :client-certificate
+             '("/home/bandali/my-cert.key"
+               "/home/bandali/my-cert.crt"))
+
+    (erc-tls :server "chat.freenode.net" :port 6697
+             :client-certificate
+             `(,(expand-file-name "~/cert-freenode.key")
+               ,(expand-file-name "~/cert-freenode.crt")))
+
+    (erc-tls :server "chat.freenode.net" :port 6697
+             :client-certificate t)
+
+In the case of ':client-certificate t', you will need to add a line
+like the following to your authinfo file (e.g. "~/.authinfo.gpg"):
+
+    machine chat.freenode.net key /home/bandali/my-cert.key cert 
/home/bandali/my-cert.crt
+
 ** Battery
 
 ---
diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index b1f97ae..67db572 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -138,6 +138,13 @@ Use `erc-current-nick' to access this.")
 (defvar-local erc-session-port nil
   "The port used to connect to.")
 
+(defvar-local erc-session-client-certificate nil
+  "TLS client certificate used when connecting over TLS.
+If non-nil, should either be a list where the first element is
+the certificate key file name, and the second element is the
+certificate file name itself, or t, which means that
+`auth-source' will be queried for the key and the certificate.")
+
 (defvar-local erc-server-announced-name nil
   "The name the server announced to use.")
 
@@ -505,18 +512,23 @@ The current buffer is given by BUFFER."
          (memq (process-status erc-server-process) '(run open)))))
 
 ;;;; Connecting to a server
-(defun erc-open-network-stream (name buffer host service)
-  "As `open-network-stream', but does non-blocking IO"
-  (make-network-process :name name :buffer  buffer
-                        :host host :service service :nowait t))
+(defun erc-open-network-stream (name buffer host service &rest parameters)
+  "Like `open-network-stream', but does non-blocking IO."
+  (let ((p (plist-put parameters :nowait t)))
+    (open-network-stream name buffer host service p)))
 
-(defun erc-server-connect (server port buffer)
+(defun erc-server-connect (server port buffer &optional client-certificate)
   "Perform the connection and login using the specified SERVER and PORT.
-We will store server variables in the buffer given by BUFFER."
-  (let ((msg (erc-format-message 'connect ?S server ?p port)) process)
+We will store server variables in the buffer given by BUFFER.
+CLIENT-CERTIFICATE may optionally be used to specify a TLS client
+certificate to use for authentication when connecting over
+TLS (see `erc-session-client-certificate' for more details)."
+  (let ((msg (erc-format-message 'connect ?S server ?p port)) process
+        (args `(,(format "erc-%s-%s" server port) nil ,server ,port)))
+    (when client-certificate
+      (setq args `(,@args :client-certificate ,client-certificate)))
     (message "%s" msg)
-    (setq process (funcall erc-server-connect-function
-                           (format "erc-%s-%s" server port) nil server port))
+    (setq process (apply erc-server-connect-function args))
     (unless (processp process)
       (error "Connection attempt failed"))
     ;; Misc server variables
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index e20aa80..43661a2 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -47,8 +47,12 @@
 ;;
 ;; M-x erc RET
 ;;
-;; After you are connected to a server, you can use C-h m or have a look at
-;; the ERC menu.
+;; or
+;;
+;; M-x erc-tls RET
+;;
+;; to connect over TLS (encrypted).  Once you are connected to a
+;; server, you can use C-h m or have a look at the ERC menu.
 
 ;;; Code:
 
@@ -1967,7 +1971,8 @@ removed from the list will be disabled."
        (switch-to-buffer buffer)))))
 
 (defun erc-open (&optional server port nick full-name
-                           connect passwd tgt-list channel process)
+                           connect passwd tgt-list channel process
+                           client-certificate)
   "Connect to SERVER on PORT as NICK with FULL-NAME.
 
 If CONNECT is non-nil, connect to the server.  Otherwise assume
@@ -1977,6 +1982,13 @@ target CHANNEL.
 Use PASSWD as user password on the server.  If TGT-LIST is
 non-nil, use it to initialize `erc-default-recipients'.
 
+CLIENT-CERTIFICATE, if non-nil, should either be a list where the
+first element is the file name of the private key corresponding
+to a client certificate and the second element is the file name
+of the client certificate itself to use when connecting over TLS,
+or t, which means that `auth-source' will be queried for the
+private key and the certificate.
+
 Returns the buffer for the given server or channel."
   (let ((server-announced-name (when (and (boundp 'erc-session-server)
                                           (string= server erc-session-server))
@@ -2059,6 +2071,8 @@ Returns the buffer for the given server or channel."
                 (if (functionp secret)
                     (funcall secret)
                   secret))))
+    ;; client certificate (only useful if connecting over TLS)
+    (setq erc-session-client-certificate client-certificate)
     ;; debug output buffer
     (setq erc-dbuf
           (when erc-log-p
@@ -2079,7 +2093,10 @@ Returns the buffer for the given server or channel."
     (run-hook-with-args 'erc-connect-pre-hook buffer)
 
     (when connect
-      (erc-server-connect erc-session-server erc-session-port buffer))
+      (erc-server-connect erc-session-server
+                          erc-session-port
+                          buffer
+                          erc-session-client-certificate))
     (erc-update-mode-line)
 
     ;; Now display the buffer in a window as per user wishes.
@@ -2196,22 +2213,22 @@ parameters SERVER and NICK."
   "ERC is a powerful, modular, and extensible IRC client.
 This function is the main entry point for ERC.
 
-It permits you to select connection parameters, and then starts ERC.
+It allows selecting connection parameters, and then starts ERC.
 
 Non-interactively, it takes the keyword arguments
    (server (erc-compute-server))
    (port   (erc-compute-port))
    (nick   (erc-compute-nick))
    password
-   (full-name (erc-compute-full-name)))
+   (full-name (erc-compute-full-name))
 
 That is, if called with
 
    (erc :server \"chat.freenode.net\" :full-name \"Harry S Truman\")
 
-then the server and full-name will be set to those values, whereas
-`erc-compute-port', `erc-compute-nick' and `erc-compute-full-name' will
-be invoked for the values of the other parameters."
+then the server and full-name will be set to those values,
+whereas `erc-compute-port' and `erc-compute-nick' will be invoked
+for the values of the other parameters."
   (interactive (erc-select-read-args))
   (erc-open server port nick full-name t password))
 
@@ -2220,21 +2237,66 @@ be invoked for the values of the other parameters."
 (defalias 'erc-ssl #'erc-tls)
 
 ;;;###autoload
-(defun erc-tls (&rest r)
-  "Interactively select TLS connection parameters and run ERC.
-Arguments are the same as for `erc'."
+(cl-defun erc-tls (&key (server (erc-compute-server))
+                        (port   (erc-compute-port))
+                        (nick   (erc-compute-nick))
+                        password
+                        (full-name (erc-compute-full-name))
+                        client-certificate)
+  "ERC is a powerful, modular, and extensible IRC client.
+This function is the main entry point for ERC over TLS.
+
+It allows selecting connection parameters, and then starts ERC
+over TLS.
+
+Non-interactively, it takes the keyword arguments
+   (server (erc-compute-server))
+   (port   (erc-compute-port))
+   (nick   (erc-compute-nick))
+   password
+   (full-name (erc-compute-full-name))
+   client-certificate
+
+That is, if called with
+
+   (erc-tls :server \"chat.freenode.net\" :full-name \"Harry S Truman\")
+
+then the server and full-name will be set to those values,
+whereas `erc-compute-port' and `erc-compute-nick' will be invoked
+for the values of their respective parameters.
+
+CLIENT-CERTIFICATE, if non-nil, should either be a list where the
+first element is the certificate key file name, and the second
+element is the certificate file name itself, or t, which means
+that `auth-source' will be queried for the key and the
+certificate.  Authenticating using a TLS client certificate is
+also refered to as \"CertFP\" (Certificate Fingerprint)
+authentication by various IRC networks.
+
+Example usage:
+
+    (erc-tls :server \"chat.freenode.net\" :port 6697
+             :client-certificate
+             '(\"/data/bandali/my-cert.key\"
+               \"/data/bandali/my-cert.crt\"))"
   (interactive (let ((erc-default-port erc-default-port-tls))
                 (erc-select-read-args)))
   (let ((erc-server-connect-function 'erc-open-tls-stream))
-    (apply #'erc r)))
+    (erc-open server port nick full-name t password
+              nil nil nil client-certificate)))
 
-(defun erc-open-tls-stream (name buffer host port)
+(defun erc-open-tls-stream (name buffer host port &rest parameters)
   "Open an TLS stream to an IRC server.
-The process will be given the name NAME, its target buffer will be
-BUFFER.  HOST and PORT specify the connection target."
-  (open-network-stream name buffer host port
-                      :nowait t
-                       :type 'tls))
+The process will be given the name NAME, its target buffer will
+be BUFFER.  HOST and PORT specify the connection target.
+PARAMETERS should be a sequence of keywords and values, per
+`open-network-stream'."
+  (let ((p (plist-put parameters :type 'tls))
+        args)
+    (unless (plist-member p :nowait)
+      (setq p (plist-put p :nowait t)))
+    (setq args `(,name ,buffer ,host ,port ,@p))
+    (apply #'open-network-stream args)))
 
 ;;; Displaying error messages
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]