summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/configure.in1
-rw-r--r--source4/include/asn_1.h3
-rw-r--r--source4/include/includes.h1
-rw-r--r--source4/libcli/ldap/config.m41
-rw-r--r--source4/libcli/ldap/config.mk6
-rw-r--r--source4/libcli/ldap/ldap.c1989
-rw-r--r--source4/libcli/ldap/ldap.h246
-rw-r--r--source4/libcli/util/asn1.c160
8 files changed, 2379 insertions, 28 deletions
diff --git a/source4/configure.in b/source4/configure.in
index a86a99f49d..49328b91df 100644
--- a/source4/configure.in
+++ b/source4/configure.in
@@ -20,6 +20,7 @@ SMB_INCLUDE_M4(lib/cmdline/config.m4)
SMB_INCLUDE_M4(param/config.m4)
SMB_INCLUDE_M4(libcli/auth/config.m4)
SMB_INCLUDE_M4(libcli/auth/gensec.m4)
+SMB_INCLUDE_M4(libcli/ldap/config.m4)
SMB_INCLUDE_M4(libcli/config.m4)
SMB_INCLUDE_M4(librpc/config.m4)
SMB_INCLUDE_M4(libcli/libsmb.m4)
diff --git a/source4/include/asn_1.h b/source4/include/asn_1.h
index 78cc340f44..6bd79e3fbe 100644
--- a/source4/include/asn_1.h
+++ b/source4/include/asn_1.h
@@ -35,10 +35,11 @@ typedef struct {
BOOL has_error;
} ASN1_DATA;
-
#define ASN1_APPLICATION(x) ((x)+0x60)
+#define ASN1_APPLICATION_SIMPLE(x) ((x)+0x40)
#define ASN1_SEQUENCE(x) ((x)+0x30)
#define ASN1_CONTEXT(x) ((x)+0xa0)
+#define ASN1_CONTEXT_SIMPLE(x) ((x)+0x80)
#define ASN1_GENERAL_STRING 0x1b
#define ASN1_OCTET_STRING 0x4
#define ASN1_OID 0x6
diff --git a/source4/include/includes.h b/source4/include/includes.h
index 1e6cc0e5a4..3821269f2b 100644
--- a/source4/include/includes.h
+++ b/source4/include/includes.h
@@ -641,6 +641,7 @@ extern int errno;
#include "rewrite.h"
#include "smb.h"
#include "ads.h"
+#include "libcli/ldap/ldap.h"
#include "nameserv.h"
#include "secrets.h"
diff --git a/source4/libcli/ldap/config.m4 b/source4/libcli/ldap/config.m4
new file mode 100644
index 0000000000..01f78279bf
--- /dev/null
+++ b/source4/libcli/ldap/config.m4
@@ -0,0 +1 @@
+SMB_SUBSYSTEM_MK(LIBCLI_LDAP,libcli/ldap/config.mk)
diff --git a/source4/libcli/ldap/config.mk b/source4/libcli/ldap/config.mk
new file mode 100644
index 0000000000..397cfe0b72
--- /dev/null
+++ b/source4/libcli/ldap/config.mk
@@ -0,0 +1,6 @@
+#################################
+# Start SUBSYSTEM LIBCLI_LDAP
+[SUBSYSTEM::LIBCLI_LDAP]
+ADD_OBJ_FILES = libcli/ldap/ldap.o
+# End SUBSYSTEM LIBCLI_LDAP
+#################################
diff --git a/source4/libcli/ldap/ldap.c b/source4/libcli/ldap/ldap.c
new file mode 100644
index 0000000000..38fff7e357
--- /dev/null
+++ b/source4/libcli/ldap/ldap.c
@@ -0,0 +1,1989 @@
+/*
+ Unix SMB/CIFS mplementation.
+ LDAP protocol helper functions for SAMBA
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Volker Lendecke 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 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 "smb_ldap.h"
+
+/****************************************************************************
+ *
+ * LDAP filter parser -- main routine is ldb_parse_filter
+ *
+ * Shamelessly stolen and adapted from Samba 4.
+ *
+ ***************************************************************************/
+
+/* Hmm. A blob might be more appropriate here :-) */
+
+struct ldb_val {
+ unsigned int length;
+ void *data;
+};
+
+enum ldb_parse_op {LDB_OP_SIMPLE, LDB_OP_AND, LDB_OP_OR, LDB_OP_NOT};
+
+struct ldb_parse_tree {
+ enum ldb_parse_op operation;
+ union {
+ struct {
+ char *attr;
+ struct ldb_val value;
+ } simple;
+ struct {
+ unsigned int num_elements;
+ struct ldb_parse_tree **elements;
+ } list;
+ struct {
+ struct ldb_parse_tree *child;
+ } not;
+ } u;
+};
+
+#define LDB_ALL_SEP "()&|=!"
+
+/*
+ return next token element. Caller frees
+*/
+static char *ldb_parse_lex(TALLOC_CTX *mem_ctx, const char **s,
+ const char *sep)
+{
+ const char *p = *s;
+ char *ret;
+
+ while (isspace(*p)) {
+ p++;
+ }
+ *s = p;
+
+ if (*p == 0) {
+ return NULL;
+ }
+
+ if (strchr(sep, *p)) {
+ (*s) = p+1;
+ ret = talloc_strndup(mem_ctx, p, 1);
+ if (!ret) {
+ errno = ENOMEM;
+ }
+ return ret;
+ }
+
+ while (*p && (isalnum(*p) || !strchr(sep, *p))) {
+ p++;
+ }
+
+ if (p == *s) {
+ return NULL;
+ }
+
+ ret = talloc_strndup(mem_ctx, *s, p - *s);
+ if (!ret) {
+ errno = ENOMEM;
+ }
+
+ *s = p;
+
+ return ret;
+}
+
+
+/*
+ find a matching close brace in a string
+*/
+static const char *match_brace(const char *s)
+{
+ unsigned int count = 0;
+ while (*s && (count != 0 || *s != ')')) {
+ if (*s == '(') {
+ count++;
+ }
+ if (*s == ')') {
+ count--;
+ }
+ s++;
+ }
+ if (! *s) {
+ return NULL;
+ }
+ return s;
+}
+
+static struct ldb_parse_tree *ldb_parse_filter(TALLOC_CTX *mem_ctx,
+ const char **s);
+
+/*
+ <simple> ::= <attributetype> <filtertype> <attributevalue>
+*/
+static struct ldb_parse_tree *ldb_parse_simple(TALLOC_CTX *mem_ctx,
+ const char *s)
+{
+ char *eq, *val, *l;
+ struct ldb_parse_tree *ret;
+
+ l = ldb_parse_lex(mem_ctx, &s, LDB_ALL_SEP);
+ if (!l) {
+ return NULL;
+ }
+
+ if (strchr("()&|=", *l))
+ return NULL;
+
+ eq = ldb_parse_lex(mem_ctx, &s, LDB_ALL_SEP);
+ if (!eq || strcmp(eq, "=") != 0)
+ return NULL;
+
+ val = ldb_parse_lex(mem_ctx, &s, ")");
+ if (val && strchr("()&|", *val))
+ return NULL;
+
+ ret = talloc(mem_ctx, sizeof(*ret));
+ if (!ret) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ret->operation = LDB_OP_SIMPLE;
+ ret->u.simple.attr = l;
+ ret->u.simple.value.data = val;
+ ret->u.simple.value.length = val?strlen(val):0;
+
+ return ret;
+}
+
+
+/*
+ parse a filterlist
+ <and> ::= '&' <filterlist>
+ <or> ::= '|' <filterlist>
+ <filterlist> ::= <filter> | <filter> <filterlist>
+*/
+static struct ldb_parse_tree *ldb_parse_filterlist(TALLOC_CTX *mem_ctx,
+ enum ldb_parse_op op,
+ const char *s)
+{
+ struct ldb_parse_tree *ret, *next;
+
+ ret = talloc(mem_ctx, sizeof(*ret));
+
+ if (!ret) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ret->operation = op;
+ ret->u.list.num_elements = 1;
+ ret->u.list.elements = talloc(mem_ctx, sizeof(*ret->u.list.elements));
+ if (!ret->u.list.elements) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ret->u.list.elements[0] = ldb_parse_filter(mem_ctx, &s);
+ if (!ret->u.list.elements[0]) {
+ return NULL;
+ }
+
+ while (isspace(*s)) s++;
+
+ while (*s && (next = ldb_parse_filter(mem_ctx, &s))) {
+ struct ldb_parse_tree **e;
+ e = talloc_realloc(mem_ctx, ret->u.list.elements,
+ sizeof(struct ldb_parse_tree) *
+ (ret->u.list.num_elements+1));
+ if (!e) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ ret->u.list.elements = e;
+ ret->u.list.elements[ret->u.list.num_elements] = next;
+ ret->u.list.num_elements++;
+ while (isspace(*s)) s++;
+ }
+
+ return ret;
+}
+
+
+/*
+ <not> ::= '!' <filter>
+*/
+static struct ldb_parse_tree *ldb_parse_not(TALLOC_CTX *mem_ctx, const char *s)
+{
+ struct ldb_parse_tree *ret;
+
+ ret = talloc(mem_ctx, sizeof(*ret));
+ if (!ret) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ret->operation = LDB_OP_NOT;
+ ret->u.not.child = ldb_parse_filter(mem_ctx, &s);
+ if (!ret->u.not.child)
+ return NULL;
+
+ return ret;
+}
+
+/*
+ parse a filtercomp
+ <filtercomp> ::= <and> | <or> | <not> | <simple>
+*/
+static struct ldb_parse_tree *ldb_parse_filtercomp(TALLOC_CTX *mem_ctx,
+ const char *s)
+{
+ while (isspace(*s)) s++;
+
+ switch (*s) {
+ case '&':
+ return ldb_parse_filterlist(mem_ctx, LDB_OP_AND, s+1);
+
+ case '|':
+ return ldb_parse_filterlist(mem_ctx, LDB_OP_OR, s+1);
+
+ case '!':
+ return ldb_parse_not(mem_ctx, s+1);
+
+ case '(':
+ case ')':
+ return NULL;
+ }
+
+ return ldb_parse_simple(mem_ctx, s);
+}
+
+
+/*
+ <filter> ::= '(' <filtercomp> ')'
+*/
+static struct ldb_parse_tree *ldb_parse_filter(TALLOC_CTX *mem_ctx,
+ const char **s)
+{
+ char *l, *s2;
+ const char *p, *p2;
+ struct ldb_parse_tree *ret;
+
+ l = ldb_parse_lex(mem_ctx, s, LDB_ALL_SEP);
+ if (!l) {
+ return NULL;
+ }
+
+ if (strcmp(l, "(") != 0) {
+ return NULL;
+ }
+
+ p = match_brace(*s);
+ if (!p) {
+ return NULL;
+ }
+ p2 = p + 1;
+
+ s2 = talloc_strndup(mem_ctx, *s, p - *s);
+ if (!s2) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ret = ldb_parse_filtercomp(mem_ctx, s2);
+
+ *s = p2;
+
+ return ret;
+}
+
+/*
+ main parser entry point. Takes a search string and returns a parse tree
+
+ expression ::= <simple> | <filter>
+*/
+static struct ldb_parse_tree *ldb_parse_tree(TALLOC_CTX *mem_ctx, const char *s)
+{
+ while (isspace(*s)) s++;
+
+ if (*s == '(') {
+ return ldb_parse_filter(mem_ctx, &s);
+ }
+
+ return ldb_parse_simple(mem_ctx, s);
+}
+
+static BOOL ldap_push_filter(ASN1_DATA *data, struct ldb_parse_tree *tree)
+{
+ switch (tree->operation) {
+ case LDB_OP_SIMPLE: {
+ if ((tree->u.simple.value.length == 1) &&
+ (((char *)(tree->u.simple.value.data))[0] == '*')) {
+ /* Just a presence test */
+ asn1_push_tag(data, 0x87);
+ asn1_write(data, tree->u.simple.attr,
+ strlen(tree->u.simple.attr));
+ asn1_pop_tag(data);
+ return !data->has_error;
+ }
+
+ /* Equality is all we currently do... */
+ asn1_push_tag(data, 0xa3);
+ asn1_write_OctetString(data, tree->u.simple.attr,
+ strlen(tree->u.simple.attr));
+ asn1_write_OctetString(data, tree->u.simple.value.data,
+ tree->u.simple.value.length);
+ asn1_pop_tag(data);
+ break;
+ }
+
+ case LDB_OP_AND: {
+ int i;
+
+ asn1_push_tag(data, 0xa0);
+ for (i=0; i<tree->u.list.num_elements; i++) {
+ ldap_push_filter(data, tree->u.list.elements[i]);
+ }
+ asn1_pop_tag(data);
+ break;
+ }
+
+ case LDB_OP_OR: {
+ int i;
+
+ asn1_push_tag(data, 0xa1);
+ for (i=0; i<tree->u.list.num_elements; i++) {
+ ldap_push_filter(data, tree->u.list.elements[i]);
+ }
+ asn1_pop_tag(data);
+ break;
+ }
+ default:
+ return False;
+ }
+ return !data->has_error;
+}
+
+/****************************************************************************
+ *
+ * LDIF parser
+ *
+ * Shamelessly stolen and adapted from Samba 4.
+ *
+ ***************************************************************************/
+
+/*
+ pull a ldif chunk, which is defined as a piece of data ending in \n\n or EOF
+ this routine removes any RFC2849 continuations and comments
+
+ caller frees
+*/
+static char *next_chunk(TALLOC_CTX *mem_ctx,
+ int (*fgetc_fn)(void *), void *private_data)
+{
+ size_t alloc_size=0, chunk_size = 0;
+ char *chunk = NULL;
+ int c;
+ int in_comment = 0;
+
+ while ((c = fgetc_fn(private_data)) != EOF) {
+ if (chunk_size+1 >= alloc_size) {
+ char *c2;
+ alloc_size += 1024;
+ c2 = talloc_realloc(mem_ctx, chunk, alloc_size);
+ if (!c2) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ chunk = c2;
+ }
+
+ if (in_comment) {
+ if (c == '\n') {
+ in_comment = 0;
+ }
+ continue;
+ }
+
+ /* handle continuation lines - see RFC2849 */
+ if (c == ' ' && chunk_size > 1 &&
+ chunk[chunk_size-1] == '\n') {
+ chunk_size--;
+ continue;
+ }
+
+ /* chunks are terminated by a double line-feed */
+ if (c == '\n' && chunk_size > 0 &&
+ chunk[chunk_size-1] == '\n') {
+ chunk[chunk_size-1] = 0;
+ return chunk;
+ }
+
+ if (c == '#' &&
+ (chunk_size == 0 || chunk[chunk_size-1] == '\n')) {
+ in_comment = 1;
+ continue;
+ }
+
+ /* ignore leading blank lines */
+ if (chunk_size == 0 && c == '\n') {
+ continue;
+ }
+
+ chunk[chunk_size++] = c;
+ }
+
+ if (chunk) {
+ chunk[chunk_size] = 0;
+ }
+
+ return chunk;
+}
+
+/* simple ldif attribute parser */
+static int next_attr(char **s, const char **attr, struct ldb_val *value)
+{
+ char *p;
+ int base64_encoded = 0;
+
+ if (strncmp(*s, "-\n", 2) == 0) {
+ value->length = 0;
+ *attr = "-";
+ *s += 2;
+ return 0;
+ }
+
+ p = strchr(*s, ':');
+ if (!p) {
+ return -1;
+ }
+
+ *p++ = 0;
+
+ if (*p == ':') {
+ base64_encoded = 1;
+ p++;
+ }
+
+ *attr = *s;
+
+ while (isspace(*p)) {
+ p++;
+ }
+
+ value->data = p;
+
+ p = strchr(p, '\n');
+
+ if (!p) {
+ value->length = strlen((char *)value->data);
+ *s = ((char *)value->data) + value->length;
+ } else {
+ value->length = p - (char *)value->data;
+ *s = p+1;
+ *p = 0;
+ }
+
+ if (base64_encoded) {
+ DATA_BLOB blob = base64_decode_data_blob(value->data);
+ memcpy(value->data, blob.data, blob.length);
+ value->length = blob.length;
+ ((char *)value->data)[value->length] = '\0';
+ }
+
+ return 0;
+}
+
+static BOOL add_value_to_attrib(TALLOC_CTX *mem_ctx, struct ldb_val *value,
+ struct ldap_attribute *attrib)
+{
+ attrib->values = talloc_realloc(mem_ctx, attrib->values,
+ sizeof(*attrib->values) *
+ (attrib->num_values+1));
+ if (attrib->values == NULL)
+ return False;
+
+ attrib->values[attrib->num_values] =
+ data_blob_talloc(mem_ctx, value->data, value->length);
+ attrib->num_values += 1;
+ return True;
+}
+
+static BOOL fill_add_attributes(struct ldap_message *msg, char **chunk)
+{
+ struct ldap_AddRequest *r = &msg->r.AddRequest;
+ const char *attr_name;
+ struct ldb_val value;
+
+ r->num_attributes = 0;
+ r->attributes = NULL;
+
+ while (next_attr(chunk, &attr_name, &value) == 0) {
+ int i;
+ struct ldap_attribute *attrib = NULL;
+
+ for (i=0; i<r->num_attributes; i++) {
+ if (strequal(r->attributes[i].name, attr_name)) {
+ attrib = &r->attributes[i];
+ break;
+ }
+ }
+
+ if (attrib == NULL) {
+ r->attributes = talloc_realloc(msg->mem_ctx,
+ r->attributes,
+ sizeof(*r->attributes) *
+ (r->num_attributes+1));
+ if (r->attributes == NULL)
+ return False;
+
+ attrib = &(r->attributes[r->num_attributes]);
+ r->num_attributes += 1;
+ ZERO_STRUCTP(attrib);
+ attrib->name = talloc_strdup(msg->mem_ctx,
+ attr_name);
+ }
+
+ if (!add_value_to_attrib(msg->mem_ctx, &value, attrib))
+ return False;
+ }
+ return True;
+}
+
+static BOOL add_mod_to_array_talloc(TALLOC_CTX *mem_ctx,
+ struct ldap_mod *mod,
+ struct ldap_mod **mods,
+ int *num_mods)
+{
+ *mods = talloc_realloc(mem_ctx, *mods,
+ sizeof(**mods) * ((*num_mods)+1));
+
+ if (*mods == NULL)
+ return False;
+
+ (*mods)[*num_mods] = *mod;
+ *num_mods += 1;
+ return True;
+}
+
+static BOOL fill_mods(struct ldap_message *msg, char **chunk)
+{
+ struct ldap_ModifyRequest *r = &msg->r.ModifyRequest;
+ const char *attr_name;
+ struct ldb_val value;
+
+ r->num_mods = 0;
+ r->mods = NULL;
+
+ while (next_attr(chunk, &attr_name, &value) == 0) {
+
+ struct ldap_mod mod;
+ mod.type = LDAP_MODIFY_NONE;
+
+ mod.attrib.name = talloc_strdup(msg->mem_ctx, value.data);
+
+ if (strequal(attr_name, "add"))
+ mod.type = LDAP_MOD_ADD;
+
+ if (strequal(attr_name, "delete"))
+ mod.type = LDAP_MOD_DELETE;
+
+ if (strequal(attr_name, "replace"))
+ mod.type = LDAP_MOD_REPLACE;
+
+ if (mod.type == LDAP_MODIFY_NONE) {
+ DEBUG(2, ("ldif modification type %s unsupported\n",
+ attr_name));
+ return False;
+ }
+
+ mod.attrib.num_values = 0;
+ mod.attrib.values = NULL;
+
+ while (next_attr(chunk, &attr_name, &value) == 0) {
+ if (strequal(attr_name, "-"))
+ break;
+ if (!strequal(attr_name, mod.attrib.name)) {
+ DEBUG(3, ("attrib name %s does not "
+ "match %s\n", attr_name,
+ mod.attrib.name));
+ return False;
+ }
+ if (!add_value_to_attrib(msg->mem_ctx, &value,
+ &mod.attrib)) {
+ DEBUG(3, ("Could not add value\n"));
+ return False;
+ }
+ }
+
+ if (!add_mod_to_array_talloc(msg->mem_ctx, &mod, &r->mods,
+ &r->num_mods))
+ return False;
+ }
+
+ return True;
+}
+
+/*
+ read from a LDIF source, creating a ldap_message
+*/
+static struct ldap_message *ldif_read(int (*fgetc_fn)(void *),
+ void *private_data)
+{
+ struct ldap_message *msg;
+ const char *attr=NULL;
+ const char *dn;
+ char *chunk=NULL, *s;
+ struct ldb_val value;
+
+ value.data = NULL;
+
+ msg = new_ldap_message();
+ if (msg == NULL)
+ return NULL;
+
+ chunk = next_chunk(msg->mem_ctx, fgetc_fn, private_data);
+ if (!chunk) {
+ goto failed;
+ }
+
+ s = chunk;
+
+ if (next_attr(&s, &attr, &value) != 0) {
+ goto failed;
+ }
+
+ /* first line must be a dn */
+ if (!strequal(attr, "dn")) {
+ DEBUG(5, ("Error: First line of ldif must be a dn not '%s'\n",
+ attr));
+ goto failed;
+ }
+
+ dn = talloc_strdup(msg->mem_ctx, value.data);
+
+ if (next_attr(&s, &attr, &value) != 0) {
+ goto failed;
+ }
+
+ if (!strequal(attr, "changetype")) {
+ DEBUG(5, ("Error: Second line of ldif must be a changetype "
+ "not '%s'\n", attr));
+ goto failed;
+ }
+
+ if (strequal(value.data, "delete")) {
+ msg->type = LDAP_TAG_DelRequest;
+ msg->r.DelRequest.dn = dn;
+ return msg;
+ }
+
+ if (strequal(value.data, "add")) {
+ msg->type = LDAP_TAG_AddRequest;
+
+ msg->r.AddRequest.dn = dn;
+
+ if (!fill_add_attributes(msg, &s))
+ goto failed;
+
+ return msg;
+ }
+
+ if (strequal(value.data, "modify")) {
+ msg->type = LDAP_TAG_ModifyRequest;
+
+ msg->r.ModifyRequest.dn = dn;
+
+ if (!fill_mods(msg, &s))
+ goto failed;
+
+ return msg;
+ }
+
+ DEBUG(3, ("changetype %s not supported\n", (char *)value.data));
+
+failed:
+ destroy_ldap_message(msg);
+ return NULL;
+}
+
+/*
+ a wrapper around ldif_read() for reading from const char*
+*/
+struct ldif_read_string_state {
+ const char *s;
+};
+
+static int fgetc_string(void *private_data)
+{
+ struct ldif_read_string_state *state = private_data;
+ if (state->s[0] != 0) {
+ return *state->s++;
+ }
+ return EOF;
+}
+
+struct ldap_message *ldap_ldif2msg(const char *s)
+{
+ struct ldif_read_string_state state;
+ state.s = s;
+ return ldif_read(fgetc_string, &state);
+}
+
+static void ldap_encode_response(enum ldap_request_tag tag,
+ struct ldap_Result *result,
+ ASN1_DATA *data)
+{
+ asn1_push_tag(data, ASN1_APPLICATION(tag));
+ asn1_write_enumerated(data, result->resultcode);
+ asn1_write_OctetString(data, result->dn,
+ (result->dn) ? strlen(result->dn) : 0);
+ asn1_write_OctetString(data, result->errormessage,
+ (result->errormessage) ?
+ strlen(result->errormessage) : 0);
+ if (result->referral != NULL)
+ asn1_write_OctetString(data, result->referral,
+ strlen(result->referral));
+ asn1_pop_tag(data);
+}
+
+BOOL ldap_encode(struct ldap_message *msg, DATA_BLOB *result)
+{
+ ASN1_DATA data;
+ int i, j;
+
+ ZERO_STRUCT(data);
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ asn1_write_Integer(&data, msg->messageid);
+
+ switch (msg->type) {
+ case LDAP_TAG_BindRequest: {
+ struct ldap_BindRequest *r = &msg->r.BindRequest;
+ asn1_push_tag(&data, ASN1_APPLICATION(LDAP_TAG_BindRequest));
+ asn1_write_Integer(&data, r->version);
+ asn1_write_OctetString(&data, r->dn,
+ (r->dn != NULL) ? strlen(r->dn) : 0);
+
+ switch (r->mechanism) {
+ case LDAP_AUTH_MECH_SIMPLE:
+ /* context, primitive */
+ asn1_push_tag(&data, r->mechanism | 0x80);
+ asn1_write(&data, r->creds.password,
+ strlen(r->creds.password));
+ asn1_pop_tag(&data);
+ break;
+ case LDAP_AUTH_MECH_SASL:
+ /* context, constructed */
+ asn1_push_tag(&data, r->mechanism | 0xa0);
+ asn1_write_OctetString(&data, r->creds.SASL.mechanism,
+ strlen(r->creds.SASL.mechanism));
+ asn1_write_OctetString(&data, r->creds.SASL.creds.data,
+ r->creds.SASL.creds.length);
+ asn1_pop_tag(&data);
+ break;
+ default:
+ return False;
+ }
+
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+ break;
+ }
+ case LDAP_TAG_BindResponse: {
+ struct ldap_BindResponse *r = &msg->r.BindResponse;
+ ldap_encode_response(msg->type, &r->response, &data);
+ break;
+ }
+ case LDAP_TAG_UnbindRequest: {
+/* struct ldap_UnbindRequest *r = &msg->r.UnbindRequest; */
+ break;
+ }
+ case LDAP_TAG_SearchRequest: {
+ struct ldap_SearchRequest *r = &msg->r.SearchRequest;
+ asn1_push_tag(&data, ASN1_APPLICATION(LDAP_TAG_SearchRequest));
+ asn1_write_OctetString(&data, r->basedn, strlen(r->basedn));
+ asn1_write_enumerated(&data, r->scope);
+ asn1_write_enumerated(&data, r->deref);
+ asn1_write_Integer(&data, r->sizelimit);
+ asn1_write_Integer(&data, r->timelimit);
+ asn1_write_BOOLEAN2(&data, r->attributesonly);
+
+ {
+ TALLOC_CTX *mem_ctx = talloc_init("ldb_parse_tree");
+ struct ldb_parse_tree *tree;
+
+ if (mem_ctx == NULL)
+ return False;
+
+ tree = ldb_parse_tree(mem_ctx, r->filter);
+
+ if (tree == NULL)
+ return False;
+
+ ldap_push_filter(&data, tree);
+
+ talloc_destroy(mem_ctx);
+ }
+
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ for (i=0; i<r->num_attributes; i++) {
+ asn1_write_OctetString(&data, r->attributes[i],
+ strlen(r->attributes[i]));
+ }
+ asn1_pop_tag(&data);
+
+ asn1_pop_tag(&data);
+ break;
+ }
+ case LDAP_TAG_SearchResultEntry: {
+ struct ldap_SearchResEntry *r = &msg->r.SearchResultEntry;
+ asn1_push_tag(&data, ASN1_APPLICATION(LDAP_TAG_SearchResultEntry));
+ asn1_write_OctetString(&data, r->dn, strlen(r->dn));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ for (i=0; i<r->num_attributes; i++) {
+ struct ldap_attribute *attr = &r->attributes[i];
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ asn1_write_OctetString(&data, attr->name,
+ strlen(attr->name));
+ asn1_push_tag(&data, ASN1_SEQUENCE(1));
+ for (j=0; j<attr->num_values; j++) {
+ asn1_write_OctetString(&data,
+ attr->values[j].data,
+ attr->values[j].length);
+ }
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+ }
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+ break;
+ }
+ case LDAP_TAG_SearchResultDone: {
+ struct ldap_SearchResultDone *r = &msg->r.SearchResultDone;
+ ldap_encode_response(msg->type, r, &data);
+ break;
+ }
+ case LDAP_TAG_ModifyRequest: {
+ struct ldap_ModifyRequest *r = &msg->r.ModifyRequest;
+ asn1_push_tag(&data, ASN1_APPLICATION(LDAP_TAG_ModifyRequest));
+ asn1_write_OctetString(&data, r->dn, strlen(r->dn));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+
+ for (i=0; i<r->num_mods; i++) {
+ struct ldap_attribute *attrib = &r->mods[i].attrib;
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ asn1_write_enumerated(&data, r->mods[i].type);
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ asn1_write_OctetString(&data, attrib->name,
+ strlen(attrib->name));
+ asn1_push_tag(&data, ASN1_SET);
+ for (j=0; j<attrib->num_values; j++) {
+ asn1_write_OctetString(&data,
+ attrib->values[j].data,
+ attrib->values[j].length);
+
+ }
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+ }
+
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+ break;
+ }
+ case LDAP_TAG_ModifyResponse: {
+ struct ldap_Result *r = &msg->r.ModifyResponse;
+ ldap_encode_response(msg->type, r, &data);
+ break;
+ }
+ case LDAP_TAG_AddRequest: {
+ struct ldap_AddRequest *r = &msg->r.AddRequest;
+ asn1_push_tag(&data, ASN1_APPLICATION(LDAP_TAG_AddRequest));
+ asn1_write_OctetString(&data, r->dn, strlen(r->dn));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+
+ for (i=0; i<r->num_attributes; i++) {
+ struct ldap_attribute *attrib = &r->attributes[i];
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ asn1_write_OctetString(&data, attrib->name,
+ strlen(attrib->name));
+ asn1_push_tag(&data, ASN1_SET);
+ for (j=0; j<r->attributes[i].num_values; j++) {
+ asn1_write_OctetString(&data,
+ attrib->values[j].data,
+ attrib->values[j].length);
+ }
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+ }
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+ break;
+ }
+ case LDAP_TAG_AddResponse: {
+ struct ldap_Result *r = &msg->r.AddResponse;
+ ldap_encode_response(msg->type, r, &data);
+ break;
+ }
+ case LDAP_TAG_DelRequest: {
+ struct ldap_DelRequest *r = &msg->r.DelRequest;
+ asn1_push_tag(&data,
+ ASN1_APPLICATION_SIMPLE(LDAP_TAG_DelRequest));
+ asn1_write(&data, r->dn, strlen(r->dn));
+ asn1_pop_tag(&data);
+ break;
+ }
+ case LDAP_TAG_DelResponse: {
+ struct ldap_Result *r = &msg->r.DelResponse;
+ ldap_encode_response(msg->type, r, &data);
+ break;
+ }
+ case LDAP_TAG_ModifyDNRequest: {
+ struct ldap_ModifyDNRequest *r = &msg->r.ModifyDNRequest;
+ asn1_push_tag(&data,
+ ASN1_APPLICATION(LDAP_TAG_ModifyDNRequest));
+ asn1_write_OctetString(&data, r->dn, strlen(r->dn));
+ asn1_write_OctetString(&data, r->newrdn, strlen(r->newrdn));
+ asn1_write_BOOLEAN2(&data, r->deleteolddn);
+ if (r->newsuperior != NULL) {
+ asn1_push_tag(&data, ASN1_CONTEXT_SIMPLE(0));
+ asn1_write(&data, r->newsuperior,
+ strlen(r->newsuperior));
+ asn1_pop_tag(&data);
+ }
+ asn1_pop_tag(&data);
+ break;
+ }
+ case LDAP_TAG_ModifyDNResponse: {
+/* struct ldap_Result *r = &msg->r.ModifyDNResponse; */
+ break;
+ }
+ case LDAP_TAG_CompareRequest: {
+ struct ldap_CompareRequest *r = &msg->r.CompareRequest;
+ asn1_push_tag(&data,
+ ASN1_APPLICATION(LDAP_TAG_CompareRequest));
+ asn1_write_OctetString(&data, r->dn, strlen(r->dn));
+ asn1_push_tag(&data, ASN1_SEQUENCE(0));
+ asn1_write_OctetString(&data, r->attribute,
+ strlen(r->attribute));
+ asn1_write_OctetString(&data, r->value,
+ strlen(r->value));
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+ break;
+ }
+ case LDAP_TAG_CompareResponse: {
+/* struct ldap_Result *r = &msg->r.CompareResponse; */
+ break;
+ }
+ case LDAP_TAG_AbandonRequest: {
+ struct ldap_AbandonRequest *r = &msg->r.AbandonRequest;
+ asn1_push_tag(&data,
+ ASN1_APPLICATION_SIMPLE(LDAP_TAG_AbandonRequest));
+ asn1_write_Integer(&data, r->messageid);
+ asn1_pop_tag(&data);
+ break;
+ }
+ case LDAP_TAG_SearchResultReference: {
+/* struct ldap_SearchResRef *r = &msg->r.SearchResultReference; */
+ break;
+ }
+ case LDAP_TAG_ExtendedRequest: {
+ struct ldap_ExtendedRequest *r = &msg->r.ExtendedRequest;
+ asn1_push_tag(&data, ASN1_APPLICATION(LDAP_TAG_ExtendedRequest));
+ asn1_push_tag(&data, ASN1_CONTEXT_SIMPLE(0));
+ asn1_write(&data, r->oid, strlen(r->oid));
+ asn1_pop_tag(&data);
+ asn1_push_tag(&data, ASN1_CONTEXT_SIMPLE(1));
+ asn1_write(&data, r->value.data, r->value.length);
+ asn1_pop_tag(&data);
+ asn1_pop_tag(&data);
+ break;
+ }
+ case LDAP_TAG_ExtendedResponse: {
+ struct ldap_ExtendedResponse *r = &msg->r.ExtendedResponse;
+ ldap_encode_response(msg->type, &r->response, &data);
+ break;
+ }
+ default:
+ return False;
+ }
+
+ asn1_pop_tag(&data);
+ *result = data_blob(data.data, data.length);
+ asn1_free(&data);
+ return True;
+}
+
+static const char *blob2string_talloc(TALLOC_CTX *mem_ctx,
+ DATA_BLOB blob)
+{
+ char *result = talloc(mem_ctx, blob.length+1);
+ memcpy(result, blob.data, blob.length);
+ result[blob.length] = '\0';
+ return result;
+}
+
+static BOOL asn1_read_OctetString_talloc(TALLOC_CTX *mem_ctx,
+ ASN1_DATA *data,
+ const char **result)
+{
+ DATA_BLOB string;
+ if (!asn1_read_OctetString(data, &string))
+ return False;
+ *result = blob2string_talloc(mem_ctx, string);
+ data_blob_free(&string);
+ return True;
+}
+
+static void ldap_decode_response(TALLOC_CTX *mem_ctx,
+ ASN1_DATA *data,
+ enum ldap_request_tag tag,
+ struct ldap_Result *result)
+{
+ asn1_start_tag(data, ASN1_APPLICATION(tag));
+ asn1_read_enumerated(data, &result->resultcode);
+ asn1_read_OctetString_talloc(mem_ctx, data, &result->dn);
+ asn1_read_OctetString_talloc(mem_ctx, data, &result->errormessage);
+ if (asn1_peek_tag(data, ASN1_OCTET_STRING))
+ asn1_read_OctetString_talloc(mem_ctx, data, &result->referral);
+ else
+ result->referral = NULL;
+ asn1_end_tag(data);
+}
+
+static BOOL add_attrib_to_array_talloc(TALLOC_CTX *mem_ctx,
+ const struct ldap_attribute *attrib,
+ struct ldap_attribute **attribs,
+ int *num_attribs)
+{
+ *attribs = talloc_realloc(mem_ctx, *attribs,
+ sizeof(**attribs) * (*num_attribs+1));
+
+ if (*attribs == NULL)
+ return False;
+
+ (*attribs)[*num_attribs] = *attrib;
+ *num_attribs += 1;
+ return True;
+}
+
+static BOOL ldap_decode_filter(TALLOC_CTX *mem_ctx, ASN1_DATA *data,
+ char **filter)
+{
+ uint8 filter_tag, tag_desc;
+
+ if (!asn1_peek_uint8(data, &filter_tag))
+ return False;
+
+ tag_desc = filter_tag;
+ filter_tag &= 0x1f; /* strip off the asn1 stuff */
+ tag_desc &= 0xe0;
+
+ switch(filter_tag) {
+ case 0: {
+ /* AND of one or more filters */
+ if (tag_desc != 0xa0) /* context compount */
+ return False;
+
+ asn1_start_tag(data, ASN1_CONTEXT(0));
+
+ *filter = talloc_strdup(mem_ctx, "(&");
+ if (*filter == NULL)
+ return False;
+
+ while (asn1_tag_remaining(data) > 0) {
+ char *subfilter;
+ if (!ldap_decode_filter(mem_ctx, data, &subfilter))
+ return False;
+ *filter = talloc_asprintf(mem_ctx, "%s%s", *filter,
+ subfilter);
+ if (*filter == NULL)
+ return False;
+ }
+ asn1_end_tag(data);
+
+ *filter = talloc_asprintf(mem_ctx, "%s)", *filter);
+ break;
+ }
+ case 1: {
+ /* OR of one or more filters */
+ if (tag_desc != 0xa0) /* context compount */
+ return False;
+
+ asn1_start_tag(data, ASN1_CONTEXT(1));
+
+ *filter = talloc_strdup(mem_ctx, "(|");
+ if (*filter == NULL)
+ return False;
+
+ while (asn1_tag_remaining(data) > 0) {
+ char *subfilter;
+ if (!ldap_decode_filter(mem_ctx, data, &subfilter))
+ return False;
+ *filter = talloc_asprintf(mem_ctx, "%s%s", *filter,
+ subfilter);
+ if (*filter == NULL)
+ return False;
+ }
+
+ asn1_end_tag(data);
+
+ *filter = talloc_asprintf(mem_ctx, "%s)", *filter);
+ break;
+ }
+ case 3: {
+ /* equalityMatch */
+ const char *attrib, *value;
+ if (tag_desc != 0xa0) /* context compound */
+ return False;
+ asn1_start_tag(data, ASN1_CONTEXT(3));
+ asn1_read_OctetString_talloc(mem_ctx, data, &attrib);
+ asn1_read_OctetString_talloc(mem_ctx, data, &value);
+ asn1_end_tag(data);
+ if ((data->has_error) || (attrib == NULL) || (value == NULL))
+ return False;
+ *filter = talloc_asprintf(mem_ctx, "(%s=%s)", attrib, value);
+ break;
+ }
+ case 7: {
+ /* Normal presence, "attribute=*" */
+ int attr_len;
+ char *attr_name;
+ if (tag_desc != 0x80) /* context simple */
+ return False;
+ if (!asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(7)))
+ return False;
+ attr_len = asn1_tag_remaining(data);
+ attr_name = malloc(attr_len+1);
+ if (attr_name == NULL)
+ return False;
+ asn1_read(data, attr_name, attr_len);
+ attr_name[attr_len] = '\0';
+ *filter = talloc_asprintf(mem_ctx, "(%s=*)", attr_name);
+ SAFE_FREE(attr_name);
+ asn1_end_tag(data);
+ break;
+ }
+ default:
+ return False;
+ }
+ if (*filter == NULL)
+ return False;
+ return True;
+}
+
+static void ldap_decode_attrib(TALLOC_CTX *mem_ctx, ASN1_DATA *data,
+ struct ldap_attribute *attrib)
+{
+ asn1_start_tag(data, ASN1_SEQUENCE(0));
+ asn1_read_OctetString_talloc(mem_ctx, data, &attrib->name);
+ asn1_start_tag(data, ASN1_SET);
+ while (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
+ DATA_BLOB blob;
+ struct ldb_val value;
+ asn1_read_OctetString(data, &blob);
+ value.data = blob.data;
+ value.length = blob.length;
+ add_value_to_attrib(mem_ctx, &value, attrib);
+ data_blob_free(&blob);
+ }
+ asn1_end_tag(data);
+ asn1_end_tag(data);
+
+}
+
+static void ldap_decode_attribs(TALLOC_CTX *mem_ctx, ASN1_DATA *data,
+ struct ldap_attribute **attributes,
+ int *num_attributes)
+{
+ asn1_start_tag(data, ASN1_SEQUENCE(0));
+ while (asn1_peek_tag(data, ASN1_SEQUENCE(0))) {
+ struct ldap_attribute attrib;
+ ZERO_STRUCT(attrib);
+ ldap_decode_attrib(mem_ctx, data, &attrib);
+ add_attrib_to_array_talloc(mem_ctx, &attrib,
+ attributes, num_attributes);
+ }
+ asn1_end_tag(data);
+}
+
+BOOL ldap_decode(ASN1_DATA *data, struct ldap_message *msg)
+{
+ uint8 tag;
+
+ asn1_start_tag(data, ASN1_SEQUENCE(0));
+ asn1_read_Integer(data, &msg->messageid);
+
+ if (!asn1_peek_uint8(data, &tag))
+ return False;
+
+ switch(tag) {
+
+ case ASN1_APPLICATION(LDAP_TAG_BindRequest): {
+ struct ldap_BindRequest *r = &msg->r.BindRequest;
+ msg->type = LDAP_TAG_BindRequest;
+ asn1_start_tag(data, ASN1_APPLICATION(LDAP_TAG_BindRequest));
+ asn1_read_Integer(data, &r->version);
+ asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->dn);
+ if (asn1_peek_tag(data, 0x80)) {
+ int pwlen;
+ r->creds.password = "";
+ /* Mechanism 0 (SIMPLE) */
+ asn1_start_tag(data, 0x80);
+ pwlen = asn1_tag_remaining(data);
+ if (pwlen != 0) {
+ char *pw = talloc(msg->mem_ctx, pwlen+1);
+ asn1_read(data, pw, pwlen);
+ pw[pwlen] = '\0';
+ r->creds.password = pw;
+ }
+ asn1_end_tag(data);
+ }
+ asn1_end_tag(data);
+ break;
+ }
+
+ case ASN1_APPLICATION(LDAP_TAG_BindResponse): {
+ struct ldap_BindResponse *r = &msg->r.BindResponse;
+ msg->type = LDAP_TAG_BindResponse;
+ ldap_decode_response(msg->mem_ctx,
+ data, LDAP_TAG_BindResponse,
+ &r->response);
+ break;
+ }
+
+ case ASN1_APPLICATION(LDAP_TAG_UnbindRequest): {
+ msg->type = LDAP_TAG_UnbindRequest;
+ break;
+ }
+
+ case ASN1_APPLICATION(LDAP_TAG_SearchRequest): {
+ struct ldap_SearchRequest *r = &msg->r.SearchRequest;
+ msg->type = LDAP_TAG_SearchRequest;
+ asn1_start_tag(data, ASN1_APPLICATION(LDAP_TAG_SearchRequest));
+ asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->basedn);
+ asn1_read_enumerated(data, (int *)&(r->scope));
+ asn1_read_enumerated(data, (int *)&(r->deref));
+ asn1_read_Integer(data, &r->sizelimit);
+ asn1_read_Integer(data, &r->timelimit);
+ asn1_read_BOOLEAN2(data, &r->attributesonly);
+
+ /* Maybe create a TALLOC_CTX for the filter? This can waste
+ * quite a bit of memory recursing down. */
+ ldap_decode_filter(msg->mem_ctx, data, &r->filter);
+
+ asn1_start_tag(data, ASN1_SEQUENCE(0));
+
+ r->num_attributes = 0;
+ r->attributes = NULL;
+
+ while (asn1_tag_remaining(data) > 0) {
+ const char *attr;
+ if (!asn1_read_OctetString_talloc(msg->mem_ctx, data,
+ &attr))
+ return False;
+ if (!add_string_to_array(msg->mem_ctx, attr,
+ &r->attributes,
+ &r->num_attributes))
+ return False;
+ }
+
+ asn1_end_tag(data);
+ asn1_end_tag(data);
+ break;
+ }
+
+ case ASN1_APPLICATION(LDAP_TAG_SearchResultEntry): {
+ struct ldap_SearchResEntry *r = &msg->r.SearchResultEntry;
+ msg->type = LDAP_TAG_SearchResultEntry;
+ r->attributes = NULL;
+ r->num_attributes = 0;
+ asn1_start_tag(data,
+ ASN1_APPLICATION(LDAP_TAG_SearchResultEntry));
+ asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->dn);
+ ldap_decode_attribs(msg->mem_ctx, data, &r->attributes,
+ &r->num_attributes);
+ asn1_end_tag(data);
+ break;
+ }
+
+ case ASN1_APPLICATION(LDAP_TAG_SearchResultDone): {
+ struct ldap_SearchResultDone *r = &msg->r.SearchResultDone;
+ msg->type = LDAP_TAG_SearchResultDone;
+ ldap_decode_response(msg->mem_ctx, data,
+ LDAP_TAG_SearchResultDone, r);
+ break;
+ }
+
+ case ASN1_APPLICATION(LDAP_TAG_SearchResultReference): {
+/* struct ldap_SearchResRef *r = &msg->r.SearchResultReference; */
+ msg->type = LDAP_TAG_SearchResultReference;
+ break;
+ }
+
+ case ASN1_APPLICATION(LDAP_TAG_ModifyRequest): {
+ struct ldap_ModifyRequest *r = &msg->r.ModifyRequest;
+ msg->type = LDAP_TAG_ModifyRequest;
+ asn1_start_tag(data, ASN1_APPLICATION(LDAP_TAG_ModifyRequest));
+ asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->dn);
+ asn1_start_tag(data, ASN1_SEQUENCE(0));
+
+ r->num_mods = 0;
+ r->mods = NULL;
+
+ while (asn1_tag_remaining(data) > 0) {
+ struct ldap_mod mod;
+ ZERO_STRUCT(mod);
+ asn1_start_tag(data, ASN1_SEQUENCE(0));
+ asn1_read_enumerated(data, &mod.type);
+ ldap_decode_attrib(msg->mem_ctx, data, &mod.attrib);
+ asn1_end_tag(data);
+ if (!add_mod_to_array_talloc(msg->mem_ctx, &mod,
+ &r->mods, &r->num_mods))
+ break;
+ }
+
+ asn1_end_tag(data);
+ asn1_end_tag(data);
+ break;
+ }
+
+ case ASN1_APPLICATION(LDAP_TAG_ModifyResponse): {
+ struct ldap_ModifyResponse *r = &msg->r.ModifyResponse;
+ msg->type = LDAP_TAG_ModifyResponse;
+ ldap_decode_response(msg->mem_ctx, data,
+ LDAP_TAG_ModifyResponse, r);
+ break;
+ }
+
+ case ASN1_APPLICATION(LDAP_TAG_AddRequest): {
+ struct ldap_AddRequest *r = &msg->r.AddRequest;
+ msg->type = LDAP_TAG_AddRequest;
+ asn1_start_tag(data, ASN1_APPLICATION(LDAP_TAG_AddRequest));
+ asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->dn);
+
+ r->attributes = NULL;
+ r->num_attributes = 0;
+ ldap_decode_attribs(msg->mem_ctx, data, &r->attributes,
+ &r->num_attributes);
+
+ asn1_end_tag(data);
+ break;
+ }
+
+ case ASN1_APPLICATION(LDAP_TAG_AddResponse): {
+ struct ldap_AddResponse *r = &msg->r.AddResponse;
+ msg->type = LDAP_TAG_AddResponse;
+ ldap_decode_response(msg->mem_ctx, data,
+ LDAP_TAG_AddResponse, r);
+ break;
+ }
+
+ case ASN1_APPLICATION_SIMPLE(LDAP_TAG_DelRequest): {
+ struct ldap_DelRequest *r = &msg->r.DelRequest;
+ int len;
+ char *dn;
+ msg->type = LDAP_TAG_DelRequest;
+ asn1_start_tag(data,
+ ASN1_APPLICATION_SIMPLE(LDAP_TAG_DelRequest));
+ len = asn1_tag_remaining(data);
+ dn = talloc(msg->mem_ctx, len+1);
+ if (dn == NULL)
+ break;
+ asn1_read(data, dn, len);
+ dn[len] = '\0';
+ r->dn = dn;
+ asn1_end_tag(data);
+ break;
+ }
+
+ case ASN1_APPLICATION(LDAP_TAG_DelResponse): {
+ struct ldap_DelResponse *r = &msg->r.DelResponse;
+ msg->type = LDAP_TAG_DelResponse;
+ ldap_decode_response(msg->mem_ctx, data,
+ LDAP_TAG_DelResponse, r);
+ break;
+ }
+
+ case ASN1_APPLICATION(LDAP_TAG_ModifyDNRequest): {
+/* struct ldap_ModifyDNRequest *r = &msg->r.ModifyDNRequest; */
+ msg->type = LDAP_TAG_ModifyDNRequest;
+ break;
+ }
+
+ case ASN1_APPLICATION(LDAP_TAG_ModifyDNResponse): {
+ struct ldap_ModifyDNResponse *r = &msg->r.ModifyDNResponse;
+ msg->type = LDAP_TAG_ModifyDNResponse;
+ ldap_decode_response(msg->mem_ctx, data,
+ LDAP_TAG_ModifyDNResponse, r);
+ break;
+ }
+
+ case ASN1_APPLICATION(LDAP_TAG_CompareRequest): {
+/* struct ldap_CompareRequest *r = &msg->r.CompareRequest; */
+ msg->type = LDAP_TAG_CompareRequest;
+ break;
+ }
+
+ case ASN1_APPLICATION(LDAP_TAG_CompareResponse): {
+ struct ldap_CompareResponse *r = &msg->r.CompareResponse;
+ msg->type = LDAP_TAG_CompareResponse;
+ ldap_decode_response(msg->mem_ctx, data,
+ LDAP_TAG_CompareResponse, r);
+ break;
+ }
+
+ case ASN1_APPLICATION(LDAP_TAG_AbandonRequest): {
+/* struct ldap_AbandonRequest *r = &msg->r.AbandonRequest; */
+ msg->type = LDAP_TAG_AbandonRequest;
+ break;
+ }
+
+ case ASN1_APPLICATION(LDAP_TAG_ExtendedRequest): {
+/* struct ldap_ExtendedRequest *r = &msg->r.ExtendedRequest; */
+ msg->type = LDAP_TAG_ExtendedRequest;
+ break;
+ }
+
+ case ASN1_APPLICATION(LDAP_TAG_ExtendedResponse): {
+ struct ldap_ExtendedResponse *r = &msg->r.ExtendedResponse;
+ msg->type = LDAP_TAG_ExtendedResponse;
+ ldap_decode_response(msg->mem_ctx, data,
+ LDAP_TAG_ExtendedResponse, &r->response);
+ /* I have to come across an operation that actually sends
+ * something back to really see what's going on. The currently
+ * needed pwdchange does not send anything back. */
+ r->name = NULL;
+ r->value.data = NULL;
+ r->value.length = 0;
+ break;
+ }
+
+ }
+
+ asn1_end_tag(data);
+ return ((!data->has_error) && (data->nesting == NULL));
+}
+
+BOOL ldap_parse_basic_url(TALLOC_CTX *mem_ctx, const char *url,
+ char **host, uint16 *port, BOOL *ldaps)
+{
+ int tmp_port = 0;
+ fstring protocol;
+ fstring tmp_host;
+ const char *p = url;
+
+ /* skip leading "URL:" (if any) */
+ if ( strnequal( p, "URL:", 4 ) ) {
+ p += 4;
+ }
+
+ /* Paranoia check */
+ SMB_ASSERT(sizeof(protocol)>10 && sizeof(tmp_host)>254);
+
+ sscanf(p, "%10[^:]://%254[^:/]:%d", protocol, tmp_host, &tmp_port);
+
+ if (strequal(protocol, "ldap")) {
+ *port = 389;
+ *ldaps = False;
+ } else if (strequal(protocol, "ldaps")) {
+ *port = 636;
+ *ldaps = True;
+ } else {
+ DEBUG(0, ("unrecognised protocol (%s)!\n", protocol));
+ return False;
+ }
+
+ if (tmp_port != 0)
+ *port = tmp_port;
+
+ *host = talloc_strdup(mem_ctx, tmp_host);
+
+ return (*host != NULL);
+}
+
+struct ldap_connection *new_ldap_connection(void)
+{
+ TALLOC_CTX *mem_ctx = talloc_init("ldap_connection");
+ struct ldap_connection *result;
+
+ if (mem_ctx == NULL)
+ return NULL;
+
+ result = talloc(mem_ctx, sizeof(*result));
+
+ if (result == NULL)
+ return NULL;
+
+ result->mem_ctx = mem_ctx;
+ result->next_msgid = 1;
+ result->outstanding = NULL;
+ result->searchid = 0;
+ result->search_entries = NULL;
+ return result;
+}
+
+BOOL ldap_connect(struct ldap_connection *conn, const char *url)
+{
+ struct hostent *hp;
+ struct in_addr ip;
+
+ if (!ldap_parse_basic_url(conn->mem_ctx, url, &conn->host,
+ &conn->port, &conn->ldaps))
+ return False;
+
+ hp = sys_gethostbyname(conn->host);
+
+ if ((hp == NULL) || (hp->h_addr == NULL))
+ return False;
+
+ putip((char *)&ip, (char *)hp->h_addr);
+
+ conn->sock = open_socket_out(SOCK_STREAM, &ip, conn->port, 10000);
+
+ return (conn->sock >= 0);
+}
+
+BOOL ldap_set_simple_creds(struct ldap_connection *conn,
+ const char *dn, const char *password)
+{
+ conn->auth_dn = talloc_strdup(conn->mem_ctx, dn);
+ conn->simple_pw = talloc_strdup(conn->mem_ctx, password);
+
+ return ((conn->auth_dn != NULL) && (conn->simple_pw != NULL));
+}
+
+struct ldap_message *new_ldap_message(void)
+{
+ TALLOC_CTX *mem_ctx = talloc_init("ldap_message");
+ struct ldap_message *result;
+
+ if (mem_ctx == NULL)
+ return NULL;
+
+ result = talloc(mem_ctx, sizeof(*result));
+
+ if (result == NULL)
+ return NULL;
+
+ result->mem_ctx = mem_ctx;
+ return result;
+}
+
+void destroy_ldap_message(struct ldap_message *msg)
+{
+ if (msg != NULL)
+ talloc_destroy(msg->mem_ctx);
+}
+
+BOOL ldap_send_msg(struct ldap_connection *conn, struct ldap_message *msg,
+ const struct timeval *endtime)
+{
+ DATA_BLOB request;
+ BOOL result;
+ struct ldap_queue_entry *entry;
+
+ msg->messageid = conn->next_msgid++;
+
+ if (!ldap_encode(msg, &request))
+ return False;
+
+ result = (write_data_until(conn->sock, request.data, request.length,
+ endtime) == request.length);
+
+ data_blob_free(&request);
+
+ if (!result)
+ return result;
+
+ /* abandon and unbind don't expect results */
+
+ if ((msg->type == LDAP_TAG_AbandonRequest) ||
+ (msg->type == LDAP_TAG_UnbindRequest))
+ return True;
+
+ entry = malloc(sizeof(*entry));
+
+ if (entry == NULL)
+ return False;
+
+ entry->msgid = msg->messageid;
+ entry->msg = NULL;
+ DLIST_ADD(conn->outstanding, entry);
+
+ return True;
+}
+
+BOOL ldap_receive_msg(struct ldap_connection *conn, struct ldap_message *msg,
+ const struct timeval *endtime)
+{
+ struct asn1_data data;
+ BOOL result;
+
+ if (!asn1_read_sequence_until(conn->sock, &data, endtime))
+ return False;
+
+ result = ldap_decode(&data, msg);
+
+ asn1_free(&data);
+ return result;
+}
+
+static struct ldap_message *recv_from_queue(struct ldap_connection *conn,
+ int msgid)
+{
+ struct ldap_queue_entry *e;
+
+ for (e = conn->outstanding; e != NULL; e = e->next) {
+
+ if (e->msgid == msgid) {
+ struct ldap_message *result = e->msg;
+ DLIST_REMOVE(conn->outstanding, e);
+ SAFE_FREE(e);
+ return result;
+ }
+ }
+
+ return NULL;
+}
+
+static void add_search_entry(struct ldap_connection *conn,
+ struct ldap_message *msg)
+{
+ struct ldap_queue_entry *e = malloc(sizeof *e);
+ struct ldap_queue_entry *tmp;
+
+ if (e == NULL)
+ return;
+
+ e->msg = msg;
+ DLIST_ADD_END(conn->search_entries, e, tmp);
+ return;
+}
+
+static void fill_outstanding_request(struct ldap_connection *conn,
+ struct ldap_message *msg)
+{
+ struct ldap_queue_entry *e;
+
+ for (e = conn->outstanding; e != NULL; e = e->next) {
+ if (e->msgid == msg->messageid) {
+ e->msg = msg;
+ return;
+ }
+ }
+
+ /* This reply has not been expected, destroy the incoming msg */
+ destroy_ldap_message(msg);
+ return;
+}
+
+struct ldap_message *ldap_receive(struct ldap_connection *conn, int msgid,
+ const struct timeval *endtime)
+{
+ struct ldap_message *result = recv_from_queue(conn, msgid);
+
+ if (result != NULL)
+ return result;
+
+ while (True) {
+ struct asn1_data data;
+ result = new_ldap_message();
+ BOOL res;
+
+ if (!asn1_read_sequence_until(conn->sock, &data, endtime))
+ return NULL;
+
+ res = ldap_decode(&data, result);
+ asn1_free(&data);
+
+ if (!res)
+ return NULL;
+
+ if (result->messageid == msgid)
+ return result;
+
+ if (result->type == LDAP_TAG_SearchResultEntry) {
+ add_search_entry(conn, result);
+ } else {
+ fill_outstanding_request(conn, result);
+ }
+ }
+
+ return NULL;
+}
+
+struct ldap_message *ldap_transaction(struct ldap_connection *conn,
+ struct ldap_message *request)
+{
+ if (!ldap_send_msg(conn, request, NULL))
+ return False;
+
+ return ldap_receive(conn, request->messageid, NULL);
+}
+
+BOOL ldap_setup_connection(struct ldap_connection *conn,
+ const char *url)
+{
+ struct ldap_message *msg = new_ldap_message();
+ struct ldap_message *response;
+ BOOL result;
+
+ if (msg == NULL)
+ return False;
+
+ if (!ldap_connect(conn, url)) {
+ destroy_ldap_message(msg);
+ return False;
+ }
+
+ msg->messageid = conn->next_msgid++;
+ msg->type = LDAP_TAG_BindRequest;
+ msg->r.BindRequest.version = 3;
+ msg->r.BindRequest.dn = conn->auth_dn;
+ msg->r.BindRequest.mechanism = LDAP_AUTH_MECH_SIMPLE;
+ msg->r.BindRequest.creds.password = conn->simple_pw;
+
+ if ((response = ldap_transaction(conn, msg)) == NULL)
+ return False;
+
+ result = (response->r.BindResponse.response.resultcode == 0);
+
+ destroy_ldap_message(msg);
+ destroy_ldap_message(response);
+ return result;
+}
+
+static BOOL ldap_abandon_message(struct ldap_connection *conn, int msgid,
+ const struct timeval *endtime)
+{
+ struct ldap_message *msg = new_ldap_message();
+ BOOL result;
+
+ if (msg == NULL)
+ return False;
+
+ msg->type = LDAP_TAG_AbandonRequest;
+ msg->r.AbandonRequest.messageid = msgid;
+
+ result = ldap_send_msg(conn, msg, endtime);
+ destroy_ldap_message(msg);
+ return result;
+}
+
+struct ldap_message *new_ldap_search_message(const char *base,
+ enum ldap_scope scope,
+ char *filter,
+ int num_attributes,
+ const char **attributes)
+{
+ struct ldap_message *res = new_ldap_message();
+
+ if (res == NULL)
+ return NULL;
+
+ res->type = LDAP_TAG_SearchRequest;
+ res->r.SearchRequest.basedn = base;
+ res->r.SearchRequest.scope = scope;
+ res->r.SearchRequest.deref = LDAP_DEREFERENCE_NEVER;
+ res->r.SearchRequest.timelimit = 0;
+ res->r.SearchRequest.sizelimit = 0;
+ res->r.SearchRequest.attributesonly = False;
+ res->r.SearchRequest.filter = filter;
+ res->r.SearchRequest.num_attributes = num_attributes;
+ res->r.SearchRequest.attributes = attributes;
+ return res;
+}
+
+struct ldap_message *new_ldap_simple_bind_msg(const char *dn, const char *pw)
+{
+ struct ldap_message *res = new_ldap_message();
+
+ if (res == NULL)
+ return NULL;
+
+ res->type = LDAP_TAG_BindRequest;
+ res->r.BindRequest.version = 3;
+ res->r.BindRequest.dn = talloc_strdup(res->mem_ctx, dn);
+ res->r.BindRequest.mechanism = LDAP_AUTH_MECH_SIMPLE;
+ res->r.BindRequest.creds.password = talloc_strdup(res->mem_ctx, pw);
+ return res;
+}
+
+BOOL ldap_setsearchent(struct ldap_connection *conn, struct ldap_message *msg,
+ const struct timeval *endtime)
+{
+ if ((conn->searchid != 0) &&
+ (!ldap_abandon_message(conn, conn->searchid, endtime)))
+ return False;
+
+ conn->searchid = conn->next_msgid;
+ return ldap_send_msg(conn, msg, endtime);
+}
+
+struct ldap_message *ldap_getsearchent(struct ldap_connection *conn,
+ const struct timeval *endtime)
+{
+ struct ldap_message *result;
+
+ if (conn->search_entries != NULL) {
+ struct ldap_queue_entry *e = conn->search_entries;
+
+ result = e->msg;
+ DLIST_REMOVE(conn->search_entries, e);
+ SAFE_FREE(e);
+ return result;
+ }
+
+ result = ldap_receive(conn, conn->searchid, endtime);
+
+ if (result->type == LDAP_TAG_SearchResultEntry)
+ return result;
+
+ if (result->type == LDAP_TAG_SearchResultDone) {
+ /* TODO: Handle Paged Results */
+ destroy_ldap_message(result);
+ return NULL;
+ }
+
+ /* TODO: Handle Search References here */
+ return NULL;
+}
+
+void ldap_endsearchent(struct ldap_connection *conn,
+ const struct timeval *endtime)
+{
+ struct ldap_queue_entry *e;
+
+ e = conn->search_entries;
+
+ while (e != NULL) {
+ struct ldap_queue_entry *next = e->next;
+ DLIST_REMOVE(conn->search_entries, e);
+ SAFE_FREE(e);
+ e = next;
+ }
+}
+
+struct ldap_message *ldap_searchone(struct ldap_connection *conn,
+ struct ldap_message *msg,
+ const struct timeval *endtime)
+{
+ struct ldap_message *res1, *res2 = NULL;
+ if (!ldap_setsearchent(conn, msg, endtime))
+ return NULL;
+
+ res1 = ldap_getsearchent(conn, endtime);
+
+ if (res1 != NULL)
+ res2 = ldap_getsearchent(conn, endtime);
+
+ ldap_endsearchent(conn, endtime);
+
+ if (res1 == NULL)
+ return NULL;
+
+ if (res2 != NULL) {
+ /* More than one entry */
+ destroy_ldap_message(res1);
+ destroy_ldap_message(res2);
+ return NULL;
+ }
+
+ return res1;
+}
+
+BOOL ldap_find_single_value(struct ldap_message *msg, const char *attr,
+ DATA_BLOB *value)
+{
+ int i;
+ struct ldap_SearchResEntry *r = &msg->r.SearchResultEntry;
+
+ if (msg->type != LDAP_TAG_SearchResultEntry)
+ return False;
+
+ for (i=0; i<r->num_attributes; i++) {
+ if (strequal(attr, r->attributes[i].name)) {
+ if (r->attributes[i].num_values != 1)
+ return False;
+
+ *value = r->attributes[i].values[0];
+ return True;
+ }
+ }
+ return False;
+}
+
+BOOL ldap_find_single_string(struct ldap_message *msg, const char *attr,
+ TALLOC_CTX *mem_ctx, char **value)
+{
+ DATA_BLOB blob;
+
+ if (!ldap_find_single_value(msg, attr, &blob))
+ return False;
+
+ *value = talloc(mem_ctx, blob.length+1);
+
+ if (*value == NULL)
+ return False;
+
+ memcpy(*value, blob.data, blob.length);
+ (*value)[blob.length] = '\0';
+ return True;
+}
+
+BOOL ldap_find_single_int(struct ldap_message *msg, const char *attr,
+ int *value)
+{
+ DATA_BLOB blob;
+ char *val;
+ int errno_save;
+ BOOL res;
+
+ if (!ldap_find_single_value(msg, attr, &blob))
+ return False;
+
+ val = malloc(blob.length+1);
+ if (val == NULL)
+ return False;
+
+ memcpy(val, blob.data, blob.length);
+ val[blob.length] = '\0';
+
+ errno_save = errno;
+ errno = 0;
+
+ *value = strtol(val, NULL, 10);
+
+ res = (errno == 0);
+
+ free(val);
+ errno = errno_save;
+
+ return res;
+}
+
+int ldap_error(struct ldap_connection *conn)
+{
+ return 0;
+}
+
+NTSTATUS ldap2nterror(int ldaperror)
+{
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/ldap/ldap.h b/source4/libcli/ldap/ldap.h
new file mode 100644
index 0000000000..96c1b82ca3
--- /dev/null
+++ b/source4/libcli/ldap/ldap.h
@@ -0,0 +1,246 @@
+/*
+ Unix SMB/CIFS Implementation.
+ LDAP protocol helper functions for SAMBA
+ Copyright (C) Volker Lendecke 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 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.
+
+*/
+
+#ifndef _SMB_LDAP_H
+#define _SMB_LDAP_H
+
+enum ldap_request_tag {
+ LDAP_TAG_BindRequest = 0,
+ LDAP_TAG_BindResponse = 1,
+ LDAP_TAG_UnbindRequest = 2,
+ LDAP_TAG_SearchRequest = 3,
+ LDAP_TAG_SearchResultEntry = 4,
+ LDAP_TAG_SearchResultDone = 5,
+ LDAP_TAG_ModifyRequest = 6,
+ LDAP_TAG_ModifyResponse = 7,
+ LDAP_TAG_AddRequest = 8,
+ LDAP_TAG_AddResponse = 9,
+ LDAP_TAG_DelRequest = 10,
+ LDAP_TAG_DelResponse = 11,
+ LDAP_TAG_ModifyDNRequest = 12,
+ LDAP_TAG_ModifyDNResponse = 13,
+ LDAP_TAG_CompareRequest = 14,
+ LDAP_TAG_CompareResponse = 15,
+ LDAP_TAG_AbandonRequest = 16,
+ LDAP_TAG_SearchResultReference = 19,
+ LDAP_TAG_ExtendedRequest = 23,
+ LDAP_TAG_ExtendedResponse = 24
+};
+
+enum ldap_auth_mechanism {
+ LDAP_AUTH_MECH_SIMPLE = 0,
+ LDAP_AUTH_MECH_SASL = 3
+};
+
+struct ldap_Result {
+ int resultcode;
+ const char *dn;
+ const char *errormessage;
+ const char *referral;
+};
+
+struct ldap_attribute {
+ const char *name;
+ int num_values;
+ DATA_BLOB *values;
+};
+
+struct ldap_BindRequest {
+ int version;
+ const char *dn;
+ enum ldap_auth_mechanism mechanism;
+ union {
+ const char *password;
+ struct {
+ const char *mechanism;
+ DATA_BLOB creds;
+ } SASL;
+ } creds;
+};
+
+struct ldap_BindResponse {
+ struct ldap_Result response;
+ union {
+ DATA_BLOB credentials;
+ } SASL_Credentials;
+};
+
+struct ldap_UnbindRequest {
+};
+
+enum ldap_scope {
+ LDAP_SEARCH_SCOPE_BASE = 0,
+ LDAP_SEARCH_SCOPE_SINGLE = 1,
+ LDAP_SEARCH_SCOPE_SUB = 2
+};
+
+enum ldap_deref {
+ LDAP_DEREFERENCE_NEVER = 0,
+ LDAP_DEREFERENCE_IN_SEARCHING = 1,
+ LDAP_DEREFERENCE_FINDING_BASE = 2,
+ LDAP_DEREFERENCE_ALWAYS
+};
+
+struct ldap_SearchRequest {
+ const char *basedn;
+ enum ldap_scope scope;
+ enum ldap_deref deref;
+ uint32 timelimit;
+ uint32 sizelimit;
+ BOOL attributesonly;
+ char *filter;
+ int num_attributes;
+ const char **attributes;
+};
+
+struct ldap_SearchResEntry {
+ const char *dn;
+ int num_attributes;
+ struct ldap_attribute *attributes;
+};
+
+struct ldap_SearchResRef {
+ int num_referrals;
+ const char **referrals;
+};
+
+enum ldap_modify_type {
+ LDAP_MODIFY_NONE = -1,
+ LDAP_MODIFY_ADD = 0,
+ LDAP_MODIFY_DELETE = 1,
+ LDAP_MODIFY_REPLACE = 2
+};
+
+struct ldap_mod {
+ enum ldap_modify_type type;
+ struct ldap_attribute attrib;
+};
+
+struct ldap_ModifyRequest {
+ const char *dn;
+ int num_mods;
+ struct ldap_mod *mods;
+};
+
+struct ldap_AddRequest {
+ const char *dn;
+ int num_attributes;
+ struct ldap_attribute *attributes;
+};
+
+struct ldap_DelRequest {
+ const char *dn;
+};
+
+struct ldap_ModifyDNRequest {
+ const char *dn;
+ const char *newrdn;
+ BOOL deleteolddn;
+ const char *newsuperior;
+};
+
+struct ldap_CompareRequest {
+ const char *dn;
+ const char *attribute;
+ const char *value;
+};
+
+struct ldap_AbandonRequest {
+ uint32 messageid;
+};
+
+struct ldap_ExtendedRequest {
+ const char *oid;
+ DATA_BLOB value;
+};
+
+struct ldap_ExtendedResponse {
+ struct ldap_Result response;
+ const char *name;
+ DATA_BLOB value;
+};
+
+union ldap_Request {
+ struct ldap_BindRequest BindRequest;
+ struct ldap_BindResponse BindResponse;
+ struct ldap_UnbindRequest UnbindRequest;
+ struct ldap_SearchRequest SearchRequest;
+ struct ldap_SearchResEntry SearchResultEntry;
+ struct ldap_Result SearchResultDone;
+ struct ldap_SearchResRef SearchResultReference;
+ struct ldap_ModifyRequest ModifyRequest;
+ struct ldap_Result ModifyResponse;
+ struct ldap_AddRequest AddRequest;
+ struct ldap_Result AddResponse;
+ struct ldap_DelRequest DelRequest;
+ struct ldap_Result DelResponse;
+ struct ldap_ModifyDNRequest ModifyDNRequest;
+ struct ldap_Result ModifyDNResponse;
+ struct ldap_CompareRequest CompareRequest;
+ struct ldap_Result CompareResponse;
+ struct ldap_AbandonRequest AbandonRequest;
+ struct ldap_ExtendedRequest ExtendedRequest;
+ struct ldap_ExtendedResponse ExtendedResponse;
+};
+
+struct ldap_Control {
+ const char *oid;
+ BOOL critical;
+ DATA_BLOB value;
+};
+
+struct ldap_message {
+ TALLOC_CTX *mem_ctx;
+ uint32 messageid;
+ uint8 type;
+ union ldap_Request r;
+ int num_controls;
+ struct ldap_Control *controls;
+};
+
+struct ldap_queue_entry {
+ struct ldap_queue_entry *next, *prev;
+ int msgid;
+ struct ldap_message *msg;
+};
+
+struct ldap_connection {
+ TALLOC_CTX *mem_ctx;
+ int sock;
+ int next_msgid;
+ char *host;
+ uint16 port;
+ BOOL ldaps;
+
+ const char *auth_dn;
+ const char *simple_pw;
+
+ /* Current outstanding search entry */
+ int searchid;
+
+ /* List for incoming search entries */
+ struct ldap_queue_entry *search_entries;
+
+ /* Outstanding LDAP requests that have not yet been replied to */
+ struct ldap_queue_entry *outstanding;
+};
+
+#endif
diff --git a/source4/libcli/util/asn1.c b/source4/libcli/util/asn1.c
index dcafb261ee..6ddce7882c 100644
--- a/source4/libcli/util/asn1.c
+++ b/source4/libcli/util/asn1.c
@@ -184,6 +184,14 @@ BOOL asn1_write_BOOLEAN2(ASN1_DATA *data, BOOL v)
return !data->has_error;
}
+BOOL asn1_read_BOOLEAN2(ASN1_DATA *data, BOOL *v)
+{
+ asn1_start_tag(data, ASN1_BOOLEAN);
+ asn1_read_uint8(data, (uint8 *)v);
+ asn1_end_tag(data);
+ return !data->has_error;
+}
+
/* check a BOOLEAN */
BOOL asn1_check_BOOLEAN(ASN1_DATA *data, BOOL v)
{
@@ -216,51 +224,52 @@ BOOL asn1_load(ASN1_DATA *data, DATA_BLOB blob)
return True;
}
-/* read from a ASN1 buffer, advancing the buffer pointer */
-BOOL asn1_read(ASN1_DATA *data, void *p, int len)
+/* Peek into an ASN1 buffer, not advancing the pointer */
+BOOL asn1_peek(ASN1_DATA *data, void *p, int len)
{
- if (len < 0 || data->ofs + len < data->ofs || data->ofs + len < len) {
- data->has_error = True;
+ if (len < 0 || data->ofs + len < data->ofs || data->ofs + len < len)
return False;
- }
- if (data->ofs + len > data->length) {
- data->has_error = True;
+ if (data->ofs + len > data->length)
return False;
- }
+
memcpy(p, data->data + data->ofs, len);
- data->ofs += len;
return True;
}
-/* read a uint8_t from a ASN1 buffer */
-BOOL asn1_read_uint8(ASN1_DATA *data, uint8_t *v)
-{
- return asn1_read(data, v, 1);
-}
-
/* read from a ASN1 buffer, advancing the buffer pointer */
-BOOL asn1_peek(ASN1_DATA *data, void *p, int len)
+BOOL asn1_read(ASN1_DATA *data, void *p, int len)
{
- if (len < 0 || data->ofs + len < data->ofs || data->ofs + len < len) {
+ if (!asn1_peek(data, p, len)) {
data->has_error = True;
return False;
}
- if (data->ofs + len > data->length) {
- data->has_error = True;
- return False;
- }
- memcpy(p, data->data + data->ofs, len);
+ data->ofs += len;
return True;
}
/* read a uint8_t from a ASN1 buffer */
+BOOL asn1_read_uint8(ASN1_DATA *data, uint8_t *v)
+{
+ return asn1_read(data, v, 1);
+}
+
BOOL asn1_peek_uint8(ASN1_DATA *data, uint8_t *v)
{
return asn1_peek(data, v, 1);
}
+BOOL asn1_peek_tag(ASN1_DATA *data, uint8_t tag)
+{
+ uint8_t b;
+
+ if (!asn1_peek(data, &b, sizeof(b)))
+ return False;
+
+ return (b == tag);
+}
+
/* start reading a nested asn1 structure */
BOOL asn1_start_tag(ASN1_DATA *data, uint8_t tag)
{
@@ -304,6 +313,89 @@ BOOL asn1_start_tag(ASN1_DATA *data, uint8_t tag)
return !data->has_error;
}
+#if 0
+static BOOL read_one_uint8(int sock, uint8_t *result, ASN1_DATA *data,
+ const struct timeval *endtime)
+{
+ if (read_data_until(sock, result, 1, endtime) != 1)
+ return False;
+
+ return asn1_write(data, result, 1);
+}
+
+/* Read a complete ASN sequence (ie LDAP result) from a socket */
+BOOL asn1_read_sequence_until(int sock, ASN1_DATA *data,
+ const struct timeval *endtime)
+{
+ uint8_t b;
+ size_t len;
+ char *buf;
+
+ ZERO_STRUCTP(data);
+
+ if (!read_one_uint8(sock, &b, data, endtime))
+ return False;
+
+ if (b != 0x30) {
+ data->has_error = True;
+ return False;
+ }
+
+ if (!read_one_uint8(sock, &b, data, endtime))
+ return False;
+
+ if (b & 0x80) {
+ int n = b & 0x7f;
+ if (!read_one_uint8(sock, &b, data, endtime))
+ return False;
+ len = b;
+ while (n > 1) {
+ if (!read_one_uint8(sock, &b, data, endtime))
+ return False;
+ len = (len<<8) | b;
+ n--;
+ }
+ } else {
+ len = b;
+ }
+
+ buf = malloc(len);
+ if (buf == NULL)
+ return False;
+
+ if (read_data_until(sock, buf, len, endtime) != len)
+ return False;
+
+ if (!asn1_write(data, buf, len))
+ return False;
+
+ free(buf);
+
+ data->ofs = 0;
+
+ return True;
+}
+#endif
+
+/* 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)
+{
+ 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 */
+ free(data.nesting);
+ return True;
+}
/* stop reading a tag */
BOOL asn1_end_tag(ASN1_DATA *data)
@@ -342,7 +434,7 @@ int asn1_tag_remaining(ASN1_DATA *data)
BOOL asn1_read_OID(ASN1_DATA *data, char **OID)
{
uint8_t b;
- char *oid = NULL;
+ char *tmp_oid = NULL;
TALLOC_CTX *mem_ctx = talloc_init("asn1_read_OID");
if (!mem_ctx) {
return False;
@@ -351,8 +443,8 @@ BOOL asn1_read_OID(ASN1_DATA *data, char **OID)
if (!asn1_start_tag(data, ASN1_OID)) return False;
asn1_read_uint8(data, &b);
- oid = talloc_asprintf(mem_ctx, "%u", b/40);
- oid = talloc_asprintf_append(mem_ctx, oid, " %u", b%40);
+ tmp_oid = talloc_asprintf(mem_ctx, "%u", b/40);
+ tmp_oid = talloc_asprintf_append(mem_ctx, tmp_oid, " %u", b%40);
while (!data->has_error && asn1_tag_remaining(data) > 0) {
uint_t v = 0;
@@ -360,12 +452,12 @@ BOOL asn1_read_OID(ASN1_DATA *data, char **OID)
asn1_read_uint8(data, &b);
v = (v<<7) | (b&0x7f);
} while (!data->has_error && b & 0x80);
- oid = talloc_asprintf_append(mem_ctx, oid, " %u", v);
+ tmp_oid = talloc_asprintf_append(mem_ctx, tmp_oid, " %u", v);
}
asn1_end_tag(data);
- *OID = strdup(oid);
+ *OID = strdup(tmp_oid);
talloc_destroy(mem_ctx);
return (*OID && !data->has_error);
@@ -439,6 +531,20 @@ BOOL asn1_read_Integer(ASN1_DATA *data, int *i)
}
+/* read an interger */
+BOOL asn1_read_enumerated(ASN1_DATA *data, int *v)
+{
+ *v = 0;
+
+ if (!asn1_start_tag(data, ASN1_ENUMERATED)) return False;
+ while (asn1_tag_remaining(data)>0) {
+ uint8_t b;
+ asn1_read_uint8(data, &b);
+ *v = (*v << 8) + b;
+ }
+ return asn1_end_tag(data);
+}
+
/* check a enumarted value is correct */
BOOL asn1_check_enumerated(ASN1_DATA *data, int v)
{