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