1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Security client support routines 5 * COPYRIGHT: Copyright Alex Ionescu <alex@relsoft.net> 6 */ 7 8 /* INCLUDES *******************************************************************/ 9 10 #include <ntoskrnl.h> 11 #define NDEBUG 12 #include <debug.h> 13 14 /* PRIVATE FUNCTIONS **********************************************************/ 15 16 /** 17 * @brief 18 * Creates a client security context based upon an access token. 19 * 20 * @param[in] Token 21 * A valid token object. 22 * 23 * @param[in] ClientSecurityQos 24 * The Quality of Service (QoS) of a client security context. 25 * 26 * @param[in] ServerIsRemote 27 * If the client is a remote server (TRUE), the function will retrieve the 28 * control information of an access token, that is, we're doing delegation 29 * and that the server isn't local. 30 * 31 * @param[in] TokenType 32 * Type of token. 33 * 34 * @param[in] ThreadEffectiveOnly 35 * If set to TRUE, the client wants that the current thread wants to modify 36 * (enable or disable) privileges and groups. 37 * 38 * @param[in] ImpersonationLevel 39 * Security impersonation level filled in the QoS context. 40 * 41 * @param[out] ClientContext 42 * The returned security client context. 43 * 44 * @return 45 * Returns STATUS_SUCCESS if client security creation has completed successfully. 46 * STATUS_INVALID_PARAMETER is returned if one or more of the parameters are bogus. 47 * STATUS_BAD_IMPERSONATION_LEVEL is returned if the current impersonation level 48 * within QoS context doesn't meet with the conditions required. A failure 49 * NTSTATUS code is returned otherwise. 50 */ 51 NTSTATUS 52 NTAPI 53 SepCreateClientSecurity( 54 _In_ PACCESS_TOKEN Token, 55 _In_ PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos, 56 _In_ BOOLEAN ServerIsRemote, 57 _In_ TOKEN_TYPE TokenType, 58 _In_ BOOLEAN ThreadEffectiveOnly, 59 _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, 60 _Out_ PSECURITY_CLIENT_CONTEXT ClientContext) 61 { 62 NTSTATUS Status; 63 PACCESS_TOKEN NewToken; 64 PAGED_CODE(); 65 66 /* Check for bogus impersonation level */ 67 if (!VALID_IMPERSONATION_LEVEL(ClientSecurityQos->ImpersonationLevel)) 68 { 69 /* Fail the call */ 70 return STATUS_INVALID_PARAMETER; 71 } 72 73 /* Check what kind of token this is */ 74 if (TokenType != TokenImpersonation) 75 { 76 /* On a primary token, if we do direct access, copy the flag from the QOS */ 77 ClientContext->DirectAccessEffectiveOnly = ClientSecurityQos->EffectiveOnly; 78 } 79 else 80 { 81 /* This is an impersonation token, is the level ok? */ 82 if (ClientSecurityQos->ImpersonationLevel > ImpersonationLevel) 83 { 84 /* Nope, fail */ 85 return STATUS_BAD_IMPERSONATION_LEVEL; 86 } 87 88 /* Is the level too low, or are we doing something other than delegation remotely */ 89 if ((ImpersonationLevel == SecurityAnonymous) || 90 (ImpersonationLevel == SecurityIdentification) || 91 ((ServerIsRemote) && (ImpersonationLevel != SecurityDelegation))) 92 { 93 /* Fail the call */ 94 return STATUS_BAD_IMPERSONATION_LEVEL; 95 } 96 97 /* Pick either the thread setting or the QOS setting */ 98 ClientContext->DirectAccessEffectiveOnly = 99 ((ThreadEffectiveOnly) || (ClientSecurityQos->EffectiveOnly)) ? TRUE : FALSE; 100 } 101 102 /* Is this static tracking */ 103 if (ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING) 104 { 105 /* Do not use direct access and make a copy */ 106 ClientContext->DirectlyAccessClientToken = FALSE; 107 Status = SeCopyClientToken(Token, 108 ClientSecurityQos->ImpersonationLevel, 109 KernelMode, 110 &NewToken); 111 if (!NT_SUCCESS(Status)) 112 return Status; 113 } 114 else 115 { 116 /* Use direct access and check if this is local */ 117 ClientContext->DirectlyAccessClientToken = TRUE; 118 if (ServerIsRemote) 119 { 120 /* We are doing delegation, so make a copy of the control data */ 121 SeGetTokenControlInformation(Token, 122 &ClientContext->ClientTokenControl); 123 } 124 125 /* Keep the same token */ 126 NewToken = Token; 127 } 128 129 /* Fill out the context and return success */ 130 ClientContext->SecurityQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); 131 ClientContext->SecurityQos.ImpersonationLevel = ClientSecurityQos->ImpersonationLevel; 132 ClientContext->SecurityQos.ContextTrackingMode = ClientSecurityQos->ContextTrackingMode; 133 ClientContext->SecurityQos.EffectiveOnly = ClientSecurityQos->EffectiveOnly; 134 ClientContext->ServerIsRemote = ServerIsRemote; 135 ClientContext->ClientToken = NewToken; 136 return STATUS_SUCCESS; 137 } 138 139 /* PUBLIC FUNCTIONS ***********************************************************/ 140 141 /** 142 * @brief 143 * Creates a client security context. 144 * 145 * @param[in] Thread 146 * Thread object of the client where impersonation has to begin. 147 * 148 * @param[in] Qos 149 * Quality of service to specify what kind of impersonation to be done. 150 * 151 * @param[in] RemoteClient 152 * If set to TRUE, the client that we're going to impersonate is remote. 153 * 154 * @param[out] ClientContext 155 * The returned security client context. 156 * 157 * @return 158 * See SepCreateClientSecurity. 159 */ 160 NTSTATUS 161 NTAPI 162 SeCreateClientSecurity( 163 _In_ PETHREAD Thread, 164 _In_ PSECURITY_QUALITY_OF_SERVICE Qos, 165 _In_ BOOLEAN RemoteClient, 166 _Out_ PSECURITY_CLIENT_CONTEXT ClientContext) 167 { 168 TOKEN_TYPE TokenType; 169 BOOLEAN ThreadEffectiveOnly; 170 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; 171 PACCESS_TOKEN Token; 172 NTSTATUS Status; 173 PAGED_CODE(); 174 175 /* Reference the correct token */ 176 Token = PsReferenceEffectiveToken(Thread, 177 &TokenType, 178 &ThreadEffectiveOnly, 179 &ImpersonationLevel); 180 181 /* Create client security from it */ 182 Status = SepCreateClientSecurity(Token, 183 Qos, 184 RemoteClient, 185 TokenType, 186 ThreadEffectiveOnly, 187 ImpersonationLevel, 188 ClientContext); 189 190 /* Check if we failed or static tracking was used */ 191 if (!(NT_SUCCESS(Status)) || (Qos->ContextTrackingMode == SECURITY_STATIC_TRACKING)) 192 { 193 /* Dereference our copy since it's not being used */ 194 ObDereferenceObject(Token); 195 } 196 197 /* Return status */ 198 return Status; 199 } 200 201 /** 202 * @brief 203 * Creates a client security context based upon the captured security 204 * subject context. 205 * 206 * @param[in] SubjectContext 207 * The captured subject context where client security is to be created 208 * from. 209 * 210 * @param[in] ClientSecurityQos 211 * Quality of service to specify what kind of impersonation to be done. 212 * 213 * @param[in] ServerIsRemote 214 * If set to TRUE, the client that we're going to impersonate is remote. 215 * 216 * @param[out] ClientContext 217 * The returned security client context. 218 * 219 * @return 220 * See SepCreateClientSecurity. 221 */ 222 NTSTATUS 223 NTAPI 224 SeCreateClientSecurityFromSubjectContext( 225 _In_ PSECURITY_SUBJECT_CONTEXT SubjectContext, 226 _In_ PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos, 227 _In_ BOOLEAN ServerIsRemote, 228 _Out_ PSECURITY_CLIENT_CONTEXT ClientContext) 229 { 230 PACCESS_TOKEN Token; 231 NTSTATUS Status; 232 PAGED_CODE(); 233 234 /* Get the right token and reference it */ 235 Token = SeQuerySubjectContextToken(SubjectContext); 236 ObReferenceObject(Token); 237 238 /* Create the context */ 239 Status = SepCreateClientSecurity(Token, 240 ClientSecurityQos, 241 ServerIsRemote, 242 SubjectContext->ClientToken ? 243 TokenImpersonation : TokenPrimary, 244 FALSE, 245 SubjectContext->ImpersonationLevel, 246 ClientContext); 247 248 /* Check if we failed or static tracking was used */ 249 if (!(NT_SUCCESS(Status)) || 250 (ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING)) 251 { 252 /* Dereference our copy since it's not being used */ 253 ObDereferenceObject(Token); 254 } 255 256 /* Return status */ 257 return Status; 258 } 259 260 /** 261 * @brief 262 * Extended function that impersonates a client. 263 * 264 * @param[in] ClientContext 265 * A valid client context. 266 * 267 * @param[in] ServerThread 268 * The thread where impersonation is to be done. 269 * 270 * @return 271 * STATUS_SUCCESS is returned if the calling thread successfully impersonates 272 * the client. A failure NTSTATUS code is returned otherwise. 273 */ 274 NTSTATUS 275 NTAPI 276 SeImpersonateClientEx( 277 _In_ PSECURITY_CLIENT_CONTEXT ClientContext, 278 _In_opt_ PETHREAD ServerThread) 279 { 280 BOOLEAN EffectiveOnly; 281 PAGED_CODE(); 282 283 /* Check if direct access is requested */ 284 if (!ClientContext->DirectlyAccessClientToken) 285 { 286 /* No, so get the flag from QOS */ 287 EffectiveOnly = ClientContext->SecurityQos.EffectiveOnly; 288 } 289 else 290 { 291 /* Yes, so see if direct access should be effective only */ 292 EffectiveOnly = ClientContext->DirectAccessEffectiveOnly; 293 } 294 295 /* Use the current thread if one was not passed */ 296 if (!ServerThread) ServerThread = PsGetCurrentThread(); 297 298 /* Call the lower layer routine */ 299 return PsImpersonateClient(ServerThread, 300 ClientContext->ClientToken, 301 TRUE, 302 EffectiveOnly, 303 ClientContext->SecurityQos.ImpersonationLevel); 304 } 305 306 /** 307 * @brief 308 * Impersonates a client user. 309 * 310 * @param[in] ClientContext 311 * A valid client context. 312 * 313 * @param[in] ServerThread 314 * The thread where impersonation is to be done. 315 * * 316 * @return 317 * Nothing. 318 */ 319 VOID 320 NTAPI 321 SeImpersonateClient( 322 _In_ PSECURITY_CLIENT_CONTEXT ClientContext, 323 _In_opt_ PETHREAD ServerThread) 324 { 325 PAGED_CODE(); 326 327 /* Call the new API */ 328 SeImpersonateClientEx(ClientContext, ServerThread); 329 } 330 331 /* EOF */ 332