>From d35ece8106b231bf5f2fccab3f70309b649161ac Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Tue, 26 Jan 2016 13:07:48 +1300 Subject: [PATCH 01/11] ldb controls: base64 encode VLV response context strings Pair-programmed-with: Douglas Bagnall Signed-off-by: Garming Sam --- lib/ldb/common/ldb_controls.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/ldb/common/ldb_controls.c b/lib/ldb/common/ldb_controls.c index af056d0..500805c 100644 --- a/lib/ldb/common/ldb_controls.c +++ b/lib/ldb/common/ldb_controls.c @@ -310,14 +310,20 @@ char *ldb_control_to_string(TALLOC_CTX *mem_ctx, const struct ldb_control *contr struct ldb_vlv_resp_control *rep_control = talloc_get_type(control->data, struct ldb_vlv_resp_control); - res = talloc_asprintf(mem_ctx, "%s:%d:%d:%d:%d:%d:%s", + char *cookie; + const uint8_t *c = (uint8_t*) rep_control->contextId; + + cookie = ldb_base64_encode(mem_ctx, + rep_control->contextId, + rep_control->ctxid_len); + + res = talloc_asprintf(mem_ctx, "%s:%d:%d:%d:%d:%s", LDB_CONTROL_VLV_RESP_NAME, control->critical, rep_control->targetPosition, rep_control->contentCount, rep_control->vlv_result, - rep_control->ctxid_len, - rep_control->contextId); + cookie); return res; } -- 2.5.0 >From 3612f6de328c082a0e305463c69481fd6e49835d Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Tue, 22 Dec 2015 16:34:53 +1300 Subject: [PATCH 02/11] ldb controls: better error string for VLV control Pair-programmed-with: Garming Sam Signed-off-by: Douglas Bagnall --- lib/ldb/common/ldb_controls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ldb/common/ldb_controls.c b/lib/ldb/common/ldb_controls.c index 500805c..8d7b430 100644 --- a/lib/ldb/common/ldb_controls.c +++ b/lib/ldb/common/ldb_controls.c @@ -468,7 +468,7 @@ struct ldb_control *ldb_parse_control_from_string(struct ldb_context *ldb, TALLO } if ((ret < 4) || (crit < 0) || (crit > 1)) { - error_string = talloc_asprintf(mem_ctx, "invalid server_sort control syntax\n"); + error_string = talloc_asprintf(mem_ctx, "invalid VLV control syntax\n"); error_string = talloc_asprintf_append(error_string, " syntax: crit(b):bc(n):ac(n):[:ctxid(o)]\n"); error_string = talloc_asprintf_append(error_string, " note: b = boolean, n = number, s = string, o = b64 binary blob"); ldb_set_errstring(ldb, error_string); -- 2.5.0 >From e9ced8fabba31ec6abec3cd666eb195575e6c054 Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Tue, 22 Dec 2015 17:07:38 +1300 Subject: [PATCH 03/11] ldap VLV: use correct ASN.1 encoding for requests The search reference points (either an integer index or a string for comparison) are supposed to use ASN1_CONTEXT or ASN1_CONTEXT_SIMPLE (respectively) ASN.1 types. We were using these types, but we also put extra ones in too, which nobody else likes. Pair-programmed-with: Douglas Bagnall Signed-off-by: Garming Sam --- source4/libcli/ldap/ldap_controls.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/source4/libcli/ldap/ldap_controls.c b/source4/libcli/ldap/ldap_controls.c index 863e5b6..6a4af70 100644 --- a/source4/libcli/ldap/ldap_controls.c +++ b/source4/libcli/ldap/ldap_controls.c @@ -586,7 +586,7 @@ static bool decode_vlv_request(void *mem_ctx, DATA_BLOB in, void *_out) lvrc->type = 1; - if (!asn1_start_tag(data, ASN1_CONTEXT(1))) { + if (!asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(1))) { return false; } @@ -1007,10 +1007,6 @@ static bool encode_vlv_request(void *mem_ctx, void *in, DATA_BLOB *out) return false; } - if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) { - return false; - } - if (!asn1_write_Integer(data, lvrc->match.byOffset.offset)) { return false; } @@ -1019,19 +1015,15 @@ static bool encode_vlv_request(void *mem_ctx, void *in, DATA_BLOB *out) return false; } - if (!asn1_pop_tag(data)) { /*SEQUENCE*/ - return false; - } - if (!asn1_pop_tag(data)) { /*CONTEXT*/ return false; } } else { - if (!asn1_push_tag(data, ASN1_CONTEXT(1))) { + if (!asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(1))) { return false; } - if (!asn1_write_OctetString(data, lvrc->match.gtOrEq.value, lvrc->match.gtOrEq.value_len)) { + if (!asn1_write(data, lvrc->match.gtOrEq.value, lvrc->match.gtOrEq.value_len)) { return false; } -- 2.5.0 >From 906de8bc3eabe26932baa762892960527f0820b2 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Tue, 22 Dec 2015 17:10:14 +1300 Subject: [PATCH 04/11] ldap VLV: memdup, not strdup VLV context_id The context ID is not a text string, it is an opaque binary field. Pair-programmed-with: Garming Sam Signed-off-by: Douglas Bagnall --- source4/libcli/ldap/ldap_controls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source4/libcli/ldap/ldap_controls.c b/source4/libcli/ldap/ldap_controls.c index 6a4af70..0a23cc7 100644 --- a/source4/libcli/ldap/ldap_controls.c +++ b/source4/libcli/ldap/ldap_controls.c @@ -675,7 +675,7 @@ static bool decode_vlv_response(void *mem_ctx, DATA_BLOB in, void *_out) if (!asn1_read_OctetString(data, mem_ctx, &context_id)) { return false; } - lvrc->contextId = talloc_strndup(lvrc, (const char *)context_id.data, context_id.length); + lvrc->contextId = talloc_memdup(lvrc, (const char *)context_id.data, context_id.length); if (!lvrc->contextId) { return false; } -- 2.5.0 >From c244680911b1468e8e7295fa0784d5a53af33fca Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Wed, 23 Dec 2015 17:34:15 +1300 Subject: [PATCH 05/11] vlv: better syntax for parsing greater than or equal strings This makes the gt_eq case different from the indexed case in the eyes of sscanf(). Pair-programmed-with: Garming Sam Signed-off-by: Douglas Bagnall --- lib/ldb/common/ldb_controls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ldb/common/ldb_controls.c b/lib/ldb/common/ldb_controls.c index 8d7b430..c760540 100644 --- a/lib/ldb/common/ldb_controls.c +++ b/lib/ldb/common/ldb_controls.c @@ -464,7 +464,7 @@ struct ldb_control *ldb_parse_control_from_string(struct ldb_context *ldb, TALLO p = &(control_strings[sizeof(LDB_CONTROL_VLV_REQ_NAME)]); ret = sscanf(p, "%d:%d:%d:%d:%d:%1023[^$]", &crit, &bc, &ac, &os, &cc, ctxid); if (ret < 5) { - ret = sscanf(p, "%d:%d:%d:%1023[^:]:%1023[^$]", &crit, &bc, &ac, attr, ctxid); + ret = sscanf(p, "%d:%d:%d:>=%1023[^:]:%1023[^$]", &crit, &bc, &ac, attr, ctxid); } if ((ret < 4) || (crit < 0) || (crit > 1)) { -- 2.5.0 >From a995d0c4ee603f0f2e72d3a4ba53b3b61ff8cc13 Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Thu, 17 Dec 2015 10:33:54 +1300 Subject: [PATCH 06/11] ldap: fix search control rule identifiers ASN.1 type Wireshark and Windows both expect matching rule identifiers to be given the ContextSimple type identifier instead of the Octet String. As far as we can tell this is not formally specified anywhere. Pair-programmed-with: Douglas Bagnall Signed-off-by: Garming Sam --- source4/libcli/ldap/ldap_controls.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source4/libcli/ldap/ldap_controls.c b/source4/libcli/ldap/ldap_controls.c index 0a23cc7..98e690d 100644 --- a/source4/libcli/ldap/ldap_controls.c +++ b/source4/libcli/ldap/ldap_controls.c @@ -757,7 +757,8 @@ static bool encode_server_sort_request(void *mem_ctx, void *in, DATA_BLOB *out) } if (lssc[num]->orderingRule) { - if (!asn1_write_OctetString(data, lssc[num]->orderingRule, strlen(lssc[num]->orderingRule))) { + DATA_BLOB order = data_blob_string_const(lssc[num]->orderingRule); + if (!asn1_write_ContextSimple(data, 0, &order)) { return false; } } -- 2.5.0 >From 0d73ab1bb27634ca9b668c4b81e19fbba5805d71 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Wed, 30 Dec 2015 12:07:35 +1300 Subject: [PATCH 07/11] ASN1: use a talloc context in read_contextSimple Pair-programmed-with: Garming Sam Signed-off-by: Douglas Bagnall --- lib/util/asn1.c | 5 +++-- lib/util/asn1.h | 2 +- libcli/ldap/ldap_message.c | 10 +++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/util/asn1.c b/lib/util/asn1.c index 9d6d416..d81e50f 100644 --- a/lib/util/asn1.c +++ b/lib/util/asn1.c @@ -927,7 +927,8 @@ bool asn1_read_OctetString(struct asn1_data *data, TALLOC_CTX *mem_ctx, DATA_BLO return false; } -bool asn1_read_ContextSimple(struct asn1_data *data, uint8_t num, DATA_BLOB *blob) +bool asn1_read_ContextSimple(struct asn1_data *data, TALLOC_CTX *mem_ctx, uint8_t num, + DATA_BLOB *blob) { int len; ZERO_STRUCTP(blob); @@ -937,7 +938,7 @@ bool asn1_read_ContextSimple(struct asn1_data *data, uint8_t num, DATA_BLOB *blo data->has_error = true; return false; } - *blob = data_blob(NULL, len); + *blob = data_blob_talloc(mem_ctx, NULL, len); if ((len != 0) && (!blob->data)) { data->has_error = true; return false; diff --git a/lib/util/asn1.h b/lib/util/asn1.h index 95e7dbf..ddd6986 100644 --- a/lib/util/asn1.h +++ b/lib/util/asn1.h @@ -87,7 +87,7 @@ bool asn1_check_OID(struct asn1_data *data, const char *OID); bool asn1_read_LDAPString(struct asn1_data *data, TALLOC_CTX *mem_ctx, char **s); bool asn1_read_GeneralString(struct asn1_data *data, TALLOC_CTX *mem_ctx, char **s); bool asn1_read_OctetString(struct asn1_data *data, TALLOC_CTX *mem_ctx, DATA_BLOB *blob); -bool asn1_read_ContextSimple(struct asn1_data *data, uint8_t num, DATA_BLOB *blob); +bool asn1_read_ContextSimple(struct asn1_data *data, TALLOC_CTX *mem_ctx, uint8_t num, DATA_BLOB *blob); bool asn1_read_implicit_Integer(struct asn1_data *data, int *i); bool asn1_read_Integer(struct asn1_data *data, int *i); bool asn1_read_BitString(struct asn1_data *data, TALLOC_CTX *mem_ctx, DATA_BLOB *blob, uint8_t *padding); diff --git a/libcli/ldap/ldap_message.c b/libcli/ldap/ldap_message.c index c89705b..9546dce 100644 --- a/libcli/ldap/ldap_message.c +++ b/libcli/ldap/ldap_message.c @@ -1232,7 +1232,7 @@ _PUBLIC_ NTSTATUS ldap_decode(struct asn1_data *data, if (!ldap_decode_response(msg, data, &r->response)) goto prot_err; if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(7))) { DATA_BLOB tmp_blob = data_blob(NULL, 0); - if (!asn1_read_ContextSimple(data, 7, &tmp_blob)) goto prot_err; + if (!asn1_read_ContextSimple(data, msg, 7, &tmp_blob)) goto prot_err; r->SASL.secblob = talloc(msg, DATA_BLOB); if (!r->SASL.secblob) { return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR); @@ -1501,7 +1501,7 @@ _PUBLIC_ NTSTATUS ldap_decode(struct asn1_data *data, msg->type = LDAP_TAG_ExtendedRequest; if (!asn1_start_tag(data,tag)) goto prot_err; - if (!asn1_read_ContextSimple(data, 0, &tmp_blob)) { + if (!asn1_read_ContextSimple(data, msg, 0, &tmp_blob)) { goto prot_err; } r->oid = blob2string_talloc(msg, tmp_blob); @@ -1511,7 +1511,7 @@ _PUBLIC_ NTSTATUS ldap_decode(struct asn1_data *data, } if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(1))) { - if (!asn1_read_ContextSimple(data, 1, &tmp_blob)) goto prot_err; + if (!asn1_read_ContextSimple(data, msg, 1, &tmp_blob)) goto prot_err; r->value = talloc(msg, DATA_BLOB); if (!r->value) { return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR); @@ -1535,7 +1535,7 @@ _PUBLIC_ NTSTATUS ldap_decode(struct asn1_data *data, if (!ldap_decode_response(msg, data, &r->response)) goto prot_err; if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(10))) { - if (!asn1_read_ContextSimple(data, 1, &tmp_blob)) goto prot_err; + if (!asn1_read_ContextSimple(data, msg, 1, &tmp_blob)) goto prot_err; r->oid = blob2string_talloc(msg, tmp_blob); data_blob_free(&tmp_blob); if (!r->oid) { @@ -1546,7 +1546,7 @@ _PUBLIC_ NTSTATUS ldap_decode(struct asn1_data *data, } if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(11))) { - if (!asn1_read_ContextSimple(data, 1, &tmp_blob)) goto prot_err; + if (!asn1_read_ContextSimple(data, msg, 1, &tmp_blob)) goto prot_err; r->value = talloc(msg, DATA_BLOB); if (!r->value) { return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR); -- 2.5.0 >From d01df4b546dd496bfeeb49a533753649a4ca7441 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Fri, 4 Mar 2016 14:46:46 +1300 Subject: [PATCH 08/11] ldap VLV: correct ASN1 parsing of VLV requests As with the encoding, the ASN1_CONTEXT tag isn't followed by an ASN1_SEQUENCE, though you wouldn't think that from reading the specification. Pair-programmed-with: Douglas Bagnall --- source4/libcli/ldap/ldap_controls.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/source4/libcli/ldap/ldap_controls.c b/source4/libcli/ldap/ldap_controls.c index 98e690d..4192e42 100644 --- a/source4/libcli/ldap/ldap_controls.c +++ b/source4/libcli/ldap/ldap_controls.c @@ -117,9 +117,9 @@ static bool decode_server_sort_request(void *mem_ctx, DATA_BLOB in, void *_out) if (!lssc [num]->attributeName) { return false; } - - if (asn1_peek_tag(data, ASN1_OCTET_STRING)) { - if (!asn1_read_OctetString(data, mem_ctx, &rule)) { + + if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(0))) { + if (!asn1_read_ContextSimple(data, mem_ctx, 0, &rule)) { return false; } lssc[num]->orderingRule = talloc_strndup(lssc[num], (const char *)rule.data, rule.length); @@ -557,12 +557,8 @@ static bool decode_vlv_request(void *mem_ctx, DATA_BLOB in, void *_out) if (asn1_peek_tag(data, ASN1_CONTEXT(0))) { lvrc->type = 0; - - if (!asn1_start_tag(data, ASN1_CONTEXT(0))) { - return false; - } - if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) { + if (!asn1_start_tag(data, ASN1_CONTEXT(0))) { return false; } @@ -574,10 +570,6 @@ static bool decode_vlv_request(void *mem_ctx, DATA_BLOB in, void *_out) return false; } - if (!asn1_end_tag(data)) { /*SEQUENCE*/ - return false; - } - if (!asn1_end_tag(data)) { /*CONTEXT*/ return false; } @@ -586,13 +578,10 @@ static bool decode_vlv_request(void *mem_ctx, DATA_BLOB in, void *_out) lvrc->type = 1; - if (!asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(1))) { + if (!asn1_read_ContextSimple(data, mem_ctx, 1, &assertion_value)){ return false; } - if (!asn1_read_OctetString(data, mem_ctx, &assertion_value)) { - return false; - } lvrc->match.gtOrEq.value_len = assertion_value.length; if (lvrc->match.gtOrEq.value_len) { lvrc->match.gtOrEq.value = talloc_memdup(lvrc, assertion_value.data, assertion_value.length); @@ -603,10 +592,6 @@ static bool decode_vlv_request(void *mem_ctx, DATA_BLOB in, void *_out) } else { lvrc->match.gtOrEq.value = NULL; } - - if (!asn1_end_tag(data)) { /*CONTEXT*/ - return false; - } } if (asn1_peek_tag(data, ASN1_OCTET_STRING)) { -- 2.5.0 >From a1a98f25dbda61dc7be9d57b10de16b364ac619a Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Tue, 26 Jan 2016 13:33:15 +1300 Subject: [PATCH 09/11] ldb controls: use uint8_t* for contextID binary blob It is never a readable string. Signed-off-by: Douglas Bagnall Signed-off-by: Garming Sam --- lib/ldb/common/ldb_controls.c | 5 ++--- lib/ldb/include/ldb.h | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/ldb/common/ldb_controls.c b/lib/ldb/common/ldb_controls.c index c760540..343c813 100644 --- a/lib/ldb/common/ldb_controls.c +++ b/lib/ldb/common/ldb_controls.c @@ -311,10 +311,9 @@ char *ldb_control_to_string(TALLOC_CTX *mem_ctx, const struct ldb_control *contr struct ldb_vlv_resp_control); char *cookie; - const uint8_t *c = (uint8_t*) rep_control->contextId; cookie = ldb_base64_encode(mem_ctx, - rep_control->contextId, + (char *)rep_control->contextId, rep_control->ctxid_len); res = talloc_asprintf(mem_ctx, "%s:%d:%d:%d:%d:%s", @@ -497,7 +496,7 @@ struct ldb_control *ldb_parse_control_from_string(struct ldb_context *ldb, TALLO } if (ctxid[0]) { control->ctxid_len = ldb_base64_decode(ctxid); - control->contextId = (char *)talloc_memdup(control, ctxid, control->ctxid_len); + control->contextId = talloc_memdup(control, ctxid, control->ctxid_len); } else { control->ctxid_len = 0; control->contextId = NULL; diff --git a/lib/ldb/include/ldb.h b/lib/ldb/include/ldb.h index e715b92..7422d46 100644 --- a/lib/ldb/include/ldb.h +++ b/lib/ldb/include/ldb.h @@ -849,7 +849,7 @@ struct ldb_vlv_req_control { } gtOrEq; } match; int ctxid_len; - char *contextId; + uint8_t *contextId; }; struct ldb_vlv_resp_control { @@ -857,7 +857,7 @@ struct ldb_vlv_resp_control { int contentCount; int vlv_result; int ctxid_len; - char *contextId; + uint8_t *contextId; }; struct ldb_verify_name_control { -- 2.5.0 >From 0cc6d56ac25c72f0f81b2e263b39510e49ab05cb Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Mon, 11 Jan 2016 11:36:07 +1300 Subject: [PATCH 10/11] asn1: make readContextSimple() add a NUL byte Pair-programmed-with: Garming Sam Signed-off-by: Douglas Bagnall --- lib/util/asn1.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/util/asn1.c b/lib/util/asn1.c index d81e50f..d3b46aa 100644 --- a/lib/util/asn1.c +++ b/lib/util/asn1.c @@ -938,12 +938,14 @@ bool asn1_read_ContextSimple(struct asn1_data *data, TALLOC_CTX *mem_ctx, uint8_ data->has_error = true; return false; } - *blob = data_blob_talloc(mem_ctx, NULL, len); + *blob = data_blob_talloc(mem_ctx, NULL, len + 1); if ((len != 0) && (!blob->data)) { data->has_error = true; return false; } if (!asn1_read(data, blob->data, len)) return false; + blob->length--; + blob->data[len] = 0; return asn1_end_tag(data); } -- 2.5.0 >From f28a71f648dbba3f73f28dd54d1b843dcbb50b24 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Fri, 29 Jan 2016 17:47:45 +1300 Subject: [PATCH 11/11] ldb_controls: add base64 option to VLV The Samba control syntax limits the range of valid search terms for VLV's gt_eq mode. To get around that, we allow base64 encoded strings using the syntax 'base64>=Zm9vCg==' rather than '>=foo'. Signed-off-by: Douglas Bagnall --- lib/ldb/common/ldb_controls.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/ldb/common/ldb_controls.c b/lib/ldb/common/ldb_controls.c index 343c813..7da0cf0 100644 --- a/lib/ldb/common/ldb_controls.c +++ b/lib/ldb/common/ldb_controls.c @@ -462,13 +462,25 @@ struct ldb_control *ldb_parse_control_from_string(struct ldb_context *ldb, TALLO ctxid[0] = '\0'; p = &(control_strings[sizeof(LDB_CONTROL_VLV_REQ_NAME)]); ret = sscanf(p, "%d:%d:%d:%d:%d:%1023[^$]", &crit, &bc, &ac, &os, &cc, ctxid); - if (ret < 5) { + /* We allow 2 ways to encode the GT_EQ case, because the + comparison string might contain null bytes or colons, which + would break sscanf (or indeed any parsing mechanism). */ + if (ret == 3) { ret = sscanf(p, "%d:%d:%d:>=%1023[^:]:%1023[^$]", &crit, &bc, &ac, attr, ctxid); } - + if (ret == 3) { + int len; + ret = sscanf(p, "%d:%d:%d:base64>=%1023[^:]:%1023[^$]", &crit, &bc, &ac, attr, ctxid); + len = ldb_base64_decode(attr); + if (len < 0) { + ret = -1; + } + } + if ((ret < 4) || (crit < 0) || (crit > 1)) { error_string = talloc_asprintf(mem_ctx, "invalid VLV control syntax\n"); - error_string = talloc_asprintf_append(error_string, " syntax: crit(b):bc(n):ac(n):[:ctxid(o)]\n"); + error_string = talloc_asprintf_append(error_string, " syntax: crit(b):bc(n):ac(n):" + "{os(n):cc(n)|>=val(s)|base64>=val(o)}[:ctxid(o)]\n"); error_string = talloc_asprintf_append(error_string, " note: b = boolean, n = number, s = string, o = b64 binary blob"); ldb_set_errstring(ldb, error_string); talloc_free(error_string); -- 2.5.0