/* Unix SMB/CIFS implementation. Start MIT krb5kdc server within Samba AD Copyright (c) 2014-2016 Andreas Schneider 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 . */ #include "includes.h" #include "talloc.h" #include "tevent.h" #include "system/filesys.h" #include "lib/param/param.h" #include "lib/util/samba_util.h" #include "source4/samba/service.h" #include "source4/samba/process_model.h" #include "kdc/kdc-service-mit.h" #include "dynconfig.h" #include "libds/common/roles.h" #include "lib/socket/netif.h" #include "samba/session.h" #include "dsdb/samdb/samdb.h" #include "kdc/samba_kdc.h" #include "kdc/kdc-server.h" #include "kdc/kpasswd-service.h" #include #include #include "source4/kdc/mit_kdc_irpc.h" /* PROTOTYPES */ static void mitkdc_server_done(struct tevent_req *subreq); static int kdc_server_destroy(struct kdc_server *kdc) { if (kdc->private_data != NULL) { kadm5_destroy(kdc->private_data); } return 0; } static NTSTATUS startup_kpasswd_server(TALLOC_CTX *mem_ctx, struct kdc_server *kdc, struct loadparm_context *lp_ctx, struct interface *ifaces) { int num_interfaces; int i; TALLOC_CTX *tmp_ctx; NTSTATUS status = NT_STATUS_UNSUCCESSFUL; uint16_t kpasswd_port; bool done_wildcard = false; bool ok; kpasswd_port = lpcfg_kpasswd_port(lp_ctx); if (kpasswd_port == 0) { return NT_STATUS_OK; } tmp_ctx = talloc_named_const(mem_ctx, 0, "kpasswd"); if (tmp_ctx == NULL) { return NT_STATUS_NO_MEMORY; } num_interfaces = iface_list_count(ifaces); ok = lpcfg_bind_interfaces_only(lp_ctx); if (!ok) { int num_binds = 0; char **wcard; wcard = iface_list_wildcard(tmp_ctx); if (wcard == NULL) { status = NT_STATUS_NO_MEMORY; goto out; } for (i = 0; wcard[i] != NULL; i++) { status = kdc_add_socket(kdc, kdc->task->model_ops, "kpasswd", wcard[i], kpasswd_port, kpasswd_process, false); if (NT_STATUS_IS_OK(status)) { num_binds++; } } talloc_free(wcard); if (num_binds == 0) { status = NT_STATUS_INVALID_PARAMETER_MIX; goto out; } done_wildcard = true; } for (i = 0; i < num_interfaces; i++) { const char *address = talloc_strdup(tmp_ctx, iface_list_n_ip(ifaces, i)); status = kdc_add_socket(kdc, kdc->task->model_ops, "kpasswd", address, kpasswd_port, kpasswd_process, done_wildcard); if (NT_STATUS_IS_OK(status)) { goto out; } } out: talloc_free(tmp_ctx); return status; } /* * Startup a copy of the krb5kdc as a child daemon */ NTSTATUS mitkdc_task_init(struct task_server *task) { struct tevent_req *subreq; const char * const *kdc_cmd; struct interface *ifaces; char *kdc_config = NULL; struct kdc_server *kdc; krb5_error_code code; NTSTATUS status; kadm5_ret_t ret; kadm5_config_params config; void *server_handle; int dbglvl = 0; task_server_set_title(task, "task[mitkdc_parent]"); switch (lpcfg_server_role(task->lp_ctx)) { case ROLE_STANDALONE: task_server_terminate(task, "The KDC is not required in standalone " "server configuration, terminate!", false); return NT_STATUS_INVALID_DOMAIN_ROLE; case ROLE_DOMAIN_MEMBER: task_server_terminate(task, "The KDC is not required in member " "server configuration", false); return NT_STATUS_INVALID_DOMAIN_ROLE; case ROLE_ACTIVE_DIRECTORY_DC: /* Yes, we want to start the KDC */ break; } /* Load interfaces for kpasswd */ load_interface_list(task, task->lp_ctx, &ifaces); if (iface_list_count(ifaces) == 0) { task_server_terminate(task, "KDC: no network interfaces configured", false); return NT_STATUS_UNSUCCESSFUL; } kdc_config = talloc_asprintf(task, "%s/kdc.conf", lpcfg_private_dir(task->lp_ctx)); if (kdc_config == NULL) { task_server_terminate(task, "KDC: no memory", false); return NT_STATUS_NO_MEMORY; } setenv("KRB5_KDC_PROFILE", kdc_config, 0); TALLOC_FREE(kdc_config); dbglvl = debuglevel_get_class(DBGC_KERBEROS); if (dbglvl >= 10) { char *kdc_trace_file = talloc_asprintf(task, "%s/mit_kdc_trace.log", get_dyn_LOGFILEBASE()); if (kdc_trace_file == NULL) { task_server_terminate(task, "KDC: no memory", false); return NT_STATUS_NO_MEMORY; } setenv("KRB5_TRACE", kdc_trace_file, 1); } /* start it as a child process */ kdc_cmd = lpcfg_mit_kdc_command(task->lp_ctx); subreq = samba_runcmd_send(task, task->event_ctx, timeval_zero(), 1, /* stdout log level */ 0, /* stderr log level */ kdc_cmd, "-n", /* Don't go into background */ #if 0 "-w 2", /* Start two workers */ #endif NULL); if (subreq == NULL) { DEBUG(0, ("Failed to start MIT KDC as child daemon\n")); task_server_terminate(task, "Failed to startup mitkdc task", true); return NT_STATUS_INTERNAL_ERROR; } tevent_req_set_callback(subreq, mitkdc_server_done, task); DEBUG(5,("Started krb5kdc process\n")); status = samba_setup_mit_kdc_irpc(task); if (!NT_STATUS_IS_OK(status)) { task_server_terminate(task, "Failed to setup kdc irpc service", true); } DEBUG(5,("Started irpc service for kdc_server\n")); kdc = talloc_zero(task, struct kdc_server); if (kdc == NULL) { task_server_terminate(task, "KDC: Out of memory", true); return NT_STATUS_NO_MEMORY; } talloc_set_destructor(kdc, kdc_server_destroy); kdc->task = task; kdc->base_ctx = talloc_zero(kdc, struct samba_kdc_base_context); if (kdc->base_ctx == NULL) { task_server_terminate(task, "KDC: Out of memory", true); return NT_STATUS_NO_MEMORY; } kdc->base_ctx->ev_ctx = task->event_ctx; kdc->base_ctx->lp_ctx = task->lp_ctx; initialize_krb5_error_table(); code = smb_krb5_init_context(kdc, kdc->task->lp_ctx, &kdc->smb_krb5_context); if (code != 0) { task_server_terminate(task, "KDC: Unable to initialize krb5 context", true); return NT_STATUS_INTERNAL_ERROR; } code = kadm5_init_krb5_context(&kdc->smb_krb5_context->krb5_context); if (code != 0) { task_server_terminate(task, "KDC: Unable to init kadm5 krb5_context", true); return NT_STATUS_INTERNAL_ERROR; } ZERO_STRUCT(config); config.mask = KADM5_CONFIG_REALM; config.realm = discard_const_p(char, lpcfg_realm(kdc->task->lp_ctx)); ret = kadm5_init(kdc->smb_krb5_context->krb5_context, discard_const_p(char, "kpasswd"), NULL, /* pass */ discard_const_p(char, "kpasswd"), &config, KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, NULL, &server_handle); if (ret != 0) { task_server_terminate(task, "KDC: Initialize kadm5", true); return NT_STATUS_INTERNAL_ERROR; } kdc->private_data = server_handle; code = krb5_db_register_keytab(kdc->smb_krb5_context->krb5_context); if (code != 0) { task_server_terminate(task, "KDC: Unable to KDB", true); return NT_STATUS_INTERNAL_ERROR; } kdc->kpasswd_keytab_name = talloc_asprintf(kdc, "KDB:"); if (kdc->kpasswd_keytab_name == NULL) { task_server_terminate(task, "KDC: Out of memory", true); return NT_STATUS_NO_MEMORY; } kdc->samdb = samdb_connect(kdc, kdc->task->event_ctx, kdc->task->lp_ctx, system_session(kdc->task->lp_ctx), NULL, 0); if (kdc->samdb == NULL) { task_server_terminate(task, "KDC: Unable to connect to samdb", true); return NT_STATUS_CONNECTION_INVALID; } status = startup_kpasswd_server(kdc, kdc, task->lp_ctx, ifaces); if (!NT_STATUS_IS_OK(status)) { task_server_terminate(task, "KDC: Unable to start kpasswd server", true); return status; } DEBUG(5,("Started kpasswd service for kdc_server\n")); return NT_STATUS_OK; } /* * This gets called the kdc exits. */ static void mitkdc_server_done(struct tevent_req *subreq) { struct task_server *task = tevent_req_callback_data(subreq, struct task_server); int sys_errno; int ret; ret = samba_runcmd_recv(subreq, &sys_errno); if (ret != 0) { DEBUG(0, ("The MIT KDC daemon died with exit status %d\n", sys_errno)); } else { DEBUG(0,("The MIT KDC daemon exited normally\n")); } task_server_terminate(task, "mitkdc child process exited", true); } /* Called at MIT KRB5 startup - register ourselves as a server service */ NTSTATUS server_service_mitkdc_init(TALLOC_CTX *mem_ctx); NTSTATUS server_service_mitkdc_init(TALLOC_CTX *mem_ctx) { static const struct service_details details = { .inhibit_fork_on_accept = true, /* * Need to prevent pre-forking on kdc. * The task_init function is run on the master process only * and the irpc process name is registered in it's event loop. * The child worker processes initialise their event loops on * fork, so are not listening for the irpc event. * * The master process does not wait on that event context * the master process is responsible for managing the worker * processes not performing work. */ .inhibit_pre_fork = true, .task_init = mitkdc_task_init, .post_fork = NULL }; return register_server_service(mem_ctx, "kdc", &details); }