diff options
-rw-r--r-- | source3/include/tldap.h | 2 | ||||
-rw-r--r-- | source3/include/tldap_util.h | 27 | ||||
-rw-r--r-- | source3/lib/tldap_util.c | 261 | ||||
-rw-r--r-- | source3/torture/torture.c | 55 |
4 files changed, 345 insertions, 0 deletions
diff --git a/source3/include/tldap.h b/source3/include/tldap.h index 27027c1f25..1d920f8adc 100644 --- a/source3/include/tldap.h +++ b/source3/include/tldap.h @@ -273,4 +273,6 @@ void tldap_set_debug(struct tldap_context *ld, #define TLDAP_SCOPE_ONE (1) #define TLDAP_SCOPE_SUB (2) +#define TLDAP_CONTROL_PAGEDRESULTS "1.2.840.113556.1.4.319" + #endif diff --git a/source3/include/tldap_util.h b/source3/include/tldap_util.h index eb6b8f6d22..9b0393eefc 100644 --- a/source3/include/tldap_util.h +++ b/source3/include/tldap_util.h @@ -66,4 +66,31 @@ bool tldap_entry_has_attrvalue(struct tldap_message *msg, const DATA_BLOB blob); bool tldap_supports_control(struct tldap_context *ld, const char *oid); +struct tldap_control *tldap_add_control(TALLOC_CTX *mem_ctx, + struct tldap_control *ctrls, + int num_ctrls, + struct tldap_control *ctrl); +struct tldap_control *tldap_msg_findcontrol(struct tldap_message *msg, + const char *oid); + +struct tevent_req *tldap_search_paged_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tldap_context *ld, + const char *base, int scope, + const char *filter, + const char **attrs, + int num_attrs, + int attrsonly, + struct tldap_control *sctrls, + int num_sctrls, + struct tldap_control *cctrls, + int num_cctrls, + int timelimit, + int sizelimit, + int deref, + int page_size); +int tldap_search_paged_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct tldap_message **pmsg); + + #endif diff --git a/source3/lib/tldap_util.c b/source3/lib/tldap_util.c index 46540111c6..f4fffb57d4 100644 --- a/source3/lib/tldap_util.c +++ b/source3/lib/tldap_util.c @@ -538,3 +538,264 @@ bool tldap_supports_control(struct tldap_context *ld, const char *oid) return tldap_entry_has_attrvalue(rootdse, "supportedControl", data_blob_const(oid, strlen(oid))); } + +struct tldap_control *tldap_add_control(TALLOC_CTX *mem_ctx, + struct tldap_control *ctrls, + int num_ctrls, + struct tldap_control *ctrl) +{ + struct tldap_control *result; + + result = talloc_array(mem_ctx, struct tldap_control, num_ctrls+1); + if (result == NULL) { + return NULL; + } + memcpy(result, ctrls, sizeof(struct tldap_control) * num_ctrls); + result[num_ctrls] = *ctrl; + return result; +} + +/* + * Find a control returned by the server + */ +struct tldap_control *tldap_msg_findcontrol(struct tldap_message *msg, + const char *oid) +{ + struct tldap_control *controls; + int i, num_controls; + + tldap_msg_sctrls(msg, &num_controls, &controls); + + for (i=0; i<num_controls; i++) { + if (strcmp(controls[i].oid, oid) == 0) { + return &controls[i]; + } + } + return NULL; +} + +struct tldap_search_paged_state { + struct tevent_context *ev; + struct tldap_context *ld; + const char *base; + const char *filter; + int scope; + const char **attrs; + int num_attrs; + int attrsonly; + struct tldap_control *sctrls; + int num_sctrls; + struct tldap_control *cctrls; + int num_cctrls; + int timelimit; + int sizelimit; + int deref; + + int page_size; + struct asn1_data *asn1; + DATA_BLOB cookie; + struct tldap_message *result; +}; + +static struct tevent_req *tldap_ship_paged_search( + TALLOC_CTX *mem_ctx, + struct tldap_search_paged_state *state) +{ + struct tldap_control *pgctrl; + struct asn1_data *asn1; + + asn1 = asn1_init(state); + if (asn1 == NULL) { + return NULL; + } + asn1_push_tag(asn1, ASN1_SEQUENCE(0)); + asn1_write_Integer(asn1, state->page_size); + asn1_write_OctetString(asn1, state->cookie.data, state->cookie.length); + asn1_pop_tag(asn1); + if (asn1->has_error) { + TALLOC_FREE(asn1); + return NULL; + } + state->asn1 = asn1; + + pgctrl = &state->sctrls[state->num_sctrls-1]; + pgctrl->oid = TLDAP_CONTROL_PAGEDRESULTS; + pgctrl->critical = true; + if (!asn1_blob(state->asn1, &pgctrl->value)) { + TALLOC_FREE(asn1); + return NULL; + } + return tldap_search_send(mem_ctx, state->ev, state->ld, state->base, + state->scope, state->filter, state->attrs, + state->num_attrs, state->attrsonly, + state->sctrls, state->num_sctrls, + state->cctrls, state->num_cctrls, + state->timelimit, state->sizelimit, + state->deref); +} + +static void tldap_search_paged_done(struct tevent_req *subreq); + +struct tevent_req *tldap_search_paged_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tldap_context *ld, + const char *base, int scope, + const char *filter, + const char **attrs, + int num_attrs, + int attrsonly, + struct tldap_control *sctrls, + int num_sctrls, + struct tldap_control *cctrls, + int num_cctrls, + int timelimit, + int sizelimit, + int deref, + int page_size) +{ + struct tevent_req *req, *subreq; + struct tldap_search_paged_state *state; + struct tldap_control empty_control; + + req = tevent_req_create(mem_ctx, &state, + struct tldap_search_paged_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->ld = ld; + state->base = base; + state->filter = filter; + state->scope = scope; + state->attrs = attrs; + state->num_attrs = num_attrs; + state->attrsonly = attrsonly; + state->cctrls = cctrls; + state->num_cctrls = num_cctrls; + state->timelimit = timelimit; + state->sizelimit = sizelimit; + state->deref = deref; + + state->page_size = page_size; + state->asn1 = NULL; + state->cookie = data_blob_null; + + ZERO_STRUCT(empty_control); + + state->sctrls = tldap_add_control(state, sctrls, num_sctrls, + &empty_control); + if (tevent_req_nomem(state->sctrls, req)) { + return tevent_req_post(req, ev); + } + state->num_sctrls = num_sctrls+1; + + subreq = tldap_ship_paged_search(state, state); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, tldap_search_paged_done, req); + + return req; +} + +static void tldap_search_paged_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct tldap_search_paged_state *state = tevent_req_data( + req, struct tldap_search_paged_state); + struct asn1_data *asn1; + struct tldap_control *pgctrl; + int rc, size; + + rc = tldap_search_recv(subreq, state, &state->result); + if (rc != TLDAP_SUCCESS) { + TALLOC_FREE(subreq); + tevent_req_error(req, rc); + return; + } + + TALLOC_FREE(state->asn1); + + switch (tldap_msg_type(state->result)) { + case TLDAP_RES_SEARCH_ENTRY: + case TLDAP_RES_SEARCH_REFERENCE: + tevent_req_notify_callback(req); + return; + case TLDAP_RES_SEARCH_RESULT: + break; + default: + TALLOC_FREE(subreq); + tevent_req_error(req, TLDAP_PROTOCOL_ERROR); + return; + } + + TALLOC_FREE(subreq); + + /* We've finished one paged search, fire the next */ + + pgctrl = tldap_msg_findcontrol(state->result, + TLDAP_CONTROL_PAGEDRESULTS); + if (pgctrl == NULL) { + /* RFC2696 requires the server to return the control */ + tevent_req_error(req, TLDAP_PROTOCOL_ERROR); + return; + } + + TALLOC_FREE(state->cookie.data); + + asn1 = asn1_init(talloc_tos()); + if (asn1 == NULL) { + tevent_req_error(req, TLDAP_NO_MEMORY); + return; + } + + asn1_load_nocopy(asn1, pgctrl->value.data, pgctrl->value.length); + asn1_start_tag(asn1, ASN1_SEQUENCE(0)); + asn1_read_Integer(asn1, &size); + asn1_read_OctetString(asn1, state, &state->cookie); + asn1_end_tag(asn1); + if (asn1->has_error) { + tevent_req_error(req, TLDAP_DECODING_ERROR); + return; + } + TALLOC_FREE(asn1); + + if (state->cookie.length == 0) { + /* We're done, no cookie anymore */ + tevent_req_done(req); + return; + } + + TALLOC_FREE(state->result); + + subreq = tldap_ship_paged_search(state, state); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, tldap_search_paged_done, req); +} + +int tldap_search_paged_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct tldap_message **pmsg) +{ + struct tldap_search_paged_state *state = tevent_req_data( + req, struct tldap_search_paged_state); + int err; + + if (!tevent_req_is_in_progress(req) + && tevent_req_is_ldap_error(req, &err)) { + return err; + } + if (tevent_req_is_in_progress(req)) { + switch (tldap_msg_type(state->result)) { + case TLDAP_RES_SEARCH_ENTRY: + case TLDAP_RES_SEARCH_REFERENCE: + break; + default: + return TLDAP_PROTOCOL_ERROR; + } + } + *pmsg = talloc_move(mem_ctx, &state->result); + return TLDAP_SUCCESS; +} diff --git a/source3/torture/torture.c b/source3/torture/torture.c index 7a4a5fc46e..888010406f 100644 --- a/source3/torture/torture.c +++ b/source3/torture/torture.c @@ -5585,12 +5585,39 @@ static bool run_shortname_test(int dummy) return correct; } +static void pagedsearch_cb(struct tevent_req *req) +{ + int rc; + struct tldap_message *msg; + char *dn; + + rc = tldap_search_paged_recv(req, talloc_tos(), &msg); + if (rc != TLDAP_SUCCESS) { + d_printf("tldap_search_paged_recv failed: %s\n", + tldap_err2string(rc)); + return; + } + if (tldap_msg_type(msg) != TLDAP_RES_SEARCH_ENTRY) { + TALLOC_FREE(msg); + return; + } + if (!tldap_entry_dn(msg, &dn)) { + d_printf("tldap_entry_dn failed\n"); + return; + } + d_printf("%s\n", dn); + TALLOC_FREE(msg); +} + static bool run_tldap(int dummy) { struct tldap_context *ld; int fd, rc; NTSTATUS status; struct sockaddr_storage addr; + struct tevent_context *ev; + struct tevent_req *req; + char *basedn; if (!resolve_name(host, &addr, 0)) { d_printf("could not find host %s\n", host); @@ -5616,6 +5643,34 @@ static bool run_tldap(int dummy) return false; } + basedn = tldap_talloc_single_attribute( + tldap_rootdse(ld), "defaultNamingContext", talloc_tos()); + if (basedn == NULL) { + d_printf("no defaultNamingContext\n"); + return false; + } + d_printf("defaultNamingContext: %s\n", basedn); + + ev = tevent_context_init(talloc_tos()); + if (ev == NULL) { + d_printf("tevent_context_init failed\n"); + return false; + } + + req = tldap_search_paged_send(talloc_tos(), ev, ld, basedn, + TLDAP_SCOPE_SUB, "(objectclass=*)", + NULL, 0, 0, + NULL, 0, NULL, 0, 0, 0, 0, 5); + if (req == NULL) { + d_printf("tldap_search_paged_send failed\n"); + return false; + } + tevent_req_set_callback(req, pagedsearch_cb, NULL); + + tevent_req_poll(req, ev); + + TALLOC_FREE(req); + TALLOC_FREE(ld); return true; } |