summaryrefslogtreecommitdiff
path: root/source4/dsdb
diff options
context:
space:
mode:
Diffstat (limited to 'source4/dsdb')
-rw-r--r--source4/dsdb/samdb/ldb_modules/samldb.c155
1 files changed, 155 insertions, 0 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c
index f4b7043e32..79b557d5c8 100644
--- a/source4/dsdb/samdb/ldb_modules/samldb.c
+++ b/source4/dsdb/samdb/ldb_modules/samldb.c
@@ -1444,6 +1444,152 @@ static int samldb_member_check(struct samldb_ctx *ac)
return LDB_SUCCESS;
}
+/* This trigger adapts the "servicePrincipalName" attributes if the
+ * "dNSHostName" attribute changes */
+static int samldb_service_principal_names_change(struct samldb_ctx *ac)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
+ struct ldb_message_element *el = NULL;
+ struct ldb_message *msg;
+ const char *attrs[] = { "servicePrincipalName", NULL };
+ struct ldb_result *res;
+ const char *dns_hostname, *old_dns_hostname;
+ unsigned int i;
+ int ret;
+
+ /* Here it's not the same logic as with "samldb_get_single_valued_attr".
+ * We need to:
+ *
+ * - consider "add" and "replace" operations - the last value we take
+ * - ignore "delete" operations - obviously this attribute isn't
+ * write protected
+ */
+ for (i = 0; i < ac->msg->num_elements; i++) {
+ if (ldb_attr_cmp(ac->msg->elements[i].name,
+ "dNSHostName") != 0) {
+ continue;
+ }
+
+ if (LDB_FLAG_MOD_TYPE(ac->msg->elements[i].flags)
+ != LDB_FLAG_MOD_DELETE) {
+ el = &ac->msg->elements[i];
+ }
+ }
+ if (el == NULL) {
+ /* we are not affected */
+ return LDB_SUCCESS;
+ }
+
+ /* Create a temporary message for fetching the "dNSHostName" */
+ msg = ldb_msg_new(ac->msg);
+ if (msg == NULL) {
+ return ldb_module_oom(ac->module);
+ }
+ ret = ldb_msg_add(msg, el, 0);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ dns_hostname = ldb_msg_find_attr_as_string(msg, "dNSHostName", "");
+ talloc_free(msg);
+
+ old_dns_hostname = samdb_search_string(ldb, ac, ac->msg->dn,
+ "dNSHostName", NULL);
+ if ((old_dns_hostname == NULL) || (strcasecmp(old_dns_hostname,
+ dns_hostname) == 0)) {
+ /* Well, if there's no old DNS hostname then we cannot do this.
+ * And if old and new DNS name do match we are also finished
+ * here. */
+ return LDB_SUCCESS;
+ }
+
+ /* Potential "servicePrincipalName" changes in the same request have to
+ * be handled before the update (Windows behaviour). */
+ el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
+ if (el != NULL) {
+ msg = ldb_msg_new(ac->msg);
+ if (msg == NULL) {
+ return ldb_module_oom(ac->module);
+ }
+ msg->dn = ac->msg->dn;
+
+ do {
+ ret = ldb_msg_add(msg, el, el->flags);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ldb_msg_remove_element(ac->msg, el);
+
+ el = ldb_msg_find_element(ac->msg,
+ "servicePrincipalName");
+ } while (el != NULL);
+
+ ret = dsdb_module_modify(ac->module, msg,
+ DSDB_FLAG_NEXT_MODULE);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ talloc_free(msg);
+ }
+
+ /* Fetch the "servicePrincipalName"s if any */
+ ret = ldb_search(ldb, ac, &res, ac->msg->dn, LDB_SCOPE_BASE, attrs,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ if ((res->count != 1) || (res->msgs[0]->num_elements > 1)) {
+ return ldb_operr(ldb);
+ }
+
+ if (res->msgs[0]->num_elements == 1) {
+ /* Yes, we do have "servicePrincipalName"s. First we update them
+ * locally, that means we do always substitute the current
+ * "dNSHostName" with the new one and then we append this to the
+ * modification request (Windows behaviour). */
+
+ for (i = 0; i < res->msgs[0]->elements[0].num_values; i++) {
+ char *old_str, *new_str, *pos;
+ const char *tok;
+
+ old_str = (char *)
+ res->msgs[0]->elements[0].values[i].data;
+
+ new_str = talloc_strdup(ac->msg,
+ strtok_r(old_str, "/", &pos));
+ if (new_str == NULL) {
+ return ldb_module_oom(ac->module);
+ }
+
+ while ((tok = strtok_r(NULL, "/", &pos)) != NULL) {
+ if (strcasecmp(tok, old_dns_hostname) == 0) {
+ tok = dns_hostname;
+ }
+
+ new_str = talloc_asprintf(ac->msg, "%s/%s",
+ new_str, tok);
+ if (new_str == NULL) {
+ return ldb_module_oom(ac->module);
+ }
+ }
+
+ ret = ldb_msg_add_string(ac->msg,
+ "servicePrincipalName",
+ new_str);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
+ el->flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ talloc_free(res);
+
+ return LDB_SUCCESS;
+}
+
/* add */
static int samldb_add(struct ldb_module *module, struct ldb_request *req)
@@ -1630,6 +1776,15 @@ static int samldb_modify(struct ldb_module *module, struct ldb_request *req)
}
}
+ el = ldb_msg_find_element(ac->msg, "dNSHostName");
+ if (el != NULL) {
+ modified = true;
+ ret = samldb_service_principal_names_change(ac);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
if (modified) {
struct ldb_request *child_req;