#include <sys/types.h>
#include <sys/queue.h>
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "ldapd.h"
#include "log.h"
#include "uuid.h"
int
ldap_delete(struct request *req)
{
struct btval key;
char *dn, *s;
struct namespace *ns;
struct referrals *refs;
struct cursor *cursor;
struct ber_element *entry = NULL, *elm, *a;
int rc = LDAP_OTHER;
++stats.req_mod;
if (ober_scanf_elements(req->op, "s", &dn) != 0)
return ldap_respond(req, LDAP_PROTOCOL_ERROR);
normalize_dn(dn);
log_debug("deleting entry %s", dn);
if ((ns = namespace_for_base(dn)) == NULL) {
refs = namespace_referrals(dn);
if (refs == NULL)
return ldap_respond(req, LDAP_NAMING_VIOLATION);
else
return ldap_refer(req, dn, NULL, refs);
}
if (!authorized(req->conn, ns, ACI_WRITE, dn, NULL, LDAP_SCOPE_BASE))
return ldap_respond(req, LDAP_INSUFFICIENT_ACCESS);
if (namespace_begin(ns) != 0) {
if (errno == EBUSY) {
if (namespace_queue_request(ns, req) != 0)
return ldap_respond(req, LDAP_BUSY);
return LDAP_BUSY;
}
return ldap_respond(req, LDAP_OTHER);
}
if ((cursor = btree_txn_cursor_open(NULL, ns->data_txn)) == NULL)
goto done;
memset(&key, 0, sizeof(key));
key.data = dn;
key.size = strlen(dn);
if (btree_cursor_get(cursor, &key, NULL, BT_CURSOR_EXACT) != 0) {
if (errno == ENOENT)
rc = LDAP_NO_SUCH_OBJECT;
goto done;
}
btval_reset(&key);
if (btree_cursor_get(cursor, &key, NULL, BT_NEXT) != 0) {
if (errno != ENOENT)
goto done;
} else if (has_suffix(&key, dn)) {
rc = LDAP_NOT_ALLOWED_ON_NONLEAF;
goto done;
}
if ((entry = namespace_get(ns, dn)) == NULL) {
rc = LDAP_NO_SUCH_OBJECT;
goto done;
}
if (entry->be_encoding != BER_TYPE_SEQUENCE)
goto done;
for (elm = entry->be_sub; elm != NULL; elm = elm->be_next) {
a = elm->be_sub;
if (a && ober_get_string(a, &s) == 0 &&
!authorized(req->conn, ns, ACI_WRITE, dn, s,
LDAP_SCOPE_BASE)) {
rc = LDAP_INSUFFICIENT_ACCESS;
goto done;
}
}
if (namespace_del(ns, dn) == 0 && namespace_commit(ns) == 0)
rc = LDAP_SUCCESS;
done:
ober_free_elements(entry);
btree_cursor_close(cursor);
btval_reset(&key);
namespace_abort(ns);
return ldap_respond(req, rc);
}
int
ldap_add(struct request *req)
{
char uuid_str[64];
struct uuid uuid;
char *dn, *s;
struct attr_type *at;
struct ber_element *attrs, *attr, *elm, *set = NULL;
struct namespace *ns;
struct referrals *refs;
int rc;
++stats.req_mod;
if (ober_scanf_elements(req->op, "{se", &dn, &attrs) != 0)
return ldap_respond(req, LDAP_PROTOCOL_ERROR);
normalize_dn(dn);
log_debug("adding entry %s", dn);
if (*dn == '\0')
return ldap_respond(req, LDAP_INVALID_DN_SYNTAX);
if ((ns = namespace_for_base(dn)) == NULL) {
refs = namespace_referrals(dn);
if (refs == NULL)
return ldap_respond(req, LDAP_NAMING_VIOLATION);
else
return ldap_refer(req, dn, NULL, refs);
}
if (!authorized(req->conn, ns, ACI_WRITE, dn, NULL, LDAP_SCOPE_BASE))
return ldap_respond(req, LDAP_INSUFFICIENT_ACCESS);
for (elm = attrs->be_sub; elm != NULL; elm = elm->be_next) {
attr = elm->be_sub;
if (attr == NULL || ober_get_string(attr, &s) != 0)
return ldap_respond(req, LDAP_PROTOCOL_ERROR);
if (!authorized(req->conn, ns, ACI_WRITE, dn, s,
LDAP_SCOPE_BASE))
return ldap_respond(req, LDAP_INSUFFICIENT_ACCESS);
if (!ns->relax) {
at = lookup_attribute(conf->schema, s);
if (at == NULL) {
log_debug("unknown attribute type %s", s);
return ldap_respond(req,
LDAP_NO_SUCH_ATTRIBUTE);
}
if (at->immutable) {
log_debug("attempt to add immutable"
" attribute %s", s);
return ldap_respond(req,
LDAP_CONSTRAINT_VIOLATION);
}
}
}
if (namespace_begin(ns) == -1) {
if (errno == EBUSY) {
if (namespace_queue_request(ns, req) != 0)
return ldap_respond(req, LDAP_BUSY);
return LDAP_BUSY;
}
return ldap_respond(req, LDAP_OTHER);
}
if ((set = ober_add_set(NULL)) == NULL)
goto fail;
if (ober_add_string(set, req->conn->binddn ? req->conn->binddn : "") == NULL)
goto fail;
if (ldap_add_attribute(attrs, "creatorsName", set) == NULL)
goto fail;
if ((set = ober_add_set(NULL)) == NULL)
goto fail;
if (ober_add_string(set, ldap_now()) == NULL)
goto fail;
if (ldap_add_attribute(attrs, "createTimestamp", set) == NULL)
goto fail;
uuid_create(&uuid);
uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
if ((set = ober_add_set(NULL)) == NULL)
goto fail;
if (ober_add_string(set, uuid_str) == NULL)
goto fail;
if (ldap_add_attribute(attrs, "entryUUID", set) == NULL)
goto fail;
if ((rc = validate_entry(dn, attrs, ns->relax)) != LDAP_SUCCESS ||
namespace_add(ns, dn, attrs) != 0) {
namespace_abort(ns);
if (rc == LDAP_SUCCESS && errno == EEXIST)
rc = LDAP_ALREADY_EXISTS;
else if (rc == LDAP_SUCCESS)
rc = LDAP_OTHER;
} else if (namespace_commit(ns) != 0)
rc = LDAP_OTHER;
return ldap_respond(req, rc);
fail:
ober_free_elements(set);
namespace_abort(ns);
return ldap_respond(req, LDAP_OTHER);
}
int
ldap_modify(struct request *req)
{
int rc = LDAP_OTHER;
char *dn;
long long op;
char *attr;
struct ber_element *mods, *entry, *mod, *a, *set;
struct ber_element *vals = NULL, *prev = NULL;
struct namespace *ns;
struct attr_type *at;
struct referrals *refs;
++stats.req_mod;
if (ober_scanf_elements(req->op, "{se", &dn, &mods) != 0)
return ldap_respond(req, LDAP_PROTOCOL_ERROR);
normalize_dn(dn);
log_debug("modifying dn %s", dn);
if (*dn == 0)
return ldap_respond(req, LDAP_INVALID_DN_SYNTAX);
if ((ns = namespace_for_base(dn)) == NULL) {
refs = namespace_referrals(dn);
if (refs == NULL)
return ldap_respond(req, LDAP_NAMING_VIOLATION);
else
return ldap_refer(req, dn, NULL, refs);
}
for (mod = mods->be_sub; mod; mod = mod->be_next) {
if (ober_scanf_elements(mod, "{E{es", &op, &prev, &attr) != 0)
return ldap_respond(req, LDAP_PROTOCOL_ERROR);
if (!authorized(req->conn, ns, ACI_WRITE, dn, attr,
LDAP_SCOPE_BASE))
return ldap_respond(req, LDAP_INSUFFICIENT_ACCESS);
}
if (namespace_begin(ns) == -1) {
if (errno == EBUSY) {
if (namespace_queue_request(ns, req) != 0)
return ldap_respond(req, LDAP_BUSY);
return LDAP_BUSY;
}
return ldap_respond(req, LDAP_OTHER);
}
if ((entry = namespace_get(ns, dn)) == NULL) {
rc = LDAP_NO_SUCH_OBJECT;
goto done;
}
for (mod = mods->be_sub; mod; mod = mod->be_next) {
if (ober_scanf_elements(mod, "{E{ese(", &op, &prev, &attr, &vals) != 0) {
rc = LDAP_PROTOCOL_ERROR;
vals = NULL;
goto done;
}
prev->be_next = NULL;
if (!ns->relax) {
at = lookup_attribute(conf->schema, attr);
if (at == NULL) {
log_debug("unknown attribute type %s", attr);
rc = LDAP_NO_SUCH_ATTRIBUTE;
goto done;
}
if (at->immutable) {
log_debug("attempt to modify immutable"
" attribute %s", attr);
rc = LDAP_CONSTRAINT_VIOLATION;
goto done;
}
}
a = ldap_get_attribute(entry, attr);
switch (op) {
case LDAP_MOD_ADD:
if (a == NULL) {
if (ldap_add_attribute(entry, attr, vals) != NULL)
vals = NULL;
} else {
if (ldap_merge_values(a, vals) == 0)
vals = NULL;
}
break;
case LDAP_MOD_DELETE:
if (vals->be_sub &&
vals->be_sub->be_type == BER_TYPE_OCTETSTRING) {
if (ldap_del_values(a, vals) == 1)
ldap_del_attribute(entry, attr);
} else {
ldap_del_attribute(entry, attr);
}
break;
case LDAP_MOD_REPLACE:
if (vals->be_sub != NULL &&
vals->be_sub->be_type != BER_TYPE_EOC) {
if (a == NULL) {
if (ldap_add_attribute(entry, attr, vals) != NULL)
vals = NULL;
} else {
if (ldap_set_values(a, vals) == 0)
vals = NULL;
}
} else if (a != NULL)
ldap_del_attribute(entry, attr);
break;
}
if (vals != NULL) {
ober_free_elements(vals);
vals = NULL;
}
}
if ((rc = validate_entry(dn, entry, ns->relax)) != LDAP_SUCCESS)
goto done;
set = ober_add_set(NULL);
ober_add_string(set, req->conn->binddn ? req->conn->binddn : "");
if ((a = ldap_get_attribute(entry, "modifiersName")) != NULL)
ldap_set_values(a, set);
else
ldap_add_attribute(entry, "modifiersName", set);
set = ober_add_set(NULL);
ober_add_string(set, ldap_now());
if ((a = ldap_get_attribute(entry, "modifyTimestamp")) != NULL)
ldap_set_values(a, set);
else
ldap_add_attribute(entry, "modifyTimestamp", set);
if (namespace_update(ns, dn, entry) == 0 && namespace_commit(ns) == 0)
rc = LDAP_SUCCESS;
else
rc = LDAP_OTHER;
done:
ober_free_elements(vals);
ober_free_elements(entry);
namespace_abort(ns);
return ldap_respond(req, rc);
}