s4:rpc_server/lsa: implement the policy security descriptor
authorStefan Metzmacher <metze@samba.org>
Wed, 25 Mar 2015 19:11:12 +0000 (19:11 +0000)
committerGünther Deschner <gd@samba.org>
Mon, 30 Mar 2015 11:41:25 +0000 (13:41 +0200)
We now check the requested access mask in OpenPolicy*()
and return NT_STATUS_ACCESS_DENIED if the request is not granted.

E.g. validating a domain trust via the Windows gui requires this
in order prompt the user for the credentials. Otherwise
we fail any other call with ACCESS_DENIED later and the
gui just displays a strange error message.

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Guenther Deschner <gd@samba.org>
source4/rpc_server/lsa/dcesrv_lsa.c
source4/rpc_server/lsa/lsa.h
source4/rpc_server/lsa/lsa_init.c
source4/rpc_server/lsa/lsa_lookup.c

index 2a465d4d8123e7c7f224595074990205ed57fec5..bbd2a727b0ac876479de75070ed9a69b61e805ce 100644 (file)
@@ -349,7 +349,9 @@ static NTSTATUS dcesrv_lsa_QuerySecurity(struct dcesrv_call_state *dce_call, TAL
                                         struct lsa_QuerySecurity *r)
 {
        struct dcesrv_handle *h;
-       struct security_descriptor *sd;
+       const struct security_descriptor *sd = NULL;
+       uint32_t access_granted = 0;
+       struct sec_desc_buf *sdbuf = NULL;
        NTSTATUS status;
        struct dom_sid *sid;
 
@@ -358,19 +360,38 @@ static NTSTATUS dcesrv_lsa_QuerySecurity(struct dcesrv_call_state *dce_call, TAL
        sid = &dce_call->conn->auth_state.session_info->security_token->sids[PRIMARY_USER_SID_INDEX];
 
        if (h->wire_handle.handle_type == LSA_HANDLE_POLICY) {
-               status = dcesrv_build_lsa_sd(mem_ctx, &sd, sid, 0);
-       } else  if (h->wire_handle.handle_type == LSA_HANDLE_ACCOUNT) {
-               status = dcesrv_build_lsa_sd(mem_ctx, &sd, sid,
+               struct lsa_policy_state *pstate = h->data;
+
+               sd = pstate->sd;
+               access_granted = pstate->access_mask;
+
+       } else if (h->wire_handle.handle_type == LSA_HANDLE_ACCOUNT) {
+               struct lsa_account_state *astate = h->data;
+               struct security_descriptor *_sd = NULL;
+
+               status = dcesrv_build_lsa_sd(mem_ctx, &_sd, sid,
                                             LSA_ACCOUNT_ALL_ACCESS);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               sd = _sd;
+               access_granted = astate->access_mask;
        } else {
                return NT_STATUS_INVALID_HANDLE;
        }
-       NT_STATUS_NOT_OK_RETURN(status);
 
-       (*r->out.sdbuf) = talloc(mem_ctx, struct sec_desc_buf);
-       NT_STATUS_HAVE_NO_MEMORY(*r->out.sdbuf);
+       sdbuf = talloc_zero(mem_ctx, struct sec_desc_buf);
+       if (sdbuf == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       status = security_descriptor_for_client(sdbuf, sd, r->in.sec_info,
+                                               access_granted, &sdbuf->sd);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
 
-       (*r->out.sdbuf)->sd = sd;
+       *r->out.sdbuf = sdbuf;
 
        return NT_STATUS_OK;
 }
@@ -421,7 +442,9 @@ static WERROR dcesrv_dssetup_DsRoleGetPrimaryDomainInformation(struct dcesrv_cal
                struct GUID domain_guid;
                struct lsa_policy_state *state;
 
-               NTSTATUS status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx, &state);
+               NTSTATUS status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx,
+                                                             0, /* we skip access checks */
+                                                             &state);
                if (!NT_STATUS_IS_OK(status)) {
                        return ntstatus_to_werror(status);
                }
index dc0c7d679506a4855838fb487cdef7c847d4ab75..5c00ba548b60ea19b06eb1132bbf342d39a666be 100644 (file)
@@ -41,7 +41,6 @@ struct lsa_policy_state {
        struct dcesrv_handle *handle;
        struct ldb_context *sam_ldb;
        struct ldb_context *pdb;
-       uint32_t access_mask;
        struct ldb_dn *domain_dn;
        struct ldb_dn *forest_dn;
        struct ldb_dn *builtin_dn;
@@ -56,6 +55,8 @@ struct lsa_policy_state {
        struct dom_sid *creator_owner_domain_sid;
        struct dom_sid *world_domain_sid;
        int mixed_domain;
+       struct security_descriptor *sd;
+       uint32_t access_mask;
 };
 
 enum lsa_handle {
index 4cf79a2346022f5d6c6ff7c8ab09bd12cd3910e5..5628c5b45b8b09892d86820ab56d65b12d61bb81 100644 (file)
 
 #include "rpc_server/lsa/lsa.h"
 
-NTSTATUS dcesrv_lsa_get_policy_state(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+/*
+ * This matches a Windows 2012R2 dc in
+ * a domain with function level 2012R2.
+ */
+#define DCESRV_LSA_POLICY_SD_SDDL \
+       "O:BAG:SY" \
+       "D:" \
+       "(D;;0x00000800;;;AN)" \
+       "(A;;GA;;;BA)" \
+       "(A;;GX;;;WD)" \
+       "(A;;0x00000801;;;AN)" \
+       "(A;;0x00001000;;;LS)" \
+       "(A;;0x00001000;;;NS)" \
+       "(A;;0x00001000;;;IS)" \
+       "(A;;0x00000801;;;S-1-15-2-1)"
+
+static const struct generic_mapping dcesrv_lsa_policy_mapping = {
+       LSA_POLICY_READ,
+       LSA_POLICY_WRITE,
+       LSA_POLICY_EXECUTE,
+       LSA_POLICY_ALL_ACCESS
+};
+
+NTSTATUS dcesrv_lsa_get_policy_state(struct dcesrv_call_state *dce_call,
+                                    TALLOC_CTX *mem_ctx,
+                                    uint32_t access_desired,
                                     struct lsa_policy_state **_state)
 {
+       struct auth_session_info *session_info = dce_call->conn->auth_state.session_info;
+       enum security_user_level security_level;
        struct lsa_policy_state *state;
        struct ldb_result *dom_res;
        const char *dom_attrs[] = {
@@ -37,7 +64,7 @@ NTSTATUS dcesrv_lsa_get_policy_state(struct dcesrv_call_state *dce_call, TALLOC_
        char *p;
        int ret;
 
-       state = talloc(mem_ctx, struct lsa_policy_state);
+       state = talloc_zero(mem_ctx, struct lsa_policy_state);
        if (!state) {
                return NT_STATUS_NO_MEMORY;
        }
@@ -143,6 +170,49 @@ NTSTATUS dcesrv_lsa_get_policy_state(struct dcesrv_call_state *dce_call, TALLOC_
                return NT_STATUS_NO_SUCH_DOMAIN;                
        }
 
+       state->sd = sddl_decode(state, DCESRV_LSA_POLICY_SD_SDDL,
+                               state->domain_sid);
+       if (state->sd == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       state->sd->dacl->revision = SECURITY_ACL_REVISION_NT4;
+
+       se_map_generic(&access_desired, &dcesrv_lsa_policy_mapping);
+       security_acl_map_generic(state->sd->dacl, &dcesrv_lsa_policy_mapping);
+
+       security_level = security_session_user_level(session_info, NULL);
+       if (security_level >= SECURITY_SYSTEM) {
+               /*
+                * The security descriptor doesn't allow system,
+                * but we want to allow system via ncalrpc as root.
+                */
+               state->access_mask = access_desired;
+               if (state->access_mask & SEC_FLAG_MAXIMUM_ALLOWED) {
+                       state->access_mask &= ~SEC_FLAG_MAXIMUM_ALLOWED;
+                       state->access_mask |= LSA_POLICY_ALL_ACCESS;
+               }
+       } else {
+               NTSTATUS status;
+
+               status = se_access_check(state->sd,
+                                        session_info->security_token,
+                                        access_desired,
+                                        &state->access_mask);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(2,("%s: access desired[0x%08X] rejected[0x%08X] - %s\n",
+                                __func__,
+                                (unsigned)access_desired,
+                                (unsigned)state->access_mask,
+                                nt_errstr(status)));
+                       return status;
+               }
+       }
+
+       DEBUG(10,("%s: access desired[0x%08X] granted[0x%08X] - success.\n",
+                 __func__,
+                (unsigned)access_desired,
+                (unsigned)state->access_mask));
+
        *_state = state;
 
        return NT_STATUS_OK;
@@ -172,7 +242,9 @@ NTSTATUS dcesrv_lsa_OpenPolicy2(struct dcesrv_call_state *dce_call, TALLOC_CTX *
                return NT_STATUS_INVALID_PARAMETER;
        }
 
-       status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx, &state);
+       status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx,
+                                            r->in.access_mask,
+                                            &state);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -184,9 +256,6 @@ NTSTATUS dcesrv_lsa_OpenPolicy2(struct dcesrv_call_state *dce_call, TALLOC_CTX *
 
        handle->data = talloc_steal(handle, state);
 
-       /* need to check the access mask against - need ACLs - fails
-          WSPP test */
-       state->access_mask = r->in.access_mask;
        state->handle = handle;
        *r->out.handle = handle->wire_handle;
 
index 8d92ba89d06610fe75555b4dacd23989dc4a2a5b..9a8fdfb74ca012dbff5e845b55683d26590681ee 100644 (file)
@@ -736,7 +736,9 @@ NTSTATUS dcesrv_lsa_LookupSids3(struct dcesrv_call_state *dce_call,
                DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
        }
 
-       status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx, &policy_state);
+       status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx,
+                                            0, /* we skip access checks */
+                                            &policy_state);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -962,7 +964,9 @@ NTSTATUS dcesrv_lsa_LookupNames4(struct dcesrv_call_state *dce_call, TALLOC_CTX
                DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
        }
 
-       status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx, &policy_state);
+       status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx,
+                                            0, /* we skip access checks */
+                                            &policy_state);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }