Update copyright
[kai/samba.git] / source4 / kdc / kdc.c
index b3d5ccdb5e6ef263e9240def585bfd06e6606a49..b7009b030f1678b73c15e910e10fb50ea3a9a098 100644 (file)
@@ -3,7 +3,7 @@
 
    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/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 {
@@ -51,7 +53,7 @@ struct kdc_reply {
        DATA_BLOB packet;
 };
 
-typedef BOOL (*kdc_process_fn_t)(struct kdc_server *kdc,
+typedef bool (*kdc_process_fn_t)(struct kdc_server *kdc,
                                 TALLOC_CTX *mem_ctx, 
                                 DATA_BLOB *input, 
                                 DATA_BLOB *reply,
@@ -311,7 +313,7 @@ static void kdc_tcp_send(struct stream_connection *conn, uint16_t flags)
    calling conventions
 */
 
-static BOOL kdc_process(struct kdc_server *kdc,
+static bool kdc_process(struct kdc_server *kdc,
                        TALLOC_CTX *mem_ctx, 
                        DATA_BLOB *input, 
                        DATA_BLOB *reply,
@@ -337,7 +339,7 @@ static BOOL kdc_process(struct kdc_server *kdc,
                                            datagram_reply);
        if (ret == -1) {
                *reply = data_blob(NULL, 0);
-               return False;
+               return false;
        }
        if (k5_reply.length) {
                *reply = data_blob_talloc(mem_ctx, k5_reply.data, k5_reply.length);
@@ -345,7 +347,7 @@ static BOOL kdc_process(struct kdc_server *kdc,
        } else {
                *reply = data_blob(NULL, 0);    
        }
-       return True;
+       return true;
 }
 
 /*
@@ -489,9 +491,13 @@ static NTSTATUS kdc_add_socket(struct kdc_server *kdc, const char *address,
                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)));
@@ -499,9 +505,13 @@ static NTSTATUS kdc_add_socket(struct kdc_server *kdc, const char *address,
                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)));
@@ -516,18 +526,20 @@ 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)
+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(), 
-                                       lp_kpasswd_port());
+               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);
        }
 
@@ -546,6 +558,108 @@ static struct krb5plugin_windc_ftable windc_plugin_table = {
 };
 
 
+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 0
+       /* Windows does not check this */
+       if (pac_validate.MessageType != 3) {
+               /* We don't implement any other message types - such as certificate validation - yet */
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+#endif 
+       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;
+}
+
+
+
 /*
   startup the kdc task
 */
@@ -554,8 +668,9 @@ 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()) {
+       switch (lp_server_role(task->lp_ctx)) {
        case ROLE_STANDALONE:
                task_server_terminate(task, "kdc: no KDC required in standalone configuration");
                return;
@@ -567,7 +682,9 @@ static void kdc_task_init(struct task_server *task)
                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;
        }
@@ -584,7 +701,7 @@ static void kdc_task_init(struct task_server *task)
 
        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)));
@@ -609,7 +726,8 @@ static void kdc_task_init(struct task_server *task)
        }
        kdc->config->num_db = 1;
                
-       status = kdc_hdb_ldb_create(kdc, kdc->smb_krb5_context->krb5_context, 
+       status = kdc_hdb_ldb_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");
@@ -623,9 +741,9 @@ static void kdc_task_init(struct task_server *task)
        }
 
        /* 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;
@@ -634,29 +752,28 @@ static void kdc_task_init(struct task_server *task)
        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);
 }