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