;;; Commentary:
-(require 'poe)
+;; This module provides common interface functions to share several
+;; SASL mechanism drivers. The toplevel is designed to be mostly
+;; compatible with [Java-SASL].
+;;
+;; [SASL] J. Myers, "Simple Authentication and Security Layer (SASL)",
+;; RFC 2222, October 1997.
+;;
+;; [Java-SASL] R. Weltman & R. Lee, "The Java SASL Application Program
+;; Interface", draft-weltman-java-sasl-03.txt, March 2000.
+
+;;; Code:
(defvar sasl-mechanisms
+ '("CRAM-MD5" "DIGEST-MD5" "PLAIN" "LOGIN" "ANONYMOUS"))
+
+(defvar sasl-mechanism-alist
'(("CRAM-MD5" sasl-cram)
("DIGEST-MD5" sasl-digest)
- ("PLAIN" sasl-plain)))
+ ("PLAIN" sasl-plain)
+ ("LOGIN" sasl-login)
+ ("ANONYMOUS" sasl-anonymous)))
(defvar sasl-unique-id-function #'sasl-unique-id-function)
-(defmacro sasl-make-authenticator (mechanism continuations)
- `(vector ,mechanism ,continuations))
+(put 'sasl-error 'error-message "SASL error")
+(put 'sasl-error 'error-conditions '(sasl-error error))
+
+(defun sasl-error (datum)
+ (signal 'sasl-error (list datum)))
+
+;;; @ SASL client
+;;;
+
+(defun sasl-make-client (mechanism name service server)
+ "Return a newly allocated SASL client.
+NAME is name of the authorization. SERVICE is name of the service desired.
+SERVER is the fully qualified host name of the server to authenticate to."
+ (vector mechanism name service server (make-symbol "sasl-client-properties")))
+
+(defun sasl-client-mechanism (client)
+ "Return the authentication mechanism driver of CLIENT."
+ (aref client 0))
+
+(defun sasl-client-name (client)
+ "Return the authorization name of CLIENT, a string."
+ (aref client 1))
+
+(defun sasl-client-service (client)
+ "Return the service name of CLIENT, a string."
+ (aref client 2))
+
+(defun sasl-client-server (client)
+ "Return the server name of CLIENT, a string."
+ (aref client 3))
+
+(defun sasl-client-set-properties (client plist)
+ "Destructively set the properties of CLIENT.
+The second argument PLIST is the new property list."
+ (setplist (aref client 4) plist))
+
+(defun sasl-client-set-property (client property value)
+ "Add the given property/value to CLIENT."
+ (put (aref client 4) property value))
+
+(defun sasl-client-property (client property)
+ "Return the value of the PROPERTY of CLIENT."
+ (get (aref client 4) property))
-(defmacro sasl-authenticator-mechanism-internal (authenticator)
- `(aref ,authenticator 0))
+(defun sasl-client-properties (client)
+ "Return the properties of CLIENT."
+ (symbol-plist (aref client 4)))
-(defmacro sasl-authenticator-continuations-internal (authenticator)
- `(aref ,authenticator 1))
+;;; @ SASL mechanism
+;;;
-(defmacro sasl-make-principal (name service server &optional realm)
- `(vector ,name ,realm ,service ,server))
+(defun sasl-make-mechanism (name steps)
+ "Make an authentication mechanism.
+NAME is a IANA registered SASL mechanism name.
+STEPS is list of continuation function."
+ (vector name
+ (mapcar
+ (lambda (step)
+ (let ((symbol (make-symbol (symbol-name step))))
+ (fset symbol (symbol-function step))
+ symbol))
+ steps)))
-(defmacro sasl-principal-name-internal (principal)
- `(aref ,principal 0))
+(defun sasl-mechanism-name (mechanism)
+ "Return name of MECHANISM, a string."
+ (aref mechanism 0))
-(defmacro sasl-principal-realm-internal (principal)
- `(aref ,principal 1))
+(defun sasl-mechanism-steps (mechanism)
+ "Return the authentication steps of MECHANISM, a list of functions."
+ (aref mechanism 1))
-(defmacro sasl-principal-service-internal (principal)
- `(aref ,principal 2))
+(defun sasl-find-mechanism (mechanisms)
+ "Retrieve an apropriate mechanism object from MECHANISMS hints."
+ (let* ((sasl-mechanisms sasl-mechanisms)
+ (mechanism
+ (catch 'done
+ (while sasl-mechanisms
+ (if (member (car sasl-mechanisms) mechanisms)
+ (throw 'done (nth 1 (assoc (car sasl-mechanisms)
+ sasl-mechanism-alist))))
+ (setq sasl-mechanisms (cdr sasl-mechanisms))))))
+ (if mechanism
+ (require mechanism))
+ (get mechanism 'sasl-mechanism)))
-(defmacro sasl-principal-server-internal (principal)
- `(aref ,principal 3))
+;;; @ SASL authentication step
+;;;
-(defun sasl-find-authenticator (mechanisms)
- "Retrieve an apropriate authenticator object from MECHANISMS hints."
- (let (mechanism)
- (while mechanisms
- (if (setq mechanism (assoc (car mechanisms) sasl-mechanisms))
- (setq mechanism (nth 1 mechanism)
- mechanisms nil))
- (setq mechanisms (cdr mechanisms)))
- (when mechanism
- (require mechanism)
- (get mechanism 'sasl-authenticator))))
+(defun sasl-step-data (step)
+ "Return the data which STEP holds, a string."
+ (aref step 1))
-(defun sasl-evaluate-challenge (authenticator principal &optional challenge)
+(defun sasl-step-set-data (step data)
+ "Store DATA string to STEP."
+ (aset step 1 data))
+
+(defun sasl-next-step (client step)
"Evaluate the challenge and prepare an appropriate next response.
-The data type of the value and the CHALLENGE is nil or a cons cell of the form
-\(CONTINUATION STRING). At the first time CONTINUATION should be set to nil."
- (let* ((continuations
- (sasl-authenticator-continuations-internal authenticator))
+The data type of the value and optional 2nd argument STEP is nil or
+opaque authentication step which holds the reference to the next action
+and the current challenge. At the first time STEP should be set to nil."
+ (let* ((steps
+ (sasl-mechanism-steps
+ (sasl-client-mechanism client)))
(function
- (if (car challenge)
- (nth 1 (memq (car challenge) continuations))
- (car continuations))))
+ (if (vectorp step)
+ (nth 1 (memq (aref step 0) steps))
+ (car steps))))
(if function
- (list function (funcall function principal challenge)))))
+ (vector function (funcall function client step)))))
(defvar sasl-read-passphrase nil)
-(defun sasl-read-passphrase (prompt &optional key)
+(defun sasl-read-passphrase (prompt)
(if (not sasl-read-passphrase)
(if (functionp 'read-passwd)
(setq sasl-read-passphrase 'read-passwd)
(char-to-string (aref "zyxwvutsrqponmlkjihgfedcba9876543210"
(% num 36))))))
-;;; PLAIN SASL mechanism (RFC2595 Section 6)
-(defconst sasl-plain-continuations
+;;; PLAIN (RFC2595 Section 6)
+(defconst sasl-plain-steps
'(sasl-plain-response))
-(unless (get 'sasl-plain 'sasl-authenticator)
- (put 'sasl-plain 'sasl-authenticator
- (sasl-make-authenticator "PLAIN" sasl-plain-continuations)))
-
-(defun sasl-plain-response (principal challenge)
+(defun sasl-plain-response (client step)
(let ((passphrase
(sasl-read-passphrase
- (format "PLAIN passphrase for %s: "
- (sasl-principal-name-internal principal)))))
+ (format "PLAIN passphrase for %s: " (sasl-client-name client))))
+ (authenticator-name
+ (sasl-client-property
+ client 'authenticator-name))
+ (name (sasl-client-name client)))
(unwind-protect
- (concat "\0" (sasl-principal-name-internal principal) "\0" passphrase)
+ (if (and authenticator-name
+ (not (string= authenticator-name name)))
+ (concat authenticator-name "\0" name "\0" passphrase)
+ (concat "\0" name "\0" passphrase))
(fillarray passphrase 0))))
+(put 'sasl-plain 'sasl-mechanism
+ (sasl-make-mechanism "PLAIN" sasl-plain-steps))
+
(provide 'sasl-plain)
+;;; LOGIN (No specification exists)
+(defconst sasl-login-steps
+ '(ignore ;no initial response
+ sasl-login-response-1
+ sasl-login-response-2))
+
+(defun sasl-login-response-1 (client step)
+ (unless (string-match "^user ?name." (sasl-step-data step)) ;; XXX
+ (sasl-error (format "Unexpected response: %s" (sasl-step-data step))))
+ (sasl-client-name client))
+
+(defun sasl-login-response-2 (client step)
+ (unless (string-match "^password." (sasl-step-data step)) ;; XXX
+ (sasl-error (format "Unexpected response: %s" (sasl-step-data step))))
+ (sasl-read-passphrase
+ (format "LOGIN passphrase for %s: " (sasl-client-name client))))
+
+(put 'sasl-login 'sasl-mechanism
+ (sasl-make-mechanism "LOGIN" sasl-login-steps))
+
+(provide 'sasl-login)
+
+;;; ANONYMOUS (RFC2245)
+(defconst sasl-anonymous-steps
+ '(identity ;no initial response
+ sasl-anonymous-response))
+
+(defun sasl-anonymous-response (client step)
+ (or (sasl-client-property client 'trace)
+ (sasl-client-name client)))
+
+(put 'sasl-anonymous 'sasl-mechanism
+ (sasl-make-mechanism "ANONYMOUS" sasl-anonymous-steps))
+
+(provide 'sasl-anonymous)
+
(provide 'sasl)
;;; sasl.el ends here