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 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 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 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