diff options
author | Andrew Bartlett <abartlet@samba.org> | 2005-09-28 01:09:10 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 13:39:04 -0500 |
commit | 8407a1a8665e188d9dc6774ce1535802e4e3cb29 (patch) | |
tree | a3823cbe5ff8762794eda3aba80d8acf9f0573f0 | |
parent | 0b2c6aec9217c40324dddcc2fef376f5a8c5c27d (diff) | |
download | samba-8407a1a8665e188d9dc6774ce1535802e4e3cb29.tar.gz samba-8407a1a8665e188d9dc6774ce1535802e4e3cb29.tar.bz2 samba-8407a1a8665e188d9dc6774ce1535802e4e3cb29.zip |
r10561: 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 handle this very
specific case of one udp packet and reply. Likewise I also handle
TCP, stolen from the winbind code.
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.
Andrew Bartlett
(This used to be commit 3b09f9e8f9f6f645cd03073ef833c8d0fb0d84e2)
-rw-r--r-- | source4/auth/kerberos/krb5_init_context.c | 353 | ||||
-rw-r--r-- | source4/heimdal/lib/krb5/context.c | 1 | ||||
-rw-r--r-- | source4/heimdal/lib/krb5/krb5-protos.h | 5 | ||||
-rw-r--r-- | source4/heimdal/lib/krb5/krb5.h | 8 | ||||
-rw-r--r-- | source4/heimdal/lib/krb5/send_to_kdc.c | 55 |
5 files changed, 412 insertions, 10 deletions
diff --git a/source4/auth/kerberos/krb5_init_context.c b/source4/auth/kerberos/krb5_init_context.c index 62260702d5..673dbe62f7 100644 --- a/source4/auth/kerberos/krb5_init_context.c +++ b/source4/auth/kerberos/krb5_init_context.c @@ -3,7 +3,9 @@ Wrapper for krb5_init_context Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005 - + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Stefan Metzmacher 2004 + 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,6 +24,28 @@ #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; + NTSTATUS status; + DATA_BLOB request, reply, partial; + + size_t partial_read; + + krb5_krbhst_info *hi; +}; static int smb_krb5_context_destroy_1(void *ptr) { @@ -51,11 +75,324 @@ static void smb_krb5_debug_wrapper(const char *timestr, const char *msg, void *p 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; + + switch (smb_krb5->hi->proto) { + case KRB5_KRBHST_UDP: + 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); + break; + case KRB5_KRBHST_TCP: + if (smb_krb5->partial.length == 0) { + smb_krb5->partial = data_blob_talloc(smb_krb5, NULL, 4); + if (!smb_krb5->partial.data) { + smb_krb5->status = NT_STATUS_NO_MEMORY; + return; + } + + smb_krb5->partial_read = 0; + } + + /* read in the packet length */ + if (smb_krb5->partial_read < 4) { + uint32_t packet_length; + + status = socket_recv(smb_krb5->sock, + smb_krb5->partial.data + smb_krb5->partial_read, + 4 - smb_krb5->partial_read, + &nread, 0); + if (NT_STATUS_IS_ERR(status)) { + smb_krb5->status = status; + return; + } + if (!NT_STATUS_IS_OK(status)) { + return; + } + + smb_krb5->partial_read += nread; + if (smb_krb5->partial_read != 4) { + return; + } + + packet_length = RIVAL(smb_krb5->partial.data, 0); + + smb_krb5->partial.data = talloc_realloc(smb_krb5, smb_krb5->partial.data, + uint8_t, packet_length + 4); + if (!smb_krb5->partial.data) { + smb_krb5->status = NT_STATUS_NO_MEMORY; + return; + } + + smb_krb5->partial.length = packet_length + 4; + } + + /* read in the body */ + status = socket_recv(smb_krb5->sock, + smb_krb5->partial.data + smb_krb5->partial_read, + smb_krb5->partial.length - smb_krb5->partial_read, + &nread, 0); + if (NT_STATUS_IS_ERR(status)) { + smb_krb5->status = status; + return; + } + if (!NT_STATUS_IS_OK(status)) return; + + smb_krb5->partial_read += nread; + + if (smb_krb5->partial_read != smb_krb5->partial.length) return; + + smb_krb5->reply = data_blob_talloc(smb_krb5, smb_krb5->partial.data + 4, smb_krb5->partial.length - 4); + break; + case KRB5_KRBHST_HTTP: + return; + } +} + +/* + 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; + } + smb_krb5->hi = hi; + + switch (a->ai_family) { + case PF_INET: + name = "ipv4"; + break; + case PF_INET6: + name = "ipv6"; + break; + default: + talloc_free(smb_krb5); + return EINVAL; + } + + status = NT_STATUS_INVALID_PARAMETER; + 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; + case KRB5_KRBHST_HTTP: + 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); + + switch (hi->proto) { + case KRB5_KRBHST_UDP: + smb_krb5->request = send_blob; + break; + case KRB5_KRBHST_TCP: + smb_krb5->request = data_blob_talloc(smb_krb5, NULL, send_blob.length + 4); + RSIVAL(smb_krb5->request.data, 0, send_blob.length); + memcpy(smb_krb5->request.data+4, send_blob.data, send_blob.length); + break; + case KRB5_KRBHST_HTTP: + talloc_free(smb_krb5); + return EINVAL; + } + smb_krb5->timeout = False; + smb_krb5->status = NT_STATUS_OK; + smb_krb5->reply = data_blob(NULL, 0); + smb_krb5->partial = data_blob(NULL, 0); + + while (!smb_krb5->timeout && (NT_STATUS_IS_OK(smb_krb5->status)) && !smb_krb5->reply.length) { + if (event_loop_once(ev) != 0) { + talloc_free(smb_krb5); + return EINVAL; + } + } + if (!NT_STATUS_IS_OK(smb_krb5->status)) { + DEBUG(2,("Error reading smb_krb5 reply packet: %s\n", nt_errstr(smb_krb5->status))); + talloc_free(smb_krb5); + continue; + } + + 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 +452,25 @@ static void smb_krb5_debug_wrapper(const char *timestr, const char *msg, void *p } 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; } diff --git a/source4/heimdal/lib/krb5/context.c b/source4/heimdal/lib/krb5/context.c index 3140f1b08f..594665235b 100644 --- a/source4/heimdal/lib/krb5/context.c +++ b/source4/heimdal/lib/krb5/context.c @@ -263,6 +263,7 @@ krb5_free_context(krb5_context context) 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); diff --git a/source4/heimdal/lib/krb5/krb5-protos.h b/source4/heimdal/lib/krb5/krb5-protos.h index 8db553e6e3..681ac4189b 100644 --- a/source4/heimdal/lib/krb5/krb5-protos.h +++ b/source4/heimdal/lib/krb5/krb5-protos.h @@ -3432,6 +3432,11 @@ krb5_write_safe_message ( 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 } diff --git a/source4/heimdal/lib/krb5/krb5.h b/source4/heimdal/lib/krb5/krb5.h index 90b239cf0d..800683ef0c 100644 --- a/source4/heimdal/lib/krb5/krb5.h +++ b/source4/heimdal/lib/krb5/krb5.h @@ -444,6 +444,7 @@ typedef struct krb5_context_data { 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 @@ enum { 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; diff --git a/source4/heimdal/lib/krb5/send_to_kdc.c b/source4/heimdal/lib/krb5/send_to_kdc.c index d55f8dc692..7bb4adabbd 100644 --- a/source4/heimdal/lib/krb5/send_to_kdc.c +++ b/source4/heimdal/lib/krb5/send_to_kdc.c @@ -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,11 +353,27 @@ krb5_sendto (krb5_context context, 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) { + return 0; + } else { + continue; + } + } + if(hi->proto == KRB5_KRBHST_HTTP && context->http_proxy) { - if (send_via_proxy (context, hi, send_data, receive)) + if (send_via_proxy (context, hi, send_data, receive)) { + /* Try again, with next host */ continue; - else - goto out; + } else { + /* Success */ + return 0; + } } ret = krb5_krbhst_get_addrinfo(context, hi, &ai); @@ -363,16 +403,15 @@ krb5_sendto (krb5_context context, break; } close (fd); - if(ret == 0 && receive->length != 0) - goto out; + if(ret == 0 && receive->length != 0) { + return 0; + } } } krb5_krbhst_reset(context, handle); } krb5_clear_error_string (context); - ret = KRB5_KDC_UNREACH; -out: - return ret; + return KRB5_KDC_UNREACH; } krb5_error_code KRB5_LIB_FUNCTION |