control.go 14 KB

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