1*5387241fSclaudio /* $OpenBSD: modify.c,v 1.24 2021/12/20 13:26:11 claudio Exp $ */
25d465952Smartinh
35d465952Smartinh /*
45d465952Smartinh * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se>
55d465952Smartinh *
65d465952Smartinh * Permission to use, copy, modify, and distribute this software for any
75d465952Smartinh * purpose with or without fee is hereby granted, provided that the above
85d465952Smartinh * copyright notice and this permission notice appear in all copies.
95d465952Smartinh *
105d465952Smartinh * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
115d465952Smartinh * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
125d465952Smartinh * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
135d465952Smartinh * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
145d465952Smartinh * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
155d465952Smartinh * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
165d465952Smartinh * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
175d465952Smartinh */
185d465952Smartinh
195d465952Smartinh #include <sys/types.h>
205d465952Smartinh #include <sys/queue.h>
215d465952Smartinh
225d465952Smartinh #include <assert.h>
230279e526Smartinh #include <errno.h>
245d465952Smartinh #include <stdlib.h>
255d465952Smartinh #include <string.h>
265d465952Smartinh
275d465952Smartinh #include "ldapd.h"
28fdd30f56Sbenno #include "log.h"
295d465952Smartinh #include "uuid.h"
305d465952Smartinh
315d465952Smartinh int
ldap_delete(struct request * req)325d465952Smartinh ldap_delete(struct request *req)
335d465952Smartinh {
3486f37e34Smartinh struct btval key;
35841a002bSreyk char *dn, *s;
365d465952Smartinh struct namespace *ns;
3738c09006Smartinh struct referrals *refs;
3886f37e34Smartinh struct cursor *cursor;
39841a002bSreyk struct ber_element *entry, *elm, *a;
40653ab0a7Smartinh int rc = LDAP_OTHER;
415d465952Smartinh
425d465952Smartinh ++stats.req_mod;
435d465952Smartinh
44696b5899Stb if (ober_scanf_elements(req->op, "s", &dn) != 0)
455d465952Smartinh return ldap_respond(req, LDAP_PROTOCOL_ERROR);
465d465952Smartinh
475d465952Smartinh normalize_dn(dn);
485d465952Smartinh log_debug("deleting entry %s", dn);
495d465952Smartinh
5038c09006Smartinh if ((ns = namespace_for_base(dn)) == NULL) {
5138c09006Smartinh refs = namespace_referrals(dn);
5238c09006Smartinh if (refs == NULL)
535d465952Smartinh return ldap_respond(req, LDAP_NAMING_VIOLATION);
5438c09006Smartinh else
5538c09006Smartinh return ldap_refer(req, dn, NULL, refs);
5638c09006Smartinh }
575d465952Smartinh
58841a002bSreyk if (!authorized(req->conn, ns, ACI_WRITE, dn, NULL, LDAP_SCOPE_BASE))
595d465952Smartinh return ldap_respond(req, LDAP_INSUFFICIENT_ACCESS);
605d465952Smartinh
610279e526Smartinh if (namespace_begin(ns) != 0) {
620279e526Smartinh if (errno == EBUSY) {
635d465952Smartinh if (namespace_queue_request(ns, req) != 0)
645d465952Smartinh return ldap_respond(req, LDAP_BUSY);
655d465952Smartinh return LDAP_BUSY;
660279e526Smartinh }
67a9ac9ba1Smartinh return ldap_respond(req, LDAP_OTHER);
685d465952Smartinh }
69a9ac9ba1Smartinh
7086f37e34Smartinh /* Check that this is a leaf node by getting a cursor to the DN
7186f37e34Smartinh * we're about to delete. If there is a next entry with the DN
7286f37e34Smartinh * as suffix (ie, a child node), the DN can't be deleted.
7386f37e34Smartinh */
7486f37e34Smartinh if ((cursor = btree_txn_cursor_open(NULL, ns->data_txn)) == NULL)
75653ab0a7Smartinh goto done;
7686f37e34Smartinh
7737f4b933Smmcc memset(&key, 0, sizeof(key));
7886f37e34Smartinh key.data = dn;
7986f37e34Smartinh key.size = strlen(dn);
80653ab0a7Smartinh if (btree_cursor_get(cursor, &key, NULL, BT_CURSOR_EXACT) != 0) {
81653ab0a7Smartinh if (errno == ENOENT)
82653ab0a7Smartinh rc = LDAP_NO_SUCH_OBJECT;
83653ab0a7Smartinh goto done;
84653ab0a7Smartinh }
85653ab0a7Smartinh
866a906d33Smartinh btval_reset(&key);
8786f37e34Smartinh if (btree_cursor_get(cursor, &key, NULL, BT_NEXT) != 0) {
8886f37e34Smartinh if (errno != ENOENT)
89653ab0a7Smartinh goto done;
9086f37e34Smartinh } else if (has_suffix(&key, dn)) {
91653ab0a7Smartinh rc = LDAP_NOT_ALLOWED_ON_NONLEAF;
92653ab0a7Smartinh goto done;
9386f37e34Smartinh }
9486f37e34Smartinh
95841a002bSreyk if ((entry = namespace_get(ns, dn)) == NULL) {
96841a002bSreyk rc = LDAP_NO_SUCH_OBJECT;
97841a002bSreyk goto done;
98841a002bSreyk }
99841a002bSreyk
100841a002bSreyk /* Fail if this leaf node includes non-writeable attributes */
101841a002bSreyk if (entry->be_encoding != BER_TYPE_SEQUENCE)
102841a002bSreyk goto done;
103841a002bSreyk for (elm = entry->be_sub; elm != NULL; elm = elm->be_next) {
104841a002bSreyk a = elm->be_sub;
105696b5899Stb if (a && ober_get_string(a, &s) == 0 &&
106841a002bSreyk !authorized(req->conn, ns, ACI_WRITE, dn, s,
107841a002bSreyk LDAP_SCOPE_BASE)) {
108841a002bSreyk rc = LDAP_INSUFFICIENT_ACCESS;
109841a002bSreyk goto done;
110841a002bSreyk }
111841a002bSreyk }
112841a002bSreyk
1131761b184Smartinh if (namespace_del(ns, dn) == 0 && namespace_commit(ns) == 0)
114653ab0a7Smartinh rc = LDAP_SUCCESS;
11586f37e34Smartinh
116653ab0a7Smartinh done:
117653ab0a7Smartinh btree_cursor_close(cursor);
1186a906d33Smartinh btval_reset(&key);
1190279e526Smartinh namespace_abort(ns);
120653ab0a7Smartinh return ldap_respond(req, rc);
1210279e526Smartinh }
1225d465952Smartinh
1235d465952Smartinh int
ldap_add(struct request * req)1245d465952Smartinh ldap_add(struct request *req)
1255d465952Smartinh {
1265d465952Smartinh char uuid_str[64];
1275d465952Smartinh struct uuid uuid;
128a2c61c8bSmartinh char *dn, *s;
129a2c61c8bSmartinh struct attr_type *at;
130a2c61c8bSmartinh struct ber_element *attrs, *attr, *elm, *set = NULL;
1315d465952Smartinh struct namespace *ns;
13238c09006Smartinh struct referrals *refs;
1335d465952Smartinh int rc;
1345d465952Smartinh
1355d465952Smartinh ++stats.req_mod;
1365d465952Smartinh
137696b5899Stb if (ober_scanf_elements(req->op, "{se", &dn, &attrs) != 0)
1385d465952Smartinh return ldap_respond(req, LDAP_PROTOCOL_ERROR);
1395d465952Smartinh
1405d465952Smartinh normalize_dn(dn);
1415d465952Smartinh log_debug("adding entry %s", dn);
1425d465952Smartinh
1435d465952Smartinh if (*dn == '\0')
1445d465952Smartinh return ldap_respond(req, LDAP_INVALID_DN_SYNTAX);
1455d465952Smartinh
14638c09006Smartinh if ((ns = namespace_for_base(dn)) == NULL) {
14738c09006Smartinh refs = namespace_referrals(dn);
14838c09006Smartinh if (refs == NULL)
1495d465952Smartinh return ldap_respond(req, LDAP_NAMING_VIOLATION);
15038c09006Smartinh else
15138c09006Smartinh return ldap_refer(req, dn, NULL, refs);
15238c09006Smartinh }
1535d465952Smartinh
154841a002bSreyk if (!authorized(req->conn, ns, ACI_WRITE, dn, NULL, LDAP_SCOPE_BASE))
1555d465952Smartinh return ldap_respond(req, LDAP_INSUFFICIENT_ACCESS);
1565d465952Smartinh
157a2c61c8bSmartinh /* Check that we're not adding immutable attributes.
158a2c61c8bSmartinh */
159a2c61c8bSmartinh for (elm = attrs->be_sub; elm != NULL; elm = elm->be_next) {
160a2c61c8bSmartinh attr = elm->be_sub;
161696b5899Stb if (attr == NULL || ober_get_string(attr, &s) != 0)
162a2c61c8bSmartinh return ldap_respond(req, LDAP_PROTOCOL_ERROR);
163841a002bSreyk if (!authorized(req->conn, ns, ACI_WRITE, dn, s,
164841a002bSreyk LDAP_SCOPE_BASE))
165841a002bSreyk return ldap_respond(req, LDAP_INSUFFICIENT_ACCESS);
1660354f496Smartinh if (!ns->relax) {
167a2c61c8bSmartinh at = lookup_attribute(conf->schema, s);
168a2c61c8bSmartinh if (at == NULL) {
169a2c61c8bSmartinh log_debug("unknown attribute type %s", s);
1700354f496Smartinh return ldap_respond(req,
1710354f496Smartinh LDAP_NO_SUCH_ATTRIBUTE);
172a2c61c8bSmartinh }
173a2c61c8bSmartinh if (at->immutable) {
1740354f496Smartinh log_debug("attempt to add immutable"
1750354f496Smartinh " attribute %s", s);
1760354f496Smartinh return ldap_respond(req,
1770354f496Smartinh LDAP_CONSTRAINT_VIOLATION);
1780354f496Smartinh }
179a2c61c8bSmartinh }
180a2c61c8bSmartinh }
181a2c61c8bSmartinh
1820279e526Smartinh if (namespace_begin(ns) == -1) {
1830279e526Smartinh if (errno == EBUSY) {
1845d465952Smartinh if (namespace_queue_request(ns, req) != 0)
1855d465952Smartinh return ldap_respond(req, LDAP_BUSY);
1865d465952Smartinh return LDAP_BUSY;
1870279e526Smartinh }
188a9ac9ba1Smartinh return ldap_respond(req, LDAP_OTHER);
1890279e526Smartinh }
1905d465952Smartinh
1915d465952Smartinh /* add operational attributes
1925d465952Smartinh */
193696b5899Stb if ((set = ober_add_set(NULL)) == NULL)
194a2c61c8bSmartinh goto fail;
195696b5899Stb if (ober_add_string(set, req->conn->binddn ? req->conn->binddn : "") == NULL)
196a2c61c8bSmartinh goto fail;
197a2c61c8bSmartinh if (ldap_add_attribute(attrs, "creatorsName", set) == NULL)
198a2c61c8bSmartinh goto fail;
1995d465952Smartinh
200696b5899Stb if ((set = ober_add_set(NULL)) == NULL)
201a2c61c8bSmartinh goto fail;
202696b5899Stb if (ober_add_string(set, ldap_now()) == NULL)
203a2c61c8bSmartinh goto fail;
204a2c61c8bSmartinh if (ldap_add_attribute(attrs, "createTimestamp", set) == NULL)
205a2c61c8bSmartinh goto fail;
2065d465952Smartinh
2075d465952Smartinh uuid_create(&uuid);
2085d465952Smartinh uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
209696b5899Stb if ((set = ober_add_set(NULL)) == NULL)
210a2c61c8bSmartinh goto fail;
211696b5899Stb if (ober_add_string(set, uuid_str) == NULL)
212a2c61c8bSmartinh goto fail;
213a2c61c8bSmartinh if (ldap_add_attribute(attrs, "entryUUID", set) == NULL)
214a2c61c8bSmartinh goto fail;
2155d465952Smartinh
2160279e526Smartinh if ((rc = validate_entry(dn, attrs, ns->relax)) != LDAP_SUCCESS ||
2170279e526Smartinh namespace_add(ns, dn, attrs) != 0) {
2180279e526Smartinh namespace_abort(ns);
2190279e526Smartinh if (rc == LDAP_SUCCESS && errno == EEXIST)
220a9ac9ba1Smartinh rc = LDAP_ALREADY_EXISTS;
221639fdb7dSmartinh else if (rc == LDAP_SUCCESS)
222a9ac9ba1Smartinh rc = LDAP_OTHER;
2230279e526Smartinh } else if (namespace_commit(ns) != 0)
2240279e526Smartinh rc = LDAP_OTHER;
225a9ac9ba1Smartinh
2260279e526Smartinh return ldap_respond(req, rc);
227a2c61c8bSmartinh
228a2c61c8bSmartinh fail:
229a2c61c8bSmartinh if (set != NULL)
230696b5899Stb ober_free_elements(set);
231a2c61c8bSmartinh namespace_abort(ns);
232a2c61c8bSmartinh return ldap_respond(req, LDAP_OTHER);
2335d465952Smartinh }
2345d465952Smartinh
2355d465952Smartinh int
ldap_modify(struct request * req)2365d465952Smartinh ldap_modify(struct request *req)
2375d465952Smartinh {
2381761b184Smartinh int rc = LDAP_OTHER;
2395d465952Smartinh char *dn;
2405d465952Smartinh long long op;
241a2a43363Smartinh char *attr;
242de677adcSpelikan struct ber_element *mods, *entry, *mod, *a, *set;
243de677adcSpelikan struct ber_element *vals = NULL, *prev = NULL;
2445d465952Smartinh struct namespace *ns;
2455d465952Smartinh struct attr_type *at;
24638c09006Smartinh struct referrals *refs;
2475d465952Smartinh
2485d465952Smartinh ++stats.req_mod;
2495d465952Smartinh
250696b5899Stb if (ober_scanf_elements(req->op, "{se", &dn, &mods) != 0)
2515d465952Smartinh return ldap_respond(req, LDAP_PROTOCOL_ERROR);
2525d465952Smartinh
2535d465952Smartinh normalize_dn(dn);
2545d465952Smartinh log_debug("modifying dn %s", dn);
2555d465952Smartinh
2565d465952Smartinh if (*dn == 0)
2575d465952Smartinh return ldap_respond(req, LDAP_INVALID_DN_SYNTAX);
2585d465952Smartinh
25938c09006Smartinh if ((ns = namespace_for_base(dn)) == NULL) {
26038c09006Smartinh refs = namespace_referrals(dn);
26138c09006Smartinh if (refs == NULL)
2625d465952Smartinh return ldap_respond(req, LDAP_NAMING_VIOLATION);
26338c09006Smartinh else
26438c09006Smartinh return ldap_refer(req, dn, NULL, refs);
26538c09006Smartinh }
2665d465952Smartinh
267841a002bSreyk /* Check authorization for each mod to consider attributes */
268841a002bSreyk for (mod = mods->be_sub; mod; mod = mod->be_next) {
269696b5899Stb if (ober_scanf_elements(mod, "{E{es", &op, &prev, &attr) != 0)
270841a002bSreyk return ldap_respond(req, LDAP_PROTOCOL_ERROR);
271841a002bSreyk if (!authorized(req->conn, ns, ACI_WRITE, dn, attr,
272841a002bSreyk LDAP_SCOPE_BASE))
2735d465952Smartinh return ldap_respond(req, LDAP_INSUFFICIENT_ACCESS);
274841a002bSreyk }
2755d465952Smartinh
2760279e526Smartinh if (namespace_begin(ns) == -1) {
2770279e526Smartinh if (errno == EBUSY) {
2785d465952Smartinh if (namespace_queue_request(ns, req) != 0)
2795d465952Smartinh return ldap_respond(req, LDAP_BUSY);
2805d465952Smartinh return LDAP_BUSY;
2810279e526Smartinh }
282a9ac9ba1Smartinh return ldap_respond(req, LDAP_OTHER);
2830279e526Smartinh }
284a9ac9ba1Smartinh
285a9ac9ba1Smartinh if ((entry = namespace_get(ns, dn)) == NULL) {
286a9ac9ba1Smartinh rc = LDAP_NO_SUCH_OBJECT;
287a9ac9ba1Smartinh goto done;
2885d465952Smartinh }
2895d465952Smartinh
2905d465952Smartinh for (mod = mods->be_sub; mod; mod = mod->be_next) {
291696b5899Stb if (ober_scanf_elements(mod, "{E{ese(", &op, &prev, &attr, &vals) != 0) {
292a9ac9ba1Smartinh rc = LDAP_PROTOCOL_ERROR;
293f12396bdSmartinh vals = NULL;
294a9ac9ba1Smartinh goto done;
295a9ac9ba1Smartinh }
2965d465952Smartinh
297f12396bdSmartinh prev->be_next = NULL;
298f12396bdSmartinh
2990354f496Smartinh if (!ns->relax) {
3000354f496Smartinh at = lookup_attribute(conf->schema, attr);
3010354f496Smartinh if (at == NULL) {
3025d465952Smartinh log_debug("unknown attribute type %s", attr);
303a9ac9ba1Smartinh rc = LDAP_NO_SUCH_ATTRIBUTE;
304a9ac9ba1Smartinh goto done;
3055d465952Smartinh }
3060354f496Smartinh if (at->immutable) {
3070354f496Smartinh log_debug("attempt to modify immutable"
3080354f496Smartinh " attribute %s", attr);
309a9ac9ba1Smartinh rc = LDAP_CONSTRAINT_VIOLATION;
310a9ac9ba1Smartinh goto done;
3115d465952Smartinh }
3120354f496Smartinh }
3135d465952Smartinh
3145d465952Smartinh a = ldap_get_attribute(entry, attr);
3155d465952Smartinh
3165d465952Smartinh switch (op) {
3175d465952Smartinh case LDAP_MOD_ADD:
318f12396bdSmartinh if (a == NULL) {
319f12396bdSmartinh if (ldap_add_attribute(entry, attr, vals) != NULL)
320f12396bdSmartinh vals = NULL;
321f12396bdSmartinh } else {
322ad54cec9Smartinh if (ldap_merge_values(a, vals) == 0)
323f12396bdSmartinh vals = NULL;
324f12396bdSmartinh }
3255d465952Smartinh break;
3265d465952Smartinh case LDAP_MOD_DELETE:
3277c698af4Sguenther /*
3287c698af4Sguenther * We're already in the "SET OF value
3297e500a57Srob * AttributeValue" (see RFC4511 section
3307e500a57Srob * 4.1.7) have either no content, so all values
3317c698af4Sguenther * for the attribute gets deleted, or we
3327c698af4Sguenther * have a (first) octetstring (there is one
3337c698af4Sguenther * for each AttributeValue to be deleted)
3347c698af4Sguenther */
3355d465952Smartinh if (vals->be_sub &&
3367c698af4Sguenther vals->be_sub->be_type == BER_TYPE_OCTETSTRING) {
337*5387241fSclaudio if (ldap_del_values(a, vals) == 1)
338*5387241fSclaudio ldap_del_attribute(entry, attr);
3397c698af4Sguenther } else {
3405d465952Smartinh ldap_del_attribute(entry, attr);
3417c698af4Sguenther }
3425d465952Smartinh break;
3435d465952Smartinh case LDAP_MOD_REPLACE:
344ad54cec9Smartinh if (vals->be_sub != NULL &&
345ad54cec9Smartinh vals->be_sub->be_type != BER_TYPE_EOC) {
346f12396bdSmartinh if (a == NULL) {
347f12396bdSmartinh if (ldap_add_attribute(entry, attr, vals) != NULL)
348f12396bdSmartinh vals = NULL;
349f12396bdSmartinh } else {
350ad54cec9Smartinh if (ldap_set_values(a, vals) == 0)
351f12396bdSmartinh vals = NULL;
352f12396bdSmartinh }
353ad54cec9Smartinh } else if (a != NULL)
3545d465952Smartinh ldap_del_attribute(entry, attr);
3555d465952Smartinh break;
3565d465952Smartinh }
357f12396bdSmartinh
358f12396bdSmartinh if (vals != NULL) {
359696b5899Stb ober_free_elements(vals);
360f12396bdSmartinh vals = NULL;
361f12396bdSmartinh }
3625d465952Smartinh }
3635d465952Smartinh
3645d465952Smartinh if ((rc = validate_entry(dn, entry, ns->relax)) != LDAP_SUCCESS)
365a9ac9ba1Smartinh goto done;
3665d465952Smartinh
367696b5899Stb set = ober_add_set(NULL);
368696b5899Stb ober_add_string(set, req->conn->binddn ? req->conn->binddn : "");
3695d465952Smartinh if ((a = ldap_get_attribute(entry, "modifiersName")) != NULL)
3705d465952Smartinh ldap_set_values(a, set);
3715d465952Smartinh else
3725d465952Smartinh ldap_add_attribute(entry, "modifiersName", set);
3735d465952Smartinh
374696b5899Stb set = ober_add_set(NULL);
375696b5899Stb ober_add_string(set, ldap_now());
3765d465952Smartinh if ((a = ldap_get_attribute(entry, "modifyTimestamp")) != NULL)
3775d465952Smartinh ldap_set_values(a, set);
3785d465952Smartinh else
3795d465952Smartinh ldap_add_attribute(entry, "modifyTimestamp", set);
3805d465952Smartinh
3811761b184Smartinh if (namespace_update(ns, dn, entry) == 0 && namespace_commit(ns) == 0)
382a9ac9ba1Smartinh rc = LDAP_SUCCESS;
3830279e526Smartinh else
384a9ac9ba1Smartinh rc = LDAP_OTHER;
385a9ac9ba1Smartinh
386a9ac9ba1Smartinh done:
387f12396bdSmartinh if (vals != NULL)
388696b5899Stb ober_free_elements(vals);
3890279e526Smartinh namespace_abort(ns);
3901761b184Smartinh return ldap_respond(req, rc);
3915d465952Smartinh }
3925d465952Smartinh
393