/* * PROJECT: ReactOS Kernel * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) * PURPOSE: Security client support routines * COPYRIGHT: Copyright Alex Ionescu */ /* INCLUDES *******************************************************************/ #include #define NDEBUG #include /* PRIVATE FUNCTIONS **********************************************************/ /** * @brief * Creates a client security context based upon an access token. * * @param[in] Token * A valid token object. * * @param[in] ClientSecurityQos * The Quality of Service (QoS) of a client security context. * * @param[in] ServerIsRemote * If the client is a remote server (TRUE), the function will retrieve the * control information of an access token, that is, we're doing delegation * and that the server isn't local. * * @param[in] TokenType * Type of token. * * @param[in] ThreadEffectiveOnly * If set to TRUE, the client wants that the current thread wants to modify * (enable or disable) privileges and groups. * * @param[in] ImpersonationLevel * Security impersonation level filled in the QoS context. * * @param[out] ClientContext * The returned security client context. * * @return * Returns STATUS_SUCCESS if client security creation has completed successfully. * STATUS_INVALID_PARAMETER is returned if one or more of the parameters are bogus. * STATUS_BAD_IMPERSONATION_LEVEL is returned if the current impersonation level * within QoS context doesn't meet with the conditions required. A failure * NTSTATUS code is returned otherwise. */ NTSTATUS NTAPI SepCreateClientSecurity( _In_ PACCESS_TOKEN Token, _In_ PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos, _In_ BOOLEAN ServerIsRemote, _In_ TOKEN_TYPE TokenType, _In_ BOOLEAN ThreadEffectiveOnly, _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, _Out_ PSECURITY_CLIENT_CONTEXT ClientContext) { NTSTATUS Status; PACCESS_TOKEN NewToken; PAGED_CODE(); /* Check for bogus impersonation level */ if (!VALID_IMPERSONATION_LEVEL(ClientSecurityQos->ImpersonationLevel)) { /* Fail the call */ return STATUS_INVALID_PARAMETER; } /* Check what kind of token this is */ if (TokenType != TokenImpersonation) { /* On a primary token, if we do direct access, copy the flag from the QOS */ ClientContext->DirectAccessEffectiveOnly = ClientSecurityQos->EffectiveOnly; } else { /* This is an impersonation token, is the level ok? */ if (ClientSecurityQos->ImpersonationLevel > ImpersonationLevel) { /* Nope, fail */ return STATUS_BAD_IMPERSONATION_LEVEL; } /* Is the level too low, or are we doing something other than delegation remotely */ if ((ImpersonationLevel == SecurityAnonymous) || (ImpersonationLevel == SecurityIdentification) || ((ServerIsRemote) && (ImpersonationLevel != SecurityDelegation))) { /* Fail the call */ return STATUS_BAD_IMPERSONATION_LEVEL; } /* Pick either the thread setting or the QOS setting */ ClientContext->DirectAccessEffectiveOnly = ((ThreadEffectiveOnly) || (ClientSecurityQos->EffectiveOnly)) ? TRUE : FALSE; } /* Is this static tracking */ if (ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING) { /* Do not use direct access and make a copy */ ClientContext->DirectlyAccessClientToken = FALSE; Status = SeCopyClientToken(Token, ClientSecurityQos->ImpersonationLevel, KernelMode, &NewToken); if (!NT_SUCCESS(Status)) return Status; } else { /* Use direct access and check if this is local */ ClientContext->DirectlyAccessClientToken = TRUE; if (ServerIsRemote) { /* We are doing delegation, so make a copy of the control data */ SeGetTokenControlInformation(Token, &ClientContext->ClientTokenControl); } /* Keep the same token */ NewToken = Token; } /* Fill out the context and return success */ ClientContext->SecurityQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); ClientContext->SecurityQos.ImpersonationLevel = ClientSecurityQos->ImpersonationLevel; ClientContext->SecurityQos.ContextTrackingMode = ClientSecurityQos->ContextTrackingMode; ClientContext->SecurityQos.EffectiveOnly = ClientSecurityQos->EffectiveOnly; ClientContext->ServerIsRemote = ServerIsRemote; ClientContext->ClientToken = NewToken; return STATUS_SUCCESS; } /* PUBLIC FUNCTIONS ***********************************************************/ /** * @brief * Creates a client security context. * * @param[in] Thread * Thread object of the client where impersonation has to begin. * * @param[in] Qos * Quality of service to specify what kind of impersonation to be done. * * @param[in] RemoteClient * If set to TRUE, the client that we're going to impersonate is remote. * * @param[out] ClientContext * The returned security client context. * * @return * See SepCreateClientSecurity. */ NTSTATUS NTAPI SeCreateClientSecurity( _In_ PETHREAD Thread, _In_ PSECURITY_QUALITY_OF_SERVICE Qos, _In_ BOOLEAN RemoteClient, _Out_ PSECURITY_CLIENT_CONTEXT ClientContext) { TOKEN_TYPE TokenType; BOOLEAN ThreadEffectiveOnly; SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; PACCESS_TOKEN Token; NTSTATUS Status; PAGED_CODE(); /* Reference the correct token */ Token = PsReferenceEffectiveToken(Thread, &TokenType, &ThreadEffectiveOnly, &ImpersonationLevel); /* Create client security from it */ Status = SepCreateClientSecurity(Token, Qos, RemoteClient, TokenType, ThreadEffectiveOnly, ImpersonationLevel, ClientContext); /* Check if we failed or static tracking was used */ if (!(NT_SUCCESS(Status)) || (Qos->ContextTrackingMode == SECURITY_STATIC_TRACKING)) { /* Dereference our copy since it's not being used */ ObDereferenceObject(Token); } /* Return status */ return Status; } /** * @brief * Creates a client security context based upon the captured security * subject context. * * @param[in] SubjectContext * The captured subject context where client security is to be created * from. * * @param[in] ClientSecurityQos * Quality of service to specify what kind of impersonation to be done. * * @param[in] ServerIsRemote * If set to TRUE, the client that we're going to impersonate is remote. * * @param[out] ClientContext * The returned security client context. * * @return * See SepCreateClientSecurity. */ NTSTATUS NTAPI SeCreateClientSecurityFromSubjectContext( _In_ PSECURITY_SUBJECT_CONTEXT SubjectContext, _In_ PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos, _In_ BOOLEAN ServerIsRemote, _Out_ PSECURITY_CLIENT_CONTEXT ClientContext) { PACCESS_TOKEN Token; NTSTATUS Status; PAGED_CODE(); /* Get the right token and reference it */ Token = SeQuerySubjectContextToken(SubjectContext); ObReferenceObject(Token); /* Create the context */ Status = SepCreateClientSecurity(Token, ClientSecurityQos, ServerIsRemote, SubjectContext->ClientToken ? TokenImpersonation : TokenPrimary, FALSE, SubjectContext->ImpersonationLevel, ClientContext); /* Check if we failed or static tracking was used */ if (!(NT_SUCCESS(Status)) || (ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING)) { /* Dereference our copy since it's not being used */ ObDereferenceObject(Token); } /* Return status */ return Status; } /** * @brief * Extended function that impersonates a client. * * @param[in] ClientContext * A valid client context. * * @param[in] ServerThread * The thread where impersonation is to be done. * * @return * STATUS_SUCCESS is returned if the calling thread successfully impersonates * the client. A failure NTSTATUS code is returned otherwise. */ NTSTATUS NTAPI SeImpersonateClientEx( _In_ PSECURITY_CLIENT_CONTEXT ClientContext, _In_opt_ PETHREAD ServerThread) { BOOLEAN EffectiveOnly; PAGED_CODE(); /* Check if direct access is requested */ if (!ClientContext->DirectlyAccessClientToken) { /* No, so get the flag from QOS */ EffectiveOnly = ClientContext->SecurityQos.EffectiveOnly; } else { /* Yes, so see if direct access should be effective only */ EffectiveOnly = ClientContext->DirectAccessEffectiveOnly; } /* Use the current thread if one was not passed */ if (!ServerThread) ServerThread = PsGetCurrentThread(); /* Call the lower layer routine */ return PsImpersonateClient(ServerThread, ClientContext->ClientToken, TRUE, EffectiveOnly, ClientContext->SecurityQos.ImpersonationLevel); } /** * @brief * Impersonates a client user. * * @param[in] ClientContext * A valid client context. * * @param[in] ServerThread * The thread where impersonation is to be done. * * * @return * Nothing. */ VOID NTAPI SeImpersonateClient( _In_ PSECURITY_CLIENT_CONTEXT ClientContext, _In_opt_ PETHREAD ServerThread) { PAGED_CODE(); /* Call the new API */ SeImpersonateClientEx(ClientContext, ServerThread); } /* EOF */