KDC Server startup
- Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2008
Copyright (C) Andrew Tridgell 2005
Copyright (C) Stefan Metzmacher 2005
#include "smbd/process_model.h"
#include "lib/events/events.h"
#include "lib/socket/socket.h"
-#include "kdc/kdc.h"
#include "system/network.h"
-#include "lib/util/dlinklist.h"
+#include "../lib/util/dlinklist.h"
#include "lib/messaging/irpc.h"
#include "lib/stream/packet.h"
#include "librpc/gen_ndr/samr.h"
+#include "librpc/gen_ndr/ndr_irpc.h"
+#include "librpc/gen_ndr/ndr_krb5pac.h"
#include "lib/socket/netif.h"
-#include "heimdal/kdc/windc_plugin.h"
-#include "heimdal/lib/krb5/krb5_locl.h"
-#include "heimdal/kdc/kdc_locl.h"
#include "param/param.h"
+#include "kdc/kdc.h"
+#include "librpc/gen_ndr/ndr_misc.h"
-/* Disgusting hack to get a mem_ctx into the hdb plugin, when used as a keytab */
+/* Disgusting hack to get a mem_ctx and lp_ctx into the hdb plugin, when
+ * used as a keytab */
TALLOC_CTX *kdc_mem_ctx;
+struct loadparm_context *kdc_lp_ctx;
/* hold all the info needed to send a reply */
struct kdc_reply {
/* within the kdc task we want to be a single process, so
ask for the single process model ops and pass these to the
stream_setup_socket() call. */
- model_ops = process_model_byname("single");
+ model_ops = process_model_startup(kdc->task->event_ctx, "single");
if (!model_ops) {
DEBUG(0,("Can't find 'single' process model_ops\n"));
talloc_free(kdc_socket);
return NT_STATUS_INTERNAL_ERROR;
}
- status = stream_setup_socket(kdc->task->event_ctx, model_ops,
+ status = stream_setup_socket(kdc->task->event_ctx,
+ kdc->task->lp_ctx,
+ model_ops,
&kdc_tcp_stream_ops,
- "ip", address, &kdc_port, kdc);
+ "ip", address, &kdc_port,
+ lp_socket_options(kdc->task->lp_ctx),
+ kdc);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("Failed to bind to %s:%u TCP - %s\n",
address, kdc_port, nt_errstr(status)));
return status;
}
- status = stream_setup_socket(kdc->task->event_ctx, model_ops,
+ status = stream_setup_socket(kdc->task->event_ctx,
+ kdc->task->lp_ctx,
+ model_ops,
&kpasswdd_tcp_stream_ops,
- "ip", address, &kpasswd_port, kdc);
+ "ip", address, &kpasswd_port,
+ lp_socket_options(kdc->task->lp_ctx),
+ kdc);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("Failed to bind to %s:%u TCP - %s\n",
address, kpasswd_port, nt_errstr(status)));
/*
setup our listening sockets on the configured network interfaces
*/
-static NTSTATUS kdc_startup_interfaces(struct kdc_server *kdc)
+static NTSTATUS kdc_startup_interfaces(struct kdc_server *kdc, struct loadparm_context *lp_ctx,
+ struct interface *ifaces)
{
- int num_interfaces = iface_count();
+ int num_interfaces;
TALLOC_CTX *tmp_ctx = talloc_new(kdc);
NTSTATUS status;
-
int i;
+
+ num_interfaces = iface_count(ifaces);
for (i=0; i<num_interfaces; i++) {
- const char *address = talloc_strdup(tmp_ctx, iface_n_ip(i));
- status = kdc_add_socket(kdc, address, lp_krb5_port(global_loadparm),
- lp_kpasswd_port(global_loadparm));
+ const char *address = talloc_strdup(tmp_ctx, iface_n_ip(ifaces, i));
+ status = kdc_add_socket(kdc, address, lp_krb5_port(lp_ctx),
+ lp_kpasswd_port(lp_ctx));
NT_STATUS_NOT_OK_RETURN(status);
}
};
+static NTSTATUS kdc_check_generic_kerberos(struct irpc_message *msg,
+ struct kdc_check_generic_kerberos *r)
+{
+ struct PAC_Validate pac_validate;
+ DATA_BLOB srv_sig;
+ struct PAC_SIGNATURE_DATA kdc_sig;
+ struct kdc_server *kdc = talloc_get_type(msg->private, struct kdc_server);
+ enum ndr_err_code ndr_err;
+ krb5_enctype etype;
+ int ret;
+ hdb_entry_ex ent;
+ krb5_principal principal;
+ krb5_keyblock keyblock;
+ Key *key;
+
+ /* There is no reply to this request */
+ r->out.generic_reply = data_blob(NULL, 0);
+
+ ndr_err = ndr_pull_struct_blob(&r->in.generic_request, msg,
+ lp_iconv_convenience(kdc->task->lp_ctx),
+ &pac_validate,
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_Validate);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (pac_validate.MessageType != 3) {
+ /* We don't implement any other message types - such as certificate validation - yet */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (pac_validate.ChecksumAndSignature.length != (pac_validate.ChecksumLength + pac_validate.SignatureLength)
+ || pac_validate.ChecksumAndSignature.length < pac_validate.ChecksumLength
+ || pac_validate.ChecksumAndSignature.length < pac_validate.SignatureLength ) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ srv_sig = data_blob_const(pac_validate.ChecksumAndSignature.data,
+ pac_validate.ChecksumLength);
+
+ if (pac_validate.SignatureType == CKSUMTYPE_HMAC_MD5) {
+ etype = ETYPE_ARCFOUR_HMAC_MD5;
+ } else {
+ ret = krb5_cksumtype_to_enctype(kdc->smb_krb5_context->krb5_context, pac_validate.SignatureType,
+ &etype);
+ if (ret != 0) {
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ }
+
+ ret = krb5_make_principal(kdc->smb_krb5_context->krb5_context, &principal,
+ lp_realm(kdc->task->lp_ctx),
+ "krbtgt", lp_realm(kdc->task->lp_ctx),
+ NULL);
+
+ if (ret != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = kdc->config->db[0]->hdb_fetch(kdc->smb_krb5_context->krb5_context,
+ kdc->config->db[0],
+ principal,
+ HDB_F_GET_KRBTGT | HDB_F_DECRYPT,
+ &ent);
+
+ if (ret != 0) {
+ hdb_free_entry(kdc->smb_krb5_context->krb5_context, &ent);
+ krb5_free_principal(kdc->smb_krb5_context->krb5_context, principal);
+
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ ret = hdb_enctype2key(kdc->smb_krb5_context->krb5_context, &ent.entry, etype, &key);
+
+ if (ret != 0) {
+ hdb_free_entry(kdc->smb_krb5_context->krb5_context, &ent);
+ krb5_free_principal(kdc->smb_krb5_context->krb5_context, principal);
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ keyblock = key->key;
+
+ kdc_sig.type = pac_validate.SignatureType;
+ kdc_sig.signature = data_blob_const(&pac_validate.ChecksumAndSignature.data[pac_validate.ChecksumLength],
+ pac_validate.SignatureLength);
+ ret = check_pac_checksum(msg, srv_sig, &kdc_sig,
+ kdc->smb_krb5_context->krb5_context, &keyblock);
+
+ hdb_free_entry(kdc->smb_krb5_context->krb5_context, &ent);
+ krb5_free_principal(kdc->smb_krb5_context->krb5_context, principal);
+
+ if (ret != 0) {
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+static struct hdb_method hdb_samba4 = {
+ .interface_version = HDB_INTERFACE_VERSION,
+ .prefix = "samba4:",
+ .create = hdb_samba4_create
+};
+
/*
startup the kdc task
*/
struct kdc_server *kdc;
NTSTATUS status;
krb5_error_code ret;
+ struct interface *ifaces;
- switch (lp_server_role(global_loadparm)) {
+ switch (lp_server_role(task->lp_ctx)) {
case ROLE_STANDALONE:
task_server_terminate(task, "kdc: no KDC required in standalone configuration");
return;
break;
}
- if (iface_count() == 0) {
+ load_interfaces(task, lp_interfaces(task->lp_ctx), &ifaces);
+
+ if (iface_count(ifaces) == 0) {
task_server_terminate(task, "kdc: no network interfaces configured");
return;
}
initialize_krb5_error_table();
- ret = smb_krb5_init_context(kdc, task->event_ctx, &kdc->smb_krb5_context);
+ ret = smb_krb5_init_context(kdc, task->event_ctx, task->lp_ctx, &kdc->smb_krb5_context);
if (ret) {
DEBUG(1,("kdc_task_init: krb5_init_context failed (%s)\n",
error_message(ret)));
}
kdc->config->num_db = 1;
- status = kdc_hdb_ldb_create(kdc, kdc->smb_krb5_context->krb5_context,
+ status = kdc_hdb_samba4_create(kdc, task->event_ctx, task->lp_ctx,
+ kdc->smb_krb5_context->krb5_context,
&kdc->config->db[0], NULL);
if (!NT_STATUS_IS_OK(status)) {
task_server_terminate(task, "kdc: hdb_ldb_create (setup KDC database) failed");
return;
}
+
+ /* Register hdb-samba4 hooks */
+ ret = krb5_plugin_register(kdc->smb_krb5_context->krb5_context,
+ PLUGIN_TYPE_DATA, "hdb",
+ &hdb_samba4);
+ if(ret) {
+ task_server_terminate(task, "kdc: failed to register hdb keytab");
+ return;
+ }
+
ret = krb5_kt_register(kdc->smb_krb5_context->krb5_context, &hdb_kt_ops);
if(ret) {
task_server_terminate(task, "kdc: failed to register hdb keytab");
}
/* Registar WinDC hooks */
- ret = _krb5_plugin_register(kdc->smb_krb5_context->krb5_context,
- PLUGIN_TYPE_DATA, "windc",
- &windc_plugin_table);
+ ret = krb5_plugin_register(kdc->smb_krb5_context->krb5_context,
+ PLUGIN_TYPE_DATA, "windc",
+ &windc_plugin_table);
if(ret) {
task_server_terminate(task, "kdc: failed to register hdb keytab");
return;
krb5_kdc_windc_init(kdc->smb_krb5_context->krb5_context);
kdc_mem_ctx = kdc->smb_krb5_context;
+ kdc_lp_ctx = task->lp_ctx;
/* start listening on the configured network interfaces */
- status = kdc_startup_interfaces(kdc);
+ status = kdc_startup_interfaces(kdc, task->lp_ctx, ifaces);
if (!NT_STATUS_IS_OK(status)) {
task_server_terminate(task, "kdc failed to setup interfaces");
return;
}
+ status = IRPC_REGISTER(task->msg_ctx, irpc, KDC_CHECK_GENERIC_KERBEROS,
+ kdc_check_generic_kerberos, kdc);
+ if (!NT_STATUS_IS_OK(status)) {
+ task_server_terminate(task, "nbtd failed to setup monitoring");
+ return;
+ }
+
irpc_add_name(task->msg_ctx, "kdc_server");
}
-/*
- called on startup of the KDC service
-*/
-static NTSTATUS kdc_init(struct event_context *event_ctx,
- const struct model_ops *model_ops)
-{
- return task_server_startup(event_ctx, model_ops, kdc_task_init);
-}
-
/* called at smbd startup - register ourselves as a server service */
NTSTATUS server_service_kdc_init(void)
{
- return register_server_service("kdc", kdc_init);
+ return register_server_service("kdc", kdc_task_init);
}