1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00
samba-mirror/source4/ldap_server/ldap_extended.c
Andrew Tridgell bb7e6f0f51 Worked around a problem with select/poll/epoll and gnutls
Our packet layer relies on the event system reliably telling us when a
packet is available. When we are using a socket layer like TLS then
things get a bit trickier, as there may be bytes in the encryption
buffer which could be read even if there are no bytes at the socket
level. The GNUTLS library is supposed to prevent this happening by
always leaving some data at the socket level when there is data to be
processed in its buffers, but it seems that this is not always
reliable.

To work around this I have added a new packet option
packet_set_unreliable_select() which tells the packet layer to not
assume that the socket layer has a reliable select, and to instead
keep trying to read from the socket until it gets back no data. This
option is set for the ldap client and server when TLS is negotiated.

This seems to fix the problems with the ldaps tests.
2009-02-18 17:37:45 +11:00

151 lines
4.6 KiB
C

/*
Unix SMB/CIFS implementation.
LDAP server
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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#include "ldap_server/ldap_server.h"
#include "../lib/util/dlinklist.h"
#include "libcli/ldap/ldap.h"
#include "lib/tls/tls.h"
#include "smbd/service_stream.h"
struct ldapsrv_starttls_context {
struct ldapsrv_connection *conn;
struct socket_context *tls_socket;
};
static void ldapsrv_start_tls(void *private_data)
{
struct ldapsrv_starttls_context *ctx = talloc_get_type(private_data, struct ldapsrv_starttls_context);
talloc_steal(ctx->conn->connection, ctx->tls_socket);
talloc_unlink(ctx->conn->connection, ctx->conn->connection->socket);
ctx->conn->sockets.tls = ctx->tls_socket;
ctx->conn->connection->socket = ctx->tls_socket;
packet_set_socket(ctx->conn->packet, ctx->conn->connection->socket);
packet_set_unreliable_select(ctx->conn->packet);
}
static NTSTATUS ldapsrv_StartTLS(struct ldapsrv_call *call,
struct ldapsrv_reply *reply,
const char **errstr)
{
struct ldapsrv_starttls_context *ctx;
(*errstr) = NULL;
/*
* TODO: give LDAP_OPERATIONS_ERROR also when
* there're pending requests or there's
* a SASL bind in progress
* (see rfc4513 section 3.1.1)
*/
if (call->conn->sockets.tls) {
(*errstr) = talloc_asprintf(reply, "START-TLS: TLS is already enabled on this LDAP session");
return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
}
ctx = talloc(call, struct ldapsrv_starttls_context);
NT_STATUS_HAVE_NO_MEMORY(ctx);
ctx->conn = call->conn;
ctx->tls_socket = tls_init_server(call->conn->service->tls_params,
call->conn->connection->socket,
call->conn->connection->event.fde,
NULL);
if (!ctx->tls_socket) {
(*errstr) = talloc_asprintf(reply, "START-TLS: Failed to setup TLS socket");
return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
}
call->send_callback = ldapsrv_start_tls;
call->send_private = ctx;
reply->msg->r.ExtendedResponse.response.resultcode = LDAP_SUCCESS;
reply->msg->r.ExtendedResponse.response.errormessage = NULL;
ldapsrv_queue_reply(call, reply);
return NT_STATUS_OK;
}
struct ldapsrv_extended_operation {
const char *oid;
NTSTATUS (*fn)(struct ldapsrv_call *call, struct ldapsrv_reply *reply, const char **errorstr);
};
static struct ldapsrv_extended_operation extended_ops[] = {
{
.oid = LDB_EXTENDED_START_TLS_OID,
.fn = ldapsrv_StartTLS,
},{
.oid = NULL,
.fn = NULL,
}
};
NTSTATUS ldapsrv_ExtendedRequest(struct ldapsrv_call *call)
{
struct ldap_ExtendedRequest *req = &call->request->r.ExtendedRequest;
struct ldapsrv_reply *reply;
int result = LDAP_PROTOCOL_ERROR;
const char *error_str = NULL;
NTSTATUS status = NT_STATUS_OK;
uint32_t i;
DEBUG(10, ("Extended\n"));
reply = ldapsrv_init_reply(call, LDAP_TAG_ExtendedResponse);
NT_STATUS_HAVE_NO_MEMORY(reply);
ZERO_STRUCT(reply->msg->r);
reply->msg->r.ExtendedResponse.oid = talloc_steal(reply, req->oid);
reply->msg->r.ExtendedResponse.response.resultcode = LDAP_PROTOCOL_ERROR;
reply->msg->r.ExtendedResponse.response.errormessage = NULL;
for (i=0; extended_ops[i].oid; i++) {
if (strcmp(extended_ops[i].oid,req->oid) != 0) continue;
/*
* if the backend function returns an error we
* need to send the reply otherwise the reply is already
* send and we need to return directly
*/
status = extended_ops[i].fn(call, reply, &error_str);
NT_STATUS_IS_OK_RETURN(status);
if (NT_STATUS_IS_LDAP(status)) {
result = NT_STATUS_LDAP_CODE(status);
} else {
result = LDAP_OPERATIONS_ERROR;
error_str = talloc_asprintf(reply, "Extended Operation(%s) failed: %s",
req->oid, nt_errstr(status));
}
}
/* if we haven't found the oid, then status is still NT_STATUS_OK */
if (NT_STATUS_IS_OK(status)) {
error_str = talloc_asprintf(reply, "Extended Operation(%s) not supported",
req->oid);
}
reply->msg->r.ExtendedResponse.response.resultcode = result;
reply->msg->r.ExtendedResponse.response.errormessage = error_str;
ldapsrv_queue_reply(call, reply);
return NT_STATUS_OK;
}