summaryrefslogtreecommitdiffstats
path: root/source4/dsdb/kcc/kcc_service.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/dsdb/kcc/kcc_service.c')
-rw-r--r--source4/dsdb/kcc/kcc_service.c364
1 files changed, 364 insertions, 0 deletions
diff --git a/source4/dsdb/kcc/kcc_service.c b/source4/dsdb/kcc/kcc_service.c
new file mode 100644
index 0000000..066cc07
--- /dev/null
+++ b/source4/dsdb/kcc/kcc_service.c
@@ -0,0 +1,364 @@
+/*
+ Unix SMB/CIFS Implementation.
+
+ KCC service
+
+ Copyright (C) Andrew Tridgell 2009
+ based on repl service code
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "samba/service.h"
+#include "lib/events/events.h"
+#include "lib/messaging/irpc.h"
+#include "dsdb/kcc/kcc_service.h"
+#include <ldb_errors.h>
+#include "../lib/util/dlinklist.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "param/param.h"
+#include "libds/common/roles.h"
+
+/*
+ establish system creds
+ */
+static WERROR kccsrv_init_creds(struct kccsrv_service *service)
+{
+ service->system_session_info = system_session(service->task->lp_ctx);
+ if (!service->system_session_info) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ return WERR_OK;
+}
+
+/*
+ connect to the local SAM
+ */
+static WERROR kccsrv_connect_samdb(struct kccsrv_service *service, struct loadparm_context *lp_ctx)
+{
+ const struct GUID *ntds_guid;
+
+ service->samdb = samdb_connect(service,
+ service->task->event_ctx,
+ lp_ctx,
+ service->system_session_info,
+ NULL,
+ 0);
+ if (!service->samdb) {
+ return WERR_DS_UNAVAILABLE;
+ }
+
+ ntds_guid = samdb_ntds_objectGUID(service->samdb);
+ if (!ntds_guid) {
+ DBG_ERR("Failed to determine own NTDS objectGUID\n");
+ return WERR_DS_UNAVAILABLE;
+ }
+
+ service->ntds_guid = *ntds_guid;
+
+ if (samdb_rodc(service->samdb, &service->am_rodc) != LDB_SUCCESS) {
+ DEBUG(0,(__location__ ": Failed to determine RODC status\n"));
+ return WERR_DS_UNAVAILABLE;
+ }
+
+ return WERR_OK;
+}
+
+
+/*
+ load our local partition list
+ */
+static WERROR kccsrv_load_partitions(struct kccsrv_service *s)
+{
+ struct ldb_dn *basedn;
+ struct ldb_result *r;
+ struct ldb_message_element *el;
+ static const char *attrs[] = { "namingContexts", "configurationNamingContext", NULL };
+ unsigned int i;
+ int ret;
+
+ basedn = ldb_dn_new(s, s->samdb, NULL);
+ W_ERROR_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->samdb, s, &r, basedn, LDB_SCOPE_BASE, attrs,
+ "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return WERR_FOOBAR;
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return WERR_FOOBAR;
+ }
+
+ el = ldb_msg_find_element(r->msgs[0], "namingContexts");
+ if (!el) {
+ return WERR_FOOBAR;
+ }
+
+ for (i=0; i < el->num_values; i++) {
+ const char *v = (const char *)el->values[i].data;
+ struct ldb_dn *pdn;
+ struct dsdb_ldb_dn_list_node *p;
+
+ pdn = ldb_dn_new(s, s->samdb, v);
+ if (!ldb_dn_validate(pdn)) {
+ return WERR_FOOBAR;
+ }
+
+ p = talloc_zero(s, struct dsdb_ldb_dn_list_node);
+ W_ERROR_HAVE_NO_MEMORY(p);
+
+ p->dn = talloc_steal(p, pdn);
+
+ DLIST_ADD(s->partitions, p);
+
+ DEBUG(2, ("kccsrv_partition[%s] loaded\n", v));
+ }
+
+ el = ldb_msg_find_element(r->msgs[0], "configurationNamingContext");
+ if (!el) {
+ return WERR_FOOBAR;
+ }
+ s->config_dn = ldb_dn_new(s, s->samdb, (const char *)el->values[0].data);
+ if (!ldb_dn_validate(s->config_dn)) {
+ return WERR_FOOBAR;
+ }
+
+ talloc_free(r);
+
+ return WERR_OK;
+}
+
+
+struct kcc_manual_runcmd_state {
+ struct irpc_message *msg;
+ struct drsuapi_DsExecuteKCC *r;
+ struct kccsrv_service *service;
+};
+
+
+/*
+ * Called when samba_kcc script has finished
+ */
+static void manual_samba_kcc_done(struct tevent_req *subreq)
+{
+ struct kcc_manual_runcmd_state *st =
+ tevent_req_callback_data(subreq,
+ struct kcc_manual_runcmd_state);
+ int rc;
+ int sys_errno;
+ NTSTATUS status;
+
+ st->service->periodic.subreq = NULL;
+
+ rc = samba_runcmd_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+
+ if (rc != 0) {
+ status = map_nt_error_from_unix_common(sys_errno);
+ } else {
+ status = NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ ": Failed manual run of samba_kcc - %s\n",
+ nt_errstr(status)));
+ } else {
+ DEBUG(3,("Completed manual run of samba_kcc OK\n"));
+ }
+
+ if (!(st->r->in.req->ctr1.flags & DRSUAPI_DS_EXECUTE_KCC_ASYNCHRONOUS_OPERATION)) {
+ irpc_send_reply(st->msg, status);
+ }
+}
+
+static NTSTATUS kccsrv_execute_kcc(struct irpc_message *msg, struct drsuapi_DsExecuteKCC *r)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status = NT_STATUS_OK;
+ struct kccsrv_service *service = talloc_get_type(msg->private_data, struct kccsrv_service);
+
+ const char * const *samba_kcc_command;
+ struct kcc_manual_runcmd_state *st;
+
+ if (!service->samba_kcc_code) {
+ mem_ctx = talloc_new(service);
+
+ status = kccsrv_simple_update(service, mem_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("kccsrv_execute_kcc failed - %s\n",
+ nt_errstr(status)));
+ }
+ talloc_free(mem_ctx);
+
+ return NT_STATUS_OK;
+ }
+
+ /* Invocation of the samba_kcc python script for replication
+ * topology generation.
+ */
+
+ samba_kcc_command =
+ lpcfg_samba_kcc_command(service->task->lp_ctx);
+
+ st = talloc(msg, struct kcc_manual_runcmd_state);
+ if (st == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ st->msg = msg;
+ st->r = r;
+ st->service = service;
+
+ /* don't run at the same time as an existing child */
+ if (service->periodic.subreq) {
+ status = NT_STATUS_DS_BUSY;
+ return status;
+ }
+
+ DEBUG(2, ("Calling samba_kcc script\n"));
+ service->periodic.subreq = samba_runcmd_send(service,
+ service->task->event_ctx,
+ timeval_current_ofs(40, 0),
+ 2, 0, samba_kcc_command, NULL);
+
+ if (service->periodic.subreq == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ DEBUG(0,(__location__ ": failed - %s\n", nt_errstr(status)));
+ return status;
+ } else {
+ tevent_req_set_callback(service->periodic.subreq,
+ manual_samba_kcc_done, st);
+ }
+
+ if (r->in.req->ctr1.flags & DRSUAPI_DS_EXECUTE_KCC_ASYNCHRONOUS_OPERATION) {
+ /* This actually means reply right away, let it run in the background */
+ } else {
+ /* mark the request as replied async, the caller wants to know when this is finished */
+ msg->defer_reply = true;
+ }
+ return status;
+
+}
+
+static NTSTATUS kccsrv_replica_get_info(struct irpc_message *msg, struct drsuapi_DsReplicaGetInfo *r)
+{
+ return kccdrs_replica_get_info(msg, r);
+}
+
+/*
+ startup the kcc service task
+*/
+static NTSTATUS kccsrv_task_init(struct task_server *task)
+{
+ WERROR status;
+ struct kccsrv_service *service;
+ uint32_t periodic_startup_interval;
+
+ switch (lpcfg_server_role(task->lp_ctx)) {
+ case ROLE_STANDALONE:
+ task_server_terminate(task, "kccsrv: no KCC required in standalone configuration", false);
+ return NT_STATUS_INVALID_DOMAIN_ROLE;
+ case ROLE_DOMAIN_MEMBER:
+ task_server_terminate(task, "kccsrv: no KCC required in domain member configuration", false);
+ return NT_STATUS_INVALID_DOMAIN_ROLE;
+ case ROLE_ACTIVE_DIRECTORY_DC:
+ /* Yes, we want a KCC */
+ break;
+ }
+
+ task_server_set_title(task, "task[kccsrv]");
+
+ service = talloc_zero(task, struct kccsrv_service);
+ if (!service) {
+ task_server_terminate(task, "kccsrv_task_init: out of memory", true);
+ return NT_STATUS_NO_MEMORY;
+ }
+ service->task = task;
+ service->startup_time = timeval_current();
+ task->private_data = service;
+
+ status = kccsrv_init_creds(service);
+ if (!W_ERROR_IS_OK(status)) {
+ task_server_terminate(task,
+ talloc_asprintf(task,
+ "kccsrv: Failed to obtain server credentials: %s\n",
+ win_errstr(status)), true);
+ return werror_to_ntstatus(status);
+ }
+
+ status = kccsrv_connect_samdb(service, task->lp_ctx);
+ if (!W_ERROR_IS_OK(status)) {
+ task_server_terminate(task, talloc_asprintf(task,
+ "kccsrv: Failed to connect to local samdb: %s\n",
+ win_errstr(status)), true);
+ return werror_to_ntstatus(status);
+ }
+
+ status = kccsrv_load_partitions(service);
+ if (!W_ERROR_IS_OK(status)) {
+ task_server_terminate(task, talloc_asprintf(task,
+ "kccsrv: Failed to load partitions: %s\n",
+ win_errstr(status)), true);
+ return werror_to_ntstatus(status);
+ }
+
+ periodic_startup_interval =
+ lpcfg_parm_int(task->lp_ctx, NULL, "kccsrv",
+ "periodic_startup_interval", 15); /* in seconds */
+ service->periodic.interval =
+ lpcfg_parm_int(task->lp_ctx, NULL, "kccsrv",
+ "periodic_interval", 300); /* in seconds */
+
+ /* (kccsrv:samba_kcc=true) will run newer samba_kcc replication
+ * topology generation code.
+ */
+ service->samba_kcc_code = lpcfg_parm_bool(task->lp_ctx, NULL,
+ "kccsrv", "samba_kcc", true);
+
+ status = kccsrv_periodic_schedule(service, periodic_startup_interval);
+ if (!W_ERROR_IS_OK(status)) {
+ task_server_terminate(task, talloc_asprintf(task,
+ "kccsrv: Failed to periodic schedule: %s\n",
+ win_errstr(status)), true);
+ return werror_to_ntstatus(status);
+ }
+
+ irpc_add_name(task->msg_ctx, "kccsrv");
+
+ IRPC_REGISTER(task->msg_ctx, drsuapi, DRSUAPI_DSEXECUTEKCC, kccsrv_execute_kcc, service);
+ IRPC_REGISTER(task->msg_ctx, drsuapi, DRSUAPI_DSREPLICAGETINFO, kccsrv_replica_get_info, service);
+ return NT_STATUS_OK;
+}
+
+/*
+ register ourselves as a available server
+*/
+NTSTATUS server_service_kcc_init(TALLOC_CTX *ctx)
+{
+ static const struct service_details details = {
+ .inhibit_fork_on_accept = true,
+ .inhibit_pre_fork = true,
+ .task_init = kccsrv_task_init,
+ .post_fork = NULL
+ };
+ return register_server_service(ctx, "kcc", &details);
+}