auth/gensec: don't allow gensec_update[_ev] to be called on a subcontext
[vlendec/samba-autobuild/.git] / auth / gensec / gensec.c
index cf00a3632471f14ce8d2b63acfa2e166cdec886e..f3969b4129be2cfaebba441a77c415b7d6aff694 100644 (file)
@@ -325,13 +325,15 @@ _PUBLIC_ NTSTATUS gensec_update_ev(struct gensec_security *gensec_security,
                                   const DATA_BLOB in, DATA_BLOB *out)
 {
        NTSTATUS status;
-       const struct gensec_security_ops *ops = gensec_security->ops;
        TALLOC_CTX *frame = NULL;
        struct tevent_req *subreq = NULL;
        bool ok;
 
-       if (gensec_security->child_security != NULL) {
-               return NT_STATUS_INVALID_PARAMETER;
+       if (gensec_security->subcontext) {
+               /*
+                * gensec modules are not allowed to call the sync version.
+                */
+               return NT_STATUS_INTERNAL_ERROR;
        }
 
        frame = talloc_stackframe();
@@ -350,7 +352,7 @@ _PUBLIC_ NTSTATUS gensec_update_ev(struct gensec_security *gensec_security,
                tevent_loop_allow_nesting(ev);
        }
 
-       subreq = ops->update_send(frame, ev, gensec_security, in);
+       subreq = gensec_update_send(frame, ev, gensec_security, in);
        if (subreq == NULL) {
                status = NT_STATUS_NO_MEMORY;
                goto fail;
@@ -359,20 +361,7 @@ _PUBLIC_ NTSTATUS gensec_update_ev(struct gensec_security *gensec_security,
        if (!ok) {
                goto fail;
        }
-       status = ops->update_recv(subreq, out_mem_ctx, out);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto fail;
-       }
-
-       /*
-        * Because callers using the
-        * gensec_start_mech_by_auth_type() never call
-        * gensec_want_feature(), it isn't sensible for them
-        * to have to call gensec_have_feature() manually, and
-        * these are not points of negotiation, but are
-        * asserted by the client
-        */
-       status = gensec_verify_features(gensec_security);
+       status = gensec_update_recv(subreq, out_mem_ctx, out);
  fail:
        TALLOC_FREE(frame);
        return status;
@@ -403,6 +392,8 @@ struct gensec_update_state {
        DATA_BLOB out;
 };
 
+static void gensec_update_cleanup(struct tevent_req *req,
+                                 enum tevent_req_state req_state);
 static void gensec_update_done(struct tevent_req *subreq);
 
 /**
@@ -430,24 +421,52 @@ _PUBLIC_ struct tevent_req *gensec_update_send(TALLOC_CTX *mem_ctx,
        if (req == NULL) {
                return NULL;
        }
-
        state->ops = gensec_security->ops;
        state->gensec_security = gensec_security;
 
+       if (gensec_security->update_busy_ptr != NULL) {
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return tevent_req_post(req, ev);
+       }
+
        if (gensec_security->child_security != NULL) {
                tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
                return tevent_req_post(req, ev);
        }
 
+       gensec_security->update_busy_ptr = &state->gensec_security;
+       tevent_req_set_cleanup_fn(req, gensec_update_cleanup);
+
        subreq = state->ops->update_send(state, ev, gensec_security, in);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
        tevent_req_set_callback(subreq, gensec_update_done, req);
 
+       DBG_DEBUG("%s[%p]: subreq: %p\n", state->ops->name,
+                 state->gensec_security, subreq);
+
        return req;
 }
 
+static void gensec_update_cleanup(struct tevent_req *req,
+                                 enum tevent_req_state req_state)
+{
+       struct gensec_update_state *state =
+               tevent_req_data(req,
+               struct gensec_update_state);
+
+       if (state->gensec_security == NULL) {
+               return;
+       }
+
+       if (state->gensec_security->update_busy_ptr == &state->gensec_security) {
+               state->gensec_security->update_busy_ptr = NULL;
+       }
+
+       state->gensec_security = NULL;
+}
+
 static void gensec_update_done(struct tevent_req *subreq)
 {
        struct tevent_req *req =
@@ -457,15 +476,35 @@ static void gensec_update_done(struct tevent_req *subreq)
                tevent_req_data(req,
                struct gensec_update_state);
        NTSTATUS status;
+       const char *debug_subreq = NULL;
+
+       if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
+               /*
+                * We need to call tevent_req_print()
+                * before calling the _recv function,
+                * before tevent_req_received() was called.
+                * in order to print the pointer value of
+                * the subreq state.
+                */
+               debug_subreq = tevent_req_print(state, subreq);
+       }
 
        status = state->ops->update_recv(subreq, state, &state->out);
        TALLOC_FREE(subreq);
        state->status = status;
-       if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-               tevent_req_done(req);
+       if (GENSEC_UPDATE_IS_NTERROR(status)) {
+               DBG_INFO("%s[%p]: %s%s%s\n", state->ops->name,
+                        state->gensec_security, nt_errstr(status),
+                        debug_subreq ? " " : "",
+                        debug_subreq ? debug_subreq : "");
+               tevent_req_nterror(req, status);
                return;
        }
-       if (tevent_req_nterror(req, status)) {
+       DBG_DEBUG("%s[%p]: %s %s\n", state->ops->name,
+                 state->gensec_security, nt_errstr(status),
+                 debug_subreq);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+               tevent_req_done(req);
                return;
        }