1 /* 2 * $Header$ 3 * 4 * Copyright 2008 Massachusetts Institute of Technology. 5 * All Rights Reserved. 6 * 7 * Export of this software from the United States of America may 8 * require a specific license from the United States Government. 9 * It is the responsibility of any person or organization contemplating 10 * export to obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of M.I.T. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. Furthermore if you modify this software you must label 20 * your software as modified software and not distribute it in such a 21 * fashion that it might be confused with the original M.I.T. software. 22 * M.I.T. makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 */ 26 27 #include "process.h" 28 #include "windows.h" 29 30 extern "C" { 31 #include "ccs_common.h" 32 #include "ccs_os_notify.h" 33 #include "ccs_os_server.h" 34 #include "ccs_reply.h" 35 #include "ccs_request.h" 36 #include "win-utils.h" 37 #include "ccutils.h" 38 } 39 40 #include "WorkQueue.h" 41 #include "util.h" 42 #include "opts.hxx" 43 #include "init.hxx" 44 45 #pragma warning (disable : 4996) 46 47 BOOL bListen = TRUE; /* Why aren't bool and true defined? */ 48 const char* sessID = NULL; /* The logon session we are running on behalf of. */ 49 time_t _sst = 0; 50 unsigned char* pszNetworkAddress = NULL; 51 unsigned char* pszStringBinding = NULL; 52 BOOL bRpcHandleInited = FALSE; 53 _RPC_ASYNC_STATE* rpcState = NULL; 54 55 /* Thread procedures can take only one void* argument. We put all the args we want 56 to pass into this struct and then pass a pointer to the struct: */ 57 struct RpcRcvArgs { 58 char* networkAddress; 59 unsigned char* protocolSequence; 60 unsigned char* sessID; /* Used for this server's endpoint */ 61 unsigned char* uuid; /* Used for client's UUID */ 62 ParseOpts::Opts* opts; 63 RPC_STATUS status; 64 } rpcargs = { NULL, /* pszNetworkAddress */ 65 (unsigned char*)"ncalrpc", /* pszProtocolSequence */ 66 NULL, /* sessID placeholder */ 67 NULL, /* uuid placeholder */ 68 NULL }; /* Opts placeholder */ 69 70 /* Command line format: 71 argv[0] Program name 72 argv[1] session ID to use 73 argv[2] "D" Debug: go into infinite loop in ccs_os_server_initialize so process 74 can be attached in debugger. 75 Any other value: continue 76 */ 77 #define N_FIXED_ARGS 3 78 #define SERVER_REPLY_RPC_HANDLE ccs_reply_IfHandle 79 80 /* Forward declarations: */ 81 void receiveLoop(void* rpcargs); 82 void connectionListener(void* rpcargs); 83 void Usage(const char* argv0); 84 void printError(TCHAR* msg); 85 void setMySST() {_sst = time(&_sst);} 86 time_t getMySST() {return _sst;} 87 RPC_STATUS send_connection_reply(ccs_pipe_t in_pipe); 88 void RPC_ENTRY clientListener( _RPC_ASYNC_STATE*, 89 void* Context, 90 RPC_ASYNC_EVENT Event); 91 RPC_STATUS RPC_ENTRY sec_callback( IN RPC_IF_ID *Interface, 92 IN void *Context); 93 RPC_STATUS send_init(char* clientUUID); 94 //DWORD alloc_name(LPSTR* pname, LPSTR postfix); 95 96 97 /* The layout of the rest of this module: 98 99 The four entrypoints defined in ccs_os_server.h: 100 ccs_os_server_initialize 101 cc_int32 ccs_os_server_cleanup 102 cc_int32 ccs_os_server_listen_loop 103 cc_int32 ccs_os_server_send_reply 104 105 Other routines needed by those four. 106 */ 107 108 /* ------------------------------------------------------------------------ */ 109 110 cc_int32 ccs_os_server_initialize (int argc, const char *argv[]) { 111 cc_int32 err = 0; 112 ParseOpts::Opts opts = { 0 }; 113 ParseOpts PO; 114 BOOL bAdjustedShutdown = FALSE; 115 HMODULE hKernel32 = GetModuleHandle("kernel32"); 116 117 if (!err) { 118 sessID = argv[1]; 119 setMySST(); 120 121 opts.cMinCalls = 1; 122 opts.cMaxCalls = 20; 123 opts.fDontWait = TRUE; 124 125 #ifdef CCAPI_TEST_OPTIONS 126 PO.SetValidOpts("kemnfubc"); 127 #else 128 PO.SetValidOpts("kc"); 129 #endif 130 131 PO.Parse(opts, argc, (char**)argv); 132 133 // while(*argv[2] == 'D') {} /* Hang here to attach process with debugger. */ 134 135 if (hKernel32) { 136 typedef BOOL (WINAPI *FP_SetProcessShutdownParameters)(DWORD, DWORD); 137 FP_SetProcessShutdownParameters pSetProcessShutdownParameters = 138 (FP_SetProcessShutdownParameters) 139 GetProcAddress(hKernel32, "SetProcessShutdownParameters"); 140 if (pSetProcessShutdownParameters) { 141 bAdjustedShutdown = pSetProcessShutdownParameters(100, 0); 142 } 143 } 144 cci_debug_printf("%s Shutdown Parameters", 145 bAdjustedShutdown ? "Adjusted" : "Did not adjust"); 146 147 err = Init::Initialize(); 148 } 149 150 // if (!err) { 151 // if (opts.bShutdown) { 152 // status = shutdown_server(opts.pszEndpoint); 153 // } 154 // } 155 // else { 156 // status = startup_server(opts); 157 // } 158 159 if (!err) { 160 err = worklist_initialize(); 161 } 162 163 if (err) { 164 Init::Cleanup(); 165 fprintf( stderr, "An error occurred while %s the server (%u)\n", 166 opts.bShutdown ? "shutting down" : "starting/running", 167 err); 168 exit(cci_check_error (err)); 169 } 170 171 return cci_check_error (err); 172 } 173 174 /* ------------------------------------------------------------------------ */ 175 176 cc_int32 ccs_os_server_cleanup (int argc, const char *argv[]) { 177 cc_int32 err = 0; 178 179 cci_debug_printf("%s for user <%s> shutting down.", argv[0], argv[1]); 180 181 worklist_cleanup(); 182 183 return cci_check_error (err); 184 } 185 186 /* ------------------------------------------------------------------------ */ 187 188 /* This function takes work items off the work queue and executes them. 189 * This is the one and only place where the multi-threaded Windows code 190 * calls into the single-threaded common code. 191 * 192 * The actual 'listening' for requests from clients happens after receiveloop 193 * establishes the RPC endpoint the clients will connect to and the RPC procedures 194 * put the work items into the work queue. 195 */ 196 cc_int32 ccs_os_server_listen_loop (int argc, const char *argv[]) { 197 cc_int32 err = 0; 198 uintptr_t threadStatus; 199 200 ParseOpts::Opts opts = { 0 }; 201 ParseOpts PO; 202 BOOL bQuitIfNoClients = FALSE; 203 204 opts.cMinCalls = 1; 205 opts.cMaxCalls = 20; 206 opts.fDontWait = TRUE; 207 208 #ifdef CCAPI_TEST_OPTIONS 209 PO.SetValidOpts("kemnfubc"); 210 #else 211 PO.SetValidOpts("kc"); 212 #endif 213 PO.Parse(opts, argc, (char**)argv); 214 215 216 //++ debug stuff 217 #define INFO_BUFFER_SIZE 32767 218 TCHAR infoBuf[INFO_BUFFER_SIZE]; 219 DWORD bufCharCount = INFO_BUFFER_SIZE; 220 // Get and display the user name. 221 bufCharCount = INFO_BUFFER_SIZE; 222 if( !GetUserName( infoBuf, &bufCharCount ) ) printError( TEXT("GetUserName") ); 223 //-- 224 225 /* Sending the reply from within the request RPC handler doesn't seem to work. 226 So we listen for requests in a separate thread and put the requests in a 227 queue. */ 228 rpcargs.sessID = (unsigned char*)sessID; 229 rpcargs.opts = &opts; 230 /// TODO: check for NULL handle, error, etc. probably move to initialize func... 231 threadStatus = _beginthread(receiveLoop, 0, (void*)&rpcargs); 232 233 /* We handle the queue entries here. Work loop: */ 234 while (ccs_server_client_count() > 0 || !bQuitIfNoClients) { 235 worklist_wait(); 236 while (!worklist_isEmpty()) { 237 k5_ipc_stream buf = NULL; 238 long rpcmsg = CCMSG_INVALID; 239 time_t serverStartTime = 0xDEADDEAD; 240 RPC_STATUS status = 0; 241 char* uuid = NULL; 242 k5_ipc_stream stream = NULL; 243 ccs_pipe_t pipe = NULL; 244 ccs_pipe_t pipe2 = NULL; 245 246 if (worklist_remove(&rpcmsg, &pipe, &buf, &serverStartTime)) { 247 uuid = ccs_win_pipe_getUuid(pipe); 248 249 if (serverStartTime <= getMySST()) { 250 switch (rpcmsg) { 251 case CCMSG_CONNECT: { 252 cci_debug_printf(" Processing CONNECT"); 253 rpcargs.uuid = (unsigned char*)uuid; 254 255 // Even if a disconnect message is received before this code finishes, 256 // it won't be dequeued and processed until after this code finishes. 257 // So we can add the client after starting the connection listener. 258 connectionListener((void*)&rpcargs); 259 status = rpcargs.status; 260 261 if (!status) { 262 status = ccs_server_add_client(pipe); 263 } 264 if (!status) {status = send_connection_reply(pipe);} 265 break; 266 } 267 case CCMSG_DISCONNECT: { 268 cci_debug_printf(" Processing DISCONNECT"); 269 if (!status) { 270 status = ccs_server_remove_client(pipe); 271 } 272 break; 273 } 274 case CCMSG_REQUEST: 275 cci_debug_printf(" Processing REQUEST"); 276 ccs_pipe_copy(&pipe2, pipe); 277 // Dispatch message here, setting both pipes to the client UUID: 278 err = ccs_server_handle_request (pipe, pipe2, buf); 279 break; 280 case CCMSG_PING: 281 cci_debug_printf(" Processing PING"); 282 err = krb5int_ipc_stream_new (&stream); 283 err = krb5int_ipc_stream_write(stream, "This is a test of the emergency broadcasting system", 52); 284 err = ccs_os_server_send_reply(pipe, stream); 285 break; 286 case CCMSG_QUIT: 287 bQuitIfNoClients = TRUE; 288 break; 289 default: 290 cci_debug_printf("Huh? Received invalid message type %ld from UUID:<%s>", 291 rpcmsg, uuid); 292 break; 293 } 294 if (buf) krb5int_ipc_stream_release(buf); 295 /* Don't free uuid, which was allocated here. A pointer to it is in the 296 rpcargs struct which was passed to connectionListener which will be 297 received by ccapi_listen when the client exits. ccapi_listen needs 298 the uuid to know which client to disconnect. 299 */ 300 } 301 // Server's start time is different from what the client thinks. 302 // That means the server has rebooted since the client connected. 303 else { 304 cci_debug_printf("Whoops! Server has rebooted since client established connection."); 305 } 306 } 307 else {cci_debug_printf("Huh? Queue not empty but no item to remove.");} 308 } 309 } 310 return cci_check_error (err); 311 } 312 313 /* ------------------------------------------------------------------------ */ 314 315 cc_int32 ccs_os_server_send_reply (ccs_pipe_t in_pipe, 316 k5_ipc_stream in_reply_stream) { 317 318 /* ccs_pipe_t in_reply_pipe is a char* reply endpoint. 319 k5_ipc_stream in_reply_stream is the data to be sent. 320 */ 321 322 cc_int32 err = 0; 323 char* uuid = ccs_win_pipe_getUuid(in_pipe); 324 UINT64 h = ccs_win_pipe_getHandle(in_pipe); 325 326 if (!err) { 327 err = send_init(uuid); // Sets RPC handle to be used. 328 } 329 330 if (!err) { 331 RpcTryExcept { 332 long status; 333 ccs_rpc_request_reply( // make call with user message 334 CCMSG_REQUEST_REPLY, /* Message type */ 335 (unsigned char*)&h, /* client's tspdata* */ 336 (unsigned char*)uuid, 337 getMySST(), 338 krb5int_ipc_stream_size(in_reply_stream), /* Length of buffer */ 339 (const unsigned char*)krb5int_ipc_stream_data(in_reply_stream), /* Data buffer */ 340 &status ); /* Return code */ 341 } 342 RpcExcept(1) { 343 cci_check_error(RpcExceptionCode()); 344 } 345 RpcEndExcept 346 } 347 348 /* The calls to the remote procedures are complete. */ 349 /* Free whatever we allocated: */ 350 err = RpcBindingFree(&SERVER_REPLY_RPC_HANDLE); 351 352 return cci_check_error (err); 353 } 354 355 356 /* Windows-specific routines: */ 357 358 void Usage(const char* argv0) { 359 printf("Usage:\n"); 360 printf("%s [m maxcalls] [n mincalls] [f dontwait] [h|?]]\n", argv0); 361 printf(" CCAPI server process.\n"); 362 printf(" h|? whow usage message. <\n"); 363 } 364 365 /* ------------------------------------------------------------------------ */ 366 /* The receive thread repeatedly issues RpcServerListen. 367 When a message arrives, it is handled in the RPC procedure. 368 */ 369 void receiveLoop(void* rpcargs) { 370 371 struct RpcRcvArgs* rcvargs = (struct RpcRcvArgs*)rpcargs; 372 RPC_STATUS status = FALSE; 373 unsigned char* pszSecurity = NULL; 374 LPSTR endpoint = NULL; 375 LPSTR event_name = NULL; 376 PSECURITY_DESCRIPTOR psd = NULL; 377 HANDLE hEvent = 0; 378 Init::InitInfo info; 379 380 cci_debug_printf("THREAD BEGIN: %s", __FUNCTION__); 381 382 status = Init::Info(info); 383 384 /* Build complete RPC endpoint using previous CCAPI implementation: */ 385 if (!status) { 386 if (!rcvargs->opts->pszEndpoint) { 387 if (!status) { 388 status = alloc_name(&endpoint, "ep", isNT()); 389 } 390 391 if (!status) { 392 status = alloc_name(&event_name, "startup", isNT()); 393 } 394 395 if (!status) { 396 hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, event_name); 397 // We ignore any error opening the event because we do not know who started us. 398 // [Comment paraphrased from previous implementation, whence it was copied.] 399 } 400 } 401 else { 402 endpoint = rcvargs->opts->pszEndpoint; 403 } 404 } 405 406 cci_debug_printf("%s Registering endpoint %s", __FUNCTION__, endpoint); 407 408 if (!status && isNT()) { 409 status = alloc_own_security_descriptor_NT(&psd); 410 } 411 412 if (!status) { 413 status = RpcServerUseProtseqEp(rcvargs->protocolSequence, 414 rcvargs->opts->cMaxCalls, 415 (RPC_CSTR)endpoint, 416 rcvargs->opts->bDontProtect ? 0 : psd); // SD 417 } 418 419 if (!status) { 420 status = RpcServerRegisterAuthInfo(0, // server principal 421 RPC_C_AUTHN_WINNT, 422 0, 423 0); 424 } 425 426 while (bListen && !status) { 427 cci_debug_printf("%s is listening ...", __FUNCTION__); 428 429 if (!info.isNT) { 430 status = RpcServerRegisterIf(ccs_request_ServerIfHandle, // interface 431 NULL, // MgrTypeUuid 432 NULL); // MgrEpv; null means use default 433 } 434 else { 435 status = info.fRpcServerRegisterIfEx(ccs_request_ServerIfHandle, // interface 436 NULL, // MgrTypeUuid 437 NULL, // MgrEpv; 0 means default 438 RPC_IF_ALLOW_SECURE_ONLY, 439 rcvargs->opts->cMaxCalls, 440 rcvargs->opts->bSecCallback ? 441 (RPC_IF_CALLBACK_FN*)sec_callback : 0 ); 442 } 443 444 if (!status) { 445 status = RpcServerListen(rcvargs->opts->cMinCalls, 446 rcvargs->opts->cMaxCalls, 447 rcvargs->opts->fDontWait); 448 } 449 450 if (!status) { 451 if (rcvargs->opts->fDontWait) { 452 if (hEvent) SetEvent(hEvent); // Ignore any error -- SetEvent is an optimization. 453 status = RpcMgmtWaitServerListen(); 454 } 455 } 456 } 457 458 if (status) { // Cleanup in case of errors: 459 if (hEvent) CloseHandle(hEvent); 460 free_alloc_p(&event_name); 461 free_alloc_p(&psd); 462 if (endpoint && (endpoint != rcvargs->opts->pszEndpoint)) 463 free_alloc_p(&endpoint); 464 } 465 466 // tell main thread to shutdown since it won't receive any more messages 467 worklist_add(CCMSG_QUIT, NULL, NULL, 0); 468 _endthread(); 469 } // End receiveLoop 470 471 472 473 /* ------------------------------------------------------------------------ */ 474 /* The connection listener thread waits forever for a call to the CCAPI_CLIENT_<UUID> 475 endpoint, ccapi_listen function to complete. If the call completes or gets an 476 RPC exception, it means the client has disappeared. 477 478 A separate connectionListener is started for each client that has connected to the server. 479 */ 480 481 void connectionListener(void* rpcargs) { 482 483 struct RpcRcvArgs* rcvargs = (struct RpcRcvArgs*)rpcargs; 484 RPC_STATUS status = FALSE; 485 char* endpoint; 486 unsigned char* pszOptions = NULL; 487 unsigned char * pszUuid = NULL; 488 489 endpoint = clientEndpoint((char*)rcvargs->uuid); 490 rpcState = (RPC_ASYNC_STATE*)malloc(sizeof(RPC_ASYNC_STATE)); 491 status = RpcAsyncInitializeHandle(rpcState, sizeof(RPC_ASYNC_STATE)); 492 cci_debug_printf(""); 493 cci_debug_printf("%s About to LISTEN to <%s>", __FUNCTION__, endpoint); 494 495 rpcState->UserInfo = rcvargs->uuid; 496 rpcState->NotificationType = RpcNotificationTypeApc; 497 rpcState->u.APC.NotificationRoutine = clientListener; 498 rpcState->u.APC.hThread = 0; 499 500 /* [If in use] Free previous binding: */ 501 if (bRpcHandleInited) { 502 // Free previous binding (could have been used to call ccapi_listen 503 // in a different client thread). 504 // Don't check result or update status. 505 RpcStringFree(&pszStringBinding); 506 RpcBindingFree(&SERVER_REPLY_RPC_HANDLE); 507 bRpcHandleInited = FALSE; 508 } 509 510 /* Set up binding to the client's endpoint: */ 511 if (!status) { 512 status = RpcStringBindingCompose( 513 pszUuid, 514 pszProtocolSequence, 515 pszNetworkAddress, 516 (RPC_CSTR)endpoint, 517 pszOptions, 518 &pszStringBinding); 519 } 520 521 /* Set the binding handle that will be used to bind to the server. */ 522 if (!status) { 523 status = RpcBindingFromStringBinding(pszStringBinding, &SERVER_REPLY_RPC_HANDLE); 524 } 525 if (!status) {bRpcHandleInited = TRUE;} 526 527 RpcTryExcept { 528 cci_debug_printf(" Calling remote procedure ccapi_listen"); 529 ccapi_listen(rpcState, SERVER_REPLY_RPC_HANDLE, CCMSG_LISTEN, &status); 530 /* Asynchronous call will return immediately. */ 531 } 532 RpcExcept(1) { 533 status = cci_check_error(RpcExceptionCode()); 534 } 535 RpcEndExcept 536 537 rcvargs->status = status; 538 } // End connectionListener 539 540 541 void RPC_ENTRY clientListener( 542 _RPC_ASYNC_STATE* pAsync, 543 void* Context, 544 RPC_ASYNC_EVENT Event 545 ) { 546 547 ccs_pipe_t pipe = ccs_win_pipe_new((char*)pAsync->UserInfo, NULL); 548 549 cci_debug_printf("%s(0x%X, ...) async routine for <0x%X:%s>!", 550 __FUNCTION__, pAsync, pAsync->UserInfo, pAsync->UserInfo); 551 552 worklist_add( CCMSG_DISCONNECT, 553 pipe, 554 NULL, /* No payload with connect request */ 555 (const time_t)0 ); /* No server session number with connect request */ 556 } 557 558 559 void printError( TCHAR* msg ) { 560 DWORD eNum; 561 TCHAR sysMsg[256]; 562 TCHAR* p; 563 564 eNum = GetLastError( ); 565 FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | 566 FORMAT_MESSAGE_IGNORE_INSERTS, 567 NULL, eNum, 568 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 569 sysMsg, 256, NULL ); 570 571 // Trim the end of the line and terminate it with a null 572 p = sysMsg; 573 while( ( *p > 31 ) || ( *p == 9 ) ) 574 ++p; 575 do { *p-- = 0; } while( ( p >= sysMsg ) && 576 ( ( *p == '.' ) || ( *p < 33 ) ) ); 577 578 // Display the message 579 cci_debug_printf("%s failed with error %d (%s)", msg, eNum, sysMsg); 580 } 581 582 583 RPC_STATUS send_init(char* clientUUID) { 584 RPC_STATUS status; 585 unsigned char * pszUuid = NULL; 586 unsigned char * pszOptions = NULL; 587 588 /* Use a convenience function to concatenate the elements of */ 589 /* the string binding into the proper sequence. */ 590 status = RpcStringBindingCompose(pszUuid, 591 pszProtocolSequence, 592 pszNetworkAddress, 593 (unsigned char*)clientEndpoint(clientUUID), 594 pszOptions, 595 &pszStringBinding); 596 if (status) {return (status);} 597 598 /* Set the binding handle that will be used to bind to the RPC server [the 'client']. */ 599 status = RpcBindingFromStringBinding(pszStringBinding, &SERVER_REPLY_RPC_HANDLE); 600 return (status); 601 } 602 603 RPC_STATUS send_finish() { 604 RPC_STATUS status; 605 /* Can't shut down client -- it runs listen function which */ 606 /* server uses to detect the client going away. */ 607 608 /* The calls to the remote procedures are complete. */ 609 /* Free the string and the binding handle */ 610 status = RpcStringFree(&pszStringBinding); // remote calls done; unbind 611 if (status) {return (status);} 612 613 status = RpcBindingFree(&SERVER_REPLY_RPC_HANDLE); // remote calls done; unbind 614 615 return (status); 616 } 617 618 RPC_STATUS send_connection_reply(ccs_pipe_t in_pipe) { 619 char* uuid = ccs_win_pipe_getUuid (in_pipe); 620 UINT64 h = ccs_win_pipe_getHandle(in_pipe); 621 RPC_STATUS status = send_init(uuid); 622 623 RpcTryExcept { 624 ccs_rpc_connect_reply( // make call with user message 625 CCMSG_CONNECT_REPLY, /* Message type */ 626 (unsigned char*)&h, /* client's tspdata* */ 627 (unsigned char*)uuid, 628 getMySST(), /* Server's session number = its start time */ 629 &status ); /* Return code */ 630 } 631 RpcExcept(1) { 632 cci_check_error(RpcExceptionCode()); 633 } 634 RpcEndExcept 635 636 status = send_finish(); 637 return (status); 638 } 639 640 RPC_STATUS GetPeerName( RPC_BINDING_HANDLE hClient, 641 LPTSTR pszClientName, 642 int iMaxLen) { 643 RPC_STATUS Status = RPC_S_OK; 644 RPC_BINDING_HANDLE hServer = NULL; 645 PTBYTE pszStringBinding = NULL; 646 PTBYTE pszClientNetAddr = NULL; 647 PTBYTE pszProtSequence = NULL; 648 649 memset(pszClientName, 0, iMaxLen * sizeof(TCHAR)); 650 651 __try { 652 // Create a partially bound server handle from the client handle. 653 Status = RpcBindingServerFromClient (hClient, &hServer); 654 if (Status != RPC_S_OK) __leave; 655 656 // Get the partially bound server string binding and parse it. 657 Status = RpcBindingToStringBinding (hServer, 658 &pszStringBinding); 659 if (Status != RPC_S_OK) __leave; 660 661 // String binding only contains protocol sequence and client 662 // address, and is not currently implemented for named pipes. 663 Status = RpcStringBindingParse (pszStringBinding, NULL, 664 &pszProtSequence, &pszClientNetAddr, 665 NULL, NULL); 666 if (Status != RPC_S_OK) 667 __leave; 668 int iLen = lstrlen(pszClientName) + 1; 669 if (iMaxLen < iLen) 670 Status = RPC_S_BUFFER_TOO_SMALL; 671 lstrcpyn(pszClientName, (LPCTSTR)pszClientNetAddr, iMaxLen); 672 } 673 __finally { 674 if (pszProtSequence) 675 RpcStringFree (&pszProtSequence); 676 677 if (pszClientNetAddr) 678 RpcStringFree (&pszClientNetAddr); 679 680 if (pszStringBinding) 681 RpcStringFree (&pszStringBinding); 682 683 if (hServer) 684 RpcBindingFree (&hServer); 685 } 686 return Status; 687 } 688 689 struct client_auth_info { 690 RPC_AUTHZ_HANDLE authz_handle; 691 unsigned char* server_principal; // need to RpcFreeString this 692 ULONG authn_level; 693 ULONG authn_svc; 694 ULONG authz_svc; 695 }; 696 697 RPC_STATUS 698 GetClientId( 699 RPC_BINDING_HANDLE hClient, 700 char* client_id, 701 int max_len, 702 client_auth_info* info 703 ) 704 { 705 RPC_AUTHZ_HANDLE authz_handle = 0; 706 unsigned char* server_principal = 0; 707 ULONG authn_level = 0; 708 ULONG authn_svc = 0; 709 ULONG authz_svc = 0; 710 RPC_STATUS status = 0; 711 712 memset(client_id, 0, max_len); 713 714 if (info) { 715 memset(info, 0, sizeof(client_auth_info)); 716 } 717 718 status = RpcBindingInqAuthClient(hClient, &authz_handle, 719 info ? &server_principal : 0, 720 &authn_level, &authn_svc, &authz_svc); 721 if (status == RPC_S_OK) 722 { 723 if (info) { 724 info->server_principal = server_principal; 725 info->authz_handle = authz_handle; 726 info->authn_level = authn_level; 727 info->authn_svc = authn_svc; 728 info->authz_svc = authz_svc; 729 } 730 731 if (authn_svc == RPC_C_AUTHN_WINNT) { 732 WCHAR* username = (WCHAR*)authz_handle; 733 int len = lstrlenW(username) + 1; 734 if (max_len < len) 735 status = RPC_S_BUFFER_TOO_SMALL; 736 _snprintf(client_id, max_len, "%S", username); 737 } else { 738 status = RPC_S_UNKNOWN_AUTHN_SERVICE; 739 } 740 } 741 return status; 742 } 743 744 char* 745 rpc_error_to_string( 746 RPC_STATUS status 747 ) 748 { 749 switch(status) { 750 case RPC_S_OK: 751 return "OK"; 752 case RPC_S_INVALID_BINDING: 753 return "Invalid binding"; 754 case RPC_S_WRONG_KIND_OF_BINDING: 755 return "Wrong binding"; 756 case RPC_S_BINDING_HAS_NO_AUTH: 757 RpcRaiseException(RPC_S_BINDING_HAS_NO_AUTH); 758 return "Binding has no auth"; 759 default: 760 return "BUG: I am confused"; 761 } 762 } 763 764 void 765 print_client_info( 766 RPC_STATUS peer_status, 767 const char* peer_name, 768 RPC_STATUS client_status, 769 const char* client_id, 770 client_auth_info* info 771 ) 772 { 773 if (peer_status == RPC_S_OK || peer_status == RPC_S_BUFFER_TOO_SMALL) { 774 cci_debug_printf("%s Peer Name is \"%s\"", __FUNCTION__, peer_name); 775 } else { 776 cci_debug_printf("%s Error %u getting Peer Name (%s)", 777 __FUNCTION__, peer_status, rpc_error_to_string(peer_status)); 778 } 779 780 if (client_status == RPC_S_OK || client_status == RPC_S_BUFFER_TOO_SMALL) { 781 if (info) { 782 cci_debug_printf("%s Client Auth Info" 783 "\tServer Principal: %s\n" 784 "\tAuthentication Level: %d\n" 785 "\tAuthentication Service: %d\n" 786 "\tAuthorization Service: %d\n", 787 __FUNCTION__, 788 info->server_principal, 789 info->authn_level, 790 info->authn_svc, 791 info->authz_svc); 792 } 793 cci_debug_printf("%s Client ID is \"%s\"", __FUNCTION__, client_id); 794 } else { 795 cci_debug_printf("%s Error getting Client Info (%u = %s)", 796 __FUNCTION__, client_status, rpc_error_to_string(client_status)); 797 } 798 } 799 800 DWORD sid_check() { 801 DWORD status = 0; 802 HANDLE hToken_c = 0; 803 HANDLE hToken_s = 0; 804 PTOKEN_USER ptu_c = 0; 805 PTOKEN_USER ptu_s = 0; 806 DWORD len = 0; 807 BOOL bImpersonate = FALSE; 808 809 // Note GetUserName will fail while impersonating at identify 810 // level. The workaround is to impersonate, OpenThreadToken, 811 // revert, call GetTokenInformation, and finally, call 812 // LookupAccountSid. 813 814 // XXX - Note: This workaround does not appear to work. 815 // OpenThreadToken fails with error 1346: "Either a requid 816 // impersonation level was not provided or the provided 817 // impersonation level is invalid". 818 819 status = RpcImpersonateClient(0); 820 821 if (!status) { 822 bImpersonate = TRUE; 823 if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken_c)) 824 status = GetLastError(); 825 } 826 827 if (!status) { 828 status = RpcRevertToSelf(); 829 } 830 831 if (!status) { 832 bImpersonate = FALSE; 833 834 len = 0; 835 GetTokenInformation(hToken_c, TokenUser, ptu_c, 0, &len); 836 if (len == 0) status = 1; 837 } 838 839 if (!status) { 840 if (!(ptu_c = (PTOKEN_USER)LocalAlloc(0, len))) 841 status = GetLastError(); 842 } 843 844 if (!status) { 845 if (!GetTokenInformation(hToken_c, TokenUser, ptu_c, len, &len)) 846 status = GetLastError(); 847 } 848 849 if (!status) { 850 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken_s)) 851 status = GetLastError(); 852 } 853 854 if (!status) { 855 len = 0; 856 GetTokenInformation(hToken_s, TokenUser, ptu_s, 0, &len); 857 if (len == 0) status = GetLastError(); 858 } 859 860 if (!status) { 861 if (!(ptu_s = (PTOKEN_USER)LocalAlloc(0, len))) 862 status = GetLastError(); 863 } 864 865 if (!status) { 866 if (!GetTokenInformation(hToken_s, TokenUser, ptu_s, len, &len)) 867 status = GetLastError(); 868 } 869 870 if (!EqualSid(ptu_s->User.Sid, ptu_c->User.Sid)) 871 status = RPC_S_ACCESS_DENIED; 872 873 /* Cleanup: */ 874 if (!hToken_c && !bImpersonate) 875 cci_debug_printf("%s Cannot impersonate (%u)", __FUNCTION__, status); 876 else if (!hToken_c) 877 cci_debug_printf("%s Failed to open client token (%u)", __FUNCTION__, status); 878 else if (bImpersonate) 879 cci_debug_printf("%s Failed to revert (%u)", __FUNCTION__, status); 880 else if (!ptu_c) 881 cci_debug_printf("%s Failed to get client token user info (%u)", 882 __FUNCTION__, status); 883 else if (!hToken_s) 884 cci_debug_printf("%s Failed to open server token (%u)", __FUNCTION__, status); 885 else if (!ptu_s) 886 cci_debug_printf("%s Failed to get server token user info (%u)", 887 __FUNCTION__, status); 888 else if (status == RPC_S_ACCESS_DENIED) 889 cci_debug_printf("%s SID **does not** match!", __FUNCTION__); 890 else if (status == RPC_S_OK) 891 cci_debug_printf("%s SID matches!", __FUNCTION__); 892 else 893 if (status) { 894 cci_debug_printf("%s unrecognized error %u", __FUNCTION__, status); 895 abort(); 896 } 897 898 if (bImpersonate) RpcRevertToSelf(); 899 if (hToken_c && hToken_c != INVALID_HANDLE_VALUE) 900 CloseHandle(hToken_c); 901 if (ptu_c) LocalFree(ptu_c); 902 if (hToken_s && hToken_s != INVALID_HANDLE_VALUE) 903 CloseHandle(hToken_s); 904 if (ptu_s) LocalFree(ptu_s); 905 if (status) cci_debug_printf("%s returning %u", __FUNCTION__, status); 906 return status; 907 } 908 909 RPC_STATUS RPC_ENTRY sec_callback( IN RPC_IF_ID *Interface, 910 IN void *Context) { 911 char peer_name[1024]; 912 char client_name[1024]; 913 RPC_STATUS peer_status; 914 RPC_STATUS client_status; 915 916 cci_debug_printf("%s", __FUNCTION__); 917 peer_status = GetPeerName(Context, peer_name, sizeof(peer_name)); 918 client_status = GetClientId(Context, client_name, sizeof(client_name), 0); 919 print_client_info(peer_status, peer_name, client_status, client_name, 0); 920 DWORD sid_status = sid_check(); 921 cci_debug_printf("%s returning (%u)", __FUNCTION__, sid_status); 922 return sid_status; 923 } 924 925 926 927 /*********************************************************************/ 928 /* MIDL allocate and free */ 929 /*********************************************************************/ 930 931 extern "C" void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len) { 932 return(malloc(len)); 933 } 934 935 extern "C" void __RPC_USER midl_user_free(void __RPC_FAR * ptr) { 936 free(ptr); 937 } 938 939 /* stubs */ 940 extern "C" cc_int32 941 ccs_os_notify_cache_collection_changed (ccs_cache_collection_t cc) 942 { 943 return 0; 944 } 945 946 extern "C" cc_int32 947 ccs_os_notify_ccache_changed (ccs_cache_collection_t cc, const char *name) 948 { 949 return 0; 950 } 951