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