A cloud-native LDAP server backed by a Consul datastore
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.

476 lines
13 KiB

  1. package main
  2. import (
  3. "fmt"
  4. "strings"
  5. ldap "bottin/ldapserver"
  6. message "github.com/lor00x/goldap/message"
  7. )
  8. // Add request ------------------------
  9. func (server *Server) handleAdd(s ldap.UserState, w ldap.ResponseWriter, m *ldap.Message) {
  10. state := s.(*State)
  11. r := m.GetAddRequest()
  12. code, err := server.handleAddInternal(state, &r)
  13. res := ldap.NewResponse(code)
  14. if err != nil {
  15. res.SetDiagnosticMessage(err.Error())
  16. }
  17. if code == ldap.LDAPResultSuccess {
  18. server.logger.Printf("Successfully added %s", string(r.Entry()))
  19. } else {
  20. server.logger.Printf("Failed to add %s (%s)", string(r.Entry()), err)
  21. }
  22. w.Write(message.AddResponse(res))
  23. }
  24. func (server *Server) handleAddInternal(state *State, r *message.AddRequest) (int, error) {
  25. dn, err := server.checkDN(string(r.Entry()), false)
  26. if err != nil {
  27. return ldap.LDAPResultInvalidDNSyntax, err
  28. }
  29. dnSplit, err := parseDN(dn)
  30. if err != nil {
  31. return ldap.LDAPResultInvalidDNSyntax, err
  32. }
  33. // Check permissions
  34. attrListStr := []string{}
  35. for _, attribute := range r.Attributes() {
  36. attrListStr = append(attrListStr, string(attribute.Type_()))
  37. }
  38. if !server.config.Acl.Check(&state.login, "add", dn, attrListStr) {
  39. return ldap.LDAPResultInsufficientAccessRights, nil
  40. }
  41. // Check that object does not already exist
  42. exists, err := server.objectExists(dn)
  43. if err != nil {
  44. return ldap.LDAPResultOperationsError, err
  45. }
  46. if exists {
  47. return ldap.LDAPResultEntryAlreadyExists, nil
  48. }
  49. // Check that parent object exists
  50. parentDn := unparseDN(dnSplit[1:])
  51. parentExists, err := server.objectExists(parentDn)
  52. if err != nil {
  53. return ldap.LDAPResultOperationsError, err
  54. }
  55. if !parentExists {
  56. return ldap.LDAPResultNoSuchObject, fmt.Errorf(
  57. "Parent object %s does not exist", parentDn)
  58. }
  59. // If adding a group, track of who the members will be so that their memberOf field can be updated later
  60. members := []string{}
  61. // Check attributes
  62. entry := Entry{}
  63. for _, attribute := range r.Attributes() {
  64. key := string(attribute.Type_())
  65. vals_str := []string{}
  66. for _, val := range attribute.Vals() {
  67. vals_str = append(vals_str, string(val))
  68. }
  69. // Fail if they are trying to write memberOf, we manage this ourselves
  70. err = checkRestrictedAttr(key)
  71. if err != nil {
  72. return ldap.LDAPResultObjectClassViolation, err
  73. }
  74. if strings.EqualFold(key, ATTR_MEMBER) {
  75. // If they are writing a member list, we have to check they are adding valid members
  76. // Also, rewrite member list to use canonical DN syntax (no spaces, all lowercase)
  77. for _, member := range vals_str {
  78. member_canonical, err := server.checkDN(member, false)
  79. if err != nil {
  80. return ldap.LDAPResultInvalidDNSyntax, err
  81. }
  82. exists, err = server.objectExists(member_canonical)
  83. if err != nil {
  84. return ldap.LDAPResultOperationsError, err
  85. }
  86. if !exists {
  87. return ldap.LDAPResultNoSuchObject, fmt.Errorf(
  88. "Cannot add %s to members, it does not exist!",
  89. member_canonical)
  90. }
  91. members = append(members, member_canonical)
  92. }
  93. entry[key] = members
  94. } else {
  95. if prev, ok := entry[key]; ok {
  96. entry[key] = append(prev, vals_str...)
  97. } else {
  98. entry[key] = vals_str
  99. }
  100. }
  101. }
  102. // Ensure object has at least one objectclass value
  103. hasObjectClass := false
  104. for k := range entry {
  105. if strings.EqualFold(k, ATTR_OBJECTCLASS) {
  106. hasObjectClass = true
  107. break
  108. }
  109. }
  110. if !hasObjectClass {
  111. entry[ATTR_OBJECTCLASS] = []string{"top"}
  112. }
  113. // Write system attributes
  114. entry[ATTR_CREATORSNAME] = []string{state.login.user}
  115. entry[ATTR_CREATETIMESTAMP] = []string{genTimestamp()}
  116. entry[ATTR_ENTRYUUID] = []string{genUuid()}
  117. entry[dnSplit[0].Type] = []string{dnSplit[0].Value}
  118. // Add our item in the DB
  119. err = server.addElements(dn, entry)
  120. if err != nil {
  121. return ldap.LDAPResultOperationsError, err
  122. }
  123. // ~~ from this point on, our operation succeeded ~~
  124. // ~~ future errors cause inconsistencies in the DB and are logged ~~
  125. // If our item has a member list, add it to all of its member's memberOf attribute
  126. for _, member := range members {
  127. server.memberOfAdd(member, dn)
  128. }
  129. return ldap.LDAPResultSuccess, nil
  130. }
  131. // Delete request ------------------------
  132. func (server *Server) handleDelete(s ldap.UserState, w ldap.ResponseWriter, m *ldap.Message) {
  133. state := s.(*State)
  134. r := m.GetDeleteRequest()
  135. code, err := server.handleDeleteInternal(state, &r)
  136. res := ldap.NewResponse(code)
  137. if err != nil {
  138. res.SetDiagnosticMessage(err.Error())
  139. }
  140. if code == ldap.LDAPResultSuccess {
  141. server.logger.Printf("Successfully deleted %s", string(r))
  142. } else {
  143. server.logger.Printf("Failed to delete %s (%s)", string(r), err)
  144. }
  145. w.Write(message.DelResponse(res))
  146. }
  147. func (server *Server) handleDeleteInternal(state *State, r *message.DelRequest) (int, error) {
  148. dn, err := server.checkDN(string(*r), false)
  149. if err != nil {
  150. return ldap.LDAPResultInvalidDNSyntax, err
  151. }
  152. // Check for delete permission
  153. if !server.config.Acl.Check(&state.login, "delete", dn, []string{}) {
  154. return ldap.LDAPResultInsufficientAccessRights, nil
  155. }
  156. // Check that this LDAP entry exists and has no children
  157. path, err := dnToConsul(dn)
  158. if err != nil {
  159. return ldap.LDAPResultInvalidDNSyntax, err
  160. }
  161. items, _, err := server.kv.List(path+"/", nil)
  162. if err != nil {
  163. return ldap.LDAPResultOperationsError, err
  164. }
  165. if len(items) == 0 {
  166. return ldap.LDAPResultNoSuchObject, fmt.Errorf("Not found: %s", dn)
  167. }
  168. for _, item := range items {
  169. itemDN, _, err := consulToDN(item.Key)
  170. if err != nil {
  171. continue
  172. }
  173. if itemDN != dn {
  174. return ldap.LDAPResultNotAllowedOnNonLeaf, fmt.Errorf(
  175. "Cannot delete %s as it has children", dn)
  176. }
  177. }
  178. // Retrieve group membership before we delete everything
  179. memberOf, err := server.getAttribute(dn, ATTR_MEMBEROF)
  180. if err != nil {
  181. return ldap.LDAPResultOperationsError, err
  182. }
  183. memberList, err := server.getAttribute(dn, ATTR_MEMBER)
  184. if err != nil {
  185. return ldap.LDAPResultOperationsError, err
  186. }
  187. // Delete the LDAP entry
  188. _, err = server.kv.DeleteTree(path+"/", nil)
  189. if err != nil {
  190. return ldap.LDAPResultOperationsError, err
  191. }
  192. // ~~ from this point on, our operation succeeded ~~
  193. // ~~ future errors cause inconsistencies in the DB and are logged ~~
  194. // Delete it from the member list of all the groups it was a member of
  195. if memberOf != nil {
  196. for _, group := range memberOf {
  197. groupMembers, err := server.getAttribute(group, ATTR_MEMBER)
  198. if err != nil {
  199. server.logger.Warnf("Could not remove %s from members of %s: %s", dn, group, err)
  200. continue
  201. }
  202. newMembers := []string{}
  203. for _, memb := range groupMembers {
  204. if memb != dn {
  205. newMembers = append(newMembers, memb)
  206. }
  207. }
  208. err = server.addElements(group, Entry{
  209. ATTR_MEMBER: newMembers,
  210. })
  211. if err != nil {
  212. server.logger.Warnf("Could not remove %s from members of %s: %s", dn, group, err)
  213. }
  214. }
  215. }
  216. // Delete it from all of its member's memberOf info
  217. for _, member := range memberList {
  218. server.memberOfRemove(member, dn)
  219. }
  220. return ldap.LDAPResultSuccess, nil
  221. }
  222. // Modify request ------------------------
  223. func (server *Server) handleModify(s ldap.UserState, w ldap.ResponseWriter, m *ldap.Message) {
  224. state := s.(*State)
  225. r := m.GetModifyRequest()
  226. code, err := server.handleModifyInternal(state, &r)
  227. res := ldap.NewResponse(code)
  228. if err != nil {
  229. res.SetDiagnosticMessage(err.Error())
  230. }
  231. if code == ldap.LDAPResultSuccess {
  232. server.logger.Printf("Successfully modified %s", string(r.Object()))
  233. } else {
  234. server.logger.Printf("Failed to modifiy %s (%s)", string(r.Object()), err)
  235. }
  236. w.Write(message.ModifyResponse(res))
  237. }
  238. func (server *Server) handleModifyInternal(state *State, r *message.ModifyRequest) (int, error) {
  239. dn, err := server.checkDN(string(r.Object()), false)
  240. if err != nil {
  241. return ldap.LDAPResultInvalidDNSyntax, err
  242. }
  243. dnSplit, err := parseDN(dn)
  244. if err != nil {
  245. return ldap.LDAPResultInvalidDNSyntax, err
  246. }
  247. // First permission check with no particular attributes
  248. if !server.config.Acl.Check(&state.login, "modify", dn, []string{}) &&
  249. !server.config.Acl.Check(&state.login, "modifyAdd", dn, []string{}) {
  250. return ldap.LDAPResultInsufficientAccessRights, nil
  251. }
  252. // Retrieve previous values (by the way, check object exists)
  253. path, err := dnToConsul(dn)
  254. if err != nil {
  255. return ldap.LDAPResultInvalidDNSyntax, err
  256. }
  257. items, _, err := server.kv.List(path+"/attribute=", nil)
  258. if err != nil {
  259. return ldap.LDAPResultOperationsError, err
  260. }
  261. if len(items) == 0 {
  262. return ldap.LDAPResultNoSuchObject, fmt.Errorf("Not found: %s", dn)
  263. }
  264. prevEntry := Entry{}
  265. for _, item := range items {
  266. itemDN, attr, err := consulToDN(item.Key)
  267. if err != nil {
  268. continue
  269. }
  270. if itemDN != dn {
  271. server.logger.Fatal("itemDN != dn in handleModifyInternal")
  272. }
  273. vals, err := parseValue(item.Value)
  274. if err != nil {
  275. return ldap.LDAPResultOperationsError, err
  276. }
  277. prevEntry[attr] = vals
  278. }
  279. // Keep track of group members added/deleted
  280. addMembers, delMembers := []string{}, []string{}
  281. // Produce new entry values to be saved
  282. entry := Entry{}
  283. for _, change := range r.Changes() {
  284. attr := string(change.Modification().Type_())
  285. changeValues := []string{}
  286. for _, v := range change.Modification().Vals() {
  287. changeValues = append(changeValues, string(v))
  288. }
  289. // If we already had an attribute with this name before,
  290. // make sure we are using the same lowercase/uppercase
  291. for prevAttr := range prevEntry {
  292. if strings.EqualFold(attr, prevAttr) {
  293. attr = prevAttr
  294. break
  295. }
  296. }
  297. // Check that this attribute is not system-managed thus restricted
  298. err = checkRestrictedAttr(attr)
  299. if err != nil {
  300. return ldap.LDAPResultObjectClassViolation, err
  301. }
  302. if strings.EqualFold(attr, dnSplit[0].Type) {
  303. return ldap.LDAPResultObjectClassViolation, fmt.Errorf("%s may not be changed as it is part of object path", attr)
  304. }
  305. // Check for permission to modify this attribute
  306. if !(server.config.Acl.Check(&state.login, "modify", dn, []string{attr}) ||
  307. (change.Operation() == ldap.ModifyRequestChangeOperationAdd &&
  308. server.config.Acl.Check(&state.login, "modifyAdd", dn, []string{attr}))) {
  309. return ldap.LDAPResultInsufficientAccessRights, nil
  310. }
  311. // If we are changing ATTR_MEMBER, rewrite all values to canonical form
  312. if strings.EqualFold(attr, ATTR_MEMBER) {
  313. for i := range changeValues {
  314. canonical_val, err := server.checkDN(changeValues[i], false)
  315. if err != nil {
  316. return ldap.LDAPResultInvalidDNSyntax, err
  317. }
  318. changeValues[i] = canonical_val
  319. }
  320. }
  321. // If we don't yet have a new value for this attr,
  322. // but one existed before, initialize entry[attr] to the old value
  323. // so that later on what we do is simply modify entry[attr] in place
  324. // (this allows to handle sequences of several changes on the same attr)
  325. if _, ok := entry[attr]; !ok {
  326. if _, ok := prevEntry[attr]; ok {
  327. entry[attr] = prevEntry[attr]
  328. }
  329. }
  330. // Apply effective modification on entry[attr]
  331. if change.Operation() == ldap.ModifyRequestChangeOperationAdd {
  332. for _, val := range changeValues {
  333. if !listContains(entry[attr], val) {
  334. entry[attr] = append(entry[attr], val)
  335. if strings.EqualFold(attr, ATTR_MEMBER) {
  336. addMembers = append(addMembers, val)
  337. }
  338. }
  339. }
  340. } else if change.Operation() == ldap.ModifyRequestChangeOperationDelete {
  341. if len(changeValues) == 0 {
  342. // Delete everything
  343. if strings.EqualFold(attr, ATTR_MEMBER) {
  344. delMembers = append(delMembers, entry[attr]...)
  345. }
  346. entry[attr] = []string{}
  347. } else {
  348. // Delete only those specified
  349. newList := []string{}
  350. for _, prevVal := range entry[attr] {
  351. if !listContains(changeValues, prevVal) {
  352. newList = append(newList, prevVal)
  353. } else {
  354. if strings.EqualFold(attr, ATTR_MEMBER) {
  355. delMembers = append(delMembers, prevVal)
  356. }
  357. }
  358. }
  359. entry[attr] = newList
  360. }
  361. } else if change.Operation() == ldap.ModifyRequestChangeOperationReplace {
  362. if strings.EqualFold(attr, ATTR_MEMBER) {
  363. for _, newMem := range changeValues {
  364. if !listContains(entry[attr], newMem) {
  365. addMembers = append(addMembers, newMem)
  366. }
  367. }
  368. for _, prevMem := range entry[attr] {
  369. if !listContains(changeValues, prevMem) {
  370. delMembers = append(delMembers, prevMem)
  371. }
  372. }
  373. }
  374. entry[attr] = changeValues
  375. }
  376. }
  377. // Check that added members actually exist
  378. for i := range addMembers {
  379. exists, err := server.objectExists(addMembers[i])
  380. if err != nil {
  381. return ldap.LDAPResultOperationsError, err
  382. }
  383. if !exists {
  384. return ldap.LDAPResultNoSuchObject, fmt.Errorf(
  385. "Cannot add member %s, it does not exist", addMembers[i])
  386. }
  387. }
  388. for k, v := range entry {
  389. if strings.EqualFold(k, ATTR_OBJECTCLASS) && len(v) == 0 {
  390. return ldap.LDAPResultInsufficientAccessRights, fmt.Errorf(
  391. "Cannot remove all objectclass values")
  392. }
  393. }
  394. // Now, the modification has been processed and accepted and we want to commit it
  395. entry[ATTR_MODIFIERSNAME] = []string{state.login.user}
  396. entry[ATTR_MODIFYTIMESTAMP] = []string{genTimestamp()}
  397. // Save the edited values
  398. err = server.addElements(dn, entry)
  399. if err != nil {
  400. return ldap.LDAPResultOperationsError, err
  401. }
  402. // ~~ from this point on, our operation succeeded ~~
  403. // ~~ future errors cause inconsistencies in the DB and are logged ~~
  404. // Update memberOf for added members and deleted members
  405. for _, addMem := range addMembers {
  406. server.memberOfAdd(addMem, dn)
  407. }
  408. for _, delMem := range delMembers {
  409. server.memberOfRemove(delMem, dn)
  410. }
  411. return ldap.LDAPResultSuccess, nil
  412. }