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