sasl.tcl

Go to the documentation of this file.
00001 /*  sasl.tcl - Copyright (C) 2005 Pat Thoyts <patthoyts@users.sourceforge.net>*/
00002 /* */
00003 /*  This is an implementation of a general purpose SASL library for use in*/
00004 /*  Tcl scripts. */
00005 /* */
00006 /*  References:*/
00007 /*     Myers, J., "Simple Authentication and Security Layer (SASL)", */
00008 /*       RFC 2222, October 1997.*/
00009 /*     Rose, M.T., "TclSASL", "http://beepcore-tcl.sourceforge.net/tclsasl.html"*/
00010 /* */
00011 /*  -------------------------------------------------------------------------*/
00012 /*  See the file "license.terms" for information on usage and redistribution*/
00013 /*  of this file, and for a DISCLAIMER OF ALL WARRANTIES.*/
00014 /*  -------------------------------------------------------------------------*/
00015 
00016 package require Tcl 8.2
00017 
00018 namespace ::SASL {
00019     variable version 1.3.1
00020     variable rcsid {$Id: sasl.tcl,v 1.11 2006/10/02 21:21:57 patthoyts Exp $}
00021 
00022     variable uid
00023     if {![info exists uid]} {  uid =  0 }
00024 
00025     variable mechanisms
00026     if {![info exists mechanisms]} {
00027          mechanisms =  [list]
00028     }
00029 }
00030 
00031 /*  SASL::mechanisms --*/
00032 /* */
00033 /*  Return a list of available SASL mechanisms. By default only the*/
00034 /*  client implementations are given but if type is set to server then*/
00035 /*  the list of available server mechanisms is returned.*/
00036 /*  No mechanism with a preference value less than 'minimum' will be*/
00037 /*  returned.*/
00038 /*  The list is sorted by the security preference with the most secure*/
00039 /*  mechanisms given first.*/
00040 /* */
00041 ret  ::SASL::mechanisms (optional type =client , optional minimum =0) {
00042     variable mechanisms
00043     set r [list]
00044     foreach mech $mechanisms {
00045         if {[lindex $mech 0] < $minimum} { continue }
00046         switch -exact -- $type {
00047             client {
00048                 if {[string length [lindex $mech 2]] > 0} {
00049                     lappend r [lindex $mech 1]
00050                 }
00051             }
00052             server {
00053                 if {[string length [lindex $mech 3]] > 0} {
00054                     lappend r [lindex $mech 1]
00055                 }
00056             }
00057             default {
00058                 return -code error "invalid type \"$type\":\
00059                     must be either client or server"
00060             }
00061         }
00062     }
00063     return $r
00064 }
00065 
00066 /*  SASL::register --*/
00067 /* */
00068 /*  Register a new SASL mechanism with a security preference. Higher*/
00069 /*  preference values are chosen before lower valued mechanisms.*/
00070 /*  If no server implementation is available then an empty string */
00071 /*  should be provided for the serverproc parameter.*/
00072 /* */
00073 ret  ::SASL::register (type mechanism , type preference , type clientproc , optional serverproc ={)} {
00074     variable mechanisms
00075     set ndx [lsearch -regexp $mechanisms $mechanism]
00076     set mech [list $preference $mechanism $clientproc $serverproc]
00077     if {$ndx == -1} {
00078         lappend mechanisms $mech
00079     } else {
00080          mechanisms =  [lreplace $mechanisms $ndx $ndx $mech]
00081     }
00082      mechanisms =  [lsort -index 0 -decreasing -integer $mechanisms]
00083     return
00084 }
00085 
00086 /*  SASL::uid --*/
00087 /* */
00088 /*  Return a unique integer.*/
00089 /* */
00090 ret  ::SASL::uid () {
00091     variable uid
00092     return [incr uid]
00093 }
00094 
00095 /*  SASL::response --*/
00096 /* */
00097 /*  Get the reponse string from the SASL state.*/
00098 /* */
00099 ret  ::SASL::response (type context) {
00100     upvar #0 $context ctx
00101     return $ctx(response)
00102 }
00103 
00104 /*  SASL::reset --*/
00105 /* */
00106 /*  Reset the SASL state. This permits the same instance to be reused*/
00107 /*  for a new round of authentication.*/
00108 /* */
00109 ret  ::SASL::reset (type context) {
00110     upvar #0 $context ctx
00111     array set ctx [list step 0 response "" valid false count 0]
00112     return $context
00113 }
00114 
00115 /*  SASL::cleanup --*/
00116 /* */
00117 /*  Free any resources used with the SASL state.*/
00118 /* */
00119 ret  ::SASL::cleanup (type context) {
00120     if {[info exists $context]} {
00121         unset $context
00122     }
00123     return
00124 }
00125 
00126 /*  SASL::new --*/
00127 /* */
00128 /*  Create a new SASL instance. */
00129 /* */
00130 ret  ::SASL::new (type args) {
00131     set context [namespace current]::[uid]
00132     upvar #0 $context ctx
00133     array set ctx [list mech {} callback {} proc {} service smtp server {} \
00134                        step 0 response "" valid false type client count 0]
00135     eval [linsert $args 0 [namespace origin configure] $context]
00136     return $context
00137 }
00138 
00139 /*  SASL::configure --*/
00140 /* */
00141 /*  Configure the SASL state.*/
00142 /* */
00143 ret  ::SASL::configure (type context , type args) {
00144     variable mechanisms
00145     upvar #0 $context ctx
00146     while {[string match -* [set option [lindex $args 0]]]} {
00147         switch -exact -- $option {
00148             -service {
00149                 set ctx(service) [Pop args 1]
00150             }
00151             -server - -serverFQDN {
00152                 set ctx(server) [Pop args 1]
00153             }
00154             -mech - -mechanism {
00155                 set mech [string toupper [Pop args 1]]
00156                 set ctx(proc) {}
00157                 foreach m $mechanisms {
00158                     if {[string equal [lindex $m 1] $mech]} {
00159                         set ctx(mech) $mech
00160                         if {[string equal $ctx(type) "server"]} {
00161                             set ctx(proc) [lindex $m 3]
00162                         } else {
00163                             set ctx(proc) [lindex $m 2]
00164                         }
00165                         break
00166                     }
00167                 }
00168                 if {[string equal $ctx(proc) {}]} {
00169                     return -code error "mechanism \"$mech\" not available:\
00170                         must be one of those given by \[sasl::mechanisms\]"
00171                 }
00172             }
00173             -callback - -callbacks {
00174                 set ctx(callback) [Pop args 1]
00175             }
00176             -type {
00177                 set type [Pop args 1]
00178                 if {[lsearch -exact {server client} $type] != -1} {
00179                     set ctx(type) $type
00180                     if {![string equal $ctx(mech) ""]} {
00181                         configure $context -mechanism $ctx(mech)
00182                     }
00183                 } else {
00184                     return -code error "bad value \"$type\":\
00185                         must be either client or server"
00186                 }
00187             }
00188             default {
00189                 return -code error "bad option \"$option\":\
00190                     must be one of -mechanism, -service, -server -type\
00191                     or -callbacks"
00192             }
00193         }
00194         Pop args
00195     }
00196         
00197 }
00198 
00199 ret  ::SASL::step (type context , type challenge , type args) {
00200     upvar #0 $context ctx
00201     incr ctx(count)
00202     return [eval [linsert $args 0 $ctx(proc) $context $challenge]]
00203 }
00204 
00205 
00206 ret  ::SASL::Pop (type varname , optional nth =0) {
00207     upvar $varname args
00208     set r [lindex $args $nth]
00209     set args [lreplace $args $nth $nth]
00210     return $r
00211 }
00212 
00213 ret  ::SASL::md5_init () {
00214     variable md5_inited
00215     if {[info exists md5_inited]} {return} else {set md5_inited 1}
00216     # Deal with either version of md5. We'd like version 2 but someone
00217     # may have already loaded version 1.
00218     set md5major [lindex [split [package require md5] .] 0]
00219     if {$md5major < 2} {
00220         # md5 v1, no options, and returns a hex string ready for us.
00221         proc ::SASL::md5_hex {data} { return [::md5::md5 $data] }
00222         proc ::SASL::md5_bin {data} { return [binary format H* [::md5::md5 $data]] }
00223         proc ::SASL::hmac_hex {pass data} { return [::md5::hmac $pass $data] }
00224         proc ::SASL::hmac_bin {pass data} { return [binary format H* [::md5::hmac $pass $data]] }
00225     } else {
00226         # md5 v2 requires -hex to return hash as hex-encoded non-binary string.
00227         proc ::SASL::md5_hex {data} { return [string tolower [::md5::md5 -hex $data]] }
00228         proc ::SASL::md5_bin {data} { return [::md5::md5 $data] }
00229         proc ::SASL::hmac_hex {pass data} { return [::md5::hmac -hex -key $pass $data] }
00230         proc ::SASL::hmac_bin {pass data} { return [::md5::hmac -key $pass $data] }
00231     }
00232 }
00233 
00234 /*  -------------------------------------------------------------------------*/
00235 
00236 /*  CRAM-MD5 SASL MECHANISM*/
00237 /* */
00238 /*      Implementation of the Challenge-Response Authentication Mechanism*/
00239 /*  (RFC2195).*/
00240 /* */
00241 /*  Comments:*/
00242 /*  This mechanism passes a server generated string containing*/
00243 /*  a timestamp and has the client generate an MD5 HMAC using the*/
00244 /*  shared secret as the key and the server string as the data.*/
00245 /*  The downside of this protocol is that the server must have access*/
00246 /*  to the plaintext password.*/
00247 /* */
00248 ret  ::SASL::CRAM-MD5:client (type context , type challenge , type args) {
00249     upvar #0 $context ctx
00250     md5_init
00251     if {$ctx(step) != 0} {
00252         return -code error "unexpected state: CRAM-MD5 has only 1 step"
00253     }
00254     if {[string length $challenge] == 0} {
00255         set ctx(response) ""
00256         return 1
00257     }
00258     set password [eval $ctx(callback) [list $context password]]
00259     set username [eval $ctx(callback) [list $context username]]
00260     set reply [hmac_hex $password $challenge]
00261     set reply "$username [string tolower $reply]"
00262     set ctx(response) $reply
00263     incr ctx(step)
00264     return 0
00265 }
00266 
00267 ret  ::SASL::CRAM-MD5:server (type context , type clientrsp , type args) {
00268     upvar #0 $context ctx
00269     md5_init
00270     incr ctx(step)
00271     switch -exact -- $ctx(step) {
00272         1 {
00273             set ctx(realm) [eval $ctx(callback) [list $context realm]]
00274             set ctx(response) "<[pid].[clock seconds]@$ctx(realm)>"
00275             return 1
00276         }
00277         2 {
00278             foreach {user hash} $clientrsp break
00279             set hash [string tolower $hash]
00280             set pass [eval $ctx(callback) [list $context password $user $ctx(realm)]]
00281             set check [hmac_bin $pass $ctx(response)]
00282             binary scan $check H* cx
00283             if {[string equal $cx $hash]} {
00284                 return 0
00285             } else {
00286                 return -code error "authentication failed"
00287             }
00288         }
00289         default {
00290             return -code error "invalid state"
00291         }
00292     }
00293 }
00294 
00295 ::SASL::register CRAM-MD5 30 ::SASL::CRAM-MD5:client ::SASL::CRAM-MD5:server
00296 
00297 /*  -------------------------------------------------------------------------*/
00298 /*  PLAIN SASL MECHANISM*/
00299 /* */
00300 /*      Implementation of the single step login SASL mechanism (RFC2595).*/
00301 /* */
00302 /*  Comments:*/
00303 /*  A single step mechanism in which the authorization ID, the*/
00304 /*  authentication ID and password are all transmitted in plain*/
00305 /*  text. This should not be used unless the channel is secured by*/
00306 /*  some other means (such as SSL/TLS).*/
00307 /* */
00308 ret  ::SASL::PLAIN:client (type context , type challenge , type args) {
00309     upvar #0 $context ctx
00310     incr ctx(step)
00311     set authzid  [eval $ctx(callback) [list $context login]]
00312     set username [eval $ctx(callback) [list $context username]]
00313     set password [eval $ctx(callback) [list $context password]]
00314     set ctx(response) "$authzid\x00$username\x00$password"
00315     return 0
00316 }
00317 
00318 ret  ::SASL::PLAIN:server (type context , type clientrsp , type args) {
00319     upvar \#0 $context ctx
00320     if {[string length $clientrsp] < 1} {
00321         set ctx(response) ""
00322         return 1
00323     } else {
00324         foreach {authzid authid pass} [split $clientrsp \0] break
00325         set realm [eval $ctx(callback) [list $context realm]]
00326         set check [eval $ctx(callback) [list $context password $authid $realm]]
00327         if {[string equal $pass $check]} {
00328             return 0
00329         } else {
00330             return -code error "authentication failed"
00331         }
00332     }
00333 }
00334 
00335 ::SASL::register PLAIN 10 ::SASL::PLAIN:client ::SASL::PLAIN:server
00336 
00337 /*  -------------------------------------------------------------------------*/
00338 /*  LOGIN SASL MECHANISM*/
00339 /* */
00340 /*      Implementation of the two step login SASL mechanism.*/
00341 /* */
00342 /*  Comments:*/
00343 /*  This is an unofficial but widely deployed SASL mechanism somewhat*/
00344 /*  akin to the PLAIN mechanism. Both the authentication ID and password*/
00345 /*  are transmitted in plain text in response to server prompts.*/
00346 /* */
00347 /*  NOT RECOMMENDED for use in new protocol implementations.*/
00348 /* */
00349 ret  ::SASL::LOGIN:client (type context , type challenge , type args) {
00350     upvar #0 $context ctx
00351     if {$ctx(step) == 0 && [string length $challenge] == 0} {
00352         set ctx(response) ""
00353         return 1
00354     }
00355     incr ctx(step)
00356     switch -exact -- $ctx(step) {
00357         1 {
00358             set ctx(response) [eval $ctx(callback) [list $context username]]
00359             set r 1
00360         }
00361         2 {
00362             set ctx(response) [eval $ctx(callback) [list $context password]]
00363             set r 0
00364         }
00365         default {
00366             return -code error "unexpected state \"$ctx(step)\":\
00367                 LOGIN has only 2 steps"
00368         }
00369     }
00370     return $r
00371 }
00372 
00373 ret  ::SASL::LOGIN:server (type context , type clientrsp , type args) {
00374     upvar #0 $context ctx
00375     incr ctx(step)
00376     switch -exact -- $ctx(step) {
00377         1 {
00378             set ctx(response) "Username:"
00379             return 1
00380         }
00381         2 {
00382             set ctx(username) $clientrsp
00383             set ctx(response) "Password:"
00384             return 1
00385         }
00386         3 {
00387             set user $ctx(username)
00388             set realm [eval $ctx(callback) [list $context realm]]
00389             set pass [eval $ctx(callback) [list $context password $user $realm]]
00390             if {[string equal $clientrsp $pass]} {
00391                 return 0
00392             } else {
00393                 return -code error "authentication failed"
00394             }
00395         }
00396         default {
00397             return -code error "invalid state"
00398         }
00399     }
00400 }
00401 
00402 ::SASL::register LOGIN 20 ::SASL::LOGIN:client ::SASL::LOGIN:server
00403 
00404 /*  -------------------------------------------------------------------------*/
00405 /*  ANONYMOUS SASL MECHANISM*/
00406 /* */
00407 /*      Implementation of the ANONYMOUS SASL mechanism (RFC2245).*/
00408 /* */
00409 /*  Comments:*/
00410 /* */
00411 /*  */
00412 ret  ::SASL::ANONYMOUS:client (type context , type challenge , type args) {
00413     upvar #0 $context ctx
00414     set user  [eval $ctx(callback) [list $context username]]
00415     set realm [eval $ctx(callback) [list $context realm]]
00416     set ctx(response) $user@$realm
00417     return 0
00418 }
00419 
00420 ret  ::SASL::ANONYMOUS:server (type context , type clientrsp , type args) {
00421     upvar #0 $context ctx
00422     set ctx(response) ""
00423     if {[string length $clientrsp] < 1} {
00424         if {$ctx(count) > 2} {
00425             return -code error "authentication failed"
00426         }
00427         return 1
00428     } else {
00429         set ctx(trace) $clientrsp
00430         return 0
00431     }
00432 }
00433 
00434 ::SASL::register ANONYMOUS 5 ::SASL::ANONYMOUS:client ::SASL::ANONYMOUS:server
00435 
00436 /*  -------------------------------------------------------------------------*/
00437 
00438 /*  DIGEST-MD5 SASL MECHANISM*/
00439 /* */
00440 /*      Implementation of the DIGEST-MD5 SASL mechanism (RFC2831).*/
00441 /* */
00442 /*  Comments:*/
00443 /* */
00444 ret  ::SASL::DIGEST-MD5:client (type context , type challenge , type args) {
00445     variable digest_md5_noncecount
00446     upvar #0 $context ctx
00447     md5_init
00448     if {$ctx(step) == 0 && [string length $challenge] == 0} {
00449         set ctx(response) ""
00450         return 1
00451     }
00452     incr ctx(step)
00453     set result 0
00454     switch -exact -- $ctx(step) {
00455         1 {
00456             array set params [DigestParameters $challenge]
00457             
00458             if {![info exists digest_md5_noncecount]} {
00459                 set digest_md5_noncecount 0
00460             }
00461             set nonce $params(nonce)
00462             set cnonce [CreateNonce]
00463             set noncecount [format %08u [incr digest_md5_noncecount]]
00464             set qop auth
00465             
00466             set username [eval $ctx(callback) [list $context username]]
00467             set password [eval $ctx(callback) [list $context password]]
00468             if {[info exists params(realm)]} {
00469                 set realm $params(realm)
00470             } else {
00471                 set realm [eval $ctx(callback) [list $context realm]]
00472             }
00473             
00474             set uri "$ctx(service)/$realm"
00475             set R [DigestResponse $username $realm $password $uri \
00476                        $qop $nonce $noncecount $cnonce]
00477             
00478             set ctx(response) "username=\"$username\",realm=\"$realm\",nonce=\"$nonce\",nc=\"$noncecount\",cnonce=\"$cnonce\",digest-uri=\"$uri\",response=\"$R\",qop=$qop"
00479             set result 1
00480         }
00481         
00482         2 {
00483             set ctx(response) ""
00484             set result 0
00485         }
00486         default {
00487             return -code error "invalid state"
00488         }
00489     }
00490     return $result
00491 }
00492 
00493 ret  ::SASL::DIGEST-MD5:server (type context , type challenge , type args) {
00494     variable digest_md5_noncecount
00495     upvar #0 $context ctx
00496     md5_init
00497     incr ctx(step)
00498     set result 0
00499     switch -exact -- $ctx(step) {
00500         1 {
00501             set realm [eval $ctx(callback) [list $context realm]]
00502             set ctx(nonce) [CreateNonce]
00503             set ctx(response) "realm=\"$realm\",nonce=\"$ctx(nonce)\",qop=\"auth\",charset=utf-8,algorithm=md5-sess"
00504             set result 1
00505         }
00506         2 {
00507             array set params [DigestParameters $challenge]
00508             set realm [eval $ctx(callback) [list $context realm]]
00509             set password [eval $ctx(callback)\
00510                               [list $context password $params(username) $realm]]
00511             set uri "$ctx(service)/$realm"
00512             set R [DigestResponse $params(username) $realm $password \
00513                        $uri auth $ctx(nonce) $params(nc) $params(cnonce)]
00514             if {[string equal $R $params(response)]} {
00515                 set R2 [DigestResponse $params(username) $realm $password \
00516                         $uri auth $ctx(nonce) $params(nc) $params(cnonce)]
00517                 set ctx(response) "rspauth=$R2"
00518                 set ctx(nc) $params(nc)
00519                 set result 1
00520             } else {
00521                 return -code error "authentication failed"
00522             }
00523         }
00524         3 {
00525             set ctx(response) ""
00526             set result 0
00527         }
00528         default {
00529             return -code error "invalid state"
00530         }
00531     }
00532     return $result
00533 }
00534 
00535 /*  RFC 2831 2.1*/
00536 /*  Char categories as per spec...*/
00537 /*  Build up a regexp for splitting the challenge into key value pairs.*/
00538 ret  ::SASL::DigestParameters (type challenge) {
00539     set sep "\\\]\\\[\\\\()<>@,;:\\\"\\\?=\\\{\\\} \t"
00540     set tok {0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\-\|\~\!\#\$\%\&\*\+\.\^\_\`}
00541     set sqot {(?:\'(?:\\.|[^\'\\])*\')}
00542     set dqot {(?:\"(?:\\.|[^\"\\])*\")}
00543     set parameters {}
00544     regsub -all "(\[${tok}\]+)=(${dqot}|(?:\[${tok}\]+))(?:\[${sep}\]+|$)" $challenge {\1 \2 } parameters
00545     return $parameters
00546 }
00547 
00548 /*  RFC 2831 2.1.2.1*/
00549 /* */
00550 ret  ::SASL::DigestResponse (type user , type realm , type pass , type uri , type qop , type nonce , type noncecount , type cnonce) {
00551     set A1 [md5_bin "$user:$realm:$pass"]
00552     set A2 "AUTHENTICATE:$uri"
00553     if {![string equal $qop "auth"]} {
00554         append A2 :[string repeat 0 32]
00555     }
00556     set A1h [md5_hex "${A1}:$nonce:$cnonce"]
00557     set A2h [md5_hex $A2]
00558     set R   [md5_hex $A1h:$nonce:$noncecount:$cnonce:$qop:$A2h]
00559     return $R
00560 }
00561 
00562 /*  RFC 2831 2.1.2.2*/
00563 /* */
00564 ret  ::SASL::DigestResponse2 (type user , type realm , type pass , type uri , type qop , type nonce , type noncecount , type cnonce) {
00565     set A1 [md5_bin "$user:$realm:$pass"]
00566     set A2 ":$uri"
00567     if {![string equal $qop "auth"]} {
00568         append A2 :[string repeat 0 32]
00569     }
00570     set A1h [md5_hex "${A1}:$nonce:$cnonce"]
00571     set A2h [md5_hex $A2]
00572     set R   [md5_hex $A1h:$nonce:$noncecount:$cnonce:$qop:$A2h]
00573     return $R
00574 }
00575 
00576 /*  Get 16 random bytes for a nonce value. If we can use /dev/random, do so*/
00577 /*  otherwise we hash some values.*/
00578 /* */
00579 ret  ::SASL::CreateNonce () {
00580     set bytes {}
00581     if {[file readable /dev/urandom]} {
00582         catch {
00583             set f [open /dev/urandom r]
00584             fconfigure $f -translation binary -buffering none
00585             set bytes [read $f 16]
00586             close $f
00587         }
00588     }
00589     if {[string length $bytes] < 1} {
00590         set bytes [md5_bin [clock seconds]:[pid]:[expr {rand()}]]
00591     }
00592     return [binary scan $bytes h* r; set r]
00593 }
00594 
00595 ::SASL::register DIGEST-MD5 40 \
00596     ::SASL::DIGEST-MD5:client ::SASL::DIGEST-MD5:server
00597 
00598 /*  -------------------------------------------------------------------------*/
00599 
00600 /*  OTP SASL MECHANISM*/
00601 /* */
00602 /*      Implementation of the OTP SASL mechanism (RFC2444).*/
00603 /* */
00604 /*  Comments:*/
00605 /* */
00606 /*  RFC 2289: A One-Time Password System*/
00607 /*  RFC 2444: OTP SASL Mechanism*/
00608 /*  RFC 2243: OTP Extended Responses*/
00609 /*  Client initializes with authid\0authzid*/
00610 /*  Server responds with extended OTP responses */
00611 /*      eg: otp-md5 498 bi32123 ext*/
00612 /*  Client responds with otp result as:*/
00613 /*   hex:xxxxxxxxxxxxxxxx*/
00614 /*      or*/
00615 /*   word:WWWW WWW WWWW WWWW WWWW*/
00616 /* */
00617 /*  To support changing the otp sequence the extended commands have:*/
00618 /*    init-hex:<current>:<new params>:<new>*/
00619 /*  eg: init-hex:xxxxxxxxxxxx:md5 499 seed987:xxxxxxxxxxxxxx*/
00620 /*  or init-word*/
00621 
00622 ret  ::SASL::OTP:client (type context , type challenge , type args) {
00623     upvar #0 $context ctx
00624     package require otp
00625     incr ctx(step)
00626     switch -exact -- $ctx(step) {
00627         1 {
00628             set authzid  [eval $ctx(callback) [list $context login]]
00629             set username [eval $ctx(callback) [list $context username]]
00630             set ctx(response) "$authzid\x00$username"
00631             set cont 1
00632         }
00633         2 {
00634             foreach {type count seed ext} $challenge break
00635             set type [lindex [split $type -] 1]
00636             if {[lsearch -exact {md4 md5 sha1 rmd160} $type] == -1} {
00637                 return -code error "unsupported digest algorithm \"$type\":\
00638                     must be one of md4, md5, sha1 or rmd160"
00639             }
00640             set challenge [lrange $challenge 3 end]
00641             set password [eval $ctx(callback) [list $context password]]
00642             set otp [::otp::otp-$type -word -seed $seed \
00643                          -count $count $password]
00644             if {[string match "ext*" $ext]} {
00645                 set otp word:$otp
00646             }
00647             set ctx(response) $otp
00648             set cont 0
00649         }
00650         default {
00651             return -code error "unexpected state \"$ctx(step)\":\
00652                the SASL OTP mechanism only has 2 steps"
00653         }
00654     }
00655     return $cont
00656 }
00657 
00658 ::SASL::register OTP 45 ::SASL::OTP:client
00659 
00660 /*  -------------------------------------------------------------------------*/
00661 
00662 package provide SASL $::SASL::version
00663 
00664 /*  -------------------------------------------------------------------------*/
00665 /* */
00666 /*  Local variables:*/
00667 /*    indent-tabs-mode: nil*/
00668 /*  End:*/
00669 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1