You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ldap.go 11KB

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