xref: /openbsd/usr.sbin/ldapd/modify.c (revision 5387241f)
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