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.

420 lines
13 KiB

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