diff options
Diffstat (limited to 'source4/lib/ldb')
24 files changed, 971 insertions, 200 deletions
diff --git a/source4/lib/ldb/common/attrib_handlers.c b/source4/lib/ldb/common/attrib_handlers.c index 1c08741f7d..464707530e 100644 --- a/source4/lib/ldb/common/attrib_handlers.c +++ b/source4/lib/ldb/common/attrib_handlers.c @@ -100,6 +100,27 @@ int ldb_handler_fold(struct ldb_context *ldb, void *mem_ctx, return 0; } +/* length limited conversion of a ldb_val to a int32_t */ +static int val_to_int64(const struct ldb_val *in, int64_t *v) +{ + char *end; + char buf[64]; + + /* make sure we don't read past the end of the data */ + if (in->length > sizeof(buf)-1) { + return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; + } + strncpy(buf, (char *)in->data, in->length); + buf[in->length] = 0; + + /* We've to use "strtoll" here to have the intended overflows. + * Otherwise we may get "LONG_MAX" and the conversion is wrong. */ + *v = (int64_t) strtoll(buf, &end, 0); + if (*end != 0) { + return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; + } + return LDB_SUCCESS; +} /* @@ -109,14 +130,17 @@ int ldb_handler_fold(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; - long long i = strtoll((char *)in->data, &end, 0); - if (*end != 0) { - return -1; + int64_t i; + int ret; + + ret = val_to_int64(in, &i); + if (ret != LDB_SUCCESS) { + return ret; } - out->data = (uint8_t *)talloc_asprintf(mem_ctx, "%lld", i); + out->data = (uint8_t *) talloc_asprintf(mem_ctx, "%lld", (long long)i); if (out->data == NULL) { - return -1; + ldb_oom(ldb); + return LDB_ERR_OPERATIONS_ERROR; } out->length = strlen((char *)out->data); return 0; @@ -128,7 +152,11 @@ static int ldb_canonicalise_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); + int64_t i1=0, i2=0; + val_to_int64(v1, &i1); + val_to_int64(v2, &i2); + if (i1 == i2) return 0; + return i1 > i2? 1 : -1; } /* @@ -338,10 +366,11 @@ static int ldb_comparison_dn(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; - t1 = ldb_string_to_time((char *)v1->data); - t2 = ldb_string_to_time((char *)v2->data); - return (int)t2 - (int)t1; + time_t t1=0, t2=0; + ldb_val_to_time(v1, &t1); + ldb_val_to_time(v2, &t2); + if (t1 == t2) return 0; + return t1 > t2? 1 : -1; } /* @@ -350,10 +379,16 @@ static int ldb_comparison_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); + time_t t; + int ret; + ret = ldb_val_to_time(in, &t); + if (ret != LDB_SUCCESS) { + return ret; + } out->data = (uint8_t *)ldb_timestring(mem_ctx, t); if (out->data == NULL) { - return -1; + ldb_oom(ldb); + return LDB_ERR_OPERATIONS_ERROR; } out->length = strlen((char *)out->data); return 0; diff --git a/source4/lib/ldb/common/ldb_modules.c b/source4/lib/ldb/common/ldb_modules.c index e79f072d50..3b8934702a 100644 --- a/source4/lib/ldb/common/ldb_modules.c +++ b/source4/lib/ldb/common/ldb_modules.c @@ -527,6 +527,11 @@ struct ldb_context *ldb_module_get_ctx(struct ldb_module *module) return module->ldb; } +const struct ldb_module_ops *ldb_module_get_ops(struct ldb_module *module) +{ + return module->ops; +} + void *ldb_module_get_private(struct ldb_module *module) { return module->private_data; @@ -601,7 +606,7 @@ int ldb_next_request(struct ldb_module *module, struct ldb_request *request) * all our modules, and leaves us one less sharp * corner for module developers to cut themselves on */ - ldb_module_done(request, NULL, NULL, ret); + ret = ldb_module_done(request, NULL, NULL, ret); } return ret; } @@ -824,8 +829,7 @@ int ldb_module_done(struct ldb_request *req, ldb_debug_end(req->handle->ldb, LDB_DEBUG_TRACE); } - req->callback(req, ares); - return error; + return req->callback(req, ares); } /* to be used *only* in modules init functions. diff --git a/source4/lib/ldb/common/ldb_msg.c b/source4/lib/ldb/common/ldb_msg.c index fbf49fbb23..9b33d7e351 100644 --- a/source4/lib/ldb/common/ldb_msg.c +++ b/source4/lib/ldb/common/ldb_msg.c @@ -832,6 +832,33 @@ time_t ldb_string_to_time(const char *s) } /* + convert a LDAP GeneralizedTime string in ldb_val format to a + time_t. +*/ +int ldb_val_to_time(const struct ldb_val *v, time_t *t) +{ + struct tm tm; + + if (v == NULL || !v->data || v->length < 14) { + return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; + } + + memset(&tm, 0, sizeof(tm)); + + if (sscanf((char *)v->data, "%04u%02u%02u%02u%02u%02u", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, + &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { + return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; + } + tm.tm_year -= 1900; + tm.tm_mon -= 1; + + *t = timegm(&tm); + + return LDB_SUCCESS; +} + +/* return a LDAP formatted UTCTime string */ char *ldb_timestring_utc(TALLOC_CTX *mem_ctx, time_t t) diff --git a/source4/lib/ldb/config.mk b/source4/lib/ldb/config.mk index 4a1f814baa..7d110fc618 100644 --- a/source4/lib/ldb/config.mk +++ b/source4/lib/ldb/config.mk @@ -11,6 +11,19 @@ ldb_asq_OBJ_FILES = $(ldbsrcdir)/modules/asq.o ################################################ ################################################ +# Start MODULE sample_module +[MODULE::sample] +PRIVATE_DEPENDENCIES = LIBTALLOC LIBTEVENT +CFLAGS = -I$(ldbsrcdir)/include +INIT_FUNCTION = LDB_MODULE(sample) +SUBSYSTEM = LIBTESTLDB + +# End MODULE sample_module +################################################ +sample_OBJ_FILES = $(ldbsrcdir)/tests/sample_module.o + + +################################################ # Start MODULE ldb_server_sort [MODULE::ldb_server_sort] PRIVATE_DEPENDENCIES = LIBTALLOC LIBTEVENT diff --git a/source4/lib/ldb/external/libtalloc.m4 b/source4/lib/ldb/external/libtalloc.m4 index 8c63fcc041..dfccaf4fb7 100644 --- a/source4/lib/ldb/external/libtalloc.m4 +++ b/source4/lib/ldb/external/libtalloc.m4 @@ -2,7 +2,7 @@ AC_SUBST(TALLOC_OBJ) AC_SUBST(TALLOC_CFLAGS) AC_SUBST(TALLOC_LIBS) -PKG_CHECK_MODULES(TALLOC, talloc >= 2.0.0, +PKG_CHECK_MODULES(TALLOC, talloc >= 2.0.1, [ ], [ AC_CHECK_HEADER(talloc.h, [ AC_CHECK_LIB(talloc, talloc_init, [TALLOC_LIBS="-ltalloc"])])]) diff --git a/source4/lib/ldb/include/ldb.h b/source4/lib/ldb/include/ldb.h index c8bfa24832..1958fd740b 100644 --- a/source4/lib/ldb/include/ldb.h +++ b/source4/lib/ldb/include/ldb.h @@ -1951,6 +1951,12 @@ char *ldb_timestring(TALLOC_CTX *mem_ctx, time_t t); time_t ldb_string_to_time(const char *s); /** + convert a LDAP GeneralizedTime string in ldb_val format to a + time_t. +*/ +int ldb_val_to_time(const struct ldb_val *v, time_t *t); + +/** Convert a time structure to a string This function converts a time_t structure to an LDAP formatted diff --git a/source4/lib/ldb/include/ldb_module.h b/source4/lib/ldb/include/ldb_module.h index 0b0f863fec..2d14634ca8 100644 --- a/source4/lib/ldb/include/ldb_module.h +++ b/source4/lib/ldb/include/ldb_module.h @@ -140,6 +140,7 @@ const char * ldb_module_get_name(struct ldb_module *module); struct ldb_context *ldb_module_get_ctx(struct ldb_module *module); void *ldb_module_get_private(struct ldb_module *module); void ldb_module_set_private(struct ldb_module *module, void *private_data); +const struct ldb_module_ops *ldb_module_get_ops(struct ldb_module *module); int ldb_next_request(struct ldb_module *module, struct ldb_request *request); int ldb_next_start_trans(struct ldb_module *module); diff --git a/source4/lib/ldb/ldb.mk b/source4/lib/ldb/ldb.mk index e87db64574..0c9b115672 100644 --- a/source4/lib/ldb/ldb.mk +++ b/source4/lib/ldb/ldb.mk @@ -34,26 +34,26 @@ lib/libldb.a: $(OBJS) sample.$(SHLIBEXT): tests/sample_module.o $(MDLD) $(MDLD_FLAGS) -o $@ tests/sample_module.o -bin/ldbadd: tools/ldbadd.o tools/cmdline.o - $(CC) -o bin/ldbadd tools/ldbadd.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) +bin/ldbadd: tools/ldbadd.o tools/cmdline.o tools/ldbutil.o + $(CC) -o bin/ldbadd tools/ldbadd.o tools/cmdline.o tools/ldbutil.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) -bin/ldbsearch: tools/ldbsearch.o tools/cmdline.o - $(CC) -o bin/ldbsearch tools/ldbsearch.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) +bin/ldbsearch: tools/ldbsearch.o tools/cmdline.o tools/ldbutil.o + $(CC) -o bin/ldbsearch tools/ldbsearch.o tools/cmdline.o tools/ldbutil.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) -bin/ldbdel: tools/ldbdel.o tools/cmdline.o - $(CC) -o bin/ldbdel tools/ldbdel.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) +bin/ldbdel: tools/ldbdel.o tools/cmdline.o tools/ldbutil.o + $(CC) -o bin/ldbdel tools/ldbdel.o tools/cmdline.o tools/ldbutil.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) -bin/ldbmodify: tools/ldbmodify.o tools/cmdline.o - $(CC) -o bin/ldbmodify tools/ldbmodify.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) +bin/ldbmodify: tools/ldbmodify.o tools/cmdline.o tools/ldbutil.o + $(CC) -o bin/ldbmodify tools/ldbmodify.o tools/cmdline.o tools/ldbutil.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) -bin/ldbedit: tools/ldbedit.o tools/cmdline.o - $(CC) -o bin/ldbedit tools/ldbedit.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) +bin/ldbedit: tools/ldbedit.o tools/cmdline.o tools/ldbutil.o + $(CC) -o bin/ldbedit tools/ldbedit.o tools/cmdline.o tools/ldbutil.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) -bin/ldbrename: tools/ldbrename.o tools/cmdline.o - $(CC) -o bin/ldbrename tools/ldbrename.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) +bin/ldbrename: tools/ldbrename.o tools/cmdline.o tools/ldbutil.o + $(CC) -o bin/ldbrename tools/ldbrename.o tools/cmdline.o tools/ldbutil.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) -bin/ldbtest: tools/ldbtest.o tools/cmdline.o - $(CC) -o bin/ldbtest tools/ldbtest.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) +bin/ldbtest: tools/ldbtest.o tools/cmdline.o + $(CC) -o bin/ldbtest tools/ldbtest.o tools/cmdline.o tools/ldbutil.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC) examples/ldbreader: examples/ldbreader.o $(CC) -o examples/ldbreader examples/ldbreader.o $(LIB_FLAGS) diff --git a/source4/lib/ldb/ldb_tdb/ldb_cache.c b/source4/lib/ldb/ldb_tdb/ldb_cache.c index cd2249d4a2..aa19f75e9c 100644 --- a/source4/lib/ldb/ldb_tdb/ldb_cache.c +++ b/source4/lib/ldb/ldb_tdb/ldb_cache.c @@ -463,7 +463,7 @@ int ltdb_increase_sequence_number(struct ldb_module *module) val_time.data = (uint8_t *)s; val_time.length = strlen(s); - ret = ltdb_modify_internal(module, msg); + ret = ltdb_modify_internal(module, msg, NULL); talloc_free(msg); diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.c b/source4/lib/ldb/ldb_tdb/ldb_tdb.c index 227a202a6f..a146b96b20 100644 --- a/source4/lib/ldb/ldb_tdb/ldb_tdb.c +++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.c @@ -592,9 +592,12 @@ static int msg_delete_element(struct ldb_module *module, yuck - this is O(n^2). Luckily n is usually small so we probably get away with it, but if we ever have really large attribute lists then we'll need to look at this again + + 'req' is optional, and is used to specify controls if supplied */ int ltdb_modify_internal(struct ldb_module *module, - const struct ldb_message *msg) + const struct ldb_message *msg, + struct ldb_request *req) { struct ldb_context *ldb = ldb_module_get_ctx(module); void *data = ldb_module_get_private(module); @@ -731,7 +734,15 @@ int ltdb_modify_internal(struct ldb_module *module, case LDB_FLAG_MOD_REPLACE: if (a && a->flags & LDB_ATTR_FLAG_SINGLE_VALUE) { - if (el->num_values > 1) { + /* the RELAX control overrides this + check for replace. This is needed as + DRS replication can produce multiple + values here for a single valued + attribute when the values are deleted + links + */ + if (el->num_values > 1 && + (!req || !ldb_request_get_control(req, LDB_CONTROL_RELAX_OID))) { ldb_asprintf_errstring(ldb, "SINGLE-VALUE attribute %s on %s specified more than once", el->name, ldb_dn_get_linearized(msg2->dn)); ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; @@ -852,7 +863,7 @@ static int ltdb_modify(struct ltdb_context *ctx) return LDB_ERR_OPERATIONS_ERROR; } - ret = ltdb_modify_internal(module, req->op.mod.message); + ret = ltdb_modify_internal(module, req->op.mod.message, req); return ret; } diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.h b/source4/lib/ldb/ldb_tdb/ldb_tdb.h index bb4cb3f8b5..70b99c340c 100644 --- a/source4/lib/ldb/ldb_tdb/ldb_tdb.h +++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.h @@ -130,7 +130,7 @@ int ltdb_lock_read(struct ldb_module *module); int ltdb_unlock_read(struct ldb_module *module); struct TDB_DATA ltdb_key(struct ldb_module *module, struct ldb_dn *dn); int ltdb_store(struct ldb_module *module, const struct ldb_message *msg, int flgs); -int ltdb_modify_internal(struct ldb_module *module, const struct ldb_message *msg); +int ltdb_modify_internal(struct ldb_module *module, const struct ldb_message *msg, struct ldb_request *req); int ltdb_delete_noindex(struct ldb_module *module, struct ldb_dn *dn); int ltdb_err_map(enum TDB_ERROR tdb_code); diff --git a/source4/lib/ldb/pyldb.c b/source4/lib/ldb/pyldb.c index 0ba69e1c48..a19768d41b 100644 --- a/source4/lib/ldb/pyldb.c +++ b/source4/lib/ldb/pyldb.c @@ -61,6 +61,8 @@ PyAPI_DATA(PyTypeObject) PyLdb; PyAPI_DATA(PyTypeObject) PyLdbMessageElement; PyAPI_DATA(PyTypeObject) PyLdbTree; +static PyObject *PyLdb_FromLdbContext(struct ldb_context *ldb_ctx); + static PyObject *PyObject_FromLdbValue(struct ldb_context *ldb_ctx, struct ldb_message_element *el, struct ldb_val *val) @@ -1357,7 +1359,7 @@ static PySequenceMethods py_ldb_seq = { .sq_contains = (objobjproc)py_ldb_contains, }; -PyObject *PyLdb_FromLdbContext(struct ldb_context *ldb_ctx) +static PyObject *PyLdb_FromLdbContext(struct ldb_context *ldb_ctx) { PyLdbObject *ret; diff --git a/source4/lib/ldb/pyldb.h b/source4/lib/ldb/pyldb.h index a0954158bd..289159c5b3 100644 --- a/source4/lib/ldb/pyldb.h +++ b/source4/lib/ldb/pyldb.h @@ -35,7 +35,6 @@ typedef struct { TALLOC_CTX *mem_ctx; } PyLdbObject; -PyObject *PyLdb_FromLdbContext(struct ldb_context *ldb_ctx); #define PyLdb_AsLdbContext(pyobj) ((PyLdbObject *)pyobj)->ldb_ctx #define PyLdb_Check(ob) PyObject_TypeCheck(ob, &PyLdb) diff --git a/source4/lib/ldb/tests/python/ldap.py b/source4/lib/ldb/tests/python/ldap.py index 54b623a903..c90727d720 100755 --- a/source4/lib/ldb/tests/python/ldap.py +++ b/source4/lib/ldb/tests/python/ldap.py @@ -26,7 +26,7 @@ from ldb import ERR_NAMING_VIOLATION, ERR_CONSTRAINT_VIOLATION from ldb import ERR_UNDEFINED_ATTRIBUTE_TYPE from ldb import Message, MessageElement, Dn from ldb import FLAG_MOD_ADD, FLAG_MOD_REPLACE, FLAG_MOD_DELETE -from samba import Ldb, param, dom_sid_to_rid +from samba import Ldb, param from samba import UF_NORMAL_ACCOUNT, UF_TEMP_DUPLICATE_ACCOUNT from samba import UF_SERVER_TRUST_ACCOUNT, UF_WORKSTATION_TRUST_ACCOUNT from samba import UF_INTERDOMAIN_TRUST_ACCOUNT @@ -456,7 +456,7 @@ class BasicTests(unittest.TestCase): self.fail() except LdbError, (num, _): self.assertEquals(num, ERR_NAMING_VIOLATION) - + self.delete_force(self.ldb, "description=xyz,cn=users," + self.base_dn) self.ldb.add({ @@ -642,17 +642,17 @@ objectClass: container 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])) + group_rid_1 = security.dom_sid(ldb.schema_format_value("objectSID", + res1[0]["objectSID"][0])).split()[1] 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])) + group_rid_2 = security.dom_sid(ldb.schema_format_value("objectSID", + res1[0]["objectSID"][0])).split()[1] # Try to create a user with an invalid primary group - try: + try: ldb.add({ "dn": "cn=ldaptestuser,cn=users," + self.base_dn, "objectclass": ["user", "person"], @@ -704,7 +704,8 @@ objectClass: container # 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 + m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn, + FLAG_MOD_REPLACE, "member") ldb.modify(m) # Make group 1 primary @@ -724,7 +725,8 @@ objectClass: container # 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 + m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn, + FLAG_MOD_ADD, "member") try: ldb.modify(m) self.fail() @@ -746,7 +748,8 @@ objectClass: container # 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 + m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn, + FLAG_MOD_ADD, "member") ldb.modify(m) # Swap the groups @@ -833,7 +836,7 @@ objectClass: container self.assertTrue(len(res1) == 1) self.assertFalse("primaryGroupToken" in res1[0]) - res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn, + res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn, scope=SCOPE_BASE) self.assertTrue(len(res1) == 1) self.assertFalse("primaryGroupToken" in res1[0]) @@ -843,7 +846,7 @@ objectClass: container 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])) + rid = security.dom_sid(ldb.schema_format_value("objectSID", res1[0]["objectSID"][0])).split()[1] self.assertEquals(primary_group_token, rid) m = Message() @@ -1990,137 +1993,6 @@ class BaseDnTests(unittest.TestCase): self.assertTrue(res[0]["configurationNamingContext"][0] in ncs) self.assertTrue(res[0]["schemaNamingContext"][0] in ncs) -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, - attrs=["objectClasses", "attributeTypes", "dITContentRules"]) - self.assertEquals(len(res), 1) - self.assertTrue("dITContentRules" in res[0]) - self.assertTrue("objectClasses" in res[0]) - self.assertTrue("attributeTypes" in res[0]) - - 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, - 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""" - attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) - attr_ldap_display_name = attr_name.replace("-", "") - - ldif = """ -dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """ -objectClass: top -objectClass: attributeSchema -adminDescription: """ + attr_name + """ -adminDisplayName: """ + attr_name + """ -cn: """ + attr_name + """ -attributeId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9940 -attributeSyntax: 2.5.5.12 -omSyntax: 64 -instanceType: 4 -isSingleValued: TRUE -systemOnly: FALSE -""" - self.ldb.add_ldif(ldif) - - class_name = "test-Class" + time.strftime("%s", time.gmtime()) - class_ldap_display_name = class_name.replace("-", "") - - ldif = """ -dn: CN=%s,%s""" % (class_name, self.schema_dn) + """ -objectClass: top -objectClass: classSchema -adminDescription: """ + class_name + """ -adminDisplayName: """ + class_name + """ -cn: """ + class_name + """ -governsId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9939 -instanceType: 4 -objectClassCategory: 1 -subClassOf: organizationalPerson -systemFlags: 16 -rDNAttID: cn -systemMustContain: cn -systemMustContain: """ + attr_ldap_display_name + """ -systemOnly: FALSE -""" - self.ldb.add_ldif(ldif) - - ldif = """ -dn: -changetype: modify -add: schemaUpdateNow -schemaUpdateNow: 1 -""" - self.ldb.modify_ldif(ldif) - - object_name = "obj" + time.strftime("%s", time.gmtime()) - - 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 + """ -""" + attr_ldap_display_name + """: test -""" - self.ldb.add_ldif(ldif) - - # Search for created attribute - res = [] - res = self.ldb.search("cn=%s,%s" % (attr_name, self.schema_dn), scope=SCOPE_BASE, attrs=["*"]) - self.assertEquals(len(res), 1) - self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_display_name) - self.assertTrue("schemaIDGUID" in res[0]) - - # Search for created objectclass - res = [] - res = self.ldb.search("cn=%s,%s" % (class_name, self.schema_dn), scope=SCOPE_BASE, attrs=["*"]) - self.assertEquals(len(res), 1) - self.assertEquals(res[0]["lDAPDisplayName"][0], class_ldap_display_name) - self.assertEquals(res[0]["defaultObjectCategory"][0], res[0]["distinguishedName"][0]) - self.assertTrue("schemaIDGUID" in res[0]) - - # Search for created object - res = [] - res = self.ldb.search("cn=%s,cn=Users,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["*"]) - self.assertEquals(len(res), 1) - # Delete the object - self.delete_force(self.ldb, "cn=%s,cn=Users,%s" % (object_name, self.base_dn)) if not "://" in host: if os.path.isfile(host): @@ -2141,6 +2013,4 @@ if not runner.run(unittest.makeSuite(BaseDnTests)).wasSuccessful(): rc = 1 if not runner.run(unittest.makeSuite(BasicTests)).wasSuccessful(): rc = 1 -if not runner.run(unittest.makeSuite(SchemaTests)).wasSuccessful(): - rc = 1 sys.exit(rc) diff --git a/source4/lib/ldb/tests/python/ldap_schema.py b/source4/lib/ldb/tests/python/ldap_schema.py new file mode 100755 index 0000000000..0a31db82f7 --- /dev/null +++ b/source4/lib/ldb/tests/python/ldap_schema.py @@ -0,0 +1,500 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# This is a port of the original in testprogs/ejs/ldap.js + +import getopt +import optparse +import sys +import time +import random +import base64 +import os + +sys.path.append("bin/python") +sys.path.append("../lib/subunit/python") + +import samba.getopt as options + +from samba.auth import system_session +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 ldb import ERR_NO_SUCH_ATTRIBUTE, ERR_INSUFFICIENT_ACCESS_RIGHTS +from ldb import ERR_OBJECT_CLASS_VIOLATION, ERR_NOT_ALLOWED_ON_RDN +from ldb import ERR_NAMING_VIOLATION, ERR_CONSTRAINT_VIOLATION +from ldb import ERR_UNDEFINED_ATTRIBUTE_TYPE +from ldb import Message, MessageElement, Dn +from ldb import FLAG_MOD_ADD, FLAG_MOD_REPLACE, FLAG_MOD_DELETE +from samba import Ldb +from samba import UF_NORMAL_ACCOUNT, UF_TEMP_DUPLICATE_ACCOUNT +from samba import UF_SERVER_TRUST_ACCOUNT, UF_WORKSTATION_TRUST_ACCOUNT +from samba import UF_INTERDOMAIN_TRUST_ACCOUNT +from samba import UF_PASSWD_NOTREQD, UF_ACCOUNTDISABLE +from samba import GTYPE_SECURITY_BUILTIN_LOCAL_GROUP +from samba import GTYPE_SECURITY_GLOBAL_GROUP, GTYPE_SECURITY_DOMAIN_LOCAL_GROUP +from samba import GTYPE_SECURITY_UNIVERSAL_GROUP +from samba import GTYPE_DISTRIBUTION_GLOBAL_GROUP +from samba import GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP +from samba import GTYPE_DISTRIBUTION_UNIVERSAL_GROUP +from samba import ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST +from samba import ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP +from samba import ATYPE_SECURITY_UNIVERSAL_GROUP +from samba import ATYPE_DISTRIBUTION_GLOBAL_GROUP +from samba import ATYPE_DISTRIBUTION_LOCAL_GROUP +from samba import ATYPE_DISTRIBUTION_UNIVERSAL_GROUP +from samba import DS_DC_FUNCTION_2003 + +from subunit import SubunitTestRunner +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) +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) + + +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, + attrs=["objectClasses", "attributeTypes", "dITContentRules"]) + self.assertEquals(len(res), 1) + self.assertTrue("dITContentRules" in res[0]) + self.assertTrue("objectClasses" in res[0]) + self.assertTrue("attributeTypes" in res[0]) + + 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, + 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""" + attr_name = "test-Attr" + time.strftime("%s", time.gmtime()) + attr_ldap_display_name = attr_name.replace("-", "") + + ldif = """ +dn: CN=%s,%s""" % (attr_name, self.schema_dn) + """ +objectClass: top +objectClass: attributeSchema +adminDescription: """ + attr_name + """ +adminDisplayName: """ + attr_name + """ +cn: """ + attr_name + """ +attributeId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9940 +attributeSyntax: 2.5.5.12 +omSyntax: 64 +instanceType: 4 +isSingleValued: TRUE +systemOnly: FALSE +""" + self.ldb.add_ldif(ldif) + + class_name = "test-Class" + time.strftime("%s", time.gmtime()) + class_ldap_display_name = class_name.replace("-", "") + + ldif = """ +dn: CN=%s,%s""" % (class_name, self.schema_dn) + """ +objectClass: top +objectClass: classSchema +adminDescription: """ + class_name + """ +adminDisplayName: """ + class_name + """ +cn: """ + class_name + """ +governsId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9939 +instanceType: 4 +objectClassCategory: 1 +subClassOf: organizationalPerson +systemFlags: 16 +rDNAttID: cn +systemMustContain: cn +systemMustContain: """ + attr_ldap_display_name + """ +systemOnly: FALSE +""" + self.ldb.add_ldif(ldif) + + ldif = """ +dn: +changetype: modify +add: schemaUpdateNow +schemaUpdateNow: 1 +""" + self.ldb.modify_ldif(ldif) + + object_name = "obj" + time.strftime("%s", time.gmtime()) + + 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 + """ +""" + attr_ldap_display_name + """: test +""" + self.ldb.add_ldif(ldif) + + # Search for created attribute + res = [] + res = self.ldb.search("cn=%s,%s" % (attr_name, self.schema_dn), scope=SCOPE_BASE, attrs=["*"]) + self.assertEquals(len(res), 1) + self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_display_name) + self.assertTrue("schemaIDGUID" in res[0]) + + # Search for created objectclass + res = [] + res = self.ldb.search("cn=%s,%s" % (class_name, self.schema_dn), scope=SCOPE_BASE, attrs=["*"]) + self.assertEquals(len(res), 1) + self.assertEquals(res[0]["lDAPDisplayName"][0], class_ldap_display_name) + self.assertEquals(res[0]["defaultObjectCategory"][0], res[0]["distinguishedName"][0]) + self.assertTrue("schemaIDGUID" in res[0]) + + # Search for created object + res = [] + res = self.ldb.search("cn=%s,cn=Users,%s" % (object_name, self.base_dn), scope=SCOPE_BASE, attrs=["*"]) + self.assertEquals(len(res), 1) + # Delete the object + self.delete_force(self.ldb, "cn=%s,cn=Users,%s" % (object_name, self.base_dn)) + + +class SchemaTests_msDS_IntId(unittest.TestCase): + + def setUp(self): + self.ldb = ldb + res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["*"]) + self.assertEquals(len(res), 1) + self.schema_dn = res[0]["schemaNamingContext"][0] + self.base_dn = res[0]["defaultNamingContext"][0] + self.forest_level = int(res[0]["forestFunctionality"][0]) + + def _ldap_schemaUpdateNow(self): + ldif = """ +dn: +changetype: modify +add: schemaUpdateNow +schemaUpdateNow: 1 +""" + self.ldb.modify_ldif(ldif) + + def _make_obj_names(self, prefix): + class_name = prefix + time.strftime("%s", time.gmtime()) + class_ldap_name = class_name.replace("-", "") + class_dn = "CN=%s,%s" % (class_name, self.schema_dn) + return (class_name, class_ldap_name, class_dn) + + def _is_schema_base_object(self, ldb_msg): + """Test systemFlags for SYSTEM_FLAG_SCHEMA_BASE_OBJECT (16)""" + systemFlags = 0 + if "systemFlags" in ldb_msg: + systemFlags = int(ldb_msg["systemFlags"][0]) + return (systemFlags & 16) != 0 + + def _make_attr_ldif(self, attr_name, attr_dn): + ldif = """ +dn: """ + attr_dn + """ +objectClass: top +objectClass: attributeSchema +adminDescription: """ + attr_name + """ +adminDisplayName: """ + attr_name + """ +cn: """ + attr_name + """ +attributeId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9940 +attributeSyntax: 2.5.5.12 +omSyntax: 64 +instanceType: 4 +isSingleValued: TRUE +systemOnly: FALSE +""" + return ldif + + def test_msDS_IntId_on_attr(self): + """Testing msDs-IntId creation for Attributes. + See MS-ADTS - 3.1.1.Attributes + + This test should verify that: + - Creating attribute with 'msDS-IntId' fails with ERR_UNWILLING_TO_PERFORM + - Adding 'msDS-IntId' on existing attribute fails with ERR_CONSTRAINT_VIOLATION + - Creating attribute with 'msDS-IntId' set and FLAG_SCHEMA_BASE_OBJECT flag + set fails with ERR_UNWILLING_TO_PERFORM + - Attributes created with FLAG_SCHEMA_BASE_OBJECT not set have + 'msDS-IntId' attribute added internally + """ + + # 1. Create attribute without systemFlags + # msDS-IntId should be created if forest functional + # level is >= DS_DC_FUNCTION_2003 + # and missing otherwise + (attr_name, attr_ldap_name, attr_dn) = self._make_obj_names("msDS-IntId-Attr-1-") + ldif = self._make_attr_ldif(attr_name, attr_dn) + + # try to add msDS-IntId during Attribute creation + ldif_fail = ldif + "msDS-IntId: -1993108831\n" + try: + self.ldb.add_ldif(ldif_fail) + self.fail("Adding attribute with preset msDS-IntId should fail") + except LdbError, (num, _): + self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) + + # add the new attribute and update schema + self.ldb.add_ldif(ldif) + self._ldap_schemaUpdateNow() + + # Search for created attribute + res = [] + res = self.ldb.search(attr_dn, scope=SCOPE_BASE, attrs=["*"]) + self.assertEquals(len(res), 1) + self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_name) + if self.forest_level >= DS_DC_FUNCTION_2003: + if self._is_schema_base_object(res[0]): + self.assertTrue("msDS-IntId" not in res[0]) + else: + self.assertTrue("msDS-IntId" in res[0]) + else: + self.assertTrue("msDS-IntId" not in res[0]) + + msg = Message() + msg.dn = Dn(self.ldb, attr_dn) + msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId") + try: + self.ldb.modify(msg) + self.fail("Modifying msDS-IntId should return error") + except LdbError, (num, _): + self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) + + # 2. Create attribute with systemFlags = FLAG_SCHEMA_BASE_OBJECT + # msDS-IntId should be created if forest functional + # level is >= DS_DC_FUNCTION_2003 + # and missing otherwise + (attr_name, attr_ldap_name, attr_dn) = self._make_obj_names("msDS-IntId-Attr-2-") + ldif = self._make_attr_ldif(attr_name, attr_dn) + ldif += "systemFlags: 16\n" + + # try to add msDS-IntId during Attribute creation + ldif_fail = ldif + "msDS-IntId: -1993108831\n" + try: + self.ldb.add_ldif(ldif_fail) + self.fail("Adding attribute with preset msDS-IntId should fail") + except LdbError, (num, _): + self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) + + # add the new attribute and update schema + self.ldb.add_ldif(ldif) + self._ldap_schemaUpdateNow() + + # Search for created attribute + res = [] + res = self.ldb.search(attr_dn, scope=SCOPE_BASE, attrs=["*"]) + self.assertEquals(len(res), 1) + self.assertEquals(res[0]["lDAPDisplayName"][0], attr_ldap_name) + if self.forest_level >= DS_DC_FUNCTION_2003: + if self._is_schema_base_object(res[0]): + self.assertTrue("msDS-IntId" not in res[0]) + else: + self.assertTrue("msDS-IntId" in res[0]) + else: + self.assertTrue("msDS-IntId" not in res[0]) + + msg = Message() + msg.dn = Dn(self.ldb, attr_dn) + msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId") + try: + self.ldb.modify(msg) + self.fail("Modifying msDS-IntId should return error") + except LdbError, (num, _): + self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) + + + def _make_class_ldif(self, class_dn, class_name): + ldif = """ +dn: """ + class_dn + """ +objectClass: top +objectClass: classSchema +adminDescription: """ + class_name + """ +adminDisplayName: """ + class_name + """ +cn: """ + class_name + """ +governsId: 1.2.840.""" + str(random.randint(1,100000)) + """.1.5.9939 +instanceType: 4 +objectClassCategory: 1 +subClassOf: organizationalPerson +rDNAttID: cn +systemMustContain: cn +systemOnly: FALSE +""" + return ldif + + def test_msDS_IntId_on_class(self): + """Testing msDs-IntId creation for Class + Reference: MS-ADTS - 3.1.1.2.4.8 Class classSchema""" + + # 1. Create Class without systemFlags + # msDS-IntId should be created if forest functional + # level is >= DS_DC_FUNCTION_2003 + # and missing otherwise + (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-1-") + ldif = self._make_class_ldif(class_dn, class_name) + + # try to add msDS-IntId during Class creation + ldif_add = ldif + "msDS-IntId: -1993108831\n" + self.ldb.add_ldif(ldif_add) + self._ldap_schemaUpdateNow() + + res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["*"]) + self.assertEquals(len(res), 1) + self.assertEquals(res[0]["msDS-IntId"][0], "-1993108831") + + # add a new Class and update schema + (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-2-") + ldif = self._make_class_ldif(class_dn, class_name) + + self.ldb.add_ldif(ldif) + self._ldap_schemaUpdateNow() + + # Search for created Class + res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["*"]) + self.assertEquals(len(res), 1) + self.assertFalse("msDS-IntId" in res[0]) + + msg = Message() + msg.dn = Dn(self.ldb, class_dn) + msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId") + try: + self.ldb.modify(msg) + self.fail("Modifying msDS-IntId should return error") + except LdbError, (num, _): + self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) + + # 2. Create Class with systemFlags = FLAG_SCHEMA_BASE_OBJECT + # msDS-IntId should be created if forest functional + # level is >= DS_DC_FUNCTION_2003 + # and missing otherwise + (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-3-") + ldif = self._make_class_ldif(class_dn, class_name) + ldif += "systemFlags: 16\n" + + # try to add msDS-IntId during Class creation + ldif_add = ldif + "msDS-IntId: -1993108831\n" + self.ldb.add_ldif(ldif_add) + + res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["*"]) + self.assertEquals(len(res), 1) + self.assertEquals(res[0]["msDS-IntId"][0], "-1993108831") + + # add the new Class and update schema + (class_name, class_ldap_name, class_dn) = self._make_obj_names("msDS-IntId-Class-4-") + ldif = self._make_class_ldif(class_dn, class_name) + ldif += "systemFlags: 16\n" + + self.ldb.add_ldif(ldif) + self._ldap_schemaUpdateNow() + + # Search for created Class + res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["*"]) + self.assertEquals(len(res), 1) + self.assertFalse("msDS-IntId" in res[0]) + + msg = Message() + msg.dn = Dn(self.ldb, class_dn) + msg["msDS-IntId"] = MessageElement("-1993108831", FLAG_MOD_REPLACE, "msDS-IntId") + try: + self.ldb.modify(msg) + self.fail("Modifying msDS-IntId should return error") + except LdbError, (num, _): + self.assertEquals(num, ERR_CONSTRAINT_VIOLATION) + res = self.ldb.search(class_dn, scope=SCOPE_BASE, attrs=["*"]) + self.assertEquals(len(res), 1) + self.assertFalse("msDS-IntId" in res[0]) + + + def test_verify_msDS_IntId(self): + """Verify msDS-IntId exists only on attributes without FLAG_SCHEMA_BASE_OBJECT flag set""" + count = 0 + res = self.ldb.search(self.schema_dn, scope=SCOPE_ONELEVEL, + expression="objectClass=attributeSchema", + attrs=["systemFlags", "msDS-IntId", "attributeID", "cn"]) + self.assertTrue(len(res) > 1) + for ldb_msg in res: + if self.forest_level >= DS_DC_FUNCTION_2003: + if self._is_schema_base_object(ldb_msg): + self.assertTrue("msDS-IntId" not in ldb_msg) + else: + # don't assert here as there are plenty of + # attributes under w2k8 that are not part of + # Base Schema (SYSTEM_FLAG_SCHEMA_BASE_OBJECT flag not set) + # has not msDS-IntId attribute set + #self.assertTrue("msDS-IntId" in ldb_msg, "msDS-IntId expected on: %s" % ldb_msg.dn) + if "msDS-IntId" not in ldb_msg: + count = count + 1 + print "%3d warning: msDS-IntId expected on: %-30s %s" % (count, ldb_msg["attributeID"], ldb_msg["cn"]) + else: + self.assertTrue("msDS-IntId" not in ldb_msg) + + +if not "://" in host: + if os.path.isfile(host): + host = "tdb://%s" % host + else: + host = "ldap://%s" % host + +ldb_options = [] +if host.startswith("ldap://"): + # user 'paged_search' module when connecting remotely + ldb_options = ["modules:paged_searches"] + +ldb = Ldb(host, credentials=creds, session_info=system_session(), lp=lp, options=ldb_options) +if not "tdb://" in host: + gc_ldb = Ldb("%s:3268" % host, credentials=creds, + session_info=system_session(), lp=lp) +else: + gc_ldb = None + +runner = SubunitTestRunner() +rc = 0 +if not runner.run(unittest.makeSuite(SchemaTests)).wasSuccessful(): + rc = 1 +if not runner.run(unittest.makeSuite(SchemaTests_msDS_IntId)).wasSuccessful(): + rc = 1 +sys.exit(rc) diff --git a/source4/lib/ldb/tests/sample_module.c b/source4/lib/ldb/tests/sample_module.c index bbe4419b59..bb7906e7ba 100644 --- a/source4/lib/ldb/tests/sample_module.c +++ b/source4/lib/ldb/tests/sample_module.c @@ -25,12 +25,40 @@ int sample_add(struct ldb_module *mod, struct ldb_request *req) { + struct ldb_control *control; + struct ldb_control *controls; ldb_msg_add_fmt(req->op.add.message, "touchedBy", "sample"); - return ldb_next_request(mod, req); + + /* check if there's a relax control */ + control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID); + if (control == NULL) { + /* not found go on */ + return ldb_next_request(mod, req); + } else { + return LDB_ERR_UNWILLING_TO_PERFORM; + } +} + +int sample_modify(struct ldb_module *mod, struct ldb_request *req) +{ + struct ldb_control *control; + struct ldb_control *controls; + + /* check if there's a relax control */ + control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID); + if (control == NULL) { + /* not found go on */ + return ldb_next_request(mod, req); + } else { + return LDB_ERR_UNWILLING_TO_PERFORM; + } } + const struct ldb_module_ops ldb_sample_module_ops = { .name = "sample", .add = sample_add, + .del = sample_modify, + .modify = sample_modify, }; diff --git a/source4/lib/ldb/tests/test-controls.sh b/source4/lib/ldb/tests/test-controls.sh new file mode 100755 index 0000000000..db139bbec7 --- /dev/null +++ b/source4/lib/ldb/tests/test-controls.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +if [ -n "$TEST_DATA_PREFIX" ]; then + LDB_URL="$TEST_DATA_PREFIX/tdbtest.ldb" +else + LDB_URL="tdbtest.ldb" +fi +export LDB_URL + +PATH=bin:$PATH +export PATH + +rm -f $LDB_URL* +LDB_MODULES_PATH=`dirname $0`/../../../bin/modules/testldb +echo $LDB_MODULES_PATH + +echo "LDB_URL: $LDB_URL" +cat <<EOF | $VALGRIND ldbadd || exit 1 +dn: @MODULES +@LIST: sample +EOF + +cat <<EOF | $VALGRIND ldbadd || exit 1 +dn: dc=bar +dc: bar +someThing: someThingElse +EOF + +$VALGRIND ldbsearch "(touchedBy=sample)" | grep "touchedBy: sample" || exit 1 +# This action are expected to fails because the sample module return an error when presented the relax control + +cat <<EOF | $VALGRIND ldbadd --controls "relax:0" && exit 1 +dn: dc=foobar +dc: foobar +someThing: someThingElse +EOF + +cat <<EOF | $VALGRIND ldbmodify --controls "relax:0" && exit 1 +dn: dc=bar +changetype: replace +replace someThing +someThing: someThingElseBetter +EOF + + +set diff --git a/source4/lib/ldb/tests/test-tdb.sh b/source4/lib/ldb/tests/test-tdb.sh index 1c35451962..9da1e57060 100755 --- a/source4/lib/ldb/tests/test-tdb.sh +++ b/source4/lib/ldb/tests/test-tdb.sh @@ -29,3 +29,5 @@ $VALGRIND ldbadd$EXEEXT $LDBDIR/tests/init.ldif || exit 1 . $LDBDIR/tests/test-extended.sh . $LDBDIR/tests/test-tdb-features.sh + +. $LDBDIR/tests/test-controls.sh diff --git a/source4/lib/ldb/tools/config.mk b/source4/lib/ldb/tools/config.mk index 6b57929df0..f0d0e85e6f 100644 --- a/source4/lib/ldb/tools/config.mk +++ b/source4/lib/ldb/tools/config.mk @@ -1,4 +1,14 @@ ################################################ +# Start SUBSYSTEM LIBLDB_UTIL +[SUBSYSTEM::LIBLDB_UTIL] +CFLAGS = -I$(ldbsrcdir) -I$(ldbsrcdir)/include +PUBLIC_DEPENDENCIES = LIBLDB +# End SUBSYSTEM LIBLDB_UTIL +################################################ + +LIBLDB_UTIL_OBJ_FILES = $(ldbsrcdir)/tools/ldbutil.o + +################################################ # Start SUBSYSTEM LIBLDB_CMDLINE [SUBSYSTEM::LIBLDB_CMDLINE] CFLAGS = -I$(ldbsrcdir) -I$(ldbsrcdir)/include @@ -14,6 +24,7 @@ LIBLDB_CMDLINE_OBJ_FILES = $(ldbsrcdir)/tools/cmdline.o [BINARY::ldbadd] INSTALLDIR = BINDIR PRIVATE_DEPENDENCIES = \ + LIBLDB_UTIL \ LIBLDB_CMDLINE LIBCLI_RESOLVE # End BINARY ldbadd ################################################ @@ -28,6 +39,7 @@ MANPAGES += $(ldbsrcdir)/man/ldbadd.1 [BINARY::ldbdel] INSTALLDIR = BINDIR PRIVATE_DEPENDENCIES = \ + LIBLDB_UTIL \ LIBLDB_CMDLINE # End BINARY ldbdel ################################################ @@ -41,6 +53,7 @@ MANPAGES += $(ldbsrcdir)/man/ldbdel.1 [BINARY::ldbmodify] INSTALLDIR = BINDIR PRIVATE_DEPENDENCIES = \ + LIBLDB_UTIL \ LIBLDB_CMDLINE # End BINARY ldbmodify ################################################ diff --git a/source4/lib/ldb/tools/ldbadd.c b/source4/lib/ldb/tools/ldbadd.c index a87c99aaee..e618ab52f7 100644 --- a/source4/lib/ldb/tools/ldbadd.c +++ b/source4/lib/ldb/tools/ldbadd.c @@ -33,6 +33,7 @@ #include "ldb.h" #include "tools/cmdline.h" +#include "ldbutil.h" static int failures; static struct ldb_cmdline *options; @@ -53,6 +54,12 @@ static int process_file(struct ldb_context *ldb, FILE *f, int *count) { struct ldb_ldif *ldif; int ret = LDB_SUCCESS; + struct ldb_control **req_ctrls = ldb_parse_control_strings(ldb, ldb, (const char **)options->controls); + if (options->controls != NULL && req_ctrls== NULL) { + printf("parsing controls failed: %s\n", ldb_errstring(ldb)); + return -1; + } + while ((ldif = ldb_ldif_read_file(ldb, f))) { if (ldif->changetype != LDB_CHANGETYPE_ADD && @@ -63,10 +70,11 @@ static int process_file(struct ldb_context *ldb, FILE *f, int *count) ldif->msg = ldb_msg_canonicalize(ldb, ldif->msg); - ret = ldb_add(ldb, ldif->msg); + ret = ldb_add_ctrl(ldb, ldif->msg,req_ctrls); if (ret != LDB_SUCCESS) { - fprintf(stderr, "ERR: \"%s\" on DN %s\n", - ldb_errstring(ldb), ldb_dn_get_linearized(ldif->msg->dn)); + fprintf(stderr, "ERR: %s : \"%s\" on DN %s\n", + ldb_strerror(ret), ldb_errstring(ldb), + ldb_dn_get_linearized(ldif->msg->dn)); failures++; } else { (*count)++; diff --git a/source4/lib/ldb/tools/ldbdel.c b/source4/lib/ldb/tools/ldbdel.c index 5740f22503..6de15ee042 100644 --- a/source4/lib/ldb/tools/ldbdel.c +++ b/source4/lib/ldb/tools/ldbdel.c @@ -33,6 +33,7 @@ #include "ldb.h" #include "tools/cmdline.h" +#include "ldbutil.h" static int dn_cmp(const void *p1, const void *p2) { @@ -42,7 +43,7 @@ static int dn_cmp(const void *p1, const void *p2) return ldb_dn_compare(msg1->dn, msg2->dn); } -static int ldb_delete_recursive(struct ldb_context *ldb, struct ldb_dn *dn) +static int ldb_delete_recursive(struct ldb_context *ldb, struct ldb_dn *dn,struct ldb_control **req_ctrls) { int ret, i, total=0; const char *attrs[] = { NULL }; @@ -55,7 +56,7 @@ static int ldb_delete_recursive(struct ldb_context *ldb, struct ldb_dn *dn) qsort(res->msgs, res->count, sizeof(res->msgs[0]), dn_cmp); for (i = 0; i < res->count; i++) { - if (ldb_delete(ldb, res->msgs[i]->dn) == 0) { + if (ldb_delete_ctrl(ldb, res->msgs[i]->dn,req_ctrls) == 0) { total++; } else { printf("Failed to delete '%s' - %s\n", @@ -83,9 +84,10 @@ static void usage(void) int main(int argc, const char **argv) { + struct ldb_control **req_ctrls; + struct ldb_cmdline *options; struct ldb_context *ldb; int ret = 0, i; - struct ldb_cmdline *options; ldb = ldb_init(NULL, NULL); @@ -96,6 +98,12 @@ int main(int argc, const char **argv) exit(1); } + req_ctrls = ldb_parse_control_strings(ldb, ldb, (const char **)options->controls); + if (options->controls != NULL && req_ctrls== NULL) { + printf("parsing controls failed: %s\n", ldb_errstring(ldb)); + return -1; + } + for (i=0;i<options->argc;i++) { struct ldb_dn *dn; @@ -105,17 +113,18 @@ int main(int argc, const char **argv) exit(1); } if (options->recursive) { - ret = ldb_delete_recursive(ldb, dn); + ret = ldb_delete_recursive(ldb, dn,req_ctrls); } else { - ret = ldb_delete(ldb, dn); + ret = ldb_delete_ctrl(ldb, dn,req_ctrls); if (ret == 0) { printf("Deleted 1 record\n"); } } if (ret != 0) { - printf("delete of '%s' failed - %s\n", - ldb_dn_get_linearized(dn), - ldb_errstring(ldb)); + printf("delete of '%s' failed - (%s) %s\n", + ldb_dn_get_linearized(dn), + ldb_strerror(ret), + ldb_errstring(ldb)); } } diff --git a/source4/lib/ldb/tools/ldbmodify.c b/source4/lib/ldb/tools/ldbmodify.c index 4936880d09..57988cbb30 100644 --- a/source4/lib/ldb/tools/ldbmodify.c +++ b/source4/lib/ldb/tools/ldbmodify.c @@ -33,6 +33,7 @@ #include "ldb.h" #include "tools/cmdline.h" +#include "ldbutil.h" static int failures; static struct ldb_cmdline *options; @@ -52,22 +53,28 @@ static int process_file(struct ldb_context *ldb, FILE *f, int *count) { struct ldb_ldif *ldif; int ret = LDB_SUCCESS; - + struct ldb_control **req_ctrls = ldb_parse_control_strings(ldb, ldb, (const char **)options->controls); + if (options->controls != NULL && req_ctrls== NULL) { + printf("parsing controls failed: %s\n", ldb_errstring(ldb)); + return -1; + } + while ((ldif = ldb_ldif_read_file(ldb, f))) { switch (ldif->changetype) { case LDB_CHANGETYPE_NONE: case LDB_CHANGETYPE_ADD: - ret = ldb_add(ldb, ldif->msg); + ret = ldb_add_ctrl(ldb, ldif->msg,req_ctrls); break; case LDB_CHANGETYPE_DELETE: - ret = ldb_delete(ldb, ldif->msg->dn); + ret = ldb_delete_ctrl(ldb, ldif->msg->dn,req_ctrls); break; case LDB_CHANGETYPE_MODIFY: - ret = ldb_modify(ldb, ldif->msg); + ret = ldb_modify_ctrl(ldb, ldif->msg,req_ctrls); break; } if (ret != LDB_SUCCESS) { - fprintf(stderr, "ERR: \"%s\" on DN %s\n", + fprintf(stderr, "ERR: (%s) \"%s\" on DN %s\n", + ldb_strerror(ret), ldb_errstring(ldb), ldb_dn_get_linearized(ldif->msg->dn)); failures++; } else { diff --git a/source4/lib/ldb/tools/ldbutil.c b/source4/lib/ldb/tools/ldbutil.c new file mode 100644 index 0000000000..5f7ea894df --- /dev/null +++ b/source4/lib/ldb/tools/ldbutil.c @@ -0,0 +1,149 @@ +/* + ldb database library utility + + Copyright (C) Matthieu Patou 2009 + + ** 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/>. +*/ + +/* + * Name: ldb + * + * Description: Common function used by ldb_add/ldb_modify/ldb_delete + * + * Author: Matthieu Patou + */ + +#include "ldb.h" +#include "ldb_module.h" + +/* autostarts a transacion if none active */ +static int ldb_do_autotransaction(struct ldb_context *ldb, + struct ldb_request *req) +{ + int ret; + + ret = ldb_transaction_start(ldb); + if (ret != LDB_SUCCESS) { + return ret; + } + + ret = ldb_request(ldb, req); + if (ret == LDB_SUCCESS) { + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + } + + if (ret == LDB_SUCCESS) { + return ldb_transaction_commit(ldb); + } + ldb_transaction_cancel(ldb); + + if (ldb_errstring(ldb) == NULL) { + /* no error string was setup by the backend */ + ldb_asprintf_errstring(ldb, "%s (%d)", ldb_strerror(ret), ret); + } + + return ret; +} +/* + Same as ldb_add but accept control +*/ +int ldb_add_ctrl(struct ldb_context *ldb, + const struct ldb_message *message, + struct ldb_control **controls) +{ + struct ldb_request *req; + int ret; + + ret = ldb_msg_sanity_check(ldb, message); + if (ret != LDB_SUCCESS) { + return ret; + } + + ret = ldb_build_add_req(&req, ldb, ldb, + message, + controls, + NULL, + ldb_op_default_callback, + NULL); + + if (ret != LDB_SUCCESS) return ret; + + /* do request and autostart a transaction */ + ret = ldb_do_autotransaction(ldb, req); + + talloc_free(req); + return ret; +} + +/* + same as ldb_delete but accept control +*/ +int ldb_delete_ctrl(struct ldb_context *ldb, struct ldb_dn *dn, + struct ldb_control **controls) +{ + struct ldb_request *req; + int ret; + + ret = ldb_build_del_req(&req, ldb, ldb, + dn, + controls, + NULL, + ldb_op_default_callback, + NULL); + + if (ret != LDB_SUCCESS) return ret; + + /* do request and autostart a transaction */ + ret = ldb_do_autotransaction(ldb, req); + + talloc_free(req); + return ret; +} + + +/* + same as ldb_modify, but accepts controls +*/ +int ldb_modify_ctrl(struct ldb_context *ldb, + const struct ldb_message *message, + struct ldb_control **controls) +{ + struct ldb_request *req; + int ret; + + ret = ldb_msg_sanity_check(ldb, message); + if (ret != LDB_SUCCESS) { + return ret; + } + + ret = ldb_build_mod_req(&req, ldb, ldb, + message, + controls, + NULL, + ldb_op_default_callback, + NULL); + + if (ret != LDB_SUCCESS) return ret; + + /* do request and autostart a transaction */ + ret = ldb_do_autotransaction(ldb, req); + + talloc_free(req); + return ret; +} diff --git a/source4/lib/ldb/tools/ldbutil.h b/source4/lib/ldb/tools/ldbutil.h new file mode 100644 index 0000000000..7747dbec64 --- /dev/null +++ b/source4/lib/ldb/tools/ldbutil.h @@ -0,0 +1,41 @@ +/* + ldb database library utility header file + + Copyright (C) Matthieu Patou 2009 + + ** 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/>. +*/ + +/* + * Name: ldb + * + * Description: Common function used by ldb_add/ldb_modify/ldb_delete + * + * Author: Matthieu Patou + */ + +#include "ldb.h" + +int ldb_add_ctrl(struct ldb_context *ldb, + const struct ldb_message *message, + struct ldb_control **controls); +int ldb_delete_ctrl(struct ldb_context *ldb, struct ldb_dn *dn, + struct ldb_control **controls); +int ldb_modify_ctrl(struct ldb_context *ldb, + const struct ldb_message *message, + struct ldb_control **controls); |