diff options
-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 |