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