X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source4%2Fkdc%2Fkdc.c;h=5700e5bbe8c21fc7bd02e85ce7c7bc6975b203b2;hb=0c89a6f2aa433e54d7af99d9214ddc186784af97;hp=18fc86b8e013b12130de9b8d062ca3e3d5b67953;hpb=c5bf20c5fe7eaa04cd11a7ce4f365aa6ffd7b124;p=samba.git diff --git a/source4/kdc/kdc.c b/source4/kdc/kdc.c index 18fc86b8e01..5700e5bbe8c 100644 --- a/source4/kdc/kdc.c +++ b/source4/kdc/kdc.c @@ -3,7 +3,7 @@ KDC Server startup - Copyright (C) Andrew Bartlett 2005 + Copyright (C) Andrew Bartlett 2005-2008 Copyright (C) Andrew Tridgell 2005 Copyright (C) Stefan Metzmacher 2005 @@ -28,21 +28,24 @@ #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 */ -TALLOC_CTX *kdc_mem_ctx; +/* Disgusting hack to get a mem_ctx and lp_ctx into the hdb plugin, when + * used as a keytab */ +TALLOC_CTX *hdb_samba4_mem_ctx; +struct tevent_context *hdb_samba4_ev_ctx; +struct loadparm_context *hdb_samba4_lp_ctx; /* hold all the info needed to send a reply */ struct kdc_reply { @@ -63,7 +66,7 @@ typedef bool (*kdc_process_fn_t)(struct kdc_server *kdc, struct kdc_socket { struct socket_context *sock; struct kdc_server *kdc; - struct fd_event *fde; + struct tevent_fd *fde; /* a queue of outgoing replies that have been deferred */ struct kdc_reply *send_queue; @@ -196,10 +199,10 @@ static void kdc_recv_handler(struct kdc_socket *kdc_socket) /* handle fd events on a KDC socket */ -static void kdc_socket_handler(struct event_context *ev, struct fd_event *fde, - uint16_t flags, void *private) +static void kdc_socket_handler(struct tevent_context *ev, struct tevent_fd *fde, + uint16_t flags, void *private_data) { - struct kdc_socket *kdc_socket = talloc_get_type(private, struct kdc_socket); + struct kdc_socket *kdc_socket = talloc_get_type(private_data, struct kdc_socket); if (flags & EVENT_FD_WRITE) { kdc_send_handler(kdc_socket); } @@ -216,9 +219,9 @@ static void kdc_tcp_terminate_connection(struct kdc_tcp_connection *kdcconn, con /* receive a full packet on a KDC connection */ -static NTSTATUS kdc_tcp_recv(void *private, DATA_BLOB blob) +static NTSTATUS kdc_tcp_recv(void *private_data, DATA_BLOB blob) { - struct kdc_tcp_connection *kdcconn = talloc_get_type(private, + struct kdc_tcp_connection *kdcconn = talloc_get_type(private_data, struct kdc_tcp_connection); NTSTATUS status = NT_STATUS_UNSUCCESSFUL; TALLOC_CTX *tmp_ctx = talloc_new(kdcconn); @@ -282,7 +285,7 @@ static NTSTATUS kdc_tcp_recv(void *private, DATA_BLOB blob) */ static void kdc_tcp_recv_handler(struct stream_connection *conn, uint16_t flags) { - struct kdc_tcp_connection *kdcconn = talloc_get_type(conn->private, + struct kdc_tcp_connection *kdcconn = talloc_get_type(conn->private_data, struct kdc_tcp_connection); packet_recv(kdcconn->packet); } @@ -290,9 +293,10 @@ static void kdc_tcp_recv_handler(struct stream_connection *conn, uint16_t flags) /* called on a tcp recv error */ -static void kdc_tcp_recv_error(void *private, NTSTATUS status) +static void kdc_tcp_recv_error(void *private_data, NTSTATUS status) { - struct kdc_tcp_connection *kdcconn = talloc_get_type(private, struct kdc_tcp_connection); + struct kdc_tcp_connection *kdcconn = talloc_get_type(private_data, + struct kdc_tcp_connection); kdc_tcp_terminate_connection(kdcconn, nt_errstr(status)); } @@ -301,7 +305,7 @@ static void kdc_tcp_recv_error(void *private, NTSTATUS status) */ static void kdc_tcp_send(struct stream_connection *conn, uint16_t flags) { - struct kdc_tcp_connection *kdcconn = talloc_get_type(conn->private, + struct kdc_tcp_connection *kdcconn = talloc_get_type(conn->private_data, struct kdc_tcp_connection); packet_queue_run(kdcconn->packet); } @@ -341,7 +345,7 @@ static bool kdc_process(struct kdc_server *kdc, } if (k5_reply.length) { *reply = data_blob_talloc(mem_ctx, k5_reply.data, k5_reply.length); - krb5_free_data_contents(kdc->smb_krb5_context->krb5_context, &k5_reply); + krb5_data_free(&k5_reply); } else { *reply = data_blob(NULL, 0); } @@ -353,7 +357,7 @@ static bool kdc_process(struct kdc_server *kdc, */ static void kdc_tcp_generic_accept(struct stream_connection *conn, kdc_process_fn_t process_fn) { - struct kdc_server *kdc = talloc_get_type(conn->private, struct kdc_server); + struct kdc_server *kdc = talloc_get_type(conn->private_data, struct kdc_server); struct kdc_tcp_connection *kdcconn; kdcconn = talloc_zero(conn, struct kdc_tcp_connection); @@ -364,7 +368,7 @@ static void kdc_tcp_generic_accept(struct stream_connection *conn, kdc_process_f kdcconn->conn = conn; kdcconn->kdc = kdc; kdcconn->process = process_fn; - conn->private = kdcconn; + conn->private_data = kdcconn; kdcconn->packet = packet_init(kdcconn); if (kdcconn->packet == NULL) { @@ -408,107 +412,59 @@ static const struct stream_server_ops kpasswdd_tcp_stream_ops = { /* start listening on the given address */ -static NTSTATUS kdc_add_socket(struct kdc_server *kdc, const char *address, - uint16_t kdc_port, uint16_t kpasswd_port) +static NTSTATUS kdc_add_socket(struct kdc_server *kdc, + const struct model_ops *model_ops, + const char *name, + const char *address, + uint16_t port, + const struct stream_server_ops *tcp_stream_ops, + kdc_process_fn_t process) { - const struct model_ops *model_ops; struct kdc_socket *kdc_socket; - struct kdc_socket *kpasswd_socket; - struct socket_address *kdc_address, *kpasswd_address; + struct socket_address *socket_address; NTSTATUS status; kdc_socket = talloc(kdc, struct kdc_socket); NT_STATUS_HAVE_NO_MEMORY(kdc_socket); - kpasswd_socket = talloc(kdc, struct kdc_socket); - NT_STATUS_HAVE_NO_MEMORY(kpasswd_socket); - status = socket_create("ip", SOCKET_TYPE_DGRAM, &kdc_socket->sock, 0); if (!NT_STATUS_IS_OK(status)) { talloc_free(kdc_socket); return status; } - status = socket_create("ip", SOCKET_TYPE_DGRAM, &kpasswd_socket->sock, 0); - if (!NT_STATUS_IS_OK(status)) { - talloc_free(kpasswd_socket); - return status; - } - kdc_socket->kdc = kdc; kdc_socket->send_queue = NULL; - kdc_socket->process = kdc_process; + kdc_socket->process = process; talloc_steal(kdc_socket, kdc_socket->sock); kdc_socket->fde = event_add_fd(kdc->task->event_ctx, kdc, - socket_get_fd(kdc_socket->sock), EVENT_FD_READ, - kdc_socket_handler, kdc_socket); - - kdc_address = socket_address_from_strings(kdc_socket, kdc_socket->sock->backend_name, - address, kdc_port); - NT_STATUS_HAVE_NO_MEMORY(kdc_address); - - status = socket_listen(kdc_socket->sock, kdc_address, 0, 0); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("Failed to bind to %s:%d UDP for kdc - %s\n", - address, kdc_port, nt_errstr(status))); - talloc_free(kdc_socket); - return status; - } - - kpasswd_socket->kdc = kdc; - kpasswd_socket->send_queue = NULL; - kpasswd_socket->process = kpasswdd_process; - - talloc_steal(kpasswd_socket, kpasswd_socket->sock); - - kpasswd_socket->fde = event_add_fd(kdc->task->event_ctx, kdc, - socket_get_fd(kpasswd_socket->sock), EVENT_FD_READ, - kdc_socket_handler, kpasswd_socket); + socket_get_fd(kdc_socket->sock), EVENT_FD_READ, + kdc_socket_handler, kdc_socket); - kpasswd_address = socket_address_from_strings(kpasswd_socket, kpasswd_socket->sock->backend_name, - address, kpasswd_port); - NT_STATUS_HAVE_NO_MEMORY(kpasswd_address); + socket_address = socket_address_from_strings(kdc_socket, kdc_socket->sock->backend_name, + address, port); + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(socket_address, kdc_socket); - status = socket_listen(kpasswd_socket->sock, kpasswd_address, 0, 0); + status = socket_listen(kdc_socket->sock, socket_address, 0, 0); if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("Failed to bind to %s:%d UDP for kpasswd - %s\n", - address, kpasswd_port, nt_errstr(status))); - talloc_free(kpasswd_socket); - return status; - } - - /* 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"); - 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, - &kdc_tcp_stream_ops, - "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))); + DEBUG(0,("Failed to bind to %s:%d UDP for %s - %s\n", + address, port, name, nt_errstr(status))); talloc_free(kdc_socket); return status; } - status = stream_setup_socket(kdc->task->event_ctx, model_ops, - &kpasswdd_tcp_stream_ops, - "ip", address, &kpasswd_port, + status = stream_setup_socket(kdc->task->event_ctx, + kdc->task->lp_ctx, + model_ops, + tcp_stream_ops, + "ip", address, &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))); + address, port, nt_errstr(status))); talloc_free(kdc_socket); return status; } @@ -520,19 +476,46 @@ static NTSTATUS kdc_add_socket(struct kdc_server *kdc, const char *address, /* setup our listening sockets on the configured network interfaces */ -static NTSTATUS kdc_startup_interfaces(struct kdc_server *kdc, struct loadparm_context *lp_ctx) +static NTSTATUS kdc_startup_interfaces(struct kdc_server *kdc, struct loadparm_context *lp_ctx, + struct interface *ifaces) { - int num_interfaces = iface_count(lp_ctx); + const struct model_ops *model_ops; + int num_interfaces; TALLOC_CTX *tmp_ctx = talloc_new(kdc); NTSTATUS status; - int i; + + /* 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_startup(kdc->task->event_ctx, "single"); + if (!model_ops) { + DEBUG(0,("Can't find 'single' process model_ops\n")); + return NT_STATUS_INTERNAL_ERROR; + } + + num_interfaces = iface_count(ifaces); for (i=0; iprivate_data, 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; +} /* @@ -558,21 +631,24 @@ static void kdc_task_init(struct task_server *task) struct kdc_server *kdc; NTSTATUS status; krb5_error_code ret; + struct interface *ifaces; switch (lp_server_role(task->lp_ctx)) { case ROLE_STANDALONE: - task_server_terminate(task, "kdc: no KDC required in standalone configuration"); + task_server_terminate(task, "kdc: no KDC required in standalone configuration", false); return; case ROLE_DOMAIN_MEMBER: - task_server_terminate(task, "kdc: no KDC required in member server configuration"); + task_server_terminate(task, "kdc: no KDC required in member server configuration", false); return; case ROLE_DOMAIN_CONTROLLER: /* Yes, we want a KDC */ break; } - if (iface_count(task->lp_ctx) == 0) { - task_server_terminate(task, "kdc: no network interfaces configured"); + load_interfaces(task, lp_interfaces(task->lp_ctx), &ifaces); + + if (iface_count(ifaces) == 0) { + task_server_terminate(task, "kdc: no network interfaces configured", false); return; } @@ -580,7 +656,7 @@ static void kdc_task_init(struct task_server *task) kdc = talloc(task, struct kdc_server); if (kdc == NULL) { - task_server_terminate(task, "kdc: out of memory"); + task_server_terminate(task, "kdc: out of memory", true); return; } @@ -592,7 +668,7 @@ static void kdc_task_init(struct task_server *task) if (ret) { DEBUG(1,("kdc_task_init: krb5_init_context failed (%s)\n", error_message(ret))); - task_server_terminate(task, "kdc: krb5_init_context failed"); + task_server_terminate(task, "kdc: krb5_init_context failed", true); return; } @@ -601,49 +677,73 @@ static void kdc_task_init(struct task_server *task) ret = krb5_kdc_get_config(kdc->smb_krb5_context->krb5_context, &kdc->config); if(ret) { - task_server_terminate(task, "kdc: failed to get KDC configuration"); + task_server_terminate(task, "kdc: failed to get KDC configuration", true); return; } - + kdc->config->logf = kdc->smb_krb5_context->logf; kdc->config->db = talloc(kdc, struct HDB *); if (!kdc->config->db) { - task_server_terminate(task, "kdc: out of memory"); + task_server_terminate(task, "kdc: out of memory", true); return; } kdc->config->num_db = 1; - status = kdc_hdb_ldb_create(kdc, task->lp_ctx, - kdc->smb_krb5_context->krb5_context, - &kdc->config->db[0], NULL); + status = hdb_samba4_create_kdc(kdc, task->event_ctx, task->lp_ctx, + kdc->smb_krb5_context->krb5_context, + &kdc->config->db[0]); if (!NT_STATUS_IS_OK(status)) { - task_server_terminate(task, "kdc: hdb_ldb_create (setup KDC database) failed"); + task_server_terminate(task, "kdc: hdb_samba4_create_kdc (setup KDC database) failed", true); return; } + /* Register hdb-samba4 hooks for use as a keytab */ + + kdc->hdb_samba4_context = talloc(kdc, struct hdb_samba4_context); + if (!kdc->hdb_samba4_context) { + task_server_terminate(task, "kdc: out of memory", true); + return; + } + + kdc->hdb_samba4_context->ev_ctx = task->event_ctx; + kdc->hdb_samba4_context->lp_ctx = task->lp_ctx; + + 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", true); + 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"); + task_server_terminate(task, "kdc: failed to register hdb keytab", true); return; } /* 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"); + task_server_terminate(task, "kdc: failed to register hdb keytab", true); return; } krb5_kdc_windc_init(kdc->smb_krb5_context->krb5_context); - kdc_mem_ctx = kdc->smb_krb5_context; - /* start listening on the configured network interfaces */ - status = kdc_startup_interfaces(kdc, task->lp_ctx); + status = kdc_startup_interfaces(kdc, task->lp_ctx, ifaces); + if (!NT_STATUS_IS_OK(status)) { + task_server_terminate(task, "kdc failed to setup interfaces", true); + 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, "kdc failed to setup interfaces"); + task_server_terminate(task, "nbtd failed to setup monitoring", true); return; } @@ -651,18 +751,8 @@ static void kdc_task_init(struct task_server *task) } -/* - called on startup of the KDC service -*/ -static NTSTATUS kdc_init(struct event_context *event_ctx, - struct loadparm_context *lp_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); }