>From cec5c02a8a82a904a68d705ad54c3ccdcd0774cc Mon Sep 17 00:00:00 2001 From: Amin Bandali Date: Wed, 14 Apr 2021 23:32:54 -0400 Subject: [PATCH] Add support for TLS client certificates to 'erc-tls' --- lisp/erc/erc-backend.el | 32 +++++++++++++++------- lisp/erc/erc.el | 59 ++++++++++++++++++++++++++++++----------- 2 files changed, 66 insertions(+), 25 deletions(-) diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el index b1f97aea06..056cc5c68f 100644 --- a/lisp/erc/erc-backend.el +++ b/lisp/erc/erc-backend.el @@ -138,6 +138,16 @@ erc-session-connector (defvar-local erc-session-port nil "The port used to connect to.") +(defvar-local erc-session-cert-key nil + "File name of the private key of the client certificate used to connect over TLS. +The corresponding client certificate's file name must be set in +`erc-session-cert-crt'.") + +(defvar-local erc-session-cert-crt nil + "File name of the client certificate used to connect over TLS. +The file name of the corresponding client certificate's private +key must be set in `erc-session-cert-key'.") + (defvar-local erc-server-announced-name nil "The name the server announced to use.") @@ -505,18 +515,22 @@ erc-server-process-alive (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-key client-crt) "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-KEY and CLIENT-CRT may optionally be used to specify a TLS +client certificate." + (let ((msg (erc-format-message 'connect ?S server ?p port)) process + (args `(,(format "erc-%s-%s" server port) nil ,server ,port))) + (when (and client-key client-crt) + (setq args `(,@args :client-certificate (,client-key ,client-crt)))) (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 e20aa8057d..45594d7b87 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -1967,7 +1967,8 @@ erc-setup-buffer (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-key client-crt) "Connect to SERVER on PORT as NICK with FULL-NAME. If CONNECT is non-nil, connect to the server. Otherwise assume @@ -1977,6 +1978,10 @@ erc-open Use PASSWD as user password on the server. If TGT-LIST is non-nil, use it to initialize `erc-default-recipients'. +CLIENT-KEY and CLIENT-CRT can be file names for the private key +and certificate parts of a client certificate to use when +connecting over TLS. + Returns the buffer for the given server or channel." (let ((server-announced-name (when (and (boundp 'erc-session-server) (string= server erc-session-server)) @@ -2073,13 +2078,18 @@ erc-open (erc-display-prompt) (goto-char (point-max))) - (erc-determine-parameters server port nick full-name) + (erc-determine-parameters server port nick full-name + client-key client-crt) ;; Saving log file on exit (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-cert-key + erc-session-cert-crt)) (erc-update-mode-line) ;; Now display the buffer in a window as per user wishes. @@ -2192,7 +2202,9 @@ erc (port (erc-compute-port)) (nick (erc-compute-nick)) password - (full-name (erc-compute-full-name))) + (full-name (erc-compute-full-name)) + client-key + client-crt) "ERC is a powerful, modular, and extensible IRC client. This function is the main entry point for ERC. @@ -2203,17 +2215,25 @@ erc (port (erc-compute-port)) (nick (erc-compute-nick)) password - (full-name (erc-compute-full-name))) + (full-name (erc-compute-full-name)) + client-key + client-crt 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', `erc-compute-nick' and +`erc-compute-full-name' will be invoked for the values of the +other parameters. CLIENT-KEY and CLIENT-CRT, if non-nil, should +be file names for the private key and certificate parts of a +client certificate respectively to be used when connecting over +TLS." (interactive (erc-select-read-args)) - (erc-open server port nick full-name t password)) + (erc-open server port nick full-name t password + nil nil nil + client-key client-crt)) ;;;###autoload (defalias 'erc-select #'erc) @@ -2228,13 +2248,18 @@ erc-tls (let ((erc-server-connect-function 'erc-open-tls-stream)) (apply #'erc r))) -(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-get p :nowait) + (setq p (plist-put p :nowait t))) + (setq args `(,name ,buffer ,host ,port ,@p)) + (apply #'open-network-stream args))) ;;; Displaying error messages @@ -6030,7 +6055,7 @@ erc-login ;; connection properties' heuristics -(defun erc-determine-parameters (&optional server port nick name) +(defun erc-determine-parameters (&optional server port nick name key crt) "Determine the connection and authentication parameters. Sets the buffer local variables: @@ -6042,6 +6067,8 @@ erc-determine-parameters (setq erc-session-connector erc-server-connect-function erc-session-server (erc-compute-server server) erc-session-port (or port erc-default-port) + erc-session-cert-key key + erc-session-cert-crt crt erc-session-user-full-name (erc-compute-full-name name)) (erc-set-current-nick (erc-compute-nick nick))) -- 2.17.1