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.

control.go 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. package ldap
  2. import (
  3. "fmt"
  4. "strconv"
  5. "gopkg.in/asn1-ber.v1"
  6. )
  7. const (
  8. // ControlTypePaging - https://www.ietf.org/rfc/rfc2696.txt
  9. ControlTypePaging = "1.2.840.113556.1.4.319"
  10. // ControlTypeBeheraPasswordPolicy - https://tools.ietf.org/html/draft-behera-ldap-password-policy-10
  11. ControlTypeBeheraPasswordPolicy = "1.3.6.1.4.1.42.2.27.8.5.1"
  12. // ControlTypeVChuPasswordMustChange - https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
  13. ControlTypeVChuPasswordMustChange = "2.16.840.1.113730.3.4.4"
  14. // ControlTypeVChuPasswordWarning - https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
  15. ControlTypeVChuPasswordWarning = "2.16.840.1.113730.3.4.5"
  16. // ControlTypeManageDsaIT - https://tools.ietf.org/html/rfc3296
  17. ControlTypeManageDsaIT = "2.16.840.1.113730.3.4.2"
  18. // ControlTypeMicrosoftNotification - https://msdn.microsoft.com/en-us/library/aa366983(v=vs.85).aspx
  19. ControlTypeMicrosoftNotification = "1.2.840.113556.1.4.528"
  20. // ControlTypeMicrosoftShowDeleted - https://msdn.microsoft.com/en-us/library/aa366989(v=vs.85).aspx
  21. ControlTypeMicrosoftShowDeleted = "1.2.840.113556.1.4.417"
  22. )
  23. // ControlTypeMap maps controls to text descriptions
  24. var ControlTypeMap = map[string]string{
  25. ControlTypePaging: "Paging",
  26. ControlTypeBeheraPasswordPolicy: "Password Policy - Behera Draft",
  27. ControlTypeManageDsaIT: "Manage DSA IT",
  28. ControlTypeMicrosoftNotification: "Change Notification - Microsoft",
  29. ControlTypeMicrosoftShowDeleted: "Show Deleted Objects - Microsoft",
  30. }
  31. // Control defines an interface controls provide to encode and describe themselves
  32. type Control interface {
  33. // GetControlType returns the OID
  34. GetControlType() string
  35. // Encode returns the ber packet representation
  36. Encode() *ber.Packet
  37. // String returns a human-readable description
  38. String() string
  39. }
  40. // ControlString implements the Control interface for simple controls
  41. type ControlString struct {
  42. ControlType string
  43. Criticality bool
  44. ControlValue string
  45. }
  46. // GetControlType returns the OID
  47. func (c *ControlString) GetControlType() string {
  48. return c.ControlType
  49. }
  50. // Encode returns the ber packet representation
  51. func (c *ControlString) Encode() *ber.Packet {
  52. packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
  53. packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, c.ControlType, "Control Type ("+ControlTypeMap[c.ControlType]+")"))
  54. if c.Criticality {
  55. packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality"))
  56. }
  57. packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, string(c.ControlValue), "Control Value"))
  58. return packet
  59. }
  60. // String returns a human-readable description
  61. func (c *ControlString) String() string {
  62. return fmt.Sprintf("Control Type: %s (%q) Criticality: %t Control Value: %s", ControlTypeMap[c.ControlType], c.ControlType, c.Criticality, c.ControlValue)
  63. }
  64. // ControlPaging implements the paging control described in https://www.ietf.org/rfc/rfc2696.txt
  65. type ControlPaging struct {
  66. // PagingSize indicates the page size
  67. PagingSize uint32
  68. // Cookie is an opaque value returned by the server to track a paging cursor
  69. Cookie []byte
  70. }
  71. // GetControlType returns the OID
  72. func (c *ControlPaging) GetControlType() string {
  73. return ControlTypePaging
  74. }
  75. // Encode returns the ber packet representation
  76. func (c *ControlPaging) Encode() *ber.Packet {
  77. packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
  78. packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypePaging, "Control Type ("+ControlTypeMap[ControlTypePaging]+")"))
  79. p2 := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (Paging)")
  80. seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Search Control Value")
  81. seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.PagingSize), "Paging Size"))
  82. cookie := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Cookie")
  83. cookie.Value = c.Cookie
  84. cookie.Data.Write(c.Cookie)
  85. seq.AppendChild(cookie)
  86. p2.AppendChild(seq)
  87. packet.AppendChild(p2)
  88. return packet
  89. }
  90. // String returns a human-readable description
  91. func (c *ControlPaging) String() string {
  92. return fmt.Sprintf(
  93. "Control Type: %s (%q) Criticality: %t PagingSize: %d Cookie: %q",
  94. ControlTypeMap[ControlTypePaging],
  95. ControlTypePaging,
  96. false,
  97. c.PagingSize,
  98. c.Cookie)
  99. }
  100. // SetCookie stores the given cookie in the paging control
  101. func (c *ControlPaging) SetCookie(cookie []byte) {
  102. c.Cookie = cookie
  103. }
  104. // ControlBeheraPasswordPolicy implements the control described in https://tools.ietf.org/html/draft-behera-ldap-password-policy-10
  105. type ControlBeheraPasswordPolicy struct {
  106. // Expire contains the number of seconds before a password will expire
  107. Expire int64
  108. // Grace indicates the remaining number of times a user will be allowed to authenticate with an expired password
  109. Grace int64
  110. // Error indicates the error code
  111. Error int8
  112. // ErrorString is a human readable error
  113. ErrorString string
  114. }
  115. // GetControlType returns the OID
  116. func (c *ControlBeheraPasswordPolicy) GetControlType() string {
  117. return ControlTypeBeheraPasswordPolicy
  118. }
  119. // Encode returns the ber packet representation
  120. func (c *ControlBeheraPasswordPolicy) Encode() *ber.Packet {
  121. packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
  122. packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeBeheraPasswordPolicy, "Control Type ("+ControlTypeMap[ControlTypeBeheraPasswordPolicy]+")"))
  123. return packet
  124. }
  125. // String returns a human-readable description
  126. func (c *ControlBeheraPasswordPolicy) String() string {
  127. return fmt.Sprintf(
  128. "Control Type: %s (%q) Criticality: %t Expire: %d Grace: %d Error: %d, ErrorString: %s",
  129. ControlTypeMap[ControlTypeBeheraPasswordPolicy],
  130. ControlTypeBeheraPasswordPolicy,
  131. false,
  132. c.Expire,
  133. c.Grace,
  134. c.Error,
  135. c.ErrorString)
  136. }
  137. // ControlVChuPasswordMustChange implements the control described in https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
  138. type ControlVChuPasswordMustChange struct {
  139. // MustChange indicates if the password is required to be changed
  140. MustChange bool
  141. }
  142. // GetControlType returns the OID
  143. func (c *ControlVChuPasswordMustChange) GetControlType() string {
  144. return ControlTypeVChuPasswordMustChange
  145. }
  146. // Encode returns the ber packet representation
  147. func (c *ControlVChuPasswordMustChange) Encode() *ber.Packet {
  148. return nil
  149. }
  150. // String returns a human-readable description
  151. func (c *ControlVChuPasswordMustChange) String() string {
  152. return fmt.Sprintf(
  153. "Control Type: %s (%q) Criticality: %t MustChange: %v",
  154. ControlTypeMap[ControlTypeVChuPasswordMustChange],
  155. ControlTypeVChuPasswordMustChange,
  156. false,
  157. c.MustChange)
  158. }
  159. // ControlVChuPasswordWarning implements the control described in https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
  160. type ControlVChuPasswordWarning struct {
  161. // Expire indicates the time in seconds until the password expires
  162. Expire int64
  163. }
  164. // GetControlType returns the OID
  165. func (c *ControlVChuPasswordWarning) GetControlType() string {
  166. return ControlTypeVChuPasswordWarning
  167. }
  168. // Encode returns the ber packet representation
  169. func (c *ControlVChuPasswordWarning) Encode() *ber.Packet {
  170. return nil
  171. }
  172. // String returns a human-readable description
  173. func (c *ControlVChuPasswordWarning) String() string {
  174. return fmt.Sprintf(
  175. "Control Type: %s (%q) Criticality: %t Expire: %b",
  176. ControlTypeMap[ControlTypeVChuPasswordWarning],
  177. ControlTypeVChuPasswordWarning,
  178. false,
  179. c.Expire)
  180. }
  181. // ControlManageDsaIT implements the control described in https://tools.ietf.org/html/rfc3296
  182. type ControlManageDsaIT struct {
  183. // Criticality indicates if this control is required
  184. Criticality bool
  185. }
  186. // GetControlType returns the OID
  187. func (c *ControlManageDsaIT) GetControlType() string {
  188. return ControlTypeManageDsaIT
  189. }
  190. // Encode returns the ber packet representation
  191. func (c *ControlManageDsaIT) Encode() *ber.Packet {
  192. //FIXME
  193. packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
  194. packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeManageDsaIT, "Control Type ("+ControlTypeMap[ControlTypeManageDsaIT]+")"))
  195. if c.Criticality {
  196. packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality"))
  197. }
  198. return packet
  199. }
  200. // String returns a human-readable description
  201. func (c *ControlManageDsaIT) String() string {
  202. return fmt.Sprintf(
  203. "Control Type: %s (%q) Criticality: %t",
  204. ControlTypeMap[ControlTypeManageDsaIT],
  205. ControlTypeManageDsaIT,
  206. c.Criticality)
  207. }
  208. // NewControlManageDsaIT returns a ControlManageDsaIT control
  209. func NewControlManageDsaIT(Criticality bool) *ControlManageDsaIT {
  210. return &ControlManageDsaIT{Criticality: Criticality}
  211. }
  212. // ControlMicrosoftNotification implements the control described in https://msdn.microsoft.com/en-us/library/aa366983(v=vs.85).aspx
  213. type ControlMicrosoftNotification struct{}
  214. // GetControlType returns the OID
  215. func (c *ControlMicrosoftNotification) GetControlType() string {
  216. return ControlTypeMicrosoftNotification
  217. }
  218. // Encode returns the ber packet representation
  219. func (c *ControlMicrosoftNotification) Encode() *ber.Packet {
  220. packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
  221. packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeMicrosoftNotification, "Control Type ("+ControlTypeMap[ControlTypeMicrosoftNotification]+")"))
  222. return packet
  223. }
  224. // String returns a human-readable description
  225. func (c *ControlMicrosoftNotification) String() string {
  226. return fmt.Sprintf(
  227. "Control Type: %s (%q)",
  228. ControlTypeMap[ControlTypeMicrosoftNotification],
  229. ControlTypeMicrosoftNotification)
  230. }
  231. // NewControlMicrosoftNotification returns a ControlMicrosoftNotification control
  232. func NewControlMicrosoftNotification() *ControlMicrosoftNotification {
  233. return &ControlMicrosoftNotification{}
  234. }
  235. // ControlMicrosoftShowDeleted implements the control described in https://msdn.microsoft.com/en-us/library/aa366989(v=vs.85).aspx
  236. type ControlMicrosoftShowDeleted struct{}
  237. // GetControlType returns the OID
  238. func (c *ControlMicrosoftShowDeleted) GetControlType() string {
  239. return ControlTypeMicrosoftShowDeleted
  240. }
  241. // Encode returns the ber packet representation
  242. func (c *ControlMicrosoftShowDeleted) Encode() *ber.Packet {
  243. packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
  244. packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeMicrosoftShowDeleted, "Control Type ("+ControlTypeMap[ControlTypeMicrosoftShowDeleted]+")"))
  245. return packet
  246. }
  247. // String returns a human-readable description
  248. func (c *ControlMicrosoftShowDeleted) String() string {
  249. return fmt.Sprintf(
  250. "Control Type: %s (%q)",
  251. ControlTypeMap[ControlTypeMicrosoftShowDeleted],
  252. ControlTypeMicrosoftShowDeleted)
  253. }
  254. // NewControlMicrosoftShowDeleted returns a ControlMicrosoftShowDeleted control
  255. func NewControlMicrosoftShowDeleted() *ControlMicrosoftShowDeleted {
  256. return &ControlMicrosoftShowDeleted{}
  257. }
  258. // FindControl returns the first control of the given type in the list, or nil
  259. func FindControl(controls []Control, controlType string) Control {
  260. for _, c := range controls {
  261. if c.GetControlType() == controlType {
  262. return c
  263. }
  264. }
  265. return nil
  266. }
  267. // DecodeControl returns a control read from the given packet, or nil if no recognized control can be made
  268. func DecodeControl(packet *ber.Packet) (Control, error) {
  269. var (
  270. ControlType = ""
  271. Criticality = false
  272. value *ber.Packet
  273. )
  274. switch len(packet.Children) {
  275. case 0:
  276. // at least one child is required for control type
  277. return nil, fmt.Errorf("at least one child is required for control type")
  278. case 1:
  279. // just type, no criticality or value
  280. packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
  281. ControlType = packet.Children[0].Value.(string)
  282. case 2:
  283. packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
  284. ControlType = packet.Children[0].Value.(string)
  285. // Children[1] could be criticality or value (both are optional)
  286. // duck-type on whether this is a boolean
  287. if _, ok := packet.Children[1].Value.(bool); ok {
  288. packet.Children[1].Description = "Criticality"
  289. Criticality = packet.Children[1].Value.(bool)
  290. } else {
  291. packet.Children[1].Description = "Control Value"
  292. value = packet.Children[1]
  293. }
  294. case 3:
  295. packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
  296. ControlType = packet.Children[0].Value.(string)
  297. packet.Children[1].Description = "Criticality"
  298. Criticality = packet.Children[1].Value.(bool)
  299. packet.Children[2].Description = "Control Value"
  300. value = packet.Children[2]
  301. default:
  302. // more than 3 children is invalid
  303. return nil, fmt.Errorf("more than 3 children is invalid for controls")
  304. }
  305. switch ControlType {
  306. case ControlTypeManageDsaIT:
  307. return NewControlManageDsaIT(Criticality), nil
  308. case ControlTypePaging:
  309. value.Description += " (Paging)"
  310. c := new(ControlPaging)
  311. if value.Value != nil {
  312. valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
  313. if err != nil {
  314. return nil, fmt.Errorf("failed to decode data bytes: %s", err)
  315. }
  316. value.Data.Truncate(0)
  317. value.Value = nil
  318. value.AppendChild(valueChildren)
  319. }
  320. value = value.Children[0]
  321. value.Description = "Search Control Value"
  322. value.Children[0].Description = "Paging Size"
  323. value.Children[1].Description = "Cookie"
  324. c.PagingSize = uint32(value.Children[0].Value.(int64))
  325. c.Cookie = value.Children[1].Data.Bytes()
  326. value.Children[1].Value = c.Cookie
  327. return c, nil
  328. case ControlTypeBeheraPasswordPolicy:
  329. value.Description += " (Password Policy - Behera)"
  330. c := NewControlBeheraPasswordPolicy()
  331. if value.Value != nil {
  332. valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
  333. if err != nil {
  334. return nil, fmt.Errorf("failed to decode data bytes: %s", err)
  335. }
  336. value.Data.Truncate(0)
  337. value.Value = nil
  338. value.AppendChild(valueChildren)
  339. }
  340. sequence := value.Children[0]
  341. for _, child := range sequence.Children {
  342. if child.Tag == 0 {
  343. //Warning
  344. warningPacket := child.Children[0]
  345. packet, err := ber.DecodePacketErr(warningPacket.Data.Bytes())
  346. if err != nil {
  347. return nil, fmt.Errorf("failed to decode data bytes: %s", err)
  348. }
  349. val, ok := packet.Value.(int64)
  350. if ok {
  351. if warningPacket.Tag == 0 {
  352. //timeBeforeExpiration
  353. c.Expire = val
  354. warningPacket.Value = c.Expire
  355. } else if warningPacket.Tag == 1 {
  356. //graceAuthNsRemaining
  357. c.Grace = val
  358. warningPacket.Value = c.Grace
  359. }
  360. }
  361. } else if child.Tag == 1 {
  362. // Error
  363. packet, err := ber.DecodePacketErr(child.Data.Bytes())
  364. if err != nil {
  365. return nil, fmt.Errorf("failed to decode data bytes: %s", err)
  366. }
  367. val, ok := packet.Value.(int8)
  368. if !ok {
  369. // what to do?
  370. val = -1
  371. }
  372. c.Error = val
  373. child.Value = c.Error
  374. c.ErrorString = BeheraPasswordPolicyErrorMap[c.Error]
  375. }
  376. }
  377. return c, nil
  378. case ControlTypeVChuPasswordMustChange:
  379. c := &ControlVChuPasswordMustChange{MustChange: true}
  380. return c, nil
  381. case ControlTypeVChuPasswordWarning:
  382. c := &ControlVChuPasswordWarning{Expire: -1}
  383. expireStr := ber.DecodeString(value.Data.Bytes())
  384. expire, err := strconv.ParseInt(expireStr, 10, 64)
  385. if err != nil {
  386. return nil, fmt.Errorf("failed to parse value as int: %s", err)
  387. }
  388. c.Expire = expire
  389. value.Value = c.Expire
  390. return c, nil
  391. case ControlTypeMicrosoftNotification:
  392. return NewControlMicrosoftNotification(), nil
  393. case ControlTypeMicrosoftShowDeleted:
  394. return NewControlMicrosoftShowDeleted(), nil
  395. default:
  396. c := new(ControlString)
  397. c.ControlType = ControlType
  398. c.Criticality = Criticality
  399. if value != nil {
  400. c.ControlValue = value.Value.(string)
  401. }
  402. return c, nil
  403. }
  404. }
  405. // NewControlString returns a generic control
  406. func NewControlString(controlType string, criticality bool, controlValue string) *ControlString {
  407. return &ControlString{
  408. ControlType: controlType,
  409. Criticality: criticality,
  410. ControlValue: controlValue,
  411. }
  412. }
  413. // NewControlPaging returns a paging control
  414. func NewControlPaging(pagingSize uint32) *ControlPaging {
  415. return &ControlPaging{PagingSize: pagingSize}
  416. }
  417. // NewControlBeheraPasswordPolicy returns a ControlBeheraPasswordPolicy
  418. func NewControlBeheraPasswordPolicy() *ControlBeheraPasswordPolicy {
  419. return &ControlBeheraPasswordPolicy{
  420. Expire: -1,
  421. Grace: -1,
  422. Error: -1,
  423. }
  424. }
  425. func encodeControls(controls []Control) *ber.Packet {
  426. packet := ber.Encode(ber.ClassContext, ber.TypeConstructed, 0, nil, "Controls")
  427. for _, control := range controls {
  428. packet.AppendChild(control.Encode())
  429. }
  430. return packet
  431. }