Add non-IRCv3 SASL module to ERC
* doc/misc/erc.texi: Add SASL section in Advanced Usage chapter to document the new SASL module. * etc/ERC-NEWS: Mention addition of erc-sasl module for SASL support. * lisp/erc/erc-compat.el (erc-compat--29-sasl-scram-construct-gs2-header, erc-compat--29-sasl-scram-client-first-message, erc-compat--29-sasl-scram--client-final-message): Fix encoding bug and add minimal authorization support with copies of SASL functions introduced in Emacs 29. * lisp/erc/erc.el (erc-modules): Add `sasl'. * lisp/erc/erc-sasl.el: New file (bug#29108). * test/lisp/erc/erc-sasl-tests.el: New file. * test/lisp/erc/erc-scenarios-sasl.el: New file. * test/lisp/erc/resources/sasl/plain-failed.eld: New file. * test/lisp/erc/resources/sasl/plain.eld: New file. * test/lisp/erc/resources/sasl/scram-sha-1.eld: New file. * test/lisp/erc/resources/sasl/scram-sha-256.eld: New file. * test/lisp/erc/resources/sasl/external.eld: New file.
This commit is contained in:
parent
ae254a65cd
commit
ed8862c404
13 changed files with 1333 additions and 5 deletions
|
@ -78,6 +78,7 @@ Getting Started
|
|||
Advanced Usage
|
||||
|
||||
* Connecting:: Ways of connecting to an IRC server.
|
||||
* SASL:: Authenticating via SASL.
|
||||
* Sample Configuration:: An example configuration file.
|
||||
* Integrations:: Integrations available for ERC.
|
||||
* Options:: Options that are available for ERC.
|
||||
|
@ -482,6 +483,10 @@ Replace text in messages
|
|||
@item ring
|
||||
Enable an input history
|
||||
|
||||
@cindex modules, sasl
|
||||
@item sasl
|
||||
Enable SASL authentication
|
||||
|
||||
@cindex modules, scrolltobottom
|
||||
@item scrolltobottom
|
||||
Scroll to the bottom of the buffer
|
||||
|
@ -561,6 +566,7 @@ toggle never mutates @code{erc-modules}.
|
|||
|
||||
@menu
|
||||
* Connecting:: Ways of connecting to an IRC server.
|
||||
* SASL:: Authenticating via SASL
|
||||
* Sample Configuration:: An example configuration file.
|
||||
* Integrations:: Integrations available for ERC.
|
||||
* Options:: Options that are available for ERC.
|
||||
|
@ -633,6 +639,7 @@ while helpers, like @code{erc-compute-nick}, will determine other
|
|||
parameters, and some, like @code{client-certificate}, will just be
|
||||
@code{nil}.
|
||||
|
||||
@anchor{ERC client-certificate}
|
||||
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
|
||||
|
@ -767,7 +774,10 @@ ERC should automatically attempt to connect with another nickname.
|
|||
You can manually set another nickname with the /NICK command.
|
||||
@end defopt
|
||||
|
||||
@anchor{ERC username}
|
||||
@subheading User
|
||||
@cindex user
|
||||
|
||||
@defun erc-compute-user &optional user
|
||||
Determine a suitable value to send as the first argument of the
|
||||
opening @samp{USER} IRC command by consulting the following sources:
|
||||
|
@ -879,6 +889,7 @@ netrc's case). The actual key goes in the @samp{password} (or
|
|||
@noindent
|
||||
For details, @pxref{Top,,auth-source, auth, Emacs auth-source Library}.
|
||||
|
||||
@anchor{ERC auth-source functions}
|
||||
@defopt erc-auth-source-server-function
|
||||
@end defopt
|
||||
@defopt erc-auth-source-services-function
|
||||
|
@ -891,7 +902,8 @@ current context, if any. For example, with NickServ queries,
|
|||
@code{:user} is the ``desired'' nickname rather than the current one.
|
||||
Generalized names, like @code{:user} and @code{:host}, are always used
|
||||
over back-end specific ones, like @code{:login} or @code{:machine}.
|
||||
ERC expects a string to use as the secret or nil, if the search fails.
|
||||
ERC expects a string to use as the secret or @code{nil}, if the search
|
||||
fails.
|
||||
|
||||
@findex erc-auth-source-search
|
||||
The default value for all three options is the function
|
||||
|
@ -953,6 +965,143 @@ When providing an ID as an entry-point argument, strings and symbols
|
|||
make the most sense, but any reasonably printable object is
|
||||
acceptable.
|
||||
|
||||
@node SASL
|
||||
@section Authenticating via SASL
|
||||
@cindex SASL
|
||||
|
||||
Regardless of the mechanism or the network, you'll likely have to be
|
||||
registered before first use. Please refer to the network's own
|
||||
instructions for details. If you're new to IRC and using a bouncer,
|
||||
know that you probably won't be needing SASL for the client-to-bouncer
|
||||
connection. To get started, just add @code{sasl} to
|
||||
@code{erc-modules} like any other module. But before that, please
|
||||
explore all custom options pertaining to your chosen mechanism.
|
||||
|
||||
@defopt erc-sasl-mechanism
|
||||
The name of an SASL subprotocol type as a @emph{lowercase} symbol.
|
||||
|
||||
@var{plain} and @var{scram} (``password-based''):
|
||||
|
||||
@indentedblock
|
||||
Here, ``password'' refers to your account password, which is usually
|
||||
your @samp{NickServ} password. To make this work, customize
|
||||
@code{erc-sasl-user} and @code{erc-sasl-password} or specify the
|
||||
@code{:user} and @code{:password} keyword arguments when invoking
|
||||
@code{erc-tls}. Note that @code{:user} cannot be given interactively.
|
||||
@end indentedblock
|
||||
|
||||
@var{external} (via Client TLS Certificate):
|
||||
|
||||
@indentedblock
|
||||
This works in conjunction with the @code{:client-certificate} keyword
|
||||
offered by @code{erc-tls}. Just ensure you've registered your
|
||||
fingerprint with the network beforehand. The fingerprint is usually a
|
||||
SHA1 or SHA256 digest in either "normalized" or "openssl" forms. The
|
||||
first is lowercase without delims (@samp{deadbeef}) and the second
|
||||
uppercase with colon seps (@samp{DE:AD:BE:EF}). These days, there's
|
||||
usually a @samp{CERT ADD} command offered by NickServ that can
|
||||
register you automatically if you issue it while connected with a
|
||||
client cert. (@pxref{ERC client-certificate}).
|
||||
|
||||
Additional considerations:
|
||||
@enumerate
|
||||
@item
|
||||
Most IRCds will allow you to authenticate with a client cert but
|
||||
without the hassle of SASL (meaning you may not need this module).
|
||||
@item
|
||||
Technically, @var{EXTERNAL} merely indicates that an out-of-band mode
|
||||
of authentication is in effect (being deferred to), so depending on
|
||||
the specific application or service, there's a remote chance your
|
||||
server has something else in mind.
|
||||
@end enumerate
|
||||
@end indentedblock
|
||||
|
||||
@var{ecdsa-nist256p-challenge}:
|
||||
|
||||
@indentedblock
|
||||
This mechanism is quite complicated and currently requires the
|
||||
external @samp{openssl} executable, so please use something else if at
|
||||
all possible. Ignoring that, specify your key file (e.g.,
|
||||
@samp{~/pki/mykey.pem}) as the value of @code{erc-sasl-password}, and
|
||||
then configure your network settings. On servers running Atheme
|
||||
services, you can add your public key with @samp{NickServ} like so:
|
||||
|
||||
@example
|
||||
ERC> /msg NickServ set property \
|
||||
pubkey AgGZmlYTUjJlea/BVz7yrjJ6gysiAPaQxzeUzTH4hd5j
|
||||
|
||||
@end example
|
||||
(You may be able to omit the @samp{property} subcommand.)
|
||||
@end indentedblock
|
||||
|
||||
@end defopt
|
||||
|
||||
@defopt erc-sasl-user
|
||||
This should be your network account username, typically the same one
|
||||
registered with nickname services. Specify this when your NickServ
|
||||
login differs from the @code{:user} you're connecting with.
|
||||
(@pxref{ERC username})
|
||||
@end defopt
|
||||
|
||||
@defopt erc-sasl-password
|
||||
As noted elsewhere, the @code{:password} parameter for @code{erc-tls}
|
||||
was orignally intended for traditional ``server passwords,'' but these
|
||||
aren't really used any more. As such, this option defaults to
|
||||
borrowing that parameter for its own uses, thus allowing you to call
|
||||
@code{erc-tls} with @code{:password} set to your NickServ password.
|
||||
|
||||
You can also set this to a nonemtpy string, and ERC will send that
|
||||
when needed, no questions asked. If you instead give a non-@code{nil}
|
||||
symbol (other than @code{:password}), like @samp{Libera.Chat}, ERC
|
||||
will use it for the @code{:host} field in an auth-source query.
|
||||
Actually, the same goes for when this option is @code{nil} but an
|
||||
explicit session ID is already on file (@pxref{Network Identifier}).
|
||||
For all such queries, ERC specifies the resolved value of
|
||||
@code{erc-sasl-user} for the @code{:user} (@code{:login}) param. Keep
|
||||
in mind that none of this matters unless
|
||||
@code{erc-sasl-auth-source-function} holds a function, and it's
|
||||
@code{nil} by default. As a last resort, ERC will prompt you for
|
||||
input.
|
||||
|
||||
Lastly, if your mechanism is @code{ecdsa-nist256p-challenge}, this
|
||||
option should instead hold the file name of your key.
|
||||
@end defopt
|
||||
|
||||
@defopt erc-sasl-auth-source-function
|
||||
This is nearly identical to the other ERC @samp{auth-source} function
|
||||
options (@pxref{ERC auth-source functions}) except that the default
|
||||
value here is @code{nil}, meaning you have to set it to something like
|
||||
@code{erc-auth-source-search} for queries to be performed.
|
||||
@end defopt
|
||||
|
||||
@defopt erc-sasl-authzid
|
||||
In the rarest of circumstances, a network may want you to specify a
|
||||
specific role or assume an alternate identity. In most cases, this
|
||||
happens because the server is buggy or misconfigured. If you suspect
|
||||
such a thing, please contact your network operator. Otherwise, just
|
||||
leave this set to @code{nil}.
|
||||
@end defopt
|
||||
|
||||
@subheading Troubleshooting
|
||||
|
||||
@strong{Warning:} ERC's SASL offering is currently limited by a lack
|
||||
of support for proper IRCv3 capability negotiation. In most cases,
|
||||
this shouldn't affect your ability to authenticate.
|
||||
|
||||
If you're struggling, remember that your SASL password is almost
|
||||
always your NickServ password. When in doubt, try restoring all SASL
|
||||
options to their defaults and calling @code{erc-tls} with @code{:user}
|
||||
set to your NickServ account name and @code{:password} to your
|
||||
NickServ password. If you're still having trouble, please contact us
|
||||
(@pxref{Getting Help and Reporting Bugs}).
|
||||
|
||||
As you try out different settings, keep in mind that it's best to
|
||||
create a fresh session for every change, for example, by calling
|
||||
@code{erc-tls} from scratch. More experienced users may be able to
|
||||
get away with cycling @code{erc-sasl-mode} and issuing a
|
||||
@samp{/reconnect}, but that's generally not recommended. Whatever the
|
||||
case, you'll probably want to temporarily disable
|
||||
@code{erc-server-auto-reconnect} while experimenting.
|
||||
|
||||
@node Sample Configuration
|
||||
@section Sample Configuration
|
||||
|
|
|
@ -48,10 +48,9 @@ hell. For some, auth-source may provide a workaround in the form of
|
|||
nonstandard server passwords. See the "Connection" node in the manual
|
||||
under the subheading "Password".
|
||||
|
||||
If you require SASL immediately, please participate in ERC development
|
||||
by volunteering to try (and give feedback on) edge features, one of
|
||||
which is SASL. All known external offerings, past and present, are
|
||||
valiant efforts whose use is nevertheless discouraged.
|
||||
** Rudimentary SASL support has arrived.
|
||||
A new module, 'erc-sasl', now ships with ERC 5.5. See the SASL
|
||||
section in the manual for details.
|
||||
|
||||
** Username argument for entry-point commands.
|
||||
Commands 'erc' and 'erc-tls' now accept a ':user' keyword argument,
|
||||
|
|
|
@ -2334,6 +2334,15 @@ See `erc-display-server-message'." nil
|
|||
(erc-display-message parsed 'notice 'active 's671
|
||||
?n nick ?a securemsg)))
|
||||
|
||||
(define-erc-response-handler (900)
|
||||
"Handle a \"RPL_LOGGEDIN\" server command.
|
||||
Some servers don't consider this SASL-specific but rather just an
|
||||
indication of a server-side state change from logged-out to
|
||||
logged-in." nil
|
||||
;; Whenever ERC starts caring about user accounts, it should record
|
||||
;; the session as being logged here.
|
||||
(erc-display-message parsed 'notice proc (erc-response.contents parsed)))
|
||||
|
||||
(define-erc-response-handler (431 445 446 451 462 463 464 481 483 484 485
|
||||
491 501 502)
|
||||
;; 431 - No nickname given
|
||||
|
|
|
@ -273,6 +273,89 @@ If START or END is negative, it counts from the end."
|
|||
auth-source-backend-parser-functions))
|
||||
|
||||
|
||||
;;;; SASL
|
||||
|
||||
(declare-function sasl-step-data "sasl" (step))
|
||||
(declare-function sasl-error "sasl" (datum))
|
||||
(declare-function sasl-client-property "sasl" (client property))
|
||||
(declare-function sasl-client-set-property "sasl" (client property value))
|
||||
(declare-function sasl-mechanism-name "sasl" (mechanism))
|
||||
(declare-function sasl-client-name "sasl" (client))
|
||||
(declare-function sasl-client-mechanism "sasl" (client))
|
||||
(declare-function sasl-read-passphrase "sasl" (prompt))
|
||||
(declare-function sasl-unique-id "sasl" nil)
|
||||
(declare-function decode-hex-string "hex-util" (string))
|
||||
(declare-function rfc2104-hash "rfc2104" (hash block-length hash-length
|
||||
key text))
|
||||
(declare-function sasl-scram--client-first-message-bare "sasl-scram-rfc"
|
||||
(client))
|
||||
(declare-function cl-mapcar "cl-lib" (cl-func cl-x &rest cl-rest))
|
||||
|
||||
(defun erc-compat--29-sasl-scram-construct-gs2-header (client)
|
||||
(let ((authzid (sasl-client-property client 'authenticator-name)))
|
||||
(concat "n," (and authzid "a=") authzid ",")))
|
||||
|
||||
(defun erc-compat--29-sasl-scram-client-first-message (client _step)
|
||||
(let ((c-nonce (sasl-unique-id)))
|
||||
(sasl-client-set-property client 'c-nonce c-nonce))
|
||||
(concat (erc-compat--29-sasl-scram-construct-gs2-header client)
|
||||
(sasl-scram--client-first-message-bare client)))
|
||||
|
||||
(defun erc-compat--29-sasl-scram--client-final-message
|
||||
(hash-fun block-length hash-length client step)
|
||||
(unless (string-match
|
||||
"^r=\\([^,]+\\),s=\\([^,]+\\),i=\\([0-9]+\\)\\(?:$\\|,\\)"
|
||||
(sasl-step-data step))
|
||||
(sasl-error "Unexpected server response"))
|
||||
(let* ((hmac-fun
|
||||
(lambda (text key)
|
||||
(decode-hex-string
|
||||
(rfc2104-hash hash-fun block-length hash-length key text))))
|
||||
(step-data (sasl-step-data step))
|
||||
(nonce (match-string 1 step-data))
|
||||
(salt-base64 (match-string 2 step-data))
|
||||
(iteration-count (string-to-number (match-string 3 step-data)))
|
||||
(c-nonce (sasl-client-property client 'c-nonce))
|
||||
(cbind-input
|
||||
(if (string-prefix-p c-nonce nonce)
|
||||
(erc-compat--29-sasl-scram-construct-gs2-header client) ; *1
|
||||
(sasl-error "Invalid nonce from server")))
|
||||
(client-final-message-without-proof
|
||||
(concat "c=" (base64-encode-string cbind-input t) "," ; *2
|
||||
"r=" nonce))
|
||||
(password
|
||||
(sasl-read-passphrase
|
||||
(format "%s passphrase for %s: "
|
||||
(sasl-mechanism-name (sasl-client-mechanism client))
|
||||
(sasl-client-name client))))
|
||||
(salt (base64-decode-string salt-base64))
|
||||
(string-xor (lambda (a b)
|
||||
(apply #'unibyte-string (cl-mapcar #'logxor a b))))
|
||||
(salted-password (let ((digest (concat salt (string 0 0 0 1)))
|
||||
(xored nil))
|
||||
(dotimes (_i iteration-count xored)
|
||||
(setq digest (funcall hmac-fun digest password))
|
||||
(setq xored (if (null xored)
|
||||
digest
|
||||
(funcall string-xor xored
|
||||
digest))))))
|
||||
(client-key (funcall hmac-fun "Client Key" salted-password))
|
||||
(stored-key (decode-hex-string (funcall hash-fun client-key)))
|
||||
(auth-message (concat "n=" (sasl-client-name client)
|
||||
",r=" c-nonce "," step-data
|
||||
"," client-final-message-without-proof))
|
||||
(client-signature (funcall hmac-fun
|
||||
(encode-coding-string auth-message 'utf-8)
|
||||
stored-key))
|
||||
(client-proof (funcall string-xor client-key client-signature))
|
||||
(client-final-message
|
||||
(concat client-final-message-without-proof ","
|
||||
"p=" (base64-encode-string client-proof t)))) ; *3
|
||||
(sasl-client-set-property client 'auth-message auth-message)
|
||||
(sasl-client-set-property client 'salted-password salted-password)
|
||||
client-final-message))
|
||||
|
||||
|
||||
;;;; Misc 29.1
|
||||
|
||||
(defmacro erc-compat--with-memoization (table &rest forms)
|
||||
|
|
417
lisp/erc/erc-sasl.el
Normal file
417
lisp/erc/erc-sasl.el
Normal file
|
@ -0,0 +1,417 @@
|
|||
;;; erc-sasl.el --- SASL for ERC -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2022 Free Software Foundation, Inc.
|
||||
;;
|
||||
;; This file is part of GNU Emacs.
|
||||
;;
|
||||
;; GNU Emacs is free software: you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published
|
||||
;; by the Free Software Foundation, either version 3 of the License,
|
||||
;; or (at your option) any later version.
|
||||
;;
|
||||
;; GNU Emacs is distributed in the hope that it will be useful, but
|
||||
;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
;; General Public License for more details.
|
||||
;;
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This "non-IRCv3" implementation resembles others that have surfaced
|
||||
;; over the years, the first possibly being from Joseph Gay:
|
||||
;;
|
||||
;; https://lists.gnu.org/archive/html/erc-discuss/2012-02/msg00001.html
|
||||
;;
|
||||
;; See options and Info manual for usage.
|
||||
;;
|
||||
;; TODO:
|
||||
;;
|
||||
;; - Find a way to obfuscate the password in memory (via something
|
||||
;; like `auth-source--obfuscate'); it's currently visible in
|
||||
;; backtraces.
|
||||
;;
|
||||
;; - Implement a proxy mechanism that chooses the strongest available
|
||||
;; mechanism for you. Requires CAP 3.2 (see bug#49860).
|
||||
;;
|
||||
;; - Integrate with whatever solution ERC eventually settles on to
|
||||
;; handle user options for different network contexts. At the
|
||||
;; moment, this does its own thing for stashing and restoring
|
||||
;; session options, but ERC should make abstractions available for
|
||||
;; all local modules to use, possibly based on connection-local
|
||||
;; variables.
|
||||
|
||||
;;; Code:
|
||||
(require 'erc)
|
||||
(require 'rx)
|
||||
(require 'sasl)
|
||||
(require 'sasl-scram-rfc)
|
||||
(require 'sasl-scram-sha256 nil t) ; not present in Emacs 27
|
||||
|
||||
(defgroup erc-sasl nil
|
||||
"SASL for ERC."
|
||||
:group 'erc
|
||||
:package-version '(ERC . "5.4.1")) ; FIXME increment on next release
|
||||
|
||||
(defcustom erc-sasl-mechanism 'plain
|
||||
"SASL mechanism to connect with.
|
||||
Note that any value other than nil or `external' likely requires
|
||||
`erc-sasl-user' and `erc-sasl-password'."
|
||||
:type '(choice (const plain)
|
||||
(const external)
|
||||
(const scram-sha-1)
|
||||
(const scram-sha-256)
|
||||
(const scram-sha-512)
|
||||
(const ecdsa-nist256p-challenge)))
|
||||
|
||||
(defcustom erc-sasl-user :user
|
||||
"Account username to send when authenticating.
|
||||
This is also referred to as the authentication identity or
|
||||
\"authcid\". A value of `:user' or `:nick' indicates that the
|
||||
corresponding connection parameter on file should be used. These
|
||||
are most often derived from arguments provided to the `erc' and
|
||||
`erc-tls' entry points. In the case of `:nick', a downcased
|
||||
version is used."
|
||||
:type '(choice string (const :user) (const :nick)))
|
||||
|
||||
(defcustom erc-sasl-password :password
|
||||
"Optional account password to send when authenticating.
|
||||
When the value is a string, ERC will use it unconditionally for
|
||||
most mechanisms. Likewise with `:password', except ERC will
|
||||
instead use the \"session password\" on file, which often
|
||||
originates from the entry-point commands `erc' or `erc-tls'.
|
||||
Otherwise, when `erc-sasl-auth-source-function' is a function,
|
||||
ERC will attempt an auth-source query, possibly using a non-nil
|
||||
symbol for the suggested `:host' parameter if set as this
|
||||
option's value or passed as an `:id' to `erc-tls'. Failing that,
|
||||
ERC will prompt for input.
|
||||
|
||||
Note that, with `:password', ERC will forgo sending a traditional
|
||||
server password via the IRC \"PASS\" command. Also, when
|
||||
`erc-sasl-mechanism' is set to `ecdsa-nist256p-challenge', this
|
||||
option should hold the file name of the key."
|
||||
:type '(choice (const nil) (const :password) string symbol))
|
||||
|
||||
(defcustom erc-sasl-auth-source-function nil
|
||||
"Function to query auth-source for an SASL password.
|
||||
Called with keyword params known to `auth-source-search', which
|
||||
includes `erc-sasl-user' for the `:user' field and
|
||||
`erc-sasl-password' for the `:host' field, when the latter option
|
||||
is a non-nil, non-keyword symbol. In return, ERC expects a
|
||||
string to send as the SASL password, or nil, to move on to the
|
||||
next approach, as described in the doc string for the option
|
||||
`erc-sasl-password'. See info node `(erc) Connecting' for
|
||||
details on ERC's auth-source integration."
|
||||
:type '(choice (function-item erc-auth-source-search)
|
||||
(const nil)
|
||||
function))
|
||||
|
||||
(defcustom erc-sasl-authzid nil
|
||||
"SASL authorization identity, likely unneeded for everyday use."
|
||||
:type '(choice (const nil) string))
|
||||
|
||||
|
||||
;; Analogous to what erc-backend does to persist opening params.
|
||||
(defvar-local erc-sasl--options nil)
|
||||
|
||||
;; Session-local (server buffer) SASL subproto state
|
||||
(defvar-local erc-sasl--state nil)
|
||||
|
||||
(cl-defstruct erc-sasl--state
|
||||
"Holder for client object and subproto state."
|
||||
(client nil :type vector)
|
||||
(step nil :type vector)
|
||||
(pending nil :type string))
|
||||
|
||||
(defun erc-sasl--get-user ()
|
||||
(pcase (alist-get 'user erc-sasl--options)
|
||||
(:user erc-session-username)
|
||||
(:nick (erc-downcase (erc-current-nick)))
|
||||
(v v)))
|
||||
|
||||
(defun erc-sasl--read-password (prompt)
|
||||
"Return configured option or server password.
|
||||
PROMPT is passed to `read-passwd' if necessary."
|
||||
(if-let
|
||||
((found (pcase (alist-get 'password erc-sasl--options)
|
||||
(:password erc-session-password)
|
||||
((and (pred stringp) v) (unless (string-empty-p v) v))
|
||||
((and (guard erc-sasl-auth-source-function)
|
||||
v (let host
|
||||
(or v (erc-networks--id-given erc-networks--id))))
|
||||
(apply erc-sasl-auth-source-function
|
||||
:user (erc-sasl--get-user)
|
||||
(and host (list :host (symbol-name host))))))))
|
||||
(copy-sequence found)
|
||||
(read-passwd prompt)))
|
||||
|
||||
(defun erc-sasl--plain-response (client steps)
|
||||
(let ((sasl-read-passphrase #'erc-sasl--read-password))
|
||||
(sasl-plain-response client steps)))
|
||||
|
||||
(declare-function erc-compat--29-sasl-scram--client-final-message "erc-compat"
|
||||
(hash-fun block-length hash-length client step))
|
||||
|
||||
(defun erc-sasl--scram-sha-hack-client-final-message (&rest args)
|
||||
;; In the future (29+), we'll hopefully be able to call
|
||||
;; `sasl-scram--client-final-message' directly
|
||||
(require 'erc-compat)
|
||||
(let ((sasl-read-passphrase #'erc-sasl--read-password))
|
||||
(apply #'erc-compat--29-sasl-scram--client-final-message args)))
|
||||
|
||||
(defun erc-sasl--scram-sha-1-client-final-message (client step)
|
||||
(erc-sasl--scram-sha-hack-client-final-message 'sha1 64 20 client step))
|
||||
|
||||
(defun erc-sasl--scram-sha-256-client-final-message (client step)
|
||||
(erc-sasl--scram-sha-hack-client-final-message 'sasl-scram-sha256 64 32
|
||||
client step))
|
||||
|
||||
(defun erc-sasl--scram-sha512 (object &optional start end binary)
|
||||
(secure-hash 'sha512 object start end binary))
|
||||
|
||||
(defun erc-sasl--scram-sha-512-client-final-message (client step)
|
||||
(erc-sasl--scram-sha-hack-client-final-message #'erc-sasl--scram-sha512
|
||||
128 64 client step))
|
||||
|
||||
(defun erc-sasl--scram-sha-512-authenticate-server (client step)
|
||||
(sasl-scram--authenticate-server #'erc-sasl--scram-sha512
|
||||
128 64 client step))
|
||||
|
||||
(defun erc-sasl--ecdsa-first (client _step)
|
||||
"Return CLIENT name."
|
||||
(sasl-client-name client))
|
||||
|
||||
;; FIXME do this with gnutls somehow
|
||||
(defun erc-sasl--ecdsa-sign (client step)
|
||||
"Return signed challenge for CLIENT and current STEP."
|
||||
(let ((challenge (sasl-step-data step)))
|
||||
(with-temp-buffer
|
||||
(set-buffer-multibyte nil)
|
||||
(insert challenge)
|
||||
(call-process-region (point-min) (point-max)
|
||||
"openssl" 'delete t nil "pkeyutl" "-inkey"
|
||||
(sasl-client-property client 'ecdsa-keyfile)
|
||||
"-sign")
|
||||
(buffer-string))))
|
||||
|
||||
(pcase-dolist
|
||||
(`(,name . ,steps)
|
||||
'(("PLAIN"
|
||||
erc-sasl--plain-response)
|
||||
("EXTERNAL"
|
||||
ignore)
|
||||
("SCRAM-SHA-1"
|
||||
erc-compat--29-sasl-scram-client-first-message
|
||||
erc-sasl--scram-sha-1-client-final-message
|
||||
sasl-scram-sha-1-authenticate-server)
|
||||
("SCRAM-SHA-256"
|
||||
erc-compat--29-sasl-scram-client-first-message
|
||||
erc-sasl--scram-sha-256-client-final-message
|
||||
sasl-scram-sha-256-authenticate-server)
|
||||
("SCRAM-SHA-512"
|
||||
erc-compat--29-sasl-scram-client-first-message
|
||||
erc-sasl--scram-sha-512-client-final-message
|
||||
erc-sasl--scram-sha-512-authenticate-server)
|
||||
("ECDSA-NIST256P-CHALLENGE"
|
||||
erc-sasl--ecdsa-first
|
||||
erc-sasl--ecdsa-sign)))
|
||||
(let ((feature (intern (concat "erc-sasl-" (downcase name)))))
|
||||
(put feature 'sasl-mechanism (sasl-make-mechanism name steps))
|
||||
(provide feature)))
|
||||
|
||||
(cl-defgeneric erc-sasl--create-client (mechanism)
|
||||
"Create and return a new SASL client object for MECHANISM."
|
||||
(let ((sasl-mechanism-alist (copy-sequence sasl-mechanism-alist))
|
||||
(sasl-mechanisms sasl-mechanisms)
|
||||
(name (upcase (symbol-name mechanism)))
|
||||
(feature (intern-soft (concat "erc-sasl-" (symbol-name mechanism))))
|
||||
client)
|
||||
(when feature
|
||||
(setf (alist-get name sasl-mechanism-alist nil nil #'equal) `(,feature))
|
||||
(cl-pushnew name sasl-mechanisms :test #'equal)
|
||||
(setq client (sasl-make-client (sasl-find-mechanism (list name))
|
||||
(erc-sasl--get-user)
|
||||
"N/A" "N/A"))
|
||||
(sasl-client-set-property client 'authenticator-name
|
||||
(alist-get 'authzid erc-sasl--options))
|
||||
client)))
|
||||
|
||||
(cl-defmethod erc-sasl--create-client ((_ (eql plain)))
|
||||
"Create and return a new PLAIN client object."
|
||||
;; https://tools.ietf.org/html/rfc4616#section-2.
|
||||
(let* ((sans (remq (assoc "PLAIN" sasl-mechanism-alist)
|
||||
sasl-mechanism-alist))
|
||||
(sasl-mechanism-alist (cons '("PLAIN" erc-sasl-plain) sans))
|
||||
(authc (erc-sasl--get-user))
|
||||
(port (if (numberp erc-session-port)
|
||||
(number-to-string erc-session-port)
|
||||
"0"))
|
||||
;; In most cases, `erc-server-announced-name' won't be known.
|
||||
(host (or erc-server-announced-name erc-session-server))
|
||||
(mech (sasl-find-mechanism '("PLAIN")))
|
||||
(client (sasl-make-client mech authc port host)))
|
||||
(sasl-client-set-property client 'authenticator-name
|
||||
(alist-get 'authzid erc-sasl--options))
|
||||
client))
|
||||
|
||||
(cl-defmethod erc-sasl--create-client ((_ (eql scram-sha-256)))
|
||||
"Create and return a new SCRAM-SHA-256 client."
|
||||
(when (featurep 'sasl-scram-sha256)
|
||||
(cl-call-next-method)))
|
||||
|
||||
(cl-defmethod erc-sasl--create-client ((_ (eql scram-sha-512)))
|
||||
"Create and return a new SCRAM-SHA-512 client."
|
||||
(when (featurep 'sasl-scram-sha256)
|
||||
(cl-call-next-method)))
|
||||
|
||||
(cl-defmethod erc-sasl--create-client ((_ (eql ecdsa-nist256p-challenge)))
|
||||
"Create and return a new ECDSA-NIST256P-CHALLENGE client."
|
||||
(let ((keyfile (cdr (assq 'password erc-sasl--options))))
|
||||
;; Better to signal usage errors now than inside a process filter.
|
||||
(cond ((or (not (stringp keyfile)) (not (file-readable-p keyfile)))
|
||||
(erc-display-error-notice
|
||||
nil "`erc-sasl-password' not accessible as a file")
|
||||
nil)
|
||||
((not (executable-find "openssl"))
|
||||
(erc-display-error-notice nil "Could not find openssl program")
|
||||
nil)
|
||||
(t
|
||||
(let ((client (cl-call-next-method)))
|
||||
(sasl-client-set-property client 'ecdsa-keyfile keyfile)
|
||||
client)))))
|
||||
|
||||
;; This stands alone because it's also used by bug#49860.
|
||||
(defun erc-sasl--init ()
|
||||
(setq erc-sasl--state (make-erc-sasl--state))
|
||||
;; If the previous attempt failed during registration, this may be
|
||||
;; non-nil and contain erroneous values, but how can we detect that?
|
||||
;; What if the server dropped the connection for some other reason?
|
||||
(setq erc-sasl--options
|
||||
(or (and erc--server-reconnecting
|
||||
(alist-get 'erc-sasl--options erc--server-reconnecting))
|
||||
`((user . ,erc-sasl-user)
|
||||
(password . ,erc-sasl-password)
|
||||
(mechanism . ,erc-sasl-mechanism)
|
||||
(authzid . ,erc-sasl-authzid)))))
|
||||
|
||||
(defun erc-sasl--mechanism-offered-p (offered)
|
||||
"Return non-nil when OFFERED appears among a list of mechanisms."
|
||||
(string-match-p (rx-to-string
|
||||
`(: (| bot ",")
|
||||
,(symbol-name (alist-get 'mechanism erc-sasl--options))
|
||||
(| eot ",")))
|
||||
(downcase offered)))
|
||||
|
||||
(erc-define-catalog
|
||||
'english
|
||||
'((s902 . "ERR_NICKLOCKED nick %n unavailable: %s")
|
||||
(s904 . "ERR_SASLFAIL (authentication failed) %s")
|
||||
(s905 . "ERR SASLTOOLONG (credentials too long) %s")
|
||||
(s906 . "ERR_SASLABORTED (authentication aborted) %s")
|
||||
(s907 . "ERR_SASLALREADY (already authenticated) %s")
|
||||
(s908 . "RPL_SASLMECHS (unsupported mechanism: %m) %s")))
|
||||
|
||||
(define-erc-module sasl nil
|
||||
"Non-IRCv3 SASL support for ERC.
|
||||
This doesn't solicit or validate a suite of supported mechanisms."
|
||||
;; See bug#49860 for a CAP 3.2-aware WIP implementation.
|
||||
((unless erc--target
|
||||
(erc-sasl--init)
|
||||
(let* ((mech (alist-get 'mechanism erc-sasl--options))
|
||||
(client (erc-sasl--create-client mech)))
|
||||
(unless client
|
||||
(erc-display-error-notice
|
||||
nil (format "Unknown or unsupported SASL mechanism: %s" mech))
|
||||
(erc-error "Unknown or unsupported SASL mechanism: %s" mech))
|
||||
(setf (erc-sasl--state-client erc-sasl--state) client))))
|
||||
((kill-local-variable 'erc-sasl--state)
|
||||
(kill-local-variable 'erc-sasl--options))
|
||||
'local)
|
||||
|
||||
(define-erc-response-handler (AUTHENTICATE)
|
||||
"Begin or resume an SASL session." nil
|
||||
(if-let* ((response (car (erc-response.command-args parsed)))
|
||||
((= 400 (length response))))
|
||||
(cl-callf (lambda (s) (concat s response))
|
||||
(erc-sasl--state-pending erc-sasl--state))
|
||||
(cl-assert response t)
|
||||
(when (string= "+" response)
|
||||
(setq response ""))
|
||||
(setf response (base64-decode-string
|
||||
(concat (erc-sasl--state-pending erc-sasl--state)
|
||||
response))
|
||||
(erc-sasl--state-pending erc-sasl--state) nil)
|
||||
(let ((client (erc-sasl--state-client erc-sasl--state))
|
||||
(step (erc-sasl--state-step erc-sasl--state))
|
||||
data)
|
||||
(when step
|
||||
(sasl-step-set-data step response))
|
||||
(setq step (setf (erc-sasl--state-step erc-sasl--state)
|
||||
(sasl-next-step client step))
|
||||
data (sasl-step-data step))
|
||||
(when (string= data "")
|
||||
(setq data nil))
|
||||
(when data
|
||||
(setq data (base64-encode-string data t)))
|
||||
(erc-server-send (concat "AUTHENTICATE " (or data "+"))))))
|
||||
|
||||
(defun erc-sasl--destroy (proc)
|
||||
(run-hook-with-args 'erc-quit-hook proc)
|
||||
(delete-process proc)
|
||||
(erc-error "Disconnected from %s; please review SASL settings" proc))
|
||||
|
||||
(define-erc-response-handler (902)
|
||||
"Handle an ERR_NICKLOCKED response." nil
|
||||
(erc-display-message parsed '(notice error) 'active 's902
|
||||
?n (car (erc-response.command-args parsed))
|
||||
?s (erc-response.contents parsed))
|
||||
(erc-sasl--destroy proc))
|
||||
|
||||
(define-erc-response-handler (903)
|
||||
"Handle a RPL_SASLSUCCESS response." nil
|
||||
(when erc-sasl-mode
|
||||
(unless erc-server-connected
|
||||
(erc-server-send "CAP END")))
|
||||
(erc-display-message parsed 'notice proc (erc-response.contents parsed)))
|
||||
|
||||
(define-erc-response-handler (907)
|
||||
"Handle a RPL_SASLALREADY response." nil
|
||||
(erc-display-message parsed '(notice error) 'active 's907
|
||||
?s (erc-response.contents parsed)))
|
||||
|
||||
(define-erc-response-handler (904 905 906)
|
||||
"Handle various SASL-related error responses." nil
|
||||
(erc-display-message parsed '(notice error) 'active
|
||||
(intern (format "s%s" (erc-response.command parsed)))
|
||||
?s (erc-response.contents parsed))
|
||||
(erc-sasl--destroy proc))
|
||||
|
||||
(define-erc-response-handler (908)
|
||||
"Handle a RPL_SASLALREADY response." nil
|
||||
(erc-display-message parsed '(notice error) 'active 's908
|
||||
?m (alist-get 'mechanism erc-sasl--options)
|
||||
?s (string-join (cdr (erc-response.command-args parsed))
|
||||
" "))
|
||||
(erc-sasl--destroy proc))
|
||||
|
||||
(cl-defmethod erc--register-connection (&context (erc-sasl-mode (eql t)))
|
||||
"Send speculative/pipelined CAP and AUTHENTICATE and hope for the best."
|
||||
(if-let* ((c (erc-sasl--state-client erc-sasl--state))
|
||||
(m (sasl-mechanism-name (sasl-client-mechanism c))))
|
||||
(progn
|
||||
(erc-server-send "CAP REQ :sasl")
|
||||
(if (and erc-session-password
|
||||
(eq :password (alist-get 'password erc-sasl--options)))
|
||||
(let (erc-session-password)
|
||||
(erc-login))
|
||||
(erc-login))
|
||||
(erc-server-send (format "AUTHENTICATE %s" m)))
|
||||
(erc-sasl--destroy erc-server-process)))
|
||||
|
||||
(provide 'erc-sasl)
|
||||
;;; erc-sasl.el ends here
|
||||
;;
|
||||
;; Local Variables:
|
||||
;; generated-autoload-file: "erc-loaddefs.el"
|
||||
;; End:
|
|
@ -1860,6 +1860,7 @@ removed from the list will be disabled."
|
|||
(const :tag "readonly: Make displayed lines read-only" readonly)
|
||||
(const :tag "replace: Replace text in messages" replace)
|
||||
(const :tag "ring: Enable an input history" ring)
|
||||
(const :tag "sasl: Enable SASL authentication" sasl)
|
||||
(const :tag "scrolltobottom: Scroll to the bottom of the buffer"
|
||||
scrolltobottom)
|
||||
(const :tag "services: Identify to Nickserv (IRC Services) automatically"
|
||||
|
|
344
test/lisp/erc/erc-sasl-tests.el
Normal file
344
test/lisp/erc/erc-sasl-tests.el
Normal file
|
@ -0,0 +1,344 @@
|
|||
;;; erc-sasl-tests.el --- Tests for erc-sasl. -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2022 Free Software Foundation, Inc.
|
||||
;;
|
||||
;; This file is part of GNU Emacs.
|
||||
;;
|
||||
;; GNU Emacs is free software: you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
;;
|
||||
;; GNU Emacs is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
;;
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'ert-x)
|
||||
(require 'erc-sasl)
|
||||
|
||||
(ert-deftest erc-sasl--mechanism-offered-p ()
|
||||
(let ((erc-sasl--options '((mechanism . external))))
|
||||
(should (erc-sasl--mechanism-offered-p "foo,external"))
|
||||
(should (erc-sasl--mechanism-offered-p "external,bar"))
|
||||
(should (erc-sasl--mechanism-offered-p "foo,external,bar"))
|
||||
(should-not (erc-sasl--mechanism-offered-p "fooexternal"))
|
||||
(should-not (erc-sasl--mechanism-offered-p "externalbar"))))
|
||||
|
||||
(ert-deftest erc-sasl--read-password--basic ()
|
||||
(ert-info ("Explicit erc-sasl-password")
|
||||
(let ((erc-sasl--options '((password . "foo"))))
|
||||
(should (string= (erc-sasl--read-password nil) "foo"))))
|
||||
|
||||
(ert-info ("Explicit session password")
|
||||
(let ((erc-session-password "foo")
|
||||
(erc-sasl--options '((password . :password))))
|
||||
(should (string= (erc-sasl--read-password nil) "foo"))))
|
||||
|
||||
(ert-info ("Fallback to prompt skip auth-source")
|
||||
(should-not erc-sasl-auth-source-function)
|
||||
(let ((erc-session-password "bar")
|
||||
(erc-networks--id (erc-networks--id-create nil)))
|
||||
(should (string= (ert-simulate-keys "bar\r"
|
||||
(erc-sasl--read-password "?"))
|
||||
"bar"))))
|
||||
|
||||
(ert-info ("Prompt when auth-source fails and `erc-sasl-password' null")
|
||||
(let ((erc-sasl--options '((password)))
|
||||
(erc-sasl-auth-source-function #'ignore))
|
||||
(should (string= (ert-simulate-keys "baz\r"
|
||||
(erc-sasl--read-password "pwd:"))
|
||||
"baz")))))
|
||||
|
||||
(ert-deftest erc-sasl--read-password--auth-source ()
|
||||
(ert-with-temp-file netrc-file
|
||||
:text (string-join
|
||||
(list
|
||||
;; If you swap these first 2 lines, *1 below fails
|
||||
"machine FSF.chat port 6697 user bob password sesame"
|
||||
"machine GNU/chat port 6697 user bob password spam"
|
||||
"machine MyHost port irc password 123")
|
||||
"\n")
|
||||
(let* ((auth-sources (list netrc-file))
|
||||
(erc-session-server "irc.gnu.org")
|
||||
(erc-session-port 6697)
|
||||
(erc-networks--id (erc-networks--id-create nil))
|
||||
calls
|
||||
(erc-sasl-auth-source-function
|
||||
(lambda (&rest r)
|
||||
(push r calls)
|
||||
(apply #'erc--auth-source-search r)))
|
||||
erc-server-announced-name ; too early
|
||||
auth-source-do-cache)
|
||||
|
||||
(ert-info ("Symbol as password specifies machine")
|
||||
(let ((erc-sasl--options '((user . "bob") (password . FSF.chat)))
|
||||
(erc-networks--id (make-erc-networks--id)))
|
||||
(should (string= (erc-sasl--read-password nil) "sesame"))
|
||||
(should (equal (pop calls) '(:user "bob" :host "FSF.chat")))))
|
||||
|
||||
(ert-info ("ID for :host and `erc-session-username' for :user") ; *1
|
||||
(let ((erc-session-username "bob")
|
||||
(erc-sasl--options '((user . :user) (password)))
|
||||
(erc-networks--id (erc-networks--id-create 'GNU/chat)))
|
||||
(should (string= (erc-sasl--read-password nil) "spam"))
|
||||
(should (equal (pop calls) '(:user "bob" :host "GNU/chat")))))
|
||||
|
||||
(ert-info ("ID for :host and current nick for :user") ; *1
|
||||
(let ((erc-server-current-nick "bob")
|
||||
(erc-sasl--options '((user . :nick) (password)))
|
||||
(erc-networks--id (erc-networks--id-create 'GNU/chat)))
|
||||
(should (string= (erc-sasl--read-password nil) "spam"))
|
||||
(should (equal (pop calls) '(:user "bob" :host "GNU/chat")))))
|
||||
|
||||
(ert-info ("Symbol as password, entry lacks user field")
|
||||
(let ((erc-server-current-nick "fake")
|
||||
(erc-sasl--options '((user . :nick) (password . MyHost)))
|
||||
(erc-networks--id (erc-networks--id-create 'GNU/chat)))
|
||||
(should (string= (erc-sasl--read-password nil) "123"))
|
||||
(should (equal (pop calls) '(:user "fake" :host "MyHost"))))))))
|
||||
|
||||
(ert-deftest erc-sasl-create-client--plain ()
|
||||
(let* ((erc-session-password "password123")
|
||||
(erc-session-username "tester")
|
||||
(erc-sasl--options '((user . :user) (password . :password)))
|
||||
(erc-session-port 1667)
|
||||
(erc-session-server "localhost")
|
||||
(client (erc-sasl--create-client 'plain))
|
||||
(result (sasl-next-step client nil)))
|
||||
(should (equal (format "%S" [erc-sasl--plain-response
|
||||
"\0tester\0password123"])
|
||||
(format "%S" result)))
|
||||
(should (string= (sasl-step-data result) "\0tester\0password123"))
|
||||
(should-not (sasl-next-step client result)))
|
||||
(should (equal (assoc-default "PLAIN" sasl-mechanism-alist) '(sasl-plain))))
|
||||
|
||||
(ert-deftest erc-sasl-create-client--external ()
|
||||
(let* ((erc-server-current-nick "tester")
|
||||
(erc-sasl--options '((user . :nick) (password . :password)))
|
||||
(client (erc-sasl--create-client 'external)) ; unused ^
|
||||
(result (sasl-next-step client nil)))
|
||||
(should (equal (format "%S" [ignore nil]) (format "%S" result)))
|
||||
(should-not (sasl-step-data result))
|
||||
(should-not (sasl-next-step client result)))
|
||||
(should-not (member "EXTERNAL" sasl-mechanisms))
|
||||
(should-not (assoc-default "EXTERNAL" sasl-mechanism-alist)))
|
||||
|
||||
(ert-deftest erc-sasl-create-client--scram-sha-1 ()
|
||||
(let* ((erc-sasl--options '((user . "jilles") (password . "sesame")
|
||||
(authzid . "jilles")))
|
||||
(mock-rvs (list "c5RqLCZy0L4fGkKAZ0hujFBs" ""))
|
||||
(sasl-unique-id-function (lambda () (pop mock-rvs)))
|
||||
(client (erc-sasl--create-client 'scram-sha-1))
|
||||
(step (sasl-next-step client nil)))
|
||||
(ert-info ("Client's initial request")
|
||||
(let ((req "n,a=jilles,n=jilles,r=c5RqLCZy0L4fGkKAZ0hujFBs"))
|
||||
(should (equal (format "%S"
|
||||
`[erc-compat--29-sasl-scram-client-first-message
|
||||
,req])
|
||||
(format "%S" step)))
|
||||
(should (string= (sasl-step-data step) req))))
|
||||
(ert-info ("Server's initial response")
|
||||
(let ((resp (concat "r=c5RqLCZy0L4fGkKAZ0hujFBsXQoKcivqCw9iDZPSpb,"
|
||||
"s=5mJO6d4rjCnsBU1X,"
|
||||
"i=4096"))
|
||||
(req (concat "c=bixhPWppbGxlcyw=,"
|
||||
"r=c5RqLCZy0L4fGkKAZ0hujFBsXQoKcivqCw9iDZPSpb,"
|
||||
"p=OVUhgPu8wEm2cDoVLfaHzVUYPWU=")))
|
||||
(sasl-step-set-data step resp)
|
||||
(setq step (sasl-next-step client step))
|
||||
(should (equal (format "%S"
|
||||
`[erc-sasl--scram-sha-1-client-final-message
|
||||
,req])
|
||||
(format "%S" step)))
|
||||
(should (string= (sasl-step-data step) req))))
|
||||
(ert-info ("Server's final message")
|
||||
(let ((resp "v=ZWR23c9MJir0ZgfGf5jEtLOn6Ng="))
|
||||
(sasl-step-set-data step resp)
|
||||
(setq step (sasl-next-step client step))
|
||||
(should-not (sasl-step-data step)))))
|
||||
(should (eq sasl-unique-id-function #'sasl-unique-id-function)))
|
||||
|
||||
(ert-deftest erc-sasl-create-client--scram-sha-256 ()
|
||||
(unless (featurep 'sasl-scram-sha256)
|
||||
(ert-skip "Emacs lacks sasl-scram-sha256"))
|
||||
(let* ((erc-server-current-nick "jilles")
|
||||
(erc-session-password "sesame")
|
||||
(erc-sasl--options '((user . :nick) (password . :password)
|
||||
(authzid . "jilles")))
|
||||
(mock-rvs (list "c5RqLCZy0L4fGkKAZ0hujFBs" ""))
|
||||
(sasl-unique-id-function (lambda () (pop mock-rvs)))
|
||||
(client (erc-sasl--create-client 'scram-sha-256))
|
||||
(step (sasl-next-step client nil)))
|
||||
(ert-info ("Client's initial request")
|
||||
(let ((req "n,a=jilles,n=jilles,r=c5RqLCZy0L4fGkKAZ0hujFBs"))
|
||||
(should (equal (format "%S"
|
||||
`[erc-compat--29-sasl-scram-client-first-message
|
||||
,req])
|
||||
(format "%S" step)))
|
||||
(should (string= (sasl-step-data step) req))))
|
||||
(ert-info ("Server's initial response")
|
||||
(let ((resp (concat
|
||||
"r=c5RqLCZy0L4fGkKAZ0hujFBse697140729d8445fb95ec94ceacb14b3,"
|
||||
"s=MTk2M2VkMzM5ZmU0NDRiYmI0MzIyOGVhN2YwNzYwNmI=,"
|
||||
"i=4096"))
|
||||
(req (concat
|
||||
"c=bixhPWppbGxlcyw=,"
|
||||
"r=c5RqLCZy0L4fGkKAZ0hujFBse697140729d8445fb95ec94ceacb14b3,"
|
||||
"p=1vDesVBzJmv0lX0Ae1kHFtdVHkC6j4gISKVqaR45HFg=")))
|
||||
(sasl-step-set-data step resp)
|
||||
(setq step (sasl-next-step client step))
|
||||
(should (equal (format "%S"
|
||||
`[erc-sasl--scram-sha-256-client-final-message
|
||||
,req])
|
||||
(format "%S" step)))
|
||||
(should (string= (sasl-step-data step) req))))
|
||||
(ert-info ("Server's final message")
|
||||
(let ((resp "v=gUePTYSZN9xgcE06KSyKO9fUmSwH26qifoapXyEs75s="))
|
||||
(sasl-step-set-data step resp)
|
||||
(setq step (sasl-next-step client step))
|
||||
(should-not (sasl-step-data step)))))
|
||||
(should (eq sasl-unique-id-function #'sasl-unique-id-function)))
|
||||
|
||||
(ert-deftest erc-sasl-create-client--scram-sha-256--no-authzid ()
|
||||
(unless (featurep 'sasl-scram-sha256)
|
||||
(ert-skip "Emacs lacks sasl-scram-sha256"))
|
||||
(let* ((erc-server-current-nick "jilles")
|
||||
(erc-session-password "sesame")
|
||||
(erc-sasl--options '((user . :nick) (password . :password) (authzid)))
|
||||
(mock-rvs (list "c5RqLCZy0L4fGkKAZ0hujFBs" ""))
|
||||
(sasl-unique-id-function (lambda () (pop mock-rvs)))
|
||||
(client (erc-sasl--create-client 'scram-sha-256))
|
||||
(step (sasl-next-step client nil)))
|
||||
(ert-info ("Client's initial request")
|
||||
(let ((req "n,,n=jilles,r=c5RqLCZy0L4fGkKAZ0hujFBs"))
|
||||
(should (equal (format "%S"
|
||||
`[erc-compat--29-sasl-scram-client-first-message
|
||||
,req])
|
||||
(format "%S" step)))
|
||||
(should (string= (sasl-step-data step) req))))
|
||||
(ert-info ("Server's initial response")
|
||||
(let ((resp (concat
|
||||
"r=c5RqLCZy0L4fGkKAZ0hujFBsd4067f0afdb54c3dbd4fe645b84cae37,"
|
||||
"s=ZTg1MmE1YmFhZGI1NDcyMjk3NzYwZmRjZDM3Y2I1OTM=,"
|
||||
"i=4096"))
|
||||
(req (concat
|
||||
"c=biws,"
|
||||
"r=c5RqLCZy0L4fGkKAZ0hujFBsd4067f0afdb54c3dbd4fe645b84cae37,"
|
||||
"p=LP4sjJrjJKp5qTsARyZCppXpKLu4FMM284hNESPvGhI=")))
|
||||
(sasl-step-set-data step resp)
|
||||
(setq step (sasl-next-step client step))
|
||||
(should (equal (format "%S"
|
||||
`[erc-sasl--scram-sha-256-client-final-message
|
||||
,req])
|
||||
(format "%S" step)))
|
||||
(should (string= (sasl-step-data step) req))))
|
||||
(ert-info ("Server's final message")
|
||||
(let ((resp "v=847WXfnmReGyE1qlq1And6R4bPBNROTZ7EMS/QrJtUM="))
|
||||
(sasl-step-set-data step resp)
|
||||
(setq step (sasl-next-step client step))
|
||||
(should-not (sasl-step-data step)))))
|
||||
(should (eq sasl-unique-id-function #'sasl-unique-id-function)))
|
||||
|
||||
(ert-deftest erc-sasl-create-client--scram-sha-512--no-authzid ()
|
||||
(unless (featurep 'sasl-scram-sha256)
|
||||
(ert-skip "Emacs lacks sasl-scram-sha512"))
|
||||
(let* ((erc-server-current-nick "jilles")
|
||||
(erc-session-password "sesame")
|
||||
(erc-sasl--options '((user . :nick) (password . :password) (authzid)))
|
||||
(mock-rvs (list "c5RqLCZy0L4fGkKAZ0hujFBs" ""))
|
||||
(sasl-unique-id-function (lambda () (pop mock-rvs)))
|
||||
(client (erc-sasl--create-client 'scram-sha-512))
|
||||
(step (sasl-next-step client nil)))
|
||||
(ert-info ("Client's initial request")
|
||||
(let ((req "n,,n=jilles,r=c5RqLCZy0L4fGkKAZ0hujFBs"))
|
||||
(should (equal (format "%S"
|
||||
`[erc-compat--29-sasl-scram-client-first-message
|
||||
,req])
|
||||
(format "%S" step)))
|
||||
(should (string= (sasl-step-data step) req))))
|
||||
(ert-info ("Server's initial response")
|
||||
(let ((resp (concat
|
||||
"r=c5RqLCZy0L4fGkKAZ0hujFBs54c592745ce14e559fcc3f27b15464f6,"
|
||||
"s=YzMzOWZiY2U0YzcwNDA0M2I4ZGE2M2ZjOTBjODExZTM=,"
|
||||
"i=4096"))
|
||||
(req (concat
|
||||
"c=biws,"
|
||||
"r=c5RqLCZy0L4fGkKAZ0hujFBs54c592745ce14e559fcc3f27b15464f6,"
|
||||
"p=vMBb9tKxFAfBtel087/GLbo4objAIYr1wM+mFv/jYLKXE"
|
||||
"NUF0vynm81qQbywQE5ScqFFdAfwYMZq/lj4s0V1OA==")))
|
||||
(sasl-step-set-data step resp)
|
||||
(setq step (sasl-next-step client step))
|
||||
(should (equal (format
|
||||
"%S" `[erc-sasl--scram-sha-512-client-final-message
|
||||
,req])
|
||||
(format "%S" step)))
|
||||
(should (string= (sasl-step-data step) req))))
|
||||
(ert-info ("Server's final message")
|
||||
(let ((resp (concat "v=Va7NIvt8wCdhvxnv+bZriSxGoto6On5EVnRHO/ece8zs0"
|
||||
"qpQassdqir1Zlwh3e3EmBq+kcSy+ClNCsbzBpXe/w==")))
|
||||
(sasl-step-set-data step resp)
|
||||
(setq step (sasl-next-step client step))
|
||||
(should-not (sasl-step-data step)))))
|
||||
(should (eq sasl-unique-id-function #'sasl-unique-id-function)))
|
||||
|
||||
(defconst erc-sasl-tests-ecdsa-key-file "
|
||||
-----BEGIN EC PARAMETERS-----
|
||||
BggqhkjOPQMBBw==
|
||||
-----END EC PARAMETERS-----
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIIJueQ3W2IrGbe9wKdOI75yGS7PYZSj6W4tg854hlsvmoAoGCCqGSM49
|
||||
AwEHoUQDQgAEAZmaVhNSMmV5r8FXPvKuMnqDKyIA9pDHN5TNMfiF3mMeikGgK10W
|
||||
IRX9cyi2wdYg9mUUYyh9GKdBCYHGUJAiCA==
|
||||
-----END EC PRIVATE KEY-----
|
||||
")
|
||||
|
||||
(ert-deftest erc-sasl-create-client-ecdsa ()
|
||||
:tags '(:unstable)
|
||||
;; This is currently useless because it just roundtrips shelling out
|
||||
;; to pkeyutl.
|
||||
(ert-skip "Placeholder")
|
||||
(unless (executable-find "openssl")
|
||||
(ert-skip "System lacks openssl"))
|
||||
(ert-with-temp-file keyfile
|
||||
:prefix "ecdsa_key"
|
||||
:suffix ".pem"
|
||||
:text erc-sasl-tests-ecdsa-key-file
|
||||
(let* ((erc-server-current-nick "jilles")
|
||||
(erc-sasl--options `((password . ,keyfile)))
|
||||
(client (erc-sasl--create-client 'ecdsa-nist256p-challenge))
|
||||
(step (sasl-next-step client nil)))
|
||||
(ert-info ("Client's initial request")
|
||||
(should (equal (format "%S" [erc-sasl--ecdsa-first "jilles"])
|
||||
(format "%S" step)))
|
||||
(should (string= (sasl-step-data step) "jilles")))
|
||||
(ert-info ("Server's initial response")
|
||||
(let ((resp (concat "\0\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20"
|
||||
"\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37")))
|
||||
(sasl-step-set-data step resp)
|
||||
(setq step (sasl-next-step client step))
|
||||
(ert-with-temp-file sigfile
|
||||
:prefix "ecdsa_sig"
|
||||
:suffix ".sig"
|
||||
:text (sasl-step-data step)
|
||||
(with-temp-buffer
|
||||
(set-buffer-multibyte nil)
|
||||
(insert resp)
|
||||
(let ((ec (call-process-region
|
||||
(point-min) (point-max)
|
||||
"openssl" 'delete t nil "pkeyutl"
|
||||
"-inkey" keyfile "-sigfile" sigfile
|
||||
"-verify")))
|
||||
(unless (zerop ec)
|
||||
(message "%s" (buffer-string)))
|
||||
(should (zerop ec)))))))
|
||||
(should-not (sasl-next-step client step)))))
|
||||
|
||||
;;; erc-sasl-tests.el ends here
|
144
test/lisp/erc/erc-scenarios-sasl.el
Normal file
144
test/lisp/erc/erc-scenarios-sasl.el
Normal file
|
@ -0,0 +1,144 @@
|
|||
;;; erc-scenarios-sasl.el --- SASL tests for ERC -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2022 Free Software Foundation, Inc.
|
||||
;;
|
||||
;; This file is part of GNU Emacs.
|
||||
;;
|
||||
;; This program is free software: you can redistribute it and/or
|
||||
;; modify it under the terms of the GNU General Public License as
|
||||
;; published by the Free Software Foundation, either version 3 of the
|
||||
;; License, or (at your option) any later version.
|
||||
;;
|
||||
;; This program is distributed in the hope that it will be useful, but
|
||||
;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
;; General Public License for more details.
|
||||
;;
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see
|
||||
;; <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'ert-x)
|
||||
(eval-and-compile
|
||||
(let ((load-path (cons (ert-resource-directory) load-path)))
|
||||
(require 'erc-scenarios-common)))
|
||||
|
||||
(require 'erc-sasl)
|
||||
|
||||
(ert-deftest erc-scenarios-sasl--plain ()
|
||||
:tags '(:expensive-test)
|
||||
(erc-scenarios-common-with-cleanup
|
||||
((erc-scenarios-common-dialog "sasl")
|
||||
(erc-server-flood-penalty 0.1)
|
||||
(dumb-server (erc-d-run "localhost" t 'plain))
|
||||
(port (process-contact dumb-server :service))
|
||||
(erc-modules (cons 'sasl erc-modules))
|
||||
(erc-sasl-password "password123")
|
||||
(expect (erc-d-t-make-expecter)))
|
||||
|
||||
(ert-info ("Connect")
|
||||
(with-current-buffer (erc :server "127.0.0.1"
|
||||
:port port
|
||||
:nick "tester"
|
||||
:user "tester"
|
||||
:full-name "tester")
|
||||
(should (string= (buffer-name) (format "127.0.0.1:%d" port)))))
|
||||
|
||||
(ert-info ("Notices received")
|
||||
(with-current-buffer (erc-d-t-wait-for 10 (get-buffer "ExampleOrg"))
|
||||
(funcall expect 10 "This server is in debug mode")
|
||||
;; Regression "\0\0\0\0 ..." caused by (fillarray passphrase 0)
|
||||
(should (string= erc-sasl-password "password123"))))))
|
||||
|
||||
(ert-deftest erc-scenarios-sasl--external ()
|
||||
:tags '(:expensive-test)
|
||||
(erc-scenarios-common-with-cleanup
|
||||
((erc-scenarios-common-dialog "sasl")
|
||||
(erc-server-flood-penalty 0.1)
|
||||
(dumb-server (erc-d-run "localhost" t 'external))
|
||||
(port (process-contact dumb-server :service))
|
||||
(erc-modules (cons 'sasl erc-modules))
|
||||
(erc-sasl-mechanism 'external)
|
||||
(expect (erc-d-t-make-expecter)))
|
||||
|
||||
(ert-info ("Connect")
|
||||
(with-current-buffer (erc :server "127.0.0.1"
|
||||
:port port
|
||||
:nick "tester"
|
||||
:user "tester"
|
||||
:full-name "tester")
|
||||
(should (string= (buffer-name) (format "127.0.0.1:%d" port)))))
|
||||
|
||||
(ert-info ("Notices received")
|
||||
(with-current-buffer (erc-d-t-wait-for 10 (get-buffer "ExampleOrg"))
|
||||
(funcall expect 10 "Authentication successful")
|
||||
(funcall expect 10 "This server is in debug mode")))))
|
||||
|
||||
(ert-deftest erc-scenarios-sasl--plain-fail ()
|
||||
:tags '(:expensive-test)
|
||||
(erc-scenarios-common-with-cleanup
|
||||
((erc-scenarios-common-dialog "sasl")
|
||||
(erc-server-flood-penalty 0.1)
|
||||
(dumb-server (erc-d-run "localhost" t 'plain-failed))
|
||||
(port (process-contact dumb-server :service))
|
||||
(erc-modules (cons 'sasl erc-modules))
|
||||
(erc-sasl-password "wrong")
|
||||
(erc-sasl-mechanism 'plain)
|
||||
(expect (erc-d-t-make-expecter))
|
||||
(buf nil))
|
||||
|
||||
(ert-info ("Connect")
|
||||
(setq buf (erc :server "127.0.0.1"
|
||||
:port port
|
||||
:nick "tester"
|
||||
:user "tester"
|
||||
:full-name "tester"))
|
||||
(let ((err (should-error
|
||||
(with-current-buffer buf
|
||||
(funcall expect 20 "Connection failed!")))))
|
||||
(should (string-search "please review" (cadr err)))
|
||||
(with-current-buffer buf
|
||||
(funcall expect 10 "Opening connection")
|
||||
(funcall expect 20 "SASL authentication failed")
|
||||
(should-not (erc-server-process-alive)))))))
|
||||
|
||||
(defun erc-scenarios--common--sasl (mech)
|
||||
(erc-scenarios-common-with-cleanup
|
||||
((erc-scenarios-common-dialog "sasl")
|
||||
(erc-server-flood-penalty 0.1)
|
||||
(dumb-server (erc-d-run "localhost" t mech))
|
||||
(port (process-contact dumb-server :service))
|
||||
(erc-modules (cons 'sasl erc-modules))
|
||||
(erc-sasl-user :nick)
|
||||
(erc-sasl-mechanism mech)
|
||||
(mock-rvs (list "c5RqLCZy0L4fGkKAZ0hujFBs" ""))
|
||||
(sasl-unique-id-function (lambda () (pop mock-rvs)))
|
||||
(expect (erc-d-t-make-expecter)))
|
||||
|
||||
(ert-info ("Connect")
|
||||
(with-current-buffer (erc :server "127.0.0.1"
|
||||
:port port
|
||||
:nick "jilles"
|
||||
:password "sesame"
|
||||
:full-name "jilles")
|
||||
(should (string= (buffer-name) (format "127.0.0.1:%d" port)))))
|
||||
|
||||
(ert-info ("Notices received")
|
||||
(with-current-buffer (erc-d-t-wait-for 10 (get-buffer "jaguar"))
|
||||
(funcall expect 10 "Found your hostname")
|
||||
(funcall expect 20 "marked as being away")))))
|
||||
|
||||
(ert-deftest erc-scenarios-sasl--scram-sha-1 ()
|
||||
:tags '(:expensive-test)
|
||||
(let ((erc-sasl-authzid "jilles"))
|
||||
(erc-scenarios--common--sasl 'scram-sha-1)))
|
||||
|
||||
(ert-deftest erc-scenarios-sasl--scram-sha-256 ()
|
||||
:tags '(:expensive-test)
|
||||
(unless (featurep 'sasl-scram-sha256)
|
||||
(ert-skip "Emacs lacks sasl-scram-sha256"))
|
||||
(erc-scenarios--common--sasl 'scram-sha-256))
|
||||
|
||||
;;; erc-scenarios-sasl.el ends here
|
33
test/lisp/erc/resources/sasl/external.eld
Normal file
33
test/lisp/erc/resources/sasl/external.eld
Normal file
|
@ -0,0 +1,33 @@
|
|||
;; -*- mode: lisp-data; -*-
|
||||
((cap-req 10 "CAP REQ :sasl"))
|
||||
((nick 1 "NICK tester"))
|
||||
((user 1 "USER tester 0 * :tester"))
|
||||
|
||||
((auth-req 3.2 "AUTHENTICATE EXTERNAL")
|
||||
(0.0 ":irc.example.org CAP * ACK :sasl")
|
||||
(0.0 "AUTHENTICATE +"))
|
||||
|
||||
((auth-noop 3.2 "AUTHENTICATE +")
|
||||
(0.0 ":irc.example.org 900 * * tester :You are now logged in as tester")
|
||||
(0.0 ":irc.example.org 903 * :Authentication successful"))
|
||||
|
||||
((cap-end 3.2 "CAP END")
|
||||
(0.0 ":irc.example.org 001 tester :Welcome to the ExampleOrg IRC Network tester")
|
||||
(0.01 ":irc.example.org 002 tester :Your host is irc.example.org, running version oragono-2.6.1")
|
||||
(0.01 ":irc.example.org 003 tester :This server was created Sat, 17 Jul 2021 09:06:42 UTC")
|
||||
(0.01 ":irc.example.org 004 tester irc.example.org oragono-2.6.1 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv")
|
||||
(0.0 ":irc.example.org 005 tester AWAYLEN=200 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server")
|
||||
(0.01 ":irc.example.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=ExampleOrg NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY :are supported by this server")
|
||||
(0.01 ":irc.example.org 005 tester draft/CHATHISTORY=100 :are supported by this server")
|
||||
(0.0 ":irc.example.org 251 tester :There are 1 users and 0 invisible on 1 server(s)")
|
||||
(0.0 ":irc.example.org 252 tester 0 :IRC Operators online")
|
||||
(0.0 ":irc.example.org 253 tester 0 :unregistered connections")
|
||||
(0.0 ":irc.example.org 254 tester 0 :channels formed")
|
||||
(0.0 ":irc.example.org 255 tester :I have 1 clients and 0 servers")
|
||||
(0.0 ":irc.example.org 265 tester 1 1 :Current local users 1, max 1")
|
||||
(0.21 ":irc.example.org 266 tester 1 1 :Current global users 1, max 1")
|
||||
(0.0 ":irc.example.org 422 tester :MOTD File is missing"))
|
||||
|
||||
((mode-user 1.2 "MODE tester +i")
|
||||
(0.0 ":irc.example.org 221 tester +Zi")
|
||||
(0.0 ":irc.example.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect."))
|
16
test/lisp/erc/resources/sasl/plain-failed.eld
Normal file
16
test/lisp/erc/resources/sasl/plain-failed.eld
Normal file
|
@ -0,0 +1,16 @@
|
|||
;; -*- mode: lisp-data; -*-
|
||||
((cap-req 10 "CAP REQ :sasl"))
|
||||
((nick 1 "NICK tester"))
|
||||
((user 1 "USER tester 0 * :tester")
|
||||
(0.0 ":irc.foonet.org NOTICE * :*** Looking up your hostname...")
|
||||
(0.0 ":irc.foonet.org NOTICE * :*** Found your hostname")
|
||||
(0.0 ":irc.foonet.org CAP * ACK :cap-notify sasl"))
|
||||
|
||||
((authenticate-plain 3.2 "AUTHENTICATE PLAIN")
|
||||
(0.0 ":irc.foonet.org AUTHENTICATE +"))
|
||||
|
||||
((authenticate-gimme 3.2 "AUTHENTICATE AHRlc3RlcgB3cm9uZw==")
|
||||
(0.0 ":irc.foonet.org 900 * * tester :You are now logged in as tester")
|
||||
(0.0 ":irc.foonet.org 904 * :SASL authentication failed: Invalid account credentials"))
|
||||
|
||||
((cap-end 3.2 "CAP END"))
|
39
test/lisp/erc/resources/sasl/plain.eld
Normal file
39
test/lisp/erc/resources/sasl/plain.eld
Normal file
|
@ -0,0 +1,39 @@
|
|||
;; -*- mode: lisp-data; -*-
|
||||
((cap-req 10 "CAP REQ :sasl"))
|
||||
((nick 1 "NICK tester"))
|
||||
((user 1 "USER tester 0 * :tester")
|
||||
(0.0 ":irc.example.org NOTICE * :*** Looking up your hostname...")
|
||||
(0.0 ":irc.example.org NOTICE * :*** Found your hostname")
|
||||
(0.0 ":irc.example.org CAP * ACK :sasl"))
|
||||
|
||||
((authenticate-plain 3.2 "AUTHENTICATE PLAIN")
|
||||
(0.0 ":irc.example.org AUTHENTICATE +"))
|
||||
|
||||
((authenticate-gimme 3.2 "AUTHENTICATE AHRlc3RlcgBwYXNzd29yZDEyMw==")
|
||||
(0.0 ":irc.example.org 900 * * tester :You are now logged in as tester")
|
||||
(0.0 ":irc.example.org 903 * :Authentication successful"))
|
||||
|
||||
((cap-end 3.2 "CAP END")
|
||||
(0.0 ":irc.example.org 001 tester :Welcome to the ExampleOrg IRC Network tester")
|
||||
(0.01 ":irc.example.org 002 tester :Your host is irc.example.org, running version oragono-2.6.1")
|
||||
(0.01 ":irc.example.org 003 tester :This server was created Sat, 17 Jul 2021 09:06:42 UTC")
|
||||
(0.01 ":irc.example.org 004 tester irc.example.org oragono-2.6.1 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv")
|
||||
(0.0 ":irc.example.org 005 tester AWAYLEN=200 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server")
|
||||
(0.01 ":irc.example.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=ExampleOrg NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY :are supported by this server")
|
||||
(0.01 ":irc.example.org 005 tester draft/CHATHISTORY=100 :are supported by this server")
|
||||
(0.0 ":irc.example.org 251 tester :There are 1 users and 0 invisible on 1 server(s)")
|
||||
(0.0 ":irc.example.org 252 tester 0 :IRC Operators online")
|
||||
(0.0 ":irc.example.org 253 tester 0 :unregistered connections")
|
||||
(0.0 ":irc.example.org 254 tester 0 :channels formed")
|
||||
(0.0 ":irc.example.org 255 tester :I have 1 clients and 0 servers")
|
||||
(0.0 ":irc.example.org 265 tester 1 1 :Current local users 1, max 1")
|
||||
(0.21 ":irc.example.org 266 tester 1 1 :Current global users 1, max 1")
|
||||
(0.0 ":irc.example.org 422 tester :MOTD File is missing"))
|
||||
|
||||
((mode-user 1.2 "MODE tester +i")
|
||||
(0.0 ":irc.example.org 221 tester +Zi")
|
||||
(0.0 ":irc.example.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect."))
|
||||
|
||||
((quit 5 "QUIT :\2ERC\2")
|
||||
(0 ":tester!~u@yuvqisyu7m7qs.irc QUIT :Quit"))
|
||||
((drop 1 DROP))
|
47
test/lisp/erc/resources/sasl/scram-sha-1.eld
Normal file
47
test/lisp/erc/resources/sasl/scram-sha-1.eld
Normal file
|
@ -0,0 +1,47 @@
|
|||
;;; -*- mode: lisp-data -*-
|
||||
((cap-req 5.2 "CAP REQ :sasl"))
|
||||
((nick 10 "NICK jilles"))
|
||||
((user 10 "USER user 0 * :jilles")
|
||||
(0 "NOTICE AUTH :*** Processing connection to jaguar.test")
|
||||
(0 "NOTICE AUTH :*** Looking up your hostname...")
|
||||
(0 "NOTICE AUTH :*** Checking Ident")
|
||||
(0 "NOTICE AUTH :*** No Ident response")
|
||||
(0 "NOTICE AUTH :*** Found your hostname")
|
||||
(0 ":jaguar.test CAP jilles ACK :sasl"))
|
||||
|
||||
((auth-init 10 "AUTHENTICATE SCRAM-SHA-1")
|
||||
(0 "AUTHENTICATE +"))
|
||||
|
||||
((auth-challenge 10 "AUTHENTICATE bixhPWppbGxlcyxuPWppbGxlcyxyPWM1UnFMQ1p5MEw0ZkdrS0FaMGh1akZCcw==")
|
||||
(0 "AUTHENTICATE cj1jNVJxTENaeTBMNGZHa0tBWjBodWpGQnNYUW9LY2l2cUN3OWlEWlBTcGIscz01bUpPNmQ0cmpDbnNCVTFYLGk9NDA5Ng=="))
|
||||
|
||||
((auth-final 10 "AUTHENTICATE Yz1iaXhoUFdwcGJHeGxjeXc9LHI9YzVScUxDWnkwTDRmR2tLQVowaHVqRkJzWFFvS2NpdnFDdzlpRFpQU3BiLHA9T1ZVaGdQdTh3RW0yY0RvVkxmYUh6VlVZUFdVPQ==")
|
||||
(0 "AUTHENTICATE dj1aV1IyM2M5TUppcjBaZ2ZHZjVqRXRMT242Tmc9"))
|
||||
|
||||
((auth-done 10 "AUTHENTICATE +")
|
||||
(0 ":jaguar.test 900 jilles jilles!jilles@localhost.stack.nl jilles :You are now logged in as jilles")
|
||||
(0 ":jaguar.test 903 jilles :SASL authentication successful"))
|
||||
|
||||
((cap-end 10.2 "CAP END")
|
||||
(0 ":jaguar.test 001 jilles :Welcome to the jaguar IRC Network jilles!~jilles@127.0.0.1")
|
||||
(0 ":jaguar.test 002 jilles :Your host is jaguar.test, running version InspIRCd-3")
|
||||
(0 ":jaguar.test 003 jilles :This server was created 09:44:05 Dec 24 2020")
|
||||
(0 ":jaguar.test 004 jilles jaguar.test InspIRCd-3 BILRSWcghiorswz ABEFHIJLMNOQRSTXYabcefghijklmnopqrstuvz :BEFHIJLXYabefghjkloqv")
|
||||
(0 ":jaguar.test 005 jilles ACCEPT=30 AWAYLEN=200 BOT=B CALLERID=g CASEMAPPING=rfc1459 CHANLIMIT=#:120 CHANMODES=IXbeg,k,BEFHJLfjl,AMNOQRSTcimnprstuz CHANNELLEN=64 CHANTYPES=# ELIST=CMNTU ESILENCE=CcdiNnPpTtx EXCEPTS=e :are supported by this server")
|
||||
(0 ":jaguar.test 005 jilles EXTBAN=,ANOQRSTUacmnprz HOSTLEN=64 INVEX=I KEYLEN=32 KICKLEN=255 LINELEN=512 MAXLIST=I:100,X:100,b:100,e:100,g:100 MAXTARGETS=20 MODES=20 MONITOR=30 NAMELEN=128 NAMESX NETWORK=jaguar :are supported by this server")
|
||||
(0 ":jaguar.test 005 jilles NICKLEN=31 PREFIX=(Yqaohv)!~&@%+ REMOVE SAFELIST SECURELIST=60 SILENCE=32 STATUSMSG=!~&@%+ TOPICLEN=307 UHNAMES USERIP USERLEN=11 USERMODES=,,s,BILRSWcghiorwz WATCH=30 :are supported by this server")
|
||||
(0 ":jaguar.test 005 jilles :are supported by this server")
|
||||
(0 ":jaguar.test 251 jilles :There are 740 users and 108 invisible on 11 servers")
|
||||
(0 ":jaguar.test 252 jilles 10 :operator(s) online")
|
||||
(0 ":jaguar.test 254 jilles 373 :channels formed")
|
||||
(0 ":jaguar.test 255 jilles :I have 28 clients and 1 servers")
|
||||
(0 ":jaguar.test 265 jilles :Current local users: 28 Max: 29")
|
||||
(0 ":jaguar.test 266 jilles :Current global users: 848 Max: 879")
|
||||
(0 ":jaguar.test 375 jilles :jaguar.test message of the day")
|
||||
(0 ":jaguar.test 372 jilles : ~~ some message of the day ~~")
|
||||
(0 ":jaguar.test 372 jilles : ~~ or rkpryyrag gb rnpu bgure ~~")
|
||||
(0 ":jaguar.test 376 jilles :End of message of the day."))
|
||||
|
||||
((mode-user 1.2 "MODE jilles +i")
|
||||
(0 ":jilles!~jilles@127.0.0.1 MODE jilles :+ri")
|
||||
(0 ":jaguar.test 306 jilles :You have been marked as being away"))
|
47
test/lisp/erc/resources/sasl/scram-sha-256.eld
Normal file
47
test/lisp/erc/resources/sasl/scram-sha-256.eld
Normal file
|
@ -0,0 +1,47 @@
|
|||
;;; -*- mode: lisp-data -*-
|
||||
((cap-req 5.2 "CAP REQ :sasl"))
|
||||
((nick 10 "NICK jilles"))
|
||||
((user 10 "USER user 0 * :jilles")
|
||||
(0 "NOTICE AUTH :*** Processing connection to jaguar.test")
|
||||
(0 "NOTICE AUTH :*** Looking up your hostname...")
|
||||
(0 "NOTICE AUTH :*** Checking Ident")
|
||||
(0 "NOTICE AUTH :*** No Ident response")
|
||||
(0 "NOTICE AUTH :*** Found your hostname")
|
||||
(0 ":jaguar.test CAP jilles ACK :sasl"))
|
||||
|
||||
((auth-init 10 "AUTHENTICATE SCRAM-SHA-256")
|
||||
(0 "AUTHENTICATE +"))
|
||||
|
||||
((auth-challenge 10 "AUTHENTICATE biwsbj1qaWxsZXMscj1jNVJxTENaeTBMNGZHa0tBWjBodWpGQnM=")
|
||||
(0 "AUTHENTICATE cj1jNVJxTENaeTBMNGZHa0tBWjBodWpGQnNkNDA2N2YwYWZkYjU0YzNkYmQ0ZmU2NDViODRjYWUzNyxzPVpUZzFNbUUxWW1GaFpHSTFORGN5TWprM056WXdabVJqWkRNM1kySTFPVE09LGk9NDA5Ng=="))
|
||||
|
||||
((auth-final 10 "AUTHENTICATE Yz1iaXdzLHI9YzVScUxDWnkwTDRmR2tLQVowaHVqRkJzZDQwNjdmMGFmZGI1NGMzZGJkNGZlNjQ1Yjg0Y2FlMzcscD1MUDRzakpyakpLcDVxVHNBUnlaQ3BwWHBLTHU0Rk1NMjg0aE5FU1B2R2hJPQ==")
|
||||
(0 "AUTHENTICATE dj04NDdXWGZubVJlR3lFMXFscTFBbmQ2UjRiUEJOUk9UWjdFTVMvUXJKdFVNPQ=="))
|
||||
|
||||
((auth-done 10 "AUTHENTICATE +")
|
||||
(0 ":jaguar.test 900 jilles jilles!jilles@localhost.stack.nl jilles :You are now logged in as jilles")
|
||||
(0 ":jaguar.test 903 jilles :SASL authentication successful"))
|
||||
|
||||
((cap-end 10.2 "CAP END")
|
||||
(0 ":jaguar.test 001 jilles :Welcome to the jaguar IRC Network jilles!~jilles@127.0.0.1")
|
||||
(0 ":jaguar.test 002 jilles :Your host is jaguar.test, running version InspIRCd-3")
|
||||
(0 ":jaguar.test 003 jilles :This server was created 09:44:05 Dec 24 2020")
|
||||
(0 ":jaguar.test 004 jilles jaguar.test InspIRCd-3 BILRSWcghiorswz ABEFHIJLMNOQRSTXYabcefghijklmnopqrstuvz :BEFHIJLXYabefghjkloqv")
|
||||
(0 ":jaguar.test 005 jilles ACCEPT=30 AWAYLEN=200 BOT=B CALLERID=g CASEMAPPING=rfc1459 CHANLIMIT=#:120 CHANMODES=IXbeg,k,BEFHJLfjl,AMNOQRSTcimnprstuz CHANNELLEN=64 CHANTYPES=# ELIST=CMNTU ESILENCE=CcdiNnPpTtx EXCEPTS=e :are supported by this server")
|
||||
(0 ":jaguar.test 005 jilles EXTBAN=,ANOQRSTUacmnprz HOSTLEN=64 INVEX=I KEYLEN=32 KICKLEN=255 LINELEN=512 MAXLIST=I:100,X:100,b:100,e:100,g:100 MAXTARGETS=20 MODES=20 MONITOR=30 NAMELEN=128 NAMESX NETWORK=jaguar :are supported by this server")
|
||||
(0 ":jaguar.test 005 jilles NICKLEN=31 PREFIX=(Yqaohv)!~&@%+ REMOVE SAFELIST SECURELIST=60 SILENCE=32 STATUSMSG=!~&@%+ TOPICLEN=307 UHNAMES USERIP USERLEN=11 USERMODES=,,s,BILRSWcghiorwz WATCH=30 :are supported by this server")
|
||||
(0 ":jaguar.test 005 jilles :are supported by this server")
|
||||
(0 ":jaguar.test 251 jilles :There are 740 users and 108 invisible on 11 servers")
|
||||
(0 ":jaguar.test 252 jilles 10 :operator(s) online")
|
||||
(0 ":jaguar.test 254 jilles 373 :channels formed")
|
||||
(0 ":jaguar.test 255 jilles :I have 28 clients and 1 servers")
|
||||
(0 ":jaguar.test 265 jilles :Current local users: 28 Max: 29")
|
||||
(0 ":jaguar.test 266 jilles :Current global users: 848 Max: 879")
|
||||
(0 ":jaguar.test 375 jilles :jaguar.test message of the day")
|
||||
(0 ":jaguar.test 372 jilles : ~~ some message of the day ~~")
|
||||
(0 ":jaguar.test 372 jilles : ~~ or rkpryyrag gb rnpu bgure ~~")
|
||||
(0 ":jaguar.test 376 jilles :End of message of the day."))
|
||||
|
||||
((mode-user 1.2 "MODE jilles +i")
|
||||
(0 ":jilles!~jilles@127.0.0.1 MODE jilles :+ri")
|
||||
(0 ":jaguar.test 306 jilles :You have been marked as being away"))
|
Loading…
Add table
Reference in a new issue