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);
setMySST()85 void setMySST() {_sst = time(&_sst);}
getMySST()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
ccs_os_server_initialize(int argc,const char * argv[])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
ccs_os_server_cleanup(int argc,const char * argv[])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 */
ccs_os_server_listen_loop(int argc,const char * argv[])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
ccs_os_server_send_reply(ccs_pipe_t in_pipe,k5_ipc_stream in_reply_stream)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
Usage(const char * argv0)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 */
receiveLoop(void * rpcargs)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
connectionListener(void * rpcargs)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
clientListener(_RPC_ASYNC_STATE * pAsync,void * Context,RPC_ASYNC_EVENT Event)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
printError(TCHAR * msg)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
send_init(char * clientUUID)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
send_finish()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
send_connection_reply(ccs_pipe_t in_pipe)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
GetPeerName(RPC_BINDING_HANDLE hClient,LPTSTR pszClientName,int iMaxLen)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
GetClientId(RPC_BINDING_HANDLE hClient,char * client_id,int max_len,client_auth_info * info)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*
rpc_error_to_string(RPC_STATUS status)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
print_client_info(RPC_STATUS peer_status,const char * peer_name,RPC_STATUS client_status,const char * client_id,client_auth_info * info)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
sid_check()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
sec_callback(IN RPC_IF_ID * Interface,IN void * Context)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
midl_user_allocate(size_t len)931 extern "C" void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len) {
932 return(malloc(len));
933 }
934
midl_user_free(void __RPC_FAR * ptr)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
ccs_os_notify_cache_collection_changed(ccs_cache_collection_t cc)941 ccs_os_notify_cache_collection_changed (ccs_cache_collection_t cc)
942 {
943 return 0;
944 }
945
946 extern "C" cc_int32
ccs_os_notify_ccache_changed(ccs_cache_collection_t cc,const char * name)947 ccs_os_notify_ccache_changed (ccs_cache_collection_t cc, const char *name)
948 {
949 return 0;
950 }
951