1 |
|
|
/* $OpenBSD: modify.c,v 1.20 2017/07/28 12:58:52 florian 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 |
32 |
|
|
ldap_delete(struct request *req) |
33 |
|
|
{ |
34 |
|
|
struct btval key; |
35 |
|
|
char *dn; |
36 |
|
|
struct namespace *ns; |
37 |
|
|
struct referrals *refs; |
38 |
|
|
struct cursor *cursor; |
39 |
|
|
int rc = LDAP_OTHER; |
40 |
|
|
|
41 |
|
|
++stats.req_mod; |
42 |
|
|
|
43 |
|
|
if (ber_scanf_elements(req->op, "s", &dn) != 0) |
44 |
|
|
return ldap_respond(req, LDAP_PROTOCOL_ERROR); |
45 |
|
|
|
46 |
|
|
normalize_dn(dn); |
47 |
|
|
log_debug("deleting entry %s", dn); |
48 |
|
|
|
49 |
|
|
if ((ns = namespace_for_base(dn)) == NULL) { |
50 |
|
|
refs = namespace_referrals(dn); |
51 |
|
|
if (refs == NULL) |
52 |
|
|
return ldap_respond(req, LDAP_NAMING_VIOLATION); |
53 |
|
|
else |
54 |
|
|
return ldap_refer(req, dn, NULL, refs); |
55 |
|
|
} |
56 |
|
|
|
57 |
|
|
if (!authorized(req->conn, ns, ACI_WRITE, dn, LDAP_SCOPE_BASE)) |
58 |
|
|
return ldap_respond(req, LDAP_INSUFFICIENT_ACCESS); |
59 |
|
|
|
60 |
|
|
if (namespace_begin(ns) != 0) { |
61 |
|
|
if (errno == EBUSY) { |
62 |
|
|
if (namespace_queue_request(ns, req) != 0) |
63 |
|
|
return ldap_respond(req, LDAP_BUSY); |
64 |
|
|
return LDAP_BUSY; |
65 |
|
|
} |
66 |
|
|
return ldap_respond(req, LDAP_OTHER); |
67 |
|
|
} |
68 |
|
|
|
69 |
|
|
/* Check that this is a leaf node by getting a cursor to the DN |
70 |
|
|
* we're about to delete. If there is a next entry with the DN |
71 |
|
|
* as suffix (ie, a child node), the DN can't be deleted. |
72 |
|
|
*/ |
73 |
|
|
if ((cursor = btree_txn_cursor_open(NULL, ns->data_txn)) == NULL) |
74 |
|
|
goto done; |
75 |
|
|
|
76 |
|
|
memset(&key, 0, sizeof(key)); |
77 |
|
|
key.data = dn; |
78 |
|
|
key.size = strlen(dn); |
79 |
|
|
if (btree_cursor_get(cursor, &key, NULL, BT_CURSOR_EXACT) != 0) { |
80 |
|
|
if (errno == ENOENT) |
81 |
|
|
rc = LDAP_NO_SUCH_OBJECT; |
82 |
|
|
goto done; |
83 |
|
|
} |
84 |
|
|
|
85 |
|
|
btval_reset(&key); |
86 |
|
|
if (btree_cursor_get(cursor, &key, NULL, BT_NEXT) != 0) { |
87 |
|
|
if (errno != ENOENT) |
88 |
|
|
goto done; |
89 |
|
|
} else if (has_suffix(&key, dn)) { |
90 |
|
|
rc = LDAP_NOT_ALLOWED_ON_NONLEAF; |
91 |
|
|
goto done; |
92 |
|
|
} |
93 |
|
|
|
94 |
|
|
if (namespace_del(ns, dn) == 0 && namespace_commit(ns) == 0) |
95 |
|
|
rc = LDAP_SUCCESS; |
96 |
|
|
|
97 |
|
|
done: |
98 |
|
|
btree_cursor_close(cursor); |
99 |
|
|
btval_reset(&key); |
100 |
|
|
namespace_abort(ns); |
101 |
|
|
return ldap_respond(req, rc); |
102 |
|
|
} |
103 |
|
|
|
104 |
|
|
int |
105 |
|
|
ldap_add(struct request *req) |
106 |
|
|
{ |
107 |
|
|
char uuid_str[64]; |
108 |
|
|
struct uuid uuid; |
109 |
|
|
char *dn, *s; |
110 |
|
|
struct attr_type *at; |
111 |
|
|
struct ber_element *attrs, *attr, *elm, *set = NULL; |
112 |
|
|
struct namespace *ns; |
113 |
|
|
struct referrals *refs; |
114 |
|
|
int rc; |
115 |
|
|
|
116 |
|
|
++stats.req_mod; |
117 |
|
|
|
118 |
|
|
if (ber_scanf_elements(req->op, "{se", &dn, &attrs) != 0) |
119 |
|
|
return ldap_respond(req, LDAP_PROTOCOL_ERROR); |
120 |
|
|
|
121 |
|
|
normalize_dn(dn); |
122 |
|
|
log_debug("adding entry %s", dn); |
123 |
|
|
|
124 |
|
|
if (*dn == '\0') |
125 |
|
|
return ldap_respond(req, LDAP_INVALID_DN_SYNTAX); |
126 |
|
|
|
127 |
|
|
if ((ns = namespace_for_base(dn)) == NULL) { |
128 |
|
|
refs = namespace_referrals(dn); |
129 |
|
|
if (refs == NULL) |
130 |
|
|
return ldap_respond(req, LDAP_NAMING_VIOLATION); |
131 |
|
|
else |
132 |
|
|
return ldap_refer(req, dn, NULL, refs); |
133 |
|
|
} |
134 |
|
|
|
135 |
|
|
if (!authorized(req->conn, ns, ACI_WRITE, dn, LDAP_SCOPE_BASE)) |
136 |
|
|
return ldap_respond(req, LDAP_INSUFFICIENT_ACCESS); |
137 |
|
|
|
138 |
|
|
/* Check that we're not adding immutable attributes. |
139 |
|
|
*/ |
140 |
|
|
for (elm = attrs->be_sub; elm != NULL; elm = elm->be_next) { |
141 |
|
|
attr = elm->be_sub; |
142 |
|
|
if (attr == NULL || ber_get_string(attr, &s) != 0) |
143 |
|
|
return ldap_respond(req, LDAP_PROTOCOL_ERROR); |
144 |
|
|
if (!ns->relax) { |
145 |
|
|
at = lookup_attribute(conf->schema, s); |
146 |
|
|
if (at == NULL) { |
147 |
|
|
log_debug("unknown attribute type %s", s); |
148 |
|
|
return ldap_respond(req, |
149 |
|
|
LDAP_NO_SUCH_ATTRIBUTE); |
150 |
|
|
} |
151 |
|
|
if (at->immutable) { |
152 |
|
|
log_debug("attempt to add immutable" |
153 |
|
|
" attribute %s", s); |
154 |
|
|
return ldap_respond(req, |
155 |
|
|
LDAP_CONSTRAINT_VIOLATION); |
156 |
|
|
} |
157 |
|
|
} |
158 |
|
|
} |
159 |
|
|
|
160 |
|
|
if (namespace_begin(ns) == -1) { |
161 |
|
|
if (errno == EBUSY) { |
162 |
|
|
if (namespace_queue_request(ns, req) != 0) |
163 |
|
|
return ldap_respond(req, LDAP_BUSY); |
164 |
|
|
return LDAP_BUSY; |
165 |
|
|
} |
166 |
|
|
return ldap_respond(req, LDAP_OTHER); |
167 |
|
|
} |
168 |
|
|
|
169 |
|
|
/* add operational attributes |
170 |
|
|
*/ |
171 |
|
|
if ((set = ber_add_set(NULL)) == NULL) |
172 |
|
|
goto fail; |
173 |
|
|
if (ber_add_string(set, req->conn->binddn ? req->conn->binddn : "") == NULL) |
174 |
|
|
goto fail; |
175 |
|
|
if (ldap_add_attribute(attrs, "creatorsName", set) == NULL) |
176 |
|
|
goto fail; |
177 |
|
|
|
178 |
|
|
if ((set = ber_add_set(NULL)) == NULL) |
179 |
|
|
goto fail; |
180 |
|
|
if (ber_add_string(set, ldap_now()) == NULL) |
181 |
|
|
goto fail; |
182 |
|
|
if (ldap_add_attribute(attrs, "createTimestamp", set) == NULL) |
183 |
|
|
goto fail; |
184 |
|
|
|
185 |
|
|
uuid_create(&uuid); |
186 |
|
|
uuid_to_string(&uuid, uuid_str, sizeof(uuid_str)); |
187 |
|
|
if ((set = ber_add_set(NULL)) == NULL) |
188 |
|
|
goto fail; |
189 |
|
|
if (ber_add_string(set, uuid_str) == NULL) |
190 |
|
|
goto fail; |
191 |
|
|
if (ldap_add_attribute(attrs, "entryUUID", set) == NULL) |
192 |
|
|
goto fail; |
193 |
|
|
|
194 |
|
|
if ((rc = validate_entry(dn, attrs, ns->relax)) != LDAP_SUCCESS || |
195 |
|
|
namespace_add(ns, dn, attrs) != 0) { |
196 |
|
|
namespace_abort(ns); |
197 |
|
|
if (rc == LDAP_SUCCESS && errno == EEXIST) |
198 |
|
|
rc = LDAP_ALREADY_EXISTS; |
199 |
|
|
else if (rc == LDAP_SUCCESS) |
200 |
|
|
rc = LDAP_OTHER; |
201 |
|
|
} else if (namespace_commit(ns) != 0) |
202 |
|
|
rc = LDAP_OTHER; |
203 |
|
|
|
204 |
|
|
return ldap_respond(req, rc); |
205 |
|
|
|
206 |
|
|
fail: |
207 |
|
|
if (set != NULL) |
208 |
|
|
ber_free_elements(set); |
209 |
|
|
namespace_abort(ns); |
210 |
|
|
return ldap_respond(req, LDAP_OTHER); |
211 |
|
|
} |
212 |
|
|
|
213 |
|
|
int |
214 |
|
|
ldap_modify(struct request *req) |
215 |
|
|
{ |
216 |
|
|
int rc = LDAP_OTHER; |
217 |
|
|
char *dn; |
218 |
|
|
long long op; |
219 |
|
|
char *attr; |
220 |
|
|
struct ber_element *mods, *entry, *mod, *a, *set; |
221 |
|
|
struct ber_element *vals = NULL, *prev = NULL; |
222 |
|
|
struct namespace *ns; |
223 |
|
|
struct attr_type *at; |
224 |
|
|
struct referrals *refs; |
225 |
|
|
|
226 |
|
|
++stats.req_mod; |
227 |
|
|
|
228 |
|
|
if (ber_scanf_elements(req->op, "{se", &dn, &mods) != 0) |
229 |
|
|
return ldap_respond(req, LDAP_PROTOCOL_ERROR); |
230 |
|
|
|
231 |
|
|
normalize_dn(dn); |
232 |
|
|
log_debug("modifying dn %s", dn); |
233 |
|
|
|
234 |
|
|
if (*dn == 0) |
235 |
|
|
return ldap_respond(req, LDAP_INVALID_DN_SYNTAX); |
236 |
|
|
|
237 |
|
|
if ((ns = namespace_for_base(dn)) == NULL) { |
238 |
|
|
refs = namespace_referrals(dn); |
239 |
|
|
if (refs == NULL) |
240 |
|
|
return ldap_respond(req, LDAP_NAMING_VIOLATION); |
241 |
|
|
else |
242 |
|
|
return ldap_refer(req, dn, NULL, refs); |
243 |
|
|
} |
244 |
|
|
|
245 |
|
|
if (!authorized(req->conn, ns, ACI_WRITE, dn, LDAP_SCOPE_BASE)) |
246 |
|
|
return ldap_respond(req, LDAP_INSUFFICIENT_ACCESS); |
247 |
|
|
|
248 |
|
|
if (namespace_begin(ns) == -1) { |
249 |
|
|
if (errno == EBUSY) { |
250 |
|
|
if (namespace_queue_request(ns, req) != 0) |
251 |
|
|
return ldap_respond(req, LDAP_BUSY); |
252 |
|
|
return LDAP_BUSY; |
253 |
|
|
} |
254 |
|
|
return ldap_respond(req, LDAP_OTHER); |
255 |
|
|
} |
256 |
|
|
|
257 |
|
|
if ((entry = namespace_get(ns, dn)) == NULL) { |
258 |
|
|
rc = LDAP_NO_SUCH_OBJECT; |
259 |
|
|
goto done; |
260 |
|
|
} |
261 |
|
|
|
262 |
|
|
for (mod = mods->be_sub; mod; mod = mod->be_next) { |
263 |
|
|
if (ber_scanf_elements(mod, "{E{ese(", &op, &prev, &attr, &vals) != 0) { |
264 |
|
|
rc = LDAP_PROTOCOL_ERROR; |
265 |
|
|
vals = NULL; |
266 |
|
|
goto done; |
267 |
|
|
} |
268 |
|
|
|
269 |
|
|
prev->be_next = NULL; |
270 |
|
|
|
271 |
|
|
if (!ns->relax) { |
272 |
|
|
at = lookup_attribute(conf->schema, attr); |
273 |
|
|
if (at == NULL) { |
274 |
|
|
log_debug("unknown attribute type %s", attr); |
275 |
|
|
rc = LDAP_NO_SUCH_ATTRIBUTE; |
276 |
|
|
goto done; |
277 |
|
|
} |
278 |
|
|
if (at->immutable) { |
279 |
|
|
log_debug("attempt to modify immutable" |
280 |
|
|
" attribute %s", attr); |
281 |
|
|
rc = LDAP_CONSTRAINT_VIOLATION; |
282 |
|
|
goto done; |
283 |
|
|
} |
284 |
|
|
} |
285 |
|
|
|
286 |
|
|
a = ldap_get_attribute(entry, attr); |
287 |
|
|
|
288 |
|
|
switch (op) { |
289 |
|
|
case LDAP_MOD_ADD: |
290 |
|
|
if (a == NULL) { |
291 |
|
|
if (ldap_add_attribute(entry, attr, vals) != NULL) |
292 |
|
|
vals = NULL; |
293 |
|
|
} else { |
294 |
|
|
if (ldap_merge_values(a, vals) == 0) |
295 |
|
|
vals = NULL; |
296 |
|
|
} |
297 |
|
|
break; |
298 |
|
|
case LDAP_MOD_DELETE: |
299 |
|
|
/* |
300 |
|
|
* We're already in the "SET OF value |
301 |
|
|
* AttributeValue" (see RFC2411 section |
302 |
|
|
* 4.1.7) have either EOC, so all values |
303 |
|
|
* for the attribute gets deleted, or we |
304 |
|
|
* have a (first) octetstring (there is one |
305 |
|
|
* for each AttributeValue to be deleted) |
306 |
|
|
*/ |
307 |
|
|
if (vals->be_sub && |
308 |
|
|
vals->be_sub->be_type == BER_TYPE_OCTETSTRING) { |
309 |
|
|
ldap_del_values(a, vals); |
310 |
|
|
} else { |
311 |
|
|
ldap_del_attribute(entry, attr); |
312 |
|
|
} |
313 |
|
|
break; |
314 |
|
|
case LDAP_MOD_REPLACE: |
315 |
|
|
if (vals->be_sub != NULL && |
316 |
|
|
vals->be_sub->be_type != BER_TYPE_EOC) { |
317 |
|
|
if (a == NULL) { |
318 |
|
|
if (ldap_add_attribute(entry, attr, vals) != NULL) |
319 |
|
|
vals = NULL; |
320 |
|
|
} else { |
321 |
|
|
if (ldap_set_values(a, vals) == 0) |
322 |
|
|
vals = NULL; |
323 |
|
|
} |
324 |
|
|
} else if (a != NULL) |
325 |
|
|
ldap_del_attribute(entry, attr); |
326 |
|
|
break; |
327 |
|
|
} |
328 |
|
|
|
329 |
|
|
if (vals != NULL) { |
330 |
|
|
ber_free_elements(vals); |
331 |
|
|
vals = NULL; |
332 |
|
|
} |
333 |
|
|
} |
334 |
|
|
|
335 |
|
|
if ((rc = validate_entry(dn, entry, ns->relax)) != LDAP_SUCCESS) |
336 |
|
|
goto done; |
337 |
|
|
|
338 |
|
|
set = ber_add_set(NULL); |
339 |
|
|
ber_add_string(set, req->conn->binddn ? req->conn->binddn : ""); |
340 |
|
|
if ((a = ldap_get_attribute(entry, "modifiersName")) != NULL) |
341 |
|
|
ldap_set_values(a, set); |
342 |
|
|
else |
343 |
|
|
ldap_add_attribute(entry, "modifiersName", set); |
344 |
|
|
|
345 |
|
|
set = ber_add_set(NULL); |
346 |
|
|
ber_add_string(set, ldap_now()); |
347 |
|
|
if ((a = ldap_get_attribute(entry, "modifyTimestamp")) != NULL) |
348 |
|
|
ldap_set_values(a, set); |
349 |
|
|
else |
350 |
|
|
ldap_add_attribute(entry, "modifyTimestamp", set); |
351 |
|
|
|
352 |
|
|
if (namespace_update(ns, dn, entry) == 0 && namespace_commit(ns) == 0) |
353 |
|
|
rc = LDAP_SUCCESS; |
354 |
|
|
else |
355 |
|
|
rc = LDAP_OTHER; |
356 |
|
|
|
357 |
|
|
done: |
358 |
|
|
if (vals != NULL) |
359 |
|
|
ber_free_elements(vals); |
360 |
|
|
namespace_abort(ns); |
361 |
|
|
return ldap_respond(req, rc); |
362 |
|
|
} |
363 |
|
|
|