summaryrefslogtreecommitdiff
path: root/jabber-sasl.el
diff options
context:
space:
mode:
Diffstat (limited to 'jabber-sasl.el')
-rw-r--r--jabber-sasl.el157
1 files changed, 157 insertions, 0 deletions
diff --git a/jabber-sasl.el b/jabber-sasl.el
new file mode 100644
index 0000000..61d3c56
--- /dev/null
+++ b/jabber-sasl.el
@@ -0,0 +1,157 @@
+;; jabber-sasl.el - SASL authentication
+
+;; Copyright (C) 2004, 2007, 2008 - Magnus Henoch - mange@freemail.hu
+
+;; This file is a part of jabber.el.
+
+;; 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 2 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, write to the Free Software
+;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+(require 'cl)
+
+;;; This file uses sasl.el from FLIM or Gnus. If it can't be found,
+;;; jabber-core.el won't use the SASL functions.
+(eval-and-compile
+ (condition-case nil
+ (require 'sasl)
+ (error nil)))
+
+;;; Alternatives to FLIM would be the command line utility of GNU SASL,
+;;; or anything the Gnus people decide to use.
+
+;;; See XMPP-CORE and XMPP-IM for details about the protocol.
+
+(require 'jabber-xml)
+
+(defun jabber-sasl-start-auth (jc stream-features)
+ ;; Find a suitable common mechanism.
+ (let* ((mechanism-elements (car (jabber-xml-get-children stream-features 'mechanisms)))
+ (mechanisms (mapcar
+ (lambda (tag)
+ (car (jabber-xml-node-children tag)))
+ (jabber-xml-get-children mechanism-elements 'mechanism)))
+ (mechanism
+ (if (and (member "ANONYMOUS" mechanisms)
+ (or jabber-silent-mode (yes-or-no-p "Use anonymous authentication? ")))
+ (sasl-find-mechanism '("ANONYMOUS"))
+ (sasl-find-mechanism mechanisms))))
+
+ ;; No suitable mechanism?
+ (if (null mechanism)
+ ;; Maybe we can use legacy authentication
+ (let ((iq-auth (find "http://jabber.org/features/iq-auth"
+ (jabber-xml-get-children stream-features 'auth)
+ :key #'jabber-xml-get-xmlns
+ :test #'string=))
+ ;; Or maybe we have to use STARTTLS, but can't
+ (starttls (find "urn:ietf:params:xml:ns:xmpp-tls"
+ (jabber-xml-get-children stream-features 'starttls)
+ :key #'jabber-xml-get-xmlns
+ :test #'string=)))
+ (cond
+ (iq-auth
+ (fsm-send jc :use-legacy-auth-instead))
+ (starttls
+ (message "STARTTLS encryption required, but disabled/non-functional at our end")
+ (fsm-send jc :authentication-failure))
+ (t
+ (message "Authentication failure: no suitable SASL mechanism found")
+ (fsm-send jc :authentication-failure))))
+
+ ;; Watch for plaintext logins over unencrypted connections
+ (if (and (not (plist-get (fsm-get-state-data jc) :encrypted))
+ (member (sasl-mechanism-name mechanism)
+ '("PLAIN" "LOGIN"))
+ (not (yes-or-no-p "Jabber server only allows cleartext password transmission! Continue? ")))
+ (fsm-send jc :authentication-failure)
+
+ ;; Start authentication.
+ (let* (passphrase
+ (client (sasl-make-client mechanism
+ (plist-get (fsm-get-state-data jc) :username)
+ "xmpp"
+ (plist-get (fsm-get-state-data jc) :server)))
+ (sasl-read-passphrase (jabber-sasl-read-passphrase-closure
+ jc
+ (lambda (p) (setq passphrase (copy-sequence p)) p)))
+ (step (sasl-next-step client nil)))
+ (jabber-send-sexp
+ jc
+ `(auth ((xmlns . "urn:ietf:params:xml:ns:xmpp-sasl")
+ (mechanism . ,(sasl-mechanism-name mechanism)))
+ ,(when (sasl-step-data step)
+ (base64-encode-string (sasl-step-data step) t))))
+ (list client step passphrase))))))
+
+(defun jabber-sasl-read-passphrase-closure (jc remember)
+ "Return a lambda function suitable for `sasl-read-passphrase' for JC.
+Call REMEMBER with the password. REMEMBER is expected to return it as well."
+ (lexical-let ((password (plist-get (fsm-get-state-data jc) :password))
+ (bare-jid (jabber-connection-bare-jid jc))
+ (remember remember))
+ (if password
+ (lambda (prompt) (funcall remember (copy-sequence password)))
+ (lambda (prompt) (funcall remember (jabber-read-password bare-jid))))))
+
+(defun jabber-sasl-process-input (jc xml-data sasl-data)
+ (let* ((client (first sasl-data))
+ (step (second sasl-data))
+ (passphrase (third sasl-data))
+ (sasl-read-passphrase (jabber-sasl-read-passphrase-closure
+ jc
+ (lambda (p) (setq passphrase (copy-sequence p)) p))))
+ (cond
+ ((eq (car xml-data) 'challenge)
+ (sasl-step-set-data step (base64-decode-string (car (jabber-xml-node-children xml-data))))
+ (setq step (sasl-next-step client step))
+ (jabber-send-sexp
+ jc
+ `(response ((xmlns . "urn:ietf:params:xml:ns:xmpp-sasl"))
+ ,(when (sasl-step-data step)
+ (base64-encode-string (sasl-step-data step) t)))))
+
+ ((eq (car xml-data) 'failure)
+ (message "%s: authentication failure: %s"
+ (jabber-connection-bare-jid jc)
+ (jabber-xml-node-name (car (jabber-xml-node-children xml-data))))
+ (fsm-send jc :authentication-failure))
+
+ ((eq (car xml-data) 'success)
+ ;; The server might, depending on the mechanism, send
+ ;; "additional data" (see RFC 4422) with the <success/> element.
+ ;; Since some SASL mechanisms perform mutual authentication, we
+ ;; need to pass this data to sasl.el - we're not necessarily
+ ;; done just because the server says we're done.
+ (let* ((data (car (jabber-xml-node-children xml-data)))
+ (decoded (if data
+ (base64-decode-string data)
+ "")))
+ (sasl-step-set-data step decoded)
+ (condition-case e
+ (progn
+ ;; Check that sasl-next-step doesn't signal an error.
+ ;; TODO: once sasl.el allows it, check that all steps have
+ ;; been completed.
+ (sasl-next-step client step)
+ (message "Authentication succeeded for %s" (jabber-connection-bare-jid jc))
+ (fsm-send jc (cons :authentication-success passphrase)))
+ (sasl-error
+ (message "%s: authentication failure: %s"
+ (jabber-connection-bare-jid jc)
+ (error-message-string e))
+ (fsm-send jc :authentication-failure))))))
+ (list client step passphrase)))
+
+(provide 'jabber-sasl)
+;;; arch-tag: 2a4a234d-34d3-49dd-950d-518c899c0fd0