PageRenderTime 50ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/tcllib-1.11.1/examples/sasl/saslclient.tcl

#
TCL | 212 lines | 157 code | 13 blank | 42 comment | 27 complexity | 3b25eef570259511d33d305064bbf136 MD5 | raw file
Possible License(s): AGPL-3.0
  1. # saslclient.tcl - Copyright (C) 2005 Pat Thoyts <patthoyts@users.sf.net>
  2. #
  3. # This is a SMTP SASL test client. It connects to a SMTP server and uses
  4. # the STARTTLS feature if available to switch to a secure link before
  5. # negotiating authentication using SASL.
  6. #
  7. # $Id: saslclient.tcl,v 1.4 2005/10/06 14:48:02 patthoyts Exp $
  8. package require SASL
  9. package require base64
  10. catch {package require SASL::NTLM}
  11. variable user
  12. array set user {username "" password ""}
  13. if {[info exists env(http_proxy_user)]} {
  14. set user(username) $env(http_proxy_user)
  15. } else {
  16. if {[info exists env(USERNAME)]} {
  17. set user(username) $env(USERNAME)
  18. }
  19. }
  20. if {[info exists env(http_proxy_pass)]} {
  21. set user(password) $env(http_proxy_pass)
  22. }
  23. # SASLCallback --
  24. #
  25. # This procedure is called from the SASL library when it needs to get
  26. # information from the client application. The callback can be specified
  27. # with additional data elements and when called the SASL library will
  28. # append the SASL context, the command and possibly additional arguments.
  29. # The command specified the type of information needed.
  30. # So far we have:
  31. # login users authorization identity (can be same as username).
  32. # username users authentication identity
  33. # password users authentication token
  34. # realm the authentication realm (domain for NTLM)
  35. # hostname the client's idea of its hostname (for NTLM)
  36. #
  37. proc SASLCallback {clientblob chan context command args} {
  38. global env
  39. variable user
  40. upvar #0 $context ctx
  41. switch -exact -- $command {
  42. login {
  43. return "";# means use the authentication id
  44. }
  45. username {
  46. return $user(username)
  47. }
  48. password {
  49. return $user(password)
  50. }
  51. realm {
  52. if {$ctx(mech) eq "NTLM"} {
  53. return "$env(USERDOMAIN)"
  54. } else {
  55. return [lindex [fconfigure $chan -peername] 1]
  56. }
  57. }
  58. hostname {
  59. return [info host]
  60. }
  61. default {
  62. return -code error "oops: client needs to write $command"
  63. }
  64. }
  65. }
  66. # SMTPClient --
  67. #
  68. # This implements a minimal SMTP client state engine. It will
  69. # do enough of the SMTP protocol to initiate a SSL/TLS link and
  70. # negotiate SASL parameters. Then it terminates.
  71. #
  72. proc Callback {chan eof line} {
  73. variable mechs
  74. variable tls
  75. variable ctx
  76. if {![info exists mechs]} {set mechs {}}
  77. if {$eof} { set ::forever 1; return }
  78. puts "> $line"
  79. switch -glob -- $line {
  80. "220 *" {
  81. if {$tls} {
  82. set tls 0
  83. puts "| switching to SSL"
  84. fileevent $chan readable {}
  85. tls::import $chan
  86. catch {tls::handshake $chan} msg
  87. set mechs {}
  88. fileevent $chan readable [list Read $chan ::Callback]
  89. }
  90. Write $chan "EHLO [info host]"
  91. }
  92. "250 *" {
  93. if {$tls} {
  94. Write $chan STARTTLS
  95. } else {
  96. set supported [SASL::mechanisms]
  97. puts "SASL mechanisms: $mechs\ncan do $supported"
  98. foreach mech $mechs {
  99. if {[lsearch -exact $supported $mech] != -1} {
  100. set ctx [SASL::new \
  101. -mechanism $mech \
  102. -callback [list [namespace origin SASLCallback] "client blob" $chan]]
  103. Write $chan "AUTH $mech"
  104. return
  105. }
  106. }
  107. puts "! No matching SASL mechanism found"
  108. }
  109. }
  110. "250-AUTH*" {
  111. set line [string trim [string range $line 9 end]]
  112. set mechs [concat $mechs [split $line]]
  113. }
  114. "250-STARTTLS*" {
  115. if {![catch {package require tls}]} {
  116. set tls 1
  117. }
  118. }
  119. "235 *" {
  120. SASL::cleanup $ctx
  121. Write $chan "QUIT"
  122. }
  123. "334 *" {
  124. set challenge [string range $line 4 end]
  125. set e [string range $challenge end-5 end]
  126. puts "? '$e' [binary scan $e H* r; set r]"
  127. if {![catch {set dec [base64::decode $challenge]}]} {
  128. set challenge $dec
  129. }
  130. set mech [set [subst $ctx](mech)]
  131. #puts "> $challenge"
  132. if {$mech eq "NTLM"} {puts ">CHA [SASL::NTLM::Debug $challenge]"}
  133. set code [catch {SASL::step $ctx $challenge} err]
  134. if {! $code} {
  135. set rsp [SASL::response $ctx]
  136. # puts "< $rsp"
  137. if {$mech eq "NTLM"} {puts "<RSP [SASL::NTLM::Debug $rsp]"}
  138. Write $chan [join [base64::encode $rsp] {}]
  139. } else {
  140. puts stderr "sasl error: $err"
  141. Write $chan "QUIT"
  142. }
  143. }
  144. "535*" {
  145. Write $chan QUIT
  146. }
  147. default {
  148. }
  149. }
  150. }
  151. # Write --
  152. #
  153. # Write data to the socket channel with logging.
  154. #
  155. proc Write {chan what} {
  156. puts "< $what"
  157. puts $chan $what
  158. return
  159. }
  160. # Read --
  161. #
  162. # fileevent handler reads data when available from the network socket
  163. # and calls the specified callback when it has recieved a complete line.
  164. #
  165. proc Read {chan callback} {
  166. if {[eof $chan]} {
  167. fileevent $chan readable {}
  168. puts stderr "eof"
  169. eval $callback [list $chan 1 {}]
  170. return
  171. }
  172. if {[gets $chan line] != -1} {
  173. eval $callback [list $chan 0 $line]
  174. }
  175. return
  176. }
  177. # connect --
  178. #
  179. # Open an SMTP session to test out the SASL implementation.
  180. #
  181. proc connect { server port {username {}} {passwd {}}} {
  182. variable mechs ; set mechs {}
  183. variable tls ; set tls 0
  184. variable user
  185. if {$username ne {}} {set user(username) $username}
  186. if {$passwd ne {}} {set user(password) $passwd}
  187. puts "Connect to $server:$port"
  188. set sock [socket $server $port]
  189. fconfigure $sock -buffering line -blocking 1 -translation {auto crlf}
  190. fileevent $sock readable [list Read $sock ::Callback]
  191. after 6000 {puts timeout ; set ::forever 1}
  192. vwait ::forever
  193. catch {close $sock}
  194. return
  195. }
  196. if {!$tcl_interactive} {
  197. catch {eval ::connect $argv} res
  198. puts $res
  199. }