s4-kerberos: raise the general kerberos debug level to 3
[ira/wip.git] / source4 / auth / kerberos / krb5_init_context.c
index d895d7a3369767a4773401353298de4a041111d8..2ca364032b5443ce2cb0c5ad9a1ac0a9543883b4 100644 (file)
@@ -8,7 +8,7 @@
 
    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
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 #include "system/kerberos.h"
+#include <tevent.h>
 #include "auth/kerberos/kerberos.h"
 #include "lib/socket/socket.h"
+#include "lib/stream/packet.h"
 #include "system/network.h"
-#include "lib/events/events.h"
-#include "roken.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
 
 /*
   context structure for operations on cldap packets
@@ -36,24 +37,25 @@ struct smb_krb5_socket {
        struct socket_context *sock;
 
        /* the fd event */
-       struct fd_event *fde;
+       struct tevent_fd *fde;
 
-       BOOL timeout;
        NTSTATUS status;
-       DATA_BLOB request, reply, partial;
+       DATA_BLOB request, reply;
+       
+       struct packet_context *packet;
 
        size_t partial_read;
 
        krb5_krbhst_info *hi;
 };
 
-static int smb_krb5_context_destroy_1(struct smb_krb5_context *ctx)
+static krb5_error_code smb_krb5_context_destroy_1(struct smb_krb5_context *ctx)
 {
        krb5_free_context(ctx->krb5_context); 
        return 0;
 }
 
-static int smb_krb5_context_destroy_2(struct smb_krb5_context *ctx)
+static krb5_error_code smb_krb5_context_destroy_2(struct smb_krb5_context *ctx)
 {
        /* Otherwise krb5_free_context will try and close what we have already free()ed */
        krb5_set_warn_dest(ctx->krb5_context, NULL);
@@ -63,11 +65,11 @@ static int smb_krb5_context_destroy_2(struct smb_krb5_context *ctx)
 }
 
 /* We never close down the DEBUG system, and no need to unreference the use */
-static void smb_krb5_debug_close(void *private) {
+static void smb_krb5_debug_close(void *private_data) {
        return;
 }
 
-static void smb_krb5_debug_wrapper(const char *timestr, const char *msg, void *private
+static void smb_krb5_debug_wrapper(const char *timestr, const char *msg, void *private_data)
 {
        DEBUG(3, ("Kerberos: %s\n", msg));
 }
@@ -81,110 +83,66 @@ static void smb_krb5_socket_recv(struct smb_krb5_socket *smb_krb5)
        DATA_BLOB blob;
        size_t nread, dsize;
 
-       switch (smb_krb5->hi->proto) {
-       case KRB5_KRBHST_UDP:
-               smb_krb5->status = socket_pending(smb_krb5->sock, &dsize);
-               if (!NT_STATUS_IS_OK(smb_krb5->status)) {
-                       talloc_free(tmp_ctx);
-                       return;
-               }
-
-               blob = data_blob_talloc(tmp_ctx, NULL, dsize);
-               if (blob.data == NULL && dsize != 0) {
-                       smb_krb5->status = NT_STATUS_NO_MEMORY;
-                       talloc_free(tmp_ctx);
-                       return;
-               }
-               
-               smb_krb5->status = socket_recv(smb_krb5->sock, blob.data, blob.length, &nread);
-               if (!NT_STATUS_IS_OK(smb_krb5->status)) {
-                       talloc_free(tmp_ctx);
-                       return;
-               }
-               blob.length = nread;
-
-               if (nread == 0) {
-                       smb_krb5->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
-                       talloc_free(tmp_ctx);
-                       return;
-               }
-               
-               DEBUG(2,("Received smb_krb5 packet of length %d\n", 
-                        (int)blob.length));
-               
-               talloc_steal(smb_krb5, blob.data);
-               smb_krb5->reply = blob;
+       smb_krb5->status = socket_pending(smb_krb5->sock, &dsize);
+       if (!NT_STATUS_IS_OK(smb_krb5->status)) {
+               talloc_free(tmp_ctx);
+               return;
+       }
+       
+       blob = data_blob_talloc(tmp_ctx, NULL, dsize);
+       if (blob.data == NULL && dsize != 0) {
+               smb_krb5->status = NT_STATUS_NO_MEMORY;
+               talloc_free(tmp_ctx);
+               return;
+       }
+       
+       smb_krb5->status = socket_recv(smb_krb5->sock, blob.data, blob.length, &nread);
+       if (!NT_STATUS_IS_OK(smb_krb5->status)) {
                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;
-                       
-                       smb_krb5->status = socket_recv(smb_krb5->sock, 
-                                            smb_krb5->partial.data + smb_krb5->partial_read,
-                                            4 - smb_krb5->partial_read,
-                                            &nread);
-                       /* todo: this should be converted to the packet_*() routines */
-                       if (!NT_STATUS_IS_OK(smb_krb5->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 */
-               smb_krb5->status = socket_recv(smb_krb5->sock, 
-                                    smb_krb5->partial.data + smb_krb5->partial_read,
-                                    smb_krb5->partial.length - smb_krb5->partial_read,
-                                    &nread);
-               if (!NT_STATUS_IS_OK(smb_krb5->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;
        }
+       blob.length = nread;
+       
+       if (nread == 0) {
+               smb_krb5->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+               talloc_free(tmp_ctx);
+               return;
+       }
+       
+       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);
+}
+
+static NTSTATUS smb_krb5_full_packet(void *private_data, DATA_BLOB data)
+{
+       struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
+       talloc_steal(smb_krb5, data.data);
+       smb_krb5->reply = data;
+       smb_krb5->reply.length -= 4;
+       smb_krb5->reply.data += 4;
+       return NT_STATUS_OK;
 }
 
 /*
   handle request timeouts
 */
-static void smb_krb5_request_timeout(struct event_context *event_ctx, 
-                                 struct timed_event *te, struct timeval t,
-                                 void *private)
+static void smb_krb5_request_timeout(struct tevent_context *event_ctx, 
+                                 struct tevent_timer *te, struct timeval t,
+                                 void *private_data)
 {
-       struct smb_krb5_socket *smb_krb5 = talloc_get_type(private, struct smb_krb5_socket);
+       struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
        DEBUG(5,("Timed out smb_krb5 packet\n"));
-       smb_krb5->timeout = True;
+       smb_krb5->status = NT_STATUS_IO_TIMEOUT;
+}
+
+static void smb_krb5_error_handler(void *private_data, NTSTATUS status)
+{
+       struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
+       smb_krb5->status = status;
 }
 
 /*
@@ -201,9 +159,9 @@ static void smb_krb5_socket_send(struct smb_krb5_socket *smb_krb5)
 
        if (!NT_STATUS_IS_OK(status)) return;
        
-       EVENT_FD_READABLE(smb_krb5->fde);
+       TEVENT_FD_READABLE(smb_krb5->fde);
 
-       EVENT_FD_NOT_WRITEABLE(smb_krb5->fde);
+       TEVENT_FD_NOT_WRITEABLE(smb_krb5->fde);
        return;
 }
 
@@ -211,24 +169,46 @@ static void smb_krb5_socket_send(struct smb_krb5_socket *smb_krb5)
 /*
   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)
+static void smb_krb5_socket_handler(struct tevent_context *ev, struct tevent_fd *fde,
+                                uint16_t flags, void *private_data)
 {
-       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);
+       struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
+       switch (smb_krb5->hi->proto) {
+       case KRB5_KRBHST_UDP:
+               if (flags & TEVENT_FD_READ) {
+                       smb_krb5_socket_recv(smb_krb5);
+                       return;
+               }
+               if (flags & TEVENT_FD_WRITE) {
+                       smb_krb5_socket_send(smb_krb5);
+                       return;
+               }
+               /* not reached */
+               return;
+       case KRB5_KRBHST_TCP:
+               if (flags & TEVENT_FD_READ) {
+                       packet_recv(smb_krb5->packet);
+                       return;
+               }
+               if (flags & TEVENT_FD_WRITE) {
+                       packet_queue_run(smb_krb5->packet);
+                       return;
+               }
+               /* not reached */
+               return;
+       case KRB5_KRBHST_HTTP:
+               /* can't happen */
+               break;
        }
 }
 
 
-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 smb_krb5_send_and_recv_func(krb5_context context,
+                                           void *data,
+                                           krb5_krbhst_info *hi,
+                                           time_t timeout,
+                                           const krb5_data *send_buf,
+                                           krb5_data *recv_buf)
 {
        krb5_error_code ret;
        NTSTATUS status;
@@ -237,7 +217,7 @@ static krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
        struct addrinfo *ai, *a;
        struct smb_krb5_socket *smb_krb5;
 
-       struct event_context *ev = talloc_get_type(data, struct event_context);
+       struct tevent_context *ev = talloc_get_type(data, struct tevent_context);
 
        DATA_BLOB send_blob = data_blob_const(send_buf->data, send_buf->length);
 
@@ -257,7 +237,7 @@ static krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
                case PF_INET:
                        name = "ipv4";
                        break;
-#ifdef HAVE_SOCKET_IPV6
+#ifdef HAVE_IPV6
                case PF_INET6:
                        name = "ipv6";
                        break;
@@ -270,14 +250,10 @@ static krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
                status = NT_STATUS_INVALID_PARAMETER;
                switch (hi->proto) {
                case KRB5_KRBHST_UDP:
-                       if (lp_parm_bool(-1, "krb5", "udp", True)) {
-                               status = socket_create(name, SOCKET_TYPE_DGRAM, &smb_krb5->sock, 0);
-                       }
+                       status = socket_create(name, SOCKET_TYPE_DGRAM, &smb_krb5->sock, 0);
                        break;
                case KRB5_KRBHST_TCP:
-                       if (lp_parm_bool(-1, "krb5", "tcp", True)) {
-                               status = socket_create(name, SOCKET_TYPE_STREAM, &smb_krb5->sock, 0);
-                       }
+                       status = socket_create(name, SOCKET_TYPE_STREAM, &smb_krb5->sock, 0);
                        break;
                case KRB5_KRBHST_HTTP:
                        talloc_free(smb_krb5);
@@ -296,54 +272,75 @@ static krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
                        continue;
                }
 
-               status = socket_connect_ev(smb_krb5->sock, NULL, remote_addr, 0, ev); 
+               status = socket_connect_ev(smb_krb5->sock, NULL, remote_addr, 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);
+               /* Setup the FDE, start listening for read events
+                * from the start (otherwise we may miss a socket
+                * drop) and mark as AUTOCLOSE along with the fde */
 
-               event_add_timed(ev, smb_krb5, 
-                               timeval_current_ofs(context->kdc_timeout, 0),
-                               smb_krb5_request_timeout, smb_krb5);
+               /* Ths is equivilant to EVENT_FD_READABLE(smb_krb5->fde) */
+               smb_krb5->fde = tevent_add_fd(ev, smb_krb5->sock,
+                                             socket_get_fd(smb_krb5->sock),
+                                             TEVENT_FD_READ,
+                                             smb_krb5_socket_handler, smb_krb5);
+               /* its now the job of the event layer to close the socket */
+               tevent_fd_set_close_fn(smb_krb5->fde, socket_tevent_fd_close_fn);
+               socket_set_flags(smb_krb5->sock, SOCKET_FLAG_NOCLOSE);
+
+               tevent_add_timer(ev, smb_krb5,
+                                timeval_current_ofs(timeout, 0),
+                                smb_krb5_request_timeout, smb_krb5);
+
+               smb_krb5->status = NT_STATUS_OK;
+               smb_krb5->reply = data_blob(NULL, 0);
 
-               EVENT_FD_WRITEABLE(smb_krb5->fde);
-               
                switch (hi->proto) {
                case KRB5_KRBHST_UDP:
+                       TEVENT_FD_WRITEABLE(smb_krb5->fde);
                        smb_krb5->request = send_blob;
                        break;
                case KRB5_KRBHST_TCP:
+
+                       smb_krb5->packet = packet_init(smb_krb5);
+                       if (smb_krb5->packet == NULL) {
+                               talloc_free(smb_krb5);
+                               return ENOMEM;
+                       }
+                       packet_set_private(smb_krb5->packet, smb_krb5);
+                       packet_set_socket(smb_krb5->packet, smb_krb5->sock);
+                       packet_set_callback(smb_krb5->packet, smb_krb5_full_packet);
+                       packet_set_full_request(smb_krb5->packet, packet_full_request_u32);
+                       packet_set_error_handler(smb_krb5->packet, smb_krb5_error_handler);
+                       packet_set_event_context(smb_krb5->packet, ev);
+                       packet_set_fde(smb_krb5->packet, smb_krb5->fde);
+
                        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);
+                       packet_send(smb_krb5->packet, smb_krb5->request);
                        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) {
+               while ((NT_STATUS_IS_OK(smb_krb5->status)) && !smb_krb5->reply.length) {
+                       if (tevent_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)));
+               if (NT_STATUS_EQUAL(smb_krb5->status, NT_STATUS_IO_TIMEOUT)) {
                        talloc_free(smb_krb5);
                        continue;
                }
 
-               if (smb_krb5->timeout) {
+               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;
                }
@@ -363,19 +360,15 @@ static krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
        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 tevent_context *ev,
+                                     struct loadparm_context *lp_ctx,
                                       struct smb_krb5_context **smb_krb5_context) 
 {
        krb5_error_code ret;
        TALLOC_CTX *tmp_ctx;
-       struct event_context *ev;
+       char **config_files;
+       const char *config_file, *realm;
        
        initialize_krb5_error_table();
        
@@ -383,7 +376,6 @@ krb5_error_code smb_krb5_init_context(void *parent_ctx,
        *smb_krb5_context = talloc(tmp_ctx, struct smb_krb5_context);
 
        if (!*smb_krb5_context || !tmp_ctx) {
-               talloc_free(*smb_krb5_context);
                talloc_free(tmp_ctx);
                return ENOMEM;
        }
@@ -392,19 +384,40 @@ krb5_error_code smb_krb5_init_context(void *parent_ctx,
        if (ret) {
                DEBUG(1,("krb5_init_context failed (%s)\n", 
                         error_message(ret)));
+               talloc_free(tmp_ctx);
                return ret;
        }
 
        talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy_1);
 
-       if (lp_realm() && *lp_realm()) {
-               char *upper_realm = strupper_talloc(tmp_ctx, lp_realm());
-               if (!upper_realm) {
-                       DEBUG(1,("gensec_krb5_start: could not uppercase realm: %s\n", lp_realm()));
-                       talloc_free(tmp_ctx);
-                       return ENOMEM;
-               }
-               ret = krb5_set_default_realm((*smb_krb5_context)->krb5_context, upper_realm);
+       config_file = config_path(tmp_ctx, lp_ctx, "krb5.conf");
+       if (!config_file) {
+               talloc_free(tmp_ctx);
+               return ENOMEM;
+       }
+               
+       /* Use our local krb5.conf file by default */
+       ret = krb5_prepend_config_files_default(config_file == NULL?"":config_file, &config_files);
+       if (ret) {
+               DEBUG(1,("krb5_prepend_config_files_default failed (%s)\n", 
+                        smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       ret = krb5_set_config_files((*smb_krb5_context)->krb5_context, 
+                                   config_files);
+       krb5_free_config_files(config_files);
+       if (ret) {
+               DEBUG(1,("krb5_set_config_files failed (%s)\n", 
+                        smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+                                               
+       realm = lp_realm(lp_ctx);
+       if (realm != NULL) {
+               ret = krb5_set_default_realm((*smb_krb5_context)->krb5_context, realm);
                if (ret) {
                        DEBUG(1,("krb5_set_default_realm failed (%s)\n", 
                                 smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
@@ -435,11 +448,10 @@ krb5_error_code smb_krb5_init_context(void *parent_ctx,
        }
        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);
+       ret = krb5_set_send_to_kdc_func((*smb_krb5_context)->krb5_context, 
+                                       smb_krb5_send_and_recv_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)));
@@ -447,19 +459,14 @@ krb5_error_code smb_krb5_init_context(void *parent_ctx,
                return ret;
        }
 
-       (*smb_krb5_context)->krb5_context->mem_ctx = *smb_krb5_context;
-
        talloc_steal(parent_ctx, *smb_krb5_context);
        talloc_free(tmp_ctx);
 
        /* Set options in kerberos */
 
-       (*smb_krb5_context)->krb5_context->fdns = FALSE;
+       krb5_set_dns_canonicalize_hostname((*smb_krb5_context)->krb5_context,
+                                          lp_parm_bool(lp_ctx, NULL, "krb5", "set_dns_canonicalize", false));
 
        return 0;
 }
 
- void smb_krb5_free_context(struct smb_krb5_context *smb_krb5_context) 
-{
-       talloc_free(smb_krb5_context);
-}