[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[SAMBA4][PATCH] Add hooks to take over KDC socket routines



This patch takes over KDC socket routines in Heimdal, and directs them
at the Samba4 socket layer. 

The intention here is to ensure that other events may be processed while
heimdal is waiting on the KDC.  The interface is designed to be
sufficiently flexible, so that the plugin may choose how to time
communication with the KDC (ie multiple outstanding requests, looking
for a functional KDC).

I've hacked the socket layer out of cldap.c to instead handle this very
specific case of one udp packet and reply, but I hope that the socket
infrastructure might improve before I need to seriously handle TCP.
Because there is only every one packet, there is no send queue.

This same plugin system might also be useful for a self-contained
testing mode in Heimdal, in conjunction with libkdc.  I would suggest
using socket-wrapper instead however.

Comments?

Andrew Bartlett
-- 
Andrew Bartlett                                http://samba.org/~abartlet/
Samba Developer, SuSE Labs, Novell Inc.        http://suse.de
Authentication Developer, Samba Team           http://samba.org
Student Network Administrator, Hawker College  http://hawkerc.net
Index: heimdal/lib/krb5/send_to_kdc.c
===================================================================
--- heimdal/lib/krb5/send_to_kdc.c	(revision 10542)
+++ heimdal/lib/krb5/send_to_kdc.c	(working copy)
@@ -35,6 +35,30 @@
 
 RCSID("$Id: send_to_kdc.c,v 1.56 2005/06/17 04:33:11 lha Exp $");
 
+struct send_and_recv {
+	krb5_send_and_recv_func_t func;
+	krb5_send_and_recv_close_func_t close;
+	void *data;
+};
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_send_recv_func(krb5_context context,
+			krb5_send_and_recv_func_t func,
+			krb5_send_and_recv_close_func_t close_fn,
+			void *data)
+{
+	free(context->send_and_recv);
+	context->send_and_recv = malloc(sizeof(*context->send_and_recv));
+	if (!context->send_and_recv) {
+		return ENOMEM;
+	}
+	context->send_and_recv->func = func;
+	context->send_and_recv->close = close_fn;
+	context->send_and_recv->data = data;
+	return 0;
+}
+
+
 /*
  * send the data in `req' on the socket `fd' (which is datagram iff udp)
  * waiting `tmout' for a reply and returning the reply in `rep'.
@@ -329,6 +353,19 @@
 	 while (krb5_krbhst_next(context, handle, &hi) == 0) {
 	     struct addrinfo *ai, *a;
 
+	     if (context->send_and_recv) {
+		 ret = context->send_and_recv->func(context, 
+						    context->send_and_recv->data, 
+						    hi, send_data, receive);
+		 if (ret) {
+		     continue;
+		 } else if (receive->length != 0) {
+		     goto out;
+		 } else {
+		     continue;
+		 }
+	     }
+
 	     if(hi->proto == KRB5_KRBHST_HTTP && context->http_proxy) {
 		 if (send_via_proxy (context, hi, send_data, receive))
 		     continue;
Index: heimdal/lib/krb5/krb5.h
===================================================================
--- heimdal/lib/krb5/krb5.h	(revision 10542)
+++ heimdal/lib/krb5/krb5.h	(working copy)
@@ -444,6 +444,7 @@
     void *mutex;			/* protects error_string/error_buf */
     int large_msg_size;
     krb5_boolean fdns;                  /* Lookup hostnames to find full name, or send as-is */
+    struct send_and_recv *send_and_recv; /* Alternate functions for KDC communication */
 } krb5_context_data;
 
 enum {
@@ -744,6 +745,13 @@
     KRB5_KRBHST_FLAGS_LARGE_MSG	  = 2
 };
 
+typedef int (*krb5_send_and_recv_func_t)(krb5_context,
+					 void *,
+					 krb5_krbhst_info *,
+					 const krb5_data *,
+					 krb5_data *);
+typedef void (*krb5_send_and_recv_close_func_t)(krb5_context, void*);
+
 struct credentials; /* this is to keep the compiler happy */
 struct getargs;
 struct sockaddr;
Index: heimdal/lib/krb5/krb5-protos.h
===================================================================
--- heimdal/lib/krb5/krb5-protos.h	(revision 10542)
+++ heimdal/lib/krb5/krb5-protos.h	(working copy)
@@ -3432,6 +3432,11 @@
 
 krb5_error_code KRB5_LIB_FUNCTION
 krb5_xfree (void */*ptr*/);
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_send_recv_func(krb5_context context,
+			krb5_send_and_recv_func_t func,
+			krb5_send_and_recv_close_func_t close_fn,
+			void *data);
 
 #ifdef __cplusplus
 }
Index: heimdal/lib/krb5/context.c
===================================================================
--- heimdal/lib/krb5/context.c	(revision 10542)
+++ heimdal/lib/krb5/context.c	(working copy)
@@ -263,6 +263,7 @@
 	krb5_closelog(context, context->warn_dest);
     krb5_set_extra_addresses(context, NULL);
     krb5_set_ignore_addresses(context, NULL);
+    free(context->send_and_recv);
     if (context->mutex != NULL) {
 	HEIMDAL_MUTEX_destroy(context->mutex);
 	free(context->mutex);
Index: auth/gensec/gensec.c
===================================================================
--- auth/gensec/gensec.c	(revision 10542)
+++ auth/gensec/gensec.c	(working copy)
@@ -489,6 +489,22 @@
 	return gensec_start_mech(gensec_security);
 }
 
+/** 
+ * Start a GENSEC sub-mechanism by an internal name
+ *
+ */
+
+NTSTATUS gensec_start_mech_by_name(struct gensec_security *gensec_security, 
+					const char *name) 
+{
+	gensec_security->ops = gensec_security_by_name(name);
+	if (!gensec_security->ops) {
+		DEBUG(3, ("Could not find GENSEC backend for name=%s\n", name));
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+	return gensec_start_mech(gensec_security);
+}
+
 /*
   wrappers for the gensec function pointers
 */
Index: auth/kerberos/krb5_init_context.c
===================================================================
--- auth/kerberos/krb5_init_context.c	(revision 10542)
+++ auth/kerberos/krb5_init_context.c	(working copy)
@@ -3,7 +3,8 @@
    Wrapper for krb5_init_context
 
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
-   
+   Copyright (C) Andrew Tridgell 2005
+
    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 2 of the License, or
@@ -22,7 +23,24 @@
 #include "includes.h"
 #include "system/kerberos.h"
 #include "auth/kerberos/kerberos.h"
+#include "system/network.h"
+#include "system/select.h"
+#include "system/filesys.h"
+#include "lib/socket/socket.h"
+#include "lib/events/events.h"
+/*
+  context structure for operations on cldap packets
+*/
+struct smb_krb5_socket {
+	struct socket_context *sock;
 
+	/* the fd event */
+	struct fd_event *fde;
+
+	BOOL timeout;
+	DATA_BLOB request, reply;
+};
+
 static int smb_krb5_context_destroy_1(void *ptr) 
 {
 	struct smb_krb5_context *ctx = ptr;
@@ -51,11 +69,236 @@
 	DEBUG(3, ("Kerberos: %s\n", msg));
 }
 
+/*
+  handle recv events on a smb_krb5 socket
+*/
+static void smb_krb5_socket_recv(struct smb_krb5_socket *smb_krb5)
+{
+	TALLOC_CTX *tmp_ctx = talloc_new(smb_krb5);
+	NTSTATUS status;
+	DATA_BLOB blob;
+	size_t nread, dsize;
+
+	status = socket_pending(smb_krb5->sock, &dsize);
+	if (!NT_STATUS_IS_OK(status)) {
+		talloc_free(tmp_ctx);
+		return;
+	}
+
+	blob = data_blob_talloc(tmp_ctx, NULL, dsize);
+	if (blob.data == NULL) {
+		talloc_free(tmp_ctx);
+		return;
+	}
+
+	status = socket_recv(smb_krb5->sock, blob.data, blob.length, &nread, 0);
+	if (!NT_STATUS_IS_OK(status)) {
+		talloc_free(tmp_ctx);
+		return;
+	}
+	blob.length = nread;
+
+	DEBUG(2,("Received smb_krb5 packet of length %d\n", 
+		 (int)blob.length));
+	
+	talloc_steal(smb_krb5, blob.data);
+	smb_krb5->reply = blob;
+	talloc_free(tmp_ctx);
+}
+
+/*
+  handle request timeouts
+*/
+static void smb_krb5_request_timeout(struct event_context *event_ctx, 
+				  struct timed_event *te, struct timeval t,
+				  void *private)
+{
+	struct smb_krb5_socket *smb_krb5 = talloc_get_type(private, struct smb_krb5_socket);
+	DEBUG(2,("Timed out smb_krb5 packet\n"));
+	smb_krb5->timeout = True;
+}
+
+/*
+  handle send events on a smb_krb5 socket
+*/
+static void smb_krb5_socket_send(struct smb_krb5_socket *smb_krb5)
+{
+	NTSTATUS status;
+
+	size_t len;
+	
+	len = smb_krb5->request.length;
+	status = socket_send(smb_krb5->sock, &smb_krb5->request, &len, 0);
+
+	if (!NT_STATUS_IS_OK(status)) return;
+	
+	EVENT_FD_READABLE(smb_krb5->fde);
+
+	EVENT_FD_NOT_WRITEABLE(smb_krb5->fde);
+	return;
+}
+
+
+/*
+  handle fd events on a smb_krb5_socket
+*/
+static void smb_krb5_socket_handler(struct event_context *ev, struct fd_event *fde,
+				 uint16_t flags, void *private)
+{
+	struct smb_krb5_socket *smb_krb5 = talloc_get_type(private, struct smb_krb5_socket);
+	if (flags & EVENT_FD_WRITE) {
+		smb_krb5_socket_send(smb_krb5);
+	} 
+	if (flags & EVENT_FD_READ) {
+		smb_krb5_socket_recv(smb_krb5);
+	}
+}
+
+
+static krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
+						   void *data,
+						   krb5_krbhst_info *hi,
+						   const krb5_data *send_buf,
+						   krb5_data *recv_buf)
+{
+	krb5_error_code ret;
+	NTSTATUS status;
+	char *remote_addr;
+	const char *name;
+	struct addrinfo *ai, *a;
+	struct smb_krb5_socket *smb_krb5;
+	int port;
+
+	struct event_context *ev = talloc_get_type(data, struct event_context);
+
+	DATA_BLOB send_blob = data_blob_const(send_buf->data, send_buf->length);
+
+	ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
+	if (ret) {
+		return ret;
+	}
+
+	for (a = ai; a; a = ai->ai_next) {
+		smb_krb5 = talloc(NULL, struct smb_krb5_socket);
+		if (!smb_krb5) {
+			return ENOMEM;
+		}
+		
+		switch (a->ai_family) {
+		case PF_INET:
+			name = "ipv4";
+			break;
+		case PF_INET6:
+			name = "ipv6";
+			break;
+		default:
+			talloc_free(smb_krb5);
+			return EINVAL;
+		}
+		
+		switch (hi->proto) {
+		case KRB5_KRBHST_UDP:
+			status = socket_create(name, SOCKET_TYPE_DGRAM, &smb_krb5->sock, 0);
+			break;
+		case KRB5_KRBHST_TCP:
+			status = socket_create(name, SOCKET_TYPE_STREAM, &smb_krb5->sock, 0);
+			break;
+		default:
+			talloc_free(smb_krb5);
+			return EINVAL;
+		}
+		if (!NT_STATUS_IS_OK(status)) {
+			talloc_free(smb_krb5);
+			continue;
+		}
+
+		talloc_steal(smb_krb5, smb_krb5->sock);
+		
+		switch (a->ai_family) {
+		case PF_INET:
+			remote_addr = talloc_strdup(smb_krb5, inet_ntoa(((struct sockaddr_in *)a->ai_addr)->sin_addr));
+			port = ntohs(((struct sockaddr_in *)a->ai_addr)->sin_port);
+			break;
+		case PF_INET6:
+		{
+			char addr[128];
+			const char *ret_addr;
+			ret_addr = inet_ntop(AF_INET6, &((struct sockaddr_in6 *)a->ai_addr)->sin6_addr, addr, sizeof(addr));
+			if (ret_addr == NULL) {
+				talloc_free(smb_krb5);
+				return EINVAL;
+			}
+	
+			remote_addr = talloc_strdup(smb_krb5, ret_addr);
+			port = ntohs(((struct sockaddr_in6 *)a->ai_addr)->sin6_port);
+			break;
+		}
+		default:
+			talloc_free(smb_krb5);
+			return EINVAL;
+		}
+		
+		status = socket_connect_ev(smb_krb5->sock, NULL, 0, remote_addr, port, 0, ev); 
+		if (!NT_STATUS_IS_OK(status)) {
+			talloc_free(smb_krb5);
+			continue;
+		}
+		talloc_free(remote_addr);
+
+		smb_krb5->fde = event_add_fd(ev, smb_krb5, 
+					     socket_get_fd(smb_krb5->sock), 0,
+					     smb_krb5_socket_handler, smb_krb5);
+
+		event_add_timed(ev, smb_krb5, 
+				timeval_current_ofs(context->kdc_timeout, 0),
+				smb_krb5_request_timeout, smb_krb5);
+
+		EVENT_FD_WRITEABLE(smb_krb5->fde);
+		EVENT_FD_READABLE(smb_krb5->fde);
+		
+		smb_krb5->request = send_blob;
+		smb_krb5->timeout = False;
+		smb_krb5->reply = data_blob(NULL, 0);
+
+		while (!smb_krb5->timeout && !smb_krb5->reply.length) {
+			if (event_loop_once(ev) != 0) {
+				talloc_free(smb_krb5);
+				return EINVAL;
+			}
+		}
+		if (smb_krb5->timeout) {
+			talloc_free(smb_krb5);
+			continue;
+		}
+
+		ret = krb5_data_copy(recv_buf, smb_krb5->reply.data, smb_krb5->reply.length);
+		if (ret) {
+			talloc_free(smb_krb5);
+			return ret;
+		}
+		talloc_free(smb_krb5);
+		
+		break;
+	}
+	if (a) {
+		return 0;
+	}
+	return KRB5_KDC_UNREACH;
+}
+
+/* NO internal data, so nothing to free */
+static void smb_krb5_send_and_recv_close_func(krb5_context context, void *data) 
+{
+	return;
+}
+
+
  krb5_error_code smb_krb5_init_context(void *parent_ctx, 
 				       struct smb_krb5_context **smb_krb5_context) 
 {
 	krb5_error_code ret;
 	TALLOC_CTX *tmp_ctx;
+	struct event_context *ev;
 	
 	initialize_krb5_error_table();
 	
@@ -115,13 +358,25 @@
 	}
 	krb5_set_warn_dest((*smb_krb5_context)->krb5_context, (*smb_krb5_context)->logf);
 
+	ev = event_context_find(*smb_krb5_context);
+	/* Set use of our socket lib */
+	ret = krb5_set_send_recv_func((*smb_krb5_context)->krb5_context, 
+				      smb_krb5_send_and_recv_func, 
+				      smb_krb5_send_and_recv_close_func, ev);
+	if (ret) {
+		DEBUG(1,("krb5_set_send_recv_func failed (%s)\n", 
+			 smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
+		talloc_free(tmp_ctx);
+		return ret;
+	}
+
 	talloc_steal(parent_ctx, *smb_krb5_context);
 	talloc_free(tmp_ctx);
 
 	/* Set options in kerberos */
 
 	(*smb_krb5_context)->krb5_context->fdns = FALSE;
-	
+
 	return 0;
 }
 

This is a digitally signed message part