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