From c3b54cf75b6aaa5448afbd4e3fd68b31d69e35fd Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 5 Sep 2006 09:42:54 +0000 Subject: [PATCH] r18068: This splits the handling of multiple SASL packets between the GENSEC backend (if it chooses to implement it), or the GENSEC socket code. This is to allow us to handle DIGEST-MD5 across to cyrus-sasl. Andrew Bartlett (This used to be commit 0a098006b431f4aa48632a27ca08e9adca8d9609) --- source4/auth/gensec/gensec.c | 36 ++++---- source4/auth/gensec/gensec.h | 18 +++- source4/auth/gensec/socket.c | 170 ++++++++++++++++++++++++++--------- source4/auth/gensec/socket.h | 20 +++++ source4/auth/gensec/spnego.c | 58 ++++++++++++ 5 files changed, 240 insertions(+), 62 deletions(-) diff --git a/source4/auth/gensec/gensec.c b/source4/auth/gensec/gensec.c index 7825949bdc3..839b538eeba 100644 --- a/source4/auth/gensec/gensec.c +++ b/source4/auth/gensec/gensec.c @@ -4,7 +4,7 @@ Generic Authentication Interface Copyright (C) Andrew Tridgell 2003 - Copyright (C) Andrew Bartlett 2004-2005 + Copyright (C) Andrew Bartlett 2004-2006 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 @@ -51,7 +51,9 @@ struct gensec_security_ops **gensec_use_kerberos_mechs(TALLOC_CTX *mem_ctx, int i, j, num_mechs_in; if (use_kerberos == CRED_AUTO_USE_KERBEROS) { - talloc_reference(mem_ctx, old_gensec_list); + if (!talloc_reference(mem_ctx, old_gensec_list)) { + return NULL; + } return old_gensec_list; } @@ -103,13 +105,17 @@ struct gensec_security_ops **gensec_security_mechs(struct gensec_security *gense struct gensec_security_ops **backends; backends = gensec_security_all(); if (!gensec_security) { - talloc_reference(mem_ctx, backends); + if (!talloc_reference(mem_ctx, backends)) { + return NULL; + } return backends; } else { enum credentials_use_kerberos use_kerberos; struct cli_credentials *creds = gensec_get_credentials(gensec_security); if (!creds) { - talloc_reference(mem_ctx, backends); + if (!talloc_reference(mem_ctx, backends)) { + return NULL; + } return backends; } use_kerberos = cli_credentials_get_kerberos_state(creds); @@ -840,15 +846,6 @@ size_t gensec_sig_size(struct gensec_security *gensec_security, size_t data_size return gensec_security->ops->sig_size(gensec_security, data_size); } -size_t gensec_max_input_size(struct gensec_security *gensec_security) -{ - if (!gensec_security->ops->max_input_size) { - return (1 << 17) - gensec_sig_size(gensec_security, 1 << 17); - } - - return gensec_security->ops->max_input_size(gensec_security); -} - size_t gensec_max_wrapped_size(struct gensec_security *gensec_security) { if (!gensec_security->ops->max_wrapped_size) { @@ -858,7 +855,16 @@ size_t gensec_max_wrapped_size(struct gensec_security *gensec_security) return gensec_security->ops->max_wrapped_size(gensec_security); } -_PUBLIC_ NTSTATUS gensec_wrap(struct gensec_security *gensec_security, +size_t gensec_max_input_size(struct gensec_security *gensec_security) +{ + if (!gensec_security->ops->max_input_size) { + return (1 << 17) - gensec_sig_size(gensec_security, 1 << 17); + } + + return gensec_security->ops->max_input_size(gensec_security); +} + +NTSTATUS gensec_wrap(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, const DATA_BLOB *in, DATA_BLOB *out) @@ -869,7 +875,7 @@ _PUBLIC_ NTSTATUS gensec_wrap(struct gensec_security *gensec_security, return gensec_security->ops->wrap(gensec_security, mem_ctx, in, out); } -_PUBLIC_ NTSTATUS gensec_unwrap(struct gensec_security *gensec_security, +NTSTATUS gensec_unwrap(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, const DATA_BLOB *in, DATA_BLOB *out) diff --git a/source4/auth/gensec/gensec.h b/source4/auth/gensec/gensec.h index b154619edfd..8156866962e 100644 --- a/source4/auth/gensec/gensec.h +++ b/source4/auth/gensec/gensec.h @@ -105,9 +105,21 @@ struct gensec_security_ops { const DATA_BLOB *in, DATA_BLOB *out); NTSTATUS (*unwrap)(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const DATA_BLOB *in, - DATA_BLOB *out); + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out); + NTSTATUS (*wrap_packets)(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out, + size_t *len_processed); + NTSTATUS (*unwrap_packets)(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out, + size_t *len_processed); + NTSTATUS (*packet_full_request)(struct gensec_security *gensec_security, + DATA_BLOB blob, size_t *size); NTSTATUS (*session_key)(struct gensec_security *gensec_security, DATA_BLOB *session_key); NTSTATUS (*session_info)(struct gensec_security *gensec_security, struct auth_session_info **session_info); diff --git a/source4/auth/gensec/socket.c b/source4/auth/gensec/socket.c index 92f23828826..592535d8dcf 100644 --- a/source4/auth/gensec/socket.c +++ b/source4/auth/gensec/socket.c @@ -58,6 +58,115 @@ static NTSTATUS gensec_socket_init_fn(struct socket_context *sock) return NT_STATUS_OK; } +/* These functions are for use here only (public because SPNEGO must + * use them for recursion) */ +NTSTATUS gensec_wrap_packets(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out, + size_t *len_processed) +{ + if (!gensec_security->ops->wrap_packets) { + NTSTATUS nt_status; + size_t max_input_size; + DATA_BLOB unwrapped, wrapped; + max_input_size = gensec_max_input_size(gensec_security); + unwrapped = data_blob_const(in->data, MIN(max_input_size, (size_t)in->length)); + + nt_status = gensec_wrap(gensec_security, + mem_ctx, + &unwrapped, &wrapped); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(mem_ctx); + return nt_status; + } + + *out = data_blob_talloc(mem_ctx, NULL, 4); + if (!out->data) { + return NT_STATUS_NO_MEMORY; + } + RSIVAL(out->data, 0, wrapped.length); + + nt_status = data_blob_append(mem_ctx, out, wrapped.data, wrapped.length); + + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + *len_processed = unwrapped.length; + return nt_status; + } + return gensec_security->ops->wrap_packets(gensec_security, mem_ctx, in, out, + len_processed); +} + +/* These functions are for use here only (public because SPNEGO must + * use them for recursion) */ +NTSTATUS gensec_unwrap_packets(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out, + size_t *len_processed) +{ + if (!gensec_security->ops->unwrap_packets) { + DATA_BLOB wrapped; + NTSTATUS nt_status; + size_t packet_size; + if (in->length < 4) { + /* Missing the header we already had! */ + DEBUG(0, ("Asked to unwrap packet of bogus length! How did we get the short packet?!\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + packet_size = RIVAL(in->data, 0); + + wrapped = data_blob_const(in->data + 4, packet_size); + + if (wrapped.length > (in->length - 4)) { + DEBUG(0, ("Asked to unwrap packed of bogus length %d > %d! How did we get this?!\n", + wrapped.length, in->length - 4)); + return NT_STATUS_INTERNAL_ERROR; + } + + nt_status = gensec_unwrap(gensec_security, + mem_ctx, + &wrapped, out); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + *len_processed = packet_size + 4; + return nt_status; + } + return gensec_security->ops->unwrap_packets(gensec_security, mem_ctx, in, out, + len_processed); +} + +/* These functions are for use here only (public because SPNEGO must + * use them for recursion) */ +NTSTATUS gensec_packet_full_request(struct gensec_security *gensec_security, + DATA_BLOB blob, size_t *size) +{ + if (gensec_security->ops->packet_full_request) { + return gensec_security->ops->packet_full_request(gensec_security, + blob, size); + } + if (gensec_security->ops->unwrap_packets) { + if (blob.length) { + *size = blob.length; + return NT_STATUS_OK; + } + return STATUS_MORE_ENTRIES; + } + return packet_full_request_u32(NULL, blob, size); +} + +static NTSTATUS gensec_socket_full_request(void *private, DATA_BLOB blob, size_t *size) +{ + struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket); + struct gensec_security *gensec_security = gensec_socket->gensec_security; + return gensec_packet_full_request(gensec_security, blob, size); +} + /* Try to figure out how much data is waiting to be read */ static NTSTATUS gensec_socket_pending(struct socket_context *sock, size_t *npending) { @@ -183,37 +292,29 @@ static NTSTATUS gensec_socket_recv(struct socket_context *sock, void *buf, static NTSTATUS gensec_socket_unwrap(void *private, DATA_BLOB blob) { struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket); - DATA_BLOB wrapped; DATA_BLOB unwrapped; NTSTATUS nt_status; TALLOC_CTX *mem_ctx; uint32_t packet_size; - if (blob.length < 4) { - /* Missing the header we already had! */ - DEBUG(0, ("Asked to unwrap packed of bogus length! How did we get the short packet?!\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - wrapped = data_blob_const(blob.data + 4, blob.length - 4); - - packet_size = RIVAL(blob.data, 0); - if (packet_size != wrapped.length) { - DEBUG(0, ("Asked to unwrap packed of bogus length! How did we get this?!\n")); - return NT_STATUS_INTERNAL_ERROR; - } - mem_ctx = talloc_new(gensec_socket); if (!mem_ctx) { return NT_STATUS_NO_MEMORY; } - nt_status = gensec_unwrap(gensec_socket->gensec_security, - mem_ctx, - &wrapped, &unwrapped); + nt_status = gensec_unwrap_packets(gensec_socket->gensec_security, + mem_ctx, + &blob, &unwrapped, + &packet_size); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(mem_ctx); return nt_status; } + + if (packet_size != blob.length) { + DEBUG(0, ("gensec_socket_unwrap: Did not consume entire packet!\n")); + return NT_STATUS_INTERNAL_ERROR; + } + /* We could change this into a linked list, and have * gensec_socket_recv() and gensec_socket_pending() walk the * linked list */ @@ -242,9 +343,8 @@ static NTSTATUS gensec_socket_send(struct socket_context *sock, { NTSTATUS nt_status; struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket); - DATA_BLOB unwrapped, wrapped, out; + DATA_BLOB wrapped; TALLOC_CTX *mem_ctx; - size_t max_input_size; if (!gensec_socket->wrap) { return socket_send(gensec_socket->socket, blob, sendlen); @@ -273,26 +373,10 @@ static NTSTATUS gensec_socket_send(struct socket_context *sock, return NT_STATUS_NO_MEMORY; } - max_input_size = gensec_max_input_size(gensec_socket->gensec_security); - unwrapped = data_blob_const(blob->data, MIN(max_input_size, (size_t)blob->length)); - - nt_status = gensec_wrap(gensec_socket->gensec_security, - mem_ctx, - &unwrapped, &wrapped); - if (!NT_STATUS_IS_OK(nt_status)) { - talloc_free(mem_ctx); - return nt_status; - } - - out = data_blob_talloc(mem_ctx, NULL, 4); - if (!out.data) { - talloc_free(mem_ctx); - return NT_STATUS_NO_MEMORY; - } - RSIVAL(out.data, 0, wrapped.length); - - nt_status = data_blob_append(gensec_socket, &out, wrapped.data, wrapped.length); - + nt_status = gensec_wrap_packets(gensec_socket->gensec_security, + mem_ctx, + blob, &wrapped, + &gensec_socket->orig_send_len); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(mem_ctx); return nt_status; @@ -300,11 +384,9 @@ static NTSTATUS gensec_socket_send(struct socket_context *sock, gensec_socket->interrupted = True; gensec_socket->error = NT_STATUS_OK; - gensec_socket->orig_send_len - = unwrapped.length; nt_status = packet_send_callback(gensec_socket->packet, - out, + wrapped, send_callback, gensec_socket); talloc_free(mem_ctx); @@ -390,7 +472,7 @@ NTSTATUS gensec_socket_init(struct gensec_security *gensec_security, packet_set_private(gensec_socket->packet, gensec_socket); packet_set_socket(gensec_socket->packet, gensec_socket->socket); packet_set_callback(gensec_socket->packet, gensec_socket_unwrap); - packet_set_full_request(gensec_socket->packet, packet_full_request_u32); + packet_set_full_request(gensec_socket->packet, gensec_socket_full_request); packet_set_error_handler(gensec_socket->packet, gensec_socket_error_handler); packet_set_serialise(gensec_socket->packet); diff --git a/source4/auth/gensec/socket.h b/source4/auth/gensec/socket.h index a70b728e3f2..53773c5e749 100644 --- a/source4/auth/gensec/socket.h +++ b/source4/auth/gensec/socket.h @@ -26,3 +26,23 @@ NTSTATUS gensec_socket_init(struct gensec_security *gensec_security, void (*recv_handler)(void *, uint16_t), void *recv_private, struct socket_context **new_socket); +/* These functions are for use here only (public because SPNEGO must + * use them for recursion) */ +NTSTATUS gensec_wrap_packets(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out, + size_t *len_processed); +/* These functions are for use here only (public because SPNEGO must + * use them for recursion) */ +NTSTATUS gensec_unwrap_packets(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out, + size_t *len_processed); + +/* These functions are for use here only (public because SPNEGO must + * use them for recursion) */ +NTSTATUS gensec_packet_full_request(struct gensec_security *gensec_security, + DATA_BLOB blob, size_t *size); + diff --git a/source4/auth/gensec/spnego.c b/source4/auth/gensec/spnego.c index a57e8cc8469..fa15176e779 100644 --- a/source4/auth/gensec/spnego.c +++ b/source4/auth/gensec/spnego.c @@ -26,6 +26,8 @@ #include "auth/auth.h" #include "auth/gensec/spnego_proto.h" #include "librpc/gen_ndr/ndr_dcerpc.h" +#include "lib/socket/socket.h" +#include "auth/gensec/socket.h" enum spnego_state_position { SPNEGO_SERVER_START, @@ -199,6 +201,59 @@ static NTSTATUS gensec_spnego_unwrap(struct gensec_security *gensec_security, mem_ctx, in, out); } +static NTSTATUS gensec_spnego_wrap_packets(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out, + size_t *len_processed) +{ + struct spnego_state *spnego_state = gensec_security->private_data; + + if (spnego_state->state_position != SPNEGO_DONE + && spnego_state->state_position != SPNEGO_FALLBACK) { + DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + return gensec_wrap_packets(spnego_state->sub_sec_security, + mem_ctx, in, out, + len_processed); +} + +static NTSTATUS gensec_spnego_packet_full_request(struct gensec_security *gensec_security, + DATA_BLOB blob, size_t *size) +{ + struct spnego_state *spnego_state = gensec_security->private_data; + + if (spnego_state->state_position != SPNEGO_DONE + && spnego_state->state_position != SPNEGO_FALLBACK) { + DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + return gensec_packet_full_request(spnego_state->sub_sec_security, + blob, size); +} + +static NTSTATUS gensec_spnego_unwrap_packets(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out, + size_t *len_processed) +{ + struct spnego_state *spnego_state = gensec_security->private_data; + + if (spnego_state->state_position != SPNEGO_DONE + && spnego_state->state_position != SPNEGO_FALLBACK) { + DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + return gensec_unwrap_packets(spnego_state->sub_sec_security, + mem_ctx, in, out, + len_processed); +} + static size_t gensec_spnego_sig_size(struct gensec_security *gensec_security, size_t data_size) { struct spnego_state *spnego_state = gensec_security->private_data; @@ -976,8 +1031,11 @@ static const struct gensec_security_ops gensec_spnego_security_ops = { .max_input_size = gensec_spnego_max_input_size, .check_packet = gensec_spnego_check_packet, .unseal_packet = gensec_spnego_unseal_packet, + .packet_full_request = gensec_spnego_packet_full_request, .wrap = gensec_spnego_wrap, .unwrap = gensec_spnego_unwrap, + .wrap_packets = gensec_spnego_wrap_packets, + .unwrap_packets = gensec_spnego_unwrap_packets, .session_key = gensec_spnego_session_key, .session_info = gensec_spnego_session_info, .have_feature = gensec_spnego_have_feature,