1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-15 18:40:39 -08:00

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.
This commit is contained in:
Amin Bandali 2021-04-22 20:22:38 -04:00
parent 1c3a86e7fc
commit 344f769491
No known key found for this signature in database
GPG key ID: 8B44A0CDC7B956F2
4 changed files with 208 additions and 31 deletions

View file

@ -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

View file

@ -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
---

View file

@ -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

View file

@ -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