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

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