diff options
author | bubulle <bubulle@alioth.debian.org> | 2010-09-06 20:54:34 +0000 |
---|---|---|
committer | bubulle <bubulle@alioth.debian.org> | 2010-09-06 20:54:34 +0000 |
commit | 53601faba8f69c3454ad07acaceeef9165cb3743 (patch) | |
tree | b31a4174a7f4d2650717c1902a6bc3f922e13117 /source4/lib/ldb | |
parent | 1b77db997b6a2ce389356509415a6e5e540bcfe0 (diff) | |
download | samba-53601faba8f69c3454ad07acaceeef9165cb3743.tar.gz |
Merge 3.5.4 in upstream branch
git-svn-id: svn://svn.debian.org/svn/pkg-samba/branches/samba/upstream@3574 fc4039ab-9d04-0410-8cac-899223bdd6b0
Diffstat (limited to 'source4/lib/ldb')
61 files changed, 4363 insertions, 1245 deletions
diff --git a/source4/lib/ldb/Makefile.in b/source4/lib/ldb/Makefile.in index 663dea9f80..c1f403d550 100644 --- a/source4/lib/ldb/Makefile.in +++ b/source4/lib/ldb/Makefile.in @@ -23,6 +23,7 @@ PACKAGE_VERSION = @PACKAGE_VERSION@ PYTHON = @PYTHON@ PYTHON_CONFIG = @PYTHON_CONFIG@ ldbdir = $(srcdir) +LIB_PATH_VAR = @LIB_PATH_VAR@ LDB_MODULESDIR = @LDB_MODULESDIR@ @@ -146,10 +147,10 @@ realdistclean:: distclean check:: test @PYTHON_CHECK_TARGET@ check-soloading: sample.$(SHLIBEXT) - LDB_MODULES_PATH=$(builddir) $(srcdir)/tests/test-soloading.sh + $(LIB_PATH_VAR)=lib LDB_MODULES_PATH=$(builddir) $(srcdir)/tests/test-soloading.sh test:: all check-soloading - for t in $(TESTS); do echo STARTING $${t}; $(srcdir)/tests/$${t} || exit 1; done + for t in $(TESTS); do echo STARTING $${t}; $(LIB_PATH_VAR)=lib $(srcdir)/tests/$${t} || exit 1; done valgrindtest:: all for t in $(TESTS); do echo STARTING $${t}; VALGRIND="valgrind -q --db-attach=yes --num-callers=30" $(srcdir)/tests/$${t} || exit 1; done diff --git a/source4/lib/ldb/common/attrib_handlers.c b/source4/lib/ldb/common/attrib_handlers.c index 80725ec04f..ba21fcac9b 100644 --- a/source4/lib/ldb/common/attrib_handlers.c +++ b/source4/lib/ldb/common/attrib_handlers.c @@ -62,7 +62,7 @@ int ldb_handler_fold(struct ldb_context *ldb, void *mem_ctx, out->data = (uint8_t *)ldb_casefold(ldb, mem_ctx, (const char *)(in->data), in->length); if (out->data == NULL) { - ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_handler_fold: unable to casefold string [%s]", in->data); + ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_handler_fold: unable to casefold string [%.*s]", (int)in->length, (const char *)in->data); return -1; } @@ -105,7 +105,7 @@ int ldb_handler_fold(struct ldb_context *ldb, void *mem_ctx, canonicalise a ldap Integer rfc2252 specifies it should be in decimal form */ -int ldb_canonicalise_Integer(struct ldb_context *ldb, void *mem_ctx, +static int ldb_canonicalise_Integer(struct ldb_context *ldb, void *mem_ctx, const struct ldb_val *in, struct ldb_val *out) { char *end; @@ -124,13 +124,45 @@ int ldb_canonicalise_Integer(struct ldb_context *ldb, void *mem_ctx, /* compare two Integers */ -int ldb_comparison_Integer(struct ldb_context *ldb, void *mem_ctx, +static int ldb_comparison_Integer(struct ldb_context *ldb, void *mem_ctx, const struct ldb_val *v1, const struct ldb_val *v2) { return strtoll((char *)v1->data, NULL, 0) - strtoll((char *)v2->data, NULL, 0); } /* + canonicalise a ldap Boolean + rfc2252 specifies it should be either "TRUE" or "FALSE" +*/ +static int ldb_canonicalise_Boolean(struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *in, struct ldb_val *out) +{ + if (strncasecmp((char *)in->data, "TRUE", in->length) == 0) { + out->data = (uint8_t *)talloc_strdup(mem_ctx, "TRUE"); + out->length = 4; + } else if (strncasecmp((char *)in->data, "FALSE", in->length) == 0) { + out->data = (uint8_t *)talloc_strdup(mem_ctx, "FALSE"); + out->length = 4; + } else { + return -1; + } + return 0; +} + +/* + compare two Booleans +*/ +static int ldb_comparison_Boolean(struct ldb_context *ldb, void *mem_ctx, + const struct ldb_val *v1, const struct ldb_val *v2) +{ + if (v1->length != v2->length) { + return v1->length - v2->length; + } + return strncasecmp((char *)v1->data, (char *)v2->data, v1->length); +} + + +/* compare two binary blobs */ int ldb_comparison_binary(struct ldb_context *ldb, void *mem_ctx, @@ -155,13 +187,13 @@ int ldb_comparison_fold(struct ldb_context *ldb, void *mem_ctx, { const char *s1=(const char *)v1->data, *s2=(const char *)v2->data; size_t n1 = v1->length, n2 = v2->length; - const char *u1, *u2; char *b1, *b2; + const char *u1, *u2; int ret; - while (*s1 == ' ' && n1) { s1++; n1--; }; - while (*s2 == ' ' && n2) { s2++; n2--; }; - /* TODO: make utf8 safe, possibly with helper function from application */ - while (*s1 && *s2 && n1 && n2) { + while (n1 && *s1 == ' ') { s1++; n1--; }; + while (n2 && *s2 == ' ') { s2++; n2--; }; + + while (n1 && n2 && *s1 && *s2) { /* the first 127 (0x7F) chars are ascii and utf8 guarantes they * never appear in multibyte sequences */ if (((unsigned char)s1[0]) & 0x80) goto utf8str; @@ -169,43 +201,58 @@ int ldb_comparison_fold(struct ldb_context *ldb, void *mem_ctx, if (toupper((unsigned char)*s1) != toupper((unsigned char)*s2)) break; if (*s1 == ' ') { - while (s1[0] == s1[1] && n1) { s1++; n1--; } - while (s2[0] == s2[1] && n2) { s2++; n2--; } + while (n1 && s1[0] == s1[1]) { s1++; n1--; } + while (n2 && s2[0] == s2[1]) { s2++; n2--; } } s1++; s2++; n1--; n2--; } - if (! (*s1 && *s2)) { - /* check for trailing spaces only if one of the pointers - * has reached the end of the strings otherwise we - * can mistakenly match. - * ex. "domain users" <-> "domainUpdates" - */ - while (*s1 == ' ') { s1++; n1--; } - while (*s2 == ' ') { s2++; n2--; } + + /* check for trailing spaces only if the other pointers has + * reached the end of the strings otherwise we can + * mistakenly match. ex. "domain users" <-> + * "domainUpdates" + */ + if (n1 && *s1 == ' ' && (!n2 || !*s2)) { + while (n1 && *s1 == ' ') { s1++; n1--; } + } + if (n2 && *s2 == ' ' && (!n1 || !*s1)) { + while (n2 && *s2 == ' ') { s2++; n2--; } } - if (n1 != n2) { - return n1 - n2; + if (n1 == 0 && n2 != 0) { + return -(int)toupper(*s2); } - return (int)(toupper(*s1)) - (int)(toupper(*s2)); + if (n2 == 0 && n1 != 0) { + return (int)toupper(*s1); + } + if (n2 == 0 && n2 == 0) { + return 0; + } + return (int)toupper(*s1) - (int)toupper(*s2); utf8str: /* no need to recheck from the start, just from the first utf8 char found */ b1 = ldb_casefold(ldb, mem_ctx, s1, n1); b2 = ldb_casefold(ldb, mem_ctx, s2, n2); - if (b1 && b2) { - /* Both strings converted correctly */ - - u1 = b1; - u2 = b2; - } else { - /* One of the strings was not UTF8, so we have no options but to do a binary compare */ - - u1 = s1; - u2 = s2; + if (!b1 || !b2) { + /* One of the strings was not UTF8, so we have no + * options but to do a binary compare */ + talloc_free(b1); + talloc_free(b2); + if (memcmp(s1, s2, MIN(n1, n2)) == 0) { + if (n1 == n2) return 0; + if (n1 > n2) { + return (int)toupper(s1[n2]); + } else { + return -(int)toupper(s2[n1]); + } + } } + u1 = b1; + u2 = b2; + while (*u1 & *u2) { if (*u1 != *u2) break; @@ -231,7 +278,7 @@ utf8str: /* canonicalise a attribute in DN format */ -int ldb_canonicalise_dn(struct ldb_context *ldb, void *mem_ctx, +static int ldb_canonicalise_dn(struct ldb_context *ldb, void *mem_ctx, const struct ldb_val *in, struct ldb_val *out) { struct ldb_dn *dn; @@ -240,7 +287,7 @@ int ldb_canonicalise_dn(struct ldb_context *ldb, void *mem_ctx, out->length = 0; out->data = NULL; - dn = ldb_dn_from_ldb_val(ldb, mem_ctx, in); + dn = ldb_dn_from_ldb_val(mem_ctx, ldb, in); if ( ! ldb_dn_validate(dn)) { return LDB_ERR_INVALID_DN_SYNTAX; } @@ -262,16 +309,16 @@ done: /* compare two dns */ -int ldb_comparison_dn(struct ldb_context *ldb, void *mem_ctx, +static int ldb_comparison_dn(struct ldb_context *ldb, void *mem_ctx, const struct ldb_val *v1, const struct ldb_val *v2) { struct ldb_dn *dn1 = NULL, *dn2 = NULL; int ret; - dn1 = ldb_dn_from_ldb_val(ldb, mem_ctx, v1); + dn1 = ldb_dn_from_ldb_val(mem_ctx, ldb, v1); if ( ! ldb_dn_validate(dn1)) return -1; - dn2 = ldb_dn_from_ldb_val(ldb, mem_ctx, v2); + dn2 = ldb_dn_from_ldb_val(mem_ctx, ldb, v2); if ( ! ldb_dn_validate(dn2)) { talloc_free(dn1); return -1; @@ -287,7 +334,7 @@ int ldb_comparison_dn(struct ldb_context *ldb, void *mem_ctx, /* compare two utc time values. 1 second resolution */ -int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx, +static int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx, const struct ldb_val *v1, const struct ldb_val *v2) { time_t t1, t2; @@ -299,7 +346,7 @@ int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx, /* canonicalise a utc time */ -int ldb_canonicalise_utctime(struct ldb_context *ldb, void *mem_ctx, +static int ldb_canonicalise_utctime(struct ldb_context *ldb, void *mem_ctx, const struct ldb_val *in, struct ldb_val *out) { time_t t = ldb_string_to_time((char *)in->data); @@ -356,7 +403,14 @@ static const struct ldb_schema_syntax ldb_standard_syntaxes[] = { .ldif_write_fn = ldb_handler_copy, .canonicalise_fn = ldb_canonicalise_utctime, .comparison_fn = ldb_comparison_utctime - } + }, + { + .name = LDB_SYNTAX_BOOLEAN, + .ldif_read_fn = ldb_handler_copy, + .ldif_write_fn = ldb_handler_copy, + .canonicalise_fn = ldb_canonicalise_Boolean, + .comparison_fn = ldb_comparison_Boolean + }, }; diff --git a/source4/lib/ldb/common/ldb.c b/source4/lib/ldb/common/ldb.c index 86ce2069a5..e9c924583e 100644 --- a/source4/lib/ldb/common/ldb.c +++ b/source4/lib/ldb/common/ldb.c @@ -41,8 +41,8 @@ static int ldb_context_destructor(void *ptr) if (ldb->transaction_active) { ldb_debug(ldb, LDB_DEBUG_FATAL, - "A transaction is still active in ldb context [%p]", - ldb); + "A transaction is still active in ldb context [%p] on %s", + ldb, (const char *)ldb_get_opaque(ldb, "ldb_url")); } return 0; @@ -240,7 +240,7 @@ int ldb_connect(struct ldb_context *ldb, const char *url, if (ldb_load_modules(ldb, options) != LDB_SUCCESS) { ldb_debug(ldb, LDB_DEBUG_FATAL, - "Unable to load modules for %s: %s\n", + "Unable to load modules for %s: %s", url, ldb_errstring(ldb)); return LDB_ERR_OTHER; } @@ -257,6 +257,9 @@ void ldb_set_errstring(struct ldb_context *ldb, const char *err_string) talloc_free(ldb->err_string); } ldb->err_string = talloc_strdup(ldb, err_string); + if (ldb->flags & LDB_FLG_ENABLE_TRACING) { + ldb_debug(ldb, LDB_DEBUG_TRACE, "ldb_set_errstring: %s", ldb->err_string); + } } void ldb_asprintf_errstring(struct ldb_context *ldb, const char *format, ...) @@ -282,15 +285,20 @@ void ldb_reset_err_string(struct ldb_context *ldb) } } -#define FIRST_OP(ldb, op) do { \ +#define FIRST_OP_NOERR(ldb, op) do { \ module = ldb->modules; \ while (module && module->ops->op == NULL) module = module->next; \ - if (module == NULL) { \ +} while (0) + +#define FIRST_OP(ldb, op) do { \ + FIRST_OP_NOERR(ldb, op); \ + if (module == NULL) { \ ldb_asprintf_errstring(ldb, "unable to find module or backend to handle operation: " #op); \ return LDB_ERR_OPERATIONS_ERROR; \ } \ } while (0) + /* start a transaction */ @@ -311,6 +319,7 @@ int ldb_transaction_start(struct ldb_context *ldb) /* start a new transaction */ ldb->transaction_active++; + ldb->prepare_commit_done = false; FIRST_OP(ldb, start_transaction); @@ -330,6 +339,57 @@ int ldb_transaction_start(struct ldb_context *ldb) } /* + prepare for transaction commit (first phase of two phase commit) +*/ +int ldb_transaction_prepare_commit(struct ldb_context *ldb) +{ + struct ldb_module *module; + int status; + + if (ldb->prepare_commit_done) { + return LDB_SUCCESS; + } + + /* commit only when all nested transactions are complete */ + if (ldb->transaction_active > 1) { + return LDB_SUCCESS; + } + + ldb->prepare_commit_done = true; + + if (ldb->transaction_active < 0) { + ldb_debug(ldb, LDB_DEBUG_FATAL, + "prepare commit called but no ldb transactions are active!"); + ldb->transaction_active = 0; + return LDB_ERR_OPERATIONS_ERROR; + } + + /* call prepare transaction if available */ + FIRST_OP_NOERR(ldb, prepare_commit); + if (module == NULL) { + return LDB_SUCCESS; + } + + status = module->ops->prepare_commit(module); + if (status != LDB_SUCCESS) { + /* if a module fails the prepare then we need + to call the end transaction for everyone */ + FIRST_OP(ldb, end_transaction); + module->ops->end_transaction(module); + if (ldb->err_string == NULL) { + /* no error string was setup by the backend */ + ldb_asprintf_errstring(ldb, + "ldb transaction prepare commit: %s (%d)", + ldb_strerror(status), + status); + } + } + + return status; +} + + +/* commit a transaction */ int ldb_transaction_commit(struct ldb_context *ldb) @@ -337,6 +397,11 @@ int ldb_transaction_commit(struct ldb_context *ldb) struct ldb_module *module; int status; + status = ldb_transaction_prepare_commit(ldb); + if (status != LDB_SUCCESS) { + return status; + } + ldb->transaction_active--; ldb_debug(ldb, LDB_DEBUG_TRACE, @@ -355,10 +420,9 @@ int ldb_transaction_commit(struct ldb_context *ldb) return LDB_ERR_OPERATIONS_ERROR; } - FIRST_OP(ldb, end_transaction); - ldb_reset_err_string(ldb); + FIRST_OP(ldb, end_transaction); status = module->ops->end_transaction(module); if (status != LDB_SUCCESS) { if (ldb->err_string == NULL) { @@ -368,10 +432,14 @@ int ldb_transaction_commit(struct ldb_context *ldb) ldb_strerror(status), status); } + /* cancel the transaction */ + FIRST_OP(ldb, del_transaction); + module->ops->del_transaction(module); } return status; } + /* cancel a transaction */ @@ -393,7 +461,7 @@ int ldb_transaction_cancel(struct ldb_context *ldb) if (ldb->transaction_active < 0) { ldb_debug(ldb, LDB_DEBUG_FATAL, - "commit called but no ldb transactions are active!"); + "cancel called but no ldb transactions are active!"); ldb->transaction_active = 0; return LDB_ERR_OPERATIONS_ERROR; } @@ -557,6 +625,99 @@ int ldb_request_get_status(struct ldb_request *req) return req->handle->status; } + +/* + trace a ldb request +*/ +static void ldb_trace_request(struct ldb_context *ldb, struct ldb_request *req) +{ + TALLOC_CTX *tmp_ctx = talloc_new(req); + int i; + + switch (req->operation) { + case LDB_SEARCH: + ldb_debug_add(ldb, "ldb_trace_request: SEARCH\n"); + ldb_debug_add(ldb, " dn: %s\n", + ldb_dn_is_null(req->op.search.base)?"<rootDSE>": + ldb_dn_get_linearized(req->op.search.base)); + ldb_debug_add(ldb, " scope: %s\n", + req->op.search.scope==LDB_SCOPE_BASE?"base": + req->op.search.scope==LDB_SCOPE_ONELEVEL?"one": + req->op.search.scope==LDB_SCOPE_SUBTREE?"sub":"UNKNOWN"); + ldb_debug_add(ldb, " expr: %s\n", + ldb_filter_from_tree(tmp_ctx, req->op.search.tree)); + if (req->op.search.attrs == NULL) { + ldb_debug_add(ldb, " attr: <ALL>\n"); + } else { + for (i=0; req->op.search.attrs[i]; i++) { + ldb_debug_add(ldb, " attr: %s\n", req->op.search.attrs[i]); + } + } + break; + case LDB_DELETE: + ldb_debug_add(ldb, "ldb_trace_request: DELETE\n"); + ldb_debug_add(ldb, " dn: %s\n", + ldb_dn_get_linearized(req->op.del.dn)); + break; + case LDB_RENAME: + ldb_debug_add(ldb, "ldb_trace_request: RENAME\n"); + ldb_debug_add(ldb, " olddn: %s\n", + ldb_dn_get_linearized(req->op.rename.olddn)); + ldb_debug_add(ldb, " newdn: %s\n", + ldb_dn_get_linearized(req->op.rename.newdn)); + break; + case LDB_EXTENDED: + ldb_debug_add(ldb, "ldb_trace_request: EXTENDED\n"); + ldb_debug_add(ldb, " oid: %s\n", req->op.extended.oid); + ldb_debug_add(ldb, " data: %s\n", req->op.extended.data?"yes":"no"); + break; + case LDB_ADD: + ldb_debug_add(ldb, "ldb_trace_request: ADD\n"); + ldb_debug_add(req->handle->ldb, "%s\n", + ldb_ldif_message_string(req->handle->ldb, tmp_ctx, + LDB_CHANGETYPE_ADD, + req->op.add.message)); + break; + case LDB_MODIFY: + ldb_debug_add(ldb, "ldb_trace_request: MODIFY\n"); + ldb_debug_add(req->handle->ldb, "%s\n", + ldb_ldif_message_string(req->handle->ldb, tmp_ctx, + LDB_CHANGETYPE_ADD, + req->op.mod.message)); + break; + case LDB_REQ_REGISTER_CONTROL: + ldb_debug_add(ldb, "ldb_trace_request: REGISTER_CONTROL\n"); + ldb_debug_add(req->handle->ldb, "%s\n", + req->op.reg_control.oid); + break; + case LDB_REQ_REGISTER_PARTITION: + ldb_debug_add(ldb, "ldb_trace_request: REGISTER_PARTITION\n"); + ldb_debug_add(req->handle->ldb, "%s\n", + ldb_dn_get_linearized(req->op.reg_partition.dn)); + break; + default: + ldb_debug_add(ldb, "ldb_trace_request: UNKNOWN(%u)\n", + req->operation); + break; + } + + if (req->controls == NULL) { + ldb_debug_add(ldb, " control: <NONE>\n"); + } else { + for (i=0; req->controls && req->controls[i]; i++) { + ldb_debug_add(ldb, " control: %s crit:%u data:%s\n", + req->controls[i]->oid, + req->controls[i]->critical, + req->controls[i]->data?"yes":"no"); + } + } + + ldb_debug_end(ldb, LDB_DEBUG_TRACE); + + talloc_free(tmp_ctx); +} + + /* start an ldb request NOTE: the request must be a talloc context. @@ -574,6 +735,10 @@ int ldb_request(struct ldb_context *ldb, struct ldb_request *req) ldb_reset_err_string(ldb); + if (ldb->flags & LDB_FLG_ENABLE_TRACING) { + ldb_trace_request(ldb, req); + } + /* call the first module in the chain */ switch (req->operation) { case LDB_SEARCH: @@ -677,10 +842,12 @@ int ldb_search_default_callback(struct ldb_request *req, /* this is the last message, and means the request is done */ /* we have to signal and eventual ldb_wait() waiting that the * async request operation was completed */ + talloc_free(ares); return ldb_request_done(req, LDB_SUCCESS); } talloc_free(ares); + return LDB_SUCCESS; } @@ -758,6 +925,10 @@ int ldb_build_search_req_ex(struct ldb_request **ret_req, return LDB_ERR_OPERATIONS_ERROR; } + if (parent) { + req->handle->nesting++; + } + *ret_req = req; return LDB_SUCCESS; } @@ -825,6 +996,10 @@ int ldb_build_add_req(struct ldb_request **ret_req, return LDB_ERR_OPERATIONS_ERROR; } + if (parent) { + req->handle->nesting++; + } + *ret_req = req; return LDB_SUCCESS; @@ -863,6 +1038,10 @@ int ldb_build_mod_req(struct ldb_request **ret_req, return LDB_ERR_OPERATIONS_ERROR; } + if (parent) { + req->handle->nesting++; + } + *ret_req = req; return LDB_SUCCESS; @@ -901,6 +1080,10 @@ int ldb_build_del_req(struct ldb_request **ret_req, return LDB_ERR_OPERATIONS_ERROR; } + if (parent) { + req->handle->nesting++; + } + *ret_req = req; return LDB_SUCCESS; @@ -941,6 +1124,10 @@ int ldb_build_rename_req(struct ldb_request **ret_req, return LDB_ERR_OPERATIONS_ERROR; } + if (parent) { + req->handle->nesting++; + } + *ret_req = req; return LDB_SUCCESS; @@ -1010,6 +1197,10 @@ int ldb_build_extended_req(struct ldb_request **ret_req, return LDB_ERR_OPERATIONS_ERROR; } + if (parent) { + req->handle->nesting++; + } + *ret_req = req; return LDB_SUCCESS; @@ -1430,3 +1621,21 @@ void *ldb_get_opaque(struct ldb_context *ldb, const char *name) } return NULL; } + +int ldb_global_init(void) +{ + /* Provided for compatibility with some older versions of ldb */ + return 0; +} + +/* return the ldb flags */ +unsigned int ldb_get_flags(struct ldb_context *ldb) +{ + return ldb->flags; +} + +/* set the ldb flags */ +void ldb_set_flags(struct ldb_context *ldb, unsigned flags) +{ + ldb->flags = flags; +} diff --git a/source4/lib/ldb/common/ldb_attributes.c b/source4/lib/ldb/common/ldb_attributes.c index 9fa0fb2ccd..79c5dd69de 100644 --- a/source4/lib/ldb/common/ldb_attributes.c +++ b/source4/lib/ldb/common/ldb_attributes.c @@ -118,8 +118,9 @@ static const struct ldb_schema_attribute ldb_attribute_default = { /* return the attribute handlers for a given attribute */ -const struct ldb_schema_attribute *ldb_schema_attribute_by_name(struct ldb_context *ldb, - const char *name) +static const struct ldb_schema_attribute *ldb_schema_attribute_by_name_internal( + struct ldb_context *ldb, + const char *name) { int i, e, b = 0, r; const struct ldb_schema_attribute *def = &ldb_attribute_default; @@ -152,6 +153,25 @@ const struct ldb_schema_attribute *ldb_schema_attribute_by_name(struct ldb_conte return def; } +/* + return the attribute handlers for a given attribute +*/ +const struct ldb_schema_attribute *ldb_schema_attribute_by_name(struct ldb_context *ldb, + const char *name) +{ + if (ldb->schema.attribute_handler_override) { + const struct ldb_schema_attribute *ret = + ldb->schema.attribute_handler_override(ldb, + ldb->schema.attribute_handler_override_private, + name); + if (ret) { + return ret; + } + } + + return ldb_schema_attribute_by_name_internal(ldb, name); +} + /* add to the list of ldif handlers for this ldb context @@ -161,7 +181,7 @@ void ldb_schema_attribute_remove(struct ldb_context *ldb, const char *name) const struct ldb_schema_attribute *a; int i; - a = ldb_schema_attribute_by_name(ldb, name); + a = ldb_schema_attribute_by_name_internal(ldb, name); if (a == NULL || a->name == NULL) { return; } @@ -273,3 +293,14 @@ const struct ldb_dn_extended_syntax *ldb_dn_extended_syntax_by_name(struct ldb_c return NULL; } +/* + set an attribute handler override function - used to delegate schema handling + to external code + */ +void ldb_schema_attribute_set_override_handler(struct ldb_context *ldb, + ldb_attribute_handler_override_fn_t override, + void *private_data) +{ + ldb->schema.attribute_handler_override_private = private_data; + ldb->schema.attribute_handler_override = override; +} diff --git a/source4/lib/ldb/common/ldb_controls.c b/source4/lib/ldb/common/ldb_controls.c index 0c587e0905..b38373ec12 100644 --- a/source4/lib/ldb/common/ldb_controls.c +++ b/source4/lib/ldb/common/ldb_controls.c @@ -39,7 +39,6 @@ struct ldb_control *ldb_request_get_control(struct ldb_request *req, const char { int i; - /* check if there's a paged request control */ if (req->controls != NULL) { for (i = 0; req->controls[i]; i++) { if (strcmp(oid, req->controls[i]->oid) == 0) { @@ -59,7 +58,6 @@ struct ldb_control *ldb_reply_get_control(struct ldb_reply *rep, const char *oid { int i; - /* check if there's a paged request control */ if (rep->controls != NULL) { for (i = 0; rep->controls[i]; i++) { if (strcmp(oid, rep->controls[i]->oid) == 0) { @@ -75,7 +73,7 @@ struct ldb_control *ldb_reply_get_control(struct ldb_reply *rep, const char *oid /* saves the current controls list into the "saver" and replace the one in req with a new one excluding the "exclude" control */ -/* returns False on error */ +/* returns 0 on error */ int save_controls(struct ldb_control *exclude, struct ldb_request *req, struct ldb_control ***saver) { struct ldb_control **lcs; @@ -129,7 +127,13 @@ int ldb_request_add_control(struct ldb_request *req, const char *oid, bool criti struct ldb_control **ctrls; struct ldb_control *ctrl; - for (n=0; req->controls && req->controls[n];) { n++; } + for (n=0; req->controls && req->controls[n];) { + /* having two controls of the same OID makes no sense */ + if (strcmp(oid, req->controls[n]->oid) == 0) { + return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; + } + n++; + } ctrls = talloc_realloc(req, req->controls, struct ldb_control *, @@ -557,6 +561,60 @@ struct ldb_control **ldb_parse_control_strings(struct ldb_context *ldb, void *me continue; } + if (strncmp(control_strings[i], "show_deactivated_link:", 22) == 0) { + const char *p; + int crit, ret; + + p = &(control_strings[i][22]); + ret = sscanf(p, "%d", &crit); + if ((ret != 1) || (crit < 0) || (crit > 1)) { + error_string = talloc_asprintf(mem_ctx, "invalid show_deactivated_link control syntax\n"); + error_string = talloc_asprintf_append(error_string, " syntax: crit(b)\n"); + error_string = talloc_asprintf_append(error_string, " note: b = boolean"); + ldb_set_errstring(ldb, error_string); + talloc_free(error_string); + return NULL; + } + + ctrl[i] = talloc(ctrl, struct ldb_control); + if (!ctrl[i]) { + ldb_oom(ldb); + return NULL; + } + ctrl[i]->oid = LDB_CONTROL_SHOW_DEACTIVATED_LINK_OID; + ctrl[i]->critical = crit; + ctrl[i]->data = NULL; + + continue; + } + + if (strncmp(control_strings[i], "show_recycled:", 14) == 0) { + const char *p; + int crit, ret; + + p = &(control_strings[i][14]); + ret = sscanf(p, "%d", &crit); + if ((ret != 1) || (crit < 0) || (crit > 1)) { + error_string = talloc_asprintf(mem_ctx, "invalid show_recycled control syntax\n"); + error_string = talloc_asprintf_append(error_string, " syntax: crit(b)\n"); + error_string = talloc_asprintf_append(error_string, " note: b = boolean"); + ldb_set_errstring(ldb, error_string); + talloc_free(error_string); + return NULL; + } + + ctrl[i] = talloc(ctrl, struct ldb_control); + if (!ctrl[i]) { + ldb_oom(ldb); + return NULL; + } + ctrl[i]->oid = LDB_CONTROL_SHOW_RECYCLED_OID; + ctrl[i]->critical = crit; + ctrl[i]->data = NULL; + + continue; + } + if (strncmp(control_strings[i], "permissive_modify:", 18) == 0) { const char *p; int crit, ret; diff --git a/source4/lib/ldb/common/ldb_debug.c b/source4/lib/ldb/common/ldb_debug.c index f8009eb8a3..6aa58ccf71 100644 --- a/source4/lib/ldb/common/ldb_debug.c +++ b/source4/lib/ldb/common/ldb_debug.c @@ -56,9 +56,19 @@ static void ldb_debug_stderr(void *context, enum ldb_debug_level level, { if (level <= LDB_DEBUG_WARNING) { vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); } } +static void ldb_debug_stderr_all(void *context, enum ldb_debug_level level, + const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0); +static void ldb_debug_stderr_all(void *context, enum ldb_debug_level level, + const char *fmt, va_list ap) +{ + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); +} + /* convenience function to setup debug messages on stderr messages of level LDB_DEBUG_WARNING and higher are printed @@ -75,13 +85,42 @@ void ldb_debug(struct ldb_context *ldb, enum ldb_debug_level level, const char * { va_list ap; if (ldb->debug_ops.debug == NULL) { - ldb_set_debug_stderr(ldb); + if (ldb->flags & LDB_FLG_ENABLE_TRACING) { + ldb_set_debug(ldb, ldb_debug_stderr_all, ldb); + } else { + ldb_set_debug_stderr(ldb); + } } va_start(ap, fmt); ldb->debug_ops.debug(ldb->debug_ops.context, level, fmt, ap); va_end(ap); } +/* + add to an accumulated log message + */ +void ldb_debug_add(struct ldb_context *ldb, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (ldb->partial_debug == NULL) { + ldb->partial_debug = talloc_vasprintf(ldb, fmt, ap); + } else { + ldb->partial_debug = talloc_vasprintf_append(ldb->partial_debug, + fmt, ap); + } + va_end(ap); +} + +/* + send the accumulated log message, and free it + */ +void ldb_debug_end(struct ldb_context *ldb, enum ldb_debug_level level) +{ + ldb_debug(ldb, level, "%s", ldb->partial_debug); + talloc_free(ldb->partial_debug); + ldb->partial_debug = NULL; +} /* log a message, and set the ldb error string to the same message diff --git a/source4/lib/ldb/common/ldb_dn.c b/source4/lib/ldb/common/ldb_dn.c index 402d629501..12a513fc42 100644 --- a/source4/lib/ldb/common/ldb_dn.c +++ b/source4/lib/ldb/common/ldb_dn.c @@ -1,4 +1,4 @@ -/* +/* ldb database library Copyright (C) Simo Sorce 2005 @@ -6,7 +6,7 @@ ** NOTE! The following LGPL license applies to the ldb ** library. This does NOT imply that all of Samba is released ** under the LGPL - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -52,7 +52,7 @@ struct ldb_dn_component { struct ldb_val cf_value; }; -struct ldb_dn_extended_component { +struct ldb_dn_ext_component { char *name; struct ldb_val value; @@ -69,37 +69,48 @@ struct ldb_dn { bool valid_case; char *linearized; - char *extended_linearized; + char *ext_linearized; char *casefold; unsigned int comp_num; struct ldb_dn_component *components; - unsigned int extended_comp_num; - struct ldb_dn_extended_component *extended_components; + unsigned int ext_comp_num; + struct ldb_dn_ext_component *ext_components; }; /* strdn may be NULL */ -struct ldb_dn *ldb_dn_from_ldb_val(void *mem_ctx, struct ldb_context *ldb, const struct ldb_val *strdn) +struct ldb_dn *ldb_dn_from_ldb_val(void *mem_ctx, + struct ldb_context *ldb, + const struct ldb_val *strdn) { struct ldb_dn *dn; if (! ldb) return NULL; + if (strdn && strdn->data + && (strlen((const char*)strdn->data) != strdn->length)) { + /* The RDN must not contain a character with value 0x0 */ + return NULL; + } + dn = talloc_zero(mem_ctx, struct ldb_dn); LDB_DN_NULL_FAILED(dn); dn->ldb = ldb; if (strdn->data && strdn->length) { - if (strdn->data[0] == '@') { + const char *data = (const char *)strdn->data; + size_t length = strdn->length; + + if (data[0] == '@') { dn->special = true; - } - dn->extended_linearized = talloc_strndup(dn, (const char *)strdn->data, strdn->length); - LDB_DN_NULL_FAILED(dn->extended_linearized); - - if (strdn->data[0] == '<') { - const char *p_save, *p = dn->extended_linearized; + } + dn->ext_linearized = talloc_strndup(dn, data, length); + LDB_DN_NULL_FAILED(dn->ext_linearized); + + if (data[0] == '<') { + const char *p_save, *p = dn->ext_linearized; do { p_save = p; p = strstr(p, ">;"); @@ -107,16 +118,16 @@ struct ldb_dn *ldb_dn_from_ldb_val(void *mem_ctx, struct ldb_context *ldb, const p = p + 2; } } while (p); - - if (p_save == dn->extended_linearized) { + + if (p_save == dn->ext_linearized) { dn->linearized = talloc_strdup(dn, ""); } else { dn->linearized = talloc_strdup(dn, p_save); } LDB_DN_NULL_FAILED(dn->linearized); } else { - dn->linearized = dn->extended_linearized; - dn->extended_linearized = NULL; + dn->linearized = dn->ext_linearized; + dn->ext_linearized = NULL; } } else { dn->linearized = talloc_strdup(dn, ""); @@ -131,7 +142,9 @@ failed: } /* strdn may be NULL */ -struct ldb_dn *ldb_dn_new(void *mem_ctx, struct ldb_context *ldb, const char *strdn) +struct ldb_dn *ldb_dn_new(void *mem_ctx, + struct ldb_context *ldb, + const char *strdn) { struct ldb_val blob; blob.data = strdn; @@ -139,7 +152,9 @@ struct ldb_dn *ldb_dn_new(void *mem_ctx, struct ldb_context *ldb, const char *st return ldb_dn_from_ldb_val(mem_ctx, ldb, &blob); } -struct ldb_dn *ldb_dn_new_fmt(void *mem_ctx, struct ldb_context *ldb, const char *new_fmt, ...) +struct ldb_dn *ldb_dn_new_fmt(void *mem_ctx, + struct ldb_context *ldb, + const char *new_fmt, ...) { char *strdn; va_list ap; @@ -155,7 +170,7 @@ struct ldb_dn *ldb_dn_new_fmt(void *mem_ctx, struct ldb_context *ldb, const char talloc_free(strdn); return dn; } - + return NULL; } @@ -175,7 +190,8 @@ static int ldb_dn_escape_internal(char *dst, const char *src, int len) if (p - src == len) /* found no escapable chars */ break; - memcpy(d, s, p - s); /* copy the part of the string before the stop */ + /* copy the part of the string before the stop */ + memcpy(d, s, p - s); d += (p - s); /* move to current position */ if (*p) { /* it is a normal escapable character */ @@ -195,7 +211,7 @@ static int ldb_dn_escape_internal(char *dst, const char *src, int len) /* return the length of the resulting string */ return (l + (d - dst)); -} +} char *ldb_dn_escape_value(void *mem_ctx, struct ldb_val value) { @@ -221,6 +237,9 @@ char *ldb_dn_escape_value(void *mem_ctx, struct ldb_val value) /* explode a DN string into a ldb_dn structure based on RFC4514 except that we don't support multiple valued RDNs + + TODO: according to MS-ADTS:3.1.1.5.2 Naming Constraints + DN must be compliant with RFC2253 */ static bool ldb_dn_explode(struct ldb_dn *dn) { @@ -244,8 +263,8 @@ static bool ldb_dn_explode(struct ldb_dn *dn) return true; } - if (dn->extended_linearized) { - parse_dn = dn->extended_linearized; + if (dn->ext_linearized) { + parse_dn = dn->ext_linearized; } else { parse_dn = dn->linearized; } @@ -254,6 +273,11 @@ static bool ldb_dn_explode(struct ldb_dn *dn) return false; } + /* The RDN size must be less than 255 characters */ + if (strlen(parse_dn) > 255) { + return false; + } + /* Empty DNs */ if (parse_dn[0] == '\0') { return true; @@ -267,11 +291,11 @@ static bool ldb_dn_explode(struct ldb_dn *dn) /* make sure we free this if alloced previously before replacing */ talloc_free(dn->components); - talloc_free(dn->extended_components); - dn->extended_components = NULL; + talloc_free(dn->ext_components); + dn->ext_components = NULL; /* in the common case we have 3 or more components */ - /* make sure all components are zeroed, other functions depend on this */ + /* make sure all components are zeroed, other functions depend on it */ dn->components = talloc_zero_array(dn, struct ldb_dn_component, 3); if ( ! dn->components) { return false; @@ -313,7 +337,7 @@ static bool ldb_dn_explode(struct ldb_dn *dn) continue; } } - + if (in_ex_name && *p == '=') { *d++ = '\0'; p++; @@ -324,46 +348,46 @@ static bool ldb_dn_explode(struct ldb_dn *dn) } if (in_ex_value && *p == '>') { - const struct ldb_dn_extended_syntax *extended_syntax; + const struct ldb_dn_extended_syntax *ext_syntax; struct ldb_val ex_val = { - .data = ex_value, + .data = (uint8_t *)ex_value, .length = d - ex_value }; - + *d++ = '\0'; p++; in_ex_value = false; /* Process name and ex_value */ - dn->extended_components = talloc_realloc(dn, - dn->extended_components, - struct ldb_dn_extended_component, - dn->extended_comp_num + 1); - if ( ! dn->extended_components) { + dn->ext_components = talloc_realloc(dn, + dn->ext_components, + struct ldb_dn_ext_component, + dn->ext_comp_num + 1); + if ( ! dn->ext_components) { /* ouch ! */ goto failed; } - extended_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, ex_name); - if (!extended_syntax) { + ext_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, ex_name); + if (!ext_syntax) { /* We don't know about this type of extended DN */ goto failed; } - dn->extended_components[dn->extended_comp_num].name = talloc_strdup(dn->extended_components, ex_name); - if (!dn->extended_components[dn->extended_comp_num].name) { + dn->ext_components[dn->ext_comp_num].name = talloc_strdup(dn->ext_components, ex_name); + if (!dn->ext_components[dn->ext_comp_num].name) { /* ouch */ goto failed; } - ret = extended_syntax->read_fn(dn->ldb, dn->extended_components, - &ex_val, &dn->extended_components[dn->extended_comp_num].value); + ret = ext_syntax->read_fn(dn->ldb, dn->ext_components, + &ex_val, &dn->ext_components[dn->ext_comp_num].value); if (ret != LDB_SUCCESS) { dn->invalid = true; goto failed; } - dn->extended_comp_num++; + dn->ext_comp_num++; if (*p == '\0') { /* We have reached the end (extended component only)! */ @@ -402,12 +426,14 @@ static bool ldb_dn_explode(struct ldb_dn *dn) is_oid = true; } else if ( ! isalpha(*p)) { - /* not a digit nor an alpha, invalid attribute name */ + /* not a digit nor an alpha, + * invalid attribute name */ dn->invalid = true; goto failed; } - - /* Copy this character across from parse_dn, now we have trimmed out spaces */ + + /* Copy this character across from parse_dn, + * now we have trimmed out spaces */ *d++ = *p++; continue; } @@ -420,7 +446,7 @@ static bool ldb_dn_explode(struct ldb_dn *dn) } if (trim && (*p != '=')) { - /* spaces/tabs are not allowed in attribute names */ + /* spaces/tabs are not allowed */ dn->invalid = true; goto failed; } @@ -432,7 +458,9 @@ static bool ldb_dn_explode(struct ldb_dn *dn) trim = true; l = 0; - /* Terminate this string in d (which is a copy of parse_dn with spaces trimmed) */ + /* Terminate this string in d + * (which is a copy of parse_dn + * with spaces trimmed) */ *d++ = '\0'; dn->components[dn->comp_num].name = talloc_strdup(dn->components, dt); if ( ! dn->components[dn->comp_num].name) { @@ -453,7 +481,8 @@ static bool ldb_dn_explode(struct ldb_dn *dn) } if (is_oid && ( ! (isdigit(*p) || (*p == '.')))) { - /* not a digit nor a dot, invalid attribute oid */ + /* not a digit nor a dot, + * invalid attribute oid */ dn->invalid = true; goto failed; } else @@ -601,7 +630,7 @@ static bool ldb_dn_explode(struct ldb_dn *dn) break; } - if (*p == ' ') { + if (*p == ' ') { if ( ! t) t = p; } else { if ( t ) t = NULL; @@ -609,7 +638,7 @@ static bool ldb_dn_explode(struct ldb_dn *dn) *d++ = *p++; l++; - + break; } @@ -630,9 +659,9 @@ static bool ldb_dn_explode(struct ldb_dn *dn) } *d++ = '\0'; - dn->components[dn->comp_num].value.data = (uint8_t *)talloc_strdup(dn->components, dt); dn->components[dn->comp_num].value.length = l; - + dn->components[dn->comp_num].value.data = + (uint8_t *)talloc_strdup(dn->components, dt); if ( ! dn->components[dn->comp_num].value.data) { /* ouch */ goto failed; @@ -676,8 +705,10 @@ const char *ldb_dn_get_linearized(struct ldb_dn *dn) /* calculate maximum possible length of DN */ for (len = 0, i = 0; i < dn->comp_num; i++) { - len += strlen(dn->components[i].name); /* name len */ - len += (dn->components[i].value.length * 3); /* max escaped data len */ + /* name len */ + len += strlen(dn->components[i].name); + /* max escaped data len */ + len += (dn->components[i].value.length * 3); len += 2; /* '=' and ',' */ } dn->linearized = talloc_array(dn, char, len); @@ -703,7 +734,8 @@ const char *ldb_dn_get_linearized(struct ldb_dn *dn) *(--d) = '\0'; /* don't waste more memory than necessary */ - dn->linearized = talloc_realloc(dn, dn->linearized, char, (d - dn->linearized + 1)); + dn->linearized = talloc_realloc(dn, dn->linearized, + char, (d - dn->linearized + 1)); return dn->linearized; } @@ -721,27 +753,26 @@ char *ldb_dn_get_extended_linearized(void *mem_ctx, struct ldb_dn *dn, int mode) if (!ldb_dn_has_extended(dn)) { return talloc_strdup(mem_ctx, linearized); } - + if (!ldb_dn_validate(dn)) { return NULL; } - for (i=0; i < dn->extended_comp_num; i++) { + for (i = 0; i < dn->ext_comp_num; i++) { + const struct ldb_dn_extended_syntax *ext_syntax; + const char *name = dn->ext_components[i].name; + struct ldb_val ec_val = dn->ext_components[i].value; struct ldb_val val; int ret; - const struct ldb_dn_extended_syntax *extended_syntax; - const char *name = dn->extended_components[i].name; - - extended_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, name); + + ext_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, name); if (mode == 1) { - ret = extended_syntax->write_clear_fn(dn->ldb, mem_ctx, - &dn->extended_components[i].value, - &val); + ret = ext_syntax->write_clear_fn(dn->ldb, mem_ctx, + &ec_val, &val); } else if (mode == 0) { - ret = extended_syntax->write_hex_fn(dn->ldb, mem_ctx, - &dn->extended_components[i].value, - &val); + ret = ext_syntax->write_hex_fn(dn->ldb, mem_ctx, + &ec_val, &val); } else { ret = -1; } @@ -751,9 +782,11 @@ char *ldb_dn_get_extended_linearized(void *mem_ctx, struct ldb_dn *dn, int mode) } if (i == 0) { - p = talloc_asprintf(mem_ctx, "<%s=%s>", dn->extended_components[i].name, val.data); + p = talloc_asprintf(mem_ctx, "<%s=%s>", + name, val.data); } else { - p = talloc_asprintf_append(p, ";<%s=%s>", dn->extended_components[i].name, val.data); + p = talloc_asprintf_append(p, ";<%s=%s>", + name, val.data); } talloc_free(val.data); @@ -763,7 +796,7 @@ char *ldb_dn_get_extended_linearized(void *mem_ctx, struct ldb_dn *dn, int mode) } } - if (dn->extended_comp_num && *linearized) { + if (dn->ext_comp_num && *linearized) { p = talloc_asprintf_append(p, ";%s", linearized); } @@ -782,7 +815,7 @@ char *ldb_dn_alloc_linearized(void *mem_ctx, struct ldb_dn *dn) } /* - casefold a dn. We need to casefold the attribute names, and canonicalize + casefold a dn. We need to casefold the attribute names, and canonicalize attribute values of case insensitive attributes. */ @@ -801,12 +834,16 @@ static bool ldb_dn_casefold_internal(struct ldb_dn *dn) for (i = 0; i < dn->comp_num; i++) { const struct ldb_schema_attribute *a; - dn->components[i].cf_name = ldb_attr_casefold(dn->components, dn->components[i].name); + dn->components[i].cf_name = + ldb_attr_casefold(dn->components, + dn->components[i].name); if (!dn->components[i].cf_name) { goto failed; } - a = ldb_schema_attribute_by_name(dn->ldb, dn->components[i].cf_name); + a = ldb_schema_attribute_by_name(dn->ldb, + dn->components[i].cf_name); + ret = a->syntax->canonicalise_fn(dn->ldb, dn->components, &(dn->components[i].value), &(dn->components[i].cf_value)); @@ -834,7 +871,7 @@ const char *ldb_dn_get_casefold(struct ldb_dn *dn) if (dn->casefold) return dn->casefold; - if (dn->special) { + if (dn->special) { dn->casefold = talloc_strdup(dn, dn->linearized); if (!dn->casefold) return NULL; dn->valid_case = true; @@ -846,20 +883,16 @@ const char *ldb_dn_get_casefold(struct ldb_dn *dn) } if (dn->comp_num == 0) { - if (dn->linearized && dn->linearized[0] == '\0') { - /* hmm a NULL dn, should we faild casefolding ? */ - dn->casefold = talloc_strdup(dn, ""); - return dn->casefold; - } - /* A DN must be NULL, special, or have components */ - dn->invalid = true; - return NULL; + dn->casefold = talloc_strdup(dn, ""); + return dn->casefold; } /* calculate maximum possible length of DN */ for (len = 0, i = 0; i < dn->comp_num; i++) { - len += strlen(dn->components[i].cf_name); /* name len */ - len += (dn->components[i].cf_value.length * 3); /* max escaped data len */ + /* name len */ + len += strlen(dn->components[i].cf_name); + /* max escaped data len */ + len += (dn->components[i].cf_value.length * 3); len += 2; /* '=' and ',' */ } dn->casefold = talloc_array(dn, char, len); @@ -884,7 +917,8 @@ const char *ldb_dn_get_casefold(struct ldb_dn *dn) *(--d) = '\0'; /* don't waste more memory than necessary */ - dn->casefold = talloc_realloc(dn, dn->casefold, char, strlen(dn->casefold) + 1); + dn->casefold = talloc_realloc(dn, dn->casefold, + char, strlen(dn->casefold) + 1); return dn->casefold; } @@ -913,8 +947,13 @@ int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn) * we will avoid exploding and casfolding */ int dif; dif = strlen(dn->linearized) - strlen(base->linearized); - if (dif < 0) return dif; - if (strcmp(base->linearized, &dn->linearized[dif]) == 0) return 0; + if (dif < 0) { + return dif; + } + if (strcmp(base->linearized, + &dn->linearized[dif]) == 0) { + return 0; + } } if ( ! ldb_dn_casefold_internal(base)) { @@ -949,15 +988,24 @@ int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn) n_dn = dn->comp_num - 1; while (n_base >= 0) { + char *b_name = base->components[n_base].cf_name; + char *dn_name = dn->components[n_dn].cf_name; + + char *b_vdata = (char *)base->components[n_base].cf_value.data; + char *dn_vdata = (char *)dn->components[n_dn].cf_value.data; + + size_t b_vlen = base->components[n_base].cf_value.length; + size_t dn_vlen = dn->components[n_dn].cf_value.length; + /* compare attr names */ - ret = strcmp(base->components[n_base].cf_name, dn->components[n_dn].cf_name); + ret = strcmp(b_name, dn_name); if (ret != 0) return ret; - /* compare attr.cf_value. */ - if (base->components[n_base].cf_value.length != dn->components[n_dn].cf_value.length) { - return base->components[n_base].cf_value.length - dn->components[n_dn].cf_value.length; + /* compare attr.cf_value. */ + if (b_vlen != dn_vlen) { + return b_vlen - dn_vlen; } - ret = strcmp((char *)base->components[n_base].cf_value.data, (char *)dn->components[n_dn].cf_value.data); + ret = strcmp(b_vdata, dn_vdata); if (ret != 0) return ret; n_base--; @@ -967,7 +1015,7 @@ int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn) return 0; } -/* compare DNs using casefolding compare functions. +/* compare DNs using casefolding compare functions. If they match, then return 0 */ @@ -976,13 +1024,17 @@ int ldb_dn_compare(struct ldb_dn *dn0, struct ldb_dn *dn1) { int i, ret; - if (( ! dn0) || dn0->invalid || ! dn1 || dn1->invalid) return -1; + if (( ! dn0) || dn0->invalid || ! dn1 || dn1->invalid) { + return -1; + } if (( ! dn0->valid_case) || ( ! dn1->valid_case)) { if (dn0->linearized && dn1->linearized) { /* try with a normal compare first, if we are lucky * we will avoid exploding and casfolding */ - if (strcmp(dn0->linearized, dn1->linearized) == 0) return 0; + if (strcmp(dn0->linearized, dn1->linearized) == 0) { + return 0; + } } if ( ! ldb_dn_casefold_internal(dn0)) { @@ -1012,22 +1064,37 @@ int ldb_dn_compare(struct ldb_dn *dn0, struct ldb_dn *dn1) } for (i = 0; i < dn0->comp_num; i++) { + char *dn0_name = dn0->components[i].cf_name; + char *dn1_name = dn1->components[i].cf_name; + + char *dn0_vdata = (char *)dn0->components[i].cf_value.data; + char *dn1_vdata = (char *)dn1->components[i].cf_value.data; + + size_t dn0_vlen = dn0->components[i].cf_value.length; + size_t dn1_vlen = dn1->components[i].cf_value.length; + /* compare attr names */ - ret = strcmp(dn0->components[i].cf_name, dn1->components[i].cf_name); - if (ret != 0) return ret; + ret = strcmp(dn0_name, dn1_name); + if (ret != 0) { + return ret; + } - /* compare attr.cf_value. */ - if (dn0->components[i].cf_value.length != dn1->components[i].cf_value.length) { - return dn0->components[i].cf_value.length - dn1->components[i].cf_value.length; + /* compare attr.cf_value. */ + if (dn0_vlen != dn1_vlen) { + return dn0_vlen - dn1_vlen; + } + ret = strcmp(dn0_vdata, dn1_vdata); + if (ret != 0) { + return ret; } - ret = strcmp((char *)dn0->components[i].cf_value.data, (char *)dn1->components[i].cf_value.data); - if (ret != 0) return ret; } return 0; } -static struct ldb_dn_component ldb_dn_copy_component(void *mem_ctx, struct ldb_dn_component *src) +static struct ldb_dn_component ldb_dn_copy_component( + void *mem_ctx, + struct ldb_dn_component *src) { struct ldb_dn_component dst; @@ -1071,9 +1138,11 @@ static struct ldb_dn_component ldb_dn_copy_component(void *mem_ctx, struct ldb_d return dst; } -static struct ldb_dn_extended_component ldb_dn_extended_copy_component(void *mem_ctx, struct ldb_dn_extended_component *src) +static struct ldb_dn_ext_component ldb_dn_ext_copy_component( + void *mem_ctx, + struct ldb_dn_ext_component *src) { - struct ldb_dn_extended_component dst; + struct ldb_dn_ext_component dst; memset(&dst, 0, sizeof(dst)); @@ -1113,14 +1182,19 @@ struct ldb_dn *ldb_dn_copy(void *mem_ctx, struct ldb_dn *dn) if (dn->components) { int i; - new_dn->components = talloc_zero_array(new_dn, struct ldb_dn_component, dn->comp_num); + new_dn->components = + talloc_zero_array(new_dn, + struct ldb_dn_component, + dn->comp_num); if ( ! new_dn->components) { talloc_free(new_dn); return NULL; } for (i = 0; i < dn->comp_num; i++) { - new_dn->components[i] = ldb_dn_copy_component(new_dn->components, &dn->components[i]); + new_dn->components[i] = + ldb_dn_copy_component(new_dn->components, + &dn->components[i]); if ( ! new_dn->components[i].value.data) { talloc_free(new_dn); return NULL; @@ -1128,18 +1202,24 @@ struct ldb_dn *ldb_dn_copy(void *mem_ctx, struct ldb_dn *dn) } } - if (dn->extended_components) { + if (dn->ext_components) { int i; - new_dn->extended_components = talloc_zero_array(new_dn, struct ldb_dn_extended_component, dn->extended_comp_num); - if ( ! new_dn->extended_components) { + new_dn->ext_components = + talloc_zero_array(new_dn, + struct ldb_dn_ext_component, + dn->ext_comp_num); + if ( ! new_dn->ext_components) { talloc_free(new_dn); return NULL; } - for (i = 0; i < dn->extended_comp_num; i++) { - new_dn->extended_components[i] = ldb_dn_extended_copy_component(new_dn->extended_components, &dn->extended_components[i]); - if ( ! new_dn->extended_components[i].value.data) { + for (i = 0; i < dn->ext_comp_num; i++) { + new_dn->ext_components[i] = + ldb_dn_ext_copy_component( + new_dn->ext_components, + &dn->ext_components[i]); + if ( ! new_dn->ext_components[i].value.data) { talloc_free(new_dn); return NULL; } @@ -1162,9 +1242,10 @@ struct ldb_dn *ldb_dn_copy(void *mem_ctx, struct ldb_dn *dn) } } - if (dn->extended_linearized) { - new_dn->extended_linearized = talloc_strdup(new_dn, dn->extended_linearized); - if ( ! new_dn->extended_linearized) { + if (dn->ext_linearized) { + new_dn->ext_linearized = talloc_strdup(new_dn, + dn->ext_linearized); + if ( ! new_dn->ext_linearized) { talloc_free(new_dn); return NULL; } @@ -1211,7 +1292,9 @@ bool ldb_dn_add_base(struct ldb_dn *dn, struct ldb_dn *base) } for (i = 0; i < base->comp_num; dn->comp_num++, i++) { - dn->components[dn->comp_num] = ldb_dn_copy_component(dn->components, &base->components[i]); + dn->components[dn->comp_num] = + ldb_dn_copy_component(dn->components, + &base->components[i]); if (dn->components[dn->comp_num].value.data == NULL) { dn->invalid = true; return false; @@ -1220,7 +1303,8 @@ bool ldb_dn_add_base(struct ldb_dn *dn, struct ldb_dn *base) if (dn->casefold && s) { if (*dn->casefold) { - t = talloc_asprintf(dn, "%s,%s", dn->casefold, s); + t = talloc_asprintf(dn, "%s,%s", + dn->casefold, s); } else { t = talloc_strdup(dn, s); } @@ -1235,9 +1319,10 @@ bool ldb_dn_add_base(struct ldb_dn *dn, struct ldb_dn *base) if ( ! s) { return false; } - + if (*dn->linearized) { - t = talloc_asprintf(dn, "%s,%s", dn->linearized, s); + t = talloc_asprintf(dn, "%s,%s", + dn->linearized, s); } else { t = talloc_strdup(dn, s); } @@ -1249,13 +1334,14 @@ bool ldb_dn_add_base(struct ldb_dn *dn, struct ldb_dn *base) dn->linearized = t; } - /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */ - if (dn->extended_linearized) { - LDB_FREE(dn->extended_linearized); + /* Wipe the ext_linearized DN, + * the GUID and SID are almost certainly no longer valid */ + if (dn->ext_linearized) { + LDB_FREE(dn->ext_linearized); } - LDB_FREE(dn->extended_components); - dn->extended_comp_num = 0; + LDB_FREE(dn->ext_components); + dn->ext_comp_num = 0; return true; } @@ -1290,7 +1376,7 @@ bool ldb_dn_add_base_fmt(struct ldb_dn *dn, const char *base_fmt, ...) talloc_free(base_str); return ret; -} +} /* modify the given dn by adding children elements. * @@ -1335,8 +1421,10 @@ bool ldb_dn_add_child(struct ldb_dn *dn, struct ldb_dn *child) dn->components[j] = dn->components[i]; } - for (i = 0; i < child->comp_num; i++) { - dn->components[i] = ldb_dn_copy_component(dn->components, &child->components[i]); + for (i = 0; i < child->comp_num; i++) { + dn->components[i] = + ldb_dn_copy_component(dn->components, + &child->components[i]); if (dn->components[i].value.data == NULL) { dn->invalid = true; return false; @@ -1358,7 +1446,7 @@ bool ldb_dn_add_child(struct ldb_dn *dn, struct ldb_dn *child) if ( ! s) { return false; } - + t = talloc_asprintf(dn, "%s,%s", s, dn->linearized); if ( ! t) { dn->invalid = true; @@ -1368,11 +1456,12 @@ bool ldb_dn_add_child(struct ldb_dn *dn, struct ldb_dn *child) dn->linearized = t; } - /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */ - LDB_FREE(dn->extended_linearized); + /* Wipe the ext_linearized DN, + * the GUID and SID are almost certainly no longer valid */ + LDB_FREE(dn->ext_linearized); - LDB_FREE(dn->extended_components); - dn->extended_comp_num = 0; + LDB_FREE(dn->ext_components); + dn->ext_comp_num = 0; return true; } @@ -1429,7 +1518,7 @@ bool ldb_dn_remove_base_components(struct ldb_dn *dn, unsigned int num) LDB_FREE(dn->components[dn->comp_num - i].cf_name); LDB_FREE(dn->components[dn->comp_num - i].cf_value.data); } - + dn->comp_num -= num; if (dn->valid_case) { @@ -1443,11 +1532,12 @@ bool ldb_dn_remove_base_components(struct ldb_dn *dn, unsigned int num) LDB_FREE(dn->casefold); LDB_FREE(dn->linearized); - /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */ - LDB_FREE(dn->extended_linearized); + /* Wipe the ext_linearized DN, + * the GUID and SID are almost certainly no longer valid */ + LDB_FREE(dn->ext_linearized); - LDB_FREE(dn->extended_components); - dn->extended_comp_num = 0; + LDB_FREE(dn->ext_components); + dn->ext_comp_num = 0; return true; } @@ -1487,11 +1577,12 @@ bool ldb_dn_remove_child_components(struct ldb_dn *dn, unsigned int num) LDB_FREE(dn->casefold); LDB_FREE(dn->linearized); - /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */ - LDB_FREE(dn->extended_linearized); + /* Wipe the ext_linearized DN, + * the GUID and SID are almost certainly no longer valid */ + LDB_FREE(dn->ext_linearized); - LDB_FREE(dn->extended_components); - dn->extended_comp_num = 0; + LDB_FREE(dn->ext_components); + dn->ext_comp_num = 0; return true; } @@ -1509,11 +1600,12 @@ struct ldb_dn *ldb_dn_get_parent(void *mem_ctx, struct ldb_dn *dn) return NULL; } - /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */ - LDB_FREE(dn->extended_linearized); + /* Wipe the ext_linearized DN, + * the GUID and SID are almost certainly no longer valid */ + LDB_FREE(dn->ext_linearized); - LDB_FREE(dn->extended_components); - dn->extended_comp_num = 0; + LDB_FREE(dn->ext_components); + dn->ext_comp_num = 0; return new_dn; } @@ -1522,7 +1614,8 @@ struct ldb_dn *ldb_dn_get_parent(void *mem_ctx, struct ldb_dn *dn) ie dc=samba,dc=org -> samba.org/ uid=administrator,ou=users,dc=samba,dc=org = samba.org/users/administrator - There are two formats, the EX format has the last / replaced with a newline (\n). + There are two formats, + the EX format has the last '/' replaced with a newline (\n). */ static char *ldb_dn_canonical(void *mem_ctx, struct ldb_dn *dn, int ex_format) { @@ -1530,7 +1623,7 @@ static char *ldb_dn_canonical(void *mem_ctx, struct ldb_dn *dn, int ex_format) { TALLOC_CTX *tmpctx; char *cracked = NULL; const char *format = (ex_format ? "\n" : "/" ); - + if ( ! ldb_dn_validate(dn)) { return NULL; } @@ -1544,10 +1637,12 @@ static char *ldb_dn_canonical(void *mem_ctx, struct ldb_dn *dn, int ex_format) { } if (cracked) { cracked = talloc_asprintf(tmpctx, "%s.%s", - ldb_dn_escape_value(tmpctx, dn->components[i].value), + ldb_dn_escape_value(tmpctx, + dn->components[i].value), cracked); } else { - cracked = ldb_dn_escape_value(tmpctx, dn->components[i].value); + cracked = ldb_dn_escape_value(tmpctx, + dn->components[i].value); } if (!cracked) { goto done; @@ -1563,8 +1658,9 @@ static char *ldb_dn_canonical(void *mem_ctx, struct ldb_dn *dn, int ex_format) { /* Now walk backwards appending remaining components */ for (; i > 0; i--) { - cracked = talloc_asprintf_append_buffer(cracked, "/%s", - ldb_dn_escape_value(tmpctx, dn->components[i].value)); + cracked = talloc_asprintf_append_buffer(cracked, "/%s", + ldb_dn_escape_value(tmpctx, + dn->components[i].value)); if (!cracked) { goto done; } @@ -1572,7 +1668,8 @@ static char *ldb_dn_canonical(void *mem_ctx, struct ldb_dn *dn, int ex_format) { /* Last one, possibly a newline for the 'ex' format */ cracked = talloc_asprintf_append_buffer(cracked, "%s%s", format, - ldb_dn_escape_value(tmpctx, dn->components[i].value)); + ldb_dn_escape_value(tmpctx, + dn->components[i].value)); talloc_steal(mem_ctx, cracked); done: @@ -1607,7 +1704,8 @@ const char *ldb_dn_get_component_name(struct ldb_dn *dn, unsigned int num) return dn->components[num].name; } -const struct ldb_val *ldb_dn_get_component_val(struct ldb_dn *dn, unsigned int num) +const struct ldb_val *ldb_dn_get_component_val(struct ldb_dn *dn, + unsigned int num) { if ( ! ldb_dn_validate(dn)) { return NULL; @@ -1634,7 +1732,8 @@ const struct ldb_val *ldb_dn_get_rdn_val(struct ldb_dn *dn) return &dn->components[0].value; } -int ldb_dn_set_component(struct ldb_dn *dn, int num, const char *name, const struct ldb_val val) +int ldb_dn_set_component(struct ldb_dn *dn, int num, + const char *name, const struct ldb_val val) { char *n; struct ldb_val v; @@ -1675,60 +1774,68 @@ int ldb_dn_set_component(struct ldb_dn *dn, int num, const char *name, const str LDB_FREE(dn->casefold); LDB_FREE(dn->linearized); - /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */ - LDB_FREE(dn->extended_linearized); + /* Wipe the ext_linearized DN, + * the GUID and SID are almost certainly no longer valid */ + LDB_FREE(dn->ext_linearized); - dn->extended_comp_num = 0; - LDB_FREE(dn->extended_components); + dn->ext_comp_num = 0; + LDB_FREE(dn->ext_components); return LDB_SUCCESS; } -const struct ldb_val *ldb_dn_get_extended_component(struct ldb_dn *dn, const char *name) +const struct ldb_val *ldb_dn_get_extended_component(struct ldb_dn *dn, + const char *name) { int i; if ( ! ldb_dn_validate(dn)) { return NULL; } - for (i=0; i < dn->extended_comp_num; i++) { - if (ldb_attr_cmp(dn->extended_components[i].name, name) == 0) { - return &dn->extended_components[i].value; + for (i=0; i < dn->ext_comp_num; i++) { + if (ldb_attr_cmp(dn->ext_components[i].name, name) == 0) { + return &dn->ext_components[i].value; } } return NULL; } -int ldb_dn_set_extended_component(struct ldb_dn *dn, const char *name, const struct ldb_val *val) +int ldb_dn_set_extended_component(struct ldb_dn *dn, + const char *name, const struct ldb_val *val) { - struct ldb_dn_extended_component *p; + struct ldb_dn_ext_component *p; int i; - + if ( ! ldb_dn_validate(dn)) { return LDB_ERR_OTHER; } - for (i=0; i < dn->extended_comp_num; i++) { - if (ldb_attr_cmp(dn->extended_components[i].name, name) == 0) { + for (i=0; i < dn->ext_comp_num; i++) { + if (ldb_attr_cmp(dn->ext_components[i].name, name) == 0) { if (val) { - dn->extended_components[i].value = ldb_val_dup(dn->extended_components, val); + dn->ext_components[i].value = + ldb_val_dup(dn->ext_components, val); - dn->extended_components[i].name = talloc_strdup(dn->extended_components, name); - if (!dn->extended_components[i].name || !dn->extended_components[i].value.data) { + dn->ext_components[i].name = + talloc_strdup(dn->ext_components, name); + if (!dn->ext_components[i].name || + !dn->ext_components[i].value.data) { dn->invalid = true; return LDB_ERR_OPERATIONS_ERROR; } - + } else { - if (i != (dn->extended_comp_num - 1)) { - memmove(&dn->extended_components[i], &dn->extended_components[i+1], - ((dn->extended_comp_num-1) - i)*sizeof(*dn->extended_components)); + if (i != (dn->ext_comp_num - 1)) { + memmove(&dn->ext_components[i], + &dn->ext_components[i+1], + ((dn->ext_comp_num-1) - i) * + sizeof(*dn->ext_components)); } - dn->extended_comp_num--; - - dn->extended_components = talloc_realloc(dn, - dn->extended_components, - struct ldb_dn_extended_component, - dn->extended_comp_num); - if (!dn->extended_components) { + dn->ext_comp_num--; + + dn->ext_components = talloc_realloc(dn, + dn->ext_components, + struct ldb_dn_ext_component, + dn->ext_comp_num); + if (!dn->ext_components) { dn->invalid = true; return LDB_ERR_OPERATIONS_ERROR; } @@ -1737,33 +1844,33 @@ int ldb_dn_set_extended_component(struct ldb_dn *dn, const char *name, const str } } - p = dn->extended_components + p = dn->ext_components = talloc_realloc(dn, - dn->extended_components, - struct ldb_dn_extended_component, - dn->extended_comp_num + 1); - if (!dn->extended_components) { + dn->ext_components, + struct ldb_dn_ext_component, + dn->ext_comp_num + 1); + if (!dn->ext_components) { dn->invalid = true; return LDB_ERR_OPERATIONS_ERROR; } - - p[dn->extended_comp_num].value = ldb_val_dup(dn->extended_components, val); - p[dn->extended_comp_num].name = talloc_strdup(p, name); - - if (!dn->extended_components[i].name || !dn->extended_components[i].value.data) { + + p[dn->ext_comp_num].value = ldb_val_dup(dn->ext_components, val); + p[dn->ext_comp_num].name = talloc_strdup(p, name); + + if (!dn->ext_components[i].name || !dn->ext_components[i].value.data) { dn->invalid = true; return LDB_ERR_OPERATIONS_ERROR; } - dn->extended_components = p; - dn->extended_comp_num++; - + dn->ext_components = p; + dn->ext_comp_num++; + return LDB_SUCCESS; } void ldb_dn_remove_extended_components(struct ldb_dn *dn) { - dn->extended_comp_num = 0; - LDB_FREE(dn->extended_components); + dn->ext_comp_num = 0; + LDB_FREE(dn->ext_components); } bool ldb_dn_is_valid(struct ldb_dn *dn) @@ -1781,8 +1888,8 @@ bool ldb_dn_is_special(struct ldb_dn *dn) bool ldb_dn_has_extended(struct ldb_dn *dn) { if ( ! dn || dn->invalid) return false; - if (dn->extended_linearized && (dn->extended_linearized[0] == '<')) return true; - return dn->extended_comp_num != 0; + if (dn->ext_linearized && (dn->ext_linearized[0] == '<')) return true; + return dn->ext_comp_num != 0; } bool ldb_dn_check_special(struct ldb_dn *dn, const char *check) diff --git a/source4/lib/ldb/common/ldb_ldif.c b/source4/lib/ldb/common/ldb_ldif.c index 400fb352ff..b7ab7300b2 100644 --- a/source4/lib/ldb/common/ldb_ldif.c +++ b/source4/lib/ldb/common/ldb_ldif.c @@ -185,11 +185,15 @@ char *ldb_base64_encode(void *mem_ctx, const char *buf, int len) /* see if a buffer should be base64 encoded */ -int ldb_should_b64_encode(const struct ldb_val *val) +int ldb_should_b64_encode(struct ldb_context *ldb, const struct ldb_val *val) { unsigned int i; uint8_t *p = val->data; + if (ldb->flags & LDB_FLG_SHOW_BINARY) { + return 0; + } + if (val->length == 0) { return 0; } @@ -296,7 +300,7 @@ int ldb_ldif_write(struct ldb_context *ldb, } } if (!ldb_changetypes[i].name) { - ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: Invalid ldif changetype %d\n", + ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: Invalid ldif changetype %d", ldif->changetype); talloc_free(mem_ctx); return -1; @@ -333,7 +337,7 @@ int ldb_ldif_write(struct ldb_context *ldb, if (ret != LDB_SUCCESS) { v = msg->elements[i].values[j]; } - if (ret != LDB_SUCCESS || ldb_should_b64_encode(&v)) { + if (ret != LDB_SUCCESS || ldb_should_b64_encode(ldb, &v)) { ret = fprintf_fn(private_data, "%s:: ", msg->elements[i].name); CHECK_RET; @@ -346,9 +350,14 @@ int ldb_ldif_write(struct ldb_context *ldb, } else { ret = fprintf_fn(private_data, "%s: ", msg->elements[i].name); CHECK_RET; - ret = fold_string(fprintf_fn, private_data, - (char *)v.data, v.length, - strlen(msg->elements[i].name)+2); + if (ldb->flags & LDB_FLG_SHOW_BINARY) { + ret = fprintf_fn(private_data, "%*.*s", + v.length, v.length, (char *)v.data); + } else { + ret = fold_string(fprintf_fn, private_data, + (char *)v.data, v.length, + strlen(msg->elements[i].name)+2); + } CHECK_RET; ret = fprintf_fn(private_data, "\n"); CHECK_RET; @@ -561,7 +570,7 @@ struct ldb_ldif *ldb_ldif_read(struct ldb_context *ldb, /* first line must be a dn */ if (ldb_attr_cmp(attr, "dn") != 0) { - ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: First line of ldif must be a dn not '%s'\n", + ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: First line of ldif must be a dn not '%s'", attr); goto failed; } @@ -569,7 +578,7 @@ struct ldb_ldif *ldb_ldif_read(struct ldb_context *ldb, msg->dn = ldb_dn_from_ldb_val(msg, ldb, &value); if ( ! ldb_dn_validate(msg->dn)) { - ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: Unable to parse dn '%s'\n", + ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: Unable to parse dn '%s'", (char *)value.data); goto failed; } @@ -588,8 +597,8 @@ struct ldb_ldif *ldb_ldif_read(struct ldb_context *ldb, } } if (!ldb_changetypes[i].name) { - ldb_debug(ldb, LDB_DEBUG_ERROR, - "Error: Bad ldif changetype '%s'\n",(char *)value.data); + ldb_debug(ldb, LDB_DEBUG_ERROR, + "Error: Bad ldif changetype '%s'",(char *)value.data); } flags = 0; continue; @@ -632,13 +641,13 @@ struct ldb_ldif *ldb_ldif_read(struct ldb_context *ldb, if (!el->values) { goto failed; } - ret = a->syntax->ldif_read_fn(ldb, ldif, &value, &el->values[el->num_values]); + ret = a->syntax->ldif_read_fn(ldb, el->values, &value, &el->values[el->num_values]); if (ret != 0) { goto failed; } if (value.length == 0) { ldb_debug(ldb, LDB_DEBUG_ERROR, - "Error: Attribute value cannot be empty for attribute '%s'\n", el->name); + "Error: Attribute value cannot be empty for attribute '%s'", el->name); goto failed; } if (value.data != el->values[el->num_values].data) { @@ -647,7 +656,7 @@ struct ldb_ldif *ldb_ldif_read(struct ldb_context *ldb, el->num_values++; } else { /* its a new attribute */ - msg->elements = talloc_realloc(ldif, msg->elements, + msg->elements = talloc_realloc(msg, msg->elements, struct ldb_message_element, msg->num_elements+1); if (!msg->elements) { @@ -661,7 +670,7 @@ struct ldb_ldif *ldb_ldif_read(struct ldb_context *ldb, goto failed; } el->num_values = 1; - ret = a->syntax->ldif_read_fn(ldb, ldif, &value, &el->values[0]); + ret = a->syntax->ldif_read_fn(ldb, el->values, &value, &el->values[0]); if (ret != 0) { goto failed; } @@ -759,3 +768,59 @@ int ldb_ldif_write_file(struct ldb_context *ldb, FILE *f, const struct ldb_ldif state.f = f; return ldb_ldif_write(ldb, fprintf_file, &state, ldif); } + +/* + wrapper around ldif_write() for a string +*/ +struct ldif_write_string_state { + char *string; +}; + +static int ldif_printf_string(void *private_data, const char *fmt, ...) PRINTF_ATTRIBUTE(2, 3); + +static int ldif_printf_string(void *private_data, const char *fmt, ...) +{ + struct ldif_write_string_state *state = + (struct ldif_write_string_state *)private_data; + va_list ap; + size_t oldlen = talloc_get_size(state->string); + va_start(ap, fmt); + + state->string = talloc_vasprintf_append(state->string, fmt, ap); + va_end(ap); + if (!state->string) { + return -1; + } + + return talloc_get_size(state->string) - oldlen; +} + +char *ldb_ldif_write_string(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, + const struct ldb_ldif *ldif) +{ + struct ldif_write_string_state state; + state.string = talloc_strdup(mem_ctx, ""); + if (!state.string) { + return NULL; + } + if (ldb_ldif_write(ldb, ldif_printf_string, &state, ldif) == -1) { + return NULL; + } + return state.string; +} + +/* + convenient function to turn a ldb_message into a string. Useful for + debugging + */ +char *ldb_ldif_message_string(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, + enum ldb_changetype changetype, + const struct ldb_message *msg) +{ + struct ldb_ldif ldif; + + ldif.changetype = changetype; + ldif.msg = discard_const_p(struct ldb_message, msg); + + return ldb_ldif_write_string(ldb, mem_ctx, &ldif); +} diff --git a/source4/lib/ldb/common/ldb_match.c b/source4/lib/ldb/common/ldb_match.c index c622701d30..e6ee0de027 100644 --- a/source4/lib/ldb/common/ldb_match.c +++ b/source4/lib/ldb/common/ldb_match.c @@ -335,7 +335,7 @@ static int ldb_match_extended(struct ldb_context *ldb, } } if (comp == NULL) { - ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: unknown extended rule_id %s\n", + ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: unknown extended rule_id %s", tree->u.extended.rule_id); return -1; } diff --git a/source4/lib/ldb/common/ldb_modules.c b/source4/lib/ldb/common/ldb_modules.c index ae97ef4cce..ea29a09a2a 100644 --- a/source4/lib/ldb/common/ldb_modules.c +++ b/source4/lib/ldb/common/ldb_modules.c @@ -1,4 +1,4 @@ -/* +/* ldb database library Copyright (C) Simo Sorce 2004-2008 @@ -6,7 +6,7 @@ ** NOTE! The following LGPL license applies to the ldb ** library. This does NOT imply that all of Samba is released ** under the LGPL - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -84,13 +84,13 @@ const char **ldb_modules_list_from_string(struct ldb_context *ldb, TALLOC_CTX *m /* spaces not admitted */ modstr = ldb_modules_strdup_no_spaces(mem_ctx, string); if ( ! modstr) { - ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_strdup_no_spaces()\n"); + ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_strdup_no_spaces()"); return NULL; } modules = talloc_realloc(mem_ctx, modules, char *, 2); if ( ! modules ) { - ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_list_from_string()\n"); + ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_list_from_string()"); talloc_free(modstr); return NULL; } @@ -106,7 +106,7 @@ const char **ldb_modules_list_from_string(struct ldb_context *ldb, TALLOC_CTX *m i++; modules = talloc_realloc(mem_ctx, modules, char *, i + 2); if ( ! modules ) { - ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_list_from_string()\n"); + ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_list_from_string()"); return NULL; } @@ -127,7 +127,7 @@ static struct backends_list_entry { static struct ops_list_entry { const struct ldb_module_ops *ops; - struct ops_list_entry *next; + struct ops_list_entry *next; } *registered_modules = NULL; static const struct ldb_builtins { @@ -239,7 +239,7 @@ int ldb_connect_backend(struct ldb_context *ldb, if (fn == NULL) { ldb_debug(ldb, LDB_DEBUG_FATAL, - "Unable to find backend for '%s'\n", url); + "Unable to find backend for '%s'", url); return LDB_ERR_OTHER; } @@ -247,7 +247,7 @@ int ldb_connect_backend(struct ldb_context *ldb, if (ret != LDB_SUCCESS) { ldb_debug(ldb, LDB_DEBUG_ERROR, - "Failed to connect to '%s'\n", url); + "Failed to connect to '%s'", url); return ret; } return ret; @@ -264,9 +264,9 @@ static const struct ldb_module_ops *ldb_find_module_ops(const char *name) if (strcmp(builtins[i].module_ops->name, name) == 0) return builtins[i].module_ops; } - + for (e = registered_modules; e; e = e->next) { - if (strcmp(e->ops->name, name) == 0) + if (strcmp(e->ops->name, name) == 0) return e->ops; } @@ -301,21 +301,21 @@ static void *ldb_dso_load_symbol(struct ldb_context *ldb, const char *name, if (ldb->modules_dir == NULL) return NULL; - path = talloc_asprintf(ldb, "%s/%s.%s", ldb->modules_dir, name, + path = talloc_asprintf(ldb, "%s/%s.%s", ldb->modules_dir, name, SHLIBEXT); - ldb_debug(ldb, LDB_DEBUG_TRACE, "trying to load %s from %s\n", name, path); + ldb_debug(ldb, LDB_DEBUG_TRACE, "trying to load %s from %s", name, path); handle = dlopen(path, RTLD_NOW); if (handle == NULL) { - ldb_debug(ldb, LDB_DEBUG_WARNING, "unable to load %s from %s: %s\n", name, path, dlerror()); + ldb_debug(ldb, LDB_DEBUG_WARNING, "unable to load %s from %s: %s", name, path, dlerror()); return NULL; } sym = (int (*)(void))dlsym(handle, symbol); if (sym == NULL) { - ldb_debug(ldb, LDB_DEBUG_ERROR, "no symbol `%s' found in %s: %s\n", symbol, path, dlerror()); + ldb_debug(ldb, LDB_DEBUG_ERROR, "no symbol `%s' found in %s: %s", symbol, path, dlerror()); return NULL; } @@ -328,7 +328,7 @@ int ldb_load_modules_list(struct ldb_context *ldb, const char **module_list, str { struct ldb_module *module; int i; - + module = backend; for (i = 0; module_list[i] != NULL; i++) { @@ -338,10 +338,10 @@ int ldb_load_modules_list(struct ldb_context *ldb, const char **module_list, str if (strcmp(module_list[i], "") == 0) { continue; } - + ops = ldb_find_module_ops(module_list[i]); if (ops == NULL) { - char *symbol_name = talloc_asprintf(ldb, "ldb_%s_module_ops", + char *symbol_name = talloc_asprintf(ldb, "ldb_%s_module_ops", module_list[i]); if (symbol_name == NULL) { return LDB_ERR_OPERATIONS_ERROR; @@ -349,31 +349,31 @@ int ldb_load_modules_list(struct ldb_context *ldb, const char **module_list, str ops = ldb_dso_load_symbol(ldb, module_list[i], symbol_name); talloc_free(symbol_name); } - + if (ops == NULL) { - ldb_debug(ldb, LDB_DEBUG_WARNING, "WARNING: Module [%s] not found\n", + ldb_debug(ldb, LDB_DEBUG_WARNING, "WARNING: Module [%s] not found", module_list[i]); continue; } - + current = talloc_zero(ldb, struct ldb_module); if (current == NULL) { return LDB_ERR_OPERATIONS_ERROR; } talloc_set_name(current, "ldb_module: %s", module_list[i]); - + current->ldb = ldb; current->ops = ops; - + DLIST_ADD(module, current); } *out = module; return LDB_SUCCESS; } -int ldb_init_module_chain(struct ldb_context *ldb, struct ldb_module *module) +int ldb_init_module_chain(struct ldb_context *ldb, struct ldb_module *module) { - while (module && module->ops->init_context == NULL) + while (module && module->ops->init_context == NULL) module = module->next; /* init is different in that it is not an error if modules @@ -382,7 +382,7 @@ int ldb_init_module_chain(struct ldb_context *ldb, struct ldb_module *module) if (module) { int ret = module->ops->init_context(module); if (ret != LDB_SUCCESS) { - ldb_debug(ldb, LDB_DEBUG_FATAL, "module %s initialization failed\n", module->ops->name); + ldb_debug(ldb, LDB_DEBUG_FATAL, "module %s initialization failed", module->ops->name); return ret; } } @@ -412,7 +412,7 @@ int ldb_load_modules(struct ldb_context *ldb, const char *options[]) } /* if not overloaded by options and the backend is not ldap try to load the modules list from ldb */ - if ((modules == NULL) && (strcmp("ldap", ldb->modules->ops->name) != 0)) { + if ((modules == NULL) && (strcmp("ldap", ldb->modules->ops->name) != 0)) { const char * const attrs[] = { "@LIST" , NULL}; struct ldb_result *res = NULL; struct ldb_dn *mods_dn; @@ -424,11 +424,11 @@ int ldb_load_modules(struct ldb_context *ldb, const char *options[]) } ret = ldb_search(ldb, mods_dn, &res, mods_dn, LDB_SCOPE_BASE, attrs, "@LIST=*"); - + if (ret == LDB_ERR_NO_SUCH_OBJECT) { ldb_debug(ldb, LDB_DEBUG_TRACE, "no modules required by the db"); } else if (ret != LDB_SUCCESS) { - ldb_debug(ldb, LDB_DEBUG_FATAL, "ldb error (%s) occurred searching for modules, bailing out\n", ldb_errstring(ldb)); + ldb_debug(ldb, LDB_DEBUG_FATAL, "ldb error (%s) occurred searching for modules, bailing out", ldb_errstring(ldb)); talloc_free(mem_ctx); return ret; } else { @@ -436,7 +436,7 @@ int ldb_load_modules(struct ldb_context *ldb, const char *options[]) if (res->count == 0) { ldb_debug(ldb, LDB_DEBUG_TRACE, "no modules required by the db"); } else if (res->count > 1) { - ldb_debug(ldb, LDB_DEBUG_FATAL, "Too many records found (%d), bailing out\n", res->count); + ldb_debug(ldb, LDB_DEBUG_FATAL, "Too many records found (%d), bailing out", res->count); talloc_free(mem_ctx); return -1; } else { @@ -472,10 +472,14 @@ int ldb_load_modules(struct ldb_context *ldb, const char *options[]) which makes writing a module simpler, and makes it more likely to keep working when ldb is extended */ -#define FIND_OP(module, op) do { \ - struct ldb_context *ldb = module->ldb; \ +#define FIND_OP_NOERR(module, op) do { \ module = module->next; \ while (module && module->ops->op == NULL) module = module->next; \ +} while (0) + +#define FIND_OP(module, op) do { \ + struct ldb_context *ldb = module->ldb; \ + FIND_OP_NOERR(module, op); \ if (module == NULL) { \ ldb_asprintf_errstring(ldb, "Unable to find backend operation for " #op ); \ return LDB_ERR_OPERATIONS_ERROR; \ @@ -536,6 +540,8 @@ int ldb_next_request(struct ldb_module *module, struct ldb_request *request) return LDB_ERR_UNWILLING_TO_PERFORM; } + request->handle->nesting++; + switch (request->operation) { case LDB_SEARCH: FIND_OP(module, search); @@ -566,6 +572,9 @@ int ldb_next_request(struct ldb_module *module, struct ldb_request *request) ret = module->ops->request(module, request); break; } + + request->handle->nesting--; + if (ret == LDB_SUCCESS) { return ret; } @@ -573,6 +582,17 @@ int ldb_next_request(struct ldb_module *module, struct ldb_request *request) /* Set a default error string, to place the blame somewhere */ ldb_asprintf_errstring(module->ldb, "error in module %s: %s (%d)", module->ops->name, ldb_strerror(ret), ret); } + + if (!(request->handle->flags & LDB_HANDLE_FLAG_DONE_CALLED)) { + /* It is _extremely_ common that a module returns a + * failure without calling ldb_module_done(), but that + * guarantees we will end up hanging in + * ldb_wait(). This fixes it without having to rewrite + * all our modules, and leaves us one less sharp + * corner for module developers to cut themselves on + */ + ldb_module_done(request, NULL, NULL, ret); + } return ret; } @@ -595,6 +615,17 @@ int ldb_next_end_trans(struct ldb_module *module) return module->ops->end_transaction(module); } +int ldb_next_prepare_commit(struct ldb_module *module) +{ + FIND_OP_NOERR(module, prepare_commit); + if (module == NULL) { + /* we are allowed to have no prepare commit in + backends */ + return LDB_SUCCESS; + } + return module->ops->prepare_commit(module); +} + int ldb_next_del_trans(struct ldb_module *module) { FIND_OP(module, del_transaction); @@ -614,6 +645,7 @@ struct ldb_handle *ldb_handle_new(TALLOC_CTX *mem_ctx, struct ldb_context *ldb) h->status = LDB_SUCCESS; h->state = LDB_ASYNC_INIT; h->ldb = ldb; + h->flags = 0; return h; } @@ -645,6 +677,16 @@ int ldb_module_send_entry(struct ldb_request *req, ares->controls = talloc_steal(ares, ctrls); ares->error = LDB_SUCCESS; + if ((req->handle->ldb->flags & LDB_FLG_ENABLE_TRACING) && + req->handle->nesting == 0) { + char *s; + ldb_debug_add(req->handle->ldb, "ldb_trace_response: ENTRY\n"); + s = ldb_ldif_message_string(req->handle->ldb, msg, LDB_CHANGETYPE_NONE, msg); + ldb_debug_add(req->handle->ldb, "%s\n", s); + talloc_free(s); + ldb_debug_end(req->handle->ldb, LDB_DEBUG_TRACE); + } + return req->callback(req, ares); } @@ -670,6 +712,13 @@ int ldb_module_send_referral(struct ldb_request *req, ares->referral = talloc_steal(ares, ref); ares->error = LDB_SUCCESS; + if ((req->handle->ldb->flags & LDB_FLG_ENABLE_TRACING) && + req->handle->nesting == 0) { + ldb_debug_add(req->handle->ldb, "ldb_trace_response: REFERRAL\n"); + ldb_debug_add(req->handle->ldb, "ref: %s\n", ref); + ldb_debug_end(req->handle->ldb, LDB_DEBUG_TRACE); + } + return req->callback(req, ares); } @@ -700,6 +749,19 @@ int ldb_module_done(struct ldb_request *req, ares->response = talloc_steal(ares, response); ares->error = error; + req->handle->flags |= LDB_HANDLE_FLAG_DONE_CALLED; + + if ((req->handle->ldb->flags & LDB_FLG_ENABLE_TRACING) && + req->handle->nesting == 0) { + ldb_debug_add(req->handle->ldb, "ldb_trace_response: DONE\n"); + ldb_debug_add(req->handle->ldb, "error: %u\n", error); + if (ldb_errstring(req->handle->ldb)) { + ldb_debug_add(req->handle->ldb, "msg: %s\n", + ldb_errstring(req->handle->ldb)); + } + ldb_debug_end(req->handle->ldb, LDB_DEBUG_TRACE); + } + req->callback(req, ares); return error; } @@ -756,7 +818,6 @@ int ldb_mod_register_control(struct ldb_module *module, const char *oid) LDB_BACKEND(tdb), \ LDAP_BACKEND \ SQLITE3_BACKEND \ - LDB_MODULE(operational), \ LDB_MODULE(rdn_name), \ LDB_MODULE(paged_results), \ LDB_MODULE(server_sort), \ diff --git a/source4/lib/ldb/common/ldb_msg.c b/source4/lib/ldb/common/ldb_msg.c index ad53a3d29d..702978a361 100644 --- a/source4/lib/ldb/common/ldb_msg.c +++ b/source4/lib/ldb/common/ldb_msg.c @@ -643,12 +643,12 @@ const char **ldb_attr_list_copy(TALLOC_CTX *mem_ctx, const char * const *attrs) { const char **ret; int i; - for (i=0;attrs[i];i++) /* noop */ ; + for (i=0;attrs && attrs[i];i++) /* noop */ ; ret = talloc_array(mem_ctx, const char *, i+1); if (ret == NULL) { return NULL; } - for (i=0;attrs[i];i++) { + for (i=0;attrs && attrs[i];i++) { ret[i] = attrs[i]; } ret[i] = attrs[i]; @@ -665,7 +665,7 @@ const char **ldb_attr_list_copy_add(TALLOC_CTX *mem_ctx, const char * const *att const char **ret; int i; bool found = false; - for (i=0;attrs[i];i++) { + for (i=0;attrs && attrs[i];i++) { if (ldb_attr_cmp(attrs[i], new_attr) == 0) { found = true; } @@ -677,7 +677,7 @@ const char **ldb_attr_list_copy_add(TALLOC_CTX *mem_ctx, const char * const *att if (ret == NULL) { return NULL; } - for (i=0;attrs[i];i++) { + for (i=0;attrs && attrs[i];i++) { ret[i] = attrs[i]; } ret[i] = new_attr; @@ -739,6 +739,10 @@ int ldb_msg_copy_attr(struct ldb_message *msg, const char *attr, const char *rep void ldb_msg_remove_element(struct ldb_message *msg, struct ldb_message_element *el) { int n = (el - msg->elements); + if (n >= msg->num_elements) { + /* should we abort() here? */ + return; + } if (n != msg->num_elements-1) { memmove(el, el+1, ((msg->num_elements-1) - n)*sizeof(*el)); } diff --git a/source4/lib/ldb/common/ldb_parse.c b/source4/lib/ldb/common/ldb_parse.c index 654a635abf..ba16b57da3 100644 --- a/source4/lib/ldb/common/ldb_parse.c +++ b/source4/lib/ldb/common/ldb_parse.c @@ -267,7 +267,8 @@ static enum ldb_parse_op ldb_parse_filtertype(void *mem_ctx, char **type, char * p++; } - while ((isascii(*p) && isalnum((unsigned char)*p)) || (*p == '-')) { /* attribute names can only be alphanums */ + while ((isascii(*p) && isalnum((unsigned char)*p)) || (*p == '-') || (*p == '.')) { + /* attribute names can only be alphanums */ p++; } @@ -817,3 +818,61 @@ void ldb_parse_tree_attr_replace(struct ldb_parse_tree *tree, break; } } + +/* + shallow copy a tree - copying only the elements array so that the caller + can safely add new elements without changing the message +*/ +struct ldb_parse_tree *ldb_parse_tree_copy_shallow(TALLOC_CTX *mem_ctx, + const struct ldb_parse_tree *ot) +{ + int i; + struct ldb_parse_tree *nt; + + nt = talloc(mem_ctx, struct ldb_parse_tree); + if (!nt) { + return NULL; + } + + *nt = *ot; + + switch (ot->operation) { + case LDB_OP_AND: + case LDB_OP_OR: + nt->u.list.elements = talloc_array(nt, struct ldb_parse_tree *, + ot->u.list.num_elements); + if (!nt->u.list.elements) { + talloc_free(nt); + return NULL; + } + + for (i=0;i<ot->u.list.num_elements;i++) { + nt->u.list.elements[i] = + ldb_parse_tree_copy_shallow(nt->u.list.elements, + ot->u.list.elements[i]); + if (!nt->u.list.elements[i]) { + talloc_free(nt); + return NULL; + } + } + break; + case LDB_OP_NOT: + nt->u.isnot.child = ldb_parse_tree_copy_shallow(nt, + ot->u.isnot.child); + if (!nt->u.isnot.child) { + talloc_free(nt); + return NULL; + } + break; + case LDB_OP_EQUALITY: + case LDB_OP_GREATER: + case LDB_OP_LESS: + case LDB_OP_APPROX: + case LDB_OP_SUBSTRING: + case LDB_OP_PRESENT: + case LDB_OP_EXTENDED: + break; + } + + return nt; +} diff --git a/source4/lib/ldb/config.guess b/source4/lib/ldb/config.guess index 4af85584ae..da83314608 100755 --- a/source4/lib/ldb/config.guess +++ b/source4/lib/ldb/config.guess @@ -808,7 +808,7 @@ EOF echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:[3456]*) - case ${UNAME_MACHINE} in + case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; @@ -984,7 +984,7 @@ EOF echo x86_64-unknown-linux-gnu exit ;; xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; i*86:Linux:*:*) # The BFD linker knows what the default object file format is, so diff --git a/source4/lib/ldb/config.mk b/source4/lib/ldb/config.mk index 6fcf3943d0..4a1f814baa 100644 --- a/source4/lib/ldb/config.mk +++ b/source4/lib/ldb/config.mk @@ -47,18 +47,6 @@ SUBSYSTEM = LIBLDB ldb_paged_searches_OBJ_FILES = $(ldbsrcdir)/modules/paged_searches.o ################################################ -# Start MODULE ldb_operational -[MODULE::ldb_operational] -SUBSYSTEM = LIBLDB -CFLAGS = -I$(ldbsrcdir)/include -PRIVATE_DEPENDENCIES = LIBTALLOC LIBTEVENT -INIT_FUNCTION = LDB_MODULE(operational) -# End MODULE ldb_operational -################################################ - -ldb_operational_OBJ_FILES = $(ldbsrcdir)/modules/operational.o - -################################################ # Start MODULE ldb_rdn_name [MODULE::ldb_rdn_name] SUBSYSTEM = LIBLDB diff --git a/source4/lib/ldb/config.sub b/source4/lib/ldb/config.sub index 80a785297e..a39437d015 100755 --- a/source4/lib/ldb/config.sub +++ b/source4/lib/ldb/config.sub @@ -1454,7 +1454,7 @@ case $basic_machine in os=-aout ;; c4x-* | tic4x-*) - os=-coff + os=-coff ;; # This must come before the *-dec entry. pdp10-*) diff --git a/source4/lib/ldb/configure.ac b/source4/lib/ldb/configure.ac index d61b31afd4..a0fab6d786 100644 --- a/source4/lib/ldb/configure.ac +++ b/source4/lib/ldb/configure.ac @@ -11,7 +11,7 @@ AC_DEFUN([SMB_MODULE_DEFAULT], [echo -n ""]) AC_DEFUN([SMB_LIBRARY_ENABLE], [echo -n ""]) AC_DEFUN([SMB_EXT_LIB], [echo -n ""]) AC_DEFUN([SMB_ENABLE], [echo -n ""]) -AC_INIT(ldb, 0.9.3) +AC_INIT(ldb, 0.9.7) AC_CONFIG_SRCDIR([common/ldb.c]) AC_LIBREPLACE_ALL_CHECKS diff --git a/source4/lib/ldb/external/libevents.m4 b/source4/lib/ldb/external/libevents.m4 index 6a0e36af8b..af046f1430 100644 --- a/source4/lib/ldb/external/libevents.m4 +++ b/source4/lib/ldb/external/libevents.m4 @@ -3,5 +3,5 @@ AC_SUBST(TEVENT_CFLAGS) AC_SUBST(TEVENT_LIBS) AC_CHECK_HEADER(tevent.h, - [AC_CHECK_LIB(tevent, tevent_context_init, [TEVENT_LIBS="-ltevent"]) ], + [AC_CHECK_LIB(tevent, tevent_context_init, [TEVENT_LIBS="-ltevent"], , -ltalloc) ], [PKG_CHECK_MODULES(TEVENT, tevent)]) diff --git a/source4/lib/ldb/external/libtalloc.m4 b/source4/lib/ldb/external/libtalloc.m4 index a4c5b8a9d9..8c63fcc041 100644 --- a/source4/lib/ldb/external/libtalloc.m4 +++ b/source4/lib/ldb/external/libtalloc.m4 @@ -2,6 +2,7 @@ AC_SUBST(TALLOC_OBJ) AC_SUBST(TALLOC_CFLAGS) AC_SUBST(TALLOC_LIBS) -AC_CHECK_HEADER(talloc.h, - [AC_CHECK_LIB(talloc, talloc_init, [TALLOC_LIBS="-ltalloc"]) ], - [PKG_CHECK_MODULES(TALLOC, talloc)]) +PKG_CHECK_MODULES(TALLOC, talloc >= 2.0.0, + [ ], + [ AC_CHECK_HEADER(talloc.h, + [ AC_CHECK_LIB(talloc, talloc_init, [TALLOC_LIBS="-ltalloc"])])]) diff --git a/source4/lib/ldb/external/libtdb.m4 b/source4/lib/ldb/external/libtdb.m4 index 8c2cab702f..44971e1fa9 100644 --- a/source4/lib/ldb/external/libtdb.m4 +++ b/source4/lib/ldb/external/libtdb.m4 @@ -4,4 +4,4 @@ AC_SUBST(TDB_LIBS) AC_CHECK_HEADER(tdb.h, [AC_CHECK_LIB(tdb, tdb_open, [TDB_LIBS="-ltdb"]) ], - [PKG_CHECK_MODULES(TDB, tdb >= 1.1.0)]) + [PKG_CHECK_MODULES(TDB, tdb >= 1.1.6)]) diff --git a/source4/lib/ldb/include/dlinklist.h b/source4/lib/ldb/include/dlinklist.h index acab9fa043..62f885dd30 100644 --- a/source4/lib/ldb/include/dlinklist.h +++ b/source4/lib/ldb/include/dlinklist.h @@ -23,6 +23,7 @@ #ifndef _DLINKLIST_H #define _DLINKLIST_H + /* hook into the front of the list */ #define DLIST_ADD(list, p) \ do { \ @@ -38,7 +39,6 @@ do { \ } while (0) /* remove an element from a list - element doesn't have to be in list. */ -#ifndef DLIST_REMOVE #define DLIST_REMOVE(list, p) \ do { \ if ((p) == (list)) { \ @@ -50,7 +50,6 @@ do { \ } \ if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \ } while (0) -#endif /* promote an element to the top of the list */ #define DLIST_PROMOTE(list, p) \ @@ -59,7 +58,7 @@ do { \ DLIST_ADD(list, p); \ } while (0) -/* hook into the end of the list - needs a tmp pointer */ +/* hook into the end of the list - needs the entry type */ #define DLIST_ADD_END(list, p, type) \ do { \ if (!(list)) { \ @@ -88,11 +87,11 @@ do { \ }\ } while (0) -/* demote an element to the end of the list, needs a tmp pointer */ -#define DLIST_DEMOTE(list, p, tmp) \ +/* demote an element to the end of the list, needs the entry type */ +#define DLIST_DEMOTE(list, p, type) \ do { \ DLIST_REMOVE(list, p); \ - DLIST_ADD_END(list, p, tmp); \ + DLIST_ADD_END(list, p, type); \ } while (0) /* concatenate two lists - putting all elements of the 2nd list at the diff --git a/source4/lib/ldb/include/ldb.h b/source4/lib/ldb/include/ldb.h index be41151409..a083696073 100644 --- a/source4/lib/ldb/include/ldb.h +++ b/source4/lib/ldb/include/ldb.h @@ -94,8 +94,8 @@ struct ldb_dn; /** There are a number of flags that are used with ldap_modify() in - ldb_message_element.flags fields. The LDA_FLAGS_MOD_ADD, - LDA_FLAGS_MOD_DELETE and LDA_FLAGS_MOD_REPLACE flags are used in + ldb_message_element.flags fields. The LDB_FLAGS_MOD_ADD, + LDB_FLAGS_MOD_DELETE and LDB_FLAGS_MOD_REPLACE flags are used in ldap_modify() calls to specify whether attributes are being added, deleted or modified respectively. */ @@ -240,6 +240,17 @@ struct ldb_utf8_fns { */ #define LDB_FLG_NOMMAP 8 +/** + Flag to tell ldif handlers not to force encoding of binary + structures in base64 +*/ +#define LDB_FLG_SHOW_BINARY 16 + +/** + Flags to enable ldb tracing +*/ +#define LDB_FLG_ENABLE_TRACING 32 + /* structures for ldb_parse_tree handling code */ @@ -375,6 +386,17 @@ const struct ldb_dn_extended_syntax *ldb_dn_extended_syntax_by_name(struct ldb_c */ #define LDB_ATTR_FLAG_FIXED (1<<2) +/* + when this is set, attempts to create two records which have the same + value for this attribute will return LDB_ERR_ENTRY_ALREADY_EXISTS + */ +#define LDB_ATTR_FLAG_UNIQUE_INDEX (1<<3) + +/* + when this is set, attempts to create two attribute values for this attribute on a single DN will return LDB_ERR_CONSTRAINT_VIOLATION + */ +#define LDB_ATTR_FLAG_SINGLE_VALUE (1<<4) + /** LDAP attribute syntax for a DN @@ -403,6 +425,15 @@ const struct ldb_dn_extended_syntax *ldb_dn_extended_syntax_by_name(struct ldb_c #define LDB_SYNTAX_INTEGER "1.3.6.1.4.1.1466.115.121.1.27" /** + LDAP attribute syntax for a boolean + + This is the well-known LDAP attribute syntax for a boolean. + + See <a href="http://www.ietf.org/rfc/rfc2252.txt">RFC 2252</a>, Section 4.3.2 +*/ +#define LDB_SYNTAX_BOOLEAN "1.3.6.1.4.1.1466.115.121.1.7" + +/** LDAP attribute syntax for an octet string This is the well-known LDAP attribute syntax for an octet string. @@ -471,6 +502,20 @@ typedef int (*ldb_qsort_cmp_fn_t) (void *v1, void *v2, void *opaque); #define LDB_CONTROL_SHOW_DELETED_OID "1.2.840.113556.1.4.417" /** + OID for getting recycled objects + + \sa <a href="http://msdn.microsoft.com/en-us/library/dd304621(PROT.13).aspx">Microsoft documentation of this OID</a> +*/ +#define LDB_CONTROL_SHOW_RECYCLED_OID "1.2.840.113556.1.4.2064" + +/** + OID for getting deactivated linked attributes + + \sa <a href="http://msdn.microsoft.com/en-us/library/dd302781(PROT.13).aspx">Microsoft documentation of this OID</a> +*/ +#define LDB_CONTROL_SHOW_DEACTIVATED_LINK_OID "1.2.840.113556.1.4.2065" + +/** OID for extended DN \sa <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/ldap_server_extended_dn_oid.asp">Microsoft documentation of this OID</a> @@ -601,8 +646,8 @@ struct ldb_extended_dn_control { }; struct ldb_server_sort_control { - char *attributeName; - char *orderingRule; + const char *attributeName; + const char *orderingRule; int reverse; }; @@ -1249,6 +1294,11 @@ int ldb_sequence_number(struct ldb_context *ldb, enum ldb_sequence_type type, ui int ldb_transaction_start(struct ldb_context *ldb); /** + first phase of two phase commit + */ +int ldb_transaction_prepare_commit(struct ldb_context *ldb); + +/** commit a transaction */ int ldb_transaction_commit(struct ldb_context *ldb); @@ -1421,6 +1471,32 @@ struct ldb_ldif *ldb_ldif_read_string(struct ldb_context *ldb, const char **s); int ldb_ldif_write_file(struct ldb_context *ldb, FILE *f, const struct ldb_ldif *msg); /** + Write an LDIF message to a string + + \param ldb the ldb context (from ldb_init()) + \param mem_ctx the talloc context on which to attach the string) + \param msg the message to write out + + \return the string containing the LDIF, or NULL on error + + \sa ldb_ldif_read_string for the reader equivalent to this function. +*/ +char * ldb_ldif_write_string(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, + const struct ldb_ldif *msg); + + +/* + Produce a string form of an ldb message + + convenient function to turn a ldb_message into a string. Useful for + debugging + */ +char *ldb_ldif_message_string(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, + enum ldb_changetype changetype, + const struct ldb_message *msg); + + +/** Base64 encode a buffer \param mem_ctx the memory context that the result is allocated @@ -1769,6 +1845,12 @@ void ldb_parse_tree_attr_replace(struct ldb_parse_tree *tree, const char *attr, const char *replace); +/* + shallow copy a tree - copying only the elements array so that the caller + can safely add new elements without changing the message +*/ +struct ldb_parse_tree *ldb_parse_tree_copy_shallow(TALLOC_CTX *mem_ctx, + const struct ldb_parse_tree *ot); /** Convert a time structure to a string @@ -1837,4 +1919,13 @@ void ldb_qsort (void *const pbase, size_t total_elems, size_t size, void *opaque */ struct ldb_control **ldb_parse_control_strings(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char **control_strings); +/** + return the ldb flags +*/ +unsigned int ldb_get_flags(struct ldb_context *ldb); + +/* set the ldb flags */ +void ldb_set_flags(struct ldb_context *ldb, unsigned flags); + + #endif diff --git a/source4/lib/ldb/include/ldb_errors.h b/source4/lib/ldb/include/ldb_errors.h index 9362233fd5..706d82732d 100644 --- a/source4/lib/ldb/include/ldb_errors.h +++ b/source4/lib/ldb/include/ldb_errors.h @@ -120,6 +120,7 @@ backend. */ #define LDB_ERR_STRONG_AUTH_REQUIRED 8 + /* 9 RESERVED */ /** @@ -181,6 +182,7 @@ already exists in the entry. */ #define LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS 20 + /** The function used an invalid (incorrect syntax) attribute value. */ diff --git a/source4/lib/ldb/include/ldb_handlers.h b/source4/lib/ldb/include/ldb_handlers.h index e1c14e679b..21fbcc33f8 100644 --- a/source4/lib/ldb/include/ldb_handlers.h +++ b/source4/lib/ldb/include/ldb_handlers.h @@ -31,37 +31,12 @@ * Author: Simo Sorce */ - int ldb_handler_copy( struct ldb_context *ldb, void *mem_ctx, const struct ldb_val *in, struct ldb_val *out); - -int ldb_handler_fold( struct ldb_context *ldb, void *mem_ctx, - const struct ldb_val *in, struct ldb_val *out); - -int ldb_canonicalise_Integer( struct ldb_context *ldb, void *mem_ctx, - const struct ldb_val *in, struct ldb_val *out); - -int ldb_comparison_Integer( struct ldb_context *ldb, void *mem_ctx, - const struct ldb_val *v1, const struct ldb_val *v2); - int ldb_comparison_binary( struct ldb_context *ldb, void *mem_ctx, const struct ldb_val *v1, const struct ldb_val *v2); - -int ldb_comparison_fold( struct ldb_context *ldb, void *mem_ctx, - const struct ldb_val *v1, const struct ldb_val *v2); - -int ldb_canonicalise_dn( struct ldb_context *ldb, void *mem_ctx, +int db_handler_fold( struct ldb_context *ldb, void *mem_ctx, const struct ldb_val *in, struct ldb_val *out); - -int ldb_comparison_dn( struct ldb_context *ldb, void *mem_ctx, - const struct ldb_val *v1, const struct ldb_val *v2); - -int ldb_comparison_objectclass( struct ldb_context *ldb, void *mem_ctx, - const struct ldb_val *v1, const struct ldb_val *v2); - -int ldb_comparison_utctime( struct ldb_context *ldb, void *mem_ctx, +int ldb_comparison_fold( struct ldb_context *ldb, void *mem_ctx, const struct ldb_val *v1, const struct ldb_val *v2); -int ldb_canonicalise_utctime( struct ldb_context *ldb, void *mem_ctx, - const struct ldb_val *in, struct ldb_val *out); - diff --git a/source4/lib/ldb/include/ldb_module.h b/source4/lib/ldb/include/ldb_module.h index 4e1019184d..ae8c4d50df 100644 --- a/source4/lib/ldb/include/ldb_module.h +++ b/source4/lib/ldb/include/ldb_module.h @@ -52,6 +52,7 @@ struct ldb_module_ops { int (*request)(struct ldb_module *, struct ldb_request *); /* match any other operation */ int (*extended)(struct ldb_module *, struct ldb_request *); /* extended operations */ int (*start_transaction)(struct ldb_module *); + int (*prepare_commit)(struct ldb_module *); int (*end_transaction)(struct ldb_module *); int (*del_transaction)(struct ldb_module *); int (*sequence_number)(struct ldb_module *, struct ldb_request *); @@ -63,6 +64,8 @@ struct ldb_module_ops { void ldb_debug(struct ldb_context *ldb, enum ldb_debug_level level, const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4); void ldb_debug_set(struct ldb_context *ldb, enum ldb_debug_level level, const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4); +void ldb_debug_add(struct ldb_context *ldb, const char *fmt, ...) PRINTF_ATTRIBUTE(2, 3); +void ldb_debug_end(struct ldb_context *ldb, enum ldb_debug_level level); #define ldb_oom(ldb) ldb_debug_set(ldb, LDB_DEBUG_FATAL, "ldb out of memory at %s:%d\n", __FILE__, __LINE__) @@ -88,13 +91,20 @@ int ldb_schema_attribute_add(struct ldb_context *ldb, const char *syntax); void ldb_schema_attribute_remove(struct ldb_context *ldb, const char *name); +/* we allow external code to override the name -> schema_attribute function */ +typedef const struct ldb_schema_attribute *(*ldb_attribute_handler_override_fn_t)(struct ldb_context *, void *, const char *); + +void ldb_schema_attribute_set_override_handler(struct ldb_context *ldb, + ldb_attribute_handler_override_fn_t override, + void *private_data); + /* The following definitions come from lib/ldb/common/ldb_controls.c */ struct ldb_control *get_control_from_list(struct ldb_control **controls, const char *oid); int save_controls(struct ldb_control *exclude, struct ldb_request *req, struct ldb_control ***saver); int check_critical_controls(struct ldb_control **controls); /* The following definitions come from lib/ldb/common/ldb_ldif.c */ -int ldb_should_b64_encode(const struct ldb_val *val); +int ldb_should_b64_encode(struct ldb_context *ldb, const struct ldb_val *val); /* The following definitions come from lib/ldb/common/ldb_match.c */ int ldb_match_msg(struct ldb_context *ldb, @@ -119,6 +129,7 @@ int ldb_next_request(struct ldb_module *module, struct ldb_request *request); int ldb_next_start_trans(struct ldb_module *module); int ldb_next_end_trans(struct ldb_module *module); int ldb_next_del_trans(struct ldb_module *module); +int ldb_next_prepare_commit(struct ldb_module *module); int ldb_next_init(struct ldb_module *module); void ldb_set_errstring(struct ldb_context *ldb, const char *err_string); @@ -158,4 +169,6 @@ int ldb_module_done(struct ldb_request *req, int ldb_mod_register_control(struct ldb_module *module, const char *oid); +void ldb_set_default_dns(struct ldb_context *ldb); + #endif diff --git a/source4/lib/ldb/include/ldb_private.h b/source4/lib/ldb/include/ldb_private.h index 2e8da9941c..0e0a1a206d 100644 --- a/source4/lib/ldb/include/ldb_private.h +++ b/source4/lib/ldb/include/ldb_private.h @@ -47,10 +47,14 @@ struct ldb_module_ops; struct ldb_backend_ops; +#define LDB_HANDLE_FLAG_DONE_CALLED 1 + struct ldb_handle { int status; enum ldb_state state; struct ldb_context *ldb; + unsigned flags; + unsigned nesting; }; /* basic module structure */ @@ -65,6 +69,9 @@ struct ldb_module { schema related information needed for matching rules */ struct ldb_schema { + void *attribute_handler_override_private; + ldb_attribute_handler_override_fn_t attribute_handler_override; + /* attribute handling table */ unsigned num_attributes; struct ldb_schema_attribute *attributes; @@ -108,17 +115,19 @@ struct ldb_context { char *modules_dir; struct tevent_context *ev_ctx; + + bool prepare_commit_done; + + char *partial_debug; }; /* The following definitions come from lib/ldb/common/ldb.c */ int ldb_connect_backend(struct ldb_context *ldb, const char *url, const char *options[], struct ldb_module **backend_module); -void ldb_set_default_dns(struct ldb_context *ldb); extern const struct ldb_module_ops ldb_objectclass_module_ops; -extern const struct ldb_module_ops ldb_operational_module_ops; extern const struct ldb_module_ops ldb_paged_results_module_ops; extern const struct ldb_module_ops ldb_rdn_name_module_ops; extern const struct ldb_module_ops ldb_schema_module_ops; diff --git a/source4/lib/ldb/ldb.mk b/source4/lib/ldb/ldb.mk index ff8c1f3baf..e87db64574 100644 --- a/source4/lib/ldb/ldb.mk +++ b/source4/lib/ldb/ldb.mk @@ -20,8 +20,8 @@ COMMON_OBJ=$(COMDIR)/ldb.o $(COMDIR)/ldb_ldif.o \ $(COMDIR)/attrib_handlers.o $(COMDIR)/ldb_controls.o $(COMDIR)/qsort.o MODDIR=modules -MODULES_OBJ=$(MODDIR)/operational.o $(MODDIR)/rdn_name.o \ - $(MODDIR)/paged_results.o $(MODDIR)/sort.o $(MODDIR)/asq.o +MODULES_OBJ=$(MODDIR)/rdn_name.o ${MODDIR}/asq.o \ + $(MODDIR)/paged_results.o $(MODDIR)/sort.o NSSDIR=nssldb NSS_OBJ= $(NSSDIR)/ldb-nss.o $(NSSDIR)/ldb-pwd.o $(NSSDIR)/ldb-grp.o @@ -66,16 +66,16 @@ build-python:: ldb.$(SHLIBEXT) pyldb.o: $(ldbdir)/pyldb.c $(CC) $(PICFLAG) -c $(ldbdir)/pyldb.c $(CFLAGS) `$(PYTHON_CONFIG) --cflags` - -ldb.$(SHLIBEXT): pyldb.o + +ldb.$(SHLIBEXT): pyldb.o $(SHLD) $(SHLD_FLAGS) -o ldb.$(SHLIBEXT) pyldb.o $(LIB_FLAGS) `$(PYTHON_CONFIG) --ldflags` install-python:: build-python mkdir -p $(DESTDIR)`$(PYTHON) -c "import distutils.sysconfig; print distutils.sysconfig.get_python_lib(1, prefix='$(prefix)')"` cp ldb.$(SHLIBEXT) $(DESTDIR)`$(PYTHON) -c "import distutils.sysconfig; print distutils.sysconfig.get_python_lib(1, prefix='$(prefix)')"` -check-python:: build-python - LD_LIBRARY_PATH=lib PYTHONPATH=.:$(ldbdir) $(PYTHON) $(ldbdir)/tests/python/api.py +check-python:: build-python lib/$(SONAME) + $(LIB_PATH_VAR)=lib PYTHONPATH=.:$(ldbdir) $(PYTHON) $(ldbdir)/tests/python/api.py clean:: rm -f ldb.$(SHLIBEXT) diff --git a/source4/lib/ldb/ldb.pc.in b/source4/lib/ldb/ldb.pc.in index b7e4c85844..01482f6bfb 100644 --- a/source4/lib/ldb/ldb.pc.in +++ b/source4/lib/ldb/ldb.pc.in @@ -2,7 +2,7 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ -modulesdir=@modulesdir@ +modulesdir=@LDB_MODULESDIR@ Name: ldb Description: An LDAP-like embedded database diff --git a/source4/lib/ldb/ldb_ildap/ldb_ildap.c b/source4/lib/ldb/ldb_ildap/ldb_ildap.c index 4447d0e09a..ffde048223 100644 --- a/source4/lib/ldb/ldb_ildap/ldb_ildap.c +++ b/source4/lib/ldb/ldb_ildap/ldb_ildap.c @@ -790,7 +790,7 @@ static int ildb_connect(struct ldb_context *ldb, const char *url, status = ldap_connect(ildb->ldap, url); if (!NT_STATUS_IS_OK(status)) { - ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to connect to ldap URL '%s' - %s\n", + ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to connect to ldap URL '%s' - %s", url, ldap_errstr(ildb->ldap, module, status)); goto failed; } @@ -810,14 +810,14 @@ static int ildb_connect(struct ldb_context *ldb, const char *url, const char *password = cli_credentials_get_password(creds); status = ldap_bind_simple(ildb->ldap, bind_dn, password); if (!NT_STATUS_IS_OK(status)) { - ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to bind - %s\n", + ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to bind - %s", ldap_errstr(ildb->ldap, module, status)); goto failed; } } else { status = ldap_bind_sasl(ildb->ldap, creds, lp_ctx); if (!NT_STATUS_IS_OK(status)) { - ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to bind - %s\n", + ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to bind - %s", ldap_errstr(ildb->ldap, module, status)); goto failed; } diff --git a/source4/lib/ldb/ldb_ldap/ldb_ldap.c b/source4/lib/ldb/ldb_ldap/ldb_ldap.c index 43a01f75a7..52c16101bb 100644 --- a/source4/lib/ldb/ldb_ldap/ldb_ldap.c +++ b/source4/lib/ldb/ldb_ldap/ldb_ldap.c @@ -219,7 +219,7 @@ static int lldb_search(struct lldb_context *lldb_ac) } if (req->controls != NULL) { - ldb_debug(ldb, LDB_DEBUG_WARNING, "Controls are not yet supported by ldb_ldap backend!\n"); + ldb_debug(ldb, LDB_DEBUG_WARNING, "Controls are not yet supported by ldb_ldap backend!"); } ldb_request_set_state(req, LDB_ASYNC_PENDING); @@ -871,7 +871,7 @@ static int lldb_connect(struct ldb_context *ldb, ret = ldap_initialize(&lldb->ldap, url); if (ret != LDAP_SUCCESS) { - ldb_debug(ldb, LDB_DEBUG_FATAL, "ldap_initialize failed for URL '%s' - %s\n", + ldb_debug(ldb, LDB_DEBUG_FATAL, "ldap_initialize failed for URL '%s' - %s", url, ldap_err2string(ret)); goto failed; } @@ -880,7 +880,7 @@ static int lldb_connect(struct ldb_context *ldb, ret = ldap_set_option(lldb->ldap, LDAP_OPT_PROTOCOL_VERSION, &version); if (ret != LDAP_SUCCESS) { - ldb_debug(ldb, LDB_DEBUG_FATAL, "ldap_set_option failed - %s\n", + ldb_debug(ldb, LDB_DEBUG_FATAL, "ldap_set_option failed - %s", ldap_err2string(ret)); goto failed; } diff --git a/source4/lib/ldb/ldb_map/ldb_map.c b/source4/lib/ldb/ldb_map/ldb_map.c index 5b4ea7910a..68e9d3f392 100644 --- a/source4/lib/ldb/ldb_map/ldb_map.c +++ b/source4/lib/ldb/ldb_map/ldb_map.c @@ -242,7 +242,7 @@ int ldb_next_remote_request(struct ldb_module *module, struct ldb_request *reque default: ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: " - "Invalid remote request!\n"); + "Invalid remote request!"); return LDB_ERR_OPERATIONS_ERROR; } @@ -503,14 +503,14 @@ struct ldb_dn *ldb_dn_map_local(struct ldb_module *module, void *mem_ctx, struct case MAP_GENERATE: ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: " "MAP_IGNORE/MAP_GENERATE attribute '%s' " - "used in DN!\n", ldb_dn_get_component_name(dn, i)); + "used in DN!", ldb_dn_get_component_name(dn, i)); goto failed; case MAP_CONVERT: if (map->u.convert.convert_local == NULL) { ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: " "'convert_local' not set for attribute '%s' " - "used in DN!\n", ldb_dn_get_component_name(dn, i)); + "used in DN!", ldb_dn_get_component_name(dn, i)); goto failed; } /* fall through */ @@ -578,14 +578,14 @@ struct ldb_dn *ldb_dn_map_remote(struct ldb_module *module, void *mem_ctx, struc case MAP_GENERATE: ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: " "MAP_IGNORE/MAP_GENERATE attribute '%s' " - "used in DN!\n", ldb_dn_get_component_name(dn, i)); + "used in DN!", ldb_dn_get_component_name(dn, i)); goto failed; case MAP_CONVERT: if (map->u.convert.convert_remote == NULL) { ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: " "'convert_remote' not set for attribute '%s' " - "used in DN!\n", ldb_dn_get_component_name(dn, i)); + "used in DN!", ldb_dn_get_component_name(dn, i)); goto failed; } /* fall through */ @@ -1007,7 +1007,7 @@ static int map_init_dns(struct ldb_module *module, struct ldb_map_context *data, dn = ldb_dn_new_fmt(data, ldb, "%s=%s", MAP_DN_NAME, name); if ( ! ldb_dn_validate(dn)) { ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: " - "Failed to construct '%s' DN!\n", MAP_DN_NAME); + "Failed to construct '%s' DN!", MAP_DN_NAME); return LDB_ERR_OPERATIONS_ERROR; } @@ -1018,13 +1018,13 @@ static int map_init_dns(struct ldb_module *module, struct ldb_map_context *data, } if (res->count == 0) { ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: " - "No results for '%s=%s'!\n", MAP_DN_NAME, name); + "No results for '%s=%s'!", MAP_DN_NAME, name); talloc_free(res); return LDB_ERR_CONSTRAINT_VIOLATION; } if (res->count > 1) { ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: " - "Too many results for '%s=%s'!\n", MAP_DN_NAME, name); + "Too many results for '%s=%s'!", MAP_DN_NAME, name); talloc_free(res); return LDB_ERR_CONSTRAINT_VIOLATION; } diff --git a/source4/lib/ldb/ldb_map/ldb_map_inbound.c b/source4/lib/ldb/ldb_map/ldb_map_inbound.c index 455740ce59..89037419fb 100644 --- a/source4/lib/ldb/ldb_map/ldb_map_inbound.c +++ b/source4/lib/ldb/ldb_map/ldb_map_inbound.c @@ -73,7 +73,7 @@ static int ldb_msg_el_partition(struct ldb_module *module, struct ldb_message *l /* Unknown attribute: ignore */ if (map == NULL) { ldb_debug(ldb, LDB_DEBUG_WARNING, "ldb_map: " - "Not mapping attribute '%s': no mapping found\n", + "Not mapping attribute '%s': no mapping found", old->name); goto local; } @@ -86,7 +86,7 @@ static int ldb_msg_el_partition(struct ldb_module *module, struct ldb_message *l if (map->u.convert.convert_local == NULL) { ldb_debug(ldb, LDB_DEBUG_WARNING, "ldb_map: " "Not mapping attribute '%s': " - "'convert_local' not set\n", + "'convert_local' not set", map->local_name); goto local; } @@ -100,7 +100,7 @@ static int ldb_msg_el_partition(struct ldb_module *module, struct ldb_message *l if (map->u.generate.generate_remote == NULL) { ldb_debug(ldb, LDB_DEBUG_WARNING, "ldb_map: " "Not mapping attribute '%s': " - "'generate_remote' not set\n", + "'generate_remote' not set", map->local_name); goto local; } @@ -167,7 +167,7 @@ static int ldb_msg_partition(struct ldb_module *module, struct ldb_message *loca /* Skip 'IS_MAPPED' */ if (ldb_attr_cmp(msg->elements[i].name, IS_MAPPED) == 0) { ldb_debug(ldb, LDB_DEBUG_WARNING, "ldb_map: " - "Skipping attribute '%s'\n", + "Skipping attribute '%s'", msg->elements[i].name); continue; } diff --git a/source4/lib/ldb/ldb_map/ldb_map_outbound.c b/source4/lib/ldb/ldb_map/ldb_map_outbound.c index ffcefad6be..6a8e796ca4 100644 --- a/source4/lib/ldb/ldb_map/ldb_map_outbound.c +++ b/source4/lib/ldb/ldb_map/ldb_map_outbound.c @@ -304,7 +304,7 @@ static int ldb_msg_el_merge(struct ldb_module *module, struct ldb_message *local if (map->u.convert.convert_remote == NULL) { ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: " "Skipping attribute '%s': " - "'convert_remote' not set\n", + "'convert_remote' not set", attr_name); return LDB_SUCCESS; } @@ -323,7 +323,7 @@ static int ldb_msg_el_merge(struct ldb_module *module, struct ldb_message *local if (map->u.generate.generate_local == NULL) { ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: " "Skipping attribute '%s': " - "'generate_local' not set\n", + "'generate_local' not set", attr_name); return LDB_SUCCESS; } @@ -900,7 +900,7 @@ static int map_subtree_collect_remote(struct ldb_module *module, void *mem_ctx, if (map->type == MAP_GENERATE) { ldb_debug(ldb, LDB_DEBUG_WARNING, "ldb_map: " "Skipping attribute '%s': " - "'convert_operator' not set\n", + "'convert_operator' not set", tree->u.equality.attr); *new = NULL; return 0; @@ -1062,7 +1062,7 @@ int map_return_entry(struct map_context *ac, struct ldb_reply *ares) ac->req->op.search.scope)) { ldb_debug(ldb, LDB_DEBUG_TRACE, "ldb_map: " "Skipping record '%s': " - "doesn't match original search\n", + "doesn't match original search", ldb_dn_get_linearized(ares->message->dn)); return LDB_SUCCESS; } @@ -1250,17 +1250,19 @@ static int map_remote_search_callback(struct ldb_request *req, ares->response, LDB_SUCCESS); } - talloc_free(ares); - /* reset the pointer to the start of the list */ ac->r_current = ac->r_list; /* no entry just return */ if (ac->r_current == NULL) { - return ldb_module_done(ac->req, ares->controls, + ret = ldb_module_done(ac->req, ares->controls, ares->response, LDB_SUCCESS); + talloc_free(ares); + return ret; } + talloc_free(ares); + ret = map_search_local(ac); if (ret != LDB_SUCCESS) { return ldb_module_done(ac->req, NULL, NULL, ret); diff --git a/source4/lib/ldb/ldb_sqlite3/base160.c b/source4/lib/ldb/ldb_sqlite3/base160.c index 423e2b6841..7ad39f7c2f 100644 --- a/source4/lib/ldb/ldb_sqlite3/base160.c +++ b/source4/lib/ldb/ldb_sqlite3/base160.c @@ -122,7 +122,7 @@ lsqlite3_base160Next(char base160[]) * We need a minimum of four digits, and we will always get a multiple of * four digits. */ - if (len = strlen(pBase160)) >= 4) + if ((len = strlen(pBase160)) >= 4) { pBase160 += strlen(pBase160) - 1; diff --git a/source4/lib/ldb/ldb_sqlite3/ldb_sqlite3.c b/source4/lib/ldb/ldb_sqlite3/ldb_sqlite3.c index 8acbac4cc3..d0573d389e 100644 --- a/source4/lib/ldb/ldb_sqlite3/ldb_sqlite3.c +++ b/source4/lib/ldb/ldb_sqlite3/ldb_sqlite3.c @@ -485,6 +485,7 @@ query_int(const struct lsqlite3_private * lsqlite3, /* Format the query */ if ((p = sqlite3_vmprintf(pSql, args)) == NULL) { + va_end(args); return SQLITE_NOMEM; } diff --git a/source4/lib/ldb/ldb_tdb/ldb_cache.c b/source4/lib/ldb/ldb_tdb/ldb_cache.c index 43b965f239..2c399686ea 100644 --- a/source4/lib/ldb/ldb_tdb/ldb_cache.c +++ b/source4/lib/ldb/ldb_tdb/ldb_cache.c @@ -32,6 +32,7 @@ */ #include "ldb_tdb.h" +#include "ldb_private.h" #define LTDB_FLAG_CASE_INSENSITIVE (1<<0) #define LTDB_FLAG_INTEGER (1<<1) @@ -115,6 +116,12 @@ static int ltdb_attributes_load(struct ldb_module *module) ldb = ldb_module_get_ctx(module); + if (ldb->schema.attribute_handler_override) { + /* we skip loading the @ATTRIBUTES record when a module is supplying + its own attribute handling */ + return LDB_SUCCESS; + } + dn = ldb_dn_new(module, ldb, LTDB_ATTRIBUTES); if (dn == NULL) goto failed; @@ -134,7 +141,7 @@ static int ltdb_attributes_load(struct ldb_module *module) const struct ldb_schema_syntax *s; if (ltdb_attributes_flags(&msg->elements[i], &flags) != 0) { - ldb_debug(ldb, LDB_DEBUG_ERROR, "Invalid @ATTRIBUTES element for '%s'\n", msg->elements[i].name); + ldb_debug(ldb, LDB_DEBUG_ERROR, "Invalid @ATTRIBUTES element for '%s'", msg->elements[i].name); goto failed; } switch (flags & ~LTDB_FLAG_HIDDEN) { @@ -149,7 +156,7 @@ static int ltdb_attributes_load(struct ldb_module *module) break; default: ldb_debug(ldb, LDB_DEBUG_ERROR, - "Invalid flag combination 0x%x for '%s' in @ATTRIBUTES\n", + "Invalid flag combination 0x%x for '%s' in @ATTRIBUTES", flags, msg->elements[i].name); goto failed; } @@ -157,7 +164,7 @@ static int ltdb_attributes_load(struct ldb_module *module) s = ldb_standard_syntax_by_name(ldb, syntax); if (s == NULL) { ldb_debug(ldb, LDB_DEBUG_ERROR, - "Invalid attribute syntax '%s' for '%s' in @ATTRIBUTES\n", + "Invalid attribute syntax '%s' for '%s' in @ATTRIBUTES", syntax, msg->elements[i].name); goto failed; } diff --git a/source4/lib/ldb/ldb_tdb/ldb_index.c b/source4/lib/ldb/ldb_tdb/ldb_index.c index c99c2936d8..b959471d16 100644 --- a/source4/lib/ldb/ldb_tdb/ldb_index.c +++ b/source4/lib/ldb/ldb_tdb/ldb_index.c @@ -426,7 +426,8 @@ struct dn_list { caller frees */ static struct ldb_dn *ltdb_index_key(struct ldb_context *ldb, - const char *attr, const struct ldb_val *value) + const char *attr, const struct ldb_val *value, + const struct ldb_schema_attribute **ap) { struct ldb_dn *ret; struct ldb_val v; @@ -440,6 +441,9 @@ static struct ldb_dn *ltdb_index_key(struct ldb_context *ldb, } a = ldb_schema_attribute_by_name(ldb, attr); + if (ap) { + *ap = a; + } r = a->syntax->canonicalise_fn(ldb, ldb, value, &v); if (r != LDB_SUCCESS) { const char *errstr = ldb_errstring(ldb); @@ -451,7 +455,7 @@ static struct ldb_dn *ltdb_index_key(struct ldb_context *ldb, talloc_free(attr_folded); return NULL; } - if (ldb_should_b64_encode(&v)) { + if (ldb_should_b64_encode(ldb, &v)) { char *vstr = ldb_base64_encode(ldb, (char *)v.data, v.length); if (!vstr) return NULL; ret = ldb_dn_new_fmt(ldb, ldb, "%s:%s::%s", LTDB_INDEX, attr_folded, vstr); @@ -531,7 +535,7 @@ static int ltdb_index_dn_simple(struct ldb_module *module, /* the attribute is indexed. Pull the list of DNs that match the search criterion */ - dn = ltdb_index_key(ldb, tree->u.equality.attr, &tree->u.equality.value); + dn = ltdb_index_key(ldb, tree->u.equality.attr, &tree->u.equality.value, NULL); if (!dn) return LDB_ERR_OPERATIONS_ERROR; msg = talloc(list, struct ldb_message); @@ -792,6 +796,18 @@ static int ltdb_index_dn_not(struct ldb_module *module, return LDB_ERR_OPERATIONS_ERROR; } + +static bool ltdb_index_unique(struct ldb_context *ldb, + const char *attr) +{ + const struct ldb_schema_attribute *a; + a = ldb_schema_attribute_by_name(ldb, attr); + if (a->flags & LDB_ATTR_FLAG_UNIQUE_INDEX) { + return true; + } + return false; +} + /* AND two index results */ @@ -802,7 +818,7 @@ static int ltdb_index_dn_and(struct ldb_module *module, { struct ldb_context *ldb; unsigned int i; - int ret; + int ret, pass; ldb = ldb_module_get_ctx(module); @@ -810,52 +826,71 @@ static int ltdb_index_dn_and(struct ldb_module *module, list->dn = NULL; list->count = 0; - for (i=0;i<tree->u.list.num_elements;i++) { - struct dn_list *list2; - int v; - - list2 = talloc(module, struct dn_list); - if (list2 == NULL) { - return LDB_ERR_OPERATIONS_ERROR; - } - - v = ltdb_index_dn(module, tree->u.list.elements[i], index_list, list2); - - if (v == LDB_ERR_NO_SUCH_OBJECT) { - /* 0 && X == 0 */ - talloc_free(list->dn); - talloc_free(list2); - return LDB_ERR_NO_SUCH_OBJECT; - } - - if (v != LDB_SUCCESS && v != LDB_ERR_NO_SUCH_OBJECT) { - talloc_free(list2); - continue; - } - - if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) { - ret = LDB_SUCCESS; - talloc_free(list->dn); - list->dn = talloc_move(list, &list2->dn); - list->count = list2->count; - } else { - if (list_intersect(ldb, list, list2) == -1) { - talloc_free(list2); + for (pass=0;pass<=1;pass++) { + /* in the first pass we only look for unique simple + equality tests, in the hope of avoiding having to look + at any others */ + bool only_unique = pass==0?true:false; + + for (i=0;i<tree->u.list.num_elements;i++) { + struct dn_list *list2; + int v; + bool is_unique = false; + const struct ldb_parse_tree *subtree = tree->u.list.elements[i]; + + if (subtree->operation == LDB_OP_EQUALITY && + ltdb_index_unique(ldb, subtree->u.equality.attr)) { + is_unique = true; + } + if (is_unique != only_unique) continue; + + list2 = talloc(module, struct dn_list); + if (list2 == NULL) { return LDB_ERR_OPERATIONS_ERROR; } - } + + v = ltdb_index_dn(module, subtree, index_list, list2); - talloc_free(list2); - - if (list->count == 0) { - talloc_free(list->dn); - return LDB_ERR_NO_SUCH_OBJECT; + if (v == LDB_ERR_NO_SUCH_OBJECT) { + /* 0 && X == 0 */ + talloc_free(list->dn); + talloc_free(list2); + return LDB_ERR_NO_SUCH_OBJECT; + } + + if (v != LDB_SUCCESS && v != LDB_ERR_NO_SUCH_OBJECT) { + talloc_free(list2); + continue; + } + + if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) { + ret = LDB_SUCCESS; + talloc_free(list->dn); + list->dn = talloc_move(list, &list2->dn); + list->count = list2->count; + } else { + if (list_intersect(ldb, list, list2) == -1) { + talloc_free(list2); + return LDB_ERR_OPERATIONS_ERROR; + } + } + + talloc_free(list2); + + if (list->count == 0) { + talloc_free(list->dn); + return LDB_ERR_NO_SUCH_OBJECT; + } + + if (list->count == 1) { + /* it isn't worth loading the next part of the tree */ + return ret; + } } - } - + } return ret; } - + /* AND index results and ONE level special index */ @@ -882,7 +917,7 @@ static int ltdb_index_dn_one(struct ldb_module *module, search criterion */ val.data = (uint8_t *)((uintptr_t)ldb_dn_get_casefold(parent_dn)); val.length = strlen((char *)val.data); - key = ltdb_index_key(ldb, LTDB_IDXONE, &val); + key = ltdb_index_key(ldb, LTDB_IDXONE, &val, NULL); if (!key) { talloc_free(list2); return LDB_ERR_OPERATIONS_ERROR; @@ -1002,7 +1037,8 @@ static int ltdb_index_dn(struct ldb_module *module, extracting just the given attributes */ static int ltdb_index_filter(const struct dn_list *dn_list, - struct ltdb_context *ac) + struct ltdb_context *ac, + uint32_t *match_count) { struct ldb_context *ldb; struct ldb_message *msg; @@ -1058,6 +1094,8 @@ static int ltdb_index_filter(const struct dn_list *dn_list, ac->request_terminated = true; return ret; } + + (*match_count)++; } return LDB_SUCCESS; @@ -1068,7 +1106,7 @@ static int ltdb_index_filter(const struct dn_list *dn_list, returns -1 if an indexed search is not possible, in which case the caller should call ltdb_search_full() */ -int ltdb_search_indexed(struct ltdb_context *ac) +int ltdb_search_indexed(struct ltdb_context *ac, uint32_t *match_count) { struct ldb_context *ldb; void *data = ldb_module_get_private(ac->module); @@ -1121,21 +1159,17 @@ int ltdb_search_indexed(struct ltdb_context *ac) if (ac->scope != LDB_SCOPE_BASE && idxattr == 1) { ret = ltdb_index_dn(ac->module, ac->tree, ltdb->cache->indexlist, dn_list); - - if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) { - talloc_free(dn_list); - return ret; - } } - if (ac->scope == LDB_SCOPE_ONELEVEL && idxone == 1) { + if (ret == LDB_ERR_OPERATIONS_ERROR && + ac->scope == LDB_SCOPE_ONELEVEL && idxone == 1) { ret = ltdb_index_dn_one(ac->module, ac->base, dn_list); } if (ret == LDB_SUCCESS) { /* we've got a candidate list - now filter by the full tree and extract the needed attributes */ - ret = ltdb_index_filter(dn_list, ac); + ret = ltdb_index_filter(dn_list, ac, match_count); } talloc_free(dn_list); @@ -1185,7 +1219,8 @@ static int ltdb_index_add1_new(struct ldb_context *ldb, static int ltdb_index_add1_add(struct ldb_context *ldb, struct ldb_message *msg, int idx, - const char *dn) + const char *dn, + const struct ldb_schema_attribute *a) { struct ldb_val *v2; unsigned int i; @@ -1197,6 +1232,10 @@ static int ltdb_index_add1_add(struct ldb_context *ldb, } } + if (a->flags & LDB_ATTR_FLAG_UNIQUE_INDEX) { + return LDB_ERR_ENTRY_ALREADY_EXISTS; + } + v2 = talloc_realloc(msg->elements, msg->elements[idx].values, struct ldb_val, msg->elements[idx].num_values+1); @@ -1223,6 +1262,7 @@ static int ltdb_index_add1(struct ldb_module *module, const char *dn, struct ldb_dn *dn_key; int ret; unsigned int i; + const struct ldb_schema_attribute *a; ldb = ldb_module_get_ctx(module); @@ -1232,7 +1272,7 @@ static int ltdb_index_add1(struct ldb_module *module, const char *dn, return LDB_ERR_OPERATIONS_ERROR; } - dn_key = ltdb_index_key(ldb, el->name, &el->values[v_idx]); + dn_key = ltdb_index_key(ldb, el->name, &el->values[v_idx], &a); if (!dn_key) { talloc_free(msg); return LDB_ERR_OPERATIONS_ERROR; @@ -1260,7 +1300,7 @@ static int ltdb_index_add1(struct ldb_module *module, const char *dn, if (i == msg->num_elements) { ret = ltdb_index_add1_new(ldb, msg, dn); } else { - ret = ltdb_index_add1_add(ldb, msg, i, dn); + ret = ltdb_index_add1_add(ldb, msg, i, dn, a); } if (ret == LDB_SUCCESS) { @@ -1343,7 +1383,7 @@ int ltdb_index_del_value(struct ldb_module *module, const char *dn, return LDB_SUCCESS; } - dn_key = ltdb_index_key(ldb, el->name, &el->values[v_idx]); + dn_key = ltdb_index_key(ldb, el->name, &el->values[v_idx], NULL); if (!dn_key) { return LDB_ERR_OPERATIONS_ERROR; } @@ -1370,14 +1410,14 @@ int ltdb_index_del_value(struct ldb_module *module, const char *dn, i = ldb_msg_find_idx(msg, dn, &j, LTDB_IDX); if (i == -1) { struct ldb_ldif ldif; - - ldb_debug(ldb, LDB_DEBUG_ERROR, - "ERROR: dn %s not found in %s\n", dn, - ldb_dn_get_linearized(dn_key)); + char *ldif_string; ldif.changetype = LDB_CHANGETYPE_NONE; ldif.msg = msg; - ldb_ldif_write_file(ldb, stdout, &ldif); - sleep(100); + ldif_string = ldb_ldif_write_string(ldb, NULL, &ldif); + ldb_debug(ldb, LDB_DEBUG_ERROR, + "ERROR: dn %s not found in %s", dn, + ldif_string); + talloc_free(ldif_string); /* it ain't there. hmmm */ talloc_free(dn_key); return LDB_SUCCESS; @@ -1459,6 +1499,10 @@ int ltdb_index_one(struct ldb_module *module, const struct ldb_message *msg, int const char *dn; int ret; + if (ldb_dn_is_special(msg->dn)) { + return LDB_SUCCESS; + } + /* We index for ONE Level only if requested */ ret = ldb_msg_find_idx(ltdb->cache->indexlist, NULL, NULL, LTDB_IDXONE); if (ret != 0) { @@ -1537,6 +1581,8 @@ static int re_index(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void * ret = ltdb_unpack_data(module, &data, msg); if (ret != 0) { + ldb_debug(ldb, LDB_DEBUG_ERROR, "Invalid data for index %s\n", + ldb_dn_get_linearized(msg->dn)); talloc_free(msg); return -1; } @@ -1546,7 +1592,7 @@ static int re_index(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void * key2 = ltdb_key(module, msg->dn); if (key2.dptr == NULL) { /* probably a corrupt record ... darn */ - ldb_debug(ldb, LDB_DEBUG_ERROR, "Invalid DN in re_index: %s\n", + ldb_debug(ldb, LDB_DEBUG_ERROR, "Invalid DN in re_index: %s", ldb_dn_get_linearized(msg->dn)); talloc_free(msg); return 0; @@ -1568,7 +1614,7 @@ static int re_index(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void * ret = ltdb_index_add0(module, dn, msg->elements, msg->num_elements); } else { ldb_debug(ldb, LDB_DEBUG_ERROR, - "Adding special ONE LEVEL index failed (%s)!\n", + "Adding special ONE LEVEL index failed (%s)!", ldb_dn_get_linearized(msg->dn)); } diff --git a/source4/lib/ldb/ldb_tdb/ldb_pack.c b/source4/lib/ldb/ldb_tdb/ldb_pack.c index 1995606f88..e7aeb47e72 100644 --- a/source4/lib/ldb/ldb_tdb/ldb_pack.c +++ b/source4/lib/ldb/ldb_tdb/ldb_pack.c @@ -236,6 +236,10 @@ int ltdb_unpack_data(struct ldb_module *module, errno = EIO; goto failed; } + if (len == 0) { + errno = EIO; + goto failed; + } message->elements[i].flags = 0; message->elements[i].name = talloc_strndup(message->elements, (char *)p, len); if (message->elements[i].name == NULL) { @@ -280,7 +284,7 @@ int ltdb_unpack_data(struct ldb_module *module, if (remaining != 0) { ldb_debug(ldb, LDB_DEBUG_ERROR, - "Error: %d bytes unread in ltdb_unpack_data\n", remaining); + "Error: %d bytes unread in ltdb_unpack_data", remaining); } return 0; diff --git a/source4/lib/ldb/ldb_tdb/ldb_search.c b/source4/lib/ldb/ldb_tdb/ldb_search.c index d395c28f28..a6647ccd50 100644 --- a/source4/lib/ldb/ldb_tdb/ldb_search.c +++ b/source4/lib/ldb/ldb_tdb/ldb_search.c @@ -265,6 +265,9 @@ int ltdb_search_dn1(struct ldb_module *module, struct ldb_dn *dn, struct ldb_mes ret = ltdb_unpack_data(module, &tdb_data, msg); free(tdb_data.dptr); if (ret == -1) { + struct ldb_context *ldb = ldb_module_get_ctx(module); + ldb_debug(ldb, LDB_DEBUG_ERROR, "Invalid data for index %s\n", + ldb_dn_get_linearized(msg->dn)); return LDB_ERR_OPERATIONS_ERROR; } @@ -535,7 +538,9 @@ int ltdb_search(struct ltdb_context *ctx) ctx->attrs = req->op.search.attrs; if (ret == LDB_SUCCESS) { - ret = ltdb_search_indexed(ctx); + uint32_t match_count = 0; + + ret = ltdb_search_indexed(ctx, &match_count); if (ret == LDB_ERR_NO_SUCH_OBJECT) { /* Not in the index, therefore OK! */ ret = LDB_SUCCESS; @@ -546,6 +551,24 @@ int ltdb_search(struct ltdb_context *ctx) * callback error */ if ( ! ctx->request_terminated && ret != LDB_SUCCESS) { /* Not indexed, so we need to do a full scan */ +#if 0 + /* useful for debugging when slow performance + * is caused by unindexed searches */ + char *expression = ldb_filter_from_tree(ctx, ctx->tree); + printf("FULL SEARCH: %s\n", expression); + talloc_free(expression); +#endif + if (match_count != 0) { + /* the indexing code gave an error + * after having returned at least one + * entry. This means the indexes are + * corrupt or a database record is + * corrupt. We cannot continue with a + * full search or we may return + * duplicate entries + */ + return LDB_ERR_OPERATIONS_ERROR; + } ret = ltdb_search_full(ctx); if (ret != LDB_SUCCESS) { ldb_set_errstring(ldb, "Indexed and full searches both failed!\n"); diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.c b/source4/lib/ldb/ldb_tdb/ldb_tdb.c index 9df62be936..55acb6132d 100644 --- a/source4/lib/ldb/ldb_tdb/ldb_tdb.c +++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.c @@ -254,7 +254,7 @@ static int ltdb_add_internal(struct ldb_module *module, const struct ldb_message *msg) { struct ldb_context *ldb = ldb_module_get_ctx(module); - int ret; + int ret, i; ret = ltdb_check_special_dn(module, msg); if (ret != LDB_SUCCESS) { @@ -265,6 +265,24 @@ static int ltdb_add_internal(struct ldb_module *module, return LDB_ERR_OPERATIONS_ERROR; } + for (i=0;i<msg->num_elements;i++) { + struct ldb_message_element *el = &msg->elements[i]; + const struct ldb_schema_attribute *a = ldb_schema_attribute_by_name(ldb, el->name); + + if (el->num_values == 0) { + ldb_asprintf_errstring(ldb, "attribute %s on %s specified, but with 0 values (illegal)", + el->name, ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + if (a && a->flags & LDB_ATTR_FLAG_SINGLE_VALUE) { + if (el->num_values > 1) { + ldb_asprintf_errstring(ldb, "SINGLE-VALUE attribute %s on %s speicified more than once", + el->name, ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + } + } + ret = ltdb_store(module, msg, TDB_INSERT); if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) { @@ -602,15 +620,28 @@ int ltdb_modify_internal(struct ldb_module *module, struct ldb_message_element *el2; struct ldb_val *vals; const char *dn; - + const struct ldb_schema_attribute *a = ldb_schema_attribute_by_name(ldb, el->name); switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) { case LDB_FLAG_MOD_ADD: + /* add this element to the message. fail if it already exists */ idx = find_element(msg2, el->name); + if (el->num_values == 0) { + ldb_asprintf_errstring(ldb, "attribute %s on %s speicified, but with 0 values (illigal)", + el->name, ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_CONSTRAINT_VIOLATION; + } if (idx == -1) { + if (a && a->flags & LDB_ATTR_FLAG_SINGLE_VALUE) { + if (el->num_values > 1) { + ldb_asprintf_errstring(ldb, "SINGLE-VALUE attribute %s on %s speicified more than once", + el->name, ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + } if (msg_add_element(ldb, msg2, el) != 0) { ret = LDB_ERR_OTHER; goto failed; @@ -618,6 +649,13 @@ int ltdb_modify_internal(struct ldb_module *module, continue; } + /* If this is an add, then if it already + * exists in the object, then we violoate the + * single-value rule */ + if (a && a->flags & LDB_ATTR_FLAG_SINGLE_VALUE) { + return LDB_ERR_CONSTRAINT_VIOLATION; + } + el2 = &msg2->elements[idx]; /* An attribute with this name already exists, @@ -657,6 +695,13 @@ int ltdb_modify_internal(struct ldb_module *module, break; case LDB_FLAG_MOD_REPLACE: + if (a && a->flags & LDB_ATTR_FLAG_SINGLE_VALUE) { + if (el->num_values > 1) { + ldb_asprintf_errstring(ldb, "SINGLE-VALUE attribute %s on %s speicified more than once", + el->name, ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + } /* replace all elements of this attribute name with the elements listed. The attribute not existing is not an error */ msg_delete_attribute(module, ldb, msg2, el->name); @@ -805,37 +850,18 @@ static int ltdb_rename(struct ltdb_context *ctx) return LDB_ERR_OPERATIONS_ERROR; } - if (ldb_dn_compare(req->op.rename.olddn, req->op.rename.newdn) == 0) { - /* The rename operation is apparently only changing case - - the DNs are the same. Delete the old DN before adding - the new one to avoid a TDB_ERR_EXISTS error. - - The only drawback to this is that if the delete - succeeds but the add fails, we rely on the - transaction to roll this all back. */ - tret = ltdb_delete_internal(module, req->op.rename.olddn); - if (tret != LDB_SUCCESS) { - return tret; - } - - tret = ltdb_add_internal(module, msg); - if (tret != LDB_SUCCESS) { - return tret; - } - } else { - /* The rename operation is changing DNs. Try to add the new - DN first to avoid clobbering another DN not related to - this rename operation. */ - tret = ltdb_add_internal(module, msg); - if (tret != LDB_SUCCESS) { - return tret; - } + /* Always delete first then add, to avoid conflicts with + * unique indexes. We rely on the transaction to make this + * atomic + */ + tret = ltdb_delete_internal(module, req->op.rename.olddn); + if (tret != LDB_SUCCESS) { + return tret; + } - tret = ltdb_delete_internal(module, req->op.rename.olddn); - if (tret != LDB_SUCCESS) { - ltdb_delete_internal(module, req->op.rename.newdn); - return LDB_ERR_OPERATIONS_ERROR; - } + tret = ltdb_add_internal(module, msg); + if (tret != LDB_SUCCESS) { + return tret; } return LDB_SUCCESS; @@ -857,18 +883,46 @@ static int ltdb_start_trans(struct ldb_module *module) return LDB_SUCCESS; } -static int ltdb_end_trans(struct ldb_module *module) +static int ltdb_prepare_commit(struct ldb_module *module) { void *data = ldb_module_get_private(module); struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private); - ltdb->in_transaction--; + if (ltdb->in_transaction != 1) { + return LDB_SUCCESS; + } if (ltdb_index_transaction_commit(module) != 0) { tdb_transaction_cancel(ltdb->tdb); + ltdb->in_transaction--; return ltdb_err_map(tdb_error(ltdb->tdb)); } + if (tdb_transaction_prepare_commit(ltdb->tdb) != 0) { + ltdb->in_transaction--; + return ltdb_err_map(tdb_error(ltdb->tdb)); + } + + ltdb->prepared_commit = true; + + return LDB_SUCCESS; +} + +static int ltdb_end_trans(struct ldb_module *module) +{ + void *data = ldb_module_get_private(module); + struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private); + + if (!ltdb->prepared_commit) { + int ret = ltdb_prepare_commit(module); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + ltdb->in_transaction--; + ltdb->prepared_commit = false; + if (tdb_transaction_commit(ltdb->tdb) != 0) { return ltdb_err_map(tdb_error(ltdb->tdb)); } @@ -1209,6 +1263,7 @@ static const struct ldb_module_ops ltdb_ops = { .extended = ltdb_handle_request, .start_transaction = ltdb_start_trans, .end_transaction = ltdb_end_trans, + .prepare_commit = ltdb_prepare_commit, .del_transaction = ltdb_del_trans, }; @@ -1266,7 +1321,7 @@ static int ltdb_connect(struct ldb_context *ldb, const char *url, ldb_get_create_perms(ldb), ldb); if (!ltdb->tdb) { ldb_debug(ldb, LDB_DEBUG_ERROR, - "Unable to open tdb '%s'\n", path); + "Unable to open tdb '%s'", path); talloc_free(ltdb); return -1; } diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.h b/source4/lib/ldb/ldb_tdb/ldb_tdb.h index 5a1c8fee2d..c8c1dad5de 100644 --- a/source4/lib/ldb/ldb_tdb/ldb_tdb.h +++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.h @@ -8,8 +8,6 @@ struct ltdb_private { TDB_CONTEXT *tdb; unsigned int connect_flags; - /* a double is used for portability and ease of string - handling. It has plenty of digits of precision */ unsigned long long sequence_number; /* the low level tdb seqnum - used to avoid loading BASEINFO when @@ -30,6 +28,7 @@ struct ltdb_private { bool check_base; struct ltdb_idxptr *idxptr; + bool prepared_commit; }; /* @@ -83,7 +82,7 @@ int ltdb_check_at_attributes_values(const struct ldb_val *value); struct ldb_parse_tree; -int ltdb_search_indexed(struct ltdb_context *ctx); +int ltdb_search_indexed(struct ltdb_context *ctx, uint32_t *); int ltdb_index_add(struct ldb_module *module, const struct ldb_message *msg); int ltdb_index_del(struct ldb_module *module, const struct ldb_message *msg); int ltdb_index_one(struct ldb_module *module, const struct ldb_message *msg, int add); diff --git a/source4/lib/ldb/modules/asq.c b/source4/lib/ldb/modules/asq.c index 475b609e41..0819f7f559 100644 --- a/source4/lib/ldb/modules/asq.c +++ b/source4/lib/ldb/modules/asq.c @@ -26,8 +26,8 @@ * * Component: ldb attribute scoped query control module * - * Description: this module searches all the the objects pointed - * by the DNs contained in the references attribute + * Description: this module searches all the objects pointed by + * the DNs contained in the references attribute * * Author: Simo Sorce */ @@ -351,7 +351,7 @@ static int asq_search(struct ldb_module *module, struct ldb_request *req) ldb = ldb_module_get_ctx(module); - /* check if there's a paged request control */ + /* check if there's an ASQ control */ control = ldb_request_get_control(req, LDB_CONTROL_ASQ_OID); if (control == NULL) { /* not found go on */ @@ -393,7 +393,7 @@ static int asq_init(struct ldb_module *module) ret = ldb_mod_register_control(module, LDB_CONTROL_ASQ_OID); if (ret != LDB_SUCCESS) { - ldb_debug(ldb, LDB_DEBUG_WARNING, "asq: Unable to register control with rootdse!\n"); + ldb_debug(ldb, LDB_DEBUG_WARNING, "asq: Unable to register control with rootdse!"); } return ldb_next_init(module); diff --git a/source4/lib/ldb/modules/operational.c b/source4/lib/ldb/modules/operational.c deleted file mode 100644 index 43b223b52e..0000000000 --- a/source4/lib/ldb/modules/operational.c +++ /dev/null @@ -1,314 +0,0 @@ -/* - ldb database library - - Copyright (C) Andrew Tridgell 2005 - Copyright (C) Simo Sorce 2006-2008 - - ** NOTE! The following LGPL license applies to the ldb - ** library. This does NOT imply that all of Samba is released - ** under the LGPL - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 3 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, see <http://www.gnu.org/licenses/>. -*/ -/* - handle operational attributes - */ - -/* - createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated - modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged - - for the above two, we do the search as normal, and if - createTimestamp or modifyTimestamp is asked for, then do - additional searches for whenCreated and whenChanged and fill in - the resulting values - - we also need to replace these with the whenCreated/whenChanged - equivalent in the search expression trees - - whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE - whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE - - on init we need to setup attribute handlers for these so - comparisons are done correctly. The resolution is 1 second. - - on add we need to add both the above, for current time - - on modify we need to change whenChanged - - - subschemaSubentry: HIDDEN, not-searchable, - points at DN CN=Aggregate,$SCHEMADN - - for this one we do the search as normal, then add the static - value if requested. How do we work out the $BASEDN from inside a - module? - - - structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass? - - for this one we do the search as normal, then if requested ask - for objectclass, change the attribute name, and add it - - allowedAttributesEffective: HIDDEN, CONSTRUCTED, not-searchable, - list of attributes that can be modified - requires schema lookup - - - attributeTypes: in schema only - objectClasses: in schema only - matchingRules: in schema only - matchingRuleUse: in schema only - creatorsName: not supported by w2k3? - modifiersName: not supported by w2k3? -*/ - -#include "ldb_includes.h" -#include "ldb_module.h" - -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) -#endif - -/* - construct a canonical name from a message -*/ -static int construct_canonical_name(struct ldb_module *module, struct ldb_message *msg) -{ - char *canonicalName; - canonicalName = ldb_dn_canonical_string(msg, msg->dn); - if (canonicalName == NULL) { - return -1; - } - return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName); -} - -/* - a list of attribute names that should be substituted in the parse - tree before the search is done -*/ -static const struct { - const char *attr; - const char *replace; -} parse_tree_sub[] = { - { "createTimestamp", "whenCreated" }, - { "modifyTimestamp", "whenChanged" } -}; - - -/* - a list of attribute names that are hidden, but can be searched for - using another (non-hidden) name to produce the correct result -*/ -static const struct { - const char *attr; - const char *replace; - int (*constructor)(struct ldb_module *, struct ldb_message *); -} search_sub[] = { - { "createTimestamp", "whenCreated", NULL }, - { "modifyTimestamp", "whenChanged", NULL }, - { "structuralObjectClass", "objectClass", NULL }, - { "canonicalName", "distinguishedName", construct_canonical_name } -}; - -/* - post process a search result record. For any search_sub[] attributes that were - asked for, we need to call the appropriate copy routine to copy the result - into the message, then remove any attributes that we added to the search but were - not asked for by the user -*/ -static int operational_search_post_process(struct ldb_module *module, - struct ldb_message *msg, - const char * const *attrs) -{ - struct ldb_context *ldb; - int i, a=0; - - ldb = ldb_module_get_ctx(module); - - for (a=0;attrs && attrs[a];a++) { - for (i=0;i<ARRAY_SIZE(search_sub);i++) { - if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) { - continue; - } - - /* construct the new attribute, using either a supplied - constructor or a simple copy */ - if (search_sub[i].constructor) { - if (search_sub[i].constructor(module, msg) != 0) { - goto failed; - } - } else if (ldb_msg_copy_attr(msg, - search_sub[i].replace, - search_sub[i].attr) != 0) { - goto failed; - } - - /* remove the added search attribute, unless it was asked for - by the user */ - if (search_sub[i].replace == NULL || - ldb_attr_in_list(attrs, search_sub[i].replace) || - ldb_attr_in_list(attrs, "*")) { - continue; - } - - ldb_msg_remove_attr(msg, search_sub[i].replace); - } - } - - return 0; - -failed: - ldb_debug_set(ldb, LDB_DEBUG_WARNING, - "operational_search_post_process failed for attribute '%s'\n", - attrs[a]); - return -1; -} - - -/* - hook search operations -*/ - -struct operational_context { - struct ldb_module *module; - struct ldb_request *req; - - const char * const *attrs; -}; - -static int operational_callback(struct ldb_request *req, struct ldb_reply *ares) -{ - struct operational_context *ac; - int ret; - - ac = talloc_get_type(req->context, struct operational_context); - - if (!ares) { - return ldb_module_done(ac->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - if (ares->error != LDB_SUCCESS) { - return ldb_module_done(ac->req, ares->controls, - ares->response, ares->error); - } - - switch (ares->type) { - case LDB_REPLY_ENTRY: - /* for each record returned post-process to add any derived - attributes that have been asked for */ - ret = operational_search_post_process(ac->module, - ares->message, - ac->attrs); - if (ret != 0) { - return ldb_module_done(ac->req, NULL, NULL, - LDB_ERR_OPERATIONS_ERROR); - } - return ldb_module_send_entry(ac->req, ares->message, ares->controls); - - case LDB_REPLY_REFERRAL: - /* ignore referrals */ - break; - - case LDB_REPLY_DONE: - - return ldb_module_done(ac->req, ares->controls, - ares->response, LDB_SUCCESS); - } - - talloc_free(ares); - return LDB_SUCCESS; -} - -static int operational_search(struct ldb_module *module, struct ldb_request *req) -{ - struct ldb_context *ldb; - struct operational_context *ac; - struct ldb_request *down_req; - const char **search_attrs = NULL; - int i, a; - int ret; - - ldb = ldb_module_get_ctx(module); - - ac = talloc(req, struct operational_context); - if (ac == NULL) { - return LDB_ERR_OPERATIONS_ERROR; - } - - ac->module = module; - ac->req = req; - ac->attrs = req->op.search.attrs; - - /* FIXME: We must copy the tree and keep the original - * unmodified. SSS */ - /* replace any attributes in the parse tree that are - searchable, but are stored using a different name in the - backend */ - for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) { - ldb_parse_tree_attr_replace(req->op.search.tree, - parse_tree_sub[i].attr, - parse_tree_sub[i].replace); - } - - /* in the list of attributes we are looking for, rename any - attributes to the alias for any hidden attributes that can - be fetched directly using non-hidden names */ - for (a=0;ac->attrs && ac->attrs[a];a++) { - for (i=0;i<ARRAY_SIZE(search_sub);i++) { - if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 && - search_sub[i].replace) { - if (!search_attrs) { - search_attrs = ldb_attr_list_copy(req, ac->attrs); - if (search_attrs == NULL) { - return LDB_ERR_OPERATIONS_ERROR; - } - } - search_attrs[a] = search_sub[i].replace; - } - } - } - - ret = ldb_build_search_req_ex(&down_req, ldb, ac, - req->op.search.base, - req->op.search.scope, - req->op.search.tree, - /* use new set of attrs if any */ - search_attrs == NULL?req->op.search.attrs:search_attrs, - req->controls, - ac, operational_callback, - req); - if (ret != LDB_SUCCESS) { - return LDB_ERR_OPERATIONS_ERROR; - } - - /* perform the search */ - return ldb_next_request(module, down_req); -} - -static int operational_init(struct ldb_module *ctx) -{ - int ret = 0; - - if (ret != 0) { - return ret; - } - - return ldb_next_init(ctx); -} - -const struct ldb_module_ops ldb_operational_module_ops = { - .name = "operational", - .search = operational_search, - .init_context = operational_init -}; diff --git a/source4/lib/ldb/modules/paged_results.c b/source4/lib/ldb/modules/paged_results.c index 2a06c5e6c5..b712f84872 100644 --- a/source4/lib/ldb/modules/paged_results.c +++ b/source4/lib/ldb/modules/paged_results.c @@ -408,8 +408,8 @@ static int paged_request_init(struct ldb_module *module) ret = ldb_mod_register_control(module, LDB_CONTROL_PAGED_RESULTS_OID); if (ret != LDB_SUCCESS) { ldb_debug(ldb, LDB_DEBUG_WARNING, - "paged_request:" - "Unable to register control with rootdse!\n"); + "paged_results:" + "Unable to register control with rootdse!"); } return ldb_next_init(module); diff --git a/source4/lib/ldb/modules/paged_searches.c b/source4/lib/ldb/modules/paged_searches.c index 01e77cb22c..c5430eb9bf 100644 --- a/source4/lib/ldb/modules/paged_searches.c +++ b/source4/lib/ldb/modules/paged_searches.c @@ -2,6 +2,7 @@ ldb database library Copyright (C) Simo Sorce 2005-2008 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009 ** NOTE! The following LGPL license applies to the ldb ** library. This does NOT imply that all of Samba is released @@ -52,23 +53,40 @@ struct ps_context { char **saved_referrals; int num_referrals; + + struct ldb_request *down_req; }; -static int check_ps_continuation(struct ldb_request *req, struct ldb_reply *ares) +static int check_ps_continuation(struct ps_context *ac, struct ldb_request *req, struct ldb_reply *ares) { - struct ps_context *ac; - struct ldb_paged_control *rep_control, *req_control; + struct ldb_context *ldb; + struct ldb_control *rep_control, *req_control; + struct ldb_paged_control *paged_rep_control = NULL, *paged_req_control = NULL; + ldb = ldb_module_get_ctx(ac->module); - ac = talloc_get_type(req->context, struct ps_context); + rep_control = ldb_reply_get_control(ares, LDB_CONTROL_PAGED_RESULTS_OID); + if (rep_control) { + paged_rep_control = talloc_get_type(rep_control->data, struct ldb_paged_control); + } - /* look up our paged control */ - if (!ares->controls || strcmp(LDB_CONTROL_PAGED_RESULTS_OID, ares->controls[0]->oid) != 0) { - /* something wrong here */ - return LDB_ERR_OPERATIONS_ERROR; + req_control = ldb_request_get_control(req, LDB_CONTROL_PAGED_RESULTS_OID); + paged_req_control = talloc_get_type(req_control->data, struct ldb_paged_control); + + if (!rep_control || !paged_rep_control) { + if (paged_req_control->cookie) { + /* something wrong here - why give us a control back befre, but not one now? */ + ldb_set_errstring(ldb, "paged_searches: ERROR: We got back a control from a previous page, but this time no control was returned!"); + return LDB_ERR_OPERATIONS_ERROR; + } else { + /* No cookie recived yet, valid to just return the full data set */ + + /* we are done */ + ac->pending = false; + return LDB_SUCCESS; + } } - rep_control = talloc_get_type(ares->controls[0]->data, struct ldb_paged_control); - if (rep_control->cookie_len == 0) { + if (paged_rep_control->cookie_len == 0) { /* we are done */ ac->pending = false; return LDB_SUCCESS; @@ -79,21 +97,14 @@ static int check_ps_continuation(struct ldb_request *req, struct ldb_reply *ares /* if there's a reply control we must find a request * control matching it */ - if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, req->controls[0]->oid) != 0) { - /* something wrong here */ - return LDB_ERR_OPERATIONS_ERROR; - } - - req_control = talloc_get_type(req->controls[0]->data, struct ldb_paged_control); - - if (req_control->cookie) { - talloc_free(req_control->cookie); + if (paged_req_control->cookie) { + talloc_free(paged_req_control->cookie); } - req_control->cookie = talloc_memdup(req_control, - rep_control->cookie, - rep_control->cookie_len); - req_control->cookie_len = rep_control->cookie_len; + paged_req_control->cookie = talloc_memdup(req_control, + paged_rep_control->cookie, + paged_rep_control->cookie_len); + paged_req_control->cookie_len = paged_rep_control->cookie_len; ac->pending = true; return LDB_SUCCESS; @@ -141,8 +152,6 @@ static int send_referrals(struct ps_context *ac) return LDB_SUCCESS; } -static int ps_next_request(struct ps_context *ac); - static int ps_callback(struct ldb_request *req, struct ldb_reply *ares) { struct ps_context *ac; @@ -176,14 +185,15 @@ static int ps_callback(struct ldb_request *req, struct ldb_reply *ares) case LDB_REPLY_DONE: - ret = check_ps_continuation(req, ares); + ret = check_ps_continuation(ac, req, ares); if (ret != LDB_SUCCESS) { return ldb_module_done(ac->req, NULL, NULL, ret); } if (ac->pending) { - ret = ps_next_request(ac); + ret = ldb_next_request(ac->module, ac->down_req); + if (ret != LDB_SUCCESS) { return ldb_module_done(ac->req, NULL, NULL, ret); @@ -214,14 +224,16 @@ static int ps_search(struct ldb_module *module, struct ldb_request *req) struct ldb_context *ldb; struct private_data *private_data; struct ps_context *ac; + struct ldb_paged_control *control; + int ret; private_data = talloc_get_type(ldb_module_get_private(module), struct private_data); ldb = ldb_module_get_ctx(module); - /* check if paging is supported and if there is a any control */ - if (!private_data || !private_data->paged_supported || req->controls) { + /* check if paging is supported */ + if (!private_data || !private_data->paged_supported) { /* do not touch this request paged controls not - * supported or explicit controls have been set or we + * supported or we * are just not setup yet */ return ldb_next_request(module, req); } @@ -238,30 +250,9 @@ static int ps_search(struct ldb_module *module, struct ldb_request *req) ac->saved_referrals = NULL; ac->num_referrals = 0; - return ps_next_request(ac); -} - -static int ps_next_request(struct ps_context *ac) { - - struct ldb_context *ldb; - struct ldb_paged_control *control; - struct ldb_control **controls; - struct ldb_request *new_req; - int ret; - ldb = ldb_module_get_ctx(ac->module); - controls = talloc_array(ac, struct ldb_control *, 2); - if (!controls) { - return LDB_ERR_OPERATIONS_ERROR; - } - - controls[0] = talloc(controls, struct ldb_control); - if (!controls[0]) { - return LDB_ERR_OPERATIONS_ERROR; - } - - control = talloc(controls[0], struct ldb_paged_control); + control = talloc(ac, struct ldb_paged_control); if (!control) { return LDB_ERR_OPERATIONS_ERROR; } @@ -270,26 +261,28 @@ static int ps_next_request(struct ps_context *ac) { control->cookie = NULL; control->cookie_len = 0; - controls[0]->oid = LDB_CONTROL_PAGED_RESULTS_OID; - controls[0]->critical = 1; - controls[0]->data = control; - controls[1] = NULL; - - ret = ldb_build_search_req_ex(&new_req, ldb, ac, + ret = ldb_build_search_req_ex(&ac->down_req, ldb, ac, ac->req->op.search.base, ac->req->op.search.scope, ac->req->op.search.tree, ac->req->op.search.attrs, - controls, + ac->req->controls, ac, ps_callback, ac->req); if (ret != LDB_SUCCESS) { return ret; } - talloc_steal(new_req, controls); - return ldb_next_request(ac->module, new_req); + ret = ldb_request_add_control(ac->down_req, LDB_CONTROL_PAGED_RESULTS_OID, + true, control); + if (ret != LDB_SUCCESS) { + return ret; + } + + talloc_steal(ac->down_req, control); + + return ldb_next_request(ac->module, ac->down_req); } static int check_supported_paged(struct ldb_request *req, diff --git a/source4/lib/ldb/modules/rdn_name.c b/source4/lib/ldb/modules/rdn_name.c index 880678d89d..8b54f52b5e 100644 --- a/source4/lib/ldb/modules/rdn_name.c +++ b/source4/lib/ldb/modules/rdn_name.c @@ -1,8 +1,8 @@ /* ldb database library - Copyright (C) Andrew Bartlet 2005 - Copyright (C) Simo Sorce 2006-2008 + Copyright (C) Andrew Bartlett 2005 + Copyright (C) Simo Sorce 2006-2008 ** NOTE! The following LGPL license applies to the ldb ** library. This does NOT imply that all of Samba is released @@ -23,13 +23,13 @@ */ /* - * Name: rdb_name + * Name: rdn_name * * Component: ldb rdn name module * * Description: keep a consistent name attribute on objects manpulations * - * Author: Andrew Bartlet + * Author: Andrew Bartlett * * Modifications: * - made the module async @@ -98,7 +98,7 @@ static int rdn_name_add(struct ldb_module *module, struct ldb_request *req) int i, ret; ldb = ldb_module_get_ctx(module); - ldb_debug(ldb, LDB_DEBUG_TRACE, "rdn_name_add_record\n"); + ldb_debug(ldb, LDB_DEBUG_TRACE, "rdn_name_add_record"); /* do not manipulate our control entries */ if (ldb_dn_is_special(req->op.add.message->dn)) { @@ -156,9 +156,15 @@ static int rdn_name_add(struct ldb_module *module, struct ldb_request *req) } } if (i == attribute->num_values) { - ldb_debug_set(ldb, LDB_DEBUG_FATAL, - "RDN mismatch on %s: %s (%s)", - ldb_dn_get_linearized(msg->dn), rdn_name, rdn_val.data); + char *rdn_errstring = talloc_asprintf(ac, "RDN mismatch on %s: %s (%.*s) should match one of:", + ldb_dn_get_linearized(msg->dn), rdn_name, + (int)rdn_val.length, (const char *)rdn_val.data); + for (i = 0; i < attribute->num_values; i++) { + rdn_errstring = talloc_asprintf_append(rdn_errstring, " (%.*s)", + (int)attribute->values[i].length, + (const char *)attribute->values[i].data); + } + ldb_debug_set(ldb, LDB_DEBUG_FATAL, "%s", rdn_errstring); talloc_free(ac); /* Match AD's error here */ return LDB_ERR_INVALID_DN_SYNTAX; @@ -288,7 +294,7 @@ static int rdn_name_rename(struct ldb_module *module, struct ldb_request *req) int ret; ldb = ldb_module_get_ctx(module); - ldb_debug(ldb, LDB_DEBUG_TRACE, "rdn_name_rename\n"); + ldb_debug(ldb, LDB_DEBUG_TRACE, "rdn_name_rename"); /* do not manipulate our control entries */ if (ldb_dn_is_special(req->op.rename.newdn)) { diff --git a/source4/lib/ldb/modules/sort.c b/source4/lib/ldb/modules/sort.c index 309101c32b..b4ea017b32 100644 --- a/source4/lib/ldb/modules/sort.c +++ b/source4/lib/ldb/modules/sort.c @@ -117,10 +117,13 @@ static int sort_compare(struct ldb_message **msg1, struct ldb_message **msg2, vo el1 = ldb_msg_find_element(*msg1, ac->attributeName); el2 = ldb_msg_find_element(*msg2, ac->attributeName); - if (!el1 || !el2) { - /* the attribute was not found return and - * set an error */ - ac->sort_result = LDB_ERR_UNWILLING_TO_PERFORM; + if (!el1 && el2) { + return 1; + } + if (el1 && !el2) { + return -1; + } + if (!el1 && !el2) { return 0; } @@ -255,7 +258,7 @@ static int server_sort_search(struct ldb_module *module, struct ldb_request *req ldb = ldb_module_get_ctx(module); - /* check if there's a paged request control */ + /* check if there's a server sort control */ control = ldb_request_get_control(req, LDB_CONTROL_SERVER_SORT_OID); if (control == NULL) { /* not found go on */ @@ -336,7 +339,7 @@ static int server_sort_init(struct ldb_module *module) if (ret != LDB_SUCCESS) { ldb_debug(ldb, LDB_DEBUG_WARNING, "server_sort:" - "Unable to register control with rootdse!\n"); + "Unable to register control with rootdse!"); } return ldb_next_init(module); diff --git a/source4/lib/ldb/pyldb.c b/source4/lib/ldb/pyldb.c index 81b960979f..1ba5109f2f 100644 --- a/source4/lib/ldb/pyldb.c +++ b/source4/lib/ldb/pyldb.c @@ -1,16 +1,17 @@ /* Unix SMB/CIFS implementation. - Swig interface to ldb. + Python interface to ldb. Copyright (C) 2005,2006 Tim Potter <tpot@samba.org> Copyright (C) 2006 Simo Sorce <idra@samba.org> - Copyright (C) 2007-2008 Jelmer Vernooij <jelmer@samba.org> + Copyright (C) 2007-2009 Jelmer Vernooij <jelmer@samba.org> + Copyright (C) 2009 Matthias Dieter Wallnöfer ** NOTE! The following LGPL license applies to the ldb ** library. This does NOT imply that all of Samba is released ** under the LGPL - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -41,49 +42,50 @@ typedef intargfunc ssizeargfunc; #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None #endif -/* Picked out of thin air. To do this properly, we should probably have some part of the - * errors in LDB be allocated to bindings ? */ -#define LDB_ERR_PYTHON_EXCEPTION 142 - -static PyObject *PyExc_LdbError; - -void PyErr_SetLdbError(int ret, struct ldb_context *ldb_ctx) +static void PyErr_SetLdbError(PyObject *error, int ret, struct ldb_context *ldb_ctx) { if (ret == LDB_ERR_PYTHON_EXCEPTION) return; /* Python exception should already be set, just keep that */ - PyErr_SetObject(PyExc_LdbError, Py_BuildValue(discard_const_p(char, "(i,s)"), - ret, ldb_ctx == NULL?ldb_strerror(ret):ldb_errstring(ldb_ctx))); + + PyErr_SetObject(error, + Py_BuildValue(discard_const_p(char, "(i,s)"), ret, + ldb_ctx == NULL?ldb_strerror(ret):ldb_errstring(ldb_ctx))); } +static PyObject *PyExc_LdbError; + +PyAPI_DATA(PyTypeObject) PyLdbMessage; +PyAPI_DATA(PyTypeObject) PyLdbModule; +PyAPI_DATA(PyTypeObject) PyLdbDn; +PyAPI_DATA(PyTypeObject) PyLdb; +PyAPI_DATA(PyTypeObject) PyLdbMessageElement; +PyAPI_DATA(PyTypeObject) PyLdbTree; + static PyObject *PyObject_FromLdbValue(struct ldb_context *ldb_ctx, struct ldb_message_element *el, struct ldb_val *val) { - const struct ldb_schema_attribute *a; struct ldb_val new_val; TALLOC_CTX *mem_ctx = talloc_new(NULL); PyObject *ret; - + new_val = *val; - - if (ldb_ctx != NULL) { - a = ldb_schema_attribute_by_name(ldb_ctx, el->name); - - if (a != NULL) { - if (a->syntax->ldif_write_fn(ldb_ctx, mem_ctx, val, &new_val) != 0) { - talloc_free(mem_ctx); - return NULL; - } - } - } - + ret = PyString_FromStringAndSize((const char *)new_val.data, new_val.length); - + talloc_free(mem_ctx); - + return ret; } +/** + * Obtain a ldb DN from a Python object. + * + * @param mem_ctx Memory context + * @param object Python object + * @param ldb_ctx LDB context + * @return Whether or not the conversion succeeded + */ bool PyObject_AsDn(TALLOC_CTX *mem_ctx, PyObject *object, struct ldb_context *ldb_ctx, struct ldb_dn **dn) { @@ -104,6 +106,12 @@ bool PyObject_AsDn(TALLOC_CTX *mem_ctx, PyObject *object, return false; } +/** + * Create a Python object from a ldb_result. + * + * @param result LDB result to convert + * @return Python object with converted result (a list object) + */ static PyObject *PyLdbResult_FromResult(struct ldb_result *result) { PyObject *ret; @@ -119,11 +127,20 @@ static PyObject *PyLdbResult_FromResult(struct ldb_result *result) return ret; } -static struct ldb_result *PyLdbResult_AsResult(TALLOC_CTX *mem_ctx, PyObject *obj) +/** + * Create a LDB Result from a Python object. + * If conversion fails, NULL will be returned and a Python exception set. + * + * @param mem_ctx Memory context in which to allocate the LDB Result + * @param obj Python object to convert + * @return a ldb_result, or NULL if the conversion failed + */ +static struct ldb_result *PyLdbResult_AsResult(TALLOC_CTX *mem_ctx, + PyObject *obj) { struct ldb_result *res; int i; - + if (obj == Py_None) return NULL; @@ -194,13 +211,35 @@ static PyObject *py_ldb_dn_check_special(PyLdbDnObject *self, PyObject *args) static int py_ldb_dn_compare(PyLdbDnObject *dn1, PyLdbDnObject *dn2) { - return ldb_dn_compare(dn1->dn, dn2->dn); + int ret; + ret = ldb_dn_compare(dn1->dn, dn2->dn); + if (ret < 0) ret = -1; + if (ret > 0) ret = 1; + return ret; } static PyObject *py_ldb_dn_get_parent(PyLdbDnObject *self) { struct ldb_dn *dn = PyLdbDn_AsDn((PyObject *)self); - return PyLdbDn_FromDn(ldb_dn_get_parent(NULL, dn)); + struct ldb_dn *parent; + PyLdbDnObject *py_ret; + TALLOC_CTX *mem_ctx = talloc_new(NULL); + + parent = ldb_dn_get_parent(mem_ctx, dn); + if (parent == NULL) { + talloc_free(mem_ctx); + Py_RETURN_NONE; + } + + py_ret = (PyLdbDnObject *)PyLdbDn.tp_alloc(&PyLdbDn, 0); + if (py_ret == NULL) { + PyErr_NoMemory(); + talloc_free(mem_ctx); + return NULL; + } + py_ret->mem_ctx = mem_ctx; + py_ret->dn = parent; + return (PyObject *)py_ret; } #define dn_ldb_ctx(dn) ((struct ldb_context *)dn) @@ -281,11 +320,20 @@ static PyObject *py_ldb_dn_concat(PyLdbDnObject *self, PyObject *py_other) { struct ldb_dn *dn = PyLdbDn_AsDn((PyObject *)self), *other; - struct ldb_dn *ret = ldb_dn_copy(NULL, dn); + PyLdbDnObject *py_ret; + if (!PyObject_AsDn(NULL, py_other, NULL, &other)) return NULL; - ldb_dn_add_child(ret, other); - return PyLdbDn_FromDn(ret); + + py_ret = (PyLdbDnObject *)PyLdbDn.tp_alloc(&PyLdbDn, 0); + if (py_ret == NULL) { + PyErr_NoMemory(); + return NULL; + } + py_ret->mem_ctx = talloc_new(NULL); + py_ret->dn = ldb_dn_copy(py_ret->mem_ctx, dn); + ldb_dn_add_child(py_ret->dn, other); + return (PyObject *)py_ret; } static PySequenceMethods py_ldb_dn_seq = { @@ -299,6 +347,7 @@ static PyObject *py_ldb_dn_new(PyTypeObject *type, PyObject *args, PyObject *kwa char *str; PyObject *py_ldb; struct ldb_context *ldb_ctx; + TALLOC_CTX *mem_ctx; PyLdbDnObject *py_ret; const char * const kwnames[] = { "ldb", "dn", NULL }; @@ -308,22 +357,28 @@ static PyObject *py_ldb_dn_new(PyTypeObject *type, PyObject *args, PyObject *kwa return NULL; ldb_ctx = PyLdb_AsLdbContext(py_ldb); - - ret = ldb_dn_new(ldb_ctx, ldb_ctx, str); - /* ldb_dn_new() doesn't accept NULL as memory context, so - we do it this way... */ - talloc_steal(NULL, ret); + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } + + ret = ldb_dn_new(mem_ctx, ldb_ctx, str); if (ret == NULL || !ldb_dn_validate(ret)) { + talloc_free(mem_ctx); PyErr_SetString(PyExc_ValueError, "unable to parse dn string"); return NULL; } py_ret = (PyLdbDnObject *)type->tp_alloc(type, 0); if (ret == NULL) { + talloc_free(mem_ctx); PyErr_NoMemory(); return NULL; } + py_ret->mem_ctx = mem_ctx; py_ret->dn = ret; return (PyObject *)py_ret; } @@ -331,6 +386,11 @@ static PyObject *py_ldb_dn_new(PyTypeObject *type, PyObject *args, PyObject *kwa PyObject *PyLdbDn_FromDn(struct ldb_dn *dn) { PyLdbDnObject *py_ret; + + if (dn == NULL) { + Py_RETURN_NONE; + } + py_ret = (PyLdbDnObject *)PyLdbDn.tp_alloc(&PyLdbDn, 0); if (py_ret == NULL) { PyErr_NoMemory(); @@ -372,14 +432,14 @@ static void py_ldb_debug(void *context, enum ldb_debug_level level, const char * static PyObject *py_ldb_set_debug(PyLdbObject *self, PyObject *args) { PyObject *cb; - + if (!PyArg_ParseTuple(args, "O", &cb)) return NULL; Py_INCREF(cb); /* FIXME: Where do we DECREF cb ? */ - PyErr_LDB_ERROR_IS_ERR_RAISE(ldb_set_debug(self->ldb_ctx, py_ldb_debug, cb), PyLdb_AsLdbContext(self)); - + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ldb_set_debug(self->ldb_ctx, py_ldb_debug, cb), PyLdb_AsLdbContext(self)); + Py_RETURN_NONE; } @@ -407,25 +467,25 @@ static PyObject *py_ldb_set_modules_dir(PyTypeObject *self, PyObject *args) static PyObject *py_ldb_transaction_start(PyLdbObject *self) { - PyErr_LDB_ERROR_IS_ERR_RAISE(ldb_transaction_start(PyLdb_AsLdbContext(self)), PyLdb_AsLdbContext(self)); + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ldb_transaction_start(PyLdb_AsLdbContext(self)), PyLdb_AsLdbContext(self)); Py_RETURN_NONE; } static PyObject *py_ldb_transaction_commit(PyLdbObject *self) { - PyErr_LDB_ERROR_IS_ERR_RAISE(ldb_transaction_commit(PyLdb_AsLdbContext(self)), PyLdb_AsLdbContext(self)); + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ldb_transaction_commit(PyLdb_AsLdbContext(self)), PyLdb_AsLdbContext(self)); Py_RETURN_NONE; } static PyObject *py_ldb_transaction_cancel(PyLdbObject *self) { - PyErr_LDB_ERROR_IS_ERR_RAISE(ldb_transaction_cancel(PyLdb_AsLdbContext(self)), PyLdb_AsLdbContext(self)); + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ldb_transaction_cancel(PyLdb_AsLdbContext(self)), PyLdb_AsLdbContext(self)); Py_RETURN_NONE; } static PyObject *py_ldb_setup_wellknown_attributes(PyLdbObject *self) { - PyErr_LDB_ERROR_IS_ERR_RAISE(ldb_setup_wellknown_attributes(PyLdb_AsLdbContext(self)), PyLdb_AsLdbContext(self)); + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ldb_setup_wellknown_attributes(PyLdb_AsLdbContext(self)), PyLdb_AsLdbContext(self)); Py_RETURN_NONE; } @@ -451,7 +511,6 @@ static PyObject *py_ldb_get_schema_basedn(PyLdbObject *self) return PyLdbDn_FromDn(dn); } - static PyObject *py_ldb_get_config_basedn(PyLdbObject *self) { struct ldb_dn *dn = ldb_get_config_basedn(PyLdb_AsLdbContext(self)); @@ -460,7 +519,6 @@ static PyObject *py_ldb_get_config_basedn(PyLdbObject *self) return PyLdbDn_FromDn(dn); } - static PyObject *py_ldb_get_default_basedn(PyLdbObject *self) { struct ldb_dn *dn = ldb_get_default_basedn(PyLdb_AsLdbContext(self)); @@ -469,22 +527,24 @@ static PyObject *py_ldb_get_default_basedn(PyLdbObject *self) return PyLdbDn_FromDn(dn); } -static const char **PyList_AsStringList(TALLOC_CTX *mem_ctx, PyObject *list) +static const char **PyList_AsStringList(TALLOC_CTX *mem_ctx, PyObject *list, + const char *paramname) { const char **ret; int i; if (!PyList_Check(list)) { - PyErr_SetString(PyExc_TypeError, "options is not a list"); + PyErr_Format(PyExc_TypeError, "%s is not a list", paramname); return NULL; } ret = talloc_array(NULL, const char *, PyList_Size(list)+1); for (i = 0; i < PyList_Size(list); i++) { PyObject *item = PyList_GetItem(list, i); if (!PyString_Check(item)) { - PyErr_SetString(PyExc_TypeError, "options should be strings"); + PyErr_Format(PyExc_TypeError, "%s should be strings", paramname); return NULL; } - ret[i] = PyString_AsString(item); + ret[i] = talloc_strndup(ret, PyString_AsString(item), + PyString_Size(item)); } ret[i] = NULL; return ret; @@ -510,15 +570,15 @@ static int py_ldb_init(PyLdbObject *self, PyObject *args, PyObject *kwargs) if (py_options == Py_None) { options = NULL; } else { - options = PyList_AsStringList(ldb, py_options); + options = PyList_AsStringList(ldb, py_options, "options"); if (options == NULL) return -1; } - + if (url != NULL) { ret = ldb_connect(ldb, url, flags, options); if (ret != LDB_SUCCESS) { - PyErr_SetLdbError(ret, ldb); + PyErr_SetLdbError(PyExc_LdbError, ret, ldb); return -1; } } @@ -531,17 +591,19 @@ static PyObject *py_ldb_new(PyTypeObject *type, PyObject *args, PyObject *kwargs { PyLdbObject *ret; struct ldb_context *ldb; - ldb = ldb_init(NULL, NULL); - if (ldb == NULL) { + ret = (PyLdbObject *)type->tp_alloc(type, 0); + if (ret == NULL) { PyErr_NoMemory(); return NULL; } + ret->mem_ctx = talloc_new(NULL); + ldb = ldb_init(ret->mem_ctx, NULL); - ret = (PyLdbObject *)type->tp_alloc(type, 0); - if (ret == NULL) { + if (ldb == NULL) { PyErr_NoMemory(); return NULL; } + ret->ldb_ctx = ldb; return (PyObject *)ret; } @@ -555,7 +617,7 @@ static PyObject *py_ldb_connect(PyLdbObject *self, PyObject *args, PyObject *kwa const char **options; const char * const kwnames[] = { "url", "flags", "options", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|iO", + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ziO", discard_const_p(char *, kwnames), &url, &flags, &py_options)) return NULL; @@ -563,15 +625,15 @@ static PyObject *py_ldb_connect(PyLdbObject *self, PyObject *args, PyObject *kwa if (py_options == Py_None) { options = NULL; } else { - options = PyList_AsStringList(NULL, py_options); + options = PyList_AsStringList(NULL, py_options, "options"); if (options == NULL) return NULL; } - + ret = ldb_connect(PyLdb_AsLdbContext(self), url, flags, options); talloc_free(options); - PyErr_LDB_ERROR_IS_ERR_RAISE(ret, PyLdb_AsLdbContext(self)); + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, PyLdb_AsLdbContext(self)); Py_RETURN_NONE; } @@ -589,7 +651,7 @@ static PyObject *py_ldb_modify(PyLdbObject *self, PyObject *args) } ret = ldb_modify(PyLdb_AsLdbContext(self), PyLdbMessage_AsMessage(py_msg)); - PyErr_LDB_ERROR_IS_ERR_RAISE(ret, PyLdb_AsLdbContext(self)); + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, PyLdb_AsLdbContext(self)); Py_RETURN_NONE; } @@ -601,23 +663,39 @@ static PyObject *py_ldb_add(PyLdbObject *self, PyObject *args) Py_ssize_t dict_pos, msg_pos; struct ldb_message_element *msgel; struct ldb_message *msg; + struct ldb_context *ldb_ctx; + struct ldb_request *req; PyObject *key, *value; + PyObject *py_controls = Py_None; + TALLOC_CTX *mem_ctx; + struct ldb_control **parsed_controls; - if (!PyArg_ParseTuple(args, "O", &py_msg)) + if (!PyArg_ParseTuple(args, "O|O", &py_msg, &py_controls )) return NULL; + ldb_ctx = PyLdb_AsLdbContext(self); + mem_ctx = talloc_new(NULL); + if (py_controls == Py_None) { + parsed_controls = NULL; + } else { + const char **controls = PyList_AsStringList(ldb_ctx, py_controls, "controls"); + parsed_controls = ldb_parse_control_strings(ldb_ctx, ldb_ctx, controls); + talloc_free(controls); + } if (PyDict_Check(py_msg)) { PyObject *dn_value = PyDict_GetItemString(py_msg, "dn"); - msg = ldb_msg_new(NULL); + msg = ldb_msg_new(mem_ctx); msg->elements = talloc_zero_array(msg, struct ldb_message_element, PyDict_Size(py_msg)); msg_pos = dict_pos = 0; if (dn_value) { - if (!PyObject_AsDn(msg, dn_value, PyLdb_AsLdbContext(self), &msg->dn)) { + if (!PyObject_AsDn(msg, dn_value, ldb_ctx, &msg->dn)) { PyErr_SetString(PyExc_TypeError, "unable to import dn object"); + talloc_free(mem_ctx); return NULL; } if (msg->dn == NULL) { PyErr_SetString(PyExc_TypeError, "dn set but not found"); + talloc_free(mem_ctx); return NULL; } } @@ -628,6 +706,7 @@ static PyObject *py_ldb_add(PyLdbObject *self, PyObject *args) msgel = PyObject_AsMessageElement(msg->elements, value, 0, key_str); if (msgel == NULL) { PyErr_SetString(PyExc_TypeError, "unable to import element"); + talloc_free(mem_ctx); return NULL; } memcpy(&msg->elements[msg_pos], msgel, sizeof(*msgel)); @@ -637,6 +716,7 @@ static PyObject *py_ldb_add(PyLdbObject *self, PyObject *args) if (msg->dn == NULL) { PyErr_SetString(PyExc_TypeError, "no dn set"); + talloc_free(mem_ctx); return NULL; } @@ -644,14 +724,57 @@ static PyObject *py_ldb_add(PyLdbObject *self, PyObject *args) } else { msg = PyLdbMessage_AsMessage(py_msg); } - - ret = ldb_add(PyLdb_AsLdbContext(self), msg); - PyErr_LDB_ERROR_IS_ERR_RAISE(ret, PyLdb_AsLdbContext(self)); + + ret = ldb_msg_sanity_check(ldb_ctx, msg); + if (ret != LDB_SUCCESS) { + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, PyLdb_AsLdbContext(self)); + talloc_free(mem_ctx); + return NULL; + } - Py_RETURN_NONE; -} + ret = ldb_build_add_req(&req, ldb_ctx, ldb_ctx, + msg, + parsed_controls, + NULL, + ldb_op_default_callback, + NULL); + if (ret != LDB_SUCCESS) { + PyErr_SetString(PyExc_TypeError, "failed to build request"); + talloc_free(mem_ctx); + return NULL; + } + + /* do request and autostart a transaction */ + /* Then let's LDB handle the message error in case of pb as they are meaningful */ + + ret = ldb_transaction_start(ldb_ctx); + if (ret != LDB_SUCCESS) { + talloc_free(req); + talloc_free(mem_ctx); + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, PyLdb_AsLdbContext(self)); + } + ret = ldb_request(ldb_ctx, req); + if (ret == LDB_SUCCESS) { + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + } + + if (ret == LDB_SUCCESS) { + ret = ldb_transaction_commit(ldb_ctx); + } else { + ldb_transaction_cancel(ldb_ctx); + if (ldb_ctx->err_string == NULL) { + /* no error string was setup by the backend */ + ldb_asprintf_errstring(ldb_ctx, "%s (%d)", ldb_strerror(ret), ret); + } + } + talloc_free(req); + talloc_free(mem_ctx); + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, PyLdb_AsLdbContext(self)); + + Py_RETURN_NONE; +} static PyObject *py_ldb_delete(PyLdbObject *self, PyObject *args) { @@ -668,7 +791,7 @@ static PyObject *py_ldb_delete(PyLdbObject *self, PyObject *args) return NULL; ret = ldb_delete(ldb, dn); - PyErr_LDB_ERROR_IS_ERR_RAISE(ret, ldb); + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, ldb); Py_RETURN_NONE; } @@ -679,18 +802,29 @@ static PyObject *py_ldb_rename(PyLdbObject *self, PyObject *args) struct ldb_dn *dn1, *dn2; int ret; struct ldb_context *ldb; + TALLOC_CTX *mem_ctx; if (!PyArg_ParseTuple(args, "OO", &py_dn1, &py_dn2)) return NULL; + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } ldb = PyLdb_AsLdbContext(self); - if (!PyObject_AsDn(NULL, py_dn1, ldb, &dn1)) + if (!PyObject_AsDn(mem_ctx, py_dn1, ldb, &dn1)) { + talloc_free(mem_ctx); return NULL; + } - if (!PyObject_AsDn(NULL, py_dn2, ldb, &dn2)) + if (!PyObject_AsDn(mem_ctx, py_dn2, ldb, &dn2)) { + talloc_free(mem_ctx); return NULL; + } ret = ldb_rename(ldb, dn1, dn2); - PyErr_LDB_ERROR_IS_ERR_RAISE(ret, ldb); + talloc_free(mem_ctx); + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, ldb); Py_RETURN_NONE; } @@ -716,7 +850,7 @@ static PyObject *py_ldb_schema_attribute_add(PyLdbObject *self, PyObject *args) ret = ldb_schema_attribute_add(PyLdb_AsLdbContext(self), attribute, flags, syntax); - PyErr_LDB_ERROR_IS_ERR_RAISE(ret, PyLdb_AsLdbContext(self)); + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, PyLdb_AsLdbContext(self)); Py_RETURN_NONE; } @@ -727,7 +861,6 @@ static PyObject *ldb_ldif_to_pyobject(struct ldb_ldif *ldif) Py_RETURN_NONE; } else { /* We don't want this attached to the 'ldb' any more */ - talloc_steal(NULL, ldif); return Py_BuildValue(discard_const_p(char, "(iO)"), ldif->changetype, PyLdbMessage_FromMessage(ldif->msg)); @@ -735,22 +868,102 @@ static PyObject *ldb_ldif_to_pyobject(struct ldb_ldif *ldif) } +static PyObject *py_ldb_write_ldif(PyLdbMessageObject *self, PyObject *args) +{ + int changetype; + PyObject *py_msg; + struct ldb_ldif ldif; + PyObject *ret; + char *string; + TALLOC_CTX *mem_ctx; + + if (!PyArg_ParseTuple(args, "Oi", &py_msg, &changetype)) + return NULL; + + if (!PyLdbMessage_Check(py_msg)) { + PyErr_SetString(PyExc_TypeError, "Expected Ldb Message for msg"); + return NULL; + } + + ldif.msg = PyLdbMessage_AsMessage(py_msg); + ldif.changetype = changetype; + + mem_ctx = talloc_new(NULL); + + string = ldb_ldif_write_string(PyLdb_AsLdbContext(self), mem_ctx, &ldif); + if (!string) { + PyErr_SetString(PyExc_KeyError, "Failed to generate LDIF"); + return NULL; + } + + ret = PyString_FromString(string); + + talloc_free(mem_ctx); + + return ret; +} + static PyObject *py_ldb_parse_ldif(PyLdbObject *self, PyObject *args) { PyObject *list; struct ldb_ldif *ldif; const char *s; + TALLOC_CTX *mem_ctx; + if (!PyArg_ParseTuple(args, "s", &s)) return NULL; + mem_ctx = talloc_new(NULL); + if (!mem_ctx) { + Py_RETURN_NONE; + } + list = PyList_New(0); - while ((ldif = ldb_ldif_read_string(self->ldb_ctx, &s)) != NULL) { - PyList_Append(list, ldb_ldif_to_pyobject(ldif)); + while (s && *s != '\0') { + ldif = ldb_ldif_read_string(self->ldb_ctx, &s); + talloc_steal(mem_ctx, ldif); + if (ldif) { + PyList_Append(list, ldb_ldif_to_pyobject(ldif)); + } else { + PyErr_SetString(PyExc_ValueError, "unable to parse ldif string"); + talloc_free(mem_ctx); + return NULL; + } } + talloc_free(mem_ctx); /* The pyobject already has a reference to the things it needs */ return PyObject_GetIter(list); } +static PyObject *py_ldb_msg_diff(PyLdbObject *self, PyObject *args) +{ + PyObject *py_msg_old; + PyObject *py_msg_new; + struct ldb_message *diff; + PyObject *py_ret; + + if (!PyArg_ParseTuple(args, "OO", &py_msg_old, &py_msg_new)) + return NULL; + + if (!PyLdbMessage_Check(py_msg_old)) { + PyErr_SetString(PyExc_TypeError, "Expected Ldb Message for old message"); + return NULL; + } + + if (!PyLdbMessage_Check(py_msg_new)) { + PyErr_SetString(PyExc_TypeError, "Expected Ldb Message for new message"); + return NULL; + } + + diff = ldb_msg_diff(PyLdb_AsLdbContext(self), PyLdbMessage_AsMessage(py_msg_old), PyLdbMessage_AsMessage(py_msg_new)); + if (diff == NULL) + return NULL; + + py_ret = PyLdbMessage_FromMessage(diff); + + return py_ret; +} + static PyObject *py_ldb_schema_format_value(PyLdbObject *self, PyObject *args) { const struct ldb_schema_attribute *a; @@ -763,18 +976,18 @@ static PyObject *py_ldb_schema_format_value(PyLdbObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "sO", &element_name, &val)) return NULL; - + mem_ctx = talloc_new(NULL); - + old_val.data = (uint8_t *)PyString_AsString(val); old_val.length = PyString_Size(val); - + a = ldb_schema_attribute_by_name(PyLdb_AsLdbContext(self), element_name); if (a == NULL) { Py_RETURN_NONE; } - + if (a->syntax->ldif_write_fn(PyLdb_AsLdbContext(self), mem_ctx, &old_val, &new_val) != 0) { talloc_free(mem_ctx); Py_RETURN_NONE; @@ -802,6 +1015,7 @@ static PyObject *py_ldb_search(PyLdbObject *self, PyObject *args, PyObject *kwar struct ldb_context *ldb_ctx; struct ldb_control **parsed_controls; struct ldb_dn *base; + PyObject *py_ret; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OizOO", discard_const_p(char *, kwnames), @@ -813,7 +1027,7 @@ static PyObject *py_ldb_search(PyLdbObject *self, PyObject *args, PyObject *kwar if (py_attrs == Py_None) { attrs = NULL; } else { - attrs = PyList_AsStringList(ldb_ctx, py_attrs); + attrs = PyList_AsStringList(NULL, py_attrs, "attrs"); if (attrs == NULL) return NULL; } @@ -821,14 +1035,16 @@ static PyObject *py_ldb_search(PyLdbObject *self, PyObject *args, PyObject *kwar if (py_base == Py_None) { base = ldb_get_default_basedn(ldb_ctx); } else { - if (!PyObject_AsDn(ldb_ctx, py_base, ldb_ctx, &base)) + if (!PyObject_AsDn(ldb_ctx, py_base, ldb_ctx, &base)) { + talloc_free(attrs); return NULL; + } } if (py_controls == Py_None) { parsed_controls = NULL; } else { - const char **controls = PyList_AsStringList(ldb_ctx, py_controls); + const char **controls = PyList_AsStringList(ldb_ctx, py_controls, "controls"); parsed_controls = ldb_parse_control_strings(ldb_ctx, ldb_ctx, controls); talloc_free(controls); } @@ -836,6 +1052,7 @@ static PyObject *py_ldb_search(PyLdbObject *self, PyObject *args, PyObject *kwar res = talloc_zero(ldb_ctx, struct ldb_result); if (res == NULL) { PyErr_NoMemory(); + talloc_free(attrs); return NULL; } @@ -849,14 +1066,16 @@ static PyObject *py_ldb_search(PyLdbObject *self, PyObject *args, PyObject *kwar ldb_search_default_callback, NULL); + talloc_steal(req, attrs); + if (ret != LDB_SUCCESS) { talloc_free(res); - PyErr_LDB_ERROR_IS_ERR_RAISE(ret, ldb_ctx); + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, ldb_ctx); return NULL; } ret = ldb_request(ldb_ctx, req); - + if (ret == LDB_SUCCESS) { ret = ldb_wait(req->handle, LDB_WAIT_ALL); } @@ -865,11 +1084,15 @@ static PyObject *py_ldb_search(PyLdbObject *self, PyObject *args, PyObject *kwar if (ret != LDB_SUCCESS) { talloc_free(res); - PyErr_LDB_ERROR_IS_ERR_RAISE(ret, ldb_ctx); + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, ldb_ctx); return NULL; } - return PyLdbResult_FromResult(res); + py_ret = PyLdbResult_FromResult(res); + + talloc_free(res); + + return py_ret; } static PyObject *py_ldb_get_opaque(PyLdbObject *self, PyObject *args) @@ -983,6 +1206,12 @@ static PyMethodDef py_ldb_methods[] = { { "parse_ldif", (PyCFunction)py_ldb_parse_ldif, METH_VARARGS, "S.parse_ldif(ldif) -> iter(messages)\n" "Parse a string formatted using LDIF." }, + { "write_ldif", (PyCFunction)py_ldb_write_ldif, METH_VARARGS, + "S.write_ldif(message, changetype) -> ldif\n" + "Print the message as a string formatted using LDIF." }, + { "msg_diff", (PyCFunction)py_ldb_msg_diff, METH_VARARGS, + "S.msg_diff(Message) -> Message\n" + "Return an LDB Message of the difference between two Message objects." }, { "get_opaque", (PyCFunction)py_ldb_get_opaque, METH_VARARGS, "S.get_opaque(name) -> value\n" "Get an opaque value set on this LDB connection. \n" @@ -1035,7 +1264,7 @@ static int py_ldb_contains(PyLdbObject *self, PyObject *obj) ret = ldb_search(ldb_ctx, ldb_ctx, &result, dn, LDB_SCOPE_BASE, NULL, NULL); if (ret != LDB_SUCCESS) { - PyErr_SetLdbError(ret, ldb_ctx); + PyErr_SetLdbError(PyExc_LdbError, ret, ldb_ctx); return -1; } @@ -1115,11 +1344,12 @@ static PyObject *py_ldb_module_del_transaction(PyLdbModuleObject *self) static PyObject *py_ldb_module_search(PyLdbModuleObject *self, PyObject *args, PyObject *kwargs) { - PyObject *py_base, *py_tree, *py_attrs; + PyObject *py_base, *py_tree, *py_attrs, *py_ret; int ret, scope; struct ldb_request *req; const char * const kwnames[] = { "base", "scope", "tree", "attrs", NULL }; struct ldb_module *mod; + const char * const*attrs; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OiOO", discard_const_p(char *, kwnames), @@ -1128,17 +1358,33 @@ static PyObject *py_ldb_module_search(PyLdbModuleObject *self, PyObject *args, P mod = self->mod; + if (py_attrs == Py_None) { + attrs = NULL; + } else { + attrs = PyList_AsStringList(NULL, py_attrs, "attrs"); + if (attrs == NULL) + return NULL; + } + ret = ldb_build_search_req(&req, mod->ldb, NULL, PyLdbDn_AsDn(py_base), - scope, NULL /* expr */, py_attrs == Py_None?NULL:PyList_AsStringList(req, py_attrs), + scope, NULL /* expr */, attrs, NULL /* controls */, NULL, NULL, NULL); - PyErr_LDB_ERROR_IS_ERR_RAISE(ret, mod->ldb); + + talloc_steal(req, attrs); + + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, mod->ldb); + + req->op.search.res = NULL; ret = mod->ops->search(mod, req); - talloc_free(req); - PyErr_LDB_ERROR_IS_ERR_RAISE(ret, mod->ldb); + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, mod->ldb); - return PyLdbResult_FromResult(req->op.search.res); + py_ret = PyLdbResult_FromResult(req->op.search.res); + + talloc_free(req); + + return py_ret; } @@ -1159,7 +1405,7 @@ static PyObject *py_ldb_module_add(PyLdbModuleObject *self, PyObject *args) mod = PyLdbModule_AsModule(self); ret = mod->ops->add(mod, req); - PyErr_LDB_ERROR_IS_ERR_RAISE(ret, mod->ldb); + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, mod->ldb); Py_RETURN_NONE; } @@ -1173,15 +1419,15 @@ static PyObject *py_ldb_module_modify(PyLdbModuleObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "O", &py_message)) return NULL; - + req = talloc_zero(NULL, struct ldb_request); req->operation = LDB_MODIFY; req->op.mod.message = PyLdbMessage_AsMessage(py_message); - + mod = PyLdbModule_AsModule(self); ret = mod->ops->modify(mod, req); - PyErr_LDB_ERROR_IS_ERR_RAISE(ret, mod->ldb); + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, mod->ldb); Py_RETURN_NONE; } @@ -1194,14 +1440,14 @@ static PyObject *py_ldb_module_delete(PyLdbModuleObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "O", &py_dn)) return NULL; - + req = talloc_zero(NULL, struct ldb_request); req->operation = LDB_DELETE; req->op.del.dn = PyLdbDn_AsDn(py_dn); - + ret = PyLdbModule_AsModule(self)->ops->del(PyLdbModule_AsModule(self), req); - PyErr_LDB_ERROR_IS_ERR_RAISE(ret, NULL); + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, NULL); Py_RETURN_NONE; } @@ -1214,16 +1460,16 @@ static PyObject *py_ldb_module_rename(PyLdbModuleObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "OO", &py_dn1, &py_dn2)) return NULL; - + req = talloc_zero(NULL, struct ldb_request); req->operation = LDB_RENAME; req->op.rename.olddn = PyLdbDn_AsDn(py_dn1); req->op.rename.newdn = PyLdbDn_AsDn(py_dn2); - + ret = PyLdbModule_AsModule(self)->ops->rename(PyLdbModule_AsModule(self), req); - PyErr_LDB_ERROR_IS_ERR_RAISE(ret, NULL); + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, NULL); Py_RETURN_NONE; } @@ -1256,6 +1502,21 @@ PyTypeObject PyLdbModule = { .tp_flags = Py_TPFLAGS_DEFAULT, }; + +/** + * Create a ldb_message_element from a Python object. + * + * This will accept any sequence objects that contains strings, or + * a string object. + * + * A reference to set_obj will be borrowed. + * + * @param mem_ctx Memory context + * @param set_obj Python object to convert + * @param flags ldb_message_element flags to set + * @param attr_name Name of the attribute + * @return New ldb_message_element, allocated as child of mem_ctx + */ struct ldb_message_element *PyObject_AsMessageElement(TALLOC_CTX *mem_ctx, PyObject *set_obj, int flags, const char *attr_name) @@ -1263,19 +1524,19 @@ struct ldb_message_element *PyObject_AsMessageElement(TALLOC_CTX *mem_ctx, struct ldb_message_element *me; if (PyLdbMessageElement_Check(set_obj)) - return PyLdbMessageElement_AsMessageElement(set_obj); + return talloc_reference(mem_ctx, + PyLdbMessageElement_AsMessageElement(set_obj)); me = talloc(mem_ctx, struct ldb_message_element); - me->name = attr_name; + me->name = talloc_strdup(me, attr_name); me->flags = flags; if (PyString_Check(set_obj)) { me->num_values = 1; me->values = talloc_array(me, struct ldb_val, me->num_values); me->values[0].length = PyString_Size(set_obj); - me->values[0].data = (uint8_t *)talloc_strndup(me->values, - PyString_AsString(set_obj), - me->values[0].length); + me->values[0].data = talloc_memdup(me, + (uint8_t *)PyString_AsString(set_obj), me->values[0].length); } else if (PySequence_Check(set_obj)) { int i; me->num_values = PySequence_Size(set_obj); @@ -1284,7 +1545,8 @@ struct ldb_message_element *PyObject_AsMessageElement(TALLOC_CTX *mem_ctx, PyObject *obj = PySequence_GetItem(set_obj, i); me->values[i].length = PyString_Size(obj); - me->values[i].data = (uint8_t *)PyString_AsString(obj); + me->values[i].data = talloc_memdup(me, + (uint8_t *)PyString_AsString(obj), me->values[i].length); } } else { talloc_free(me); @@ -1324,8 +1586,30 @@ static PyObject *py_ldb_msg_element_get(PyLdbMessageElementObject *self, PyObjec &(PyLdbMessageElement_AsMessageElement(self)->values[i])); } +static PyObject *py_ldb_msg_element_flags(PyLdbMessageElementObject *self, PyObject *args) +{ + struct ldb_message_element *el; + + el = PyLdbMessageElement_AsMessageElement(self); + return PyInt_FromLong(el->flags); +} + +static PyObject *py_ldb_msg_element_set_flags(PyLdbMessageElementObject *self, PyObject *args) +{ + int flags; + struct ldb_message_element *el; + if (!PyArg_ParseTuple(args, "i", &flags)) + return NULL; + + el = PyLdbMessageElement_AsMessageElement(self); + el->flags = flags; + Py_RETURN_NONE; +} + static PyMethodDef py_ldb_msg_element_methods[] = { { "get", (PyCFunction)py_ldb_msg_element_get, METH_VARARGS, NULL }, + { "set_flags", (PyCFunction)py_ldb_msg_element_set_flags, METH_VARARGS, NULL }, + { "flags", (PyCFunction)py_ldb_msg_element_flags, METH_NOARGS, NULL }, { NULL }, }; @@ -1385,33 +1669,49 @@ static PyObject *py_ldb_msg_element_new(PyTypeObject *type, PyObject *args, PyOb char *name = NULL; const char * const kwnames[] = { "elements", "flags", "name", NULL }; PyLdbMessageElementObject *ret; + TALLOC_CTX *mem_ctx; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Ois", discard_const_p(char *, kwnames), &py_elements, &flags, &name)) return NULL; - el = talloc_zero(NULL, struct ldb_message_element); + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } + + el = talloc_zero(mem_ctx, struct ldb_message_element); if (py_elements != NULL) { int i; if (PyString_Check(py_elements)) { el->num_values = 1; el->values = talloc_array(el, struct ldb_val, 1); - el->values[0].data = (uint8_t *)PyString_AsString(py_elements); el->values[0].length = PyString_Size(py_elements); + el->values[0].data = talloc_memdup(el, + (uint8_t *)PyString_AsString(py_elements), el->values[0].length); } else if (PySequence_Check(py_elements)) { el->num_values = PySequence_Size(py_elements); el->values = talloc_array(el, struct ldb_val, el->num_values); for (i = 0; i < el->num_values; i++) { PyObject *item = PySequence_GetItem(py_elements, i); - el->values[i].data = (uint8_t *)PyString_AsString(item); + if (!PyString_Check(item)) { + PyErr_Format(PyExc_TypeError, + "Expected string as element %d in list", + i); + talloc_free(mem_ctx); + return NULL; + } el->values[i].length = PyString_Size(item); + el->values[i].data = talloc_memdup(el, + (uint8_t *)PyString_AsString(item), el->values[i].length); } } else { PyErr_SetString(PyExc_TypeError, "Expected string or list"); - talloc_free(el); + talloc_free(mem_ctx); return NULL; } } @@ -1422,12 +1722,12 @@ static PyObject *py_ldb_msg_element_new(PyTypeObject *type, PyObject *args, PyOb ret = (PyLdbMessageElementObject *)PyLdbMessageElement.tp_alloc(&PyLdbMessageElement, 0); if (ret == NULL) { PyErr_NoMemory(); - talloc_free(el); + talloc_free(mem_ctx); return NULL; } - ret->mem_ctx = talloc_new(NULL); - ret->el = talloc_reference(ret->mem_ctx, el); + ret->mem_ctx = mem_ctx; + ret->el = el; return (PyObject *)ret; } @@ -1513,8 +1813,13 @@ static PyObject *py_ldb_msg_keys(PyLdbMessageObject *self) static PyObject *py_ldb_msg_getitem_helper(PyLdbMessageObject *self, PyObject *py_name) { struct ldb_message_element *el; - char *name = PyString_AsString(py_name); + char *name; struct ldb_message *msg = PyLdbMessage_AsMessage(self); + if (!PyString_Check(py_name)) { + PyErr_SetNone(PyExc_TypeError); + return NULL; + } + name = PyString_AsString(py_name); if (!strcmp(name, "dn")) return PyLdbDn_FromDn(msg->dn); el = ldb_msg_find_element(msg, name); @@ -1541,8 +1846,11 @@ static PyObject *py_ldb_msg_get(PyLdbMessageObject *self, PyObject *args) return NULL; ret = py_ldb_msg_getitem_helper(self, name); - if (ret == NULL) + if (ret == NULL) { + if (PyErr_Occurred()) + return NULL; Py_RETURN_NONE; + } return ret; } @@ -1582,15 +1890,22 @@ static PyObject *py_ldb_msg_iter(PyLdbMessageObject *self) static int py_ldb_msg_setitem(PyLdbMessageObject *self, PyObject *name, PyObject *value) { - char *attr_name = PyString_AsString(name); + char *attr_name; + + if (!PyString_Check(name)) { + PyErr_SetNone(PyExc_TypeError); + return -1; + } + + attr_name = PyString_AsString(name); if (value == NULL) { + /* delitem */ ldb_msg_remove_attr(self->msg, attr_name); } else { - struct ldb_message_element *el = PyObject_AsMessageElement(NULL, + struct ldb_message_element *el = PyObject_AsMessageElement(self->msg, value, 0, attr_name); if (el == NULL) return -1; - talloc_steal(self->msg, el); ldb_msg_remove_attr(PyLdbMessage_AsMessage(self), attr_name); ldb_msg_add(PyLdbMessage_AsMessage(self), el, el->flags); } @@ -1612,6 +1927,7 @@ static PyObject *py_ldb_msg_new(PyTypeObject *type, PyObject *args, PyObject *kw { const char * const kwnames[] = { "dn", NULL }; struct ldb_message *ret; + TALLOC_CTX *mem_ctx; PyObject *pydn = NULL; PyLdbMessageObject *py_ret; @@ -1620,24 +1936,37 @@ static PyObject *py_ldb_msg_new(PyTypeObject *type, PyObject *args, PyObject *kw &pydn)) return NULL; - ret = ldb_msg_new(NULL); + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } + + ret = ldb_msg_new(mem_ctx); if (ret == NULL) { + talloc_free(mem_ctx); PyErr_NoMemory(); return NULL; } - if (pydn != NULL) - if (!PyObject_AsDn(NULL, pydn, NULL, &ret->dn)) + if (pydn != NULL) { + struct ldb_dn *dn; + if (!PyObject_AsDn(NULL, pydn, NULL, &dn)) { + talloc_free(mem_ctx); return NULL; + } + ret->dn = talloc_reference(ret, dn); + } py_ret = (PyLdbMessageObject *)type->tp_alloc(type, 0); if (py_ret == NULL) { PyErr_NoMemory(); + talloc_free(mem_ctx); return NULL; } - py_ret->mem_ctx = talloc_new(NULL); - py_ret->msg = talloc_reference(py_ret->mem_ctx, ret); + py_ret->mem_ctx = mem_ctx; + py_ret->msg = ret; return (PyObject *)py_ret; } @@ -1657,12 +1986,19 @@ PyObject *PyLdbMessage_FromMessage(struct ldb_message *msg) static PyObject *py_ldb_msg_get_dn(PyLdbMessageObject *self, void *closure) { - return PyLdbDn_FromDn(PyLdbMessage_AsMessage(self)->dn); + struct ldb_message *msg = PyLdbMessage_AsMessage(self); + return PyLdbDn_FromDn(msg->dn); } static int py_ldb_msg_set_dn(PyLdbMessageObject *self, PyObject *value, void *closure) { - PyLdbMessage_AsMessage(self)->dn = PyLdbDn_AsDn(value); + struct ldb_message *msg = PyLdbMessage_AsMessage(self); + if (!PyLdbDn_Check(value)) { + PyErr_SetNone(PyExc_TypeError); + return -1; + } + + msg->dn = talloc_reference(msg, PyLdbDn_AsDn(value)); return 0; } @@ -1709,7 +2045,7 @@ PyObject *PyLdbTree_FromTree(struct ldb_parse_tree *tree) PyErr_NoMemory(); return NULL; } - + ret->mem_ctx = talloc_new(NULL); ret->tree = talloc_reference(ret->mem_ctx, tree); return (PyObject *)ret; @@ -2023,7 +2359,7 @@ static PyObject *py_register_module(PyObject *module, PyObject *args) ret = ldb_register_module(ops); - PyErr_LDB_ERROR_IS_ERR_RAISE(ret, NULL); + PyErr_LDB_ERROR_IS_ERR_RAISE(PyExc_LdbError, ret, NULL); Py_RETURN_NONE; } @@ -2113,6 +2449,10 @@ void initldb(void) PyModule_AddObject(m, "CHANGETYPE_DELETE", PyInt_FromLong(LDB_CHANGETYPE_DELETE)); PyModule_AddObject(m, "CHANGETYPE_MODIFY", PyInt_FromLong(LDB_CHANGETYPE_MODIFY)); + PyModule_AddObject(m, "FLAG_MOD_ADD", PyInt_FromLong(LDB_FLAG_MOD_ADD)); + PyModule_AddObject(m, "FLAG_MOD_REPLACE", PyInt_FromLong(LDB_FLAG_MOD_REPLACE)); + PyModule_AddObject(m, "FLAG_MOD_DELETE", PyInt_FromLong(LDB_FLAG_MOD_DELETE)); + PyModule_AddObject(m, "SUCCESS", PyInt_FromLong(LDB_SUCCESS)); PyModule_AddObject(m, "ERR_OPERATIONS_ERROR", PyInt_FromLong(LDB_ERR_OPERATIONS_ERROR)); PyModule_AddObject(m, "ERR_PROTOCOL_ERROR", PyInt_FromLong(LDB_ERR_PROTOCOL_ERROR)); @@ -2151,9 +2491,14 @@ void initldb(void) PyModule_AddObject(m, "ERR_ENTRY_ALREADY_EXISTS", PyInt_FromLong(LDB_ERR_ENTRY_ALREADY_EXISTS)); PyModule_AddObject(m, "ERR_OBJECT_CLASS_MODS_PROHIBITED", PyInt_FromLong(LDB_ERR_OBJECT_CLASS_MODS_PROHIBITED)); PyModule_AddObject(m, "ERR_AFFECTS_MULTIPLE_DSAS", PyInt_FromLong(LDB_ERR_AFFECTS_MULTIPLE_DSAS)); - PyModule_AddObject(m, "ERR_OTHER", PyInt_FromLong(LDB_ERR_OTHER)); + PyModule_AddObject(m, "FLG_RDONLY", PyInt_FromLong(LDB_FLG_RDONLY)); + PyModule_AddObject(m, "FLG_NOSYNC", PyInt_FromLong(LDB_FLG_NOSYNC)); + PyModule_AddObject(m, "FLG_RECONNECT", PyInt_FromLong(LDB_FLG_RECONNECT)); + PyModule_AddObject(m, "FLG_NOMMAP", PyInt_FromLong(LDB_FLG_NOMMAP)); + + PyModule_AddObject(m, "__docformat__", PyString_FromString("restructuredText")); PyExc_LdbError = PyErr_NewException(discard_const_p(char, "_ldb.LdbError"), NULL, NULL); diff --git a/source4/lib/ldb/pyldb.h b/source4/lib/ldb/pyldb.h index 731911a387..a0954158bd 100644 --- a/source4/lib/ldb/pyldb.h +++ b/source4/lib/ldb/pyldb.h @@ -27,6 +27,7 @@ #define _PYLDB_H_ #include <Python.h> +#include <talloc.h> typedef struct { PyObject_HEAD @@ -34,7 +35,6 @@ typedef struct { TALLOC_CTX *mem_ctx; } PyLdbObject; -PyAPI_DATA(PyTypeObject) PyLdb; PyObject *PyLdb_FromLdbContext(struct ldb_context *ldb_ctx); #define PyLdb_AsLdbContext(pyobj) ((PyLdbObject *)pyobj)->ldb_ctx #define PyLdb_Check(ob) PyObject_TypeCheck(ob, &PyLdb) @@ -45,7 +45,6 @@ typedef struct { TALLOC_CTX *mem_ctx; } PyLdbDnObject; -PyAPI_DATA(PyTypeObject) PyLdbDn; PyObject *PyLdbDn_FromDn(struct ldb_dn *); bool PyObject_AsDn(TALLOC_CTX *mem_ctx, PyObject *object, struct ldb_context *ldb_ctx, struct ldb_dn **dn); #define PyLdbDn_AsDn(pyobj) ((PyLdbDnObject *)pyobj)->dn @@ -56,8 +55,6 @@ typedef struct { struct ldb_message *msg; TALLOC_CTX *mem_ctx; } PyLdbMessageObject; -PyAPI_DATA(PyTypeObject) PyLdbMessage; -PyObject *PyLdbMessage_FromMessage(struct ldb_message *message); #define PyLdbMessage_Check(ob) PyObject_TypeCheck(ob, &PyLdbMessage) #define PyLdbMessage_AsMessage(pyobj) ((PyLdbMessageObject *)pyobj)->msg @@ -66,7 +63,7 @@ typedef struct { struct ldb_module *mod; TALLOC_CTX *mem_ctx; } PyLdbModuleObject; -PyAPI_DATA(PyTypeObject) PyLdbModule; +PyObject *PyLdbMessage_FromMessage(struct ldb_message *message); PyObject *PyLdbModule_FromModule(struct ldb_module *mod); #define PyLdbModule_AsModule(pyobj) ((PyLdbModuleObject *)pyobj)->mod @@ -75,7 +72,6 @@ typedef struct { struct ldb_message_element *el; TALLOC_CTX *mem_ctx; } PyLdbMessageElementObject; -PyAPI_DATA(PyTypeObject) PyLdbMessageElement; struct ldb_message_element *PyObject_AsMessageElement(TALLOC_CTX *mem_ctx, PyObject *obj, int flags, const char *name); PyObject *PyLdbMessageElement_FromMessageElement(struct ldb_message_element *, TALLOC_CTX *mem_ctx); #define PyLdbMessageElement_AsMessageElement(pyobj) ((PyLdbMessageElementObject *)pyobj)->el @@ -86,16 +82,17 @@ typedef struct { struct ldb_parse_tree *tree; TALLOC_CTX *mem_ctx; } PyLdbTreeObject; -PyAPI_DATA(PyTypeObject) PyLdbTree; PyObject *PyLdbTree_FromTree(struct ldb_parse_tree *); #define PyLdbTree_AsTree(pyobj) ((PyLdbTreeObject *)pyobj)->tree -void PyErr_SetLdbError(int ret, struct ldb_context *ldb_ctx); -#define PyErr_LDB_ERROR_IS_ERR_RAISE(ret,ldb) \ +#define PyErr_LDB_ERROR_IS_ERR_RAISE(err,ret,ldb) \ if (ret != LDB_SUCCESS) { \ - PyErr_SetLdbError(ret, ldb); \ + PyErr_SetLdbError(err, ret, ldb); \ return NULL; \ } +/* Picked out of thin air. To do this properly, we should probably have some part of the + * errors in LDB be allocated to bindings ? */ +#define LDB_ERR_PYTHON_EXCEPTION 142 #endif /* _PYLDB_H_ */ diff --git a/source4/lib/ldb/python.mk b/source4/lib/ldb/python.mk index 6cc6d2e90e..dbc2eb27eb 100644 --- a/source4/lib/ldb/python.mk +++ b/source4/lib/ldb/python.mk @@ -2,5 +2,5 @@ LIBRARY_REALNAME = ldb.$(SHLIBEXT) PUBLIC_DEPENDENCIES = LIBLDB PYTALLOC -pyldb_OBJ_FILES = $(ldbsrcdir)/pyldb.o +pyldb_OBJ_FILES = $(ldbsrcdir)/pyldb.o $(pyldb_OBJ_FILES): CFLAGS+=-I$(ldbsrcdir)/include diff --git a/source4/lib/ldb/rules.mk b/source4/lib/ldb/rules.mk index 639271b76d..0598f8039b 100644 --- a/source4/lib/ldb/rules.mk +++ b/source4/lib/ldb/rules.mk @@ -19,6 +19,7 @@ ctags: showflags:: @echo 'ldb will be compiled with flags:' @echo ' CFLAGS = $(CFLAGS)' + @echo ' SHLD_FLAGS = $(SHLD_FLAGS)' @echo ' LIBS = $(LIBS)' distclean:: diff --git a/source4/lib/ldb/tests/python/api.py b/source4/lib/ldb/tests/python/api.py index c372b8fa71..956908d7d6 100755 --- a/source4/lib/ldb/tests/python/api.py +++ b/source4/lib/ldb/tests/python/api.py @@ -14,6 +14,7 @@ def filename(): return os.tempnam() class NoContextTests(unittest.TestCase): + def test_valid_attr_name(self): self.assertTrue(ldb.valid_attr_name("foo")) self.assertFalse(ldb.valid_attr_name("24foo")) @@ -28,6 +29,7 @@ class NoContextTests(unittest.TestCase): class SimpleLdb(unittest.TestCase): + def test_connect(self): ldb.Ldb(filename()) @@ -86,23 +88,23 @@ class SimpleLdb(unittest.TestCase): def test_search_scope_base(self): l = ldb.Ldb(filename()) - self.assertEquals(len(l.search(ldb.Dn(l, "dc=foo"), + self.assertEquals(len(l.search(ldb.Dn(l, "dc=foo1"), ldb.SCOPE_ONELEVEL)), 0) def test_delete(self): l = ldb.Ldb(filename()) - self.assertRaises(ldb.LdbError, lambda: l.delete(ldb.Dn(l, "dc=foo"))) + self.assertRaises(ldb.LdbError, lambda: l.delete(ldb.Dn(l, "dc=foo2"))) def test_contains(self): l = ldb.Ldb(filename()) - self.assertFalse(ldb.Dn(l, "dc=foo") in l) + self.assertFalse(ldb.Dn(l, "dc=foo3") in l) l = ldb.Ldb(filename()) m = ldb.Message() - m.dn = ldb.Dn(l, "dc=foo") + m.dn = ldb.Dn(l, "dc=foo3") m["b"] = ["a"] l.add(m) try: - self.assertTrue(ldb.Dn(l, "dc=foo") in l) + self.assertTrue(ldb.Dn(l, "dc=foo3") in l) finally: l.delete(m.dn) @@ -125,45 +127,53 @@ class SimpleLdb(unittest.TestCase): def test_add(self): l = ldb.Ldb(filename()) m = ldb.Message() - m.dn = ldb.Dn(l, "dc=foo") + m.dn = ldb.Dn(l, "dc=foo4") m["bla"] = "bla" self.assertEquals(len(l.search()), 1) l.add(m) try: self.assertEquals(len(l.search()), 2) finally: - l.delete(ldb.Dn(l, "dc=foo")) + l.delete(ldb.Dn(l, "dc=foo4")) + + def test_add_w_unhandled_ctrl(self): + l = ldb.Ldb(filename()) + m = ldb.Message() + m.dn = ldb.Dn(l, "dc=foo4") + m["bla"] = "bla" + self.assertEquals(len(l.search()), 1) + self.assertRaises(ldb.LdbError, lambda: l.add(m,["search_options:1:2"])) def test_add_dict(self): l = ldb.Ldb(filename()) - m = {"dn": ldb.Dn(l, "dc=foo"), + m = {"dn": ldb.Dn(l, "dc=foo5"), "bla": "bla"} self.assertEquals(len(l.search()), 1) l.add(m) try: self.assertEquals(len(l.search()), 2) finally: - l.delete(ldb.Dn(l, "dc=foo")) + l.delete(ldb.Dn(l, "dc=foo5")) def test_add_dict_string_dn(self): l = ldb.Ldb(filename()) - m = {"dn": "dc=foo", "bla": "bla"} + m = {"dn": "dc=foo6", "bla": "bla"} self.assertEquals(len(l.search()), 1) l.add(m) try: self.assertEquals(len(l.search()), 2) finally: - l.delete(ldb.Dn(l, "dc=foo")) + l.delete(ldb.Dn(l, "dc=foo6")) def test_rename(self): l = ldb.Ldb(filename()) m = ldb.Message() - m.dn = ldb.Dn(l, "dc=foo") + m.dn = ldb.Dn(l, "dc=foo7") m["bla"] = "bla" self.assertEquals(len(l.search()), 1) l.add(m) try: - l.rename(ldb.Dn(l, "dc=foo"), ldb.Dn(l, "dc=bar")) + l.rename(ldb.Dn(l, "dc=foo7"), ldb.Dn(l, "dc=bar")) self.assertEquals(len(l.search()), 2) finally: l.delete(ldb.Dn(l, "dc=bar")) @@ -171,13 +181,13 @@ class SimpleLdb(unittest.TestCase): def test_rename_string_dns(self): l = ldb.Ldb(filename()) m = ldb.Message() - m.dn = ldb.Dn(l, "dc=foo") + m.dn = ldb.Dn(l, "dc=foo8") m["bla"] = "bla" self.assertEquals(len(l.search()), 1) l.add(m) self.assertEquals(len(l.search()), 2) try: - l.rename("dc=foo", "dc=bar") + l.rename("dc=foo8", "dc=bar") self.assertEquals(len(l.search()), 2) finally: l.delete(ldb.Dn(l, "dc=bar")) @@ -193,10 +203,13 @@ class SimpleLdb(unittest.TestCase): try: m = ldb.Message() m.dn = ldb.Dn(l, "dc=modifydelete") - m["bla"] = ldb.MessageElement([], ldb.CHANGETYPE_DELETE, "bla") + m["bla"] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE, "bla") + self.assertEquals(ldb.FLAG_MOD_DELETE, m["bla"].flags()) l.modify(m) rm = l.search(m.dn)[0] self.assertEquals(1, len(rm)) + rm = l.search(m.dn, attrs=["bla"])[0] + self.assertEquals(0, len(rm)) finally: l.delete(ldb.Dn(l, "dc=modifydelete")) @@ -209,7 +222,8 @@ class SimpleLdb(unittest.TestCase): try: m = ldb.Message() m.dn = ldb.Dn(l, "dc=add") - m["bla"] = ldb.MessageElement(["456"], ldb.CHANGETYPE_ADD, "bla") + m["bla"] = ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla") + self.assertEquals(ldb.FLAG_MOD_ADD, m["bla"].flags()) l.modify(m) rm = l.search(m.dn)[0] self.assertEquals(2, len(rm)) @@ -217,7 +231,7 @@ class SimpleLdb(unittest.TestCase): finally: l.delete(ldb.Dn(l, "dc=add")) - def test_modify_modify(self): + def test_modify_replace(self): l = ldb.Ldb(filename()) m = ldb.Message() m.dn = ldb.Dn(l, "dc=modify2") @@ -226,18 +240,48 @@ class SimpleLdb(unittest.TestCase): try: m = ldb.Message() m.dn = ldb.Dn(l, "dc=modify2") - m["bla"] = ldb.MessageElement(["456"], ldb.CHANGETYPE_MODIFY, "bla") + m["bla"] = ldb.MessageElement(["789"], ldb.FLAG_MOD_REPLACE, "bla") + self.assertEquals(ldb.FLAG_MOD_REPLACE, m["bla"].flags()) l.modify(m) rm = l.search(m.dn)[0] self.assertEquals(2, len(rm)) - self.assertEquals(["1234"], list(rm["bla"])) + self.assertEquals(["789"], list(rm["bla"])) + rm = l.search(m.dn, attrs=["bla"])[0] + self.assertEquals(1, len(rm)) finally: l.delete(ldb.Dn(l, "dc=modify2")) + def test_modify_flags_change(self): + l = ldb.Ldb(filename()) + m = ldb.Message() + m.dn = ldb.Dn(l, "dc=add") + m["bla"] = ["1234"] + l.add(m) + try: + m = ldb.Message() + m.dn = ldb.Dn(l, "dc=add") + m["bla"] = ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla") + self.assertEquals(ldb.FLAG_MOD_ADD, m["bla"].flags()) + l.modify(m) + rm = l.search(m.dn)[0] + self.assertEquals(2, len(rm)) + self.assertEquals(["1234", "456"], list(rm["bla"])) + + #Now create another modify, but switch the flags before we do it + m["bla"] = ldb.MessageElement(["456"], ldb.FLAG_MOD_ADD, "bla") + m["bla"].set_flags(ldb.FLAG_MOD_DELETE) + l.modify(m) + rm = l.search(m.dn, attrs=["bla"])[0] + self.assertEquals(1, len(rm)) + self.assertEquals(["1234"], list(rm["bla"])) + + finally: + l.delete(ldb.Dn(l, "dc=add")) + def test_transaction_commit(self): l = ldb.Ldb(filename()) l.transaction_start() - m = ldb.Message(ldb.Dn(l, "dc=foo")) + m = ldb.Message(ldb.Dn(l, "dc=foo9")) m["foo"] = ["bar"] l.add(m) l.transaction_commit() @@ -246,11 +290,11 @@ class SimpleLdb(unittest.TestCase): def test_transaction_cancel(self): l = ldb.Ldb(filename()) l.transaction_start() - m = ldb.Message(ldb.Dn(l, "dc=foo")) + m = ldb.Message(ldb.Dn(l, "dc=foo10")) m["foo"] = ["bar"] l.add(m) l.transaction_cancel() - self.assertEquals(0, len(l.search(ldb.Dn(l, "dc=foo")))) + self.assertEquals(0, len(l.search(ldb.Dn(l, "dc=foo10")))) def test_set_debug(self): def my_report_fn(level, text): @@ -258,45 +302,69 @@ class SimpleLdb(unittest.TestCase): l = ldb.Ldb(filename()) l.set_debug(my_report_fn) + def test_zero_byte_string(self): + """Testing we do not get trapped in the \0 byte in a property string.""" + l = ldb.Ldb(filename()) + l.add({ + "dn" : "dc=somedn", + "objectclass" : "user", + "cN" : "LDAPtestUSER", + "givenname" : "ldap", + "displayname" : "foo\0bar", + }) + res = l.search(expression="(dn=dc=somedn)") + self.assertEquals("foo\0bar", res[0]["displayname"][0]) + class DnTests(unittest.TestCase): + def setUp(self): self.ldb = ldb.Ldb(filename()) + def test_set_dn_invalid(self): + x = ldb.Message() + def assign(): + x.dn = "astring" + self.assertRaises(TypeError, assign) + def test_eq(self): - x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") - y = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + x = ldb.Dn(self.ldb, "dc=foo11,bar=bloe") + y = ldb.Dn(self.ldb, "dc=foo11,bar=bloe") self.assertEquals(x, y) def test_str(self): - x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") - self.assertEquals(x.__str__(), "dc=foo,bar=bloe") + x = ldb.Dn(self.ldb, "dc=foo12,bar=bloe") + self.assertEquals(x.__str__(), "dc=foo12,bar=bloe") def test_repr(self): - x = ldb.Dn(self.ldb, "dc=foo,bla=blie") - self.assertEquals(x.__repr__(), "Dn('dc=foo,bla=blie')") + x = ldb.Dn(self.ldb, "dc=foo13,bla=blie") + self.assertEquals(x.__repr__(), "Dn('dc=foo13,bla=blie')") def test_get_casefold(self): - x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") - self.assertEquals(x.get_casefold(), "DC=FOO,BAR=bloe") + x = ldb.Dn(self.ldb, "dc=foo14,bar=bloe") + self.assertEquals(x.get_casefold(), "DC=FOO14,BAR=bloe") def test_validate(self): - x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + x = ldb.Dn(self.ldb, "dc=foo15,bar=bloe") self.assertTrue(x.validate()) def test_parent(self): - x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + x = ldb.Dn(self.ldb, "dc=foo16,bar=bloe") self.assertEquals("bar=bloe", x.parent().__str__()) + def test_parent_nonexistant(self): + x = ldb.Dn(self.ldb, "@BLA") + self.assertEquals(None, x.parent()) + def test_compare(self): - x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") - y = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + x = ldb.Dn(self.ldb, "dc=foo17,bar=bloe") + y = ldb.Dn(self.ldb, "dc=foo17,bar=bloe") self.assertEquals(x, y) - z = ldb.Dn(self.ldb, "dc=foo,bar=blie") + z = ldb.Dn(self.ldb, "dc=foo17,bar=blie") self.assertNotEquals(z, y) def test_is_valid(self): - x = ldb.Dn(self.ldb, "dc=foo,dc=bloe") + x = ldb.Dn(self.ldb, "dc=foo18,dc=bloe") self.assertTrue(x.is_valid()) x = ldb.Dn(self.ldb, "") # is_valid()'s return values appears to be a side effect of @@ -304,45 +372,47 @@ class DnTests(unittest.TestCase): # self.assertFalse(x.is_valid()) def test_is_special(self): - x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + x = ldb.Dn(self.ldb, "dc=foo19,bar=bloe") self.assertFalse(x.is_special()) x = ldb.Dn(self.ldb, "@FOOBAR") self.assertTrue(x.is_special()) def test_check_special(self): - x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + x = ldb.Dn(self.ldb, "dc=foo20,bar=bloe") self.assertFalse(x.check_special("FOOBAR")) x = ldb.Dn(self.ldb, "@FOOBAR") self.assertTrue(x.check_special("@FOOBAR")) def test_len(self): - x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + x = ldb.Dn(self.ldb, "dc=foo21,bar=bloe") self.assertEquals(2, len(x)) - x = ldb.Dn(self.ldb, "dc=foo") + x = ldb.Dn(self.ldb, "dc=foo21") self.assertEquals(1, len(x)) def test_add_child(self): - x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + x = ldb.Dn(self.ldb, "dc=foo22,bar=bloe") self.assertTrue(x.add_child(ldb.Dn(self.ldb, "bla=bloe"))) - self.assertEquals("bla=bloe,dc=foo,bar=bloe", x.__str__()) + self.assertEquals("bla=bloe,dc=foo22,bar=bloe", x.__str__()) def test_add_base(self): - x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") + x = ldb.Dn(self.ldb, "dc=foo23,bar=bloe") base = ldb.Dn(self.ldb, "bla=bloe") self.assertTrue(x.add_base(base)) - self.assertEquals("dc=foo,bar=bloe,bla=bloe", x.__str__()) + self.assertEquals("dc=foo23,bar=bloe,bla=bloe", x.__str__()) def test_add(self): - x = ldb.Dn(self.ldb, "dc=foo") + x = ldb.Dn(self.ldb, "dc=foo24") y = ldb.Dn(self.ldb, "bar=bla") - self.assertEquals("dc=foo,bar=bla", str(y + x)) + self.assertEquals("dc=foo24,bar=bla", str(y + x)) def test_parse_ldif(self): msgs = self.ldb.parse_ldif("dn: foo=bar\n") msg = msgs.next() self.assertEquals("foo=bar", str(msg[1].dn)) self.assertTrue(isinstance(msg[1], ldb.Message)) - + ldif = self.ldb.write_ldif(msg[1], ldb.CHANGETYPE_NONE) + self.assertEquals("dn: foo=bar\n\n", ldif) + def test_parse_ldif_more(self): msgs = self.ldb.parse_ldif("dn: foo=bar\n\n\ndn: bar=bar") msg = msgs.next() @@ -351,31 +421,32 @@ class DnTests(unittest.TestCase): self.assertEquals("bar=bar", str(msg[1].dn)) def test_canonical_string(self): - x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") - self.assertEquals("/bloe/foo", x.canonical_str()) + x = ldb.Dn(self.ldb, "dc=foo25,bar=bloe") + self.assertEquals("/bloe/foo25", x.canonical_str()) def test_canonical_ex_string(self): - x = ldb.Dn(self.ldb, "dc=foo,bar=bloe") - self.assertEquals("/bloe\nfoo", x.canonical_ex_str()) + x = ldb.Dn(self.ldb, "dc=foo26,bar=bloe") + self.assertEquals("/bloe\nfoo26", x.canonical_ex_str()) class LdbMsgTests(unittest.TestCase): + def setUp(self): self.msg = ldb.Message() def test_init_dn(self): - self.msg = ldb.Message(ldb.Dn(ldb.Ldb(), "dc=foo")) - self.assertEquals("dc=foo", str(self.msg.dn)) + self.msg = ldb.Message(ldb.Dn(ldb.Ldb(), "dc=foo27")) + self.assertEquals("dc=foo27", str(self.msg.dn)) def test_iter_items(self): self.assertEquals(0, len(self.msg.items())) - self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "dc=foo") + self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "dc=foo28") self.assertEquals(1, len(self.msg.items())) def test_repr(self): - self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "dc=foo") + self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "dc=foo29") self.msg["dc"] = "foo" - self.assertEquals("Message({'dn': Dn('dc=foo'), 'dc': MessageElement(['foo'])})", repr(self.msg)) + self.assertEquals("Message({'dn': Dn('dc=foo29'), 'dc': MessageElement(['foo'])})", repr(self.msg)) def test_len(self): self.assertEquals(0, len(self.msg)) @@ -417,6 +488,10 @@ class LdbMsgTests(unittest.TestCase): self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "@BASEINFO") self.assertEquals("@BASEINFO", self.msg.get("dn").__str__()) + def test_get_invalid(self): + self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "@BASEINFO") + self.assertRaises(TypeError, self.msg.get, 42) + def test_get_other(self): self.msg["foo"] = ["bar"] self.assertEquals("bar", self.msg.get("foo")[0]) @@ -424,8 +499,20 @@ class LdbMsgTests(unittest.TestCase): def test_get_unknown(self): self.assertEquals(None, self.msg.get("lalalala")) + def test_msg_diff(self): + l = ldb.Ldb() + msgs = l.parse_ldif("dn: foo=bar\nfoo: bar\nbaz: do\n\ndn: foo=bar\nfoo: bar\nbaz: dont\n") + msg1 = msgs.next()[1] + msg2 = msgs.next()[1] + msgdiff = l.msg_diff(msg1, msg2) + self.assertEquals("foo=bar", msgdiff.get("dn").__str__()) + self.assertRaises(KeyError, lambda: msgdiff["foo"]) + self.assertEquals(1, len(msgdiff)) + + class MessageElementTests(unittest.TestCase): + def test_cmp_element(self): x = ldb.MessageElement(["foo"]) y = ldb.MessageElement(["foo"]) @@ -466,6 +553,7 @@ class MessageElementTests(unittest.TestCase): class ModuleTests(unittest.TestCase): + def test_register_module(self): class ExampleModule: name = "example" @@ -492,6 +580,7 @@ class ModuleTests(unittest.TestCase): l = ldb.Ldb("usemodule.ldb") self.assertEquals(["init"], ops) + if __name__ == '__main__': import unittest unittest.TestProgram() diff --git a/source4/lib/ldb/tests/python/ldap.py b/source4/lib/ldb/tests/python/ldap.py index a30273fc66..5b2d380f11 100755 --- a/source4/lib/ldb/tests/python/ldap.py +++ b/source4/lib/ldb/tests/python/ldap.py @@ -6,6 +6,8 @@ import getopt import optparse import sys import time +import random +import base64 sys.path.append("bin/python") sys.path.append("../lib/subunit/python") @@ -17,11 +19,14 @@ from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError from ldb import ERR_NO_SUCH_OBJECT, ERR_ATTRIBUTE_OR_VALUE_EXISTS from ldb import ERR_ENTRY_ALREADY_EXISTS, ERR_UNWILLING_TO_PERFORM from ldb import ERR_NOT_ALLOWED_ON_NON_LEAF, ERR_OTHER, ERR_INVALID_DN_SYNTAX -from samba import Ldb +from ldb import Message, MessageElement, Dn, FLAG_MOD_REPLACE +from samba import Ldb, param, dom_sid_to_rid from subunit import SubunitTestRunner -from samba import param import unittest +from samba.ndr import ndr_pack, ndr_unpack +from samba.dcerpc import security + parser = optparse.OptionParser("ldap [options] <host>") sambaopts = options.SambaOptions(parser) parser.add_option_group(sambaopts) @@ -44,11 +49,11 @@ class BasicTests(unittest.TestCase): def delete_force(self, ldb, dn): try: ldb.delete(dn) - except LdbError, (num, _): + except LdbError, (num, _): self.assertEquals(num, ERR_NO_SUCH_OBJECT) def find_basedn(self, ldb): - res = ldb.search(base="", expression="", scope=SCOPE_BASE, + res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["defaultNamingContext"]) self.assertEquals(len(res), 1) return res[0]["defaultNamingContext"][0] @@ -63,12 +68,17 @@ class BasicTests(unittest.TestCase): self.assertEquals(len(res), 1) return res[0]["schemaNamingContext"][0] + def find_domain_sid(self): + res = self.ldb.search(base=self.base_dn, expression="(objectClass=*)", scope=SCOPE_BASE) + return ndr_unpack( security.dom_sid,res[0]["objectSid"][0]) + def setUp(self): self.ldb = ldb self.gc_ldb = gc_ldb self.base_dn = self.find_basedn(ldb) self.configuration_dn = self.find_configurationdn(ldb) self.schema_dn = self.find_schemadn(ldb) + self.domain_sid = self.find_domain_sid() print "baseDN: %s\n" % self.base_dn @@ -78,7 +88,10 @@ class BasicTests(unittest.TestCase): self.delete_force(self.ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn) self.delete_force(self.ldb, "cn=ldaptestutf8user èùéìòà ,cn=users," + self.base_dn) self.delete_force(self.ldb, "cn=ldaptestutf8user2 èùéìòà ,cn=users," + self.base_dn) - + self.delete_force(self.ldb, "cn=parentguidtest,cn=users," + self.base_dn) + self.delete_force(self.ldb, "cn=parentguidtest,cn=testotherusers," + self.base_dn) + self.delete_force(self.ldb, "cn=testotherusers," + self.base_dn) + def test_group_add_invalid_member(self): """Testing group add with invalid member""" try: @@ -87,21 +100,235 @@ class BasicTests(unittest.TestCase): "objectclass": "group", "member": "cn=ldaptestuser,cn=useRs," + self.base_dn}) self.fail() - except LdbError, (num, _): + except LdbError, (num, _): self.assertEquals(num, ERR_NO_SUCH_OBJECT) + def test_parentGUID(self): + """Test parentGUID behaviour""" + print "Testing parentGUID behaviour\n" + + self.ldb.add({ + "dn": "cn=parentguidtest,cn=users," + self.base_dn, + "objectclass":"user", + "samaccountname":"parentguidtest"}); + res1 = ldb.search(base="cn=parentguidtest,cn=users," + self.base_dn, scope=SCOPE_BASE, + attrs=["parentGUID"]); + res2 = ldb.search(base="cn=users," + self.base_dn,scope=SCOPE_BASE, + attrs=["objectGUID"]); + self.assertEquals(res1[0]["parentGUID"], res2[0]["objectGUID"]); + + """Test parentGUID behaviour""" + print "Testing parentGUID behaviour on rename\n" + + self.ldb.add({ + "dn": "cn=testotherusers," + self.base_dn, + "objectclass":"container"}); + res1 = ldb.search(base="cn=testotherusers," + self.base_dn,scope=SCOPE_BASE, + attrs=["objectGUID"]); + ldb.rename("cn=parentguidtest,cn=users," + self.base_dn, + "cn=parentguidtest,cn=testotherusers," + self.base_dn); + res2 = ldb.search(base="cn=parentguidtest,cn=testotherusers," + self.base_dn, + scope=SCOPE_BASE, + attrs=["parentGUID"]); + self.assertEquals(res1[0]["objectGUID"], res2[0]["parentGUID"]); + ldb.delete("cn=parentguidtest,cn=testotherusers," + self.base_dn) + ldb.delete("cn=testotherusers," + self.base_dn) + + def test_groupType(self): + """Test groupType behaviour (should appear to be casted to a 32 bit signed integer before comparsion)""" + print "Testing groupType behaviour\n" + + res1 = ldb.search(base=self.base_dn, scope=SCOPE_SUBTREE, + attrs=["groupType"], expression="groupType=2147483653"); + + res2 = ldb.search(base=self.base_dn, scope=SCOPE_SUBTREE, + attrs=["groupType"], expression="groupType=-2147483643"); + + self.assertEquals(len(res1), len(res2)) + + self.assertTrue(res1.count > 0) + + self.assertEquals(res1[0]["groupType"][0], "-2147483643") + + def test_primary_group(self): + """This tests the primary group behaviour (setting, changing) of a user account""" + print "Testing primary group behaviour\n" + + ldb.add({ + "dn": "cn=ldaptestuser,cn=users," + self.base_dn, + "objectclass": ["user", "person"]}) + + ldb.add({ + "dn": "cn=ldaptestgroup,cn=users," + self.base_dn, + "objectclass": "group"}) + + ldb.add({ + "dn": "cn=ldaptestgroup2,cn=users," + self.base_dn, + "objectclass": "group"}) + + res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn, + scope=SCOPE_BASE, attrs=["objectSID"]) + self.assertTrue(len(res1) == 1) + group_rid_1 = dom_sid_to_rid(ldb.schema_format_value("objectSID", + res1[0]["objectSID"][0])) + + res1 = ldb.search("cn=ldaptestgroup2,cn=users," + self.base_dn, + scope=SCOPE_BASE, attrs=["objectSID"]) + self.assertTrue(len(res1) == 1) + group_rid_2 = dom_sid_to_rid(ldb.schema_format_value("objectSID", + res1[0]["objectSID"][0])) + + # Try to add invalid primary group + m = Message() + m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn) + m["primaryGroupID"] = MessageElement("0", FLAG_MOD_REPLACE, + "primaryGroupID") + try: + ldb.modify(m) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) + + # Try to make group 1 primary - should be denied since it is not yet + # secondary + m = Message() + m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn) + m["primaryGroupID"] = MessageElement(str(group_rid_1), + FLAG_MOD_REPLACE, "primaryGroupID") + try: + ldb.modify(m) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) + + # Make group 1 secondary + m = Message() + m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) + m["member"] = "cn=ldaptestuser,cn=users," + self.base_dn + ldb.modify(m) + + # Make group 1 primary + m = Message() + m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn) + m["primaryGroupID"] = MessageElement(str(group_rid_1), + FLAG_MOD_REPLACE, "primaryGroupID") + ldb.modify(m) + + # Try to delete group 1 - should be denied + try: + ldb.delete("cn=ldaptestgroup,cn=users," + self.base_dn) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS) + + # Try to add group 1 also as secondary - should be denied + m = Message() + m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) + m["member"] = "cn=ldaptestuser,cn=users," + self.base_dn + try: + ldb.modify(m) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS) + + # Make group 2 secondary + m = Message() + m.dn = Dn(ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn) + m["member"] = "cn=ldaptestuser,cn=users," + self.base_dn + ldb.modify(m) + + # Swap the groups + m = Message() + m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn) + m["primaryGroupID"] = MessageElement(str(group_rid_2), + FLAG_MOD_REPLACE, "primaryGroupID") + ldb.modify(m) + + # Old primary group should contain a "member" attribute for the user, + # the new shouldn't contain anymore one + res1 = ldb.search("cn=ldaptestgroup, cn=users," + self.base_dn, + scope=SCOPE_BASE, attrs=["member"]) + self.assertTrue(len(res1) == 1) + self.assertTrue(len(res1[0]["member"]) == 1) + self.assertEquals(res1[0]["member"][0].lower(), + ("cn=ldaptestuser,cn=users," + self.base_dn).lower()) + + res1 = ldb.search("cn=ldaptestgroup2, cn=users," + self.base_dn, + scope=SCOPE_BASE, attrs=["member"]) + self.assertTrue(len(res1) == 1) + self.assertFalse("member" in res1[0]) + + # Also this should be denied + try: + ldb.add({ + "dn": "cn=ldaptestuser1,cn=users," + self.base_dn, + "objectclass": ["user", "person"], + "primaryGroupID": "0"}) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) + + self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn) + self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) + self.delete_force(self.ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn) + + def test_primary_group_token(self): + """Test the primary group token behaviour (hidden-generated-readonly attribute on groups)""" + print "Testing primary group token behaviour\n" + + ldb.add({ + "dn": "cn=ldaptestuser,cn=users," + self.base_dn, + "objectclass": ["user", "person"]}) + + ldb.add({ + "dn": "cn=ldaptestgroup,cn=users," + self.base_dn, + "objectclass": "group"}) + + res1 = ldb.search("cn=ldaptestuser, cn=users," + self.base_dn, + scope=SCOPE_BASE, attrs=["primaryGroupToken"]) + self.assertTrue(len(res1) == 1) + self.assertFalse("primaryGroupToken" in res1[0]) + + res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn, + scope=SCOPE_BASE) + self.assertTrue(len(res1) == 1) + self.assertFalse("primaryGroupToken" in res1[0]) + + res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn, + scope=SCOPE_BASE, attrs=["primaryGroupToken", "objectSID"]) + self.assertTrue(len(res1) == 1) + primary_group_token = int(res1[0]["primaryGroupToken"][0]) + + rid = dom_sid_to_rid(ldb.schema_format_value("objectSID", res1[0]["objectSID"][0])) + self.assertEquals(primary_group_token, rid) + +# Has to wait until we support read-only generated attributes correctly +# m = Message() +# m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) +# m["primaryGroupToken"] = "100" +# try: +# ldb.modify(m) +# self.fail() +# except LdbError, (num, msg): + + self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn) + self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) + def test_all(self): """Basic tests""" + print "Testing user add" + self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn) - print "Testing user add" ldb.add({ - "dn": "cn=ldaptestuser,cn=uSers," + self.base_dn, - "objectclass": ["user", "person"], - "cN": "LDAPtestUSER", - "givenname": "ldap", - "sn": "testy"}) + "dn": "cn=ldaptestuser,cn=uSers," + self.base_dn, + "objectclass": ["user", "person"], + "cN": "LDAPtestUSER", + "givenname": "ldap", + "sn": "testy"}) + + self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn) ldb.add({ "dn": "cn=ldaptestgroup,cn=uSers," + self.base_dn, @@ -128,9 +355,9 @@ class BasicTests(unittest.TestCase): "cn": "LDAPtest2COMPUTER" }) self.fail() - except LdbError, (num, _): + except LdbError, (num, _): self.assertEquals(num, ERR_INVALID_DN_SYNTAX) - + self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn) try: ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn, @@ -139,9 +366,9 @@ class BasicTests(unittest.TestCase): "sAMAccountType": "805306368" }) self.fail() - except LdbError, (num, _): + except LdbError, (num, _): self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) - + self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn) try: ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn, @@ -150,9 +377,9 @@ class BasicTests(unittest.TestCase): "userAccountControl": "0" }) self.fail() - except LdbError, (num, _): + except LdbError, (num, _): self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) - + self.delete_force(self.ldb, "cn=ldaptestuser7,cn=users," + self.base_dn) try: ldb.add({"dn": "cn=ldaptestuser7,cn=users," + self.base_dn, @@ -161,9 +388,9 @@ class BasicTests(unittest.TestCase): "userAccountControl": "0" }) self.fail() - except LdbError, (num, _): + except LdbError, (num, _): self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) - + self.delete_force(self.ldb, "cn=ldaptestuser7,cn=users," + self.base_dn) ldb.add({"dn": "cn=ldaptestuser7,cn=users," + self.base_dn, @@ -171,7 +398,7 @@ class BasicTests(unittest.TestCase): "cn": "LDAPtestuser7", "userAccountControl": "2" }) - + self.delete_force(self.ldb, "cn=ldaptestuser7,cn=users," + self.base_dn) self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn) @@ -179,7 +406,7 @@ class BasicTests(unittest.TestCase): "objectClass": "computer", "cn": "LDAPtestCOMPUTER3" }) - + print "Testing ldb.search for (&(cn=ldaptestcomputer3)(objectClass=user))"; res = ldb.search(self.base_dn, expression="(&(cn=ldaptestcomputer3)(objectClass=user))"); self.assertEquals(len(res), 1, "Found only %d for (&(cn=ldaptestcomputer3)(objectClass=user))" % len(res)) @@ -239,7 +466,7 @@ dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """ changetype: modify replace: servicePrincipalName """) - + ldb.modify_ldif(""" dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """ changetype: modify @@ -276,7 +503,7 @@ servicePrincipalName: host/ldaptest2computer28 servicePrincipalName: host/ldaptest2computer29 """) - res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-*"]) self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") #print len(res[0]["servicePrincipalName;range=0-*"]) @@ -287,7 +514,7 @@ servicePrincipalName: host/ldaptest2computer29 # print res[0]["servicePrincipalName;range=0-19"].length self.assertEquals(len(res[0]["servicePrincipalName;range=0-19"]), 20) - + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-30"]) self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") self.assertEquals(len(res[0]["servicePrincipalName;range=0-*"]), 30) @@ -300,7 +527,7 @@ servicePrincipalName: host/ldaptest2computer29 self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") self.assertEquals(len(res[0]["servicePrincipalName;range=30-*"]), 0) - + res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=10-40"]) self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)") self.assertEquals(len(res[0]["servicePrincipalName;range=10-*"]), 20) @@ -445,7 +672,7 @@ member: cn=ldaptestuser3,cn=users,""" + self.base_dn + """ res_user = ldb.search(self.base_dn, expression="(&(cn=ldaptestUSer2)(objectClass=user))", scope=SCOPE_SUBTREE, attrs=attrs) self.assertEquals(len(res_user), 1, "Could not find (&(cn=ldaptestUSer2)(objectClass=user))") - #Check rename works with extended/alternate DN forms + #Check rename works with extended/alternate DN forms ldb.rename("<SID=" + ldb.schema_format_value("objectSID", res_user[0]["objectSID"][0]) + ">" , "cn=ldaptestuser3,cn=users," + self.base_dn) ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestuser3,cn=users," + self.base_dn) @@ -512,17 +739,17 @@ member: cn=ldaptestuser3,cn=users,""" + self.base_dn + """ # ensure we cannnot rename it twice try: - ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, + ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestuser2,cn=users," + self.base_dn) self.fail() - except LdbError, (num, _): + except LdbError, (num, _): self.assertEquals(num, ERR_NO_SUCH_OBJECT) # ensure can now use that name ldb.add({"dn": "cn=ldaptestuser3,cn=users," + self.base_dn, "objectClass": ["person", "user"], "cn": "LDAPtestUSER3"}) - + # ensure we now cannnot rename try: ldb.rename("cn=ldaptestuser2,cn=users," + self.base_dn, "cn=ldaptestuser3,cn=users," + self.base_dn) @@ -545,11 +772,11 @@ member: cn=ldaptestuser3,cn=users,""" + self.base_dn + """ print "Testing subtree Renames" - ldb.add({"dn": "cn=ldaptestcontainer," + self.base_dn, + ldb.add({"dn": "cn=ldaptestcontainer," + self.base_dn, "objectClass": "container"}) - + self.delete_force(self.ldb, "cn=ldaptestuser4,cn=ldaptestcontainer," + self.base_dn) - ldb.add({"dn": "CN=ldaptestuser4,CN=ldaptestcontainer," + self.base_dn, + ldb.add({"dn": "CN=ldaptestuser4,CN=ldaptestcontainer," + self.base_dn, "objectClass": ["person", "user"], "cn": "LDAPtestUSER4"}) @@ -559,7 +786,7 @@ changetype: modify add: member member: cn=ldaptestuser4,cn=ldaptestcontainer,""" + self.base_dn + """ """) - + print "Testing ldb.rename of cn=ldaptestcontainer," + self.base_dn + " to cn=ldaptestcontainer2," + self.base_dn ldb.rename("CN=ldaptestcontainer," + self.base_dn, "CN=ldaptestcontainer2," + self.base_dn) @@ -569,8 +796,8 @@ member: cn=ldaptestuser4,cn=ldaptestcontainer,""" + self.base_dn + """ print "Testing subtree ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in (just renamed from) cn=ldaptestcontainer," + self.base_dn try: - res = ldb.search("cn=ldaptestcontainer," + self.base_dn, - expression="(&(cn=ldaptestuser4)(objectClass=user))", + res = ldb.search("cn=ldaptestcontainer," + self.base_dn, + expression="(&(cn=ldaptestuser4)(objectClass=user))", scope=SCOPE_SUBTREE) self.fail(res) except LdbError, (num, _): @@ -578,7 +805,7 @@ member: cn=ldaptestuser4,cn=ldaptestcontainer,""" + self.base_dn + """ print "Testing one-level ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in (just renamed from) cn=ldaptestcontainer," + self.base_dn try: - res = ldb.search("cn=ldaptestcontainer," + self.base_dn, + res = ldb.search("cn=ldaptestcontainer," + self.base_dn, expression="(&(cn=ldaptestuser4)(objectClass=user))", scope=SCOPE_ONELEVEL) self.fail() except LdbError, (num, _): @@ -636,7 +863,7 @@ member: cn=ldaptestuser4,cn=ldaptestcontainer,""" + self.base_dn + """ ldb.delete(("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn)) print "Testing delete of renamed cn=ldaptestcontainer2," + self.base_dn ldb.delete("cn=ldaptestcontainer2," + self.base_dn) - + self.delete_force(self.ldb, "cn=ldaptestutf8user èùéìòà ,cn=users," + self.base_dn) ldb.add({"dn": "cn=ldaptestutf8user èùéìòà ,cn=users," + self.base_dn, "objectClass": "user"}) @@ -658,7 +885,7 @@ member: cn=ldaptestuser4,cn=ldaptestcontainer,""" + self.base_dn + """ self.assertEquals(int(res[0]["userAccountControl"][0]), 546) self.assertEquals(res[0]["memberOf"][0].upper(), ("CN=ldaptestgroup2,CN=Users," + self.base_dn).upper()) self.assertEquals(len(res[0]["memberOf"]), 1) - + print "Testing ldb.search for (&(cn=ldaptestuser)(objectCategory=cn=person,cn=schema,cn=configuration," + self.base_dn + "))" res2 = ldb.search(expression="(&(cn=ldaptestuser)(objectCategory=cn=person,cn=schema,cn=configuration," + self.base_dn + "))") self.assertEquals(len(res2), 1, "Could not find (&(cn=ldaptestuser)(objectCategory=cn=person,cn=schema,cn=configuration," + self.base_dn + "))") @@ -675,14 +902,14 @@ member: cn=ldaptestuser4,cn=ldaptestcontainer,""" + self.base_dn + """ print "Testing ldb.search for (&(cn=ldaptestuser)(objectCategory=PerSon)) in Global Catalog" res3gc = gc_ldb.search(expression="(&(cn=ldaptestuser)(objectCategory=PerSon))") self.assertEquals(len(res3gc), 1) - + self.assertEquals(res[0].dn, res3gc[0].dn) print "Testing ldb.search for (&(cn=ldaptestuser)(objectCategory=PerSon)) in with 'phantom root' control" - + res3control = gc_ldb.search(self.base_dn, expression="(&(cn=ldaptestuser)(objectCategory=PerSon))", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["search_options:1:2"]) self.assertEquals(len(res3control), 1, "Could not find (&(cn=ldaptestuser)(objectCategory=PerSon)) in Global Catalog") - + self.assertEquals(res[0].dn, res3control[0].dn) ldb.delete(res[0].dn) @@ -747,7 +974,7 @@ member: cn=ldaptestuser4,cn=ldaptestcontainer,""" + self.base_dn + """ self.assertEquals(len(res6), 1, "Could not find (&(cn=*daptestcomputer)(objectCategory=compuTER))") self.assertEquals(res[0].dn, res6[0].dn) - + ldb.delete("<GUID=" + ldb.schema_format_value("objectGUID", res[0]["objectGUID"][0]) + ">") print "Testing ldb.search for (&(cn=ldaptest2computer)(objectClass=user))" @@ -825,14 +1052,14 @@ replace: member member: CN=ldaptestuser2,CN=Users,""" + self.base_dn + """ member: CN=ldaptestutf8user èùéìòà ,CN=Users,""" + self.base_dn + """ """) - + ldb.modify_ldif(""" dn: <GUID=""" + ldb.schema_format_value("objectGUID", res[0]["objectGUID"][0]) + """> changetype: modify replace: member member: CN=ldaptestutf8user èùéìòà ,CN=Users,""" + self.base_dn + """ """) - + ldb.modify_ldif(""" dn: <SID=""" + ldb.schema_format_value("objectSid", res[0]["objectSid"][0]) + """> changetype: modify @@ -846,13 +1073,13 @@ add: member member: <GUID=""" + ldb.schema_format_value("objectGUID", res[0]["objectGUID"][0]) + """> member: CN=ldaptestutf8user èùéìòà ,CN=Users,""" + self.base_dn + """ """) - + ldb.modify_ldif(""" dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """ changetype: modify replace: member """) - + ldb.modify_ldif(""" dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """ changetype: modify @@ -860,14 +1087,14 @@ add: member member: <SID=""" + ldb.schema_format_value("objectSid", res_user[0]["objectSid"][0]) + """> member: CN=ldaptestutf8user èùéìòà ,CN=Users,""" + self.base_dn + """ """) - + ldb.modify_ldif(""" dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """ changetype: modify delete: member member: CN=ldaptestutf8user èùéìòà ,CN=Users,""" + self.base_dn + """ """) - + res = ldb.search(self.base_dn, expression="(&(cn=ldaptestgroup2)(objectClass=group))", scope=SCOPE_SUBTREE, attrs=attrs) self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestgroup2)(objectClass=group))") @@ -923,14 +1150,14 @@ member: CN=ldaptestutf8user èùéìòà ,CN=Users,""" + self.base_dn + """ if gc_ldb is not None: print "Testing that we can get at the configuration DN from the main search base on the GC port with the search_options control == 0" - + res = gc_ldb.search(self.base_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["search_options:1:0"]) self.assertTrue(len(res) > 0) print "Testing that we do find configuration elements in the global catlog" res = gc_ldb.search(self.base_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"]) self.assertTrue(len(res) > 0) - + print "Testing that we do find configuration elements and user elements at the same time" res = gc_ldb.search(self.base_dn, expression="(|(objectClass=crossRef)(objectClass=person))", scope=SCOPE_SUBTREE, attrs=["cn"]) self.assertTrue(len(res) > 0) @@ -947,13 +1174,13 @@ member: CN=ldaptestutf8user èùéìòà ,CN=Users,""" + self.base_dn + """ res = ldb.search(self.configuration_dn, expression="objectCategory=ntDsDSA", scope=SCOPE_SUBTREE, attrs=["cn"]) self.assertTrue(len(res) > 0, "Didn't find any records with objectCategory=ntDsDSA") self.assertTrue(len(res) != 0) - + res = ldb.search(self.configuration_dn, expression="objectCategory=CN=ntDs-DSA," + self.schema_dn, scope=SCOPE_SUBTREE, attrs=["cn"]) self.assertTrue(len(res) > 0, "Didn't find any records with objectCategory=CN=ntDs-DSA," + self.schema_dn) self.assertTrue(len(res) != 0) - + print "Testing objectClass attribute order on "+ self.base_dn - res = ldb.search(expression="objectClass=domain", base=self.base_dn, + res = ldb.search(expression="objectClass=domain", base=self.base_dn, scope=SCOPE_BASE, attrs=["objectClass"]) self.assertEquals(len(res), 1) @@ -968,15 +1195,15 @@ member: CN=ldaptestutf8user èùéìòà ,CN=Users,""" + self.base_dn + """ print "Testing ldb.search for objectCategory=person with domain scope control" res = ldb.search(self.base_dn, expression="objectCategory=person", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["domain_scope:1"]) self.assertTrue(len(res) > 0) - + print "Testing ldb.search for objectCategory=user" res = ldb.search(self.base_dn, expression="objectCategory=user", scope=SCOPE_SUBTREE, attrs=["cn"]) self.assertTrue(len(res) > 0) - + print "Testing ldb.search for objectCategory=user with domain scope control" res = ldb.search(self.base_dn, expression="objectCategory=user", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["domain_scope:1"]) self.assertTrue(len(res) > 0) - + print "Testing ldb.search for objectCategory=group" res = ldb.search(self.base_dn, expression="objectCategory=group", scope=SCOPE_SUBTREE, attrs=["cn"]) self.assertTrue(len(res) > 0) @@ -985,6 +1212,189 @@ member: CN=ldaptestutf8user èùéìòà ,CN=Users,""" + self.base_dn + """ res = ldb.search(self.base_dn, expression="objectCategory=group", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["domain_scope:1"]) self.assertTrue(len(res) > 0) + def test_security_descriptor_add(self): + """ Testing ldb.add_ldif() for nTSecurityDescriptor """ + user_name = "testdescriptoruser1" + user_dn = "CN=%s,CN=Users,%s" % (user_name, self.base_dn) + # + # Test add_ldif() with SDDL security descriptor input + # + self.delete_force(self.ldb, user_dn) + try: + sddl = "O:DUG:DUD:PAI(A;;RPWP;;;AU)S:PAI" + self.ldb.add_ldif(""" +dn: """ + user_dn + """ +objectclass: user +sAMAccountName: """ + user_name + """ +nTSecurityDescriptor: """ + sddl) + res = self.ldb.search(base=user_dn, attrs=["nTSecurityDescriptor"]) + desc = res[0]["nTSecurityDescriptor"][0] + desc = ndr_unpack( security.descriptor, desc ) + desc_sddl = desc.as_sddl( self.domain_sid ) + self.assertEqual(desc_sddl, sddl) + finally: + self.delete_force(self.ldb, user_dn) + # + # Test add_ldif() with BASE64 security descriptor + # + try: + sddl = "O:DUG:DUD:PAI(A;;RPWP;;;AU)S:PAI" + desc = security.descriptor.from_sddl(sddl, self.domain_sid) + desc_binary = ndr_pack(desc) + desc_base64 = base64.b64encode(desc_binary) + self.ldb.add_ldif(""" +dn: """ + user_dn + """ +objectclass: user +sAMAccountName: """ + user_name + """ +nTSecurityDescriptor:: """ + desc_base64) + res = self.ldb.search(base=user_dn, attrs=["nTSecurityDescriptor"]) + desc = res[0]["nTSecurityDescriptor"][0] + desc = ndr_unpack(security.descriptor, desc) + desc_sddl = desc.as_sddl(self.domain_sid) + self.assertEqual(desc_sddl, sddl) + finally: + self.delete_force(self.ldb, user_dn) + + def test_security_descriptor_add_neg(self): + """Test add_ldif() with BASE64 security descriptor input using WRONG domain SID + Negative test + """ + user_name = "testdescriptoruser1" + user_dn = "CN=%s,CN=Users,%s" % (user_name, self.base_dn) + self.delete_force(self.ldb, user_dn) + try: + sddl = "O:DUG:DUD:PAI(A;;RPWP;;;AU)S:PAI" + desc = security.descriptor.from_sddl(sddl, security.dom_sid('S-1-5-21')) + desc_base64 = base64.b64encode( ndr_pack(desc) ) + self.ldb.add_ldif(""" +dn: """ + user_dn + """ +objectclass: user +sAMAccountName: """ + user_name + """ +nTSecurityDescriptor:: """ + desc_base64) + res = self.ldb.search(base=user_dn, attrs=["nTSecurityDescriptor"]) + print res + self.assertRaises(KeyError, lambda: res[0]["nTSecurityDescriptor"]) + finally: + self.delete_force(self.ldb, user_dn) + + def test_security_descriptor_modify(self): + """ Testing ldb.modify_ldif() for nTSecurityDescriptor """ + user_name = "testdescriptoruser2" + user_dn = "CN=%s,CN=Users,%s" % (user_name, self.base_dn) + # + # Delete user object and test modify_ldif() with SDDL security descriptor input + # Add ACE to the original descriptor test + # + try: + self.delete_force(self.ldb, user_dn) + self.ldb.add_ldif(""" +dn: """ + user_dn + """ +objectclass: user +sAMAccountName: """ + user_name) + # Modify descriptor + res = self.ldb.search(base=user_dn, attrs=["nTSecurityDescriptor"]) + desc = res[0]["nTSecurityDescriptor"][0] + desc = ndr_unpack(security.descriptor, desc) + desc_sddl = desc.as_sddl(self.domain_sid) + sddl = desc_sddl[:desc_sddl.find("(")] + "(A;;RPWP;;;AU)" + desc_sddl[desc_sddl.find("("):] + mod = """ +dn: """ + user_dn + """ +changetype: modify +replace: nTSecurityDescriptor +nTSecurityDescriptor: """ + sddl + self.ldb.modify_ldif(mod) + # Read modified descriptor + res = self.ldb.search(base=user_dn, attrs=["nTSecurityDescriptor"]) + desc = res[0]["nTSecurityDescriptor"][0] + desc = ndr_unpack(security.descriptor, desc) + desc_sddl = desc.as_sddl(self.domain_sid) + self.assertEqual(desc_sddl, sddl) + finally: + self.delete_force(self.ldb, user_dn) + # + # Test modify_ldif() with SDDL security descriptor input + # New desctiptor test + # + try: + self.ldb.add_ldif(""" +dn: """ + user_dn + """ +objectclass: user +sAMAccountName: """ + user_name) + # Modify descriptor + sddl = "O:DUG:DUD:PAI(A;;RPWP;;;AU)S:PAI" + mod = """ +dn: """ + user_dn + """ +changetype: modify +replace: nTSecurityDescriptor +nTSecurityDescriptor: """ + sddl + self.ldb.modify_ldif(mod) + # Read modified descriptor + res = self.ldb.search(base=user_dn, attrs=["nTSecurityDescriptor"]) + desc = res[0]["nTSecurityDescriptor"][0] + desc = ndr_unpack(security.descriptor, desc) + desc_sddl = desc.as_sddl(self.domain_sid) + self.assertEqual(desc_sddl, sddl) + finally: + self.delete_force(self.ldb, user_dn) + # + # Test modify_ldif() with BASE64 security descriptor input + # Add ACE to the original descriptor test + # + try: + self.ldb.add_ldif(""" +dn: """ + user_dn + """ +objectclass: user +sAMAccountName: """ + user_name) + # Modify descriptor + res = self.ldb.search(base=user_dn, attrs=["nTSecurityDescriptor"]) + desc = res[0]["nTSecurityDescriptor"][0] + desc = ndr_unpack(security.descriptor, desc) + desc_sddl = desc.as_sddl(self.domain_sid) + sddl = desc_sddl[:desc_sddl.find("(")] + "(A;;RPWP;;;AU)" + desc_sddl[desc_sddl.find("("):] + desc = security.descriptor.from_sddl(sddl, self.domain_sid) + desc_base64 = base64.b64encode(ndr_pack(desc)) + mod = """ +dn: """ + user_dn + """ +changetype: modify +replace: nTSecurityDescriptor +nTSecurityDescriptor:: """ + desc_base64 + self.ldb.modify_ldif(mod) + # Read modified descriptor + res = self.ldb.search(base=user_dn, attrs=["nTSecurityDescriptor"]) + desc = res[0]["nTSecurityDescriptor"][0] + desc = ndr_unpack(security.descriptor, desc) + desc_sddl = desc.as_sddl(self.domain_sid) + self.assertEqual(desc_sddl, sddl) + finally: + self.delete_force(self.ldb, user_dn) + # + # Test modify_ldif() with BASE64 security descriptor input + # New descriptor test + # + try: + self.delete_force(self.ldb, user_dn) + self.ldb.add_ldif(""" +dn: """ + user_dn + """ +objectclass: user +sAMAccountName: """ + user_name) + # Modify descriptor + sddl = "O:DUG:DUD:PAI(A;;RPWP;;;AU)S:PAI" + desc = security.descriptor.from_sddl(sddl, self.domain_sid) + desc_base64 = base64.b64encode(ndr_pack(desc)) + mod = """ +dn: """ + user_dn + """ +changetype: modify +replace: nTSecurityDescriptor +nTSecurityDescriptor:: """ + desc_base64 + self.ldb.modify_ldif(mod) + # Read modified descriptor + res = self.ldb.search(base=user_dn, attrs=["nTSecurityDescriptor"]) + desc = res[0]["nTSecurityDescriptor"][0] + desc = ndr_unpack(security.descriptor, desc) + desc_sddl = desc.as_sddl(self.domain_sid) + self.assertEqual(desc_sddl, sddl) + finally: + self.delete_force(self.ldb, user_dn) class BaseDnTests(unittest.TestCase): def setUp(self): @@ -1008,23 +1418,36 @@ class BaseDnTests(unittest.TestCase): def test_netlogon_highestcommitted_usn(self): """Testing for netlogon and highestCommittedUSN via LDAP""" - res = self.ldb.search("", scope=SCOPE_BASE, + res = self.ldb.search("", scope=SCOPE_BASE, attrs=["netlogon", "highestCommittedUSN"]) self.assertEquals(len(res), 0) class SchemaTests(unittest.TestCase): + def delete_force(self, ldb, dn): + try: + ldb.delete(dn) + except LdbError, (num, _): + self.assertEquals(num, ERR_NO_SUCH_OBJECT) + def find_schemadn(self, ldb): res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["schemaNamingContext"]) self.assertEquals(len(res), 1) return res[0]["schemaNamingContext"][0] + def find_basedn(self, ldb): + res = ldb.search(base="", expression="", scope=SCOPE_BASE, + attrs=["defaultNamingContext"]) + self.assertEquals(len(res), 1) + return res[0]["defaultNamingContext"][0] + def setUp(self): self.ldb = ldb self.schema_dn = self.find_schemadn(ldb) + self.base_dn = self.find_basedn(ldb) def test_generated_schema(self): """Testing we can read the generated schema via LDAP""" - res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE, + res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE, attrs=["objectClasses", "attributeTypes", "dITContentRules"]) self.assertEquals(len(res), 1) self.assertTrue("dITContentRules" in res[0]) @@ -1033,18 +1456,77 @@ class SchemaTests(unittest.TestCase): def test_generated_schema_is_operational(self): """Testing we don't get the generated schema via LDAP by default""" - res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE, + res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE, attrs=["*"]) self.assertEquals(len(res), 1) self.assertFalse("dITContentRules" in res[0]) self.assertFalse("objectClasses" in res[0]) self.assertFalse("attributeTypes" in res[0]) - + + def test_schemaUpdateNow(self): + """Testing schemaUpdateNow""" + class_name = "test-class" + time.strftime("%s", time.gmtime()) + class_ldap_display_name = class_name.replace("-", "") + object_name = "obj" + time.strftime("%s", time.gmtime()) + + ldif = """ +dn: CN=%s,%s""" % (class_name, self.schema_dn) + """ +lDAPDisplayName: """ + class_ldap_display_name + """ +objectClass: top +objectClass: classSchema +adminDescription: """ + class_name + """ +adminDisplayName: """ + class_name + """ +cn: """ + class_name + """ +objectCategory: CN=Class-Schema,""" + self.schema_dn + """ +defaultObjectCategory: CN=%s,%s""" % (class_name, self.schema_dn) + """ +distinguishedName: CN=%s,%s""" % (class_name, self.schema_dn) + """ +governsID: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9939 +instanceType: 4 +name: """ + class_name + """ +objectClassCategory: 1 +subClassOf: organizationalPerson +systemFlags: 16 +rDNAttID: cn +systemMustContain: cn +systemOnly: FALSE +""" + self.ldb.add_ldif(ldif) + ldif = """ +dn: +changetype: modify +add: schemaUpdateNow +schemaUpdateNow: 1 +""" + self.ldb.modify_ldif(ldif) + ldif = """ +dn: CN=%s,CN=Users,%s"""% (object_name, self.base_dn) + """ +objectClass: organizationalPerson +objectClass: person +objectClass: """ + class_ldap_display_name + """ +objectClass: top +cn: """ + object_name + """ +instanceType: 4 +objectCategory: CN=%s,%s"""% (class_name, self.schema_dn) + """ +distinguishedName: CN=%s,CN=Users,%s"""% (object_name, self.base_dn) + """ +name: """ + object_name + """ +""" + self.ldb.add_ldif(ldif) + # Search for created objectClass + res = [] + res = self.ldb.search("cn=%s,%s" % (class_name, self.schema_dn), scope=SCOPE_BASE, attrs=["*"]) + self.assertNotEqual(res, []) + + res = [] + res = self.ldb.search("cn=%s,cn=Users,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["*"]) + self.assertNotEqual(res, []) + # Delete the object + self.delete_force(self.ldb, "cn=%s,cn=Users,%s" % (object_name, self.base_dn)) + if not "://" in host: host = "ldap://%s" % host ldb = Ldb(host, credentials=creds, session_info=system_session(), lp=lp) -gc_ldb = Ldb("%s:3268" % host, credentials=creds, +gc_ldb = Ldb("%s:3268" % host, credentials=creds, session_info=system_session(), lp=lp) runner = SubunitTestRunner() diff --git a/source4/lib/ldb/tests/python/sec_descriptor.py b/source4/lib/ldb/tests/python/sec_descriptor.py new file mode 100755 index 0000000000..155b65f4ab --- /dev/null +++ b/source4/lib/ldb/tests/python/sec_descriptor.py @@ -0,0 +1,1615 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import getopt +import optparse +import sys +import os +import base64 +import re +import random + +sys.path.append("bin/python") +sys.path.append("../lib/subunit/python") + +import samba.getopt as options + +# Some error messages that are being tested +from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError +from ldb import ERR_NO_SUCH_OBJECT, ERR_INVALID_DN_SYNTAX, ERR_UNWILLING_TO_PERFORM +from ldb import ERR_INSUFFICIENT_ACCESS_RIGHTS + +# For running the test unit +from samba.ndr import ndr_pack, ndr_unpack +from samba.dcerpc import security + +from samba.auth import system_session +from samba import Ldb, DS_DOMAIN_FUNCTION_2008 +from subunit import SubunitTestRunner +import unittest + +parser = optparse.OptionParser("sec_descriptor [options] <host>") +sambaopts = options.SambaOptions(parser) +parser.add_option_group(sambaopts) +parser.add_option_group(options.VersionOptions(parser)) + +# use command line creds if available +credopts = options.CredentialsOptions(parser) +parser.add_option_group(credopts) +opts, args = parser.parse_args() + +if len(args) < 1: + parser.print_usage() + sys.exit(1) + +host = args[0] + +lp = sambaopts.get_loadparm() +creds = credopts.get_credentials(lp) + +# +# Tests start here +# + +class DescriptorTests(unittest.TestCase): + + def delete_force(self, ldb, dn): + try: + ldb.delete(dn) + except LdbError, (num, _): + self.assertEquals(num, ERR_NO_SUCH_OBJECT) + + def find_basedn(self, ldb): + res = ldb.search(base="", expression="", scope=SCOPE_BASE, + attrs=["defaultNamingContext"]) + self.assertEquals(len(res), 1) + return res[0]["defaultNamingContext"][0] + + def find_configurationdn(self, ldb): + res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["configurationNamingContext"]) + self.assertEquals(len(res), 1) + return res[0]["configurationNamingContext"][0] + + def find_schemadn(self, ldb): + res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["schemaNamingContext"]) + self.assertEquals(len(res), 1) + return res[0]["schemaNamingContext"][0] + + def find_domain_sid(self, ldb): + res = ldb.search(base=self.base_dn, expression="(objectClass=*)", scope=SCOPE_BASE) + return ndr_unpack( security.dom_sid,res[0]["objectSid"][0]) + + def get_users_domain_dn(self, name): + return "CN=%s,CN=Users,%s" % (name, self.base_dn) + + def modify_desc(self, object_dn, desc): + assert(isinstance(desc, str) or isinstance(desc, security.descriptor)) + mod = """ +dn: """ + object_dn + """ +changetype: modify +replace: nTSecurityDescriptor +""" + if isinstance(desc, str): + mod += "nTSecurityDescriptor: %s" % desc + elif isinstance(desc, security.descriptor): + mod += "nTSecurityDescriptor:: %s" % base64.b64encode(ndr_pack(desc)) + self.ldb_admin.modify_ldif(mod) + + def create_domain_ou(self, _ldb, ou_dn, desc=None): + ldif = """ +dn: """ + ou_dn + """ +ou: """ + ou_dn.split(",")[0][3:] + """ +objectClass: organizationalUnit +url: www.example.com +""" + if desc: + assert(isinstance(desc, str) or isinstance(desc, security.descriptor)) + if isinstance(desc, str): + ldif += "nTSecurityDescriptor: %s" % desc + elif isinstance(desc, security.descriptor): + ldif += "nTSecurityDescriptor:: %s" % base64.b64encode(ndr_pack(desc)) + _ldb.add_ldif(ldif) + + def create_domain_user(self, _ldb, user_dn, desc=None): + ldif = """ +dn: """ + user_dn + """ +sAMAccountName: """ + user_dn.split(",")[0][3:] + """ +objectClass: user +userPassword: samba123@ +url: www.example.com +""" + if desc: + assert(isinstance(desc, str) or isinstance(desc, security.descriptor)) + if isinstance(desc, str): + ldif += "nTSecurityDescriptor: %s" % desc + elif isinstance(desc, security.descriptor): + ldif += "nTSecurityDescriptor:: %s" % base64.b64encode(ndr_pack(desc)) + _ldb.add_ldif(ldif) + + def create_domain_group(self, _ldb, group_dn, desc=None): + ldif = """ +dn: """ + group_dn + """ +objectClass: group +sAMAccountName: """ + group_dn.split(",")[0][3:] + """ +groupType: 4 +url: www.example.com +""" + if desc: + assert(isinstance(desc, str) or isinstance(desc, security.descriptor)) + if isinstance(desc, str): + ldif += "nTSecurityDescriptor: %s" % desc + elif isinstance(desc, security.descriptor): + ldif += "nTSecurityDescriptor:: %s" % base64.b64encode(ndr_pack(desc)) + _ldb.add_ldif(ldif) + + def get_unique_schema_class_name(self): + while True: + class_name = "test-class%s" % random.randint(1,100000) + class_dn = "CN=%s,%s" % (class_name, self.schema_dn) + try: + self.ldb_admin.search(base=class_dn, attrs=["*"]) + except LdbError, (num, _): + self.assertEquals(num, ERR_NO_SUCH_OBJECT) + return class_name + + def create_schema_class(self, _ldb, object_dn, desc=None): + ldif = """ +dn: """ + object_dn + """ +objectClass: classSchema +objectCategory: CN=Class-Schema,""" + self.schema_dn + """ +defaultObjectCategory: """ + object_dn + """ +distinguishedName: """ + object_dn + """ +governsID: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9939 +instanceType: 4 +objectClassCategory: 1 +subClassOf: organizationalPerson +systemFlags: 16 +rDNAttID: cn +systemMustContain: cn +systemOnly: FALSE +""" + if desc: + assert(isinstance(desc, str) or isinstance(desc, security.descriptor)) + if isinstance(desc, str): + ldif += "nTSecurityDescriptor: %s" % desc + elif isinstance(desc, security.descriptor): + ldif += "nTSecurityDescriptor:: %s" % base64.b64encode(ndr_pack(desc)) + _ldb.add_ldif(ldif) + + def create_configuration_container(self, _ldb, object_dn, desc=None): + ldif = """ +dn: """ + object_dn + """ +objectClass: container +objectCategory: CN=Container,""" + self.schema_dn + """ +showInAdvancedViewOnly: TRUE +instanceType: 4 +""" + if desc: + assert(isinstance(desc, str) or isinstance(desc, security.descriptor)) + if isinstance(desc, str): + ldif += "nTSecurityDescriptor: %s" % desc + elif isinstance(desc, security.descriptor): + ldif += "nTSecurityDescriptor:: %s" % base64.b64encode(ndr_pack(desc)) + _ldb.add_ldif(ldif) + + def create_configuration_specifier(self, _ldb, object_dn, desc=None): + ldif = """ +dn: """ + object_dn + """ +objectClass: displaySpecifier +showInAdvancedViewOnly: TRUE +""" + if desc: + assert(isinstance(desc, str) or isinstance(desc, security.descriptor)) + if isinstance(desc, str): + ldif += "nTSecurityDescriptor: %s" % desc + elif isinstance(desc, security.descriptor): + ldif += "nTSecurityDescriptor:: %s" % base64.b64encode(ndr_pack(desc)) + _ldb.add_ldif(ldif) + + def read_desc(self, object_dn): + res = self.ldb_admin.search(base=object_dn, attrs=["nTSecurityDescriptor"]) + desc = res[0]["nTSecurityDescriptor"][0] + return ndr_unpack( security.descriptor, desc ) + + def enable_account(self, user_dn): + """Enable an account. + :param user_dn: Dn of the account to enable. + """ + res = self.ldb_admin.search(user_dn, SCOPE_BASE, None, ["userAccountControl"]) + assert len(res) == 1 + userAccountControl = res[0]["userAccountControl"][0] + userAccountControl = int(userAccountControl) + if (userAccountControl & 0x2): + userAccountControl = userAccountControl & ~0x2 # remove disabled bit + if (userAccountControl & 0x20): + userAccountControl = userAccountControl & ~0x20 # remove 'no password required' bit + mod = """ +dn: """ + user_dn + """ +changetype: modify +replace: userAccountControl +userAccountControl: %s""" % userAccountControl + if self.WIN2003: + mod = re.sub("userAccountControl: \d.*", "userAccountControl: 544", mod) + self.ldb_admin.modify_ldif(mod) + + def get_ldb_connection(self, target_username, target_password): + username_save = creds.get_username(); password_save = creds.get_password() + creds.set_username(target_username) + creds.set_password(target_password) + ldb_target = Ldb(host, credentials=creds, session_info=system_session(), lp=lp) + creds.set_username(username_save); creds.set_password(password_save) + return ldb_target + + def get_object_sid(self, object_dn): + res = self.ldb_admin.search(object_dn) + return ndr_unpack( security.dom_sid, res[0]["objectSid"][0] ) + + def dacl_add_ace(self, object_dn, ace): + desc = self.read_desc( object_dn ) + desc_sddl = desc.as_sddl( self.domain_sid ) + if ace in desc_sddl: + return + if desc_sddl.find("(") >= 0: + desc_sddl = desc_sddl[0:desc_sddl.index("(")] + ace + desc_sddl[desc_sddl.index("("):] + else: + desc_sddl = desc_sddl + ace + self.modify_desc(object_dn, desc_sddl) + + def get_desc_sddl(self, object_dn): + """ Return object nTSecutiryDescriptor in SDDL format + """ + desc = self.read_desc(object_dn) + return desc.as_sddl(self.domain_sid) + + def setUp(self): + self.ldb_admin = ldb + self.base_dn = self.find_basedn(self.ldb_admin) + self.configuration_dn = self.find_configurationdn(self.ldb_admin) + self.schema_dn = self.find_schemadn(self.ldb_admin) + self.domain_sid = self.find_domain_sid(self.ldb_admin) + print "baseDN: %s" % self.base_dn + self.SAMBA = False; self.WIN2003 = False + res = self.ldb_admin.search(base="", expression="", scope=SCOPE_BASE, attrs=["vendorName"]) + if "vendorName" in res[0].keys() and "Samba Team" in res[0]["vendorName"][0]: + self.SAMBA = True + else: + self.WIN2003 = True + #print "self.SAMBA:", self.SAMBA + #print "self.WIN2003:", self.WIN2003 + + ################################################################################################ + + ## Tests for DOMAIN + + # Default descriptor tests ##################################################################### + +class OwnerGroupDescriptorTests(DescriptorTests): + + def setUp(self): + DescriptorTests.setUp(self) + if self.SAMBA: + ### Create users + # User 1 + user_dn = self.get_users_domain_dn("testuser1") + self.create_domain_user(self.ldb_admin, user_dn) + self.enable_account(user_dn) + ldif = """ +dn: CN=Enterprise Admins,CN=Users,""" + self.base_dn + """ +changetype: add +member: """ + user_dn + self.ldb_admin.modify_ldif(ldif) + # User 2 + user_dn = self.get_users_domain_dn("testuser2") + self.create_domain_user(self.ldb_admin, user_dn) + self.enable_account(user_dn) + ldif = """ +dn: CN=Domain Admins,CN=Users,""" + self.base_dn + """ +changetype: add +member: """ + user_dn + self.ldb_admin.modify_ldif(ldif) + # User 3 + user_dn = self.get_users_domain_dn("testuser3") + self.create_domain_user(self.ldb_admin, user_dn) + self.enable_account(user_dn) + ldif = """ +dn: CN=Schema Admins,CN=Users,""" + self.base_dn + """ +changetype: add +member: """ + user_dn + self.ldb_admin.modify_ldif(ldif) + # User 4 + user_dn = self.get_users_domain_dn("testuser4") + self.create_domain_user(self.ldb_admin, user_dn) + self.enable_account(user_dn) + # User 5 + user_dn = self.get_users_domain_dn("testuser5") + self.create_domain_user(self.ldb_admin, user_dn) + self.enable_account(user_dn) + ldif = """ +dn: CN=Enterprise Admins,CN=Users,""" + self.base_dn + """ +changetype: add +member: """ + user_dn + """ + +dn: CN=Domain Admins,CN=Users,""" + self.base_dn + """ +changetype: add +member: """ + user_dn + self.ldb_admin.modify_ldif(ldif) + # User 6 + user_dn = self.get_users_domain_dn("testuser6") + self.create_domain_user(self.ldb_admin, user_dn) + self.enable_account(user_dn) + ldif = """ +dn: CN=Enterprise Admins,CN=Users,""" + self.base_dn + """ +changetype: add +member: """ + user_dn + """ + +dn: CN=Domain Admins,CN=Users,""" + self.base_dn + """ +changetype: add +member: """ + user_dn + """ + +dn: CN=Schema Admins,CN=Users,""" + self.base_dn + """ +changetype: add +member: """ + user_dn + self.ldb_admin.modify_ldif(ldif) + # User 7 + user_dn = self.get_users_domain_dn("testuser7") + self.create_domain_user(self.ldb_admin, user_dn) + self.enable_account(user_dn) + ldif = """ +dn: CN=Domain Admins,CN=Users,""" + self.base_dn + """ +changetype: add +member: """ + user_dn + """ + +dn: CN=Schema Admins,CN=Users,""" + self.base_dn + """ +changetype: add +member: """ + user_dn + self.ldb_admin.modify_ldif(ldif) + # User 8 + user_dn = self.get_users_domain_dn("testuser8") + self.create_domain_user(self.ldb_admin, user_dn) + self.enable_account(user_dn) + ldif = """ +dn: CN=Enterprise Admins,CN=Users,""" + self.base_dn + """ +changetype: add +member: """ + user_dn + """ + +dn: CN=Schema Admins,CN=Users,""" + self.base_dn + """ +changetype: add +member: """ + user_dn + self.ldb_admin.modify_ldif(ldif) + self.results = { + # msDS-Behavior-Version < DS_DOMAIN_FUNCTION_2008 + "ds_behavior_win2003" : { + "100" : "O:EAG:DU", + "101" : "O:DAG:DU", + "102" : "O:%sG:DU", + "103" : "O:%sG:DU", + "104" : "O:DAG:DU", + "105" : "O:DAG:DU", + "106" : "O:DAG:DU", + "107" : "O:EAG:DU", + "108" : "O:DAG:DA", + "109" : "O:DAG:DA", + "110" : "O:%sG:DA", + "111" : "O:%sG:DA", + "112" : "O:DAG:DA", + "113" : "O:DAG:DA", + "114" : "O:DAG:DA", + "115" : "O:DAG:DA", + "130" : "O:EAG:DU", + "131" : "O:DAG:DU", + "132" : "O:SAG:DU", + "133" : "O:%sG:DU", + "134" : "O:EAG:DU", + "135" : "O:SAG:DU", + "136" : "O:SAG:DU", + "137" : "O:SAG:DU", + "138" : "O:DAG:DA", + "139" : "O:DAG:DA", + "140" : "O:%sG:DA", + "141" : "O:%sG:DA", + "142" : "O:DAG:DA", + "143" : "O:DAG:DA", + "144" : "O:DAG:DA", + "145" : "O:DAG:DA", + "160" : "O:EAG:DU", + "161" : "O:DAG:DU", + "162" : "O:%sG:DU", + "163" : "O:%sG:DU", + "164" : "O:EAG:DU", + "165" : "O:EAG:DU", + "166" : "O:DAG:DU", + "167" : "O:EAG:DU", + "168" : "O:DAG:DA", + "169" : "O:DAG:DA", + "170" : "O:%sG:DA", + "171" : "O:%sG:DA", + "172" : "O:DAG:DA", + "173" : "O:DAG:DA", + "174" : "O:DAG:DA", + "175" : "O:DAG:DA", + }, + # msDS-Behavior-Version >= 3 + "ds_behavior_win2008" : { + "100" : "O:EAG:EA", + "101" : "O:DAG:DA", + "102" : "O:%sG:DU", + "103" : "O:%sG:DU", + "104" : "O:DAG:DA", + "105" : "O:DAG:DA", + "106" : "O:DAG:DA", + "107" : "O:EAG:EA", + "108" : "O:DAG:DA", + "109" : "O:DAG:DA", + "110" : "O:%sG:DA", + "111" : "O:%sG:DA", + "112" : "O:DAG:DA", + "113" : "O:DAG:DA", + "114" : "O:DAG:DA", + "115" : "O:DAG:DA", + "130" : "", + "131" : "", + "132" : "", + "133" : "%s", + "134" : "", + "135" : "", + "136" : "", + "137" : "", + "138" : "", + "139" : "", + "140" : "%s", + "141" : "%s", + "142" : "", + "143" : "", + "144" : "", + "145" : "", + "160" : "O:EAG:EA", + "161" : "O:DAG:DA", + "162" : "O:%sG:DU", + "163" : "O:%sG:DU", + "164" : "O:EAG:EA", + "165" : "O:EAG:EA", + "166" : "O:DAG:DA", + "167" : "O:EAG:EA", + "168" : "O:DAG:DA", + "169" : "O:DAG:DA", + "170" : "O:%sG:DA", + "171" : "O:%sG:DA", + "172" : "O:DAG:DA", + "173" : "O:DAG:DA", + "174" : "O:DAG:DA", + "175" : "O:DAG:DA", + }, + } + # Discover 'msDS-Behavior-Version' + res = self.ldb_admin.search(base=self.base_dn, expression="distinguishedName=%s" % self.base_dn, \ + attrs=['msDS-Behavior-Version']) + res = int(res[0]['msDS-Behavior-Version'][0]) + if res < DS_DOMAIN_FUNCTION_2008: + self.DS_BEHAVIOR = "ds_behavior_win2003" + else: + self.DS_BEHAVIOR = "ds_behavior_win2008" + + def tearDown(self): + if self.SAMBA: + self.delete_force(self.ldb_admin, self.get_users_domain_dn("testuser1")) + self.delete_force(self.ldb_admin, self.get_users_domain_dn("testuser2")) + self.delete_force(self.ldb_admin, self.get_users_domain_dn("testuser3")) + self.delete_force(self.ldb_admin, self.get_users_domain_dn("testuser4")) + self.delete_force(self.ldb_admin, self.get_users_domain_dn("testuser5")) + self.delete_force(self.ldb_admin, self.get_users_domain_dn("testuser6")) + self.delete_force(self.ldb_admin, self.get_users_domain_dn("testuser7")) + self.delete_force(self.ldb_admin, self.get_users_domain_dn("testuser8")) + # DOMAIN + self.delete_force(self.ldb_admin, self.get_users_domain_dn("test_domain_group1")) + self.delete_force(self.ldb_admin, "CN=test_domain_user1,OU=test_domain_ou1," + self.base_dn) + self.delete_force(self.ldb_admin, "OU=test_domain_ou2,OU=test_domain_ou1," + self.base_dn) + self.delete_force(self.ldb_admin, "OU=test_domain_ou1," + self.base_dn) + # SCHEMA + # CONFIGURATION + self.delete_force(self.ldb_admin, "CN=test-specifier1,CN=test-container1,CN=DisplaySpecifiers," \ + + self.configuration_dn) + self.delete_force(self.ldb_admin, "CN=test-container1,CN=DisplaySpecifiers," + self.configuration_dn) + + def check_user_belongs(self, user_dn, groups=[]): + """ Test wether user is member of the expected group(s) """ + if groups != []: + # User is member of at least one additional group + res = self.ldb_admin.search(user_dn, attrs=["memberOf"]) + res = [x.upper() for x in sorted(list(res[0]["memberOf"]))] + expected = [] + for x in groups: + expected.append(self.get_users_domain_dn(x)) + expected = [x.upper() for x in sorted(expected)] + self.assertEqual(expected, res) + else: + # User is not a member of any additional groups but default + res = self.ldb_admin.search(user_dn, attrs=["*"]) + res = [x.upper() for x in res[0].keys()] + self.assertFalse( "MEMBEROF" in res) + + def test_100(self): + """ Enterprise admin group member creates object (default nTSecurityDescriptor) in DOMAIN + """ + user_name = "testuser1" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection("testuser1", "samba123@") + group_dn = "CN=test_domain_group1,CN=Users," + self.base_dn + self.delete_force(self.ldb_admin, group_dn) + self.create_domain_group(_ldb, group_dn) + desc_sddl = self.get_desc_sddl(group_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["100"], res) + + def test_101(self): + """ Dmain admin group member creates object (default nTSecurityDescriptor) in DOMAIN + """ + user_name = "testuser2" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Domain Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + group_dn = "CN=test_domain_group1,CN=Users," + self.base_dn + self.delete_force(self.ldb_admin, group_dn) + self.create_domain_group(_ldb, group_dn) + desc_sddl = self.get_desc_sddl(group_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["101"], res) + + def test_102(self): + """ Schema admin group member with CC right creates object (default nTSecurityDescriptor) in DOMAIN + """ + user_name = "testuser3" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + object_dn = "OU=test_domain_ou1," + self.base_dn + self.delete_force(self.ldb_admin, object_dn) + self.create_domain_ou(self.ldb_admin, object_dn) + user_sid = self.get_object_sid( self.get_users_domain_dn(user_name) ) + mod = "(A;;CC;;;%s)" % str(user_sid) + self.dacl_add_ace(object_dn, mod) + # Create additional object into the first one + object_dn = "CN=test_domain_user1," + object_dn + self.delete_force(self.ldb_admin, object_dn) + self.create_domain_user(_ldb, object_dn) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["102"] % str(user_sid), res) + + def test_103(self): + """ Regular user with CC right creates object (default nTSecurityDescriptor) in DOMAIN + """ + user_name = "testuser4" + self.check_user_belongs(self.get_users_domain_dn(user_name), []) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + object_dn = "OU=test_domain_ou1," + self.base_dn + self.delete_force(self.ldb_admin, object_dn) + self.create_domain_ou(self.ldb_admin, object_dn) + user_sid = self.get_object_sid( self.get_users_domain_dn(user_name) ) + mod = "(A;;CC;;;%s)" % str(user_sid) + self.dacl_add_ace(object_dn, mod) + # Create additional object into the first one + object_dn = "CN=test_domain_user1," + object_dn + self.delete_force(self.ldb_admin, object_dn) + self.create_domain_user(_ldb, object_dn) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["103"] % str(user_sid), res) + + def test_104(self): + """ Enterprise & Domain admin group member creates object (default nTSecurityDescriptor) in DOMAIN + """ + user_name = "testuser5" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins", "Domain Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + group_dn = "CN=test_domain_group1,CN=Users," + self.base_dn + self.delete_force(self.ldb_admin, group_dn) + self.create_domain_group(_ldb, group_dn) + desc_sddl = self.get_desc_sddl(group_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["104"], res) + + def test_105(self): + """ Enterprise & Domain & Schema admin group member creates object (default nTSecurityDescriptor) in DOMAIN + """ + user_name = "testuser6" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins", "Domain Admins", "Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + group_dn = "CN=test_domain_group1,CN=Users," + self.base_dn + self.delete_force(self.ldb_admin, group_dn) + self.create_domain_group(_ldb, group_dn) + desc_sddl = self.get_desc_sddl(group_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["105"], res) + + def test_106(self): + """ Domain & Schema admin group member creates object (default nTSecurityDescriptor) in DOMAIN + """ + user_name = "testuser7" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Domain Admins", "Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + group_dn = "CN=test_domain_group1,CN=Users," + self.base_dn + self.delete_force(self.ldb_admin, group_dn) + self.create_domain_group(_ldb, group_dn) + desc_sddl = self.get_desc_sddl(group_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["106"], res) + + def test_107(self): + """ Enterprise & Schema admin group member creates object (default nTSecurityDescriptor) in DOMAIN + """ + user_name = "testuser8" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins", "Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + group_dn = "CN=test_domain_group1,CN=Users," + self.base_dn + self.delete_force(self.ldb_admin, group_dn) + self.create_domain_group(_ldb, group_dn) + desc_sddl = self.get_desc_sddl(group_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["107"], res) + + # Control descriptor tests ##################################################################### + + def test_108(self): + """ Enterprise admin group member creates object (custom descriptor) in DOMAIN + """ + user_name = "testuser1" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + group_dn = "CN=test_domain_group1,CN=Users," + self.base_dn + self.delete_force(self.ldb_admin, group_dn) + # Create a custom security descriptor + desc_sddl = "O:DAG:DAD:(A;;RP;;;DU)" + self.create_domain_group(_ldb, group_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(group_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["108"], res) + + def test_109(self): + """ Domain admin group member creates object (custom descriptor) in DOMAIN + """ + user_name = "testuser2" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Domain Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + group_dn = "CN=test_domain_group1,CN=Users," + self.base_dn + self.delete_force(self.ldb_admin, group_dn) + # Create a custom security descriptor + desc_sddl = "O:DAG:DAD:(A;;RP;;;DU)" + self.create_domain_group(_ldb, group_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(group_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["109"], res) + + def test_110(self): + """ Schema admin group member with CC right creates object (custom descriptor) in DOMAIN + """ + user_name = "testuser3" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + object_dn = "OU=test_domain_ou1," + self.base_dn + self.delete_force(self.ldb_admin, object_dn) + self.create_domain_ou(self.ldb_admin, object_dn) + user_sid = self.get_object_sid( self.get_users_domain_dn(user_name) ) + mod = "(A;;CC;;;%s)" % str(user_sid) + self.dacl_add_ace(object_dn, mod) + # Create a custom security descriptor + # NB! Problematic owner part won't accept DA only <User Sid> !!! + desc_sddl = "O:%sG:DAD:(A;;RP;;;DU)" % str(user_sid) + # Create additional object into the first one + object_dn = "CN=test_domain_user1," + object_dn + self.delete_force(self.ldb_admin, object_dn) + self.create_domain_user(_ldb, object_dn, desc_sddl) + desc = self.read_desc(object_dn) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["110"] % str(user_sid), res) + + def test_111(self): + """ Regular user with CC right creates object (custom descriptor) in DOMAIN + """ + user_name = "testuser4" + self.check_user_belongs(self.get_users_domain_dn(user_name), []) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + object_dn = "OU=test_domain_ou1," + self.base_dn + self.delete_force(self.ldb_admin, object_dn) + self.create_domain_ou(self.ldb_admin, object_dn) + user_sid = self.get_object_sid( self.get_users_domain_dn(user_name) ) + mod = "(A;;CC;;;%s)" % str(user_sid) + self.dacl_add_ace(object_dn, mod) + # Create a custom security descriptor + # NB! Problematic owner part won't accept DA only <User Sid> !!! + desc_sddl = "O:%sG:DAD:(A;;RP;;;DU)" % str(user_sid) + # Create additional object into the first one + object_dn = "CN=test_domain_user1," + object_dn + self.delete_force(self.ldb_admin, object_dn) + self.create_domain_user(_ldb, object_dn, desc_sddl) + desc = self.read_desc(object_dn) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["111"] % str(user_sid), res) + + def test_112(self): + """ Domain & Enterprise admin group member creates object (custom descriptor) in DOMAIN + """ + user_name = "testuser5" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins", "Domain Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + group_dn = "CN=test_domain_group1,CN=Users," + self.base_dn + self.delete_force(self.ldb_admin, group_dn) + # Create a custom security descriptor + desc_sddl = "O:DAG:DAD:(A;;RP;;;DU)" + self.create_domain_group(_ldb, group_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(group_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["112"], res) + + def test_113(self): + """ Domain & Enterprise & Schema admin group member creates object (custom descriptor) in DOMAIN + """ + user_name = "testuser6" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins", "Domain Admins", "Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + group_dn = "CN=test_domain_group1,CN=Users," + self.base_dn + self.delete_force(self.ldb_admin, group_dn) + # Create a custom security descriptor + desc_sddl = "O:DAG:DAD:(A;;RP;;;DU)" + self.create_domain_group(_ldb, group_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(group_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["113"], res) + + def test_114(self): + """ Domain & Schema admin group member creates object (custom descriptor) in DOMAIN + """ + user_name = "testuser7" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Domain Admins", "Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + group_dn = "CN=test_domain_group1,CN=Users," + self.base_dn + self.delete_force(self.ldb_admin, group_dn) + # Create a custom security descriptor + desc_sddl = "O:DAG:DAD:(A;;RP;;;DU)" + self.create_domain_group(_ldb, group_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(group_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["114"], res) + + def test_115(self): + """ Enterprise & Schema admin group member creates object (custom descriptor) in DOMAIN + """ + user_name = "testuser8" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins", "Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + group_dn = "CN=test_domain_group1,CN=Users," + self.base_dn + self.delete_force(self.ldb_admin, group_dn) + # Create a custom security descriptor + desc_sddl = "O:DAG:DAD:(A;;RP;;;DU)" + self.create_domain_group(_ldb, group_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(group_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["115"], res) + + + def test_999(self): + user_name = "Administrator" + object_dn = "OU=test_domain_ou1," + self.base_dn + self.delete_force(self.ldb_admin, object_dn) + self.create_domain_ou(self.ldb_admin, object_dn) + user_sid = self.get_object_sid( self.get_users_domain_dn(user_name) ) + mod = "(D;CI;WP;;;S-1-3-0)" + #mod = "" + self.dacl_add_ace(object_dn, mod) + desc_sddl = self.get_desc_sddl(object_dn) + # Create additional object into the first one + object_dn = "OU=test_domain_ou2," + object_dn + self.delete_force(self.ldb_admin, object_dn) + self.create_domain_ou(self.ldb_admin, object_dn) + desc_sddl = self.get_desc_sddl(object_dn) + + ## Tests for SCHEMA + + # Defalt descriptor tests ################################################################## + + def test_130(self): + user_name = "testuser1" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Change Schema partition descriptor + user_sid = self.get_object_sid( self.get_users_domain_dn(user_name) ) + mod = "(A;;CC;;;AU)" + self.dacl_add_ace(self.schema_dn, mod) + # Create example Schema class + class_name = self.get_unique_schema_class_name() + class_dn = "CN=%s,%s" % (class_name, self.schema_dn) + self.create_schema_class(_ldb, class_dn) + desc_sddl = self.get_desc_sddl(class_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["130"], res) + + def test_131(self): + user_name = "testuser2" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Domain Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Change Schema partition descriptor + mod = "(A;;CC;;;AU)" + self.dacl_add_ace(self.schema_dn, mod) + # Create example Schema class + class_name = self.get_unique_schema_class_name() + class_dn = "CN=%s,%s" % (class_name, self.schema_dn) + self.create_schema_class(_ldb, class_dn) + desc_sddl = self.get_desc_sddl(class_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["131"], res) + + def test_132(self): + user_name = "testuser3" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Change Schema partition descriptor + mod = "(A;;CC;;;AU)" + self.dacl_add_ace(self.schema_dn, mod) + # Create example Schema class + class_name = self.get_unique_schema_class_name() + class_dn = "CN=%s,%s" % (class_name, self.schema_dn) + self.create_schema_class(_ldb, class_dn) + desc_sddl = self.get_desc_sddl(class_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["132"], res) + + def test_133(self): + user_name = "testuser4" + self.check_user_belongs(self.get_users_domain_dn(user_name), []) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + #Change Schema partition descriptor + user_sid = self.get_object_sid( self.get_users_domain_dn(user_name) ) + mod = "(A;;CC;;;AU)" + self.dacl_add_ace(self.schema_dn, mod) + # Create example Schema class + class_name = self.get_unique_schema_class_name() + class_dn = "CN=%s,%s" % (class_name, self.schema_dn) + self.create_schema_class(_ldb, class_dn) + desc_sddl = self.get_desc_sddl(class_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["133"] % str(user_sid), res) + + def test_134(self): + user_name = "testuser5" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins", "Domain Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + #Change Schema partition descriptor + mod = "(A;;CC;;;AU)" + self.dacl_add_ace(self.schema_dn, mod) + # Create example Schema class + class_name = self.get_unique_schema_class_name() + class_dn = "CN=%s,%s" % (class_name, self.schema_dn) + self.create_schema_class(_ldb, class_dn) + desc_sddl = self.get_desc_sddl(class_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["134"], res) + + def test_135(self): + user_name = "testuser6" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins", "Domain Admins", "Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Change Schema partition descriptor + mod = "(A;;CC;;;AU)" + self.dacl_add_ace(self.schema_dn, mod) + # Create example Schema class + class_name = self.get_unique_schema_class_name() + class_dn = "CN=%s,%s" % (class_name, self.schema_dn) + self.create_schema_class(_ldb, class_dn) + desc_sddl = self.get_desc_sddl(class_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["135"], res) + + def test_136(self): + user_name = "testuser7" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Domain Admins", "Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Change Schema partition descriptor + mod = "(A;;CC;;;AU)" + self.dacl_add_ace(self.schema_dn, mod) + # Create example Schema class + class_name = self.get_unique_schema_class_name() + class_dn = "CN=%s,%s" % (class_name, self.schema_dn) + self.create_schema_class(_ldb, class_dn) + desc_sddl = self.get_desc_sddl(class_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["136"], res) + + def test_137(self): + user_name = "testuser8" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins", "Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Change Schema partition descriptor + mod = "(A;;CC;;;AU)" + self.dacl_add_ace(self.schema_dn, mod) + # Create example Schema class + class_name = self.get_unique_schema_class_name() + class_dn = "CN=%s,%s" % (class_name, self.schema_dn) + self.create_schema_class(_ldb, class_dn) + desc_sddl = self.get_desc_sddl(class_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["137"], res) + + # Custom descriptor tests ################################################################## + + def test_138(self): + user_name = "testuser1" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Change Schema partition descriptor + mod = "(A;;CC;;;AU)" + self.dacl_add_ace(self.schema_dn, mod) + # Create a custom security descriptor + desc_sddl = "O:DAG:DAD:(A;;RP;;;DU)" + # Create example Schema class + class_name = self.get_unique_schema_class_name() + class_dn = "CN=%s,%s" % (class_name, self.schema_dn) + self.create_schema_class(_ldb, class_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(class_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual("O:DAG:DA", res) + + def test_139(self): + user_name = "testuser2" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Domain Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Change Schema partition descriptor + mod = "(A;;CC;;;AU)" + self.dacl_add_ace(self.schema_dn, mod) + # Create a custom security descriptor + desc_sddl = "O:DAG:DAD:(A;;RP;;;DU)" + # Create example Schema class + class_name = self.get_unique_schema_class_name() + class_dn = "CN=%s,%s" % (class_name, self.schema_dn) + self.create_schema_class(_ldb, class_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(class_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual("O:DAG:DA", res) + + def test_140(self): + user_name = "testuser3" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Create a custom security descriptor + # NB! Problematic owner part won't accept DA only <User Sid> !!! + user_sid = self.get_object_sid( self.get_users_domain_dn(user_name) ) + desc_sddl = "O:%sG:DAD:(A;;RP;;;DU)" % str(user_sid) + # Create example Schema class + class_name = self.get_unique_schema_class_name() + class_dn = "CN=%s,%s" % (class_name, self.schema_dn) + self.create_schema_class(_ldb, class_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(class_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["140"] % str(user_sid), res) + + def test_141(self): + user_name = "testuser4" + self.check_user_belongs(self.get_users_domain_dn(user_name), []) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Create a custom security descriptor + # NB! Problematic owner part won't accept DA only <User Sid> !!! + user_sid = self.get_object_sid( self.get_users_domain_dn(user_name) ) + desc_sddl = "O:%sG:DAD:(A;;RP;;;DU)" % str(user_sid) + # Create example Schema class + class_name = self.get_unique_schema_class_name() + class_dn = "CN=%s,%s" % (class_name, self.schema_dn) + self.create_schema_class(_ldb, class_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(class_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["141"] % str(user_sid), res) + + def test_142(self): + user_name = "testuser5" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins", "Domain Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Change Schema partition descriptor + mod = "(A;;CC;;;AU)" + self.dacl_add_ace(self.schema_dn, mod) + # Create a custom security descriptor + desc_sddl = "O:DAG:DAD:(A;;RP;;;DU)" + # Create example Schema class + class_name = self.get_unique_schema_class_name() + class_dn = "CN=%s,%s" % (class_name, self.schema_dn) + self.create_schema_class(_ldb, class_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(class_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual("O:DAG:DA", res) + + def test_143(self): + user_name = "testuser6" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins", "Domain Admins", "Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Change Schema partition descriptor + mod = "(A;;CC;;;AU)" + self.dacl_add_ace(self.schema_dn, mod) + # Create a custom security descriptor + desc_sddl = "O:DAG:DAD:(A;;RP;;;DU)" + # Create example Schema class + class_name = self.get_unique_schema_class_name() + class_dn = "CN=%s,%s" % (class_name, self.schema_dn) + self.create_schema_class(_ldb, class_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(class_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual("O:DAG:DA", res) + + def test_144(self): + user_name = "testuser7" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Domain Admins", "Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Change Schema partition descriptor + mod = "(A;;CC;;;AU)" + self.dacl_add_ace(self.schema_dn, mod) + # Create a custom security descriptor + desc_sddl = "O:DAG:DAD:(A;;RP;;;DU)" + # Create example Schema class + class_name = self.get_unique_schema_class_name() + class_dn = "CN=%s,%s" % (class_name, self.schema_dn) + self.create_schema_class(_ldb, class_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(class_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual("O:DAG:DA", res) + + def test_145(self): + user_name = "testuser8" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins", "Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Change Schema partition descriptor + mod = "(A;;CC;;;AU)" + self.dacl_add_ace(self.schema_dn, mod) + # Create a custom security descriptor + desc_sddl = "O:DAG:DAD:(A;;RP;;;DU)" + # Create example Schema class + class_name = self.get_unique_schema_class_name() + class_dn = "CN=%s,%s" % (class_name, self.schema_dn) + self.create_schema_class(_ldb, class_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(class_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual("O:DAG:DA", res) + + ## Tests for CONFIGURATION + + # Defalt descriptor tests ################################################################## + + def test_160(self): + user_name = "testuser1" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Create example Configuration container + container_name = "test-container1" + object_dn = "CN=%s,CN=DisplaySpecifiers,%s" % (container_name, self.configuration_dn) + self.delete_force(self.ldb_admin, object_dn) + self.create_configuration_container(_ldb, object_dn, ) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["160"], res) + + def test_161(self): + user_name = "testuser2" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Domain Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Create example Configuration container + container_name = "test-container1" + object_dn = "CN=%s,CN=DisplaySpecifiers,%s" % (container_name, self.configuration_dn) + self.delete_force(self.ldb_admin, object_dn) + self.create_configuration_container(_ldb, object_dn, ) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["161"], res) + + def test_162(self): + user_name = "testuser3" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Create example Configuration container + object_dn = "CN=test-container1,CN=DisplaySpecifiers," + self.configuration_dn + self.delete_force(self.ldb_admin, object_dn) + self.create_configuration_container(self.ldb_admin, object_dn, ) + user_sid = self.get_object_sid( self.get_users_domain_dn(user_name) ) + mod = "(A;;CC;;;AU)" + self.dacl_add_ace(object_dn, mod) + # Create child object with user's credentials + object_dn = "CN=test-specifier1," + object_dn + self.delete_force(self.ldb_admin, object_dn) + self.create_configuration_specifier(_ldb, object_dn) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["162"] % str(user_sid), res) + + def test_163(self): + user_name = "testuser4" + self.check_user_belongs(self.get_users_domain_dn(user_name), []) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Create example Configuration container + object_dn = "CN=test-container1,CN=DisplaySpecifiers," + self.configuration_dn + self.delete_force(self.ldb_admin, object_dn) + self.create_configuration_container(self.ldb_admin, object_dn, ) + user_sid = self.get_object_sid( self.get_users_domain_dn(user_name) ) + mod = "(A;;CC;;;AU)" + self.dacl_add_ace(object_dn, mod) + # Create child object with user's credentials + object_dn = "CN=test-specifier1," + object_dn + self.delete_force(self.ldb_admin, object_dn) + self.create_configuration_specifier(_ldb, object_dn) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["163"] % str(user_sid), res) + + def test_164(self): + user_name = "testuser5" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins", "Domain Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Create example Configuration container + container_name = "test-container1" + object_dn = "CN=%s,CN=DisplaySpecifiers,%s" % (container_name, self.configuration_dn) + self.delete_force(self.ldb_admin, object_dn) + self.create_configuration_container(_ldb, object_dn, ) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["164"], res) + + def test_165(self): + user_name = "testuser6" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins", "Domain Admins", "Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Create example Configuration container + container_name = "test-container1" + object_dn = "CN=%s,CN=DisplaySpecifiers,%s" % (container_name, self.configuration_dn) + self.delete_force(self.ldb_admin, object_dn) + self.create_configuration_container(_ldb, object_dn, ) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["165"], res) + + def test_166(self): + user_name = "testuser7" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Domain Admins", "Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Create example Configuration container + container_name = "test-container1" + object_dn = "CN=%s,CN=DisplaySpecifiers,%s" % (container_name, self.configuration_dn) + self.delete_force(self.ldb_admin, object_dn) + self.create_configuration_container(_ldb, object_dn, ) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["166"], res) + + def test_167(self): + user_name = "testuser8" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins", "Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Create example Configuration container + container_name = "test-container1" + object_dn = "CN=%s,CN=DisplaySpecifiers,%s" % (container_name, self.configuration_dn) + self.delete_force(self.ldb_admin, object_dn) + self.create_configuration_container(_ldb, object_dn, ) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["167"], res) + + # Custom descriptor tests ################################################################## + + def test_168(self): + user_name = "testuser1" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Create example Configuration container + container_name = "test-container1" + object_dn = "CN=%s,CN=DisplaySpecifiers,%s" % (container_name, self.configuration_dn) + self.delete_force(self.ldb_admin, object_dn) + # Create a custom security descriptor + desc_sddl = "O:DAG:DAD:(A;;RP;;;DU)" + self.create_configuration_container(_ldb, object_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual("O:DAG:DA", res) + + def test_169(self): + user_name = "testuser2" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Domain Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Create example Configuration container + container_name = "test-container1" + object_dn = "CN=%s,CN=DisplaySpecifiers,%s" % (container_name, self.configuration_dn) + self.delete_force(self.ldb_admin, object_dn) + # Create a custom security descriptor + desc_sddl = "O:DAG:DAD:(A;;RP;;;DU)" + self.create_configuration_container(_ldb, object_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual("O:DAG:DA", res) + + def test_170(self): + user_name = "testuser3" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Create example Configuration container + object_dn = "CN=test-container1,CN=DisplaySpecifiers," + self.configuration_dn + self.delete_force(self.ldb_admin, object_dn) + self.create_configuration_container(self.ldb_admin, object_dn, ) + user_sid = self.get_object_sid( self.get_users_domain_dn(user_name) ) + mod = "(A;;CC;;;AU)" + self.dacl_add_ace(object_dn, mod) + # Create child object with user's credentials + object_dn = "CN=test-specifier1," + object_dn + self.delete_force(self.ldb_admin, object_dn) + # Create a custom security descriptor + # NB! Problematic owner part won't accept DA only <User Sid> !!! + desc_sddl = "O:%sG:DAD:(A;;RP;;;DU)" % str(user_sid) + self.create_configuration_specifier(_ldb, object_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["170"] % str(user_sid), res) + + def test_171(self): + user_name = "testuser4" + self.check_user_belongs(self.get_users_domain_dn(user_name), []) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Create example Configuration container + object_dn = "CN=test-container1,CN=DisplaySpecifiers," + self.configuration_dn + self.delete_force(self.ldb_admin, object_dn) + self.create_configuration_container(self.ldb_admin, object_dn, ) + user_sid = self.get_object_sid( self.get_users_domain_dn(user_name) ) + mod = "(A;;CC;;;AU)" + self.dacl_add_ace(object_dn, mod) + # Create child object with user's credentials + object_dn = "CN=test-specifier1," + object_dn + self.delete_force(self.ldb_admin, object_dn) + # Create a custom security descriptor + # NB! Problematic owner part won't accept DA only <User Sid> !!! + desc_sddl = "O:%sG:DAD:(A;;RP;;;DU)" % str(user_sid) + self.create_configuration_specifier(_ldb, object_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual(self.results[self.DS_BEHAVIOR]["171"] % str(user_sid), res) + + def test_172(self): + user_name = "testuser5" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins", "Domain Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Create example Configuration container + container_name = "test-container1" + object_dn = "CN=%s,CN=DisplaySpecifiers,%s" % (container_name, self.configuration_dn) + self.delete_force(self.ldb_admin, object_dn) + # Create a custom security descriptor + desc_sddl = "O:DAG:DAD:(A;;RP;;;DU)" + self.create_configuration_container(_ldb, object_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual("O:DAG:DA", res) + + def test_173(self): + user_name = "testuser6" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins", "Domain Admins", "Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Create example Configuration container + container_name = "test-container1" + object_dn = "CN=%s,CN=DisplaySpecifiers,%s" % (container_name, self.configuration_dn) + self.delete_force(self.ldb_admin, object_dn) + # Create a custom security descriptor + desc_sddl = "O:DAG:DAD:(A;;RP;;;DU)" + self.create_configuration_container(_ldb, object_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual("O:DAG:DA", res) + + def test_174(self): + user_name = "testuser7" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Domain Admins", "Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Create example Configuration container + container_name = "test-container1" + object_dn = "CN=%s,CN=DisplaySpecifiers,%s" % (container_name, self.configuration_dn) + self.delete_force(self.ldb_admin, object_dn) + # Create a custom security descriptor + desc_sddl = "O:DAG:DAD:(A;;RP;;;DU)" + self.create_configuration_container(_ldb, object_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual("O:DAG:DA", res) + + def test_175(self): + user_name = "testuser8" + self.check_user_belongs(self.get_users_domain_dn(user_name), ["Enterprise Admins", "Schema Admins"]) + # Open Ldb connection with the tested user + _ldb = self.get_ldb_connection(user_name, "samba123@") + # Create example Configuration container + container_name = "test-container1" + object_dn = "CN=%s,CN=DisplaySpecifiers,%s" % (container_name, self.configuration_dn) + self.delete_force(self.ldb_admin, object_dn) + # Create a custom security descriptor + desc_sddl = "O:DAG:DAD:(A;;RP;;;DU)" + self.create_configuration_container(_ldb, object_dn, desc_sddl) + desc_sddl = self.get_desc_sddl(object_dn) + res = re.search("(O:.*G:.*?)D:", desc_sddl).group(1) + self.assertEqual("O:DAG:DA", res) + + ######################################################################################## + # Inharitance tests for DACL + +class DaclDescriptorTests(DescriptorTests): + + def setUp(self): + DescriptorTests.setUp(self) + + def tearDown(self): + self.delete_force(self.ldb_admin, "CN=test_inherit_group,OU=test_inherit_ou," + self.base_dn) + self.delete_force(self.ldb_admin, "OU=test_inherit_ou," + self.base_dn) + + def create_clean_ou(self, object_dn): + """ Base repeating setup for unittests to follow """ + res = self.ldb_admin.search(base=self.base_dn, scope=SCOPE_SUBTREE, \ + expression="distinguishedName=%s" % object_dn) + # Make sure top testing OU has been deleted before starting the test + self.assertEqual(res, []) + self.create_domain_ou(self.ldb_admin, object_dn) + desc_sddl = self.get_desc_sddl(object_dn) + # Make sutre there are inheritable ACEs initially + self.assertTrue("CI" in desc_sddl or "OI" in desc_sddl) + # Find and remove all inherit ACEs + res = re.findall("\(.*?\)", desc_sddl) + res = [x for x in res if ("CI" in x) or ("OI" in x)] + for x in res: + desc_sddl = desc_sddl.replace(x, "") + # Add flag 'protected' in both DACL and SACL so no inherit ACEs + # can propagate from above + desc_sddl = desc_sddl.replace(":AI", ":AIP") + # colon at the end breaks ldif parsing, fix it + res = re.findall(".*?S:", desc_sddl) + if res: + desc_sddl = desc_sddl.replace("S:", "") + self.modify_desc(object_dn, desc_sddl) + # Verify all inheritable ACEs are gone + desc_sddl = self.get_desc_sddl(object_dn) + self.assertFalse("CI" in desc_sddl) + self.assertFalse("OI" in desc_sddl) + + def test_200(self): + """ OU with protected flag and child group. See if the group has inherit ACEs. + """ + ou_dn = "OU=test_inherit_ou," + self.base_dn + group_dn = "CN=test_inherit_group," + ou_dn + # Create inheritable-free OU + self.create_clean_ou(ou_dn) + # Create group child object + self.create_domain_group(self.ldb_admin, group_dn) + # Make sure created group object contains NO inherit ACEs + desc_sddl = self.get_desc_sddl(group_dn) + self.assertFalse("ID" in desc_sddl) + + def test_201(self): + """ OU with protected flag and no inherit ACEs, child group with custom descriptor. + Verify group has custom and default ACEs only. + """ + ou_dn = "OU=test_inherit_ou," + self.base_dn + group_dn = "CN=test_inherit_group," + ou_dn + # Create inheritable-free OU + self.create_clean_ou(ou_dn) + # Create group child object using custom security descriptor + sddl = "O:AUG:AUD:AI(D;;WP;;;DU)" + self.create_domain_group(self.ldb_admin, group_dn, sddl) + # Make sure created group descriptor has NO additional ACEs + desc_sddl = self.get_desc_sddl(group_dn) + print "group descriptor: " + desc_sddl + self.assertEqual(desc_sddl, sddl) + + def test_202(self): + """ OU with protected flag and add couple non-inheritable ACEs, child group. + See if the group has any of the added ACEs. + """ + ou_dn = "OU=test_inherit_ou," + self.base_dn + group_dn = "CN=test_inherit_group," + ou_dn + # Create inheritable-free OU + self.create_clean_ou(ou_dn) + # Add some custom non-inheritable ACEs + mod = "(D;;WP;;;DU)(A;;RP;;;DU)" + self.dacl_add_ace(ou_dn, mod) + # Verify all inheritable ACEs are gone + desc_sddl = self.get_desc_sddl(ou_dn) + # Create group child object + self.create_domain_group(self.ldb_admin, group_dn) + # Make sure created group object contains NO inherit ACEs + # also make sure the added above non-inheritable ACEs are absant too + desc_sddl = self.get_desc_sddl(group_dn) + self.assertFalse("ID" in desc_sddl) + for x in re.findall("\(.*?\)", mod): + self.assertFalse(x in desc_sddl) + + def test_203(self): + """ OU with protected flag and add 'CI' ACE, child group. + See if the group has the added inherited ACE. + """ + ou_dn = "OU=test_inherit_ou," + self.base_dn + group_dn = "CN=test_inherit_group," + ou_dn + # Create inheritable-free OU + self.create_clean_ou(ou_dn) + # Add some custom 'CI' ACE + mod = "(D;CI;WP;;;DU)" + self.dacl_add_ace(ou_dn, mod) + desc_sddl = self.get_desc_sddl(ou_dn) + # Create group child object + self.create_domain_group(self.ldb_admin, group_dn, "O:AUG:AUD:AI(A;;CC;;;AU)") + # Make sure created group object contains only the above inherited ACE + # that we've added manually + desc_sddl = self.get_desc_sddl(group_dn) + mod = mod.replace(";CI;", ";CIID;") + self.assertTrue(mod in desc_sddl) + + def test_204(self): + """ OU with protected flag and add 'OI' ACE, child group. + See if the group has the added inherited ACE. + """ + ou_dn = "OU=test_inherit_ou," + self.base_dn + group_dn = "CN=test_inherit_group," + ou_dn + # Create inheritable-free OU + self.create_clean_ou(ou_dn) + # Add some custom 'CI' ACE + mod = "(D;OI;WP;;;DU)" + self.dacl_add_ace(ou_dn, mod) + desc_sddl = self.get_desc_sddl(ou_dn) + # Create group child object + self.create_domain_group(self.ldb_admin, group_dn, "O:AUG:AUD:AI(A;;CC;;;AU)") + # Make sure created group object contains only the above inherited ACE + # that we've added manually + desc_sddl = self.get_desc_sddl(group_dn) + mod = mod.replace(";OI;", ";OIIOID;") # change it how it's gonna look like + self.assertTrue(mod in desc_sddl) + + def test_205(self): + """ OU with protected flag and add 'OA' for GUID & 'CI' ACE, child group. + See if the group has the added inherited ACE. + """ + ou_dn = "OU=test_inherit_ou," + self.base_dn + group_dn = "CN=test_inherit_group," + ou_dn + # Create inheritable-free OU + self.create_clean_ou(ou_dn) + # Add some custom 'OA' for 'name' attribute & 'CI' ACE + mod = "(OA;CI;WP;bf967a0e-0de6-11d0-a285-00aa003049e2;;DU)" + self.dacl_add_ace(ou_dn, mod) + desc_sddl = self.get_desc_sddl(ou_dn) + # Create group child object + self.create_domain_group(self.ldb_admin, group_dn, "O:AUG:AUD:AI(A;;CC;;;AU)") + # Make sure created group object contains only the above inherited ACE + # that we've added manually + desc_sddl = self.get_desc_sddl(group_dn) + mod = mod.replace(";CI;", ";CIID;") # change it how it's gonna look like + self.assertTrue(mod in desc_sddl) + + def test_206(self): + """ OU with protected flag and add 'OA' for GUID & 'OI' ACE, child group. + See if the group has the added inherited ACE. + """ + ou_dn = "OU=test_inherit_ou," + self.base_dn + group_dn = "CN=test_inherit_group," + ou_dn + # Create inheritable-free OU + self.create_clean_ou(ou_dn) + # Add some custom 'OA' for 'name' attribute & 'OI' ACE + mod = "(OA;OI;WP;bf967a0e-0de6-11d0-a285-00aa003049e2;;DU)" + self.dacl_add_ace(ou_dn, mod) + desc_sddl = self.get_desc_sddl(ou_dn) + # Create group child object + self.create_domain_group(self.ldb_admin, group_dn, "O:AUG:AUD:AI(A;;CC;;;AU)") + # Make sure created group object contains only the above inherited ACE + # that we've added manually + desc_sddl = self.get_desc_sddl(group_dn) + mod = mod.replace(";OI;", ";OIIOID;") # change it how it's gonna look like + self.assertTrue(mod in desc_sddl) + + def test_207(self): + """ OU with protected flag and add 'OA' for OU specific GUID & 'CI' ACE, child group. + See if the group has the added inherited ACE. + """ + ou_dn = "OU=test_inherit_ou," + self.base_dn + group_dn = "CN=test_inherit_group," + ou_dn + # Create inheritable-free OU + self.create_clean_ou(ou_dn) + # Add some custom 'OA' for 'st' attribute (OU specific) & 'CI' ACE + mod = "(OA;CI;WP;bf967a39-0de6-11d0-a285-00aa003049e2;;DU)" + self.dacl_add_ace(ou_dn, mod) + desc_sddl = self.get_desc_sddl(ou_dn) + # Create group child object + self.create_domain_group(self.ldb_admin, group_dn, "O:AUG:AUD:AI(A;;CC;;;AU)") + # Make sure created group object contains only the above inherited ACE + # that we've added manually + desc_sddl = self.get_desc_sddl(group_dn) + mod = mod.replace(";CI;", ";CIID;") # change it how it's gonna look like + self.assertTrue(mod in desc_sddl) + + def test_208(self): + """ OU with protected flag and add 'OA' for OU specific GUID & 'OI' ACE, child group. + See if the group has the added inherited ACE. + """ + ou_dn = "OU=test_inherit_ou," + self.base_dn + group_dn = "CN=test_inherit_group," + ou_dn + # Create inheritable-free OU + self.create_clean_ou(ou_dn) + # Add some custom 'OA' for 'st' attribute (OU specific) & 'OI' ACE + mod = "(OA;OI;WP;bf967a39-0de6-11d0-a285-00aa003049e2;;DU)" + self.dacl_add_ace(ou_dn, mod) + desc_sddl = self.get_desc_sddl(ou_dn) + # Create group child object + self.create_domain_group(self.ldb_admin, group_dn, "O:AUG:AUD:AI(A;;CC;;;AU)") + # Make sure created group object contains only the above inherited ACE + # that we've added manually + desc_sddl = self.get_desc_sddl(group_dn) + mod = mod.replace(";OI;", ";OIIOID;") # change it how it's gonna look like + self.assertTrue(mod in desc_sddl) + + def test_209(self): + """ OU with protected flag and add 'CI' ACE with 'CO' SID, child group. + See if the group has the added inherited ACE. + """ + ou_dn = "OU=test_inherit_ou," + self.base_dn + group_dn = "CN=test_inherit_group," + ou_dn + # Create inheritable-free OU + self.create_clean_ou(ou_dn) + # Add some custom 'CI' ACE + mod = "(D;CI;WP;;;CO)" + self.dacl_add_ace(ou_dn, mod) + desc_sddl = self.get_desc_sddl(ou_dn) + # Create group child object + self.create_domain_group(self.ldb_admin, group_dn, "O:AUG:AUD:AI(A;;CC;;;AU)") + # Make sure created group object contains only the above inherited ACE(s) + # that we've added manually + desc_sddl = self.get_desc_sddl(group_dn) + self.assertTrue("(D;ID;WP;;;AU)" in desc_sddl) + self.assertTrue("(D;CIIOID;WP;;;CO)" in desc_sddl) + + ######################################################################################## + +if not "://" in host: + host = "ldap://%s" % host +ldb = Ldb(host, credentials=creds, session_info=system_session(), lp=lp, options=["modules:paged_searches"]) + +runner = SubunitTestRunner() +rc = 0 +if not runner.run(unittest.makeSuite(OwnerGroupDescriptorTests)).wasSuccessful(): + rc = 1 +if not runner.run(unittest.makeSuite(DaclDescriptorTests)).wasSuccessful(): + rc = 1 + +sys.exit(rc) diff --git a/source4/lib/ldb/tools/cmdline.c b/source4/lib/ldb/tools/cmdline.c index 2701de5a48..73bf2a93a7 100644 --- a/source4/lib/ldb/tools/cmdline.c +++ b/source4/lib/ldb/tools/cmdline.c @@ -35,7 +35,45 @@ #include "param/param.h" #endif +static struct ldb_cmdline options; /* needs to be static for older compilers */ + +static struct poptOption popt_options[] = { + POPT_AUTOHELP + { "url", 'H', POPT_ARG_STRING, &options.url, 0, "database URL", "URL" }, + { "basedn", 'b', POPT_ARG_STRING, &options.basedn, 0, "base DN", "DN" }, + { "editor", 'e', POPT_ARG_STRING, &options.editor, 0, "external editor", "PROGRAM" }, + { "scope", 's', POPT_ARG_STRING, NULL, 's', "search scope", "SCOPE" }, + { "verbose", 'v', POPT_ARG_NONE, NULL, 'v', "increase verbosity", NULL }, + { "trace", 0, POPT_ARG_NONE, &options.tracing, 0, "enable tracing", NULL }, + { "interactive", 'i', POPT_ARG_NONE, &options.interactive, 0, "input from stdin", NULL }, + { "recursive", 'r', POPT_ARG_NONE, &options.recursive, 0, "recursive delete", NULL }, + { "modules-path", 0, POPT_ARG_STRING, &options.modules_path, 0, "modules path", "PATH" }, + { "num-searches", 0, POPT_ARG_INT, &options.num_searches, 0, "number of test searches", NULL }, + { "num-records", 0, POPT_ARG_INT, &options.num_records, 0, "number of test records", NULL }, + { "all", 'a', POPT_ARG_NONE, &options.all_records, 0, "(|(objectClass=*)(distinguishedName=*))", NULL }, + { "nosync", 0, POPT_ARG_NONE, &options.nosync, 0, "non-synchronous transactions", NULL }, + { "sorted", 'S', POPT_ARG_NONE, &options.sorted, 0, "sort attributes", NULL }, + { "input", 'I', POPT_ARG_STRING, &options.input, 0, "Input File", "Input" }, + { "output", 'O', POPT_ARG_STRING, &options.output, 0, "Output File", "Output" }, + { NULL, 'o', POPT_ARG_STRING, NULL, 'o', "ldb_connect option", "OPTION" }, + { "controls", 0, POPT_ARG_STRING, NULL, 'c', "controls", NULL }, + { "show-binary", 0, POPT_ARG_NONE, &options.show_binary, 0, "display binary LDIF", NULL }, +#if (_SAMBA_BUILD_ >= 4) + POPT_COMMON_SAMBA + POPT_COMMON_CREDENTIALS + POPT_COMMON_CONNECTION + POPT_COMMON_VERSION +#endif + { NULL } +}; +void ldb_cmdline_help(const char *cmdname, FILE *f) +{ + poptContext pc; + pc = poptGetContext(cmdname, 0, NULL, popt_options, + POPT_CONTEXT_KEEP_FIRST); + poptPrintHelp(pc, f, 0); +} /** process command line options @@ -44,7 +82,6 @@ struct ldb_cmdline *ldb_cmdline_process(struct ldb_context *ldb, int argc, const char **argv, void (*usage)(void)) { - static struct ldb_cmdline options; /* needs to be static for older compilers */ struct ldb_cmdline *ret=NULL; poptContext pc; #if (_SAMBA_BUILD_ >= 4) @@ -54,34 +91,6 @@ struct ldb_cmdline *ldb_cmdline_process(struct ldb_context *ldb, int opt; int flags = 0; - struct poptOption popt_options[] = { - POPT_AUTOHELP - { "url", 'H', POPT_ARG_STRING, &options.url, 0, "database URL", "URL" }, - { "basedn", 'b', POPT_ARG_STRING, &options.basedn, 0, "base DN", "DN" }, - { "editor", 'e', POPT_ARG_STRING, &options.editor, 0, "external editor", "PROGRAM" }, - { "scope", 's', POPT_ARG_STRING, NULL, 's', "search scope", "SCOPE" }, - { "verbose", 'v', POPT_ARG_NONE, NULL, 'v', "increase verbosity", NULL }, - { "interactive", 'i', POPT_ARG_NONE, &options.interactive, 0, "input from stdin", NULL }, - { "recursive", 'r', POPT_ARG_NONE, &options.recursive, 0, "recursive delete", NULL }, - { "modules-path", 0, POPT_ARG_STRING, &options.modules_path, 0, "modules path", "PATH" }, - { "num-searches", 0, POPT_ARG_INT, &options.num_searches, 0, "number of test searches", NULL }, - { "num-records", 0, POPT_ARG_INT, &options.num_records, 0, "number of test records", NULL }, - { "all", 'a', POPT_ARG_NONE, &options.all_records, 0, "(|(objectClass=*)(distinguishedName=*))", NULL }, - { "nosync", 0, POPT_ARG_NONE, &options.nosync, 0, "non-synchronous transactions", NULL }, - { "sorted", 'S', POPT_ARG_NONE, &options.sorted, 0, "sort attributes", NULL }, - { "input", 'I', POPT_ARG_STRING, &options.input, 0, "Input File", "Input" }, - { "output", 'O', POPT_ARG_STRING, &options.output, 0, "Output File", "Output" }, - { NULL, 'o', POPT_ARG_STRING, NULL, 'o', "ldb_connect option", "OPTION" }, - { "controls", 0, POPT_ARG_STRING, NULL, 'c', "controls", NULL }, -#if (_SAMBA_BUILD_ >= 4) - POPT_COMMON_SAMBA - POPT_COMMON_CREDENTIALS - POPT_COMMON_CONNECTION - POPT_COMMON_VERSION -#endif - { NULL } - }; - #if (_SAMBA_BUILD_ >= 4) r = ldb_register_samba_handlers(ldb); if (r != 0) { @@ -208,6 +217,14 @@ struct ldb_cmdline *ldb_cmdline_process(struct ldb_context *ldb, flags |= LDB_FLG_NOSYNC; } + if (options.show_binary) { + flags |= LDB_FLG_SHOW_BINARY; + } + + if (options.tracing) { + flags |= LDB_FLG_ENABLE_TRACING; + } + #if (_SAMBA_BUILD_ >= 4) /* Must be after we have processed command line options */ gensec_init(cmdline_lp_ctx); diff --git a/source4/lib/ldb/tools/cmdline.h b/source4/lib/ldb/tools/cmdline.h index 45619ce496..28061a5a7d 100644 --- a/source4/lib/ldb/tools/cmdline.h +++ b/source4/lib/ldb/tools/cmdline.h @@ -44,6 +44,8 @@ struct ldb_cmdline { const char *input; const char *output; char **controls; + int show_binary; + int tracing; }; struct ldb_cmdline *ldb_cmdline_process(struct ldb_context *ldb, int argc, const char **argv, @@ -51,3 +53,5 @@ struct ldb_cmdline *ldb_cmdline_process(struct ldb_context *ldb, int argc, const int handle_controls_reply(struct ldb_control **reply, struct ldb_control **request); +void ldb_cmdline_help(const char *cmdname, FILE *f); + diff --git a/source4/lib/ldb/tools/ldbadd.c b/source4/lib/ldb/tools/ldbadd.c index be02334797..f022486db8 100644 --- a/source4/lib/ldb/tools/ldbadd.c +++ b/source4/lib/ldb/tools/ldbadd.c @@ -38,13 +38,9 @@ static int failures; static void usage(void) { - printf("Usage: ldbadd <options> <ldif...>\n"); - printf("Options:\n"); - printf(" -H ldb_url choose the database (or $LDB_URL)\n"); - printf(" -o options pass options like modules to activate\n"); - printf(" e.g: -o modules:timestamps\n"); - printf("\n"); + printf("Usage: ldbadd <options> <ldif...>\n"); printf("Adds records to a ldb, reading ldif the specified list of files\n\n"); + ldb_cmdline_help("ldbadd", stdout); exit(1); } @@ -113,9 +109,13 @@ int main(int argc, const char **argv) } } - if (count != 0 && ldb_transaction_commit(ldb) != 0) { - printf("Failed to commit transaction: %s\n", ldb_errstring(ldb)); - exit(1); + if (count != 0) { + if (ldb_transaction_commit(ldb) != 0) { + printf("Failed to commit transaction: %s\n", ldb_errstring(ldb)); + exit(1); + } + } else { + ldb_transaction_cancel(ldb); } talloc_free(ldb); diff --git a/source4/lib/ldb/tools/ldbdel.c b/source4/lib/ldb/tools/ldbdel.c index 232f51681a..ddf168d574 100644 --- a/source4/lib/ldb/tools/ldbdel.c +++ b/source4/lib/ldb/tools/ldbdel.c @@ -61,13 +61,8 @@ static int ldb_delete_recursive(struct ldb_context *ldb, struct ldb_dn *dn) static void usage(void) { printf("Usage: ldbdel <options> <DN...>\n"); - printf("Options:\n"); - printf(" -r recursively delete the given subtree\n"); - printf(" -H ldb_url choose the database (or $LDB_URL)\n"); - printf(" -o options pass options like modules to activate\n"); - printf(" e.g: -o modules:timestamps\n"); - printf("\n"); printf("Deletes records from a ldb\n\n"); + ldb_cmdline_help("ldbdel", stdout); exit(1); } diff --git a/source4/lib/ldb/tools/ldbedit.c b/source4/lib/ldb/tools/ldbedit.c index 9d3bd27983..ecadf0f61c 100644 --- a/source4/lib/ldb/tools/ldbedit.c +++ b/source4/lib/ldb/tools/ldbedit.c @@ -128,6 +128,7 @@ static int merge_edits(struct ldb_context *ldb, fprintf(stderr, "failed to add %s - %s\n", ldb_dn_get_linearized(msgs2[i]->dn), ldb_errstring(ldb)); + ldb_transaction_cancel(ldb); return -1; } adds++; @@ -149,6 +150,7 @@ static int merge_edits(struct ldb_context *ldb, fprintf(stderr, "failed to delete %s - %s\n", ldb_dn_get_linearized(msgs1[i]->dn), ldb_errstring(ldb)); + ldb_transaction_cancel(ldb); return -1; } deletes++; @@ -270,13 +272,7 @@ static int do_edit(struct ldb_context *ldb, struct ldb_message **msgs1, int coun static void usage(void) { printf("Usage: ldbedit <options> <expression> <attributes ...>\n"); - printf("Options:\n"); - printf(" -H ldb_url choose the database (or $LDB_URL)\n"); - printf(" -s base|sub|one choose search scope\n"); - printf(" -b basedn choose baseDN\n"); - printf(" -a edit all records (expression 'objectclass=*')\n"); - printf(" -e editor choose editor (or $VISUAL or $EDITOR)\n"); - printf(" -v verbose mode\n"); + ldb_cmdline_help("ldbedit", stdout); exit(1); } diff --git a/source4/lib/ldb/tools/ldbmodify.c b/source4/lib/ldb/tools/ldbmodify.c index c3f55c6096..d0bca0479b 100644 --- a/source4/lib/ldb/tools/ldbmodify.c +++ b/source4/lib/ldb/tools/ldbmodify.c @@ -39,12 +39,8 @@ static int failures; static void usage(void) { printf("Usage: ldbmodify <options> <ldif...>\n"); - printf("Options:\n"); - printf(" -H ldb_url choose the database (or $LDB_URL)\n"); - printf(" -o options pass options like modules to activate\n"); - printf(" e.g: -o modules:timestamps\n"); - printf("\n"); printf("Modifies a ldb based upon ldif change records\n\n"); + ldb_cmdline_help("ldbmodify", stdout); exit(1); } @@ -105,6 +101,7 @@ int main(int argc, const char **argv) exit(1); } ret = process_file(ldb, f, &count); + fclose(f); } } diff --git a/source4/lib/ldb/tools/ldbrename.c b/source4/lib/ldb/tools/ldbrename.c index 01ed3d9835..fcae766a20 100644 --- a/source4/lib/ldb/tools/ldbrename.c +++ b/source4/lib/ldb/tools/ldbrename.c @@ -39,12 +39,8 @@ static void usage(void) { printf("Usage: ldbrename [<options>] <olddn> <newdn>\n"); - printf("Options:\n"); - printf(" -H ldb_url choose the database (or $LDB_URL)\n"); - printf(" -o options pass options like modules to activate\n"); - printf(" e.g: -o modules:timestamps\n"); - printf("\n"); printf("Renames records in a ldb\n\n"); + ldb_cmdline_help("ldbmodify", stdout); exit(1); } diff --git a/source4/lib/ldb/tools/ldbsearch.c b/source4/lib/ldb/tools/ldbsearch.c index ba0a2a8927..8f7ee1ce38 100644 --- a/source4/lib/ldb/tools/ldbsearch.c +++ b/source4/lib/ldb/tools/ldbsearch.c @@ -38,14 +38,7 @@ static void usage(void) { printf("Usage: ldbsearch <options> <expression> <attrs...>\n"); - printf("Options:\n"); - printf(" -H ldb_url choose the database (or $LDB_URL)\n"); - printf(" -s base|sub|one choose search scope\n"); - printf(" -b basedn choose baseDN\n"); - printf(" -i read search expressions from stdin\n"); - printf(" -S sort returned attributes\n"); - printf(" -o options pass options like modules to activate\n"); - printf(" e.g: -o modules:timestamps\n"); + ldb_cmdline_help("ldbsearch", stdout); exit(1); } |