1 ;;; sasl.el --- SASL client framework
3 ;; Copyright (C) 2000 Daiki Ueno
5 ;; Author: Daiki Ueno <ueno@unixuser.org>
8 ;; This file is part of FLIM (Faithful Library about Internet Message).
10 ;; This program is free software; you can redistribute it and/or
11 ;; modify it under the terms of the GNU General Public License as
12 ;; published by the Free Software Foundation; either version 2, or (at
13 ;; your option) any later version.
15 ;; This program is distributed in the hope that it will be useful, but
16 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 ;; General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with this program; see the file COPYING. If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
27 ;; This module provides common interface functions to share several
28 ;; SASL mechanism drivers. The toplevel is designed to be mostly
29 ;; compatible with [Java-SASL].
31 ;; [SASL] J. Myers, "Simple Authentication and Security Layer (SASL)",
32 ;; RFC 2222, October 1997.
34 ;; [Java-SASL] R. Weltman & R. Lee, "The Java SASL Application Program
35 ;; Interface", draft-weltman-java-sasl-03.txt, March 2000.
41 (defvar sasl-mechanisms
42 '("CRAM-MD5" "DIGEST-MD5" "PLAIN" "LOGIN" "ANONYMOUS"))
44 (defvar sasl-mechanism-alist
45 '(("CRAM-MD5" sasl-cram)
46 ("DIGEST-MD5" sasl-digest)
49 ("ANONYMOUS" sasl-anonymous)))
51 (defvar sasl-unique-id-function #'sasl-unique-id-function)
53 (put 'sasl-error 'error-message "SASL error")
54 (put 'sasl-error 'error-conditions '(sasl-error error))
56 (defun sasl-error (datum)
57 (signal 'sasl-error (list datum)))
59 ;;; @ SASL instantiator
62 (defmacro sasl-make-instantiator (name service server)
63 "Return a newly allocated SASL instantiator.
64 NAME is name of the authorization. SERVICE is name of the service desired.
65 SERVER is the fully qualified host name of the server to authenticate to."
66 (let ((props (make-symbol "sasl-instantiator-properties")))
67 `(vector ,name ,service ,server ',props)))
69 (defmacro sasl-instantiator-name (instantiator)
70 "Return the authorization name of INSTANTIATOR, a string."
71 `(aref ,instantiator 0))
73 (defmacro sasl-instantiator-service (instantiator)
74 "Return the service name of INSTANTIATOR, a string."
75 `(aref ,instantiator 1))
77 (defmacro sasl-instantiator-server (instantiator)
78 "Return the server name of INSTANTIATOR, a string."
79 `(aref ,instantiator 2))
81 (defmacro sasl-instantiator-set-properties (instantiator plist)
82 "Destructively set the properties of INSTANTIATOR.
83 The second argument PLIST is the new property list."
84 `(setplist (aref ,instantiator 3) ,plist))
86 (defmacro sasl-instantiator-set-property (instantiator property value)
87 "Add the given property/value to INSTANTIATOR."
88 `(put (aref ,instantiator 3) ,property ,value))
90 (defmacro sasl-instantiator-property (instantiator property)
91 "Return the value of the PROPERTY of INSTANTIATOR."
92 `(get (aref ,instantiator 3) ,property))
94 (defmacro sasl-instantiator-properties (instantiator)
95 "Return the properties of INSTANTIATOR."
96 `(symbol-plist (aref ,instantiator 3)))
98 ;;; @ SASL authenticator
101 (defun sasl-make-authenticator (mechanism continuations)
102 "Make an authenticator.
103 MECHANISM is a IANA registered SASL mechanism name.
104 CONTINUATIONS is list of continuation function."
107 (lambda (continuation)
108 (let ((symbol (make-symbol (symbol-name continuation))))
109 (fset symbol (symbol-function continuation))
113 (defmacro sasl-authenticator-mechanism (authenticator)
114 "Return name of the mechanism AUTHENTICATOR supports, a string."
115 `(aref ,authenticator 0))
117 (defmacro sasl-authenticator-continuations (authenticator)
118 "Return continuation steps of AUTHENTICATOR, a list of functions."
119 `(aref ,authenticator 1))
121 (defun sasl-find-authenticator (mechanisms)
122 "Retrieve an apropriate authenticator object from MECHANISMS hints."
123 (let* ((sasl-mechanisms sasl-mechanisms)
126 (while sasl-mechanisms
127 (if (member (car sasl-mechanisms) mechanisms)
128 (throw 'done (nth 1 (assoc (car sasl-mechanisms)
129 sasl-mechanism-alist))))
130 (setq sasl-mechanisms (cdr sasl-mechanisms))))))
133 (get mechanism 'sasl-authenticator))))
135 (defun sasl-evaluate-challenge (authenticator instantiator &optional challenge)
136 "Evaluate the challenge and prepare an appropriate next response.
137 The data type of the value and optional 3rd argument CHALLENGE is nil or
138 a cons cell of the form \(CONTINUATION STRING).
139 At the first time CONTINUATION should be set to nil.
141 Argument AUTHENTICATOR is the current evaluator.
142 Argument INSTANTIATOR is the instantiator instantiator."
143 (let* ((continuations
144 (sasl-authenticator-continuations authenticator))
147 (nth 1 (memq (car challenge) continuations))
148 (car continuations))))
150 (list function (funcall function instantiator challenge)))))
152 (defvar sasl-read-passphrase nil)
153 (defun sasl-read-passphrase (prompt)
154 (if (not sasl-read-passphrase)
155 (if (functionp 'read-passwd)
156 (setq sasl-read-passphrase 'read-passwd)
157 (if (load "passwd" t)
158 (setq sasl-read-passphrase 'read-passwd)
159 (autoload 'ange-ftp-read-passwd "ange-ftp")
160 (setq sasl-read-passphrase 'ange-ftp-read-passwd))))
161 (funcall sasl-read-passphrase prompt))
163 (defun sasl-unique-id ()
164 "Compute a data string which must be different each time.
165 It contain at least 64 bits of entropy."
166 (concat (funcall sasl-unique-id-function)(funcall sasl-unique-id-function)))
168 (defvar sasl-unique-id-char nil)
170 ;; stolen (and renamed) from message.el
171 (defun sasl-unique-id-function ()
172 ;; Don't use microseconds from (current-time), they may be unsupported.
173 ;; Instead we use this randomly inited counter.
174 (setq sasl-unique-id-char
175 (% (1+ (or sasl-unique-id-char (logand (random t) (1- (lsh 1 20)))))
176 ;; (current-time) returns 16-bit ints,
177 ;; and 2^16*25 just fits into 4 digits i base 36.
179 (let ((tm (current-time)))
181 (sasl-unique-id-number-base36
183 (lsh (% sasl-unique-id-char 25) 16)) 4)
184 (sasl-unique-id-number-base36
186 (lsh (/ sasl-unique-id-char 25) 16)) 4))))
188 (defun sasl-unique-id-number-base36 (num len)
193 (concat (sasl-unique-id-number-base36 (/ num 36) (1- len))
194 (char-to-string (aref "zyxwvutsrqponmlkjihgfedcba9876543210"
197 ;;; PLAIN (RFC2595 Section 6)
198 (defconst sasl-plain-continuations
199 '(sasl-plain-response))
201 (defun sasl-plain-response (instantiator challenge)
203 (sasl-read-passphrase
204 (format "PLAIN passphrase for %s: "
205 (sasl-instantiator-name instantiator))))
207 (sasl-instantiator-property
208 instantiator 'authentication-name))
209 (name (sasl-instantiator-name instantiator)))
211 (if (and authentication-name
212 (not (string= authentication-name name)))
213 (concat authentication-name "\0" name "\0" passphrase)
214 (concat "\0" name "\0" passphrase))
215 (fillarray passphrase 0))))
217 (put 'sasl-plain 'sasl-authenticator
218 (sasl-make-authenticator "PLAIN" sasl-plain-continuations))
220 (provide 'sasl-plain)
222 ;;; LOGIN (No specification exists)
223 (defconst sasl-login-continuations
224 '(ignore ;no initial response
225 sasl-login-response-1
226 sasl-login-response-2))
228 (defun sasl-login-response-1 (instantiator challenge)
229 (unless (string= (nth 1 challenge) "Username:")
230 (sasl-error (format "Unexpected response: %s" (nth 1 challenge))))
231 (sasl-instantiator-name instantiator))
233 (defun sasl-login-response-2 (instantiator challenge)
234 (unless (string= (nth 1 challenge) "Password:")
235 (sasl-error (format "Unexpected response: %s" (nth 1 challenge))))
236 (sasl-read-passphrase
237 (format "LOGIN passphrase for %s: " (sasl-instantiator-name instantiator))))
239 (put 'sasl-login 'sasl-authenticator
240 (sasl-make-authenticator "LOGIN" sasl-login-continuations))
242 (provide 'sasl-login)
244 ;;; ANONYMOUS (RFC2245)
245 (defconst sasl-anonymous-continuations
246 '(identity ;no initial response
247 sasl-anonymous-response))
249 (defun sasl-anonymous-response (instantiator challenge)
250 (or (sasl-instantiator-property
252 (sasl-instantiator-name instantiator)))
254 (put 'sasl-anonymous 'sasl-authenticator
255 (sasl-make-authenticator "ANONYMOUS" sasl-anonymous-continuations))
257 (provide 'sasl-anonymous)
261 ;;; sasl.el ends here