1
0
mirror of https://github.com/samba-team/samba.git synced 2025-11-05 04:23:51 +03:00

r7746: - added TLS support to our ldap server

- this involved changing the buffer handling in the ldap server quite a
  lot, as it didn't handle partial packets at all

- removed completely bogus asn1_object_length() function. You can't
  do that with BER/DER
This commit is contained in:
Andrew Tridgell
2005-06-19 07:21:18 +00:00
committed by Gerald (Jerry) Carter
parent 42d8a1a222
commit fed6f4cc6c
3 changed files with 115 additions and 102 deletions

View File

@@ -1,6 +1,9 @@
/* /*
Unix SMB/CIFS implementation. Unix SMB/CIFS implementation.
LDAP server LDAP server
Copyright (C) Andrew Tridgell 2005
Copyright (C) Volker Lendecke 2004 Copyright (C) Volker Lendecke 2004
Copyright (C) Stefan Metzmacher 2004 Copyright (C) Stefan Metzmacher 2004
@@ -25,14 +28,18 @@
#include "dlinklist.h" #include "dlinklist.h"
#include "asn_1.h" #include "asn_1.h"
#include "ldap_server/ldap_server.h" #include "ldap_server/ldap_server.h"
#include "smbd/service_task.h"
#include "smbd/service_stream.h" #include "smbd/service_stream.h"
#include "lib/socket/socket.h" #include "lib/socket/socket.h"
#include "lib/tls/tls.h"
/* /*
close the socket and shutdown a server_context close the socket and shutdown a server_context
*/ */
static void ldapsrv_terminate_connection(struct ldapsrv_connection *ldap_conn, const char *reason) static void ldapsrv_terminate_connection(struct ldapsrv_connection *ldap_conn, const char *reason)
{ {
talloc_free(ldap_conn->tls);
ldap_conn->tls = NULL;
stream_terminate_connection(ldap_conn->connection, reason); stream_terminate_connection(ldap_conn->connection, reason);
} }
@@ -65,27 +72,36 @@ BOOL ldapsrv_append_to_buf(struct rw_buffer *buf, uint8_t *data, size_t length)
memcpy(buf->data+buf->length, data, length); memcpy(buf->data+buf->length, data, length);
buf->length += length; buf->length += length;
return True; return True;
} }
static BOOL read_into_buf(struct socket_context *sock, struct rw_buffer *buf) static BOOL read_into_buf(struct ldapsrv_connection *conn, struct rw_buffer *buf)
{ {
NTSTATUS status; NTSTATUS status;
DATA_BLOB tmp_blob; DATA_BLOB tmp_blob;
BOOL ret; BOOL ret;
size_t nread; size_t nread;
tmp_blob = data_blob_talloc(sock, NULL, 1024); tmp_blob = data_blob_talloc(conn, NULL, 1024);
if (tmp_blob.data == NULL) { if (tmp_blob.data == NULL) {
return False; return False;
} }
status = socket_recv(sock, tmp_blob.data, tmp_blob.length, &nread, 0); status = tls_socket_recv(conn->tls, tmp_blob.data, tmp_blob.length, &nread);
if (NT_STATUS_IS_OK(status) && nread == 0) {
return False;
}
if (NT_STATUS_IS_ERR(status)) { if (NT_STATUS_IS_ERR(status)) {
DEBUG(10,("socket_recv: %s\n",nt_errstr(status))); DEBUG(10,("socket_recv: %s\n",nt_errstr(status)));
talloc_free(tmp_blob.data); talloc_free(tmp_blob.data);
return False; return False;
} }
if (!NT_STATUS_IS_OK(status)) {
talloc_free(tmp_blob.data);
return True;
}
tmp_blob.length = nread; tmp_blob.length = nread;
ret = ldapsrv_append_to_buf(buf, tmp_blob.data, tmp_blob.length); ret = ldapsrv_append_to_buf(buf, tmp_blob.data, tmp_blob.length);
@@ -104,19 +120,13 @@ static BOOL ldapsrv_read_buf(struct ldapsrv_connection *conn)
BOOL ret; BOOL ret;
uint8_t *buf; uint8_t *buf;
size_t buf_length, sasl_length; size_t buf_length, sasl_length;
struct socket_context *sock = conn->connection->socket;
TALLOC_CTX *mem_ctx; TALLOC_CTX *mem_ctx;
size_t nread; size_t nread;
if (!conn->gensec) { if (!conn->gensec || !conn->session_info ||
return read_into_buf(sock, &conn->in_buffer); !(gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN) ||
}
if (!conn->session_info) {
return read_into_buf(sock, &conn->in_buffer);
}
if (!(gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN) ||
gensec_have_feature(conn->gensec, GENSEC_FEATURE_SEAL))) { gensec_have_feature(conn->gensec, GENSEC_FEATURE_SEAL))) {
return read_into_buf(sock, &conn->in_buffer); return read_into_buf(conn, &conn->in_buffer);
} }
mem_ctx = talloc_new(conn); mem_ctx = talloc_new(conn);
@@ -131,12 +141,20 @@ static BOOL ldapsrv_read_buf(struct ldapsrv_connection *conn)
return False; return False;
} }
status = socket_recv(sock, tmp_blob.data, tmp_blob.length, &nread, 0); status = tls_socket_recv(conn->tls, tmp_blob.data, tmp_blob.length, &nread);
if (NT_STATUS_IS_OK(status) && nread == 0) {
talloc_free(conn->tls);
return False;
}
if (NT_STATUS_IS_ERR(status)) { if (NT_STATUS_IS_ERR(status)) {
DEBUG(10,("socket_recv: %s\n",nt_errstr(status))); DEBUG(10,("socket_recv: %s\n",nt_errstr(status)));
talloc_free(mem_ctx); talloc_free(mem_ctx);
return False; return False;
} }
if (!NT_STATUS_IS_OK(status)) {
talloc_free(mem_ctx);
return True;
}
tmp_blob.length = nread; tmp_blob.length = nread;
ret = ldapsrv_append_to_buf(&conn->sasl_in_buffer, tmp_blob.data, tmp_blob.length); ret = ldapsrv_append_to_buf(&conn->sasl_in_buffer, tmp_blob.data, tmp_blob.length);
@@ -185,7 +203,7 @@ static BOOL ldapsrv_read_buf(struct ldapsrv_connection *conn)
return ret; return ret;
} }
static BOOL write_from_buf(struct socket_context *sock, struct rw_buffer *buf) static BOOL write_from_buf(struct ldapsrv_connection *conn, struct rw_buffer *buf)
{ {
NTSTATUS status; NTSTATUS status;
DATA_BLOB tmp_blob; DATA_BLOB tmp_blob;
@@ -194,7 +212,7 @@ static BOOL write_from_buf(struct socket_context *sock, struct rw_buffer *buf)
tmp_blob.data = buf->data; tmp_blob.data = buf->data;
tmp_blob.length = buf->length; tmp_blob.length = buf->length;
status = socket_send(sock, &tmp_blob, &sendlen, 0); status = tls_socket_send(conn->tls, &tmp_blob, &sendlen);
if (!NT_STATUS_IS_OK(status)) { if (!NT_STATUS_IS_OK(status)) {
DEBUG(10,("socket_send() %s\n",nt_errstr(status))); DEBUG(10,("socket_send() %s\n",nt_errstr(status)));
return False; return False;
@@ -213,20 +231,19 @@ static BOOL ldapsrv_write_buf(struct ldapsrv_connection *conn)
DATA_BLOB sasl; DATA_BLOB sasl;
size_t sendlen; size_t sendlen;
BOOL ret; BOOL ret;
struct socket_context *sock = conn->connection->socket;
TALLOC_CTX *mem_ctx; TALLOC_CTX *mem_ctx;
if (!conn->gensec) { if (!conn->gensec) {
return write_from_buf(sock, &conn->out_buffer); return write_from_buf(conn, &conn->out_buffer);
} }
if (!conn->session_info) { if (!conn->session_info) {
return write_from_buf(sock, &conn->out_buffer); return write_from_buf(conn, &conn->out_buffer);
} }
if (conn->sasl_out_buffer.length == 0 && if (conn->sasl_out_buffer.length == 0 &&
!(gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN) || !(gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN) ||
gensec_have_feature(conn->gensec, GENSEC_FEATURE_SEAL))) { gensec_have_feature(conn->gensec, GENSEC_FEATURE_SEAL))) {
return write_from_buf(sock, &conn->out_buffer); return write_from_buf(conn, &conn->out_buffer);
} }
mem_ctx = talloc_new(conn); mem_ctx = talloc_new(conn);
@@ -270,7 +287,7 @@ nodata:
tmp_blob.data = conn->sasl_out_buffer.data; tmp_blob.data = conn->sasl_out_buffer.data;
tmp_blob.length = conn->sasl_out_buffer.length; tmp_blob.length = conn->sasl_out_buffer.length;
status = socket_send(sock, &tmp_blob, &sendlen, 0); status = tls_socket_send(conn->tls, &tmp_blob, &sendlen);
if (!NT_STATUS_IS_OK(status)) { if (!NT_STATUS_IS_OK(status)) {
DEBUG(10,("socket_send() %s\n",nt_errstr(status))); DEBUG(10,("socket_send() %s\n",nt_errstr(status)));
talloc_free(mem_ctx); talloc_free(mem_ctx);
@@ -334,14 +351,10 @@ static void ldapsrv_recv(struct stream_connection *conn, uint16_t flags)
{ {
struct ldapsrv_connection *ldap_conn = talloc_get_type(conn->private, struct ldapsrv_connection); struct ldapsrv_connection *ldap_conn = talloc_get_type(conn->private, struct ldapsrv_connection);
uint8_t *buf; uint8_t *buf;
size_t buf_length, msg_length; size_t buf_length;
DATA_BLOB blob;
struct asn1_data data;
struct ldapsrv_call *call; struct ldapsrv_call *call;
NTSTATUS status; NTSTATUS status;
DEBUG(10,("ldapsrv_recv\n"));
if (!ldapsrv_read_buf(ldap_conn)) { if (!ldapsrv_read_buf(ldap_conn)) {
ldapsrv_terminate_connection(ldap_conn, "ldapsrv_read_buf() failed"); ldapsrv_terminate_connection(ldap_conn, "ldapsrv_read_buf() failed");
return; return;
@@ -350,59 +363,45 @@ static void ldapsrv_recv(struct stream_connection *conn, uint16_t flags)
peek_into_read_buf(&ldap_conn->in_buffer, &buf, &buf_length); peek_into_read_buf(&ldap_conn->in_buffer, &buf, &buf_length);
while (buf_length > 0) { while (buf_length > 0) {
/* LDAP Messages are always SEQUENCES */ DATA_BLOB blob;
struct asn1_data data;
if (!asn1_object_length(buf, buf_length, ASN1_SEQUENCE(0), struct ldap_message *msg = talloc(conn, struct ldap_message);
&msg_length)) {
ldapsrv_terminate_connection(ldap_conn, "asn1_object_length() failed");
return;
}
if (buf_length < msg_length) {
/* Not enough yet */
break;
}
/* We've got a complete LDAP request in the in-buffer, convert
* that to a ldap_message and put it into the incoming
* queue. */
blob.data = buf; blob.data = buf;
blob.length = msg_length; blob.length = buf_length;
if (!asn1_load(&data, blob)) { if (!asn1_load(&data, blob)) {
ldapsrv_terminate_connection(ldap_conn, "asn1_load() failed"); ldapsrv_terminate_connection(ldap_conn, "asn1_load() failed");
return; return;
} }
if (!ldap_decode(&data, msg)) {
if (data.ofs == data.length) {
/* we don't have a complete msg yet */
talloc_free(msg);
asn1_free(&data);
return;
}
asn1_free(&data);
talloc_free(msg);
ldapsrv_terminate_connection(ldap_conn, "ldap_decode() failed");
return;
}
ldapsrv_consumed_from_buf(&ldap_conn->in_buffer, data.ofs);
asn1_free(&data);
call = talloc_zero(ldap_conn, struct ldapsrv_call); call = talloc_zero(ldap_conn, struct ldapsrv_call);
if (!call) { if (!call) {
ldapsrv_terminate_connection(ldap_conn, "no memory"); ldapsrv_terminate_connection(ldap_conn, "no memory");
return; return;
} }
call->request = talloc_zero(call, struct ldap_message); call->request = talloc_steal(call, msg);
if (call->request == NULL) {
ldapsrv_terminate_connection(ldap_conn, "no memory");
return;
}
call->state = LDAPSRV_CALL_STATE_NEW; call->state = LDAPSRV_CALL_STATE_NEW;
call->conn = ldap_conn; call->conn = ldap_conn;
if (!ldap_decode(&data, call->request)) { DLIST_ADD_END(ldap_conn->calls, call, struct ldapsrv_call *);
dump_data(0,buf, msg_length);
asn1_free(&data);
ldapsrv_terminate_connection(ldap_conn, "ldap_decode() failed");
return;
}
asn1_free(&data);
DLIST_ADD_END(ldap_conn->calls, call,
struct ldapsrv_call *);
ldapsrv_consumed_from_buf(&ldap_conn->in_buffer, msg_length);
status = ldapsrv_do_call(call); status = ldapsrv_do_call(call);
if (!NT_STATUS_IS_OK(status)) { if (!NT_STATUS_IS_OK(status)) {
@@ -453,18 +452,27 @@ static void ldapsrv_send(struct stream_connection *conn, uint16_t flags)
*/ */
static void ldapsrv_accept(struct stream_connection *conn) static void ldapsrv_accept(struct stream_connection *conn)
{ {
struct ldapsrv_service *ldapsrv_service =
talloc_get_type(conn->private, struct ldapsrv_service);
struct ldapsrv_connection *ldap_conn; struct ldapsrv_connection *ldap_conn;
DEBUG(10, ("ldapsrv_accept\n"));
ldap_conn = talloc_zero(conn, struct ldapsrv_connection); ldap_conn = talloc_zero(conn, struct ldapsrv_connection);
if (ldap_conn == NULL) goto failed;
if (ldap_conn == NULL)
return;
ldap_conn->connection = conn; ldap_conn->connection = conn;
ldap_conn->service = talloc_get_type(conn->private, struct ldapsrv_service); ldap_conn->service = talloc_get_type(conn->private, struct ldapsrv_service);
conn->private = ldap_conn; conn->private = ldap_conn;
/* note that '0' is a ASN1_SEQUENCE(0), which is the first byte on
any ldap connection */
ldap_conn->tls = tls_init_server(ldapsrv_service->tls_params, conn->socket,
conn->event.fde, "0");
if (ldap_conn->tls == NULL) goto failed;
return;
failed:
talloc_free(conn);
} }
static const struct stream_server_ops ldap_stream_ops = { static const struct stream_server_ops ldap_stream_ops = {
@@ -485,31 +493,40 @@ static NTSTATUS add_socket(struct event_context *event_context, const struct mod
status = stream_setup_socket(event_context, model_ops, &ldap_stream_ops, status = stream_setup_socket(event_context, model_ops, &ldap_stream_ops,
"ipv4", address, &port, ldap_service); "ipv4", address, &port, ldap_service);
NT_STATUS_NOT_OK_RETURN(status); if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("ldapsrv failed to bind to %s:%u - %s\n",
address, port, nt_errstr(status)));
}
port = 3268; /* add ldaps server */
port = 636;
return stream_setup_socket(event_context, model_ops, &ldap_stream_ops, status = stream_setup_socket(event_context, model_ops, &ldap_stream_ops,
"ipv4", address, &port, ldap_service); "ipv4", address, &port, ldap_service);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("ldapsrv failed to bind to %s:%u - %s\n",
address, port, nt_errstr(status)));
}
return status;
} }
/* /*
open the ldap server sockets open the ldap server sockets
*/ */
static NTSTATUS ldapsrv_init(struct event_context *event_context, const struct model_ops *model_ops) static void ldapsrv_task_init(struct task_server *task)
{ {
struct ldapsrv_service *ldap_service; struct ldapsrv_service *ldap_service;
struct ldapsrv_partition *rootDSE_part; struct ldapsrv_partition *rootDSE_part;
struct ldapsrv_partition *part; struct ldapsrv_partition *part;
NTSTATUS status; NTSTATUS status;
DEBUG(10,("ldapsrv_init\n")); ldap_service = talloc_zero(task, struct ldapsrv_service);
if (ldap_service == NULL) goto failed;
ldap_service = talloc_zero(event_context, struct ldapsrv_service); ldap_service->tls_params = tls_initialise(ldap_service);
NT_STATUS_HAVE_NO_MEMORY(ldap_service); if (ldap_service->tls_params == NULL) goto failed;
rootDSE_part = talloc(ldap_service, struct ldapsrv_partition); rootDSE_part = talloc(ldap_service, struct ldapsrv_partition);
NT_STATUS_HAVE_NO_MEMORY(rootDSE_part); if (rootDSE_part == NULL) goto failed;
rootDSE_part->base_dn = ""; /* RootDSE */ rootDSE_part->base_dn = ""; /* RootDSE */
rootDSE_part->ops = ldapsrv_get_rootdse_partition_ops(); rootDSE_part->ops = ldapsrv_get_rootdse_partition_ops();
@@ -518,7 +535,7 @@ static NTSTATUS ldapsrv_init(struct event_context *event_context, const struct m
DLIST_ADD_END(ldap_service->partitions, rootDSE_part, struct ldapsrv_partition *); DLIST_ADD_END(ldap_service->partitions, rootDSE_part, struct ldapsrv_partition *);
part = talloc(ldap_service, struct ldapsrv_partition); part = talloc(ldap_service, struct ldapsrv_partition);
NT_STATUS_HAVE_NO_MEMORY(part); if (part == NULL) goto failed;
part->base_dn = "*"; /* default partition */ part->base_dn = "*"; /* default partition */
if (lp_parm_bool(-1, "ldapsrv", "hacked", False)) { if (lp_parm_bool(-1, "ldapsrv", "hacked", False)) {
@@ -540,15 +557,28 @@ static NTSTATUS ldapsrv_init(struct event_context *event_context, const struct m
*/ */
for(i = 0; i < num_interfaces; i++) { for(i = 0; i < num_interfaces; i++) {
const char *address = iface_n_ip(i); const char *address = iface_n_ip(i);
status = add_socket(event_context, model_ops, address, ldap_service); status = add_socket(task->event_ctx, task->model_ops, address, ldap_service);
NT_STATUS_NOT_OK_RETURN(status); if (!NT_STATUS_IS_OK(status)) goto failed;
} }
} else { } else {
status = add_socket(event_context, model_ops, lp_socket_address(), ldap_service); status = add_socket(task->event_ctx, task->model_ops, lp_socket_address(), ldap_service);
NT_STATUS_NOT_OK_RETURN(status); if (!NT_STATUS_IS_OK(status)) goto failed;
} }
return NT_STATUS_OK; return;
failed:
task_terminate(task, "Failed to startup ldap server task");
}
/*
called on startup of the web server service It's job is to start
listening on all configured sockets
*/
static NTSTATUS ldapsrv_init(struct event_context *event_context,
const struct model_ops *model_ops)
{
return task_server_startup(event_context, model_ops, ldapsrv_task_init);
} }

View File

@@ -73,6 +73,8 @@ struct ldapsrv_connection {
struct ldapsrv_call *calls; struct ldapsrv_call *calls;
struct ldapsrv_service *service; struct ldapsrv_service *service;
struct tls_context *tls;
}; };
struct ldapsrv_partition; struct ldapsrv_partition;
@@ -103,4 +105,5 @@ struct ldapsrv_service {
struct ldapsrv_partition *rootDSE; struct ldapsrv_partition *rootDSE;
struct ldapsrv_partition *default_partition; struct ldapsrv_partition *default_partition;
struct ldapsrv_partition *partitions; struct ldapsrv_partition *partitions;
struct tls_params *tls_params;
}; };

View File

@@ -387,26 +387,6 @@ BOOL asn1_start_tag(struct asn1_data *data, uint8_t tag)
} }
/* Get the length to be expected in buf */
BOOL asn1_object_length(uint8_t *buf, size_t buf_length,
uint8_t tag, size_t *result)
{
struct asn1_data data;
/* Fake the asn1_load to avoid the memdup, this is just to be able to
* re-use the length-reading in asn1_start_tag */
ZERO_STRUCT(data);
data.data = buf;
data.length = buf_length;
if (!asn1_start_tag(&data, tag))
return False;
*result = asn1_tag_remaining(&data)+data.ofs;
/* We can't use asn1_end_tag here, as we did not consume the complete
* tag, so asn1_end_tag would flag an error and not free nesting */
talloc_free(data.nesting);
return True;
}
/* stop reading a tag */ /* stop reading a tag */
BOOL asn1_end_tag(struct asn1_data *data) BOOL asn1_end_tag(struct asn1_data *data)
{ {