1 /* $OpenBSD: modify.c,v 1.24 2021/12/20 13:26:11 claudio Exp $ */
2
3 /*
4 * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/types.h>
20 #include <sys/queue.h>
21
22 #include <assert.h>
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "ldapd.h"
28 #include "log.h"
29 #include "uuid.h"
30
31 int
ldap_delete(struct request * req)32 ldap_delete(struct request *req)
33 {
34 struct btval key;
35 char *dn, *s;
36 struct namespace *ns;
37 struct referrals *refs;
38 struct cursor *cursor;
39 struct ber_element *entry, *elm, *a;
40 int rc = LDAP_OTHER;
41
42 ++stats.req_mod;
43
44 if (ober_scanf_elements(req->op, "s", &dn) != 0)
45 return ldap_respond(req, LDAP_PROTOCOL_ERROR);
46
47 normalize_dn(dn);
48 log_debug("deleting entry %s", dn);
49
50 if ((ns = namespace_for_base(dn)) == NULL) {
51 refs = namespace_referrals(dn);
52 if (refs == NULL)
53 return ldap_respond(req, LDAP_NAMING_VIOLATION);
54 else
55 return ldap_refer(req, dn, NULL, refs);
56 }
57
58 if (!authorized(req->conn, ns, ACI_WRITE, dn, NULL, LDAP_SCOPE_BASE))
59 return ldap_respond(req, LDAP_INSUFFICIENT_ACCESS);
60
61 if (namespace_begin(ns) != 0) {
62 if (errno == EBUSY) {
63 if (namespace_queue_request(ns, req) != 0)
64 return ldap_respond(req, LDAP_BUSY);
65 return LDAP_BUSY;
66 }
67 return ldap_respond(req, LDAP_OTHER);
68 }
69
70 /* Check that this is a leaf node by getting a cursor to the DN
71 * we're about to delete. If there is a next entry with the DN
72 * as suffix (ie, a child node), the DN can't be deleted.
73 */
74 if ((cursor = btree_txn_cursor_open(NULL, ns->data_txn)) == NULL)
75 goto done;
76
77 memset(&key, 0, sizeof(key));
78 key.data = dn;
79 key.size = strlen(dn);
80 if (btree_cursor_get(cursor, &key, NULL, BT_CURSOR_EXACT) != 0) {
81 if (errno == ENOENT)
82 rc = LDAP_NO_SUCH_OBJECT;
83 goto done;
84 }
85
86 btval_reset(&key);
87 if (btree_cursor_get(cursor, &key, NULL, BT_NEXT) != 0) {
88 if (errno != ENOENT)
89 goto done;
90 } else if (has_suffix(&key, dn)) {
91 rc = LDAP_NOT_ALLOWED_ON_NONLEAF;
92 goto done;
93 }
94
95 if ((entry = namespace_get(ns, dn)) == NULL) {
96 rc = LDAP_NO_SUCH_OBJECT;
97 goto done;
98 }
99
100 /* Fail if this leaf node includes non-writeable attributes */
101 if (entry->be_encoding != BER_TYPE_SEQUENCE)
102 goto done;
103 for (elm = entry->be_sub; elm != NULL; elm = elm->be_next) {
104 a = elm->be_sub;
105 if (a && ober_get_string(a, &s) == 0 &&
106 !authorized(req->conn, ns, ACI_WRITE, dn, s,
107 LDAP_SCOPE_BASE)) {
108 rc = LDAP_INSUFFICIENT_ACCESS;
109 goto done;
110 }
111 }
112
113 if (namespace_del(ns, dn) == 0 && namespace_commit(ns) == 0)
114 rc = LDAP_SUCCESS;
115
116 done:
117 btree_cursor_close(cursor);
118 btval_reset(&key);
119 namespace_abort(ns);
120 return ldap_respond(req, rc);
121 }
122
123 int
ldap_add(struct request * req)124 ldap_add(struct request *req)
125 {
126 char uuid_str[64];
127 struct uuid uuid;
128 char *dn, *s;
129 struct attr_type *at;
130 struct ber_element *attrs, *attr, *elm, *set = NULL;
131 struct namespace *ns;
132 struct referrals *refs;
133 int rc;
134
135 ++stats.req_mod;
136
137 if (ober_scanf_elements(req->op, "{se", &dn, &attrs) != 0)
138 return ldap_respond(req, LDAP_PROTOCOL_ERROR);
139
140 normalize_dn(dn);
141 log_debug("adding entry %s", dn);
142
143 if (*dn == '\0')
144 return ldap_respond(req, LDAP_INVALID_DN_SYNTAX);
145
146 if ((ns = namespace_for_base(dn)) == NULL) {
147 refs = namespace_referrals(dn);
148 if (refs == NULL)
149 return ldap_respond(req, LDAP_NAMING_VIOLATION);
150 else
151 return ldap_refer(req, dn, NULL, refs);
152 }
153
154 if (!authorized(req->conn, ns, ACI_WRITE, dn, NULL, LDAP_SCOPE_BASE))
155 return ldap_respond(req, LDAP_INSUFFICIENT_ACCESS);
156
157 /* Check that we're not adding immutable attributes.
158 */
159 for (elm = attrs->be_sub; elm != NULL; elm = elm->be_next) {
160 attr = elm->be_sub;
161 if (attr == NULL || ober_get_string(attr, &s) != 0)
162 return ldap_respond(req, LDAP_PROTOCOL_ERROR);
163 if (!authorized(req->conn, ns, ACI_WRITE, dn, s,
164 LDAP_SCOPE_BASE))
165 return ldap_respond(req, LDAP_INSUFFICIENT_ACCESS);
166 if (!ns->relax) {
167 at = lookup_attribute(conf->schema, s);
168 if (at == NULL) {
169 log_debug("unknown attribute type %s", s);
170 return ldap_respond(req,
171 LDAP_NO_SUCH_ATTRIBUTE);
172 }
173 if (at->immutable) {
174 log_debug("attempt to add immutable"
175 " attribute %s", s);
176 return ldap_respond(req,
177 LDAP_CONSTRAINT_VIOLATION);
178 }
179 }
180 }
181
182 if (namespace_begin(ns) == -1) {
183 if (errno == EBUSY) {
184 if (namespace_queue_request(ns, req) != 0)
185 return ldap_respond(req, LDAP_BUSY);
186 return LDAP_BUSY;
187 }
188 return ldap_respond(req, LDAP_OTHER);
189 }
190
191 /* add operational attributes
192 */
193 if ((set = ober_add_set(NULL)) == NULL)
194 goto fail;
195 if (ober_add_string(set, req->conn->binddn ? req->conn->binddn : "") == NULL)
196 goto fail;
197 if (ldap_add_attribute(attrs, "creatorsName", set) == NULL)
198 goto fail;
199
200 if ((set = ober_add_set(NULL)) == NULL)
201 goto fail;
202 if (ober_add_string(set, ldap_now()) == NULL)
203 goto fail;
204 if (ldap_add_attribute(attrs, "createTimestamp", set) == NULL)
205 goto fail;
206
207 uuid_create(&uuid);
208 uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
209 if ((set = ober_add_set(NULL)) == NULL)
210 goto fail;
211 if (ober_add_string(set, uuid_str) == NULL)
212 goto fail;
213 if (ldap_add_attribute(attrs, "entryUUID", set) == NULL)
214 goto fail;
215
216 if ((rc = validate_entry(dn, attrs, ns->relax)) != LDAP_SUCCESS ||
217 namespace_add(ns, dn, attrs) != 0) {
218 namespace_abort(ns);
219 if (rc == LDAP_SUCCESS && errno == EEXIST)
220 rc = LDAP_ALREADY_EXISTS;
221 else if (rc == LDAP_SUCCESS)
222 rc = LDAP_OTHER;
223 } else if (namespace_commit(ns) != 0)
224 rc = LDAP_OTHER;
225
226 return ldap_respond(req, rc);
227
228 fail:
229 if (set != NULL)
230 ober_free_elements(set);
231 namespace_abort(ns);
232 return ldap_respond(req, LDAP_OTHER);
233 }
234
235 int
ldap_modify(struct request * req)236 ldap_modify(struct request *req)
237 {
238 int rc = LDAP_OTHER;
239 char *dn;
240 long long op;
241 char *attr;
242 struct ber_element *mods, *entry, *mod, *a, *set;
243 struct ber_element *vals = NULL, *prev = NULL;
244 struct namespace *ns;
245 struct attr_type *at;
246 struct referrals *refs;
247
248 ++stats.req_mod;
249
250 if (ober_scanf_elements(req->op, "{se", &dn, &mods) != 0)
251 return ldap_respond(req, LDAP_PROTOCOL_ERROR);
252
253 normalize_dn(dn);
254 log_debug("modifying dn %s", dn);
255
256 if (*dn == 0)
257 return ldap_respond(req, LDAP_INVALID_DN_SYNTAX);
258
259 if ((ns = namespace_for_base(dn)) == NULL) {
260 refs = namespace_referrals(dn);
261 if (refs == NULL)
262 return ldap_respond(req, LDAP_NAMING_VIOLATION);
263 else
264 return ldap_refer(req, dn, NULL, refs);
265 }
266
267 /* Check authorization for each mod to consider attributes */
268 for (mod = mods->be_sub; mod; mod = mod->be_next) {
269 if (ober_scanf_elements(mod, "{E{es", &op, &prev, &attr) != 0)
270 return ldap_respond(req, LDAP_PROTOCOL_ERROR);
271 if (!authorized(req->conn, ns, ACI_WRITE, dn, attr,
272 LDAP_SCOPE_BASE))
273 return ldap_respond(req, LDAP_INSUFFICIENT_ACCESS);
274 }
275
276 if (namespace_begin(ns) == -1) {
277 if (errno == EBUSY) {
278 if (namespace_queue_request(ns, req) != 0)
279 return ldap_respond(req, LDAP_BUSY);
280 return LDAP_BUSY;
281 }
282 return ldap_respond(req, LDAP_OTHER);
283 }
284
285 if ((entry = namespace_get(ns, dn)) == NULL) {
286 rc = LDAP_NO_SUCH_OBJECT;
287 goto done;
288 }
289
290 for (mod = mods->be_sub; mod; mod = mod->be_next) {
291 if (ober_scanf_elements(mod, "{E{ese(", &op, &prev, &attr, &vals) != 0) {
292 rc = LDAP_PROTOCOL_ERROR;
293 vals = NULL;
294 goto done;
295 }
296
297 prev->be_next = NULL;
298
299 if (!ns->relax) {
300 at = lookup_attribute(conf->schema, attr);
301 if (at == NULL) {
302 log_debug("unknown attribute type %s", attr);
303 rc = LDAP_NO_SUCH_ATTRIBUTE;
304 goto done;
305 }
306 if (at->immutable) {
307 log_debug("attempt to modify immutable"
308 " attribute %s", attr);
309 rc = LDAP_CONSTRAINT_VIOLATION;
310 goto done;
311 }
312 }
313
314 a = ldap_get_attribute(entry, attr);
315
316 switch (op) {
317 case LDAP_MOD_ADD:
318 if (a == NULL) {
319 if (ldap_add_attribute(entry, attr, vals) != NULL)
320 vals = NULL;
321 } else {
322 if (ldap_merge_values(a, vals) == 0)
323 vals = NULL;
324 }
325 break;
326 case LDAP_MOD_DELETE:
327 /*
328 * We're already in the "SET OF value
329 * AttributeValue" (see RFC4511 section
330 * 4.1.7) have either no content, so all values
331 * for the attribute gets deleted, or we
332 * have a (first) octetstring (there is one
333 * for each AttributeValue to be deleted)
334 */
335 if (vals->be_sub &&
336 vals->be_sub->be_type == BER_TYPE_OCTETSTRING) {
337 if (ldap_del_values(a, vals) == 1)
338 ldap_del_attribute(entry, attr);
339 } else {
340 ldap_del_attribute(entry, attr);
341 }
342 break;
343 case LDAP_MOD_REPLACE:
344 if (vals->be_sub != NULL &&
345 vals->be_sub->be_type != BER_TYPE_EOC) {
346 if (a == NULL) {
347 if (ldap_add_attribute(entry, attr, vals) != NULL)
348 vals = NULL;
349 } else {
350 if (ldap_set_values(a, vals) == 0)
351 vals = NULL;
352 }
353 } else if (a != NULL)
354 ldap_del_attribute(entry, attr);
355 break;
356 }
357
358 if (vals != NULL) {
359 ober_free_elements(vals);
360 vals = NULL;
361 }
362 }
363
364 if ((rc = validate_entry(dn, entry, ns->relax)) != LDAP_SUCCESS)
365 goto done;
366
367 set = ober_add_set(NULL);
368 ober_add_string(set, req->conn->binddn ? req->conn->binddn : "");
369 if ((a = ldap_get_attribute(entry, "modifiersName")) != NULL)
370 ldap_set_values(a, set);
371 else
372 ldap_add_attribute(entry, "modifiersName", set);
373
374 set = ober_add_set(NULL);
375 ober_add_string(set, ldap_now());
376 if ((a = ldap_get_attribute(entry, "modifyTimestamp")) != NULL)
377 ldap_set_values(a, set);
378 else
379 ldap_add_attribute(entry, "modifyTimestamp", set);
380
381 if (namespace_update(ns, dn, entry) == 0 && namespace_commit(ns) == 0)
382 rc = LDAP_SUCCESS;
383 else
384 rc = LDAP_OTHER;
385
386 done:
387 if (vals != NULL)
388 ober_free_elements(vals);
389 namespace_abort(ns);
390 return ldap_respond(req, rc);
391 }
392
393