xref: /openbsd/usr.sbin/ldapd/modify.c (revision 264ca280)
1 /*	$OpenBSD: modify.c,v 1.17 2015/12/24 17:47:57 mmcc 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 	memset(&key, 0, 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 ? 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, *a, *set;
220 	struct ber_element	*vals = NULL, *prev = NULL;
221 	struct namespace	*ns;
222 	struct attr_type	*at;
223 	struct referrals	*refs;
224 
225 	++stats.req_mod;
226 
227 	if (ber_scanf_elements(req->op, "{se", &dn, &mods) != 0)
228 		return ldap_respond(req, LDAP_PROTOCOL_ERROR);
229 
230 	normalize_dn(dn);
231 	log_debug("modifying dn %s", dn);
232 
233 	if (*dn == 0)
234 		return ldap_respond(req, LDAP_INVALID_DN_SYNTAX);
235 
236 	if ((ns = namespace_for_base(dn)) == NULL) {
237 		refs = namespace_referrals(dn);
238 		if (refs == NULL)
239 			return ldap_respond(req, LDAP_NAMING_VIOLATION);
240 		else
241 			return ldap_refer(req, dn, NULL, refs);
242 	}
243 
244 	if (!authorized(req->conn, ns, ACI_WRITE, dn, LDAP_SCOPE_BASE) != 0)
245 		return ldap_respond(req, LDAP_INSUFFICIENT_ACCESS);
246 
247 	if (namespace_begin(ns) == -1) {
248 		if (errno == EBUSY) {
249 			if (namespace_queue_request(ns, req) != 0)
250 				return ldap_respond(req, LDAP_BUSY);
251 			return LDAP_BUSY;
252 		}
253 		return ldap_respond(req, LDAP_OTHER);
254 	}
255 
256 	if ((entry = namespace_get(ns, dn)) == NULL) {
257 		rc = LDAP_NO_SUCH_OBJECT;
258 		goto done;
259 	}
260 
261 	for (mod = mods->be_sub; mod; mod = mod->be_next) {
262 		if (ber_scanf_elements(mod, "{E{ese(", &op, &prev, &attr, &vals) != 0) {
263 			rc = LDAP_PROTOCOL_ERROR;
264 			vals = NULL;
265 			goto done;
266 		}
267 
268 		prev->be_next = NULL;
269 
270 		if (!ns->relax) {
271 			at = lookup_attribute(conf->schema, attr);
272 			if (at == NULL) {
273 				log_debug("unknown attribute type %s", attr);
274 				rc = LDAP_NO_SUCH_ATTRIBUTE;
275 				goto done;
276 			}
277 			if (at->immutable) {
278 				log_debug("attempt to modify immutable"
279 				    " attribute %s", attr);
280 				rc = LDAP_CONSTRAINT_VIOLATION;
281 				goto done;
282 			}
283 		}
284 
285 		a = ldap_get_attribute(entry, attr);
286 
287 		switch (op) {
288 		case LDAP_MOD_ADD:
289 			if (a == NULL) {
290 				if (ldap_add_attribute(entry, attr, vals) != NULL)
291 					vals = NULL;
292 			} else {
293 				if (ldap_merge_values(a, vals) == 0)
294 					vals = NULL;
295 			}
296 			break;
297 		case LDAP_MOD_DELETE:
298 			if (vals->be_sub &&
299 			    vals->be_sub->be_type == BER_TYPE_SET)
300 				ldap_del_values(a, vals);
301 			else
302 				ldap_del_attribute(entry, attr);
303 			break;
304 		case LDAP_MOD_REPLACE:
305 			if (vals->be_sub != NULL &&
306 			    vals->be_sub->be_type != BER_TYPE_EOC) {
307 				if (a == NULL) {
308 					if (ldap_add_attribute(entry, attr, vals) != NULL)
309 						vals = NULL;
310 				} else {
311 					if (ldap_set_values(a, vals) == 0)
312 						vals = NULL;
313 				}
314 			} else if (a != NULL)
315 				ldap_del_attribute(entry, attr);
316 			break;
317 		}
318 
319 		if (vals != NULL) {
320 			ber_free_elements(vals);
321 			vals = NULL;
322 		}
323 	}
324 
325 	if ((rc = validate_entry(dn, entry, ns->relax)) != LDAP_SUCCESS)
326 		goto done;
327 
328 	set = ber_add_set(NULL);
329 	ber_add_string(set, req->conn->binddn ? req->conn->binddn : "");
330 	if ((a = ldap_get_attribute(entry, "modifiersName")) != NULL)
331 		ldap_set_values(a, set);
332 	else
333 		ldap_add_attribute(entry, "modifiersName", set);
334 
335 	set = ber_add_set(NULL);
336 	ber_add_string(set, ldap_now());
337 	if ((a = ldap_get_attribute(entry, "modifyTimestamp")) != NULL)
338 		ldap_set_values(a, set);
339 	else
340 		ldap_add_attribute(entry, "modifyTimestamp", set);
341 
342 	if (namespace_update(ns, dn, entry) == 0 && namespace_commit(ns) == 0)
343 		rc = LDAP_SUCCESS;
344 	else
345 		rc = LDAP_OTHER;
346 
347 done:
348 	if (vals != NULL)
349 		ber_free_elements(vals);
350 	namespace_abort(ns);
351 	return ldap_respond(req, rc);
352 }
353 
354