saslclient.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009 package require SASL
00010 package require base64
00011 catch {package require SASL::NTLM}
00012
00013 variable user
00014 array user = {username "" password ""}
00015 if {[info exists env(http_proxy_user)]} {
00016 user = (username) $env(http_proxy_user)
00017 } else {
00018 if {[info exists env(USERNAME)]} {
00019 user = (username) $env(USERNAME)
00020 }
00021 }
00022 if {[info exists env(http_proxy_pass)]} {
00023 user = (password) $env(http_proxy_pass)
00024 }
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041 ret SASLCallback (type clientblob , type chan , type context , type command , type args) {
00042 global env
00043 variable user
00044 upvar #0 $context ctx
00045 switch -exact -- $command {
00046 login {
00047 return "";# means use the authentication id
00048 }
00049 username {
00050 return $user(username)
00051 }
00052 password {
00053 return $user(password)
00054 }
00055 realm {
00056 if {$ctx(mech) eq "NTLM"} {
00057 return "$env(USERDOMAIN)"
00058 } else {
00059 return [lindex [fconfigure $chan -peername] 1]
00060 }
00061 }
00062 hostname {
00063 return [info host]
00064 }
00065 default {
00066 return -code error "oops: client needs to write $command"
00067 }
00068 }
00069 }
00070
00071
00072
00073
00074
00075
00076
00077 ret Callback (type chan , type eof , type line) {
00078 variable mechs
00079 variable tls
00080 variable ctx
00081 if {![info exists mechs]} {set mechs {}}
00082 if {$eof} { set ::forever 1; return }
00083 puts "> $line"
00084 switch -glob -- $line {
00085 "220 *" {
00086 if {$tls} {
00087 set tls 0
00088 puts "| switching to SSL"
00089 fileevent $chan readable {}
00090 tls::import $chan
00091 catch {tls::handshake $chan} msg
00092 set mechs {}
00093 fileevent $chan readable [list Read $chan ::Callback]
00094 }
00095 Write $chan "EHLO [info host]"
00096 }
00097 "250 *" {
00098 if {$tls} {
00099 Write $chan STARTTLS
00100 } else {
00101 set supported [SASL::mechanisms]
00102 puts "SASL mechanisms: $mechs\ncan do $supported"
00103 foreach mech $mechs {
00104 if {[lsearch -exact $supported $mech] != -1} {
00105
00106 set ctx [SASL::new \
00107 -mechanism $mech \
00108 -callback [list [namespace origin SASLCallback] "client blob" $chan]]
00109 Write $chan "AUTH $mech"
00110 return
00111 }
00112 }
00113 puts "! No matching SASL mechanism found"
00114 }
00115 }
00116 "250-AUTH*" {
00117 set line [string trim [string range $line 9 end]]
00118 set mechs [concat $mechs [split $line]]
00119 }
00120 "250-STARTTLS*" {
00121 if {![catch {package require tls}]} {
00122 set tls 1
00123 }
00124 }
00125 "235 *" {
00126 SASL::cleanup $ctx
00127 Write $chan "QUIT"
00128 }
00129 "334 *" {
00130 set challenge [string range $line 4 end]
00131 set e [string range $challenge end-5 end]
00132 puts "? '$e' [binary scan $e H* r; set r]"
00133 if {![catch {set dec [base64::decode $challenge]}]} {
00134 set challenge $dec
00135 }
00136
00137 set mech [set [subst $ctx](mech)]
00138 #puts "> $challenge"
00139 if {$mech eq "NTLM"} {puts ">CHA [SASL::NTLM::Debug $challenge]"}
00140 set code [catch {SASL::step $ctx $challenge} err]
00141 if {! $code} {
00142 set rsp [SASL::response $ctx]
00143 # puts "< $rsp"
00144 if {$mech eq "NTLM"} {puts "<RSP [SASL::NTLM::Debug $rsp]"}
00145 Write $chan [join [base64::encode $rsp] {}]
00146 } else {
00147 puts stderr "sasl error: $err"
00148 Write $chan "QUIT"
00149 }
00150 }
00151 "535*" {
00152 Write $chan QUIT
00153 }
00154 default {
00155 }
00156 }
00157 }
00158
00159 /* Write --*/
00160 /* */
00161 /* Write data to the socket channel with logging.*/
00162 /* */
00163 ret Write (type chan , type what) {
00164 puts "< $what"
00165 puts $chan $what
00166 return
00167 }
00168
00169 /* Read --*/
00170 /* */
00171 /* fileevent handler reads data when available from the network socket*/
00172 /* and calls the specified callback when it has recieved a complete line.*/
00173 /* */
00174 ret Read (type chan , type callback) {
00175 if {[eof $chan]} {
00176 fileevent $chan readable {}
00177 puts stderr "eof"
00178 eval $callback [list $chan 1 {}]
00179 return
00180 }
00181 if {[gets $chan line] != -1} {
00182 eval $callback [list $chan 0 $line]
00183 }
00184 return
00185 }
00186
00187 /* connect -- */
00188 /* */
00189 /* Open an SMTP session to test out the SASL implementation.*/
00190 /* */
00191 ret connect ( type server , type port , optional username ={) {passwd {}}} {
00192 variable mechs ; mechs = {}
00193 variable tls ; tls = 0
00194
00195 variable user
00196 if {$username ne {}} { user = (username) $username}
00197 if {$passwd ne {}} { user = (password) $passwd}
00198
00199 puts "Connect to $server:$port"
00200 sock = [socket $server $port]
00201 fconfigure $sock -buffering line -blocking 1 -translation {auto crlf}
00202 fileevent $sock readable [list Read $sock ::Callback]
00203 after 6000 {puts timeout ; ::forever = 1}
00204 vwait ::forever
00205 catch {close $sock}
00206 return
00207 }
00208
00209 if {!$tcl_interactive} {
00210 catch {eval ::connect $argv} res
00211 puts $res
00212 }
00213