ldap.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. // Copyright 2011 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package ldap
  5. import (
  6. "errors"
  7. "io/ioutil"
  8. "os"
  9. ber "gopkg.in/asn1-ber.v1"
  10. )
  11. // LDAP Application Codes
  12. const (
  13. ApplicationBindRequest = 0
  14. ApplicationBindResponse = 1
  15. ApplicationUnbindRequest = 2
  16. ApplicationSearchRequest = 3
  17. ApplicationSearchResultEntry = 4
  18. ApplicationSearchResultDone = 5
  19. ApplicationModifyRequest = 6
  20. ApplicationModifyResponse = 7
  21. ApplicationAddRequest = 8
  22. ApplicationAddResponse = 9
  23. ApplicationDelRequest = 10
  24. ApplicationDelResponse = 11
  25. ApplicationModifyDNRequest = 12
  26. ApplicationModifyDNResponse = 13
  27. ApplicationCompareRequest = 14
  28. ApplicationCompareResponse = 15
  29. ApplicationAbandonRequest = 16
  30. ApplicationSearchResultReference = 19
  31. ApplicationExtendedRequest = 23
  32. ApplicationExtendedResponse = 24
  33. )
  34. // ApplicationMap contains human readable descriptions of LDAP Application Codes
  35. var ApplicationMap = map[uint8]string{
  36. ApplicationBindRequest: "Bind Request",
  37. ApplicationBindResponse: "Bind Response",
  38. ApplicationUnbindRequest: "Unbind Request",
  39. ApplicationSearchRequest: "Search Request",
  40. ApplicationSearchResultEntry: "Search Result Entry",
  41. ApplicationSearchResultDone: "Search Result Done",
  42. ApplicationModifyRequest: "Modify Request",
  43. ApplicationModifyResponse: "Modify Response",
  44. ApplicationAddRequest: "Add Request",
  45. ApplicationAddResponse: "Add Response",
  46. ApplicationDelRequest: "Del Request",
  47. ApplicationDelResponse: "Del Response",
  48. ApplicationModifyDNRequest: "Modify DN Request",
  49. ApplicationModifyDNResponse: "Modify DN Response",
  50. ApplicationCompareRequest: "Compare Request",
  51. ApplicationCompareResponse: "Compare Response",
  52. ApplicationAbandonRequest: "Abandon Request",
  53. ApplicationSearchResultReference: "Search Result Reference",
  54. ApplicationExtendedRequest: "Extended Request",
  55. ApplicationExtendedResponse: "Extended Response",
  56. }
  57. // Ldap Behera Password Policy Draft 10 (https://tools.ietf.org/html/draft-behera-ldap-password-policy-10)
  58. const (
  59. BeheraPasswordExpired = 0
  60. BeheraAccountLocked = 1
  61. BeheraChangeAfterReset = 2
  62. BeheraPasswordModNotAllowed = 3
  63. BeheraMustSupplyOldPassword = 4
  64. BeheraInsufficientPasswordQuality = 5
  65. BeheraPasswordTooShort = 6
  66. BeheraPasswordTooYoung = 7
  67. BeheraPasswordInHistory = 8
  68. )
  69. // BeheraPasswordPolicyErrorMap contains human readable descriptions of Behera Password Policy error codes
  70. var BeheraPasswordPolicyErrorMap = map[int8]string{
  71. BeheraPasswordExpired: "Password expired",
  72. BeheraAccountLocked: "Account locked",
  73. BeheraChangeAfterReset: "Password must be changed",
  74. BeheraPasswordModNotAllowed: "Policy prevents password modification",
  75. BeheraMustSupplyOldPassword: "Policy requires old password in order to change password",
  76. BeheraInsufficientPasswordQuality: "Password fails quality checks",
  77. BeheraPasswordTooShort: "Password is too short for policy",
  78. BeheraPasswordTooYoung: "Password has been changed too recently",
  79. BeheraPasswordInHistory: "New password is in list of old passwords",
  80. }
  81. // Adds descriptions to an LDAP Response packet for debugging
  82. func addLDAPDescriptions(packet *ber.Packet) (err error) {
  83. defer func() {
  84. if r := recover(); r != nil {
  85. err = NewError(ErrorDebugging, errors.New("ldap: cannot process packet to add descriptions"))
  86. }
  87. }()
  88. packet.Description = "LDAP Response"
  89. packet.Children[0].Description = "Message ID"
  90. application := uint8(packet.Children[1].Tag)
  91. packet.Children[1].Description = ApplicationMap[application]
  92. switch application {
  93. case ApplicationBindRequest:
  94. addRequestDescriptions(packet)
  95. case ApplicationBindResponse:
  96. addDefaultLDAPResponseDescriptions(packet)
  97. case ApplicationUnbindRequest:
  98. addRequestDescriptions(packet)
  99. case ApplicationSearchRequest:
  100. addRequestDescriptions(packet)
  101. case ApplicationSearchResultEntry:
  102. packet.Children[1].Children[0].Description = "Object Name"
  103. packet.Children[1].Children[1].Description = "Attributes"
  104. for _, child := range packet.Children[1].Children[1].Children {
  105. child.Description = "Attribute"
  106. child.Children[0].Description = "Attribute Name"
  107. child.Children[1].Description = "Attribute Values"
  108. for _, grandchild := range child.Children[1].Children {
  109. grandchild.Description = "Attribute Value"
  110. }
  111. }
  112. if len(packet.Children) == 3 {
  113. addControlDescriptions(packet.Children[2])
  114. }
  115. case ApplicationSearchResultDone:
  116. addDefaultLDAPResponseDescriptions(packet)
  117. case ApplicationModifyRequest:
  118. addRequestDescriptions(packet)
  119. case ApplicationModifyResponse:
  120. case ApplicationAddRequest:
  121. addRequestDescriptions(packet)
  122. case ApplicationAddResponse:
  123. case ApplicationDelRequest:
  124. addRequestDescriptions(packet)
  125. case ApplicationDelResponse:
  126. case ApplicationModifyDNRequest:
  127. addRequestDescriptions(packet)
  128. case ApplicationModifyDNResponse:
  129. case ApplicationCompareRequest:
  130. addRequestDescriptions(packet)
  131. case ApplicationCompareResponse:
  132. case ApplicationAbandonRequest:
  133. addRequestDescriptions(packet)
  134. case ApplicationSearchResultReference:
  135. case ApplicationExtendedRequest:
  136. addRequestDescriptions(packet)
  137. case ApplicationExtendedResponse:
  138. }
  139. return nil
  140. }
  141. func addControlDescriptions(packet *ber.Packet) {
  142. packet.Description = "Controls"
  143. for _, child := range packet.Children {
  144. child.Description = "Control"
  145. child.Children[0].Description = "Control Type (" + ControlTypeMap[child.Children[0].Value.(string)] + ")"
  146. value := child.Children[1]
  147. if len(child.Children) == 3 {
  148. child.Children[1].Description = "Criticality"
  149. value = child.Children[2]
  150. }
  151. value.Description = "Control Value"
  152. switch child.Children[0].Value.(string) {
  153. case ControlTypePaging:
  154. value.Description += " (Paging)"
  155. if value.Value != nil {
  156. valueChildren := ber.DecodePacket(value.Data.Bytes())
  157. value.Data.Truncate(0)
  158. value.Value = nil
  159. valueChildren.Children[1].Value = valueChildren.Children[1].Data.Bytes()
  160. value.AppendChild(valueChildren)
  161. }
  162. value.Children[0].Description = "Real Search Control Value"
  163. value.Children[0].Children[0].Description = "Paging Size"
  164. value.Children[0].Children[1].Description = "Cookie"
  165. case ControlTypeBeheraPasswordPolicy:
  166. value.Description += " (Password Policy - Behera Draft)"
  167. if value.Value != nil {
  168. valueChildren := ber.DecodePacket(value.Data.Bytes())
  169. value.Data.Truncate(0)
  170. value.Value = nil
  171. value.AppendChild(valueChildren)
  172. }
  173. sequence := value.Children[0]
  174. for _, child := range sequence.Children {
  175. if child.Tag == 0 {
  176. //Warning
  177. child := child.Children[0]
  178. packet := ber.DecodePacket(child.Data.Bytes())
  179. val, ok := packet.Value.(int64)
  180. if ok {
  181. if child.Tag == 0 {
  182. //timeBeforeExpiration
  183. value.Description += " (TimeBeforeExpiration)"
  184. child.Value = val
  185. } else if child.Tag == 1 {
  186. //graceAuthNsRemaining
  187. value.Description += " (GraceAuthNsRemaining)"
  188. child.Value = val
  189. }
  190. }
  191. } else if child.Tag == 1 {
  192. // Error
  193. packet := ber.DecodePacket(child.Data.Bytes())
  194. val, ok := packet.Value.(int8)
  195. if !ok {
  196. val = -1
  197. }
  198. child.Description = "Error"
  199. child.Value = val
  200. }
  201. }
  202. }
  203. }
  204. }
  205. func addRequestDescriptions(packet *ber.Packet) {
  206. packet.Description = "LDAP Request"
  207. packet.Children[0].Description = "Message ID"
  208. packet.Children[1].Description = ApplicationMap[uint8(packet.Children[1].Tag)]
  209. if len(packet.Children) == 3 {
  210. addControlDescriptions(packet.Children[2])
  211. }
  212. }
  213. func addDefaultLDAPResponseDescriptions(packet *ber.Packet) {
  214. resultCode, _ := getLDAPResultCode(packet)
  215. packet.Children[1].Children[0].Description = "Result Code (" + LDAPResultCodeMap[resultCode] + ")"
  216. packet.Children[1].Children[1].Description = "Matched DN"
  217. packet.Children[1].Children[2].Description = "Error Message"
  218. if len(packet.Children[1].Children) > 3 {
  219. packet.Children[1].Children[3].Description = "Referral"
  220. }
  221. if len(packet.Children) == 3 {
  222. addControlDescriptions(packet.Children[2])
  223. }
  224. }
  225. // DebugBinaryFile reads and prints packets from the given filename
  226. func DebugBinaryFile(fileName string) error {
  227. file, err := ioutil.ReadFile(fileName)
  228. if err != nil {
  229. return NewError(ErrorDebugging, err)
  230. }
  231. ber.PrintBytes(os.Stdout, file, "")
  232. packet := ber.DecodePacket(file)
  233. addLDAPDescriptions(packet)
  234. ber.PrintPacket(packet)
  235. return nil
  236. }
  237. var hex = "0123456789abcdef"
  238. func mustEscape(c byte) bool {
  239. return c > 0x7f || c == '(' || c == ')' || c == '\\' || c == '*' || c == 0
  240. }
  241. // EscapeFilter escapes from the provided LDAP filter string the special
  242. // characters in the set `()*\` and those out of the range 0 < c < 0x80,
  243. // as defined in RFC4515.
  244. func EscapeFilter(filter string) string {
  245. escape := 0
  246. for i := 0; i < len(filter); i++ {
  247. if mustEscape(filter[i]) {
  248. escape++
  249. }
  250. }
  251. if escape == 0 {
  252. return filter
  253. }
  254. buf := make([]byte, len(filter)+escape*2)
  255. for i, j := 0, 0; i < len(filter); i++ {
  256. c := filter[i]
  257. if mustEscape(c) {
  258. buf[j+0] = '\\'
  259. buf[j+1] = hex[c>>4]
  260. buf[j+2] = hex[c&0xf]
  261. j += 3
  262. } else {
  263. buf[j] = c
  264. j++
  265. }
  266. }
  267. return string(buf)
  268. }