r26192: Handle, test and implement the style of extended_dn requiest that MMC uses.
authorAndrew Bartlett <abartlet@samba.org>
Thu, 29 Nov 2007 07:00:04 +0000 (08:00 +0100)
committerStefan Metzmacher <metze@samba.org>
Fri, 21 Dec 2007 04:46:44 +0000 (05:46 +0100)
It appears that the control value is optional, implying type 0 responses.

Failing to parse this was causing LDAP disconnects with 'unavailable
critical extension'.

Andrew Bartlett
(This used to be commit 833dfc2f2af84c45f954e428c9ea6babf100ba92)

source4/dsdb/samdb/ldb_modules/extended_dn.c
source4/lib/ldb/common/ldb_controls.c
source4/libcli/ldap/ldap.c
source4/libcli/ldap/ldap.h
source4/libcli/ldap/ldap_client.c
source4/libcli/ldap/ldap_controls.c
testprogs/blackbox/test_ldb.sh

index d64673fdd53fde98903d0cbaa764a8f79cb4a428..b62e806398dbf4520d4a5312e74626c42abd2a92 100644 (file)
@@ -104,35 +104,58 @@ static bool inject_extended_dn(struct ldb_message *msg,
        const struct ldb_val *val;
        struct GUID guid;
        struct dom_sid *sid;
+       const DATA_BLOB *guid_blob;
+       const DATA_BLOB *sid_blob;
        char *object_guid;
        char *object_sid;
        char *new_dn;
 
-       /* retrieve object_guid */
-       guid = samdb_result_guid(msg, "objectGUID");
-       object_guid = GUID_string(msg, &guid);
-       if (!object_guid)
-               return false;
-
-       if (remove_guid)
-               ldb_msg_remove_attr(msg, "objectGUID");
+       guid_blob = ldb_msg_find_ldb_val(msg, "objectGUID");
+       sid_blob = ldb_msg_find_ldb_val(msg, "objectSID");
 
-       /* retrieve object_sid */
-       object_sid = NULL;
-       sid = samdb_result_dom_sid(msg, msg, "objectSID");
-       if (sid) {
-               object_sid = dom_sid_string(msg, sid);
-               if (!object_sid)
-                       return false;
-
-               if (remove_sid)
-                       ldb_msg_remove_attr(msg, "objectSID");
-       }
+       if (!guid_blob)
+               return false;
 
-       /* TODO: handle type */
        switch (type) {
                case 0:
+                       /* return things in hexadecimal format */
+                       if (sid_blob) {
+                               const char *lower_guid_hex = strlower_talloc(msg, data_blob_hex_string(msg, guid_blob));
+                               const char *lower_sid_hex = strlower_talloc(msg, data_blob_hex_string(msg, sid_blob));
+                               if (!lower_guid_hex || !lower_sid_hex) {
+                                       return false;
+                               }
+                               new_dn = talloc_asprintf(msg, "<GUID=%s>;<SID=%s>;%s",
+                                                        lower_guid_hex, 
+                                                        lower_sid_hex,
+                                                        ldb_dn_get_linearized(msg->dn));
+                       } else {
+                               const char *lower_guid_hex = strlower_talloc(msg, data_blob_hex_string(msg, guid_blob));
+                               if (!lower_guid_hex) {
+                                       return false;
+                               }
+                               new_dn = talloc_asprintf(msg, "<GUID=%s>;%s",
+                                                        lower_guid_hex, 
+                                                        ldb_dn_get_linearized(msg->dn));
+                       }
+
+                       break;
                case 1:
+                       /* retrieve object_guid */
+                       guid = samdb_result_guid(msg, "objectGUID");
+                       object_guid = GUID_string(msg, &guid);
+                       
+                       /* retrieve object_sid */
+                       object_sid = NULL;
+                       sid = samdb_result_dom_sid(msg, msg, "objectSID");
+                       if (sid) {
+                               object_sid = dom_sid_string(msg, sid);
+                               if (!object_sid)
+                                       return false;
+                               
+                       }
+                       
+                       /* Normal, sane format */
                        if (object_sid) {
                                new_dn = talloc_asprintf(msg, "<GUID=%s>;<SID=%s>;%s",
                                                         object_guid, object_sid,
@@ -147,8 +170,17 @@ static bool inject_extended_dn(struct ldb_message *msg,
                        return false;
        }
 
-       if (!new_dn)
+       if (!new_dn) {
                return false;
+       }
+
+       if (remove_guid) {
+               ldb_msg_remove_attr(msg, "objectGUID");
+       }
+
+       if (sid_blob && remove_sid) {
+               ldb_msg_remove_attr(msg, "objectSID");
+       }
 
        msg->dn = ldb_dn_new(msg, ldb, new_dn);
        if (! ldb_dn_validate(msg->dn))
@@ -201,7 +233,7 @@ error:
 static int extended_search(struct ldb_module *module, struct ldb_request *req)
 {
        struct ldb_control *control;
-       struct ldb_extended_dn_control *extended_ctrl;
+       struct ldb_extended_dn_control *extended_ctrl = NULL;
        struct ldb_control **saved_controls;
        struct extended_context *ac;
        struct ldb_request *down_req;
@@ -215,9 +247,11 @@ static int extended_search(struct ldb_module *module, struct ldb_request *req)
                return ldb_next_request(module, req);
        }
 
-       extended_ctrl = talloc_get_type(control->data, struct ldb_extended_dn_control);
-       if (!extended_ctrl) {
-               return LDB_ERR_PROTOCOL_ERROR;
+       if (control->data) {
+               extended_ctrl = talloc_get_type(control->data, struct ldb_extended_dn_control);
+               if (!extended_ctrl) {
+                       return LDB_ERR_PROTOCOL_ERROR;
+               }
        }
 
        ac = talloc(req, struct extended_context);
@@ -231,7 +265,11 @@ static int extended_search(struct ldb_module *module, struct ldb_request *req)
        ac->attrs = req->op.search.attrs;
        ac->remove_guid = false;
        ac->remove_sid = false;
-       ac->extended_type = extended_ctrl->type;
+       if (extended_ctrl) {
+               ac->extended_type = extended_ctrl->type;
+       } else {
+               ac->extended_type = 0;
+       }
 
        down_req = talloc_zero(req, struct ldb_request);
        if (down_req == NULL) {
index 137d35ac4e47b7d1b84d1020bcb43af7ddbd5996..e3f8551407a393042af393f3886c95f52857f910 100644 (file)
@@ -291,12 +291,22 @@ struct ldb_control **ldb_parse_control_strings(struct ldb_context *ldb, void *me
                        p = &(control_strings[i][12]);
                        ret = sscanf(p, "%d:%d", &crit, &type);
                        if ((ret != 2) || (crit < 0) || (crit > 1) || (type < 0) || (type > 1)) {
-                               error_string = talloc_asprintf(mem_ctx, "invalid extended_dn control syntax\n");
-                               error_string = talloc_asprintf_append(error_string, " syntax: crit(b):type(b)\n");
-                               error_string = talloc_asprintf_append(error_string, "   note: b = boolean");
-                               ldb_set_errstring(ldb, error_string);
-                               talloc_free(error_string);
-                               return NULL;
+                               ret = sscanf(p, "%d", &crit);
+                               if ((ret != 1) || (crit < 0) || (crit > 1)) {
+                                       error_string = talloc_asprintf(mem_ctx, "invalid extended_dn control syntax\n");
+                                       error_string = talloc_asprintf_append(error_string, " syntax: crit(b)[:type(i)]\n");
+                                       error_string = talloc_asprintf_append(error_string, "   note: b = boolean\n");
+                                       error_string = talloc_asprintf_append(error_string, "         i = integer\n");
+                                       error_string = talloc_asprintf_append(error_string, "   valid values are: 0 - hexadecimal representation\n");
+                                       error_string = talloc_asprintf_append(error_string, "                     1 - normal string representation");
+                                       ldb_set_errstring(ldb, error_string);
+                                       talloc_free(error_string);
+                                       return NULL;
+                               }
+                               control = NULL;
+                       } else {
+                               control = talloc(ctrl, struct ldb_extended_dn_control);
+                               control->type = type;
                        }
 
                        ctrl[i] = talloc(ctrl, struct ldb_control);
@@ -306,9 +316,7 @@ struct ldb_control **ldb_parse_control_strings(struct ldb_context *ldb, void *me
                        }
                        ctrl[i]->oid = LDB_CONTROL_EXTENDED_DN_OID;
                        ctrl[i]->critical = crit;
-                       control = talloc(ctrl[i], struct ldb_extended_dn_control);
-                       control->type = type;
-                       ctrl[i]->data = control;
+                       ctrl[i]->data = talloc_steal(ctrl[i], control);
 
                        continue;
                }
index 11689fbd7999901151571c022319e6b1d15bf939..34d715e3e5ba17c4c67296c147b48d379b7dc2ea 100644 (file)
@@ -1325,10 +1325,12 @@ NTSTATUS ldap_decode(struct asn1_data *data, struct ldap_message *msg)
        }
 
        msg->controls = NULL;
+       msg->controls_decoded = NULL;
 
        if (asn1_peek_tag(data, ASN1_CONTEXT(0))) {
                int i = 0;
                struct ldb_control **ctrl = NULL;
+               bool *decoded = NULL;
 
                asn1_start_tag(data, ASN1_CONTEXT(0));
 
@@ -1341,6 +1343,11 @@ NTSTATUS ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                                return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
                        }
 
+                       decoded = talloc_realloc(msg, decoded, bool, i+1);
+                       if (!decoded) {
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                       }
+
                        ctrl[i] = talloc(ctrl, struct ldb_control);
                        if (!ctrl[i]) {
                                return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
@@ -1352,12 +1359,15 @@ NTSTATUS ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                        
                        if (!ldap_decode_control_value(ctrl, value, ctrl[i])) {
                                if (ctrl[i]->critical) {
-                                       return NT_STATUS_LDAP(LDAP_UNAVAILABLE_CRITICAL_EXTENSION);
+                                       ctrl[i]->data = NULL;
+                                       decoded[i] = false;
+                                       i++;
                                } else {
                                        talloc_free(ctrl[i]);
                                        ctrl[i] = NULL;
                                }
                        } else {
+                               decoded[i] = true;
                                i++;
                        }
                }
@@ -1367,6 +1377,7 @@ NTSTATUS ldap_decode(struct asn1_data *data, struct ldap_message *msg)
                }
 
                msg->controls = ctrl;
+               msg->controls_decoded = decoded;
 
                asn1_end_tag(data);
        }
index e89322213aa891a4a43a556cd1bb2e580f0506cb..6f5e86744e0de8ab4d6db6296ec735cdc76543ae 100644 (file)
@@ -240,11 +240,13 @@ union ldap_Request {
        struct ldap_ExtendedResponse    ExtendedResponse;
 };
 
+
 struct ldap_message {
        int                     messageid;
        enum ldap_request_tag   type;
        union ldap_Request      r;
        struct ldb_control    **controls;
+       bool                   *controls_decoded;
 };
 
 struct event_context;
index fcb2d922148b106d8c608dd74ca21f0f007d128d..41e9c3719610135d0c15768435fd5fcd6c7a2a5b 100644 (file)
@@ -116,6 +116,7 @@ static void ldap_error_handler(void *private_data, NTSTATUS status)
 static void ldap_match_message(struct ldap_connection *conn, struct ldap_message *msg)
 {
        struct ldap_request *req;
+       int i;
 
        for (req=conn->pending; req; req=req->next) {
                if (req->messageid == msg->messageid) break;
@@ -132,6 +133,20 @@ static void ldap_match_message(struct ldap_connection *conn, struct ldap_message
                return;
        }
 
+       /* Check for undecoded critical extensions */
+       for (i=0; msg->controls && msg->controls[i]; i++) {
+               if (!msg->controls_decoded[i] && 
+                   msg->controls[i]->critical) {
+                       req->status = NT_STATUS_LDAP(LDAP_UNAVAILABLE_CRITICAL_EXTENSION);
+                       req->state = LDAP_REQUEST_DONE;
+                       DLIST_REMOVE(conn->pending, req);
+                       if (req->async.fn) {
+                               req->async.fn(req);
+                       }
+                       return;
+               }
+       }
+
        /* add to the list of replies received */
        talloc_steal(req, msg);
        req->replies = talloc_realloc(req, req->replies, 
index b7fd1ce17823a0244362efcf7784c6b5a698cabd..34e5cccf75bb6ab02f100e59c78972dbcc639849 100644 (file)
@@ -156,9 +156,16 @@ static bool decode_server_sort_request(void *mem_ctx, DATA_BLOB in, void **out)
 
 static bool decode_extended_dn_request(void *mem_ctx, DATA_BLOB in, void **out)
 {
-       struct asn1_data *data = asn1_init(mem_ctx);
+       struct asn1_data *data;
        struct ldb_extended_dn_control *ledc;
 
+       /* The content of this control is optional */
+       if (in.length == 0) {
+               *out = NULL;
+               return true;
+       }
+
+       data = asn1_init(mem_ctx);
        if (!data) return false;
 
        if (!asn1_load(data, in)) {
@@ -717,7 +724,14 @@ static bool encode_server_sort_request(void *mem_ctx, void *in, DATA_BLOB *out)
 static bool encode_extended_dn_request(void *mem_ctx, void *in, DATA_BLOB *out)
 {
        struct ldb_extended_dn_control *ledc = talloc_get_type(in, struct ldb_extended_dn_control);
-       struct asn1_data *data = asn1_init(mem_ctx);
+       struct asn1_data *data;
+
+       if (!in) {
+               *out = data_blob(NULL, 0);
+               return true;
+       }
+
+       data = asn1_init(mem_ctx);
 
        if (!data) return false;
 
index fd925fc99b10668d583607eb2430a883cbf23159..e35d3547d9d4c30c48c198d17864a6e493e5381e 100755 (executable)
@@ -71,11 +71,21 @@ failed=`expr $failed + 1`
 fi
 
 echo "Test Extended DN Control"
+nentries=`bin/ldbsearch $options $CONFIGURATION -H $p://$SERVER --controls=extended_dn:1 '(objectclass=user)' | grep sAMAccountName | wc -l`
+if [ $nentries -lt 1 ]; then
+echo "Extended DN Control test returned 0 items"
+failed=`expr $failed + 1`
+fi
 nentries=`bin/ldbsearch $options $CONFIGURATION -H $p://$SERVER --controls=extended_dn:1:0 '(objectclass=user)' | grep sAMAccountName | wc -l`
 if [ $nentries -lt 1 ]; then
 echo "Extended DN Control test returned 0 items"
 failed=`expr $failed + 1`
 fi
+nentries=`bin/ldbsearch $options $CONFIGURATION -H $p://$SERVER --controls=extended_dn:1:1 '(objectclass=user)' | grep sAMAccountName | wc -l`
+if [ $nentries -lt 1 ]; then
+echo "Extended DN Control test returned 0 items"
+failed=`expr $failed + 1`
+fi
 
 echo "Test Domain scope Control"
 nentries=`bin/ldbsearch $options $CONFIGURATION -H $p://$SERVER --controls=domain_scope:1 '(objectclass=user)' | grep sAMAccountName | wc -l`