1 krisbash 1.1 /*============================================================================
2 * Copyright (C) Microsoft Corporation, All rights reserved.
3 *============================================================================
4 */
5
6 #if defined (_MSC_VER)
7 #include <nt.h>
8 #include <ntrtl.h>
9 #include <windef.h>
10 #include <ntstatus.h>
11 #include <winerror.h>
12 #include <nturtl.h>
13 #include <sddl.h>
14 #include <windows.h>
15 #else
16 #include <base/user.h>
17 #endif
18
19 #include "miapi_common.h"
20 #include "Application.h"
21 #include "Session.h"
22 krisbash 1.1 #include "Operation.h"
23 #include "SafeHandle.h"
24 #include "ChildList.h"
25 #include "Options.h"
26 #include <MI.h>
27 #include <pal/lock.h>
28 #include <pal/format.h>
29 #include <base/log.h>
30
31 #if defined(_MSC_VER)
32 MI_Result WindowsError_To_MI_Result(DWORD winError)
33 {
34 switch(winError)
35 {
36 case NO_ERROR:
37 return MI_RESULT_OK;
38 case ERROR_ACCESS_DENIED:
39 return MI_RESULT_ACCESS_DENIED;
40 case ERROR_OUTOFMEMORY:
41 return MI_RESULT_SERVER_LIMITS_EXCEEDED;
42 default:
43 krisbash 1.1 return MI_RESULT_FAILED;
44 }
45 }
46 #define MAX_TOKEN_USER_SIZE sizeof(TOKEN_USER)+sizeof(SID)+(SID_MAX_SUB_AUTHORITIES*sizeof(DWORD))
47 #endif
48
49 /* Defined for real at bottom of file */
50 extern const MI_SessionFT g_sessionFT; /* Main function table for session */
51 extern const MI_SessionFT g_sessionFT_OOM; /* Special out of memory version */
52
53 /* Real back-end MI_Session structure */
54 typedef struct _SessionObject SessionObject;
55
56 struct _SessionObject
57 {
58 /* Linked list for child application sessions. Includes clients session handle */
59 ChildListNode sessionNode;
60
61 MI_Application clientApplication;
62 ProtocolHandlerCacheItem *protocolHandlerItem;
63 MI_Session protocolHandlerSession;
64 krisbash 1.1 MI_Session *clientSessionPtr; /*Pointer for client session for logging purposes only */
65 MI_SessionCallbacks callbacks;
66 ChildList operationList; /* List of child operations */
67 MI_DestinationOptions clientDestinationOptions;
68
69 #if defined(_MSC_VER)
70 HANDLE clientSessionCreationToken; //duplicate of clients token. All operations need to use the same token
71 BYTE _SIDArray[MAX_TOKEN_USER_SIZE];
72 TOKEN_USER *clientSessionCreationTokenSID;
73 #else
74 uid_t uid;
75 gid_t gid;
76
77 #endif
78
79 /* MI_Session_Close data */
80 void *sessionCloseCallbackContext;
81 void (MI_CALL *sessionCloseCallback)(_In_opt_ void *completionContext) ;
82 volatile ptrdiff_t sessionCloseCallbackCalled;
83 };
84
85 krisbash 1.1 MI_Result Session_ImpersonateClientInternal(_In_ SessionObject *sessionObject, _Out_ MI_CLIENT_IMPERSONATION_TOKEN *originalImpersonation);
86
87 /* CIM Extension callback pass-through for WriteMessage */
88 void MI_CALL Session_WriteMessage_Callback(
89 _In_ MI_Application *application,
90 _In_opt_ void *callbackContext,
91 MI_Uint32 channel,
92 _In_z_ const MI_Char * message)
93 {
94 SessionObject *sessionObject = (SessionObject*) callbackContext;
95 if (sessionObject->callbacks.writeMessage)
96 {
97 sessionObject->callbacks.writeMessage(&sessionObject->clientApplication, sessionObject->callbacks.callbackContext, channel, message);
98 }
99 }
100
101 /* CIM Extensions callback pass-through for WriteError */
102 void MI_CALL Session_WriteError_Callback(
103 _In_ MI_Application *application,
104 _In_opt_ void *callbackContext,
105 _In_ MI_Instance *instance)
106 krisbash 1.1 {
107 SessionObject *sessionObject = (SessionObject*) callbackContext;
108 if (sessionObject->callbacks.writeError)
109 {
110 sessionObject->callbacks.writeError(&sessionObject->clientApplication, sessionObject->callbacks.callbackContext, instance);
111 }
112 }
113
114 /* When thunk handle ref count gets to zero means no one is referencing this object any more
115 * so we can now delete the SessionObject
116 */
117 void Session_Destructor(
118 _In_ ThunkHandle *thunkHandle)
119 {
120 SessionObject *sessionObject = (SessionObject *) thunkHandle->u.object;
121 thunkHandle->u.object = NULL;
122 //NitsAssert(sessionObject->operationList.childCount == 0, L"child operation count should be zero");
123
124 ChildList_DeInitialize(&sessionObject->operationList);
125
126 /* Unregister self from application */
127 krisbash 1.1 Application_UnregisterSession(&sessionObject->clientApplication, &sessionObject->sessionNode);
128
129 /* Free our copy of the client-side destination options */
130 MI_DestinationOptions_Delete(&sessionObject->clientDestinationOptions);
131
132 #if defined(_MSC_VER)
133 if (sessionObject->clientSessionCreationToken != INVALID_HANDLE_VALUE)
134 {
135 CloseHandle(sessionObject->clientSessionCreationToken);
136 }
137 #endif
138
139 PAL_Free(sessionObject);
140
141 }
142 void Session_Destructor_NoAppUnRegister(
143 _In_ ThunkHandle *thunkHandle)
144 {
145 SessionObject *sessionObject = (SessionObject *) thunkHandle->u.object;
146 //NitsAssert(sessionObject->operationList.childCount == 0, L"child operation count should be zero");
147
148 krisbash 1.1 ChildList_DeInitialize(&sessionObject->operationList);
149
150 /* Note: No need to Unregister self from application as we didn't successfully register */
151
152
153 PAL_Free(sessionObject);
154
155 }
156
157 /* PUBLIC: Application calls this
158 * This function needs to find the transport handler and load if not already loaded.
159 * Then calls into the transport handler to itself to create their session.
160 */
161 _Success_(return == MI_RESULT_OK)
162 MI_Result MI_CALL Session_Create(
163 _In_ MI_Application *application,
164 _In_opt_z_ const MI_Char *protocol,
165 _In_opt_z_ const MI_Char *destination,
166 _In_opt_ MI_DestinationOptions *options,
167 _In_opt_ MI_SessionCallbacks *callbacks,
168 _Outptr_opt_result_maybenull_ MI_Instance **extendedError,
169 krisbash 1.1 _Out_ MI_Session *session)
170 {
171 SessionObject *sessionObject = NULL;
172 MI_DestinationOptions protocolHandlerDestinationOptions = MI_DESTINATIONOPTIONS_NULL;
173 MI_DestinationOptions tempDestinationOptions = MI_DESTINATIONOPTIONS_NULL;
174 GenericHandle *genericHandle = (GenericHandle *)session;
175 MI_SessionCallbacks myCallbacks;
176 MI_Result returnCode = MI_RESULT_OK;
177 MI_CLIENT_IMPERSONATION_TOKEN originalImpersonation = INVALID_HANDLE_VALUE;
178
179 trace_MISessionEnter(__FUNCTION__, application, protocol, destination, session);
180
181 /* initialize the client session in case we fail to fill it in */
182 if (session)
183 {
184 memset(session, 0, sizeof(MI_Session));
185 }
186 if (extendedError)
187 {
188 *extendedError = NULL;
189 }
190 krisbash 1.1
191 if ((application == NULL) || (session == NULL))
192 {
193 return MI_RESULT_INVALID_PARAMETER;
194 }
195
196 /* Allocate our real back-end session */
197 sessionObject = (SessionObject *) PAL_Malloc(sizeof(SessionObject));
198 if (sessionObject == NULL)
199 {
200 session->ft = &g_sessionFT_OOM;
201 return MI_RESULT_FAILED;
202 }
203 memset(sessionObject, 0, sizeof(SessionObject));
204 #if defined(_MSC_VER)
205 sessionObject->clientSessionCreationToken = INVALID_HANDLE_VALUE;
206 sessionObject->clientSessionCreationTokenSID = (TOKEN_USER*) sessionObject->_SIDArray;
207 #endif
208
209 returnCode = ChildList_Initialize(&sessionObject->operationList);
210 if (returnCode != MI_RESULT_OK)
211 krisbash 1.1 {
212 PAL_Free(sessionObject);
213 return returnCode;
214 }
215
216 /* Get a safe-handle */
217 if (Application_NewGenericHandle(application, genericHandle) != MI_RESULT_OK)
218 {
219 ChildList_DeInitialize(&sessionObject->operationList);
220 PAL_Free(sessionObject);
221 session->ft = &g_sessionFT_OOM;
222 return MI_RESULT_FAILED;
223 }
224
225 /* Set up links within thunk handle */
226 sessionObject->clientApplication = *application;
227 genericHandle->thunkHandle->u.object = sessionObject;
228
229
230 /* Copy callbacks */
231 if (callbacks)
232 krisbash 1.1 {
233 sessionObject->callbacks = *callbacks;
234 }
235
236 /* Set up protocol handlers callbacks to call back into us */
237 memset(&myCallbacks, 0, sizeof(myCallbacks));
238 myCallbacks.callbackContext = sessionObject;
239 myCallbacks.writeMessage = Session_WriteMessage_Callback;
240 myCallbacks.writeError = Session_WriteError_Callback;
241
242 /* copy the clients token. This is the token that all operations validate against */
243 #if defined(_MSC_VER)
244 {
245 DWORD windowsError = ERROR_SUCCESS;
246 MI_CLIENT_IMPERSONATION_TOKEN currentToken = INVALID_HANDLE_VALUE;
247 BOOL bRet = OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE|TOKEN_READ|TOKEN_DUPLICATE, TRUE, ¤tToken) ;
248 if (bRet == FALSE)
249 {
250 /*Not impersonating or out of memory*/
251 windowsError = GetLastError();
252 if (windowsError == ERROR_NO_TOKEN)
253 krisbash 1.1 {
254 /*OK, so no token on thread so try process token instead*/
255 bRet = ImpersonateSelf(SecurityImpersonation);
256 if (bRet == FALSE)
257 {
258 /* failed */
259 windowsError = GetLastError();
260 }
261 else
262 {
263 bRet = OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE|TOKEN_READ|TOKEN_DUPLICATE, TRUE, ¤tToken) ;
264 if (bRet == FALSE)
265 {
266 /* failed */
267 windowsError = GetLastError();
268 }
269 else
270 {
271 //Now got the impersonated process token
272 }
273 RevertToSelf();
274 krisbash 1.1 }
275 }
276 else
277 {
278 /*Failed, either out of memory or anonymous token*/
279 }
280 }
281 else
282 {
283 /*Using impersonation token*/
284 }
285
286 if (bRet == TRUE)
287 {
288 //Copy off the token for later validation and impersonation use
289 sessionObject->clientSessionCreationToken = currentToken;
290 }
291 else
292 {
293 ThunkHandle_Shutdown(genericHandle->thunkHandle, NULL);
294 ChildList_DeInitialize(&sessionObject->operationList);
295 krisbash 1.1 PAL_Free(sessionObject);
296 session->reserved2 = 0;
297 session->ft = &g_sessionFT_OOM;
298 return WindowsError_To_MI_Result(windowsError);
299 }
300 }
301
302 {
303 DWORD windowsError;
304 DWORD tokenUserSizeUsed;
305 if (!GetTokenInformation(sessionObject->clientSessionCreationToken,TokenUser,sessionObject->clientSessionCreationTokenSID, MAX_TOKEN_USER_SIZE, &tokenUserSizeUsed))
306 {
307 ThunkHandle_Shutdown(genericHandle->thunkHandle, NULL);
308 ChildList_DeInitialize(&sessionObject->operationList);
309 CloseHandle(sessionObject->clientSessionCreationToken);
310 PAL_Free(sessionObject);
311 session->reserved2 = 0;
312 session->ft = &g_sessionFT_OOM;
313 return MI_RESULT_FAILED;
314 }
315 }
316 krisbash 1.1 #else
317 sessionObject->uid = getuid();
318 sessionObject->gid = getgid();
319 #endif
320
321 /* Register session with application */
322 returnCode = Application_RegisterSession(application, &sessionObject->sessionNode);
323 if (returnCode != MI_RESULT_OK)
324 {
325 ThunkHandle_Shutdown(genericHandle->thunkHandle, NULL);
326 ChildList_DeInitialize(&sessionObject->operationList);
327 #if defined(_MSC_VER)
328 CloseHandle(sessionObject->clientSessionCreationToken);
329 #endif
330 PAL_Free(sessionObject);
331 session->reserved2 = 0;
332 session->ft = &g_sessionFT_OOM;
333 return returnCode;
334 }
335
336 returnCode = Session_ImpersonateClientInternal(sessionObject, &originalImpersonation);
337 krisbash 1.1 if (returnCode != MI_RESULT_OK)
338 {
339 Application_UnregisterSession(application, &sessionObject->sessionNode);
340 ThunkHandle_Shutdown(genericHandle->thunkHandle, NULL);
341 ChildList_DeInitialize(&sessionObject->operationList);
342 #if defined(_MSC_VER)
343 CloseHandle(sessionObject->clientSessionCreationToken);
344 #endif
345 PAL_Free(sessionObject);
346 session->reserved2 = 0;
347 session->ft = &g_sessionFT_OOM;
348 return returnCode;
349 }
350
351 /* select transport & load it */
352 returnCode = Application_GetProtocolHandler(application, destination, protocol, &sessionObject->protocolHandlerItem);
353 if (returnCode != MI_RESULT_OK)
354 {
355 if (Session_RevertImpersonation(originalImpersonation) != MI_RESULT_OK)
356 {
357 TerminateProcess(GetCurrentProcess(), -1);
358 krisbash 1.1 }
359 Application_UnregisterSession(application, &sessionObject->sessionNode);
360 ThunkHandle_Shutdown(genericHandle->thunkHandle, NULL);
361 ChildList_DeInitialize(&sessionObject->operationList);
362 #if defined(_MSC_VER)
363 CloseHandle(sessionObject->clientSessionCreationToken);
364 #endif
365 PAL_Free(sessionObject);
366 session->reserved2 = 0;
367 session->ft = &g_sessionFT_OOM;
368 return returnCode;
369 }
370
371 /* create a DestinationOptions if not given */
372 if (!options)
373 {
374 returnCode = MI_Application_NewDestinationOptions(application, &tempDestinationOptions);
375 if (returnCode == MI_RESULT_OK)
376 {
377 options = &tempDestinationOptions;
378 }
379 krisbash 1.1 }
380
381 if (returnCode == MI_RESULT_OK)
382 {
383 /* duplicate the client options so we can use them to merge with operation options */
384 DestinationOptions_Duplicate(options, &sessionObject->clientDestinationOptions);
385
386 /* Create a protocol handler version to use now */
387 returnCode = MI_Application_NewDestinationOptions(&sessionObject->protocolHandlerItem->application, &protocolHandlerDestinationOptions);
388 if (returnCode == MI_RESULT_OK)
389 {
390 returnCode = DestinationOptions_MigrateOptions(options, &protocolHandlerDestinationOptions, sessionObject->protocolHandlerItem->name != NULL? sessionObject->protocolHandlerItem->name : MI_T(""), extendedError);
391 if (returnCode != MI_RESULT_OK)
392 {
393 MI_DestinationOptions_Delete(&protocolHandlerDestinationOptions);
394 }
395 }
396 }
397
398 if (returnCode != MI_RESULT_OK)
399 {
400 krisbash 1.1 if (tempDestinationOptions.ft != NULL)
401 {
402 MI_DestinationOptions_Delete(&tempDestinationOptions);
403 }
404 if (Session_RevertImpersonation(originalImpersonation) != MI_RESULT_OK)
405 {
406 TerminateProcess(GetCurrentProcess(), -1);
407 }
408 Application_UnregisterSession(application, &sessionObject->sessionNode);
409 ThunkHandle_Shutdown(genericHandle->thunkHandle, NULL);
410 ChildList_DeInitialize(&sessionObject->operationList);
411 MI_DestinationOptions_Delete(&sessionObject->clientDestinationOptions);
412 #if defined(_MSC_VER)
413 CloseHandle(sessionObject->clientSessionCreationToken);
414 #endif
415 PAL_Free(sessionObject);
416 session->reserved2 = 0;
417 session->ft = &g_sessionFT_OOM;
418 return returnCode;
419 }
420
421 krisbash 1.1 /* Copy client application handle */
422 sessionObject->clientApplication = *application;
423
424 returnCode = sessionObject->protocolHandlerItem->application.ft->NewSession(&sessionObject->protocolHandlerItem->application, protocol, destination, &protocolHandlerDestinationOptions, &myCallbacks, extendedError, &sessionObject->protocolHandlerSession);
425 if (returnCode != MI_RESULT_OK)
426 {
427 if (tempDestinationOptions.ft != NULL)
428 {
429 MI_DestinationOptions_Delete(&tempDestinationOptions);
430 }
431 MI_DestinationOptions_Delete(&protocolHandlerDestinationOptions);
432 ThunkHandle_Shutdown(genericHandle->thunkHandle, Session_Destructor);
433 session->reserved2 = 0;
434 session->ft = &g_sessionFT_OOM;
435 if (Session_RevertImpersonation(originalImpersonation) != MI_RESULT_OK)
436 {
437 TerminateProcess(GetCurrentProcess(), -1);
438 }
439 return returnCode;
440 }
441
442 krisbash 1.1 if (tempDestinationOptions.ft != NULL)
443 {
444 MI_DestinationOptions_Delete(&tempDestinationOptions);
445 }
446
447 MI_DestinationOptions_Delete(&protocolHandlerDestinationOptions);
448
449 /* Fix up clients function table to point to us*/
450 session->ft = &g_sessionFT;
451
452 sessionObject->clientSessionPtr = session; /*for debug logging only*/
453
454 /* Copy client handle */
455 sessionObject->sessionNode.clientHandle = *(GenericHandle*)session;
456
457 trace_MIClient_SessionCreate(application, session, sessionObject);
458
459 if (Session_RevertImpersonation(originalImpersonation) != MI_RESULT_OK)
460 {
461 TerminateProcess(GetCurrentProcess(), -1);
462 }
463 krisbash 1.1
464 return returnCode;
465 }
466
467 _Success_(return == MI_RESULT_OK)
468 MI_Result Session_RegisterOperation(_Inout_ MI_Session *session, _Inout_ ChildListNode *operation)
469 {
470 GenericHandle *genericHandle = (GenericHandle *)session;
471 ThunkHandle *thunkHandle = NULL;
472 MI_Result errorReturn = MI_RESULT_OK;
473
474 ThunkHandle_FromGeneric(genericHandle, &thunkHandle);
475 if (thunkHandle)
476 {
477 SessionObject *sessionObject = (SessionObject*) thunkHandle->u.object;
478 errorReturn = ChildList_AddNode(&sessionObject->operationList, operation);
479 ThunkHandle_Release(thunkHandle);
480 }
481 else
482 {
483 errorReturn = MI_RESULT_FAILED;
484 krisbash 1.1 }
485
486 return errorReturn;
487 }
488
489 _Success_(return == MI_RESULT_OK)
490 MI_Result Session_UnregisterOperation(_Inout_ ThunkHandle *thunkHandle, _Inout_ ChildListNode *operation)
491 {
492 SessionObject *sessionObject = (SessionObject*) thunkHandle->u.object;
493 ChildList_RemoveNode(&sessionObject->operationList, operation);
494
495 return MI_RESULT_OK;
496 }
497
498
499 /* Callback from protocol handler that Close has competed. It is
500 * safe for us to finish closing things down now and then callback
501 * to the client.
502 */
503 void MI_CALL Session_CloseCallback(_In_ void *completionContext)
504 {
505 krisbash 1.1 ThunkHandle *thunkHandle = (ThunkHandle*) completionContext;
506 SessionObject *sessionObject = (SessionObject*) thunkHandle->u.object;
507
508 /* Final callback */
509 if (sessionObject->sessionCloseCallback != NULL)
510 {
511 MI_CLIENT_IMPERSONATION_TOKEN currentImpersonationToken;
512 MI_Result impersonationResult = Session_ImpersonateClientInternal(sessionObject, ¤tImpersonationToken);
513
514 if (impersonationResult == MI_RESULT_OK)
515 {
516 /* Client was async, so callback */
517 sessionObject->sessionCloseCallback(sessionObject->sessionCloseCallbackContext);
518
519 trace_MIClient_SessionCloseCompleted(sessionObject->clientSessionPtr, sessionObject);
520
521 if (Session_RevertImpersonation(currentImpersonationToken) != MI_RESULT_OK)
522 {
523 TerminateProcess(GetCurrentProcess(), -1);
524 }
525 }
526 krisbash 1.1 /* Shutdown was holding open refcount until callback was called */
527 ThunkHandle_Release(thunkHandle);
528 }
529 else
530 {
531 /* Client was synchronous, so signal waiting thread */
532 sessionObject->sessionCloseCallbackCalled = 1;
533 CondLock_Broadcast((ptrdiff_t)sessionObject);
534 }
535 }
536
537 /* Shutdown of all child operations was successful so now
538 * we need to call into the protocol handler to tell it
539 * to shutdown
540 */
541 void Session_AllOperationsShutdown(void *context)
542 {
543 MI_CLIENT_IMPERSONATION_TOKEN originalImpersonation = INVALID_HANDLE_VALUE;
544 MI_Result miResult;
545 ThunkHandle *sessionThunk = (ThunkHandle*) context;
546 SessionObject *sessionObject = (SessionObject*) sessionThunk->u.object;
547 krisbash 1.1 ProtocolHandlerCacheItem *protocolHandlerItem = sessionObject->protocolHandlerItem;
548 /* Call into protocol handler to initiate shutdown */
549 ProtocolHandlerCache_IncrementApiCount(protocolHandlerItem);
550
551 miResult = Session_ImpersonateClientInternal(sessionObject, &originalImpersonation);
552 //If it is failed it is going to have to be best effort and hope it works!
553 sessionObject->protocolHandlerSession.ft->Close(&sessionObject->protocolHandlerSession, sessionThunk, Session_CloseCallback);
554
555 if ((miResult == MI_RESULT_OK) && (Session_RevertImpersonation(originalImpersonation) != MI_RESULT_OK))
556 {
557 TerminateProcess(GetCurrentProcess(), -1);
558 }
559
560 ProtocolHandlerCache_DecrementApiCount(protocolHandlerItem);
561
562 /* Close is async so do nothing else after this */
563 }
564
565 /* Trigger cancellation of all operations */
566 void Session_CancelAllOperations(_Inout_ MI_Session *session)
567 {
568 krisbash 1.1 ThunkHandle *thunkHandle;
569 SessionObject *sessionObject;
570
571 ThunkHandle_FromGeneric((GenericHandle*)session, &thunkHandle);
572 if (thunkHandle == NULL)
573 {
574 /* Failed means version was wrong or object was shutdown */
575 return;
576 }
577
578 sessionObject = (SessionObject*) thunkHandle->u.object;
579
580 {
581 ChildListOutstandingHandles _smallBuffer[100];
582 ChildListOutstandingHandles* outstandingOperations = _smallBuffer;
583 ptrdiff_t outstandingOperationSize = sizeof(_smallBuffer)/sizeof(_smallBuffer[0]);
584 ptrdiff_t outstandingOperationCount;
585
586 int r = ChildList_Shutdown(&sessionObject->operationList);
587 if (r)
588 {
589 krisbash 1.1 r = ChildList_GetCurrentList(&sessionObject->operationList, outstandingOperations, outstandingOperationSize, &outstandingOperationCount);
590 if (r == 0 && outstandingOperationCount > outstandingOperationSize)
591 {
592 outstandingOperations = (ChildListOutstandingHandles*) PAL_Malloc(outstandingOperationCount*sizeof(ChildListOutstandingHandles));
593 if (outstandingOperations == NULL)
594 {
595 //TSASSERT(0, L"ignored memory allocation on purpose", TLINE);
596 //Note that we cannot cancel the operations.
597 //It is completely up to the client to close all operations in this case and it will cause it to not respond if they do not
598 }
599 else
600 {
601 outstandingOperationSize = outstandingOperationCount;
602 r = ChildList_GetCurrentList(&sessionObject->operationList, outstandingOperations, outstandingOperationSize, &outstandingOperationCount);
603 }
604 }
605
606 /* Cancel all child operations */
607 if (r)
608 {
609 #ifdef _PREFAST_
610 krisbash 1.1 //famous last words, but I am pretty sure this code is not an overflow. outstandingOperationCount has how many items are in it and it will never be more than
611 //outstandingOperationSize if r is non-zero
612 #pragma prefast(push)
613 #pragma prefast(disable:26015)
614 #endif
615 while (outstandingOperationCount)
616 {
617 MI_Operation *operation = (MI_Operation*)&outstandingOperations[outstandingOperationCount-1].clientHandle;
618
619 /* Mode to next one as cancel may cause current operation to get deleted */
620 outstandingOperationCount--;
621
622 MI_Operation_Cancel(operation, MI_REASON_NONE);
623 }
624 #ifdef _PREFAST_
625 #pragma prefast(pop)
626 #endif
627 }
628 if (outstandingOperations != _smallBuffer)
629 {
630 PAL_Free(outstandingOperations);
631 krisbash 1.1 }
632 }
633 }
634 ThunkHandle_Release(thunkHandle);
635 }
636
637 /* Client API MI_Session_Close method.
638 */
639 _Success_(return == MI_RESULT_OK)
640 MI_Result MI_CALL Session_Close(
641 _In_ MI_Session *session,
642 _In_opt_ void *completionContext,
643 _In_opt_ void (MI_CALL *completionCallback)(_In_opt_ void *completionContext))
644 {
645 GenericHandle *genericHandle = (GenericHandle *)session;
646 ThunkHandle *thunkHandle = NULL;
647 SessionObject *sessionObject = NULL;
648 MI_CLIENT_IMPERSONATION_TOKEN originalImpersonation = INVALID_HANDLE_VALUE;
649
650 trace_MISessionClose(__FUNCTION__, session, completionContext, (void*)completionCallback);
651
652 krisbash 1.1 /* invalid parameter check, already closed, or out of memory during creation */
653 if ((session == NULL) || (genericHandle->thunkHandle == NULL))
654 {
655 if (completionCallback != NULL)
656 {
657 completionCallback(completionContext);
658 }
659 trace_MIInvalidSession(__FUNCTION__, session);
660 return MI_RESULT_INVALID_PARAMETER;
661 }
662
663 ThunkHandle_FromGeneric(genericHandle, &thunkHandle);
664 if (thunkHandle == NULL)
665 {
666 /* Failed means version was wrong or object was shutdown */
667 trace_MISessionInvalidThunkHandle(__FUNCTION__, session);
668 return MI_RESULT_INVALID_PARAMETER;
669 }
670
671 sessionObject = (SessionObject*) thunkHandle->u.object;
672
673 krisbash 1.1 {
674 MI_Result securityResult = Session_AccessCheck(session, MI_T("close session"));
675 if (securityResult != MI_RESULT_OK)
676 {
677 ThunkHandle_Release(thunkHandle);
678 trace_MISession_AccessCheckFailed(__FUNCTION__, session);
679 return securityResult;
680 }
681 }
682
683 trace_MIClient_SessionClose(session, sessionObject);
684
685 /* Mark handle for shutdown */
686 if (ThunkHandle_Shutdown(thunkHandle, Session_Destructor))
687 {
688 MI_Result miResult = Session_ImpersonateClientInternal(sessionObject, &originalImpersonation);
689 if (miResult != MI_RESULT_OK)
690 {
691 ThunkHandle_Release(thunkHandle);
692 return miResult;
693 }
694 krisbash 1.1
695 /* remember callback details */
696 if (completionCallback)
697 {
698 sessionObject->sessionCloseCallbackContext = completionContext;
699 sessionObject->sessionCloseCallback = completionCallback;
700 }
701
702 /* Cancel all child operations */
703 {
704 ChildListOutstandingHandles _smallBuffer[100];
705 ChildListOutstandingHandles* outstandingOperations = _smallBuffer;
706 ptrdiff_t outstandingOperationSize = sizeof(_smallBuffer)/sizeof(_smallBuffer[0]);
707 ptrdiff_t outstandingOperationCount;
708
709 int r = ChildList_Shutdown(&sessionObject->operationList);
710 if (r)
711 {
712 r = ChildList_GetCurrentList(&sessionObject->operationList, outstandingOperations, outstandingOperationSize, &outstandingOperationCount);
713 if (r == 0 && outstandingOperationCount > outstandingOperationSize)
714 {
715 krisbash 1.1 outstandingOperations = (ChildListOutstandingHandles*) PAL_Malloc(outstandingOperationCount*sizeof(ChildListOutstandingHandles));
716 if (outstandingOperations == NULL)
717 {
718 //TSASSERT(0, L"ignored memory allocation on purpose", TLINE);
719 //Note that we cannot cancel the operations.
720 //It is completely up to the client to close all operations in this case and it will cause it to not respond if they do not
721 }
722 else
723 {
724 outstandingOperationSize = outstandingOperationCount;
725 r = ChildList_GetCurrentList(&sessionObject->operationList, outstandingOperations, outstandingOperationSize, &outstandingOperationCount);
726 }
727 }
728
729 /* Cancel all child operations */
730 if (r)
731 {
732 #ifdef _PREFAST_
733 //famous last words, but I am pretty sure this code is not an overflow. outstandingOperationCount has how many items are in it and it will never be more than
734 //outstandingOperationSize if r is non-zero
735 #pragma prefast(push)
736 krisbash 1.1 #pragma prefast(disable:26015)
737 #endif
738 while (outstandingOperationCount)
739 {
740 MI_Operation *operation = (MI_Operation*)&outstandingOperations[outstandingOperationCount-1].clientHandle;
741
742 /* Mode to next one as cancel may cause current operation to get deleted */
743 outstandingOperationCount--;
744
745 MI_Operation_Cancel(operation, MI_REASON_NONE);
746 }
747 #ifdef _PREFAST_
748 #pragma prefast(pop)
749 #endif
750 }
751 if (outstandingOperations != _smallBuffer)
752 {
753 PAL_Free(outstandingOperations);
754 }
755 }
756 ChildList_RegisterShutdownCallback(&sessionObject->operationList, Session_AllOperationsShutdown, thunkHandle);
757 krisbash 1.1 }
758
759 /* Callback into the protocol handler happens asynchronously once all child operations are shutdown */
760
761 /* CALL IS ASYNC. NO MORE CODE HERE except for code to make close synchronous. Anything else that is needed goes here: Session_CloseCallback */
762
763 if (completionCallback == NULL)
764 {
765 ptrdiff_t curSessionCloseCallbackCalled = sessionObject->sessionCloseCallbackCalled;
766 /* block until protocol handler calls back */
767 while (!curSessionCloseCallbackCalled)
768 {
769 trace_MIClient_SessionClose_WaitingOnOperations(session, sessionObject, sessionObject->operationList.childCount);
770 /* 0 is the current value of state, the value we don't want to see. */
771 CondLock_Wait((ptrdiff_t) sessionObject, &sessionObject->sessionCloseCallbackCalled, curSessionCloseCallbackCalled, CONDLOCK_DEFAULT_SPINCOUNT);
772 curSessionCloseCallbackCalled = sessionObject->sessionCloseCallbackCalled;
773 }
774 trace_MIClient_SessionCloseCompleted(session, sessionObject);
775 ThunkHandle_Release(thunkHandle);
776 }
777 else
778 krisbash 1.1 {
779 /* ThunkHandle_Release(thunkHandle) happens in callback code */
780 }
781
782 if (Session_RevertImpersonation(originalImpersonation) != MI_RESULT_OK)
783 {
784 TerminateProcess(GetCurrentProcess(), -1);
785 }
786 }
787 else
788 {
789 /* Not the first call to Close so ignore this. It is unlikely this will happen
790 * as the ThunkHandle_FromGeneric would generally fail but there is a small
791 * race condition that could cause this to happen
792 */
793 ThunkHandle_Release(thunkHandle);
794 }
795
796 return MI_RESULT_OK;
797 }
798
799 krisbash 1.1 _Success_(return == MI_RESULT_OK)
800 MI_Result MI_CALL Session_GetApplication(
801 _In_ MI_Session *session,
802 _Out_ MI_Application *application)
803 {
804 GenericHandle *genericHandle = (GenericHandle*) session;
805 ThunkHandle * thunkHandle = NULL;
806 SessionObject *sessionObject;
807
808 if (application)
809 {
810 memset(application, 0, sizeof(MI_Application));
811 }
812 if ((session == NULL) || (application == NULL))
813 {
814 return MI_RESULT_INVALID_PARAMETER;
815 }
816
817
818 ThunkHandle_FromGeneric(genericHandle, &thunkHandle);
819 if (thunkHandle == NULL)
820 krisbash 1.1 {
821 /* Failed means version was wrong or object was shutdown */
822 return MI_RESULT_INVALID_PARAMETER;
823 }
824
825 sessionObject = (SessionObject *) thunkHandle->u.object;
826 *application = sessionObject->clientApplication;
827
828 /* finished with object for now... */
829 ThunkHandle_Release(thunkHandle);
830 return MI_RESULT_OK;
831 }
832
833 /* Get a copy of the session destination options */
834 void Session_GetDestinationOptions(_Inout_ MI_Session *session, _Out_ MI_DestinationOptions *destOptions)
835 {
836 GenericHandle *genericHandle = (GenericHandle*) session;
837 ThunkHandle * thunkHandle = NULL;
838 SessionObject *sessionObject;
839
840 memset(destOptions, 0, sizeof(MI_DestinationOptions));
841 krisbash 1.1
842 ThunkHandle_FromGeneric(genericHandle, &thunkHandle);
843 if (thunkHandle == NULL)
844 {
845 /* Failed means version was wrong or object was shutdown */
846 return;
847 }
848
849 sessionObject = (SessionObject *) thunkHandle->u.object;
850 DestinationOptions_Duplicate(&sessionObject->clientDestinationOptions, destOptions);
851 /* finished with object for now... */
852 ThunkHandle_Release(thunkHandle);
853 }
854
855 /* From a client session get the transport session */
856 _Success_(return==MI_RESULT_OK)
857 MI_Result Session_GetProtocolHandlerSession(
858 _In_ MI_Session *clientSession,
859 _Out_ MI_Session *protocolHandlerSession,
860 _Out_ ProtocolHandlerCacheItem **protocolHandlerItem)
861 {
862 krisbash 1.1 ThunkHandle *sessionThunk = NULL;
863 SessionObject *sessionObject = NULL;
864
865 ThunkHandle_FromGeneric((GenericHandle*)clientSession, &sessionThunk);
866 if (sessionThunk == NULL)
867 {
868 return MI_RESULT_FAILED;
869 }
870
871 sessionObject = (SessionObject *) sessionThunk->u.object;
872 *protocolHandlerSession = sessionObject->protocolHandlerSession;
873 *protocolHandlerItem = sessionObject->protocolHandlerItem;
874 /* release up sessionThunk */
875 ThunkHandle_Release(sessionThunk);
876
877 return MI_RESULT_OK;
878 }
879
880 _Success_(return==MI_RESULT_OK)
881 MI_Result Session_GetProtocolHandlerApplication(
882 _In_ MI_Session *clientSession,
883 krisbash 1.1 _Out_ MI_Application *protocolHandlerApplication)
884 {
885 ThunkHandle *sessionThunk = NULL;
886 SessionObject *sessionObject = NULL;
887
888 ThunkHandle_FromGeneric((GenericHandle*)clientSession, &sessionThunk);
889 if (sessionThunk == NULL)
890 {
891 return MI_RESULT_FAILED;
892 }
893
894 sessionObject = (SessionObject *) sessionThunk->u.object;
895 *protocolHandlerApplication = sessionObject->protocolHandlerItem->application;
896
897 /* release up sessionThunk */
898 ThunkHandle_Release(sessionThunk);
899
900 return MI_RESULT_OK;
901 }
902
903 _Success_(return == MI_RESULT_OK)
904 krisbash 1.1 MI_Result Session_AccessCheck(_In_ MI_Session *session, _In_opt_z_ const MI_Char *operationName)
905 {
906 ThunkHandle *sessionThunk = NULL;
907 SessionObject *sessionObject = NULL;
908 MI_Result r;
909
910 trace_MIEnter(__FUNCTION__, session);
911
912 /* NOTE: Access check may be called from Operat_Cancel or Operation_Close after the
913 * session has been closed. Therefore do thunk and ignore Active bit on handle.
914 */
915 ThunkHandle_FromGeneric_ForCompletionCallback((GenericHandle*)session, &sessionThunk);
916 if (sessionThunk == NULL)
917 {
918 trace_MISession_InvalidSessionThunk(__FUNCTION__, session);
919 return MI_RESULT_INVALID_PARAMETER;
920 }
921
922 sessionObject = (SessionObject *) sessionThunk->u.object;
923
924 #if defined(_MSC_VER)
925 krisbash 1.1 {
926 MI_CLIENT_IMPERSONATION_TOKEN currentToken = INVALID_HANDLE_VALUE;
927 BOOL bRet;
928 DWORD windowsError = NO_ERROR;
929
930 bRet = OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE|TOKEN_READ|TOKEN_DUPLICATE, TRUE, ¤tToken) ;
931 if (bRet == FALSE)
932 {
933 /*Not impersonating or out of memory*/
934 windowsError = GetLastError();
935 if (windowsError == ERROR_NO_TOKEN)
936 {
937 /*OK, so no token on thread so try process token instead*/
938 bRet = ImpersonateSelf(SecurityImpersonation);
939 if (bRet == FALSE)
940 {
941 /* failed */
942 windowsError = GetLastError();
943 }
944 else
945 {
946 krisbash 1.1 bRet = OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE|TOKEN_READ|TOKEN_DUPLICATE, TRUE, ¤tToken) ;
947 if (bRet == FALSE)
948 {
949 /* failed */
950 windowsError = GetLastError();
951 }
952 else
953 {
954 /*Now got the impersonated process token*/
955 windowsError = NO_ERROR;
956 }
957 RevertToSelf();
958 }
959 }
960 else
961 {
962 /*Failed, either out of memory or anonymous token*/
963 }
964 }
965 else
966 {
967 krisbash 1.1 /*Using impersonation token*/
968 }
969
970 if (bRet)
971 {
972 //Compare user SID
973 DWORD dwSize = sizeof(TOKEN_USER)+sizeof(SID)+(SID_MAX_SUB_AUTHORITIES*sizeof(DWORD));
974 BYTE Array[sizeof(TOKEN_USER)+sizeof(SID)+(SID_MAX_SUB_AUTHORITIES*sizeof(DWORD))];
975 TOKEN_USER * pTokenUser = (TOKEN_USER *)Array;
976
977 if (!GetTokenInformation(currentToken,TokenUser,pTokenUser,dwSize,&dwSize))
978 {
979 windowsError = GetLastError();
980 }
981 else
982 {
983 if (!EqualSid(pTokenUser->User.Sid, sessionObject->clientSessionCreationTokenSID->User.Sid))
984 {
985 windowsError = ERROR_ACCESS_DENIED;
986 }
987 }
988 krisbash 1.1
989 CloseHandle(currentToken);
990 }
991 r = WindowsError_To_MI_Result(windowsError);
992 }
993 #else
994 if ((getuid() == sessionObject->uid) && (getgid() == sessionObject->gid))
995 {
996 r = MI_RESULT_OK;
997 }
998 else
999 {
1000 r = MI_RESULT_ACCESS_DENIED;
1001 }
1002 #endif
1003
1004 /* release up sessionThunk */
1005 ThunkHandle_Release(sessionThunk);
1006
1007 trace_MILeavingSessionWithOperation(__FUNCTION__, session, operationName);
1008 return r;
1009 krisbash 1.1 }
1010
1011 /*Impersonates based on who created the session, returning original thread token to call Session_RevertImpersonation with*/
1012 _Success_(return == MI_RESULT_OK)
1013 MI_Result Session_ImpersonateClient(_In_ MI_Session *session, _Out_ MI_CLIENT_IMPERSONATION_TOKEN *originalImpersonation)
1014 {
1015 #if defined(_MSC_VER)
1016 ThunkHandle *sessionThunk = NULL;
1017 SessionObject *sessionObject = NULL;
1018 MI_Result ret = MI_RESULT_OK;
1019
1020 *originalImpersonation = INVALID_HANDLE_VALUE ;
1021
1022 /* There is a change we need to do this when the session has already been marked for close
1023 however the session cannot go away until the operations have gone so probably OK
1024 */
1025 ThunkHandle_FromGeneric_ForCompletionCallback((GenericHandle*)session, &sessionThunk);
1026 if (sessionThunk == NULL)
1027 {
1028 return MI_RESULT_FAILED;
1029 }
1030 krisbash 1.1
1031 sessionObject = (SessionObject *) sessionThunk->u.object;
1032
1033 ret = Session_ImpersonateClientInternal(sessionObject, originalImpersonation);
1034
1035 /* release up sessionThunk */
1036 ThunkHandle_Release(sessionThunk);
1037
1038 return ret;
1039 #else
1040 return MI_RESULT_OK;
1041 #endif
1042 }
1043
1044 /*Impersonates based on who created the session, returning original thread token to call Session_RevertImpersonation with*/
1045 MI_Result Session_ImpersonateClientInternal(_In_ SessionObject *sessionObject, _Out_ MI_CLIENT_IMPERSONATION_TOKEN *originalImpersonation)
1046 {
1047 #if defined(_MSC_VER)
1048 MI_Result ret = MI_RESULT_OK;
1049
1050 *originalImpersonation = INVALID_HANDLE_VALUE ;
1051 krisbash 1.1
1052 /* Get the current impersonation token */
1053 if (!OpenThreadToken(GetCurrentThread(),
1054 TOKEN_IMPERSONATE,
1055 TRUE,
1056 originalImpersonation))
1057 {
1058 DWORD error = GetLastError();
1059 if ( error == ERROR_NO_TOKEN )
1060 {
1061 /*thread is not impersonating, nothing to do*/
1062 ret = MI_RESULT_OK;
1063 }
1064 else
1065 {
1066 ret = WindowsError_To_MI_Result(error);
1067 }
1068 }
1069 if (ret == MI_RESULT_OK)
1070 {
1071 /*Good so far, now set the token to the session creation token */
1072 krisbash 1.1 if (SetThreadToken( NULL, sessionObject->clientSessionCreationToken) == FALSE)
1073 {
1074 /*Failed! Not good. Clean-up*/
1075 ret = WindowsError_To_MI_Result(GetLastError());
1076 CloseHandle(*originalImpersonation);
1077 *originalImpersonation = INVALID_HANDLE_VALUE;
1078 }
1079 }
1080
1081 return ret;
1082 #else
1083 *originalImpersonation = INVALID_HANDLE_VALUE;
1084 return MI_RESULT_OK;
1085 #endif
1086 }
1087
1088 //Reverts the impersonation token back to what it was before Session_ImpersonateClient was called
1089 _Success_(return == MI_RESULT_OK)
1090 MI_Result Session_RevertImpersonation(MI_CLIENT_IMPERSONATION_TOKEN originalImpersonation)
1091 {
1092 #if defined(_MSC_VER)
1093 krisbash 1.1 MI_Result ret = MI_RESULT_OK;
1094
1095 if (INVALID_HANDLE_VALUE == originalImpersonation)
1096 {
1097 originalImpersonation = NULL;
1098 }
1099
1100
1101 if (SetThreadToken( NULL, originalImpersonation) == FALSE)
1102 {
1103 //
1104 // If we fail to re-attach the original token, we return FALSE. It is *** CRITICAL *** that the callee check the
1105 // return as potential security issues may arise if they dont.
1106 //
1107 ret = WindowsError_To_MI_Result(GetLastError());
1108 }
1109
1110 CloseHandle( originalImpersonation );
1111
1112 return ret;
1113 #else
1114 krisbash 1.1 return MI_RESULT_OK;
1115 #endif
1116 }
1117
1118 const MI_SessionFT g_sessionFT_OOM = {
1119 Session_Close,
1120 NULL
1121 };
1122
1123 const MI_SessionFT g_sessionFT = {
1124 Session_Close,
1125 Session_GetApplication,
1126 Operation_Execute_GetInstance,
1127 Operation_Execute_ModifyInstance,
1128 Operation_Execute_CreateInstance,
1129 Operation_Execute_DeleteInstance,
1130 Operation_Execute_Invoke,
1131 Operation_Execute_EnumerateInstances,
1132 Operation_Execute_QueryInstances,
1133 Operation_Execute_AssociatorInstances,
1134 Operation_Execute_ReferenceInstances,
1135 krisbash 1.1 Operation_Execute_Subscribe,
1136 Operation_Execute_GetClass,
1137 Operation_Execute_EnumerateClasses,
1138 Operation_Execute_TestConnection
1139 };
|