ldap.go 11 KB

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