mirror of
https://github.com/samba-team/samba.git
synced 2024-12-22 13:34:15 +03:00
r17197: This patch moves the encryption of bulk data on SASL negotiated security
contexts from the application layer into the socket layer.
This improves a number of correctness aspects, as we now allow LDAP
packets to cross multiple SASL packets. It should also make it much
easier to write async LDAP tests from windows clients, as they use SASL
by default. It is also vital to allowing OpenLDAP clients to use GSSAPI
against Samba4, as it negotiates a rather small SASL buffer size.
This patch mirrors the earlier work done to move TLS into the socket
layer.
Unusual in this pstch is the extra read callback argument I take. As
SASL is a layer on top of a socket, it is entirely possible for the
SASL layer to drain a socket dry, but for the caller not to have read
all the decrypted data. This would leave the system without an event
to restart the read (as the socket is dry).
As such, I re-invoke the read handler from a timed callback, which
should trigger on the next running of the event loop. I believe that
the TLS code does require a similar callback.
In trying to understand why this is required, imagine a SASL-encrypted
LDAP packet in the following formation:
+-----------------+---------------------+
| SASL Packet #1 | SASL Packet #2 |
----------------------------------------+
| LDAP Packet #1 | LDAP Packet #2 |
----------------------------------------+
In the old code, this was illegal, but it is perfectly standard
SASL-encrypted LDAP. Without the callback, we would read and process
the first LDAP packet, and the SASL code would have read the second SASL
packet (to decrypt enough data for the LDAP packet), and no data would
remain on the socket.
Without data on the socket, read events stop. That is why I add timed
events, until the SASL buffer is drained.
Another approach would be to add a hack to the event system, to have it
pretend there remained data to read off the network (but that is ugly).
In improving the code, to handle more real-world cases, I've been able
to remove almost all the special-cases in the testnonblock code. The
only special case is that we must use a deterministic partial packet
when calling send, rather than a random length. (1 + n/2). This is
needed because of the way the SASL and TLS code works, and the 'resend
on failure' requirements.
Andrew Bartlett
(This used to be commit 5d7c9c12cb
)
This commit is contained in:
parent
74b68a7555
commit
ba07fa43d0
@ -67,3 +67,14 @@ OBJ_FILES = \
|
||||
# End SUBSYSTEM SCHANNELDB
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start SUBSYSTEM GENSEC_SOCKET
|
||||
[SUBSYSTEM::GENSEC_SOCKET]
|
||||
OBJ_FILES = \
|
||||
socket.o
|
||||
PUBLIC_DEPENDENCIES = samba-socket
|
||||
#PUBLIC_DEPENDENCIES = gensec
|
||||
#
|
||||
# End SUBSYSTEM GENSEC_SOCKET
|
||||
################################################
|
||||
|
||||
|
421
source4/auth/gensec/socket.c
Normal file
421
source4/auth/gensec/socket.c
Normal file
@ -0,0 +1,421 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
GENSEC socket interface
|
||||
|
||||
Copyright (C) Andrew Bartlett 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
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "lib/stream/packet.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
|
||||
static const struct socket_ops gensec_socket_ops;
|
||||
|
||||
struct gensec_socket {
|
||||
struct gensec_security *gensec_security;
|
||||
struct socket_context *socket;
|
||||
struct event_context *ev;
|
||||
struct packet_context *packet;
|
||||
DATA_BLOB read_buffer; /* SASL packets are turned into liniarlised data here, for reading */
|
||||
size_t orig_send_len;
|
||||
BOOL eof;
|
||||
NTSTATUS error;
|
||||
BOOL interrupted;
|
||||
void (*recv_handler)(void *, uint16_t);
|
||||
void *recv_private;
|
||||
int in_extra_read;
|
||||
};
|
||||
|
||||
static NTSTATUS gensec_socket_init_fn(struct socket_context *sock)
|
||||
{
|
||||
switch (sock->type) {
|
||||
case SOCKET_TYPE_STREAM:
|
||||
break;
|
||||
default:
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
sock->backend_name = "gensec";
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* Try to figure out how much data is waiting to be read */
|
||||
static NTSTATUS gensec_socket_pending(struct socket_context *sock, size_t *npending)
|
||||
{
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
if (gensec_socket->read_buffer.length > 0) {
|
||||
*npending = gensec_socket->read_buffer.length;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* This is a lie. We hope the decrypted data will always be
|
||||
* less than this value, so the application just gets a short
|
||||
* read. Without reading and decrypting it, we can't tell.
|
||||
* If the SASL mech does compression, then we just need to
|
||||
* manually trigger read events */
|
||||
return socket_pending(gensec_socket->socket, npending);
|
||||
}
|
||||
|
||||
/* Note if an error occours, so we can return it up the stack */
|
||||
static void gensec_socket_error_handler(void *private, NTSTATUS status)
|
||||
{
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
|
||||
gensec_socket->eof = True;
|
||||
} else {
|
||||
gensec_socket->error = status;
|
||||
}
|
||||
}
|
||||
|
||||
static void gensec_socket_trigger_read(struct event_context *ev,
|
||||
struct timed_event *te,
|
||||
struct timeval t, void *private)
|
||||
{
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
|
||||
|
||||
gensec_socket->in_extra_read++;
|
||||
gensec_socket->recv_handler(gensec_socket->recv_private, EVENT_FD_READ);
|
||||
gensec_socket->in_extra_read--;
|
||||
|
||||
/* It may well be that, having run the recv handler, we still
|
||||
* have even more data waiting for us!
|
||||
*/
|
||||
if (gensec_socket->read_buffer.length && gensec_socket->recv_handler) {
|
||||
/* Schedule this funcion to run again */
|
||||
event_add_timed(gensec_socket->ev, gensec_socket, timeval_zero(),
|
||||
gensec_socket_trigger_read, gensec_socket);
|
||||
}
|
||||
}
|
||||
|
||||
/* These two routines could be changed to use a circular buffer of
|
||||
* some kind, or linked lists, or ... */
|
||||
static NTSTATUS gensec_socket_recv(struct socket_context *sock, void *buf,
|
||||
size_t wantlen, size_t *nread)
|
||||
{
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
|
||||
gensec_socket->error = NT_STATUS_OK;
|
||||
|
||||
if (gensec_socket->read_buffer.length == 0) {
|
||||
/* Process any data on the socket, into the read buffer. At
|
||||
* this point, the socket is not available for read any
|
||||
* longer */
|
||||
packet_recv(gensec_socket->packet);
|
||||
|
||||
if (gensec_socket->eof) {
|
||||
*nread = 0;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(gensec_socket->error)) {
|
||||
return gensec_socket->error;
|
||||
}
|
||||
|
||||
if (gensec_socket->read_buffer.length == 0) {
|
||||
/* Clearly we don't have the entire SASL packet yet,
|
||||
* so it has not been written into the buffer */
|
||||
*nread = 0;
|
||||
return STATUS_MORE_ENTRIES;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*nread = MIN(wantlen, gensec_socket->read_buffer.length);
|
||||
memcpy(buf, gensec_socket->read_buffer.data, *nread);
|
||||
|
||||
if (gensec_socket->read_buffer.length > *nread) {
|
||||
memmove(gensec_socket->read_buffer.data,
|
||||
gensec_socket->read_buffer.data + *nread,
|
||||
gensec_socket->read_buffer.length - *nread);
|
||||
}
|
||||
|
||||
gensec_socket->read_buffer.length -= *nread;
|
||||
gensec_socket->read_buffer.data = talloc_realloc(gensec_socket,
|
||||
gensec_socket->read_buffer.data,
|
||||
uint8_t,
|
||||
gensec_socket->read_buffer.length);
|
||||
|
||||
if (gensec_socket->read_buffer.length &&
|
||||
gensec_socket->in_extra_read == 0 &&
|
||||
gensec_socket->recv_handler) {
|
||||
/* Manually call a read event, to get this moving
|
||||
* again (as the socket should be dry, so the normal
|
||||
* event handler won't trigger) */
|
||||
event_add_timed(gensec_socket->ev, gensec_socket, timeval_zero(),
|
||||
gensec_socket_trigger_read, gensec_socket);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* Completed SASL packet callback. When we have a 'whole' SASL
|
||||
* packet, decrypt it, and add it to the read buffer
|
||||
*
|
||||
* This function (and anything under it) MUST NOT call the event system
|
||||
*/
|
||||
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);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
/* We could change this into a linked list, and have
|
||||
* gensec_socket_recv() and gensec_socket_pending() walk the
|
||||
* linked list */
|
||||
|
||||
nt_status = data_blob_append(gensec_socket, &gensec_socket->read_buffer,
|
||||
unwrapped.data, unwrapped.length);
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/* when the data is sent, we know we have not been interrupted */
|
||||
static void send_callback(void *private)
|
||||
{
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
|
||||
gensec_socket->interrupted = False;
|
||||
}
|
||||
|
||||
/*
|
||||
send data, but only as much as we allow in one packet.
|
||||
|
||||
If this returns STATUS_MORE_ENTRIES, the caller must retry with
|
||||
exactly the same data, or a NULL blob.
|
||||
*/
|
||||
static NTSTATUS gensec_socket_send(struct socket_context *sock,
|
||||
const DATA_BLOB *blob, size_t *sendlen)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
DATA_BLOB unwrapped, wrapped, out;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
size_t max_input_size;
|
||||
|
||||
*sendlen = 0;
|
||||
|
||||
/* We have have been interupted, so the caller should be
|
||||
* giving us the same data again. */
|
||||
if (gensec_socket->interrupted) {
|
||||
packet_queue_run(gensec_socket->packet);
|
||||
|
||||
if (!NT_STATUS_IS_OK(gensec_socket->error)) {
|
||||
return gensec_socket->error;
|
||||
} else if (gensec_socket->interrupted) {
|
||||
return STATUS_MORE_ENTRIES;
|
||||
} else {
|
||||
*sendlen = gensec_socket->orig_send_len;
|
||||
gensec_socket->orig_send_len = 0;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
mem_ctx = talloc_new(gensec_socket);
|
||||
if (!mem_ctx) {
|
||||
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);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
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,
|
||||
send_callback, gensec_socket);
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
packet_queue_run(gensec_socket->packet);
|
||||
|
||||
if (!NT_STATUS_IS_OK(gensec_socket->error)) {
|
||||
return gensec_socket->error;
|
||||
} else if (gensec_socket->interrupted) {
|
||||
return STATUS_MORE_ENTRIES;
|
||||
} else {
|
||||
*sendlen = gensec_socket->orig_send_len;
|
||||
gensec_socket->orig_send_len = 0;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
struct socket_context *gensec_socket_init(struct gensec_security *gensec_security,
|
||||
struct socket_context *socket,
|
||||
struct event_context *ev,
|
||||
void (*recv_handler)(void *, uint16_t),
|
||||
void *recv_private)
|
||||
{
|
||||
struct gensec_socket *gensec_socket;
|
||||
struct socket_context *new_sock;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
/* Nothing to do here, if we are not actually wrapping on this socket */
|
||||
if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) &&
|
||||
!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
|
||||
return socket;
|
||||
}
|
||||
|
||||
nt_status = socket_create_with_ops(socket, &gensec_socket_ops, &new_sock,
|
||||
SOCKET_TYPE_STREAM, socket->flags | SOCKET_FLAG_ENCRYPT);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gensec_socket = talloc(new_sock, struct gensec_socket);
|
||||
if (gensec_socket == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gensec_socket->eof = False;
|
||||
gensec_socket->error = NT_STATUS_OK;
|
||||
gensec_socket->interrupted = False;
|
||||
gensec_socket->in_extra_read = 0;
|
||||
|
||||
gensec_socket->read_buffer = data_blob(NULL, 0);
|
||||
|
||||
gensec_socket->gensec_security = gensec_security;
|
||||
gensec_socket->socket = socket;
|
||||
if (talloc_reference(gensec_socket, socket) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
gensec_socket->recv_handler = recv_handler;
|
||||
gensec_socket->recv_private = recv_private;
|
||||
gensec_socket->ev = ev;
|
||||
|
||||
new_sock->private_data = gensec_socket;
|
||||
|
||||
gensec_socket->packet = packet_init(gensec_socket);
|
||||
if (gensec_socket->packet == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
packet_set_private(gensec_socket->packet, gensec_socket);
|
||||
packet_set_socket(gensec_socket->packet, socket);
|
||||
packet_set_callback(gensec_socket->packet, gensec_socket_unwrap);
|
||||
packet_set_full_request(gensec_socket->packet, packet_full_request_u32);
|
||||
packet_set_error_handler(gensec_socket->packet, gensec_socket_error_handler);
|
||||
packet_set_serialise(gensec_socket->packet);
|
||||
|
||||
/* TODO: full-request that knows about maximum packet size */
|
||||
|
||||
new_sock->state = socket->state;
|
||||
|
||||
return new_sock;
|
||||
}
|
||||
|
||||
|
||||
static NTSTATUS gensec_socket_set_option(struct socket_context *sock, const char *option, const char *val)
|
||||
{
|
||||
set_socket_options(socket_get_fd(sock), option);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static char *gensec_socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
return socket_get_peer_name(gensec->socket, mem_ctx);
|
||||
}
|
||||
|
||||
static struct socket_address *gensec_socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
return socket_get_peer_addr(gensec->socket, mem_ctx);
|
||||
}
|
||||
|
||||
static struct socket_address *gensec_socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
return socket_get_my_addr(gensec->socket, mem_ctx);
|
||||
}
|
||||
|
||||
static int gensec_socket_get_fd(struct socket_context *sock)
|
||||
{
|
||||
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
return socket_get_fd(gensec->socket);
|
||||
}
|
||||
|
||||
static const struct socket_ops gensec_socket_ops = {
|
||||
.name = "gensec",
|
||||
.fn_init = gensec_socket_init_fn,
|
||||
.fn_recv = gensec_socket_recv,
|
||||
.fn_send = gensec_socket_send,
|
||||
.fn_pending = gensec_socket_pending,
|
||||
|
||||
.fn_set_option = gensec_socket_set_option,
|
||||
|
||||
.fn_get_peer_name = gensec_socket_get_peer_name,
|
||||
.fn_get_peer_addr = gensec_socket_get_peer_addr,
|
||||
.fn_get_my_addr = gensec_socket_get_my_addr,
|
||||
.fn_get_fd = gensec_socket_get_fd
|
||||
};
|
||||
|
27
source4/auth/gensec/socket.h
Normal file
27
source4/auth/gensec/socket.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Generic Authentication Interface (socket wrapper)
|
||||
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 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
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
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.
|
||||
*/
|
||||
|
||||
struct socket_context *gensec_socket_init(struct gensec_security *gensec_security,
|
||||
struct socket_context *socket,
|
||||
struct event_context *ev,
|
||||
void (*recv_handler)(void *, uint16_t),
|
||||
void *recv_private);
|
@ -211,6 +211,30 @@ static size_t gensec_spnego_sig_size(struct gensec_security *gensec_security, si
|
||||
return gensec_sig_size(spnego_state->sub_sec_security, data_size);
|
||||
}
|
||||
|
||||
static size_t gensec_spnego_max_input_size(struct gensec_security *gensec_security)
|
||||
{
|
||||
struct spnego_state *spnego_state = gensec_security->private_data;
|
||||
|
||||
if (spnego_state->state_position != SPNEGO_DONE
|
||||
&& spnego_state->state_position != SPNEGO_FALLBACK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return gensec_max_input_size(spnego_state->sub_sec_security);
|
||||
}
|
||||
|
||||
static size_t gensec_spnego_max_wrapped_size(struct gensec_security *gensec_security)
|
||||
{
|
||||
struct spnego_state *spnego_state = gensec_security->private_data;
|
||||
|
||||
if (spnego_state->state_position != SPNEGO_DONE
|
||||
&& spnego_state->state_position != SPNEGO_FALLBACK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return gensec_max_wrapped_size(spnego_state->sub_sec_security);
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_spnego_session_key(struct gensec_security *gensec_security,
|
||||
DATA_BLOB *session_key)
|
||||
{
|
||||
@ -938,24 +962,26 @@ static const char *gensec_spnego_oids[] = {
|
||||
};
|
||||
|
||||
static const struct gensec_security_ops gensec_spnego_security_ops = {
|
||||
.name = "spnego",
|
||||
.sasl_name = "GSS-SPNEGO",
|
||||
.auth_type = DCERPC_AUTH_TYPE_SPNEGO,
|
||||
.oid = gensec_spnego_oids,
|
||||
.client_start = gensec_spnego_client_start,
|
||||
.server_start = gensec_spnego_server_start,
|
||||
.update = gensec_spnego_update,
|
||||
.seal_packet = gensec_spnego_seal_packet,
|
||||
.sign_packet = gensec_spnego_sign_packet,
|
||||
.sig_size = gensec_spnego_sig_size,
|
||||
.check_packet = gensec_spnego_check_packet,
|
||||
.unseal_packet = gensec_spnego_unseal_packet,
|
||||
.wrap = gensec_spnego_wrap,
|
||||
.unwrap = gensec_spnego_unwrap,
|
||||
.session_key = gensec_spnego_session_key,
|
||||
.session_info = gensec_spnego_session_info,
|
||||
.have_feature = gensec_spnego_have_feature,
|
||||
.enabled = True,
|
||||
.name = "spnego",
|
||||
.sasl_name = "GSS-SPNEGO",
|
||||
.auth_type = DCERPC_AUTH_TYPE_SPNEGO,
|
||||
.oid = gensec_spnego_oids,
|
||||
.client_start = gensec_spnego_client_start,
|
||||
.server_start = gensec_spnego_server_start,
|
||||
.update = gensec_spnego_update,
|
||||
.seal_packet = gensec_spnego_seal_packet,
|
||||
.sign_packet = gensec_spnego_sign_packet,
|
||||
.sig_size = gensec_spnego_sig_size,
|
||||
.max_wrapped_size = gensec_spnego_max_wrapped_size,
|
||||
.max_input_size = gensec_spnego_max_input_size,
|
||||
.check_packet = gensec_spnego_check_packet,
|
||||
.unseal_packet = gensec_spnego_unseal_packet,
|
||||
.wrap = gensec_spnego_wrap,
|
||||
.unwrap = gensec_spnego_unwrap,
|
||||
.session_key = gensec_spnego_session_key,
|
||||
.session_info = gensec_spnego_session_info,
|
||||
.have_feature = gensec_spnego_have_feature,
|
||||
.enabled = True,
|
||||
};
|
||||
|
||||
NTSTATUS gensec_spnego_init(void)
|
||||
|
@ -11,6 +11,6 @@ OBJ_FILES = \
|
||||
ldap_backend.o \
|
||||
ldap_bind.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
LIBCLI_LDAP SAMDB process_model auth
|
||||
LIBCLI_LDAP SAMDB process_model auth GENSEC_SOCKET
|
||||
# End SUBSYSTEM SMB
|
||||
#######################
|
||||
|
@ -22,10 +22,11 @@
|
||||
#include "ldap_server/ldap_server.h"
|
||||
#include "auth/auth.h"
|
||||
#include "libcli/ldap/ldap.h"
|
||||
#include "smbd/service_stream.h"
|
||||
#include "smbd/service.h"
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "lib/ldb/include/ldb_errors.h"
|
||||
#include "dsdb/samdb/samdb.h"
|
||||
#include "auth/gensec/socket.h"
|
||||
|
||||
static NTSTATUS ldapsrv_BindSimple(struct ldapsrv_call *call)
|
||||
{
|
||||
@ -89,6 +90,23 @@ static NTSTATUS ldapsrv_BindSimple(struct ldapsrv_call *call)
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static void ldapsrv_set_sasl(void *private)
|
||||
{
|
||||
struct ldapsrv_connection *conn = talloc_get_type(private, struct ldapsrv_connection);
|
||||
struct socket_context *socket = gensec_socket_init(conn->gensec,
|
||||
conn->connection->socket,
|
||||
conn->connection->event.ctx,
|
||||
stream_io_handler_callback,
|
||||
conn->connection);
|
||||
if (socket) {
|
||||
conn->connection->socket = socket;
|
||||
talloc_steal(conn->connection->socket, socket);
|
||||
packet_set_socket(conn->packet, socket);
|
||||
} else {
|
||||
ldapsrv_terminate_connection(conn, "Failed to setup SASL wrapping on socket");
|
||||
}
|
||||
}
|
||||
|
||||
static NTSTATUS ldapsrv_BindSASL(struct ldapsrv_call *call)
|
||||
{
|
||||
struct ldap_BindRequest *req = &call->request->r.BindRequest;
|
||||
@ -175,10 +193,10 @@ static NTSTATUS ldapsrv_BindSASL(struct ldapsrv_call *call)
|
||||
|
||||
result = LDAP_SUCCESS;
|
||||
errstr = NULL;
|
||||
if (gensec_have_feature(conn->gensec, GENSEC_FEATURE_SEAL) ||
|
||||
gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN)) {
|
||||
conn->enable_wrap = True;
|
||||
}
|
||||
|
||||
call->send_callback = ldapsrv_set_sasl;
|
||||
call->send_private = conn;
|
||||
|
||||
old_session_info = conn->session_info;
|
||||
conn->session_info = NULL;
|
||||
status = gensec_session_info(conn->gensec, &conn->session_info);
|
||||
|
@ -31,10 +31,8 @@
|
||||
#include "smbd/service_task.h"
|
||||
#include "smbd/service_stream.h"
|
||||
#include "smbd/service.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "lib/tls/tls.h"
|
||||
#include "lib/messaging/irpc.h"
|
||||
#include "lib/stream/packet.h"
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "lib/ldb/include/ldb_errors.h"
|
||||
#include "system/network.h"
|
||||
@ -43,7 +41,7 @@
|
||||
/*
|
||||
close the socket and shutdown a server_context
|
||||
*/
|
||||
static void ldapsrv_terminate_connection(struct ldapsrv_connection *conn,
|
||||
void ldapsrv_terminate_connection(struct ldapsrv_connection *conn,
|
||||
const char *reason)
|
||||
{
|
||||
stream_terminate_connection(conn->connection, reason);
|
||||
@ -68,7 +66,6 @@ static void ldapsrv_process_message(struct ldapsrv_connection *conn,
|
||||
struct ldapsrv_call *call;
|
||||
NTSTATUS status;
|
||||
DATA_BLOB blob;
|
||||
BOOL enable_wrap = conn->enable_wrap;
|
||||
|
||||
call = talloc(conn, struct ldapsrv_call);
|
||||
if (!call) {
|
||||
@ -79,11 +76,14 @@ static void ldapsrv_process_message(struct ldapsrv_connection *conn,
|
||||
call->request = talloc_steal(call, msg);
|
||||
call->conn = conn;
|
||||
call->replies = NULL;
|
||||
|
||||
call->send_callback = NULL;
|
||||
call->send_private = NULL;
|
||||
|
||||
/* make the call */
|
||||
status = ldapsrv_do_call(call);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto failed;
|
||||
talloc_free(call);
|
||||
return;
|
||||
}
|
||||
|
||||
blob = data_blob(NULL, 0);
|
||||
@ -100,49 +100,36 @@ static void ldapsrv_process_message(struct ldapsrv_connection *conn,
|
||||
msg = call->replies->msg;
|
||||
if (!ldap_encode(msg, &b, call)) {
|
||||
DEBUG(0,("Failed to encode ldap reply of type %d\n", msg->type));
|
||||
goto failed;
|
||||
talloc_free(call);
|
||||
return;
|
||||
}
|
||||
|
||||
status = data_blob_append(call, &blob, b.data, b.length);
|
||||
data_blob_free(&b);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) goto failed;
|
||||
talloc_set_name_const(blob.data, "Outgoing, encoded LDAP packet");
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(call);
|
||||
return;
|
||||
}
|
||||
|
||||
DLIST_REMOVE(call->replies, call->replies);
|
||||
}
|
||||
|
||||
/* possibly encrypt/sign the reply */
|
||||
if (enable_wrap) {
|
||||
DATA_BLOB wrapped;
|
||||
|
||||
status = gensec_wrap(conn->gensec, call, &blob, &wrapped);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto failed;
|
||||
}
|
||||
data_blob_free(&blob);
|
||||
blob = data_blob_talloc(call, NULL, wrapped.length + 4);
|
||||
if (blob.data == NULL) {
|
||||
goto failed;
|
||||
}
|
||||
RSIVAL(blob.data, 0, wrapped.length);
|
||||
memcpy(blob.data+4, wrapped.data, wrapped.length);
|
||||
data_blob_free(&wrapped);
|
||||
}
|
||||
|
||||
packet_send(conn->packet, blob);
|
||||
packet_send_callback(conn->packet, blob,
|
||||
call->send_callback, call->send_private);
|
||||
talloc_free(call);
|
||||
return;
|
||||
|
||||
failed:
|
||||
talloc_free(call);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
decode the input buffer
|
||||
decode/process data
|
||||
*/
|
||||
static NTSTATUS ldapsrv_decode_plain(struct ldapsrv_connection *conn, DATA_BLOB blob)
|
||||
static NTSTATUS ldapsrv_decode(void *private, DATA_BLOB blob)
|
||||
{
|
||||
struct ldapsrv_connection *conn = talloc_get_type(private,
|
||||
struct ldapsrv_connection);
|
||||
struct asn1_data asn1;
|
||||
struct ldap_message *msg = talloc(conn, struct ldap_message);
|
||||
|
||||
@ -165,63 +152,6 @@ static NTSTATUS ldapsrv_decode_plain(struct ldapsrv_connection *conn, DATA_BLOB
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
decode/process wrapped data
|
||||
*/
|
||||
static NTSTATUS ldapsrv_decode_wrapped(struct ldapsrv_connection *conn,
|
||||
DATA_BLOB blob)
|
||||
{
|
||||
DATA_BLOB wrapped, unwrapped;
|
||||
struct asn1_data asn1;
|
||||
struct ldap_message *msg = talloc(conn, struct ldap_message);
|
||||
NTSTATUS status;
|
||||
|
||||
if (msg == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
wrapped = data_blob_const(blob.data+4, blob.length-4);
|
||||
|
||||
status = gensec_unwrap(conn->gensec, msg, &wrapped, &unwrapped);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
|
||||
}
|
||||
|
||||
data_blob_free(&blob);
|
||||
|
||||
if (!asn1_load(&asn1, unwrapped)) {
|
||||
return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
|
||||
}
|
||||
|
||||
while (ldap_decode(&asn1, msg)) {
|
||||
ldapsrv_process_message(conn, msg);
|
||||
msg = talloc(conn, struct ldap_message);
|
||||
}
|
||||
|
||||
if (asn1.ofs < asn1.length) {
|
||||
return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
|
||||
}
|
||||
|
||||
talloc_free(msg);
|
||||
asn1_free(&asn1);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
decode/process data
|
||||
*/
|
||||
static NTSTATUS ldapsrv_decode(void *private, DATA_BLOB blob)
|
||||
{
|
||||
struct ldapsrv_connection *conn = talloc_get_type(private,
|
||||
struct ldapsrv_connection);
|
||||
if (conn->enable_wrap) {
|
||||
return ldapsrv_decode_wrapped(conn, blob);
|
||||
}
|
||||
return ldapsrv_decode_plain(conn, blob);
|
||||
}
|
||||
|
||||
/*
|
||||
Idle timeout handler
|
||||
*/
|
||||
@ -238,7 +168,7 @@ static void ldapsrv_conn_idle_timeout(struct event_context *ev,
|
||||
/*
|
||||
called when a LDAP socket becomes readable
|
||||
*/
|
||||
static void ldapsrv_recv(struct stream_connection *c, uint16_t flags)
|
||||
void ldapsrv_recv(struct stream_connection *c, uint16_t flags)
|
||||
{
|
||||
struct ldapsrv_connection *conn =
|
||||
talloc_get_type(c->private, struct ldapsrv_connection);
|
||||
@ -261,20 +191,6 @@ static void ldapsrv_recv(struct stream_connection *c, uint16_t flags)
|
||||
ldapsrv_conn_idle_timeout, conn);
|
||||
}
|
||||
|
||||
/*
|
||||
check if a blob is a complete ldap packet
|
||||
handle wrapper or unwrapped connections
|
||||
*/
|
||||
NTSTATUS ldapsrv_complete_packet(void *private, DATA_BLOB blob, size_t *size)
|
||||
{
|
||||
struct ldapsrv_connection *conn = talloc_get_type(private,
|
||||
struct ldapsrv_connection);
|
||||
if (conn->enable_wrap) {
|
||||
return packet_full_request_u32(private, blob, size);
|
||||
}
|
||||
return ldap_full_packet(private, blob, size);
|
||||
}
|
||||
|
||||
/*
|
||||
called when a LDAP socket becomes writable
|
||||
*/
|
||||
@ -411,7 +327,6 @@ static void ldapsrv_accept(struct stream_connection *c)
|
||||
return;
|
||||
}
|
||||
|
||||
conn->enable_wrap = False;
|
||||
conn->packet = NULL;
|
||||
conn->connection = c;
|
||||
conn->service = ldapsrv_service;
|
||||
@ -445,7 +360,7 @@ static void ldapsrv_accept(struct stream_connection *c)
|
||||
packet_set_private(conn->packet, conn);
|
||||
packet_set_socket(conn->packet, c->socket);
|
||||
packet_set_callback(conn->packet, ldapsrv_decode);
|
||||
packet_set_full_request(conn->packet, ldapsrv_complete_packet);
|
||||
packet_set_full_request(conn->packet, ldap_full_packet);
|
||||
packet_set_error_handler(conn->packet, ldapsrv_error_handler);
|
||||
packet_set_event_context(conn->packet, c->event.ctx);
|
||||
packet_set_fde(conn->packet, c->event.fde);
|
||||
|
@ -20,6 +20,8 @@
|
||||
*/
|
||||
|
||||
#include "libcli/ldap/ldap.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "lib/stream/packet.h"
|
||||
|
||||
struct ldapsrv_connection {
|
||||
struct stream_connection *connection;
|
||||
@ -29,9 +31,6 @@ struct ldapsrv_connection {
|
||||
struct cli_credentials *server_credentials;
|
||||
struct ldb_context *ldb;
|
||||
|
||||
/* are we using gensec wrapping? */
|
||||
BOOL enable_wrap;
|
||||
|
||||
BOOL global_catalog;
|
||||
|
||||
struct packet_context *packet;
|
||||
@ -54,6 +53,8 @@ struct ldapsrv_call {
|
||||
struct ldapsrv_reply *prev, *next;
|
||||
struct ldap_message *msg;
|
||||
} *replies;
|
||||
packet_send_callback_fn_t send_callback;
|
||||
void *send_private;
|
||||
};
|
||||
|
||||
struct ldapsrv_service;
|
||||
|
@ -189,15 +189,9 @@ _PUBLIC_ NTSTATUS socket_recv(struct socket_context *sock, void *buf,
|
||||
if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
|
||||
&& wantlen > 1) {
|
||||
|
||||
/* The returning of 0 and MORE_ENTRIES is incompatible
|
||||
with TLS and SASL sockets, as there is not a
|
||||
constant event source to re-trigger the reads */
|
||||
|
||||
if (!(sock->flags & SOCKET_FLAG_FAKE)) {
|
||||
if (random() % 10 == 0) {
|
||||
*nread = 0;
|
||||
return STATUS_MORE_ENTRIES;
|
||||
}
|
||||
if (random() % 10 == 0) {
|
||||
*nread = 0;
|
||||
return STATUS_MORE_ENTRIES;
|
||||
}
|
||||
return sock->ops->fn_recv(sock, buf, 1+(random() % wantlen), nread);
|
||||
}
|
||||
@ -240,17 +234,22 @@ _PUBLIC_ NTSTATUS socket_send(struct socket_context *sock,
|
||||
|
||||
if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
|
||||
&& blob->length > 1) {
|
||||
DATA_BLOB blob2 = *blob;
|
||||
if (random() % 10 == 0) {
|
||||
*sendlen = 0;
|
||||
return STATUS_MORE_ENTRIES;
|
||||
}
|
||||
/* The variable size sends are incompatilbe with TLS and SASL
|
||||
/* The random size sends are incompatible with TLS and SASL
|
||||
* sockets, which require re-sends to be consistant */
|
||||
if (!(sock->flags & SOCKET_FLAG_FAKE)) {
|
||||
DATA_BLOB blob2 = *blob;
|
||||
if (!(sock->flags & SOCKET_FLAG_ENCRYPT)) {
|
||||
blob2.length = 1+(random() % blob2.length);
|
||||
return sock->ops->fn_send(sock, &blob2, sendlen);
|
||||
} else {
|
||||
/* This is particularly stressful on buggy
|
||||
* LDAP clients, that don't expect on LDAP
|
||||
* packet in many SASL packets */
|
||||
blob2.length = 1 + blob2.length/2;
|
||||
}
|
||||
return sock->ops->fn_send(sock, &blob2, sendlen);
|
||||
}
|
||||
return sock->ops->fn_send(sock, blob, sendlen);
|
||||
}
|
||||
|
@ -102,7 +102,13 @@ enum socket_state {
|
||||
#define SOCKET_FLAG_BLOCK 0x00000001
|
||||
#define SOCKET_FLAG_PEEK 0x00000002
|
||||
#define SOCKET_FLAG_TESTNONBLOCK 0x00000004
|
||||
#define SOCKET_FLAG_FAKE 0x00000008 /* This is an implementation not directly on top of a real socket */
|
||||
#define SOCKET_FLAG_ENCRYPT 0x00000008 /* This socket
|
||||
* implementation requires
|
||||
* that re-sends be
|
||||
* consistant, because it
|
||||
* is encrypting data.
|
||||
* This modifies the
|
||||
* TESTNONBLOCK case */
|
||||
|
||||
struct socket_context {
|
||||
enum socket_type type;
|
||||
|
@ -28,7 +28,6 @@
|
||||
#include "lib/socket/socket.h"
|
||||
#include "lib/stream/packet.h"
|
||||
|
||||
|
||||
struct packet_context {
|
||||
packet_callback_fn_t callback;
|
||||
packet_full_request_fn_t full_request;
|
||||
@ -53,6 +52,8 @@ struct packet_context {
|
||||
struct send_element *next, *prev;
|
||||
DATA_BLOB blob;
|
||||
size_t nsent;
|
||||
packet_send_callback_fn_t send_callback;
|
||||
void *send_callback_private;
|
||||
} *send_queue;
|
||||
};
|
||||
|
||||
@ -374,6 +375,7 @@ next_partial:
|
||||
return;
|
||||
}
|
||||
|
||||
/* Have we consumed the whole buffer yet? */
|
||||
if (pc->partial.length == 0) {
|
||||
return;
|
||||
}
|
||||
@ -436,7 +438,7 @@ _PUBLIC_ void packet_queue_run(struct packet_context *pc)
|
||||
status = socket_send(pc->sock, &blob, &nwritten);
|
||||
|
||||
if (NT_STATUS_IS_ERR(status)) {
|
||||
packet_error(pc, NT_STATUS_NET_WRITE_FAULT);
|
||||
packet_error(pc, status);
|
||||
return;
|
||||
}
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
@ -445,6 +447,9 @@ _PUBLIC_ void packet_queue_run(struct packet_context *pc)
|
||||
el->nsent += nwritten;
|
||||
if (el->nsent == el->blob.length) {
|
||||
DLIST_REMOVE(pc->send_queue, el);
|
||||
if (el->send_callback) {
|
||||
el->send_callback(el->send_callback_private);
|
||||
}
|
||||
talloc_free(el);
|
||||
}
|
||||
}
|
||||
@ -455,9 +460,15 @@ _PUBLIC_ void packet_queue_run(struct packet_context *pc)
|
||||
}
|
||||
|
||||
/*
|
||||
put a packet in the send queue
|
||||
put a packet in the send queue. When the packet is actually sent,
|
||||
call send_callback.
|
||||
|
||||
Useful for operations that must occour after sending a message, such
|
||||
as the switch to SASL encryption after as sucessful LDAP bind relpy.
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS packet_send(struct packet_context *pc, DATA_BLOB blob)
|
||||
_PUBLIC_ NTSTATUS packet_send_callback(struct packet_context *pc, DATA_BLOB blob,
|
||||
packet_send_callback_fn_t send_callback,
|
||||
void *private)
|
||||
{
|
||||
struct send_element *el;
|
||||
el = talloc(pc, struct send_element);
|
||||
@ -466,6 +477,8 @@ _PUBLIC_ NTSTATUS packet_send(struct packet_context *pc, DATA_BLOB blob)
|
||||
DLIST_ADD_END(pc->send_queue, el, struct send_element *);
|
||||
el->blob = blob;
|
||||
el->nsent = 0;
|
||||
el->send_callback = send_callback;
|
||||
el->send_callback_private = private;
|
||||
|
||||
/* if we aren't going to free the packet then we must reference it
|
||||
to ensure it doesn't disappear before going out */
|
||||
@ -477,11 +490,23 @@ _PUBLIC_ NTSTATUS packet_send(struct packet_context *pc, DATA_BLOB blob)
|
||||
talloc_steal(el, blob.data);
|
||||
}
|
||||
|
||||
if (private && !talloc_reference(el, private)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
EVENT_FD_WRITEABLE(pc->fde);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
put a packet in the send queue
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS packet_send(struct packet_context *pc, DATA_BLOB blob)
|
||||
{
|
||||
return packet_send_callback(pc, blob, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
a full request checker for NBT formatted packets (first 3 bytes are length)
|
||||
|
@ -24,6 +24,9 @@
|
||||
typedef NTSTATUS (*packet_full_request_fn_t)(void *private,
|
||||
DATA_BLOB blob, size_t *packet_size);
|
||||
typedef NTSTATUS (*packet_callback_fn_t)(void *private, DATA_BLOB blob);
|
||||
|
||||
/* Used to notify that a packet has been sent, and is on the wire */
|
||||
typedef void (*packet_send_callback_fn_t)(void *private);
|
||||
typedef void (*packet_error_handler_fn_t)(void *private, NTSTATUS status);
|
||||
|
||||
|
||||
@ -43,6 +46,9 @@ void packet_recv(struct packet_context *pc);
|
||||
void packet_recv_disable(struct packet_context *pc);
|
||||
void packet_recv_enable(struct packet_context *pc);
|
||||
NTSTATUS packet_send(struct packet_context *pc, DATA_BLOB blob);
|
||||
NTSTATUS packet_send_callback(struct packet_context *pc, DATA_BLOB blob,
|
||||
packet_send_callback_fn_t send_callback,
|
||||
void *private);
|
||||
void packet_queue_run(struct packet_context *pc);
|
||||
|
||||
/*
|
||||
|
@ -444,7 +444,7 @@ struct socket_context *tls_init_server(struct tls_params *params,
|
||||
|
||||
nt_status = socket_create_with_ops(socket, &tls_socket_ops, &new_sock,
|
||||
SOCKET_TYPE_STREAM,
|
||||
socket->flags | SOCKET_FLAG_FAKE);
|
||||
socket->flags | SOCKET_FLAG_ENCRYPT);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return NULL;
|
||||
}
|
||||
@ -524,7 +524,7 @@ struct socket_context *tls_init_client(struct socket_context *socket,
|
||||
|
||||
nt_status = socket_create_with_ops(socket, &tls_socket_ops, &new_sock,
|
||||
SOCKET_TYPE_STREAM,
|
||||
socket->flags | SOCKET_FLAG_FAKE);
|
||||
socket->flags | SOCKET_FLAG_ENCRYPT);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ OBJ_FILES = ldap.o \
|
||||
ldap_ildap.o \
|
||||
ldap_controls.o
|
||||
PUBLIC_DEPENDENCIES = LIBSAMBA-ERRORS LIBEVENTS LIBPACKET
|
||||
PRIVATE_DEPENDENCIES = LIBCLI_COMPOSITE samba-socket LIBCLI_RESOLVE NDR_SAMR LIBTLS ASN1_UTIL
|
||||
PRIVATE_DEPENDENCIES = LIBCLI_COMPOSITE samba-socket LIBCLI_RESOLVE NDR_SAMR LIBTLS ASN1_UTIL GENSEC_SOCKET
|
||||
#PRIVATE_DEPENDENCIES = gensec
|
||||
# End SUBSYSTEM LIBCLI_LDAP
|
||||
#################################
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include "libcli/ldap/ldap_client.h"
|
||||
#include "lib/tls/tls.h"
|
||||
#include "auth/auth.h"
|
||||
#include "auth/gensec/socket.h"
|
||||
#include "lib/stream/packet.h"
|
||||
|
||||
struct ldap_simple_creds {
|
||||
const char *dn;
|
||||
@ -365,15 +367,23 @@ NTSTATUS ldap_bind_sasl(struct ldap_connection *conn, struct cli_credentials *cr
|
||||
}
|
||||
}
|
||||
|
||||
if (NT_STATUS_IS_OK(status) &&
|
||||
(gensec_have_feature(conn->gensec, GENSEC_FEATURE_SEAL) ||
|
||||
gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN))) {
|
||||
conn->enable_wrap = True;
|
||||
}
|
||||
|
||||
talloc_free(tmp_ctx);
|
||||
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
struct socket_context *socket = gensec_socket_init(conn->gensec,
|
||||
conn->sock,
|
||||
conn->event.event_ctx,
|
||||
ldap_read_io_handler,
|
||||
conn);
|
||||
if (socket) {
|
||||
conn->sock = socket;
|
||||
talloc_steal(conn->sock, socket);
|
||||
packet_set_socket(conn->packet, socket);
|
||||
} else {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
conn->bind.type = LDAP_BIND_SASL;
|
||||
conn->bind.creds = creds;
|
||||
}
|
||||
|
@ -165,25 +165,13 @@ static void ldap_match_message(struct ldap_connection *conn, struct ldap_message
|
||||
|
||||
|
||||
/*
|
||||
check if a blob is a complete ldap packet
|
||||
handle wrapper or unwrapped connections
|
||||
decode/process LDAP data
|
||||
*/
|
||||
NTSTATUS ldap_complete_packet(void *private_data, DATA_BLOB blob, size_t *size)
|
||||
{
|
||||
struct ldap_connection *conn = talloc_get_type(private_data,
|
||||
struct ldap_connection);
|
||||
if (conn->enable_wrap) {
|
||||
return packet_full_request_u32(private_data, blob, size);
|
||||
}
|
||||
return ldap_full_packet(private_data, blob, size);
|
||||
}
|
||||
|
||||
/*
|
||||
decode/process plain data
|
||||
*/
|
||||
static NTSTATUS ldap_decode_plain(struct ldap_connection *conn, DATA_BLOB blob)
|
||||
static NTSTATUS ldap_recv_handler(void *private_data, DATA_BLOB blob)
|
||||
{
|
||||
struct asn1_data asn1;
|
||||
struct ldap_connection *conn = talloc_get_type(private_data,
|
||||
struct ldap_connection);
|
||||
struct ldap_message *msg = talloc(conn, struct ldap_message);
|
||||
|
||||
if (msg == NULL) {
|
||||
@ -205,60 +193,14 @@ static NTSTATUS ldap_decode_plain(struct ldap_connection *conn, DATA_BLOB blob)
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
decode/process wrapped data
|
||||
*/
|
||||
static NTSTATUS ldap_decode_wrapped(struct ldap_connection *conn, DATA_BLOB blob)
|
||||
{
|
||||
DATA_BLOB wrapped, unwrapped;
|
||||
struct asn1_data asn1;
|
||||
struct ldap_message *msg = talloc(conn, struct ldap_message);
|
||||
NTSTATUS status;
|
||||
|
||||
if (msg == NULL) {
|
||||
return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
|
||||
}
|
||||
|
||||
wrapped = data_blob_const(blob.data+4, blob.length-4);
|
||||
|
||||
status = gensec_unwrap(conn->gensec, msg, &wrapped, &unwrapped);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
|
||||
}
|
||||
|
||||
data_blob_free(&blob);
|
||||
|
||||
if (!asn1_load(&asn1, unwrapped)) {
|
||||
return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
|
||||
}
|
||||
|
||||
while (ldap_decode(&asn1, msg)) {
|
||||
ldap_match_message(conn, msg);
|
||||
msg = talloc(conn, struct ldap_message);
|
||||
}
|
||||
|
||||
talloc_free(msg);
|
||||
asn1_free(&asn1);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
handle ldap recv events
|
||||
*/
|
||||
static NTSTATUS ldap_recv_handler(void *private_data, DATA_BLOB blob)
|
||||
/* Handle read events, from the GENSEC socket callback, or real events */
|
||||
void ldap_read_io_handler(void *private_data, uint16_t flags)
|
||||
{
|
||||
struct ldap_connection *conn = talloc_get_type(private_data,
|
||||
struct ldap_connection);
|
||||
if (conn->enable_wrap) {
|
||||
return ldap_decode_wrapped(conn, blob);
|
||||
}
|
||||
|
||||
return ldap_decode_plain(conn, blob);
|
||||
packet_recv(conn->packet);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
handle ldap socket events
|
||||
*/
|
||||
@ -272,7 +214,7 @@ static void ldap_io_handler(struct event_context *ev, struct fd_event *fde,
|
||||
if (!tls_enabled(conn->sock)) return;
|
||||
}
|
||||
if (flags & EVENT_FD_READ) {
|
||||
packet_recv(conn->packet);
|
||||
ldap_read_io_handler(private_data, flags);
|
||||
}
|
||||
}
|
||||
|
||||
@ -417,7 +359,7 @@ static void ldap_connect_recv_conn(struct composite_context *ctx)
|
||||
packet_set_private(conn->packet, conn);
|
||||
packet_set_socket(conn->packet, conn->sock);
|
||||
packet_set_callback(conn->packet, ldap_recv_handler);
|
||||
packet_set_full_request(conn->packet, ldap_complete_packet);
|
||||
packet_set_full_request(conn->packet, ldap_full_packet);
|
||||
packet_set_error_handler(conn->packet, ldap_error_handler);
|
||||
packet_set_event_context(conn->packet, conn->event.event_ctx);
|
||||
packet_set_fde(conn->packet, conn->event.fde);
|
||||
@ -561,24 +503,6 @@ struct ldap_request *ldap_request_send(struct ldap_connection *conn,
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* possibly encrypt/sign the request */
|
||||
if (conn->enable_wrap) {
|
||||
DATA_BLOB wrapped;
|
||||
|
||||
status = gensec_wrap(conn->gensec, req, &req->data, &wrapped);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto failed;
|
||||
}
|
||||
data_blob_free(&req->data);
|
||||
req->data = data_blob_talloc(req, NULL, wrapped.length + 4);
|
||||
if (req->data.data == NULL) {
|
||||
goto failed;
|
||||
}
|
||||
RSIVAL(req->data.data, 0, wrapped.length);
|
||||
memcpy(req->data.data+4, wrapped.data, wrapped.length);
|
||||
data_blob_free(&wrapped);
|
||||
}
|
||||
|
||||
status = packet_send(conn->packet, req->data);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto failed;
|
||||
|
@ -80,9 +80,6 @@ struct ldap_connection {
|
||||
/* Let's support SASL */
|
||||
struct gensec_security *gensec;
|
||||
|
||||
/* set if we are wrapping requests */
|
||||
BOOL enable_wrap;
|
||||
|
||||
/* the default timeout for messages */
|
||||
int timeout;
|
||||
|
||||
|
@ -100,6 +100,11 @@ static void stream_io_handler(struct event_context *ev, struct fd_event *fde,
|
||||
}
|
||||
}
|
||||
|
||||
void stream_io_handler_callback(void *conn, uint16_t flags)
|
||||
{
|
||||
stream_io_handler(NULL, NULL, flags, conn);
|
||||
}
|
||||
|
||||
/*
|
||||
this creates a stream_connection from an already existing connection,
|
||||
used for protocols, where a client connection needs to switched into
|
||||
|
Loading…
Reference in New Issue
Block a user