*/
#include <Python.h>
+#include "python/py3compat.h"
#include "includes.h"
#include <ldb.h>
#include <pyldb.h>
#include "auth/kerberos/kerberos.h"
#include "librpc/rpc/pyrpc_util.h"
#include "lib/policy/policy.h"
+#include "param/pyparam.h"
+#include "lib/util/dlinklist.h"
+#include "dsdb/kcc/garbage_collect_tombstones.h"
+#include "dsdb/kcc/scavenge_dns_records.h"
-void initdsdb(void);
/* FIXME: These should be in a header file somewhere */
#define PyErr_LDB_OR_RAISE(py_ldb, ldb) \
if (!py_check_dcerpc_type(py_ldb, "ldb", "Ldb")) { \
- PyErr_SetString(py_ldb_get_exception(), "Ldb connection object required"); \
+ PyErr_SetString(PyExc_TypeError, "Ldb connection object required"); \
return NULL; \
} \
ldb = pyldb_Ldb_AsLdbContext(py_ldb);
#define PyErr_LDB_DN_OR_RAISE(py_ldb_dn, dn) \
if (!py_check_dcerpc_type(py_ldb_dn, "ldb", "Dn")) { \
- PyErr_SetString(py_ldb_get_exception(), "ldb Dn object required"); \
+ PyErr_SetString(PyExc_TypeError, "ldb Dn object required"); \
return NULL; \
} \
dn = pyldb_Dn_AsDn(py_ldb_dn);
return NULL;
}
- result = PyString_FromString(site);
+ result = PyStr_FromString(site);
talloc_free(mem_ctx);
return result;
}
return NULL;
}
- ret = PyString_FromString(retstr);
+ ret = PyStr_FromString(retstr);
talloc_free(retstr);
return ret;
}
PyErr_LDB_OR_RAISE(py_ldb, ldb);
- sid = dom_sid_parse_talloc(NULL, PyString_AsString(py_sid));
+ sid = dom_sid_parse_talloc(NULL, PyStr_AsString(py_sid));
if (sid == NULL) {
PyErr_NoMemory();
return NULL;
PyErr_NoMemory();
return NULL;
}
- ret = PyString_FromString(retstr);
+ ret = PyStr_FromString(retstr);
talloc_free(retstr);
return ret;
}
PyErr_NoMemory();
return NULL;
}
- result = PyString_FromString(retstr);
+ result = PyStr_FromString(retstr);
talloc_free(retstr);
return result;
}
return NULL;
}
- ret = PyString_FromString(oid);
+ ret = PyStr_FromString(oid);
talloc_free(mem_ctx);
Py_RETURN_NONE;
}
- return PyString_FromString(target_attr->lDAPDisplayName);
+ return PyStr_FromString(target_attr->lDAPDisplayName);
}
return NULL;
}
- return PyString_FromString(a->lDAPDisplayName);
+ return PyStr_FromString(a->lDAPDisplayName);
}
return NULL;
}
- return PyString_FromString(attribute->syntax->ldap_oid);
+ return PyStr_FromString(attribute->syntax->ldap_oid);
}
/*
"list of strings or ldb MessageElement object required");
return NULL;
}
+ /*
+ * NOTE:
+ * el may not be a valid talloc context, it
+ * could be part of an array
+ */
el = pyldb_MessageElement_AsMessageElement(el_list);
} else {
el = talloc_zero(tmp_ctx, struct ldb_message_element);
for (i = 0; i < el->num_values; i++) {
PyObject *item = PyList_GetItem(el_list, i);
- if (!PyString_Check(item)) {
- PyErr_Format(PyExc_TypeError, "ldif_elements should be strings");
+ if (!(PyBytes_Check(item))) {
+ PyErr_Format(PyExc_TypeError,
+ "ldif_element type should be "
+ PY_DESC_PY3_BYTES
+ );
talloc_free(tmp_ctx);
return NULL;
}
- el->values[i].data = (uint8_t *)PyString_AsString(item);
- el->values[i].length = PyString_Size(item);
+ el->values[i].data =
+ (uint8_t *)PyBytes_AsString(item);
+ el->values[i].length = PyBytes_Size(item);
}
}
*/
static PyObject *py_dsdb_normalise_attributes(PyObject *self, PyObject *args)
{
- PyObject *py_ldb, *el_list, *ret;
+ PyObject *py_ldb, *el_list, *py_ret;
struct ldb_context *ldb;
char *ldap_display_name;
const struct dsdb_attribute *a;
struct dsdb_schema *schema;
struct dsdb_syntax_ctx syntax_ctx;
- struct ldb_message_element *el;
+ struct ldb_message_element *el, *new_el;
struct drsuapi_DsReplicaAttribute *attr;
+ PyLdbMessageElementObject *ret;
TALLOC_CTX *tmp_ctx;
WERROR werr;
Py_ssize_t i;
+ PyTypeObject *py_type = NULL;
+ PyObject *module = NULL;
if (!PyArg_ParseTuple(args, "OsO", &py_ldb, &ldap_display_name, &el_list)) {
return NULL;
"list of strings or ldb MessageElement object required");
return NULL;
}
+ /*
+ * NOTE:
+ * el may not be a valid talloc context, it
+ * could be part of an array
+ */
el = pyldb_MessageElement_AsMessageElement(el_list);
} else {
el = talloc_zero(tmp_ctx, struct ldb_message_element);
for (i = 0; i < el->num_values; i++) {
PyObject *item = PyList_GetItem(el_list, i);
- if (!PyString_Check(item)) {
- PyErr_Format(PyExc_TypeError, "ldif_elements should be strings");
+ if (!PyBytes_Check(item)) {
+ PyErr_Format(PyExc_TypeError,
+ "ldif_element type should be "
+ PY_DESC_PY3_BYTES
+ );
talloc_free(tmp_ctx);
return NULL;
}
- el->values[i].data = (uint8_t *)PyString_AsString(item);
- el->values[i].length = PyString_Size(item);
+ el->values[i].data = (uint8_t *)PyBytes_AsString(item);
+ el->values[i].length = PyBytes_Size(item);
}
}
+ new_el = talloc_zero(tmp_ctx, struct ldb_message_element);
+ if (new_el == NULL) {
+ PyErr_NoMemory();
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
/* Normalise "objectClass" attribute if needed */
if (ldb_attr_cmp(a->lDAPDisplayName, "objectClass") == 0) {
int iret;
- iret = dsdb_sort_objectClass_attr(ldb, schema, el, tmp_ctx, el);
+ iret = dsdb_sort_objectClass_attr(ldb, schema, el, new_el, new_el);
if (iret != LDB_SUCCESS) {
PyErr_SetString(PyExc_RuntimeError, ldb_errstring(ldb));
talloc_free(tmp_ctx);
PyErr_WERROR_NOT_OK_RAISE(werr);
/* now convert back again */
- werr = a->syntax->drsuapi_to_ldb(&syntax_ctx, a, attr, el, el);
+ werr = a->syntax->drsuapi_to_ldb(&syntax_ctx, a, attr, new_el, new_el);
PyErr_WERROR_NOT_OK_RAISE(werr);
- ret = py_return_ndr_struct("ldb", "MessageElement", el, el);
+ module = PyImport_ImportModule("ldb");
+ if (module == NULL) {
+ return NULL;
+ }
+
+ py_type = (PyTypeObject *)PyObject_GetAttrString(module, "MessageElement");
+ if (py_type == NULL) {
+ return NULL;
+ }
+ py_ret = py_type->tp_alloc(py_type, 0);
+ ret = (PyLdbMessageElementObject *)py_ret;
+
+ ret->mem_ctx = talloc_new(NULL);
+ if (talloc_reference(ret->mem_ctx, new_el) == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ ret->el = new_el;
talloc_free(tmp_ctx);
- return ret;
+ return py_ret;
}
return NULL;
PyErr_LDB_OR_RAISE(py_ldb, ldb);
- GUID_from_string(PyString_AsString(py_guid), &guid);
+ GUID_from_string(PyStr_AsString(py_guid), &guid);
if (GUID_all_zero(&guid)) {
PyErr_SetString(PyExc_RuntimeError, "set_ntds_invocation_id rejected due to all-zero invocation ID");
PyErr_NoMemory();
return NULL;
}
- result = PyString_FromString(retstr);
+ result = PyStr_FromString(retstr);
talloc_free(retstr);
return result;
}
struct ldb_context *from_ldb;
struct dsdb_schema *schema;
int ret;
- char write_indices_and_attributes = true;
+ char write_indices_and_attributes = SCHEMA_WRITE;
if (!PyArg_ParseTuple(args, "OO|b",
&py_ldb, &py_from_ldb, &write_indices_and_attributes))
return NULL;
return PyBool_FromLong(am_pdc);
}
+/*
+ call DSDB_EXTENDED_CREATE_OWN_RID_SET to get a new RID set for this server
+ */
+static PyObject *py_dsdb_create_own_rid_set(PyObject *self, PyObject *args)
+{
+ PyObject *py_ldb;
+ struct ldb_context *ldb;
+ int ret;
+ struct ldb_result *ext_res;
+
+ if (!PyArg_ParseTuple(args, "O", &py_ldb))
+ return NULL;
+
+ PyErr_LDB_OR_RAISE(py_ldb, ldb);
+
+ /*
+ * Run DSDB_EXTENDED_CREATE_OWN_RID_SET to get a RID set
+ */
+
+ ret = ldb_extended(ldb, DSDB_EXTENDED_CREATE_OWN_RID_SET, NULL, &ext_res);
+
+ PyErr_LDB_ERROR_IS_ERR_RAISE(py_ldb_get_exception(), ret, ldb);
+
+ TALLOC_FREE(ext_res);
+
+ Py_RETURN_NONE;
+}
+
+/*
+ call DSDB_EXTENDED_ALLOCATE_RID to get a new RID set for this server
+ */
+static PyObject *py_dsdb_allocate_rid(PyObject *self, PyObject *args)
+{
+ PyObject *py_ldb;
+ struct ldb_context *ldb;
+ int ret;
+ uint32_t rid;
+ struct ldb_result *ext_res = NULL;
+ struct dsdb_extended_allocate_rid *rid_return = NULL;
+ if (!PyArg_ParseTuple(args, "O", &py_ldb)) {
+ return NULL;
+ }
+
+ PyErr_LDB_OR_RAISE(py_ldb, ldb);
+
+ rid_return = talloc_zero(ldb, struct dsdb_extended_allocate_rid);
+ if (rid_return == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ /*
+ * Run DSDB_EXTENDED_ALLOCATE_RID to get a new RID
+ */
+
+ ret = ldb_extended(ldb, DSDB_EXTENDED_ALLOCATE_RID, rid_return, &ext_res);
+ if (ret != LDB_SUCCESS) {
+ TALLOC_FREE(rid_return);
+ TALLOC_FREE(ext_res);
+ PyErr_LDB_ERROR_IS_ERR_RAISE(py_ldb_get_exception(), ret, ldb);
+ }
+
+ rid = rid_return->rid;
+ TALLOC_FREE(rid_return);
+ TALLOC_FREE(ext_res);
+
+ return PyInt_FromLong(rid);
+}
+
+static PyObject *py_dns_delete_tombstones(PyObject *self, PyObject *args)
+{
+ PyObject *py_ldb;
+ NTSTATUS status;
+ struct ldb_context *ldb = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ char *error_string = NULL;
+
+ if (!PyArg_ParseTuple(args, "O", &py_ldb)) {
+ return NULL;
+ }
+ PyErr_LDB_OR_RAISE(py_ldb, ldb);
+
+ mem_ctx = talloc_new(ldb);
+ if (mem_ctx == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ status = dns_delete_tombstones(mem_ctx, ldb, &error_string);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ if (error_string) {
+ PyErr_Format(PyExc_RuntimeError, "%s", error_string);
+ } else {
+ PyErr_SetNTSTATUS(status);
+ }
+ TALLOC_FREE(mem_ctx);
+ return NULL;
+ }
+
+ TALLOC_FREE(mem_ctx);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_scavenge_dns_records(PyObject *self, PyObject *args)
+{
+ PyObject *py_ldb;
+ NTSTATUS status;
+ struct ldb_context *ldb = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ char *error_string = NULL;
+
+ if (!PyArg_ParseTuple(args, "O", &py_ldb)) {
+ return NULL;
+ }
+ PyErr_LDB_OR_RAISE(py_ldb, ldb);
+
+ mem_ctx = talloc_new(ldb);
+ if (mem_ctx == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ status = dns_tombstone_records(mem_ctx, ldb, &error_string);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ if (error_string) {
+ PyErr_Format(PyExc_RuntimeError, "%s", error_string);
+ } else {
+ PyErr_SetNTSTATUS(status);
+ }
+ TALLOC_FREE(mem_ctx);
+ return NULL;
+ }
+
+ TALLOC_FREE(mem_ctx);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_dsdb_garbage_collect_tombstones(PyObject *self, PyObject *args)
+{
+ PyObject *py_ldb, *py_list_dn;
+ struct ldb_context *ldb = NULL;
+ Py_ssize_t i;
+ Py_ssize_t length;
+ long long _current_time, _tombstone_lifetime = LLONG_MAX;
+ uint32_t tombstone_lifetime32;
+ struct dsdb_ldb_dn_list_node *part = NULL;
+ time_t current_time, tombstone_lifetime;
+ TALLOC_CTX *mem_ctx = NULL;
+ NTSTATUS status;
+ unsigned int num_objects_removed = 0;
+ unsigned int num_links_removed = 0;
+ char *error_string = NULL;
+
+ if (!PyArg_ParseTuple(args, "OOL|L", &py_ldb,
+ &py_list_dn, &_current_time, &_tombstone_lifetime)) {
+ return NULL;
+ }
+
+
+ PyErr_LDB_OR_RAISE(py_ldb, ldb);
+
+ mem_ctx = talloc_new(ldb);
+ if (mem_ctx == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ current_time = _current_time;
+
+ if (_tombstone_lifetime == LLONG_MAX) {
+ int ret = dsdb_tombstone_lifetime(ldb, &tombstone_lifetime32);
+ if (ret != LDB_SUCCESS) {
+ PyErr_Format(PyExc_RuntimeError,
+ "Failed to get tombstone lifetime: %s",
+ ldb_errstring(ldb));
+ TALLOC_FREE(mem_ctx);
+ return NULL;
+ }
+ tombstone_lifetime = tombstone_lifetime32;
+ } else {
+ tombstone_lifetime = _tombstone_lifetime;
+ }
+
+ if (!PyList_Check(py_list_dn)) {
+ PyErr_SetString(PyExc_TypeError, "A list of DNs were expected");
+ TALLOC_FREE(mem_ctx);
+ return NULL;
+ }
+
+ length = PyList_GET_SIZE(py_list_dn);
+
+ for (i = 0; i < length; i++) {
+ char *part_str = PyStr_AsString(PyList_GetItem(py_list_dn, i));
+ struct ldb_dn *p;
+ struct dsdb_ldb_dn_list_node *node;
+
+ if (part_str == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return PyErr_NoMemory();
+ }
+
+ p = ldb_dn_new(mem_ctx, ldb, part_str);
+ if (p == NULL) {
+ PyErr_Format(PyExc_RuntimeError, "Failed to parse DN %s", part_str);
+ TALLOC_FREE(mem_ctx);
+ return NULL;
+ }
+ node = talloc_zero(mem_ctx, struct dsdb_ldb_dn_list_node);
+ node->dn = p;
+
+ DLIST_ADD_END(part, node);
+ }
+
+ status = dsdb_garbage_collect_tombstones(mem_ctx, ldb,
+ part, current_time,
+ tombstone_lifetime,
+ &num_objects_removed,
+ &num_links_removed,
+ &error_string);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ if (error_string) {
+ PyErr_Format(PyExc_RuntimeError, "%s", error_string);
+ } else {
+ PyErr_SetNTSTATUS(status);
+ }
+ TALLOC_FREE(mem_ctx);
+ return NULL;
+ }
+
+ TALLOC_FREE(mem_ctx);
+
+ return Py_BuildValue("(II)", num_objects_removed,
+ num_links_removed);
+}
+
+static PyObject *py_dsdb_load_udv_v2(PyObject *self, PyObject *args)
+{
+ uint32_t count;
+ int ret, i;
+ bool ok;
+ PyObject *py_ldb = NULL, *py_dn = NULL, *pylist = NULL;
+ struct ldb_context *samdb = NULL;
+ struct ldb_dn *dn = NULL;
+ struct drsuapi_DsReplicaCursor2 *cursors = NULL;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_dn)) {
+ return NULL;
+ }
+
+ PyErr_LDB_OR_RAISE(py_ldb, samdb);
+
+ tmp_ctx = talloc_new(samdb);
+ if (tmp_ctx == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ ok = pyldb_Object_AsDn(tmp_ctx, py_dn, samdb, &dn);
+ if (!ok) {
+ TALLOC_FREE(tmp_ctx);
+ return NULL;
+ }
+
+ ret = dsdb_load_udv_v2(samdb, dn, tmp_ctx, &cursors, &count);
+ if (ret != LDB_SUCCESS) {
+ TALLOC_FREE(tmp_ctx);
+ PyErr_SetString(PyExc_RuntimeError,
+ "Failed to load udv from ldb");
+ return NULL;
+ }
+
+ pylist = PyList_New(count);
+ if (pylist == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return PyErr_NoMemory();
+ }
+
+ for (i = 0; i < count; i++) {
+ PyObject *py_cursor;
+ struct drsuapi_DsReplicaCursor2 *cursor;
+ cursor = talloc(tmp_ctx, struct drsuapi_DsReplicaCursor2);
+ if (cursor == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return PyErr_NoMemory();
+ }
+ *cursor = cursors[i];
+
+ py_cursor = py_return_ndr_struct("samba.dcerpc.drsuapi",
+ "DsReplicaCursor2",
+ cursor, cursor);
+ if (py_cursor == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return PyErr_NoMemory();
+ }
+
+ PyList_SetItem(pylist, i, py_cursor);
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return pylist;
+}
static PyMethodDef py_dsdb_methods[] = {
{ "_samdb_server_site_name", (PyCFunction)py_samdb_server_site_name,
{ "_dsdb_get_wellknown_dn", (PyCFunction)py_dsdb_get_wellknown_dn, METH_VARARGS, NULL },
{ "_dsdb_DsReplicaAttribute", (PyCFunction)py_dsdb_DsReplicaAttribute, METH_VARARGS, NULL },
{ "_dsdb_normalise_attributes", (PyCFunction)py_dsdb_normalise_attributes, METH_VARARGS, NULL },
+ { "_dsdb_garbage_collect_tombstones", (PyCFunction)py_dsdb_garbage_collect_tombstones, METH_VARARGS,
+ "_dsdb_kcc_check_deleted(samdb, [dn], current_time, tombstone_lifetime)"
+ " -> (num_objects_expunged, num_links_expunged)" },
+ { "_scavenge_dns_records", (PyCFunction)py_scavenge_dns_records,
+ METH_VARARGS, NULL},
+ { "_dns_delete_tombstones", (PyCFunction)py_dns_delete_tombstones,
+ METH_VARARGS, NULL},
+ { "_dsdb_create_own_rid_set", (PyCFunction)py_dsdb_create_own_rid_set, METH_VARARGS,
+ "_dsdb_create_own_rid_set(samdb)"
+ " -> None" },
+ { "_dsdb_allocate_rid", (PyCFunction)py_dsdb_allocate_rid, METH_VARARGS,
+ "_dsdb_allocate_rid(samdb)"
+ " -> RID" },
+ { "_dsdb_load_udv_v2", (PyCFunction)py_dsdb_load_udv_v2, METH_VARARGS, NULL },
{ NULL }
};
-void initdsdb(void)
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "dsdb",
+ .m_doc = "Python bindings for the directory service databases.",
+ .m_size = -1,
+ .m_methods = py_dsdb_methods,
+};
+
+MODULE_INIT_FUNC(dsdb)
{
PyObject *m;
- m = Py_InitModule3("dsdb", py_dsdb_methods,
- "Python bindings for the directory service databases.");
+ m = PyModule_Create(&moduledef);
+
if (m == NULL)
- return;
+ return NULL;
#define ADD_DSDB_FLAG(val) PyModule_AddObject(m, #val, PyInt_FromLong(val))
ADD_DSDB_FLAG(DS_DOMAIN_FUNCTION_2008_R2);
ADD_DSDB_FLAG(DS_DOMAIN_FUNCTION_2012);
ADD_DSDB_FLAG(DS_DOMAIN_FUNCTION_2012_R2);
+ ADD_DSDB_FLAG(DS_DOMAIN_FUNCTION_2016);
/* nc replica flags */
ADD_DSDB_FLAG(INSTANCE_TYPE_IS_NC_HEAD);
ADD_DSDB_FLAG(GPO_INHERIT);
ADD_DSDB_FLAG(GPO_BLOCK_INHERITANCE);
-#define ADD_DSDB_STRING(val) PyModule_AddObject(m, #val, PyString_FromString(val))
+#define ADD_DSDB_STRING(val) PyModule_AddObject(m, #val, PyStr_FromString(val))
ADD_DSDB_STRING(DSDB_SYNTAX_BINARY_DN);
ADD_DSDB_STRING(DSDB_SYNTAX_STRING_DN);
ADD_DSDB_STRING(DSDB_SYNTAX_OR_NAME);
ADD_DSDB_STRING(DSDB_CONTROL_DBCHECK);
ADD_DSDB_STRING(DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA);
+ ADD_DSDB_STRING(DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
+ ADD_DSDB_STRING(DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
+ ADD_DSDB_STRING(DSDB_CONTROL_REPLMD_VANISH_LINKS);
ADD_DSDB_STRING(DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID);
+ ADD_DSDB_STRING(DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID);
+ ADD_DSDB_STRING(DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID);
+ ADD_DSDB_STRING(DSDB_CONTROL_INVALID_NOT_IMPLEMENTED);
ADD_DSDB_STRING(DS_GUID_COMPUTERS_CONTAINER);
ADD_DSDB_STRING(DS_GUID_DELETED_OBJECTS_CONTAINER);
ADD_DSDB_STRING(DS_GUID_PROGRAM_DATA_CONTAINER);
ADD_DSDB_STRING(DS_GUID_SYSTEMS_CONTAINER);
ADD_DSDB_STRING(DS_GUID_USERS_CONTAINER);
+
+ return m;
}