00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045 package require Tcl 8.4
00046 package require asn 0.7
00047 package provide ldap 1.6.8
00048
00049 namespace ldap {
00050
00051 namespace export connect secure_connect \
00052 disconnect \
00053 bind unbind \
00054 bindSASL \
00055 search \
00056 searchInit \
00057 searchNext \
00058 searchEnd \
00059 modify \
00060 modifyMulti \
00061 add \
00062 addMulti \
00063 delete \
00064 modifyDN \
00065 info
00066
00067 namespace import ::asn::*
00068
00069 variable SSLCertifiedAuthoritiesFile
00070 variable doDebug
00071
00072 doDebug = 0
00073
00074
00075 variable resultCode2String
00076 array resultCode2String = {
00077 0 success
00078 1 operationsError
00079 2 protocolError
00080 3 timeLimitExceeded
00081 4 sizeLimitExceeded
00082 5 compareFalse
00083 6 compareTrue
00084 7 authMethodNotSupported
00085 8 strongAuthRequired
00086 10 referral
00087 11 adminLimitExceeded
00088 12 unavailableCriticalExtension
00089 13 confidentialityRequired
00090 14 saslBindInProgress
00091 16 noSuchAttribute
00092 17 undefinedAttributeType
00093 18 inappropriateMatching
00094 19 constraintViolation
00095 20 attributeOrValueExists
00096 21 invalidAttributeSyntax
00097 32 noSuchObject
00098 33 aliasProblem
00099 34 invalidDNSyntax
00100 35 isLeaf
00101 36 aliasDereferencingProblem
00102 48 inappropriateAuthentication
00103 49 invalidCredentials
00104 50 insufficientAccessRights
00105 51 busy
00106 52 unavailable
00107 53 unwillingToPerform
00108 54 loopDetect
00109 64 namingViolation
00110 65 objectClassViolation
00111 66 notAllowedOnNonLeaf
00112 67 notAllowedOnRDN
00113 68 entryAlreadyExists
00114 69 objectClassModsProhibited
00115 80 other
00116 }
00117
00118 }
00119
00120
00121
00122
00123
00124
00125 ret ::ldap::resultCode2String (type code) {
00126 variable resultCode2String
00127 if {[::info exists resultCode2String($code)]} {
00128 return $resultCode2String($code)
00129 } else {
00130 return "unknownError"
00131 }
00132 }
00133
00134
00135
00136
00137
00138 ret ::ldap::CheckHandle (type handle) {
00139 if {![array exists $handle]} {
00140 return -code error \
00141 [format "Not a valid LDAP connection handle: %s" $handle]
00142 }
00143 }
00144
00145
00146
00147
00148
00149
00150 ret ldap::info (type args) {
00151 set cmd [lindex $args 0]
00152 set cmds {connections bound bounduser control extensions features ip saslmechanisms tls whoami}
00153 if {[llength $args] == 0} {
00154 return -code error \
00155 "Usage: \"info subcommand ?handle?\""
00156 }
00157 if {[lsearch -exact $cmds $cmd] == -1} {
00158 return -code error \
00159 "Invalid subcommand \"$cmd\", valid commands are\
00160 [join [lrange $cmds 0 end-1] ,] and [lindex $cmds end]"
00161 }
00162 eval [linsert [lrange $args 1 end] 0 ldap::info_$cmd]
00163 }
00164
00165
00166
00167
00168
00169 ret ldap::info_ip (type args) {
00170 if {[llength $args] != 1} {
00171 return -code error \
00172 "Wrong # of arguments. Usage: ldap::info ip handle"
00173 }
00174 CheckHandle [lindex $args 0]
00175 upvar #0 [lindex $args 0] conn
00176 if {![::info exists conn(sock)]} {
00177 return -code error \
00178 "\"[lindex $args 0]\" is not a ldap connection handle"
00179 }
00180 return [lindex [fconfigure $conn(sock) -peername] 0]
00181 }
00182
00183
00184
00185
00186
00187 ret ldap::info_connections (type args) {
00188 if {[llength $args] != 0} {
00189 return -code error \
00190 "Wrong # of arguments. Usage: ldap::info connections"
00191 }
00192 return [::info vars ::ldap::ldap*]
00193 }
00194
00195
00196
00197
00198
00199 ret ldap::info_bound (type args) {
00200 if {[llength $args] != 1} {
00201 return -code error \
00202 "Wrong # of arguments. Usage: ldap::info bound handle"
00203 }
00204 CheckHandle [lindex $args 0]
00205 upvar #0 [lindex $args 0] conn
00206 if {![::info exists conn(bound)]} {
00207 return -code error \
00208 "\"[lindex $args 0]\" is not a ldap connection handle"
00209 }
00210
00211 return $conn(bound)
00212 }
00213
00214
00215
00216
00217
00218 ret ldap::info_bounduser (type args) {
00219 if {[llength $args] != 1} {
00220 return -code error \
00221 "Wrong # of arguments. Usage: ldap::info bounduser handle"
00222 }
00223 CheckHandle [lindex $args 0]
00224 upvar #0 [lindex $args 0] conn
00225 if {![::info exists conn(bound)]} {
00226 return -code error \
00227 "\"[lindex $args 0]\" is not a ldap connection handle"
00228 }
00229
00230 return $conn(bounduser)
00231 }
00232
00233
00234
00235
00236
00237
00238 ret ldap::info_tls (type args) {
00239 if {[llength $args] != 1} {
00240 return -code error \
00241 "Wrong # of arguments. Usage: ldap::info tls handle"
00242 }
00243 CheckHandle [lindex $args 0]
00244 upvar #0 [lindex $args 0] conn
00245 if {![::info exists conn(tls)]} {
00246 return -code error \
00247 "\"[lindex $args 0]\" is not a ldap connection handle"
00248 }
00249 return $conn(tls)
00250 }
00251
00252 ret ldap::info_saslmechanisms (type args) {
00253 if {[llength $args] != 1} {
00254 return -code error \
00255 "Wrong # of arguments. Usage: ldap::info saslmechanisms handle"
00256 }
00257 return [Saslmechanisms [lindex $args 0]]
00258 }
00259
00260 ret ldap::info_extensions (type args) {
00261 if {[llength $args] != 1} {
00262 return -code error \
00263 "Wrong # of arguments. Usage: ldap::info extensions handle"
00264 }
00265 return [Extensions [lindex $args 0]]
00266 }
00267
00268 ret ldap::info_control (type args) {
00269 if {[llength $args] != 1} {
00270 return -code error \
00271 "Wrong # of arguments. Usage: ldap::info control handle"
00272 }
00273 return [Control [lindex $args 0]]
00274 }
00275
00276 ret ldap::info_features (type args) {
00277 if {[llength $args] != 1} {
00278 return -code error \
00279 "Wrong # of arguments. Usage: ldap::info features handle"
00280 }
00281 return [Features [lindex $args 0]]
00282 }
00283
00284 ret ldap::info_whoami (type args) {
00285 if {[llength $args] != 1} {
00286 return -code error \
00287 "Wrong # of arguments. Usage: ldap::info whoami handle"
00288 }
00289 return [Whoami [lindex $args 0]]
00290 }
00291
00292
00293
00294
00295
00296
00297 ret ldap::Saslmechanisms (type conn) {
00298 CheckHandle $conn
00299 lindex [ldap::search $conn {} {(objectClass=*)} \
00300 {supportedSASLMechanisms} -scope base] 0 1 1
00301 }
00302
00303 ret ldap::Extensions (type conn) {
00304 CheckHandle $conn
00305 lindex [ldap::search $conn {} {(objectClass=*)} \
00306 {supportedExtension} -scope base] 0 1 1
00307 }
00308
00309 ret ldap::Control (type conn) {
00310 CheckHandle $conn
00311 lindex [ldap::search $conn {} {(objectClass=*)} \
00312 {supportedControl} -scope base] 0 1 1
00313 }
00314
00315 ret ldap::Features (type conn) {
00316 CheckHandle $conn
00317 lindex [ldap::search $conn {} {(objectClass=*)} \
00318 {supportedFeatures} -scope base] 0 1 1
00319 }
00320
00321
00322
00323
00324
00325 ret ldap::Whoami (type handle) {
00326 CheckHandle $handle
00327 if {[lsearch [ldap::Extensions $handle] 1.3.6.1.4.1.4203.1.11.3] == -1} {
00328 return -code error \
00329 "Server does not support the \"Who am I?\" extension"
00330 }
00331
00332 set request [asnApplicationConstr 23 [asnOctetString 1.3.6.1.4.1.4203.1.11.3]]
00333 set mid [SendMessage $handle $request]
00334 set response [WaitForResponse $handle $mid]
00335
00336 asnGetApplication response appNum
00337 if {$appNum != 24} {
00338 return -code error \
00339 "unexpected application number ($appNum != 24)"
00340 }
00341
00342 asnGetEnumeration response resultCode
00343 asnGetOctetString response matchedDN
00344 asnGetOctetString response errorMessage
00345 if {$resultCode != 0} {
00346 return -code error \
00347 -errorcode [list LDAP [resultCode2String $resultCode] $matchedDN $errorMessage] \
00348 "LDAP error [resultCode2String $resultCode] '$matchedDN': $errorMessage"
00349 }
00350 set whoami ""
00351 if {[string length $response]} {
00352 asnRetag response 0x04
00353 asnGetOctetString response whoami
00354 }
00355 return $whoami
00356 }
00357
00358
00359
00360
00361
00362 ret ldap::connect ( type host , optional port =389 ) {
00363
00364 #--------------------------------------
00365 # connect via TCP/IP
00366 #--------------------------------------
00367 set sock [socket $host $port]
00368 fconfigure $sock -blocking no -translation binary -buffering full
00369
00370 #--------------------------------------
00371 # initialize connection array
00372 #--------------------------------------
00373 upvar #0 ::ldap::ldap$sock conn
00374 catch { unset conn }
00375
00376 set conn(host) $host
00377 set conn(sock) $sock
00378 set conn(messageId) 0
00379 set conn(tls) 0
00380 set conn(bound) 0
00381 set conn(bounduser) ""
00382 set conn(saslBindInProgress) 0
00383 set conn(tlsHandshakeInProgress) 0
00384 set conn(lastError) ""
00385
00386 fileevent $sock readable [list ::ldap::MessageReceiver ::ldap::ldap$sock]
00387 return ::ldap::ldap$sock
00388 }
00389
00390
00391
00392
00393
00394 ret ldap::secure_connect ( type host , optional port =636 ) {
00395
00396 variable SSLCertifiedAuthoritiesFile
00397
00398 package require tls
00399
00400 #------------------------------------------------------------------
00401 # connect via TCP/IP
00402 #------------------------------------------------------------------
00403 set sock [socket $host $port]
00404 fconfigure $sock -blocking no -translation binary -buffering full
00405
00406 #------------------------------------------------------------------
00407 # make it a SSL connection
00408 #
00409 #------------------------------------------------------------------
00410 #tls::import $sock -cafile $SSLCertifiedAuthoritiesFile -ssl2 no -ssl3 yes -tls1 yes
00411 tls::import $sock -cafile "" -certfile "" -keyfile "" \
00412 -request 1 -server 0 -require 0 -ssl2 no -ssl3 yes -tls1 yes
00413 set retry 0
00414 while {1} {
00415 if {$retry > 20} {
00416 close $sock
00417 return -code error "too long retry to setup SSL connection"
00418 }
00419 if {[catch { tls::handshake $sock } err]} {
00420 if {[string match "*resource temporarily unavailable*" $err]} {
00421 after 50
00422 incr retry
00423 } else {
00424 close $sock
00425 return -code error $err
00426 }
00427 } else {
00428 break
00429 }
00430 }
00431
00432 #--------------------------------------
00433 # initialize connection array
00434 #--------------------------------------
00435 upvar ::ldap::ldap$sock conn
00436 catch { unset conn }
00437
00438 set conn(host) $host
00439 set conn(sock) $sock
00440 set conn(messageId) 0
00441 set conn(tls) 1
00442 set conn(bound) 0
00443 set conn(bounduser) ""
00444 set conn(saslBindInProgress) 0
00445 set conn(tlsHandshakeInProgress) 0
00446 set conn(lasterror) ""
00447
00448 fileevent $sock readable [list ::ldap::MessageReceiver ::ldap::ldap$sock]
00449 return ::ldap::ldap$sock
00450 }
00451
00452
00453
00454
00455
00456
00457 ret ldap::starttls (type handle , optional cafile ="" , optional certfile ="" , optional keyfile ="") {
00458 CheckHandle $handle
00459
00460 upvar #0 $handle conn
00461
00462 if {$conn(tls)} {
00463 return -code error \
00464 "Cannot StartTLS on connection, TLS already running"
00465 }
00466
00467 if {[ldap::waitingForMessages $handle]} {
00468 return -code error \
00469 "Cannot StartTLS while waiting for repsonses"
00470 }
00471
00472 if {$conn(saslBindInProgress)} {
00473 return -code error \
00474 "Cannot StartTLS while SASL bind in progress"
00475 }
00476
00477 if {[lsearch -exact [ldap::Extensions $handle] 1.3.6.1.4.1.1466.20037] == -1} {
00478 return -code error \
00479 "Server does not support the StartTLS extension"
00480 }
00481 package require tls
00482
00483
00484 set request [asnApplicationConstr 23 [asnOctetString 1.3.6.1.4.1.1466.20037]]
00485 set mid [SendMessage $handle $request]
00486 set conn(tlsHandshakeInProgress) 1
00487 set response [WaitForResponse $handle $mid]
00488
00489 asnGetApplication response appNum
00490 if {$appNum != 24} {
00491 set conn(tlsHandshakeInProgress) 0
00492 return -code error \
00493 "unexpected application number ($appNum != 24)"
00494 }
00495
00496 asnGetEnumeration response resultCode
00497 asnGetOctetString response matchedDN
00498 asnGetOctetString response errorMessage
00499 if {$resultCode != 0} {
00500 set conn(tlsHandshakeInProgress) 0
00501 return -code error \
00502 -errorcode [list LDAP [resultCode2String $resultCode] $matchedDN $errorMessage] \
00503 "LDAP error [resultCode2String $resultCode] '$matchedDN': $errorMessage"
00504 }
00505 set oid "1.3.6.1.4.1.1466.20037"
00506 if {[string length $response]} {
00507 asnRetag response 0x04
00508 asnGetOctetString response oid
00509 }
00510 if {$oid ne "1.3.6.1.4.1.1466.20037"} {
00511 set conn(tlsHandshakeInProgress) 0
00512 return -code error \
00513 "Unexpected LDAP response"
00514 }
00515
00516 tls::import $conn(sock) -cafile $cafile -certfile $certfile -keyfile $keyfile \
00517 -request 1 -server 0 -require 0 -ssl2 no -ssl3 yes -tls1 yes
00518 set retry 0
00519 while {1} {
00520 if {$retry > 20} {
00521 close $sock
00522 return -code error "too long retry to setup SSL connection"
00523 }
00524 if {[catch { tls::handshake $conn(sock) } err]} {
00525 if {[string match "*resource temporarily unavailable*" $err]} {
00526 after 50
00527 incr retry
00528 } else {
00529 close $conn(sock)
00530 return -code error $err
00531 }
00532 } else {
00533 break
00534 }
00535 }
00536 set conn(tls) 1
00537 set conn(tlsHandshakeInProgress) 0
00538 return 1
00539 }
00540
00541
00542
00543
00544
00545
00546
00547
00548 ret ldap::CreateAndSendMessage (type handle , type payload) {
00549 upvar #0 $handle conn
00550
00551 if {$conn(tlsHandshakeInProgress)} {
00552 return -code error \
00553 "Cannot send other LDAP PDU while TLS handshake in progress"
00554 }
00555
00556 incr conn(messageId)
00557 set message [asnSequence [asnInteger $conn(messageId)] $payload]
00558 debugData "Message $conn(messageId) Sent" $message
00559 puts -nonewline $conn(sock) $message
00560 flush $conn(sock)
00561 return $conn(messageId)
00562 }
00563
00564
00565
00566
00567
00568
00569
00570 ret ldap::SendMessage (type handle , type pdu) {
00571 upvar #0 $handle conn
00572 set mid [CreateAndSendMessage $handle $pdu]
00573
00574 # safe the state to match responses
00575 set conn(message,$mid) [list]
00576 return $mid
00577 }
00578
00579
00580
00581
00582
00583 ret ldap::SendMessageNoReply (type handle , type pdu) {
00584 upvar #0 $handle conn
00585 return [CreateAndSendMessage $handle $pdu]
00586 }
00587
00588
00589
00590
00591
00592 ret ldap::FinalizeMessage (type handle , type messageId) {
00593 upvar #0 $handle conn
00594 trace "Message $messageId finalized"
00595 unset -nocomplain conn(message,$messageId)
00596 }
00597
00598
00599
00600
00601
00602
00603
00604
00605 ret ldap::WaitForResponse (type handle , type messageId) {
00606 upvar #0 $handle conn
00607
00608 trace "Waiting for Message $messageId"
00609 # check if the message waits for a reply
00610 if {![::info exists conn(message,$messageId)]} {
00611 return -code error \
00612 [format "Cannot wait for message %d." $messageId]
00613 }
00614
00615 # check if we have a received response in the buffer
00616 if {[llength $conn(message,$messageId)] > 0} {
00617 set response [lindex $conn(message,$messageId) 0]
00618 set conn(message,$messageId) [lrange $conn(message,$messageId) 1 end]
00619 return $response
00620 }
00621
00622 # wait for an incoming response
00623 vwait [namespace which -variable $handle](message,$messageId)
00624 if {[llength $conn(message,$messageId)] == 0} {
00625 # We have waited and have been awakended but no message is there
00626 if {[string length $conn(lastError)]} {
00627 return -code error \
00628 [format "Protocol error: %s" $conn(lastError)]
00629 } else {
00630 return -code error \
00631 [format "Broken response for message %d" $messageId]
00632 }
00633 }
00634 set response [lindex $conn(message,$messageId) 0]
00635 set conn(message,$messageId) [lrange $conn(message,$messageId) 1 end]
00636 return $response
00637 }
00638
00639 ret ldap::waitingForMessages (type handle) {
00640 upvar #0 $handle conn
00641 return [llength [array names conn message,*]]
00642 }
00643
00644
00645
00646
00647
00648
00649
00650 ret ldap::ProcessMessage (type handle , type response) {
00651 upvar #0 $handle conn
00652
00653 # decode the messageId
00654 asnGetInteger response messageId
00655
00656 # check if we wait for a response
00657 if {[::info exists conn(message,$messageId)]} {
00658 # append the new message, which triggers
00659 # message handlers using vwait on the entry
00660 lappend conn(message,$messageId) $response
00661 return
00662 }
00663
00664 # handle unsolicited server responses
00665
00666 if {0} {
00667 asnGetApplication response appNum
00668 #if { $appNum != 24 } {
00669 # error "unexpected application number ($appNum != 24)"
00670 #}
00671 asnGetEnumeration response resultCode
00672 asnGetOctetString response matchedDN
00673 asnGetOctetString response errorMessage
00674 if {[string length $response]} {
00675 asnGetOctetString response responseName
00676 }
00677 if {[string length $response]} {
00678 asnGetOctetString response responseValue
00679 }
00680 if {$resultCode != 0} {
00681 return -code error \
00682 -errorcode [list LDAP [resultCode2String $resultCode] $matchedDN $errorMessage] \
00683 "LDAP error [resultCode2String $resultCode] '$matchedDN': $errorMessage"
00684 }
00685 }
00686 #dumpASN1Parse $response
00687 #error "Unsolicited message from server"
00688
00689 }
00690
00691
00692
00693
00694
00695 ret ldap::CleanupWaitingMessages (type handle) {
00696 upvar #0 $handle conn
00697 foreach message [array names conn message,*] {
00698 set conn($message) [list]
00699 }
00700 }
00701
00702
00703
00704
00705
00706
00707 ret ldap::MessageReceiver (type handle) {
00708 upvar #0 $handle conn
00709
00710 # We have to account for partial PDUs received, so
00711 # we keep some state information.
00712 #
00713 # conn(pdu,partial) -- we are reading a partial pdu if non zero
00714 # conn(pdu,length_bytes) -- the buffer for loading the length
00715 # conn(pdu,length) -- we have decoded the length if >= 0, if <0 it contains
00716 # the length of the length encoding in bytes
00717 # conn(pdu,payload) -- the payload buffer
00718 # conn(pdu,received) -- the data received
00719
00720 # fetch the sequence byte
00721 if {[::info exists conn(pdu,partial)] && $conn(pdu,partial) != 0} {
00722 # we have decoded at least the type byte
00723 } else {
00724 foreach {code type} [ReceiveBytes $conn(sock) 1] {break}
00725 switch -- $code {
00726 ok {
00727 binary scan $type c byte
00728 set type [expr {($byte + 0x100) % 0x100}]
00729 if {$type != 0x30} {
00730 CleanupWaitingMessages $handle
00731 set conn(lastError) [format "Expected SEQUENCE (0x30) but got %x" $type]
00732 return
00733 } else {
00734 set conn(pdu,partial) 1
00735 append conn(pdu,received) $type
00736 }
00737 }
00738 eof {
00739 CleanupWaitingMessages $handle
00740 set conn(lastError) "Server closed connection"
00741 catch {close $conn(sock)}
00742 return
00743 }
00744 default {
00745 CleanupWaitingMessages $handle
00746 set bytes $type[read $conn(sock)]
00747 binary scan $bytes h* values
00748 set conn(lastError) [format \
00749 "Error reading SEQUENCE response for handle %s : %s : %s" $handle $code $values]
00750 return
00751 }
00752 }
00753 }
00754
00755
00756 # fetch the length
00757 if {[::info exists conn(pdu,length)] && $conn(pdu,length) >= 0} {
00758 # we already have a decoded length
00759 } else {
00760 if {[::info exists conn(pdu,length)] && $conn(pdu,length) < 0} {
00761 # we already know the length, but have not received enough bytes to decode it
00762 set missing [expr {1+abs($conn(pdu,length))-[string length $conn(pdu,length_bytes)]}]
00763 if {$missing != 0} {
00764 foreach {code bytes} [ReceiveBytes $conn(sock) $missing] {break}
00765 switch -- $code {
00766 "ok" {
00767 append conn(pdu,length_bytes) $bytes
00768 append conn(pdu,received) $bytes
00769 asnGetLength conn(pdu,length_bytes) conn(pdu,length)
00770 }
00771 "partial" {
00772 append conn(pdu,length_bytes) $bytes
00773 append conn(pdu,received) $bytes
00774 return
00775 }
00776 "eof" {
00777 CleanupWaitingMessages $handle
00778 catch {close $conn(sock)}
00779 set conn(lastError) "Server closed connection"
00780 return
00781 }
00782 default {
00783 CleanupWaitingMessages $handle
00784 set conn(lastError) [format \
00785 "Error reading LENGTH2 response for handle %s : %s" $handle $code]
00786 return
00787 }
00788 }
00789 }
00790 } else {
00791 # we know nothing, need to read the first length byte
00792 foreach {code bytes} [ReceiveBytes $conn(sock) 1] {break}
00793 switch -- $code {
00794 "ok" {
00795 set conn(pdu,length_bytes) $bytes
00796 binary scan $bytes c byte
00797 set size [expr {($byte + 0x100) % 0x100}]
00798 if {$size > 0x080} {
00799 set conn(pdu,length) [expr {-1* ($size & 0x7f)}]
00800 # fetch the rest with the next fileevent
00801 return
00802 } else {
00803 asnGetLength conn(pdu,length_bytes) conn(pdu,length)
00804 }
00805 }
00806 "eof" {
00807 CleanupWaitingMessages $handle
00808 catch {close $conn(sock)}
00809 set conn(lastError) "Server closed connection"
00810 }
00811 default {
00812 CleanupWaitingMessages $handle
00813 set conn(lastError) [format \
00814 "Error reading LENGTH1 response for handle %s : %s" $handle $code]
00815 return
00816 }
00817 }
00818 }
00819 }
00820
00821 if {[::info exists conn(pdu,payload)]} {
00822 # length is decoded, we can read the rest
00823 set missing [expr {$conn(pdu,length) - [string length $conn(pdu,payload)]}]
00824 } else {
00825 set missing $conn(pdu,length)
00826 }
00827 if {$missing > 0} {
00828 foreach {code bytes} [ReceiveBytes $conn(sock) $missing] {break}
00829 switch -- $code {
00830 "ok" {
00831 append conn(pdu,payload) $bytes
00832 }
00833 "partial" {
00834 append conn(pdu,payload) $bytes
00835 return
00836 }
00837 "eof" {
00838 CleanupWaitingMessages $handle
00839 catch {close $conn(sock)}
00840 set conn(lastError) "Server closed connection"
00841 }
00842 default {
00843 CleanupWaitingMessages $handle
00844 set conn(lastError) [format \
00845 "Error reading DATA response for handle %s : %s" $handle $code]
00846 return
00847 }
00848 }
00849 }
00850
00851 # we have a complete PDU, push it for processing
00852 set pdu $conn(pdu,payload)
00853 set conn(pdu,payload) ""
00854 set conn(pdu,partial) 0
00855 unset -nocomplain set conn(pdu,length)
00856 set conn(pdu,length_bytes) ""
00857
00858 # reschedule message Processing
00859 after 0 [list ::ldap::ProcessMessage $handle $pdu]
00860 }
00861
00862
00863
00864
00865
00866 ret ldap::ReceiveBytes (type sock , type bytes) {
00867 set status [catch {read $sock $bytes} block]
00868 if { $status != 0 } {
00869 return [list error $block]
00870 } elseif { [string length $block] == $bytes } {
00871 # we have all bytes we wanted
00872 return [list ok $block]
00873 } elseif { [eof $sock] } {
00874 return [list eof $block]
00875 } elseif { [fblocked $sock] || ([string length $block] < $bytes)} {
00876 return [list partial $block]
00877 } else {
00878 error "Socket state for socket $sock undefined!"
00879 }
00880 }
00881
00882
00883
00884
00885
00886 ret ldap::bindSASL (type handle , optional name ="" , optional password ="" ) {
00887 CheckHandle $handle
00888
00889 package require SASL
00890
00891 upvar #0 $handle conn
00892
00893 set mechs [ldap::Saslmechanisms $handle]
00894
00895 set conn(saslBindInProgress) 1
00896 set auth 0
00897 foreach mech [SASL::mechanisms] {
00898 if {[lsearch -exact $mechs $mech] == -1} { continue }
00899 trace "Using $mech for SASL Auth"
00900 if {[catch {
00901 SASLAuth $handle $mech $name $password
00902 } msg]} {
00903 trace [format "AUTH %s failed: %s" $mech $msg]
00904 } else {
00905 # AUTH was successful
00906 if {$msg == 1} {
00907 set auth 1
00908 break
00909 }
00910 }
00911 }
00912
00913 set conn(saslBindInProgress) 0
00914 return $auth
00915 }
00916
00917
00918
00919
00920
00921
00922
00923
00924 ret ::ldap::SASLCallback (type handle , type context , type command , type args) {
00925 upvar #0 $handle conn
00926 upvar #0 $context ctx
00927 array set options $conn(options)
00928 trace "SASLCallback $command"
00929 switch -exact -- $command {
00930 login { return $options(-username) }
00931 username { return $options(-username) }
00932 password { return $options(-password) }
00933 hostname { return [::info hostname] }
00934 realm {
00935 if {[string equal $ctx(mech) "NTLM"] \
00936 && [info exists ::env(USERDOMAIN)]} {
00937 return $::env(USERDOMAIN)
00938 } else {
00939 return ""
00940 }
00941 }
00942 default {
00943 return -code error "error: unsupported SASL information requested"
00944 }
00945 }
00946 }
00947
00948
00949
00950
00951
00952
00953 ret ldap::SASLAuth (type handle , type mech , type name , type password) {
00954 upvar 1 $handle conn
00955
00956 set conn(options) [list -password $password -username $name]
00957
00958 # check for tcllib bug # 1545306 and reset the nonce-count if
00959 # found, so a second call to this code does not fail
00960 #
00961 if {[::info exists ::SASL::digest_md5_noncecount]} {
00962 set ::SASL::digest_md5_noncecount 0
00963 }
00964
00965 set ctx [SASL::new -mechanism $mech \
00966 -service ldap \
00967 -callback [list ::ldap::SASLCallback $handle]]
00968
00969 set msg(serverSASLCreds) ""
00970 # Do the SASL Message exchanges
00971 while {[SASL::step $ctx $msg(serverSASLCreds)]} {
00972 # Create and send the BindRequest
00973 set request [buildSASLBindRequest "" $mech [SASL::response $ctx]]
00974 set messageId [SendMessage $handle $request]
00975 debugData bindRequest $request
00976
00977 set response [WaitForResponse $handle $messageId]
00978 FinalizeMessage $handle $messageId
00979 debugData bindResponse $response
00980
00981 array set msg [decodeSASLBindResponse $handle $response]
00982
00983 # Check for Bind success
00984 if {$msg(resultCode) == 0} {
00985 set conn(bound) 1
00986 set conn(bounduser) $name
00987 SASL::cleanup $ctx
00988 break
00989 }
00990
00991 # Check if next SASL step is requested
00992 if {$msg(resultCode) == 14} {
00993 continue
00994 }
00995
00996 SASL::cleanup $ctx
00997 # Something went wrong
00998 return -code error \
00999 -errorcode [list LDAP [resultCode2String $msg(resultCode)] \
01000 $msg(matchedDN) $msg(errorMessage)] \
01001 "LDAP error [resultCode2String $msg(resultCode)] '$msg(matchedDN)': $msg(errorMessage)"
01002 }
01003
01004 return 1
01005 }
01006
01007
01008
01009
01010
01011
01012
01013 ret ldap::buildSASLBindRequest (type name , type mech , optional credentials ={)} {
01014 if {$credentials ne {}} {
01015 request = [ asnApplicationConstr 0 \
01016 [asnInteger 3] \
01017 [asnOctetString $name] \
01018 [asnChoiceConstr 3 \
01019 [asnOctetString $mech] \
01020 [asnOctetString $credentials] \
01021 ] \
01022 ]
01023 } else {
01024 request = [ asnApplicationConstr 0 \
01025 [asnInteger 3] \
01026 [asnOctetString $name] \
01027 [asnChoiceConstr 3 \
01028 [asnOctetString $mech] \
01029 ] \
01030 ]
01031 }
01032 return $request
01033 }
01034
01035
01036
01037
01038
01039
01040 ret ldap::decodeSASLBindResponse (type handle , type response) {
01041 upvar #0 $handle conn
01042
01043 asnGetApplication response appNum
01044 if { $appNum != 1 } {
01045 error "unexpected application number ($appNum != 1)"
01046 }
01047 asnGetEnumeration response resultCode
01048 asnGetOctetString response matchedDN
01049 asnGetOctetString response errorMessage
01050
01051 # Check if we have a serverSASLCreds field left,
01052 # or if this is a simple response without it
01053 # probably an error message then.
01054 if {[string length $response]} {
01055 asnRetag response 0x04
01056 asnGetOctetString response serverSASLCreds
01057 } else {
01058 set serverSASLCreds ""
01059 }
01060 return [list appNum $appNum \
01061 resultCode $resultCode matchedDN $matchedDN \
01062 errorMessage $errorMessage serverSASLCreds $serverSASLCreds]
01063 }
01064
01065
01066
01067
01068
01069
01070 ret ldap::bind ( type handle , optional name ="" , optional password ="" ) {
01071 CheckHandle $handle
01072
01073 upvar #0 $handle conn
01074
01075 #-----------------------------------------------------------------
01076 # marshal bind request packet and send it
01077 #
01078 #-----------------------------------------------------------------
01079 set request [asnApplicationConstr 0 \
01080 [asnInteger 3] \
01081 [asnOctetString $name] \
01082 [asnChoice 0 $password] \
01083 ]
01084 set messageId [SendMessage $handle $request]
01085 debugData bindRequest $request
01086
01087 set response [WaitForResponse $handle $messageId]
01088 FinalizeMessage $handle $messageId
01089 debugData bindResponse $response
01090
01091 asnGetApplication response appNum
01092 if { $appNum != 1 } {
01093 error "unexpected application number ($appNum != 1)"
01094 }
01095 asnGetEnumeration response resultCode
01096 asnGetOctetString response matchedDN
01097 asnGetOctetString response errorMessage
01098 if {$resultCode != 0} {
01099 return -code error \
01100 -errorcode [list LDAP [resultCode2String $resultCode] $matchedDN $errorMessage] \
01101 "LDAP error [resultCode2String $resultCode] '$matchedDN': $errorMessage"
01102 }
01103 set conn(bound) 1
01104 set conn(bounduser) $name
01105 }
01106
01107
01108
01109
01110
01111
01112 ret ldap::unbind ( type handle ) {
01113 CheckHandle $handle
01114
01115 upvar #0 $handle conn
01116
01117 #------------------------------------------------
01118 # marshal unbind request packet and send it
01119 #------------------------------------------------
01120 set request [asnApplication 2 ""]
01121 SendMessageNoReply $handle $request
01122
01123 set conn(bounduser) ""
01124 set conn(bound) 0
01125 close $conn(sock)
01126 set conn(sock) ""
01127 }
01128
01129
01130
01131
01132
01133
01134
01135
01136
01137 ret ldap::buildUpFilter ( type filter ) {
01138
01139 set first [lindex $filter 0]
01140 set data ""
01141 switch -regexp -- $first {
01142 ^\\&$ { #--- and -------------------------------------------
01143 foreach term [lrange $filter 1 end] {
01144 append data [buildUpFilter $term]
01145 }
01146 return [asnChoiceConstr 0 $data]
01147 }
01148 ^\\|$ { #--- or --------------------------------------------
01149 foreach term [lrange $filter 1 end] {
01150 append data [buildUpFilter $term]
01151 }
01152 return [asnChoiceConstr 1 $data]
01153 }
01154 ^\\!$ { #--- not -------------------------------------------
01155 return [asnChoiceConstr 2 [buildUpFilter [lindex $filter 1]]]
01156 }
01157 =\\*$ { #--- present ---------------------------------------
01158 set endpos [expr {[string length $first] -3}]
01159 set attributetype [string range $first 0 $endpos]
01160 return [asnChoice 7 $attributetype]
01161 }
01162 ^[0-9A-z.]*~= { #--- approxMatch --------------------------
01163 regexp {^([0-9A-z.]*)~=(.*)$} $first all attributetype value
01164 return [asnChoiceConstr 8 [asnOctetString $attributetype] \
01165 [asnOctetString $value] ]
01166 }
01167 ^[0-9A-z.]*<= { #--- lessOrEqual --------------------------
01168 regexp {^([0-9A-z.]*)<=(.*)$} $first all attributetype value
01169 return [asnChoiceConstr 6 [asnOctetString $attributetype] \
01170 [asnOctetString $value] ]
01171 }
01172 ^[0-9A-z.]*>= { #--- greaterOrEqual -----------------------
01173 regexp {^([0-9A-z.]*)>=(.*)$} $first all attributetype value
01174 return [asnChoiceConstr 5 [asnOctetString $attributetype] \
01175 [asnOctetString $value] ]
01176 }
01177 ^[0-9A-z.]*=.*\\*.* { #--- substrings -----------------
01178 regexp {^([0-9A-z.]*)=(.*)$} $first all attributetype value
01179 regsub -all {\*+} $value {*} value
01180 set value [split $value "*"]
01181
01182 set firstsubstrtype 0 ;# initial
01183 set lastsubstrtype 2 ;# final
01184 if {[string equal [lindex $value 0] ""]} {
01185 set firstsubstrtype 1 ;# any
01186 set value [lreplace $value 0 0]
01187 }
01188 if {[string equal [lindex $value end] ""]} {
01189 set lastsubstrtype 1 ;# any
01190 set value [lreplace $value end end]
01191 }
01192
01193 set n [llength $value]
01194
01195 set i 1
01196 set l {}
01197 set substrtype 0 ;# initial
01198 foreach str $value {
01199 if {$i == 1 && $i == $n} {
01200 if {$firstsubstrtype == 0} {
01201 set substrtype 0 ;# initial
01202 } elseif {$lastsubstrtype == 2} {
01203 set substrtype 2 ;# final
01204 } else {
01205 set substrtype 1 ;# any
01206 }
01207 } elseif {$i == 1} {
01208 set substrtype $firstsubstrtype
01209 } elseif {$i == $n} {
01210 set substrtype $lastsubstrtype
01211 } else {
01212 set substrtype 1 ;# any
01213 }
01214 lappend l [asnChoice $substrtype $str]
01215 incr i
01216 }
01217 return [asnChoiceConstr 4 [asnOctetString $attributetype] \
01218 [asnSequenceFromList $l] ]
01219 }
01220 ^[0-9A-z.]*= { #--- equal ---------------------------------
01221 regexp {^([0-9A-z.]*)=(.*)$} $first all attributetype value
01222 trace "equal: attributetype='$attributetype' value='$value'"
01223 return [asnChoiceConstr 3 [asnOctetString $attributetype] \
01224 [asnOctetString $value] ]
01225 }
01226 default {
01227 return [buildUpFilter $first]
01228 #error "cant handle $first for filter part"
01229 }
01230 }
01231 }
01232
01233
01234
01235
01236
01237
01238
01239
01240
01241
01242 ret ldap::search ( type handle , type baseObject , type filterString , type attributes , type args) {
01243 CheckHandle $handle
01244
01245 upvar #0 $handle conn
01246
01247 searchInit $handle $baseObject $filterString $attributes $args
01248
01249 set results {}
01250 set lastPacket 0
01251 while { !$lastPacket } {
01252
01253 set r [searchNext $handle]
01254 if {[llength $r] > 0} then {
01255 lappend results $r
01256 } else {
01257 set lastPacket 1
01258 }
01259 }
01260 searchEnd $handle
01261
01262 return $results
01263 }
01264
01265
01266
01267
01268
01269 ret ldap::searchInProgress (type handle) {
01270 CheckHandle $handle
01271 upvar #0 $handle conn
01272 if {[::info exists conn(searchInProgress)]} {
01273 return $conn(searchInProgress)
01274 } else {
01275 return 0
01276 }
01277 }
01278
01279
01280
01281
01282
01283 ret ldap::searchInit ( type handle , type baseObject , type filterString , type attributes , type opt) {
01284 CheckHandle $handle
01285
01286 upvar #0 $handle conn
01287
01288 if {[searchInProgress $handle]} {
01289 return -code error \
01290 "Cannot start search. Already a search in progress for this handle."
01291 }
01292
01293 set scope 2
01294 set derefAliases 0
01295 set sizeLimit 0
01296 set timeLimit 0
01297 set attrsOnly 0
01298
01299 foreach {key value} $opt {
01300 switch -- [string tolower $key] {
01301 -scope {
01302 switch -- $value {
01303 base { set scope 0 }
01304 one - onelevel { set scope 1 }
01305 sub - subtree { set scope 2 }
01306 default { }
01307 }
01308 }
01309 -derefaliases {
01310 switch -- $value {
01311 never { set derefAliases 0 }
01312 search { set derefAliases 1 }
01313 find { set derefAliases 2 }
01314 always { set derefAliases 3 }
01315 default { }
01316 }
01317 }
01318 -sizelimit {
01319 set sizeLimit $value
01320 }
01321 -timelimit {
01322 set timeLimit $value
01323 }
01324 -attrsonly {
01325 set attrsOnly $value
01326 }
01327 default {
01328 return -code error \
01329 "Invalid search option '$key'"
01330 }
01331 }
01332 }
01333
01334 set request [buildSearchRequest $baseObject $scope \
01335 $derefAliases $sizeLimit $timeLimit $attrsOnly $filterString \
01336 $attributes]
01337 set messageId [SendMessage $handle $request]
01338 debugData searchRequest $request
01339
01340 # Keep the message Id, so we know about the search
01341 set conn(searchInProgress) $messageId
01342
01343 return $conn(searchInProgress)
01344 }
01345
01346 ret ldap::buildSearchRequest (type baseObject , type scope , type derefAliases
01347 , type sizeLimit , type timeLimit , type attrsOnly , type filterString
01348 , type attributes) {
01349 #----------------------------------------------------------
01350 # marshal filter and attributes parameter
01351 #----------------------------------------------------------
01352 regsub -all {\(} $filterString " \{" filterString
01353 regsub -all {\)} $filterString "\} " filterString
01354
01355 set berFilter [buildUpFilter $filterString]
01356
01357 set berAttributes ""
01358 foreach attribute $attributes {
01359 append berAttributes [asnOctetString $attribute]
01360 }
01361
01362 #----------------------------------------------------------
01363 # marshal search request packet and send it
01364 #----------------------------------------------------------
01365 set request [asnApplicationConstr 3 \
01366 [asnOctetString $baseObject] \
01367 [asnEnumeration $scope] \
01368 [asnEnumeration $derefAliases] \
01369 [asnInteger $sizeLimit] \
01370 [asnInteger $timeLimit] \
01371 [asnBoolean $attrsOnly] \
01372 $berFilter \
01373 [asnSequence $berAttributes] \
01374 ]
01375
01376 }
01377
01378
01379
01380
01381 ret ldap::searchNext ( type handle ) {
01382 CheckHandle $handle
01383
01384 upvar #0 $handle conn
01385
01386 if {! [::info exists conn(searchInProgress)]} then {
01387 return -code error \
01388 "No search in progress"
01389 }
01390
01391 set result {}
01392 set lastPacket 0
01393
01394 #----------------------------------------------------------
01395 # Wait for a search response packet
01396 #----------------------------------------------------------
01397
01398 set response [WaitForResponse $handle $conn(searchInProgress)]
01399 debugData searchResponse $response
01400
01401 asnGetApplication response appNum
01402
01403 if {$appNum == 4} {
01404 trace "Search Response Continue"
01405 #----------------------------------------------------------
01406 # unmarshal search data packet
01407 #----------------------------------------------------------
01408 asnGetOctetString response objectName
01409 asnGetSequence response attributes
01410 set result_attributes {}
01411 while { [string length $attributes] != 0 } {
01412 asnGetSequence attributes attribute
01413 asnGetOctetString attribute attrType
01414 asnGetSet attribute attrValues
01415 set result_attrValues {}
01416 while { [string length $attrValues] != 0 } {
01417 asnGetOctetString attrValues attrValue
01418 lappend result_attrValues $attrValue
01419 }
01420 lappend result_attributes $attrType $result_attrValues
01421 }
01422 set result [list $objectName $result_attributes]
01423 } elseif {$appNum == 5} {
01424 trace "Search Response Done"
01425 #----------------------------------------------------------
01426 # unmarshal search final response packet
01427 #----------------------------------------------------------
01428 asnGetEnumeration response resultCode
01429 asnGetOctetString response matchedDN
01430 asnGetOctetString response errorMessage
01431 set result {}
01432 FinalizeMessage $handle $conn(searchInProgress)
01433 unset conn(searchInProgress)
01434
01435 if {$resultCode != 0} {
01436 return -code error \
01437 -errorcode [list LDAP [resultCode2String $resultCode] $matchedDN $errorMessage] \
01438 "LDAP error [resultCode2String $resultCode] : $errorMessage"
01439 }
01440 } else {
01441 error "unexpected application number ($appNum != 4 or 5)"
01442 }
01443
01444 return $result
01445 }
01446
01447
01448
01449
01450
01451 ret ldap::searchEnd ( type handle ) {
01452 CheckHandle $handle
01453
01454 upvar #0 $handle conn
01455
01456 if {! [::info exists conn(searchInProgress)]} then {
01457 # no harm done, just do nothing
01458 return
01459 }
01460 abandon $handle $conn(searchInProgress)
01461 FinalizeMessage $handle $conn(searchInProgress)
01462
01463 unset conn(searchInProgress)
01464 return
01465 }
01466
01467
01468
01469
01470
01471
01472 ret ldap::abandon (type handle , type messageId) {
01473 CheckHandle $handle
01474
01475 upvar #0 $handle conn
01476 trace "MessagesPending: [string length $conn(messageId)]"
01477 set request [asnApplication 16 \
01478 [asnInteger $messageId] \
01479 ]
01480 SendMessageNoReply $handle $request
01481 }
01482
01483
01484
01485
01486
01487
01488
01489
01490 ret ldap::modify ( type handle , type dn
01491 , type attrValToReplace , optional attrToDelete ={ ) { attrValToAdd {} } } {
01492
01493 CheckHandle $handle
01494
01495 upvar
01496
01497 lrep = {}
01498 foreach {attr value} $attrValToReplace {
01499 lappend lrep $attr [list $value]
01500 }
01501
01502 ldel = {}
01503 foreach {attr value} $attrToDelete {
01504 if {[string equal $value ""]} then {
01505 lappend ldel $attr {}
01506 } else {
01507 lappend ldel $attr [list $value]
01508 }
01509 }
01510
01511 ladd = {}
01512 foreach {attr value} $attrValToAdd {
01513 lappend ladd $attr [list $value]
01514 }
01515
01516 modifyMulti $handle $dn $lrep $ldel $ladd
01517 }
01518
01519
01520
01521
01522
01523
01524
01525
01526
01527 ret ldap::modifyMulti (type handle , type dn
01528 , type attrValToReplace , optional attrValToDelete ={) {attrValToAdd {}}} {
01529
01530 CheckHandle $handle
01531 upvar
01532
01533 operationAdd = 0
01534 operationDelete = 1
01535 operationReplace = 2
01536
01537 modifications = ""
01538
01539
01540
01541
01542
01543
01544
01545
01546
01547
01548
01549
01550 append modifications [ldap::packOpAttrVal $operationReplace \
01551 $attrValToReplace]
01552
01553
01554
01555
01556
01557 append modifications [ldap::packOpAttrVal $operationAdd \
01558 $attrValToAdd]
01559
01560
01561
01562
01563
01564
01565
01566
01567
01568
01569
01570 append modifications [ldap::packOpAttrVal $operationDelete \
01571 $attrValToDelete]
01572
01573
01574
01575
01576 request = [asnApplicationConstr 6 \
01577 [asnOctetString $dn ] \
01578 [asnSequence $modifications ] \
01579 ]
01580 messageId = [SendMessage $handle $request]
01581 debugData modifyRequest $request
01582 response = [WaitForResponse $handle $messageId]
01583 FinalizeMessage $handle $messageId
01584 debugData bindResponse $response
01585
01586 asnGetApplication response appNum
01587 if { $appNum != 7 } {
01588 error "unexpected application number ($appNum != 7)"
01589 }
01590 asnGetEnumeration response resultCode
01591 asnGetOctetString response matchedDN
01592 asnGetOctetString response errorMessage
01593 if {$resultCode != 0} {
01594 return -code error \
01595 -errorcode [list LDAP [resultCode2String $resultCode] $matchedDN $errorMessage] \
01596 "LDAP error [resultCode2String $resultCode] '$matchedDN': $errorMessage"
01597 }
01598 }
01599
01600 ret ldap::packOpAttrVal (type op , type attrValueTuples) {
01601 set p ""
01602 foreach {attrName attrValues} $attrValueTuples {
01603 set l {}
01604 foreach v $attrValues {
01605 lappend l [asnOctetString $v]
01606 }
01607 append p [asnSequence \
01608 [asnEnumeration $op ] \
01609 [asnSequence \
01610 [asnOctetString $attrName ] \
01611 [asnSetFromList $l] \
01612 ] \
01613 ]
01614 }
01615 return $p
01616 }
01617
01618
01619
01620
01621
01622
01623
01624
01625 ret ldap::add ( type handle , type dn , type attrValueTuples ) {
01626
01627 CheckHandle $handle
01628
01629 #
01630 # In order to handle multi-valuated attributes (see bug 1191326 on
01631 # sourceforge), we walk through tuples to collect all values for
01632 # an attribute.
01633 # http://sourceforge.net/tracker/index.php?func=detail&atid=112883&group_id=12883&aid=1191326
01634 #
01635
01636 foreach { attrName attrValue } $attrValueTuples {
01637 lappend avpairs($attrName) $attrValue
01638 }
01639
01640 return [addMulti $handle $dn [array get avpairs]]
01641 }
01642
01643
01644
01645
01646
01647
01648 ret ldap::addMulti ( type handle , type dn , type attrValueTuples ) {
01649
01650 CheckHandle $handle
01651
01652 upvar #0 $handle conn
01653
01654 #------------------------------------------------------------------
01655 # marshal attribute list
01656 #
01657 #------------------------------------------------------------------
01658 set attrList ""
01659
01660 foreach { attrName attrValues } $attrValueTuples {
01661 set valList {}
01662 foreach val $attrValues {
01663 lappend valList [asnOctetString $val]
01664 }
01665 append attrList [asnSequence \
01666 [asnOctetString $attrName ] \
01667 [asnSetFromList $valList] \
01668 ]
01669 }
01670
01671 #----------------------------------------------------------
01672 # marshal search 'add' request packet and send it
01673 #----------------------------------------------------------
01674 set request [asnApplicationConstr 8 \
01675 [asnOctetString $dn ] \
01676 [asnSequence $attrList ] \
01677 ]
01678
01679 set messageId [SendMessage $handle $request]
01680 debugData addRequest $request
01681 set response [WaitForResponse $handle $messageId]
01682 FinalizeMessage $handle $messageId
01683 debugData bindResponse $response
01684
01685 asnGetApplication response appNum
01686 if { $appNum != 9 } {
01687 error "unexpected application number ($appNum != 9)"
01688 }
01689 asnGetEnumeration response resultCode
01690 asnGetOctetString response matchedDN
01691 asnGetOctetString response errorMessage
01692 if {$resultCode != 0} {
01693 return -code error \
01694 -errorcode [list LDAP [resultCode2String $resultCode] $matchedDN $errorMessage] \
01695 "LDAP error [resultCode2String $resultCode] '$matchedDN': $errorMessage"
01696 }
01697 }
01698
01699
01700
01701
01702
01703 ret ldap::delete ( type handle , type dn ) {
01704
01705 CheckHandle $handle
01706
01707 upvar #0 $handle conn
01708
01709 #----------------------------------------------------------
01710 # marshal 'delete' request packet and send it
01711 #----------------------------------------------------------
01712 set request [asnApplication 10 $dn ]
01713 set messageId [SendMessage $handle $request]
01714 debugData deleteRequest $request
01715 set response [WaitForResponse $handle $messageId]
01716 FinalizeMessage $handle $messageId
01717
01718 debugData deleteResponse $response
01719
01720 asnGetApplication response appNum
01721 if { $appNum != 11 } {
01722 error "unexpected application number ($appNum != 11)"
01723 }
01724 asnGetEnumeration response resultCode
01725 asnGetOctetString response matchedDN
01726 asnGetOctetString response errorMessage
01727 if {$resultCode != 0} {
01728 return -code error \
01729 -errorcode [list LDAP [resultCode2String $resultCode] $matchedDN $errorMessage] \
01730 "LDAP error [resultCode2String $resultCode] '$matchedDN': $errorMessage"
01731 }
01732 }
01733
01734
01735
01736
01737
01738
01739 ret ldap::modifyDN ( type handle , type dn , type newrdn , optional deleteOld =1 , optional newSuperior =! ) {
01740
01741 CheckHandle $handle
01742
01743 upvar #0 $handle conn
01744
01745 #----------------------------------------------------------
01746 # marshal 'modifyDN' request packet and send it
01747 #----------------------------------------------------------
01748
01749 if {[string equal $newSuperior "!"]} then {
01750 set request [asnApplicationConstr 12 \
01751 [asnOctetString $dn ] \
01752 [asnOctetString $newrdn ] \
01753 [asnBoolean $deleteOld ] \
01754 ]
01755
01756 } else {
01757 set request [asnApplicationConstr 12 \
01758 [asnOctetString $dn ] \
01759 [asnOctetString $newrdn ] \
01760 [asnBoolean $deleteOld ] \
01761 [asnContext 0 $newSuperior] \
01762 ]
01763 }
01764 set messageId [SendMessage $handle $request]
01765 debugData modifyRequest $request
01766 set response [WaitForResponse $handle $messageId]
01767
01768 asnGetApplication response appNum
01769 if { $appNum != 13 } {
01770 error "unexpected application number ($appNum != 13)"
01771 }
01772 asnGetEnumeration response resultCode
01773 asnGetOctetString response matchedDN
01774 asnGetOctetString response errorMessage
01775 if {$resultCode != 0} {
01776 return -code error \
01777 -errorcode [list LDAP [resultCode2String $resultCode] $matchedDN $errorMessage] \
01778 "LDAP error [resultCode2String $resultCode] '$matchedDN': $errorMessage"
01779
01780 }
01781 }
01782
01783
01784
01785
01786
01787 ret ldap::disconnect ( type handle ) {
01788
01789 CheckHandle $handle
01790
01791 upvar #0 $handle conn
01792
01793 # should we sent an 'unbind' ?
01794 catch {close $conn(sock)}
01795 unset conn
01796
01797 return
01798 }
01799
01800
01801
01802
01803
01804
01805
01806 ret ldap::trace ( type message ) {
01807
01808 variable doDebug
01809
01810 if {!$doDebug} return
01811
01812 puts stderr $message
01813 }
01814
01815
01816
01817
01818
01819
01820 ret ldap::debugData ( type info , type data ) {
01821
01822 variable doDebug
01823
01824 if {!$doDebug} return
01825
01826 set len [string length $data]
01827 trace "$info ($len bytes):"
01828 set address ""
01829 set hexnums ""
01830 set ascii ""
01831 for {set i 0} {$i < $len} {incr i} {
01832 set v [string index $data $i]
01833 binary scan $v H2 hex
01834 binary scan $v c num
01835 set num [expr {( $num + 0x100 ) % 0x100}]
01836 set text .
01837 if {$num > 31} {
01838 set text $v
01839 }
01840 if { ($i % 16) == 0 } {
01841 if {$address != ""} {
01842 trace [format "%4s %-48s |%s|" $address $hexnums $ascii ]
01843 set address ""
01844 set hexnums ""
01845 set ascii ""
01846 }
01847 append address [format "%04d" $i]
01848 }
01849 append hexnums "$hex "
01850 append ascii $text
01851 #trace [format "%3d %2s %s" $i $hex $text]
01852 }
01853 if {$address != ""} {
01854 trace [format "%4s %-48s |%s|" $address $hexnums $ascii ]
01855 }
01856 trace ""
01857 }
01858