#include "../lib/util/tevent_unix.h"
static TLDAPRC tldap_simple_recv(struct tevent_req *req);
+static bool tldap_msg_set_pending(struct tevent_req *req);
#define TEVENT_TLDAP_RC_MAGIC (0x87bcd26e)
struct tldap_context {
int ld_version;
- struct tstream_context *conn;
- bool server_down;
+ struct tstream_context *plain;
+ struct tstream_context *gensec;
+ struct tstream_context *active;
int msgid;
struct tevent_queue *outgoing;
struct tevent_req **pending;
+ struct tevent_req *read_req;
/* For the sync wrappers we need something like get_last_error... */
struct tldap_message *last_msg;
ld->log_private = log_private;
}
+static void tldap_debug(
+ struct tldap_context *ld,
+ enum tldap_debug_level level,
+ const char *fmt, ...) PRINTF_ATTRIBUTE(3,4);
+
static void tldap_debug(struct tldap_context *ld,
enum tldap_debug_level level,
const char *fmt, ...)
int result;
result = ld->msgid++;
- if (ld->msgid == 2147483647) {
+ if (ld->msgid == INT_MAX) {
ld->msgid = 1;
}
return result;
if (ctx == NULL) {
return NULL;
}
- ret = tstream_bsd_existing_socket(ctx, fd, &ctx->conn);
+ ret = tstream_bsd_existing_socket(ctx, fd, &ctx->plain);
if (ret == -1) {
TALLOC_FREE(ctx);
return NULL;
}
+ ctx->active = ctx->plain;
ctx->msgid = 1;
ctx->ld_version = 3;
ctx->outgoing = tevent_queue_create(ctx, "tldap_outgoing");
bool tldap_connection_ok(struct tldap_context *ld)
{
+ int ret;
+
if (ld == NULL) {
return false;
}
- return !ld->server_down;
+
+ if (ld->active == NULL) {
+ return false;
+ }
+
+ ret = tstream_pending_bytes(ld->active);
+ if (ret == -1) {
+ return false;
+ }
+
+ return true;
}
static size_t tldap_pending_reqs(struct tldap_context *ld)
return talloc_array_length(ld->pending);
}
-struct tstream_context *tldap_get_tstream(struct tldap_context *ld)
+struct tstream_context *tldap_get_plain_tstream(struct tldap_context *ld)
{
- return ld->conn;
+ return ld->plain;
}
-void tldap_set_tstream(struct tldap_context *ld,
- struct tstream_context *stream)
+bool tldap_has_gensec_tstream(struct tldap_context *ld)
{
- ld->conn = stream;
+ return ld->gensec != NULL && ld->active == ld->gensec;
+}
+
+void tldap_set_gensec_tstream(struct tldap_context *ld,
+ struct tstream_context **stream)
+{
+ TALLOC_FREE(ld->gensec);
+ if (stream != NULL) {
+ ld->gensec = talloc_move(ld, stream);
+ }
+ if (ld->gensec != NULL) {
+ ld->active = ld->gensec;
+ } else {
+ ld->active = ld->plain;
+ }
}
static struct tldap_ctx_attribute *tldap_context_findattr(
struct read_ldap_state {
uint8_t *buf;
- bool done;
};
static ssize_t read_ldap_more(uint8_t *buf, size_t buflen, void *private_data);
if (req == NULL) {
return NULL;
}
- state->done = false;
- subreq = tstream_read_packet_send(state, ev, conn, 2, read_ldap_more,
+ subreq = tstream_read_packet_send(state, ev, conn, 7, read_ldap_more,
state);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
static ssize_t read_ldap_more(uint8_t *buf, size_t buflen, void *private_data)
{
- struct read_ldap_state *state = talloc_get_type_abort(
- private_data, struct read_ldap_state);
- size_t len;
- int i, lensize;
-
- if (state->done) {
- /* We've been here, we're done */
- return 0;
- }
+ const DATA_BLOB blob = data_blob_const(buf, buflen);
+ size_t pdu_len = 0;
+ int ret;
- /*
- * From ldap.h: LDAP_TAG_MESSAGE is 0x30
- */
- if (buf[0] != 0x30) {
+ if (buflen < 7) {
+ /*
+ * We need at least 6 bytes to workout the length
+ * of the pdu.
+ *
+ * And we have asked for 7 because the that's
+ * the size of the smallest possible LDAP pdu.
+ */
return -1;
}
- len = buf[1];
- if ((len & 0x80) == 0) {
- state->done = true;
- return len;
- }
-
- lensize = (len & 0x7f);
- len = 0;
-
- if (buflen == 2) {
- /* Please get us the full length */
- return lensize;
- }
- if (buflen > 2 + lensize) {
- state->done = true;
+ ret = asn1_peek_full_tag(blob, ASN1_SEQUENCE(0), &pdu_len);
+ if (ret == 0) {
return 0;
}
- if (buflen != 2 + lensize) {
- return -1;
+ if (ret == EAGAIN) {
+ return pdu_len - buflen;
}
- for (i=0; i<lensize; i++) {
- len = (len << 8) | buf[2+i];
- }
- return len;
+ return -1;
}
static void read_ldap_done(struct tevent_req *subreq)
return asn1_pop_tag(data); /* ASN1_CONTEXT(0) */
}
+#define tldap_context_disconnect(ld, status) \
+ _tldap_context_disconnect(ld, status, __location__)
+
+static void _tldap_context_disconnect(struct tldap_context *ld,
+ TLDAPRC status,
+ const char *location)
+{
+ if (ld->active == NULL) {
+ /*
+ * We don't need to tldap_debug() on
+ * a potential 2nd run.
+ *
+ * The rest of the function would just
+ * be a noop for the 2nd run anyway.
+ */
+ return;
+ }
+
+ tldap_debug(ld, TLDAP_DEBUG_WARNING,
+ "tldap_context_disconnect: %s at %s\n",
+ tldap_rc2string(status),
+ location);
+ tevent_queue_stop(ld->outgoing);
+ TALLOC_FREE(ld->read_req);
+ ld->active = NULL;
+ TALLOC_FREE(ld->gensec);
+ TALLOC_FREE(ld->plain);
+
+ while (talloc_array_length(ld->pending) > 0) {
+ struct tevent_req *req = NULL;
+ struct tldap_msg_state *state = NULL;
+
+ req = ld->pending[0];
+ state = tevent_req_data(req, struct tldap_msg_state);
+ tevent_req_defer_callback(req, state->ev);
+ tevent_req_ldap_error(req, status);
+ }
+}
+
static void tldap_msg_sent(struct tevent_req *subreq);
static void tldap_msg_received(struct tevent_req *subreq);
struct tevent_req *req, *subreq;
struct tldap_msg_state *state;
DATA_BLOB blob;
+ bool ok;
tldap_debug(ld, TLDAP_DEBUG_TRACE, "tldap_msg_send: sending msg %d\n",
id);
state->ev = ev;
state->id = id;
- if (state->ld->server_down) {
+ ok = tldap_connection_ok(ld);
+ if (!ok) {
tevent_req_ldap_error(req, TLDAP_SERVER_DOWN);
return tevent_req_post(req, ev);
}
return tevent_req_post(req, ev);
}
+ if (!tldap_msg_set_pending(req)) {
+ tevent_req_oom(req);
+ return tevent_req_post(req, ev);;
+ }
+
state->iov.iov_base = (void *)blob.data;
state->iov.iov_len = blob.length;
- subreq = tstream_writev_queue_send(state, ev, ld->conn, ld->outgoing,
+ subreq = tstream_writev_queue_send(state, ev, ld->active, ld->outgoing,
&state->iov, 1);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
tevent_req_set_cleanup_fn(req, NULL);
- if (num_pending == 1) {
- TALLOC_FREE(ld->pending);
- return;
- }
-
for (i=0; i<num_pending; i++) {
if (req == ld->pending[i]) {
break;
return;
}
+ if (num_pending == 1) {
+ TALLOC_FREE(ld->pending);
+ return;
+ }
+
/*
* Remove ourselves from the cli->pending array
*/
static void tldap_msg_cleanup(struct tevent_req *req,
enum tevent_req_state req_state)
{
- switch (req_state) {
- case TEVENT_REQ_USER_ERROR:
- case TEVENT_REQ_RECEIVED:
- tldap_msg_unset_pending(req);
- return;
- default:
- return;
- }
+ tldap_msg_unset_pending(req);
}
static bool tldap_msg_set_pending(struct tevent_req *req)
struct tldap_context *ld;
struct tevent_req **pending;
int num_pending;
- struct tevent_req *subreq;
ld = state->ld;
num_pending = tldap_pending_reqs(ld);
ld->pending = pending;
tevent_req_set_cleanup_fn(req, tldap_msg_cleanup);
- if (num_pending > 0) {
+ if (ld->read_req != NULL) {
return true;
}
* We're the first one, add the read_ldap request that waits for the
* answer from the server
*/
- subreq = read_ldap_send(ld->pending, state->ev, ld->conn);
- if (subreq == NULL) {
+ ld->read_req = read_ldap_send(ld->pending, state->ev, ld->active);
+ if (ld->read_req == NULL) {
tldap_msg_unset_pending(req);
return false;
}
- tevent_req_set_callback(subreq, tldap_msg_received, ld);
+ tevent_req_set_callback(ld->read_req, tldap_msg_received, ld);
return true;
}
nwritten = tstream_writev_queue_recv(subreq, &err);
TALLOC_FREE(subreq);
if (nwritten == -1) {
- state->ld->server_down = true;
- tevent_req_ldap_error(req, TLDAP_SERVER_DOWN);
- return;
- }
-
- if (!tldap_msg_set_pending(req)) {
- tevent_req_oom(req);
+ tldap_context_disconnect(state->ld, TLDAP_SERVER_DOWN);
return;
}
}
uint8_t *inbuf;
ssize_t received;
size_t num_pending;
- int i, err;
- TLDAPRC status;
+ size_t i;
+ int err;
+ TLDAPRC status = TLDAP_PROTOCOL_ERROR;
int id;
uint8_t type;
bool ok;
received = read_ldap_recv(subreq, talloc_tos(), &inbuf, &err);
TALLOC_FREE(subreq);
+ ld->read_req = NULL;
if (received == -1) {
- ld->server_down = true;
status = TLDAP_SERVER_DOWN;
goto fail;
}
- data = asn1_init(talloc_tos());
+ data = asn1_init(talloc_tos(), ASN1_MAX_TREE_DEPTH);
if (data == NULL) {
+ /*
+ * We have to disconnect all, we can't tell which of
+ * the requests this reply is for.
+ */
status = TLDAP_NO_MEMORY;
goto fail;
}
tldap_debug(ld, TLDAP_DEBUG_TRACE, "tldap_msg_received: got msg %d "
"type %d\n", id, (int)type);
+ if (id == 0) {
+ tldap_debug(
+ ld,
+ TLDAP_DEBUG_WARNING,
+ "tldap_msg_received: got msgid 0 of "
+ "type %"PRIu8", disconnecting\n",
+ type);
+ tldap_context_disconnect(ld, TLDAP_SERVER_DOWN);
+ return;
+ }
+
num_pending = talloc_array_length(ld->pending);
for (i=0; i<num_pending; i++) {
tldap_msg_unset_pending(req);
num_pending = talloc_array_length(ld->pending);
+ tevent_req_defer_callback(req, state->ev);
tevent_req_done(req);
done:
if (num_pending == 0) {
return;
}
- if (talloc_array_length(ld->pending) > num_pending) {
- /*
- * The callback functions called from tevent_req_done() above
- * have put something on the pending queue. We don't have to
- * trigger the read_ldap_send(), tldap_msg_set_pending() has
- * done it for us already.
- */
- return;
- }
state = tevent_req_data(ld->pending[0], struct tldap_msg_state);
- subreq = read_ldap_send(ld->pending, state->ev, ld->conn);
- if (subreq == NULL) {
+ ld->read_req = read_ldap_send(ld->pending, state->ev, ld->active);
+ if (ld->read_req == NULL) {
status = TLDAP_NO_MEMORY;
goto fail;
}
- tevent_req_set_callback(subreq, tldap_msg_received, ld);
+ tevent_req_set_callback(ld->read_req, tldap_msg_received, ld);
return;
fail:
- while (talloc_array_length(ld->pending) > 0) {
- req = ld->pending[0];
- state = tevent_req_data(req, struct tldap_msg_state);
- tevent_req_defer_callback(req, state->ev);
- tevent_req_ldap_error(req, status);
- }
+ tldap_context_disconnect(ld, status);
}
static TLDAPRC tldap_msg_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
if (req == NULL) {
return NULL;
}
- state->out = asn1_init(state);
+ state->out = asn1_init(state, ASN1_MAX_TREE_DEPTH);
if (state->out == NULL) {
goto err;
}
while (*s) {
s = strchr(s, ')');
if (s && (*(s - 1) == '\\')) {
+ s++;
continue;
}
break;
static bool tldap_unescape_inplace(char *value, size_t *val_len)
{
- int c, i, p;
+ int c;
+ size_t i, p;
for (i = 0,p = 0; i < *val_len; i++) {
return asn1_pop_tag(data);
}
-/* NOTE: although openldap libraries allow for spaces in some places, mosly
- * around parenthesis, we do not allow any spaces (except in values of
- * course) as I couldn't fine any place in RFC 4512 or RFC 4515 where
- * leading or trailing spaces where allowed.
+/* NOTE: although openldap libraries allow for spaces in some places, mostly
+ * around parentheses, we do not allow any spaces (except in values of
+ * course) as I couldn't find any place in RFC 4512 or RFC 4515 where
+ * leading or trailing spaces were allowed.
*/
static bool tldap_push_filter(struct tldap_context *ld,
struct asn1_data *data,
rc = tldap_search_recv(subreq, state, &msg);
/* No TALLOC_FREE(subreq), this is multi-step */
if (tevent_req_ldap_error(req, rc)) {
- TALLOC_FREE(subreq);
return;
}