f84239491dac005a1ea5865a1f74cc3547535ee0
[platform/upstream/krb5.git] / src / ccapi / server / win / ccs_os_server.cpp
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 occured 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 #if 0
249                 cci_debug_printf("%s: processing WorkItem msg:%ld pipeUUID:<%s> pipeHandle:0x%X SST:%ld",
250                     __FUNCTION__, rpcmsg, uuid, ccs_win_pipe_getHandle(pipe), serverStartTime);
251 #endif
252                 if (serverStartTime <= getMySST()) {
253                     switch (rpcmsg) {
254                         case CCMSG_CONNECT: {
255                             cci_debug_printf("  Processing CONNECT");
256                             rpcargs.uuid    = (unsigned char*)uuid;
257
258                             // Even if a disconnect message is received before this code finishes,
259                             //  it won't be dequeued and processed until after this code finishes.
260                             //  So we can add the client after starting the connection listener.
261                             connectionListener((void*)&rpcargs);
262                             status  = rpcargs.status;
263
264                             if (!status) {
265                                 status = ccs_server_add_client(pipe);
266                                 }
267                             if (!status) {status = send_connection_reply(pipe);}
268                             break;
269                             }
270                         case CCMSG_DISCONNECT: {
271                             cci_debug_printf("  Processing DISCONNECT");
272                             if (!status) {
273                                 status = ccs_server_remove_client(pipe);
274                                 }
275                             break;
276                             }
277                         case CCMSG_REQUEST:
278                             cci_debug_printf("  Processing REQUEST");
279                             ccs_pipe_copy(&pipe2, pipe);
280                             // Dispatch message here, setting both pipes to the client UUID:
281                             err = ccs_server_handle_request (pipe, pipe2, buf);
282                             break;
283                         case CCMSG_PING:
284                             cci_debug_printf("  Processing PING");
285                             err = krb5int_ipc_stream_new  (&stream);
286                             err = krb5int_ipc_stream_write(stream, "This is a test of the emergency broadcasting system", 52);
287                             err = ccs_os_server_send_reply(pipe, stream);
288                             break;
289                         case CCMSG_QUIT:
290                             bQuitIfNoClients = TRUE;
291                             break;
292                         default:
293                             cci_debug_printf("Huh?  Received invalid message type %ld from UUID:<%s>",
294                                 rpcmsg, uuid);
295                             break;
296                         }
297                     if (buf)        krb5int_ipc_stream_release(buf);
298                     /* Don't free uuid, which was allocated here.  A pointer to it is in the
299                        rpcargs struct which was passed to connectionListener which will be
300                        received by ccapi_listen when the client exits.  ccapi_listen needs
301                        the uuid to know which client to disconnect.
302                      */
303                     }
304                 // Server's start time is different from what the client thinks.
305                 // That means the server has rebooted since the client connected.
306                 else {
307                     cci_debug_printf("Whoops!  Server has rebooted since client established connection.");
308                     }
309                 }
310             else {cci_debug_printf("Huh?  Queue not empty but no item to remove.");}
311             }
312         }
313     return cci_check_error (err);
314     }
315
316 /* ------------------------------------------------------------------------ */
317
318 cc_int32 ccs_os_server_send_reply (ccs_pipe_t   in_pipe,
319                                    k5_ipc_stream in_reply_stream) {
320
321     /* ccs_pipe_t in_reply_pipe     is a char* reply endpoint.
322        k5_ipc_stream in_reply_stream is the data to be sent.
323      */
324
325     cc_int32    err     = 0;
326     char*       uuid    = ccs_win_pipe_getUuid(in_pipe);
327     UINT64      h       = ccs_win_pipe_getHandle(in_pipe);
328
329     if (!err) {
330         err = send_init(uuid);      // Sets RPC handle to be used.
331         }
332
333     if (!err) {
334         RpcTryExcept {
335             long    status;
336             ccs_rpc_request_reply(                  // make call with user message
337                 CCMSG_REQUEST_REPLY,                /* Message type */
338                 (unsigned char*)&h,                 /* client's tspdata* */
339                 (unsigned char*)uuid,
340                 getMySST(),
341                 krb5int_ipc_stream_size(in_reply_stream),   /* Length of buffer */
342                 (const unsigned char*)krb5int_ipc_stream_data(in_reply_stream),   /* Data buffer */
343                 &status );                          /* Return code */
344             }
345         RpcExcept(1) {
346             cci_check_error(RpcExceptionCode());
347             }
348         RpcEndExcept
349         }
350
351     /*  The calls to the remote procedures are complete. */
352     /*  Free whatever we allocated:                      */
353     err = RpcBindingFree(&SERVER_REPLY_RPC_HANDLE);
354
355     return cci_check_error (err);
356     }
357
358
359 /* Windows-specific routines: */
360
361 void Usage(const char* argv0) {
362     printf("Usage:\n");
363     printf("%s [m maxcalls] [n mincalls] [f dontwait] [h|?]]\n", argv0);
364     printf("    CCAPI server process.\n");
365     printf("    h|? whow usage message. <\n");
366     }
367
368 /* ------------------------------------------------------------------------ */
369 /* The receive thread repeatedly issues RpcServerListen.
370    When a message arrives, it is handled in the RPC procedure.
371  */
372 void    receiveLoop(void* rpcargs) {
373
374     struct RpcRcvArgs*      rcvargs     = (struct RpcRcvArgs*)rpcargs;
375     RPC_STATUS              status      = FALSE;
376     unsigned char*          pszSecurity = NULL;
377     LPSTR                   endpoint    = NULL;
378     LPSTR                   event_name  = NULL;
379     PSECURITY_DESCRIPTOR    psd         = NULL;
380     HANDLE                  hEvent      = 0;
381     Init::InitInfo          info;
382
383     cci_debug_printf("THREAD BEGIN: %s", __FUNCTION__);
384
385     status = Init::Info(info);
386
387     /* Build complete RPC endpoint using previous CCAPI implementation: */
388     if (!status) {
389         if (!rcvargs->opts->pszEndpoint) {
390             if (!status) {
391                 status  = alloc_name(&endpoint,     "ep", isNT());
392                 }
393
394             if (!status) {
395                 status  = alloc_name(&event_name,   "startup", isNT());
396                 }
397
398             if (!status) {
399                  hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, event_name);
400                 // We ignore any error opening the event because we do not know who started us.
401                 //  [Comment paraphrased from previous implementation, whence it was copied.]
402                 }
403             }
404         else {
405             endpoint = rcvargs->opts->pszEndpoint;
406             }
407         }
408
409     cci_debug_printf("%s Registering endpoint %s", __FUNCTION__, endpoint);
410
411     if (!status && isNT()) {
412         status = alloc_own_security_descriptor_NT(&psd);
413         }
414
415     if (!status) {
416         status = RpcServerUseProtseqEp(rcvargs->protocolSequence,
417                                        rcvargs->opts->cMaxCalls,
418                                        (RPC_CSTR)endpoint,
419                                        rcvargs->opts->bDontProtect ? 0 : psd);  // SD
420         }
421
422     if (!status) {
423         status = RpcServerRegisterAuthInfo(0, // server principal
424                                            RPC_C_AUTHN_WINNT,
425                                            0,
426                                            0);
427         }
428
429     while (bListen && !status) {
430         cci_debug_printf("%s is listening ...", __FUNCTION__);
431
432         if (!info.isNT) {
433             status = RpcServerRegisterIf(ccs_request_ServerIfHandle,    // interface
434                                          NULL,                          // MgrTypeUuid
435                                          NULL);                         // MgrEpv; null means use default
436             }
437         else {
438             status = info.fRpcServerRegisterIfEx(ccs_request_ServerIfHandle,  // interface
439                                          NULL,                          // MgrTypeUuid
440                                          NULL,                          // MgrEpv; 0 means default
441                                          RPC_IF_ALLOW_SECURE_ONLY,
442                                          rcvargs->opts->cMaxCalls,
443                                          rcvargs->opts->bSecCallback ?
444                                          (RPC_IF_CALLBACK_FN*)sec_callback : 0 );
445             }
446
447         if (!status) {
448             status = RpcServerListen(rcvargs->opts->cMinCalls,
449                                      rcvargs->opts->cMaxCalls,
450                                      rcvargs->opts->fDontWait);
451             }
452
453         if (!status) {
454             if (rcvargs->opts->fDontWait) {
455                 if (hEvent) SetEvent(hEvent);   // Ignore any error -- SetEvent is an optimization.
456                 status = RpcMgmtWaitServerListen();
457                 }
458             }
459         }
460
461     if (status) {           // Cleanup in case of errors:
462         if (hEvent) CloseHandle(hEvent);
463         free_alloc_p(&event_name);
464         free_alloc_p(&psd);
465         if (endpoint && (endpoint != rcvargs->opts->pszEndpoint))
466             free_alloc_p(&endpoint);
467         }
468
469     // tell main thread to shutdown since it won't receive any more messages
470     worklist_add(CCMSG_QUIT, NULL, NULL, 0);
471     _endthread();
472     }   // End receiveLoop
473
474
475 #if 0
476
477     return status;
478 }
479 #endif
480
481
482
483 /* ------------------------------------------------------------------------ */
484 /* The connection listener thread waits forever for a call to the CCAPI_CLIENT_<UUID>
485    endpoint, ccapi_listen function to complete.  If the call completes or gets an
486    RPC exception, it means the client has disappeared.
487
488    A separate connectionListener is started for each client that has connected to the server.
489  */
490
491 void    connectionListener(void* rpcargs) {
492
493     struct RpcRcvArgs*  rcvargs     = (struct RpcRcvArgs*)rpcargs;
494     RPC_STATUS          status      = FALSE;
495     char*               endpoint;
496     unsigned char*      pszOptions  = NULL;
497     unsigned char *     pszUuid     = NULL;
498
499     endpoint    = clientEndpoint((char*)rcvargs->uuid);
500     rpcState    = (RPC_ASYNC_STATE*)malloc(sizeof(RPC_ASYNC_STATE));
501     status      = RpcAsyncInitializeHandle(rpcState, sizeof(RPC_ASYNC_STATE));
502     cci_debug_printf("");
503     cci_debug_printf("%s About to LISTEN to <%s>", __FUNCTION__, endpoint);
504
505     rpcState->UserInfo                  = rcvargs->uuid;
506     rpcState->NotificationType          = RpcNotificationTypeApc;
507     rpcState->u.APC.NotificationRoutine = clientListener;
508     rpcState->u.APC.hThread             = 0;
509
510     /* [If in use] Free previous binding: */
511     if (bRpcHandleInited) {
512         // Free previous binding (could have been used to call ccapi_listen
513         //  in a different client thread).
514         // Don't check result or update status.
515         RpcStringFree(&pszStringBinding);
516         RpcBindingFree(&SERVER_REPLY_RPC_HANDLE);
517         bRpcHandleInited  = FALSE;
518         }
519
520     /* Set up binding to the client's endpoint: */
521     if (!status) {
522         status = RpcStringBindingCompose(
523                     pszUuid,
524                     pszProtocolSequence,
525                     pszNetworkAddress,
526                     (RPC_CSTR)endpoint,
527                     pszOptions,
528                     &pszStringBinding);
529         }
530
531     /* Set the binding handle that will be used to bind to the server. */
532     if (!status) {
533         status = RpcBindingFromStringBinding(pszStringBinding, &SERVER_REPLY_RPC_HANDLE);
534         }
535     if (!status) {bRpcHandleInited  = TRUE;}
536
537     RpcTryExcept {
538         cci_debug_printf("  Calling remote procedure ccapi_listen");
539         ccapi_listen(rpcState, SERVER_REPLY_RPC_HANDLE, CCMSG_LISTEN, &status);
540         /* Asynchronous call will return immediately. */
541         }
542     RpcExcept(1) {
543         status = cci_check_error(RpcExceptionCode());
544         }
545     RpcEndExcept
546
547     rcvargs->status = status;
548     }   // End connectionListener
549
550
551 void RPC_ENTRY clientListener(
552     _RPC_ASYNC_STATE* pAsync,
553     void* Context,
554     RPC_ASYNC_EVENT Event
555     ) {
556
557     ccs_pipe_t pipe = ccs_win_pipe_new((char*)pAsync->UserInfo, NULL);
558
559     cci_debug_printf("%s(0x%X, ...) async routine for <0x%X:%s>!",
560         __FUNCTION__, pAsync, pAsync->UserInfo, pAsync->UserInfo);
561
562     worklist_add(   CCMSG_DISCONNECT,
563                     pipe,
564                     NULL,               /* No payload with connect request */
565                     (const time_t)0 );  /* No server session number with connect request */
566     }
567
568
569 void printError( TCHAR* msg ) {
570     DWORD eNum;
571     TCHAR sysMsg[256];
572     TCHAR* p;
573
574     eNum = GetLastError( );
575     FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM |
576          FORMAT_MESSAGE_IGNORE_INSERTS,
577          NULL, eNum,
578          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
579          sysMsg, 256, NULL );
580
581     // Trim the end of the line and terminate it with a null
582     p = sysMsg;
583     while( ( *p > 31 ) || ( *p == 9 ) )
584         ++p;
585     do { *p-- = 0; } while( ( p >= sysMsg ) &&
586                           ( ( *p == '.' ) || ( *p < 33 ) ) );
587
588     // Display the message
589     cci_debug_printf("%s failed with error %d (%s)", msg, eNum, sysMsg);
590     }
591
592
593 RPC_STATUS send_init(char* clientUUID) {
594     RPC_STATUS      status;
595     unsigned char * pszUuid             = NULL;
596     unsigned char * pszOptions          = NULL;
597
598     /* Use a convenience function to concatenate the elements of */
599     /* the string binding into the proper sequence.              */
600     status = RpcStringBindingCompose(pszUuid,
601                                      pszProtocolSequence,
602                                      pszNetworkAddress,
603                                      (unsigned char*)clientEndpoint(clientUUID),
604                                      pszOptions,
605                                      &pszStringBinding);
606     if (status) {return (status);}
607
608     /* Set the binding handle that will be used to bind to the RPC server [the 'client']. */
609     status = RpcBindingFromStringBinding(pszStringBinding, &SERVER_REPLY_RPC_HANDLE);
610     return (status);
611     }
612
613 RPC_STATUS send_finish() {
614     RPC_STATUS  status;
615     /* Can't shut down client -- it runs listen function which  */
616     /* server uses to detect the client going away.             */
617
618     /*  The calls to the remote procedures are complete. */
619     /*  Free the string and the binding handle           */
620     status = RpcStringFree(&pszStringBinding);  // remote calls done; unbind
621     if (status) {return (status);}
622
623     status = RpcBindingFree(&SERVER_REPLY_RPC_HANDLE);  // remote calls done; unbind
624
625     return (status);
626     }
627
628 RPC_STATUS send_connection_reply(ccs_pipe_t in_pipe) {
629     char*       uuid    = ccs_win_pipe_getUuid  (in_pipe);
630     UINT64      h       = ccs_win_pipe_getHandle(in_pipe);
631     RPC_STATUS  status  = send_init(uuid);
632
633     RpcTryExcept {
634         ccs_rpc_connect_reply(      // make call with user message
635             CCMSG_CONNECT_REPLY,    /* Message type */
636             (unsigned char*)&h,      /* client's tspdata* */
637             (unsigned char*)uuid,
638             getMySST(),             /* Server's session number = it's start time */
639             &status );              /* Return code */
640         }
641     RpcExcept(1) {
642         cci_check_error(RpcExceptionCode());
643         }
644     RpcEndExcept
645
646     status  = send_finish();
647     return (status);
648     }
649
650 #if 0
651 DWORD alloc_name(LPSTR* pname, LPSTR postfix) {
652     DWORD len = strlen(sessID) + 1 + strlen(postfix) + 1;
653
654     *pname = (LPSTR)malloc(len);
655     if (!*pname) return GetLastError();
656     _snprintf(*pname, len, "%s.%s", sessID, postfix);
657     return 0;
658     }
659 #endif
660
661 RPC_STATUS GetPeerName( RPC_BINDING_HANDLE hClient,
662                         LPTSTR pszClientName,
663                         int iMaxLen) {
664     RPC_STATUS Status           = RPC_S_OK;
665     RPC_BINDING_HANDLE hServer  = NULL;
666     PTBYTE pszStringBinding     = NULL;
667     PTBYTE pszClientNetAddr     = NULL;
668     PTBYTE pszProtSequence      = NULL;
669
670     memset(pszClientName, 0, iMaxLen * sizeof(TCHAR));
671
672     __try {
673         // Create a partially bound server handle from the client handle.
674         Status = RpcBindingServerFromClient (hClient, &hServer);
675         if (Status != RPC_S_OK) __leave;
676
677         // Get the partially bound server string binding and parse it.
678         Status = RpcBindingToStringBinding (hServer,
679                                             &pszStringBinding);
680         if (Status != RPC_S_OK) __leave;
681
682         // String binding only contains protocol sequence and client
683         // address, and is not currently implemented for named pipes.
684         Status = RpcStringBindingParse (pszStringBinding, NULL,
685                                         &pszProtSequence, &pszClientNetAddr,
686                                         NULL, NULL);
687         if (Status != RPC_S_OK)
688             __leave;
689         int iLen = lstrlen(pszClientName) + 1;
690         if (iMaxLen < iLen)
691             Status = RPC_S_BUFFER_TOO_SMALL;
692         lstrcpyn(pszClientName, (LPCTSTR)pszClientNetAddr, iMaxLen);
693     }
694     __finally {
695         if (pszProtSequence)
696             RpcStringFree (&pszProtSequence);
697
698         if (pszClientNetAddr)
699             RpcStringFree (&pszClientNetAddr);
700
701         if (pszStringBinding)
702             RpcStringFree (&pszStringBinding);
703
704         if (hServer)
705             RpcBindingFree (&hServer);
706     }
707     return Status;
708 }
709
710 struct client_auth_info {
711     RPC_AUTHZ_HANDLE authz_handle;
712     unsigned char* server_principal; // need to RpcFreeString this
713     ULONG authn_level;
714     ULONG authn_svc;
715     ULONG authz_svc;
716 };
717
718 RPC_STATUS
719 GetClientId(
720     RPC_BINDING_HANDLE hClient,
721     char* client_id,
722     int max_len,
723     client_auth_info* info
724     )
725 {
726     RPC_AUTHZ_HANDLE authz_handle = 0;
727     unsigned char* server_principal = 0;
728     ULONG authn_level = 0;
729     ULONG authn_svc = 0;
730     ULONG authz_svc = 0;
731     RPC_STATUS status = 0;
732
733     memset(client_id, 0, max_len);
734
735     if (info) {
736         memset(info, 0, sizeof(client_auth_info));
737     }
738
739     status = RpcBindingInqAuthClient(hClient, &authz_handle,
740                                      info ? &server_principal : 0,
741                                      &authn_level, &authn_svc, &authz_svc);
742     if (status == RPC_S_OK)
743     {
744         if (info) {
745             info->server_principal = server_principal;
746             info->authz_handle = authz_handle;
747             info->authn_level = authn_level;
748             info->authn_svc = authn_svc;
749             info->authz_svc = authz_svc;
750         }
751
752         if (authn_svc == RPC_C_AUTHN_WINNT) {
753             WCHAR* username = (WCHAR*)authz_handle;
754             int len = lstrlenW(username) + 1;
755             if (max_len < len)
756                 status = RPC_S_BUFFER_TOO_SMALL;
757             _snprintf(client_id, max_len, "%S", username);
758         } else {
759             status = RPC_S_UNKNOWN_AUTHN_SERVICE;
760         }
761     }
762     return status;
763 }
764
765 char*
766 rpc_error_to_string(
767     RPC_STATUS status
768     )
769 {
770     switch(status) {
771     case RPC_S_OK:
772         return "OK";
773     case RPC_S_INVALID_BINDING:
774         return "Invalid binding";
775     case RPC_S_WRONG_KIND_OF_BINDING:
776         return "Wrong binding";
777     case RPC_S_BINDING_HAS_NO_AUTH:
778         RpcRaiseException(RPC_S_BINDING_HAS_NO_AUTH);
779         return "Binding has no auth";
780     default:
781         return "BUG: I am confused";
782     }
783 }
784
785 void
786 print_client_info(
787     RPC_STATUS peer_status,
788     const char* peer_name,
789     RPC_STATUS client_status,
790     const char* client_id,
791     client_auth_info* info
792     )
793 {
794     if (peer_status == RPC_S_OK || peer_status == RPC_S_BUFFER_TOO_SMALL) {
795         cci_debug_printf("%s Peer Name is \"%s\"", __FUNCTION__, peer_name);
796     } else {
797         cci_debug_printf("%s Error %u getting Peer Name (%s)",
798                      __FUNCTION__, peer_status, rpc_error_to_string(peer_status));
799     }
800
801     if (client_status == RPC_S_OK || client_status == RPC_S_BUFFER_TOO_SMALL) {
802         if (info) {
803             cci_debug_printf("%s Client Auth Info"
804                          "\tServer Principal:       %s\n"
805                          "\tAuthentication Level:   %d\n"
806                          "\tAuthentication Service: %d\n"
807                          "\tAuthorization Service:  %d\n",
808                          __FUNCTION__,
809                          info->server_principal,
810                          info->authn_level,
811                          info->authn_svc,
812                          info->authz_svc);
813         }
814         cci_debug_printf("%s Client ID is \"%s\"", __FUNCTION__, client_id);
815     } else {
816         cci_debug_printf("%s Error getting Client Info (%u = %s)",
817                      __FUNCTION__, client_status, rpc_error_to_string(client_status));
818     }
819 }
820
821 DWORD sid_check() {
822     DWORD status = 0;
823     HANDLE hToken_c = 0;
824     HANDLE hToken_s = 0;
825     PTOKEN_USER ptu_c = 0;
826     PTOKEN_USER ptu_s = 0;
827     DWORD len = 0;
828     BOOL bImpersonate = FALSE;
829
830     // Note GetUserName will fail while impersonating at identify
831     // level.  The workaround is to impersonate, OpenThreadToken,
832     // revert, call GetTokenInformation, and finally, call
833     // LookupAccountSid.
834
835     // XXX - Note: This workaround does not appear to work.
836     // OpenThreadToken fails with error 1346: "Either a requid
837     // impersonation level was not provided or the provided
838     // impersonation level is invalid".
839
840     status = RpcImpersonateClient(0);
841
842     if (!status) {
843         bImpersonate = TRUE;
844         if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken_c))
845             status = GetLastError();
846         }
847
848     if (!status) {
849         status = RpcRevertToSelf();
850         }
851
852     if (!status) {
853         bImpersonate = FALSE;
854
855         len = 0;
856         GetTokenInformation(hToken_c, TokenUser, ptu_c, 0, &len);
857         if (len == 0) status = 1;
858         }
859
860     if (!status) {
861         if (!(ptu_c = (PTOKEN_USER)LocalAlloc(0, len)))
862             status = GetLastError();
863         }
864
865     if (!status) {
866         if (!GetTokenInformation(hToken_c, TokenUser, ptu_c, len, &len))
867             status = GetLastError();
868         }
869
870     if (!status) {
871         if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken_s))
872             status = GetLastError();
873         }
874
875     if (!status) {
876         len = 0;
877         GetTokenInformation(hToken_s, TokenUser, ptu_s, 0, &len);
878         if (len == 0) status = GetLastError();
879         }
880
881     if (!status) {
882         if (!(ptu_s = (PTOKEN_USER)LocalAlloc(0, len)))
883             status = GetLastError();
884         }
885
886     if (!status) {
887         if (!GetTokenInformation(hToken_s, TokenUser, ptu_s, len, &len))
888             status = GetLastError();
889         }
890
891     if (!EqualSid(ptu_s->User.Sid, ptu_c->User.Sid))
892         status = RPC_S_ACCESS_DENIED;
893
894 /* Cleanup: */
895     if (!hToken_c && !bImpersonate)
896         cci_debug_printf("%s Cannot impersonate (%u)", __FUNCTION__, status);
897     else if (!hToken_c)
898         cci_debug_printf("%s Failed to open client token (%u)", __FUNCTION__, status);
899     else if (bImpersonate)
900         cci_debug_printf("%s Failed to revert (%u)", __FUNCTION__, status);
901     else if (!ptu_c)
902         cci_debug_printf("%s Failed to get client token user info (%u)",
903                      __FUNCTION__, status);
904     else if (!hToken_s)
905         cci_debug_printf("%s Failed to open server token (%u)", __FUNCTION__, status);
906     else if (!ptu_s)
907         cci_debug_printf("%s Failed to get server token user info (%u)",
908                      __FUNCTION__, status);
909     else if (status == RPC_S_ACCESS_DENIED)
910         cci_debug_printf("%s SID **does not** match!", __FUNCTION__);
911     else if (status == RPC_S_OK)
912         cci_debug_printf("%s SID matches!", __FUNCTION__);
913     else
914         if (status) {
915             cci_debug_printf("%s unrecognized error %u", __FUNCTION__, status);
916             abort();
917             }
918
919     if (bImpersonate)   RpcRevertToSelf();
920     if (hToken_c && hToken_c != INVALID_HANDLE_VALUE)
921         CloseHandle(hToken_c);
922     if (ptu_c)          LocalFree(ptu_c);
923     if (hToken_s && hToken_s != INVALID_HANDLE_VALUE)
924         CloseHandle(hToken_s);
925     if (ptu_s)          LocalFree(ptu_s);
926     if (status) cci_debug_printf("%s returning %u", __FUNCTION__, status);
927     return status;
928     }
929
930 RPC_STATUS RPC_ENTRY sec_callback(  IN RPC_IF_ID *Interface,
931                                     IN void *Context) {
932     char peer_name[1024];
933     char client_name[1024];
934     RPC_STATUS peer_status;
935     RPC_STATUS client_status;
936
937     cci_debug_printf("%s", __FUNCTION__);
938     peer_status = GetPeerName(Context, peer_name, sizeof(peer_name));
939     client_status = GetClientId(Context, client_name, sizeof(client_name), 0);
940     print_client_info(peer_status, peer_name, client_status, client_name, 0);
941     DWORD sid_status = sid_check();
942     cci_debug_printf("%s returning (%u)", __FUNCTION__, sid_status);
943     return sid_status;
944     }
945
946
947
948 /*********************************************************************/
949 /*                 MIDL allocate and free                            */
950 /*********************************************************************/
951
952 extern "C" void  __RPC_FAR * __RPC_USER midl_user_allocate(size_t len) {
953     return(malloc(len));
954     }
955
956 extern "C" void __RPC_USER midl_user_free(void __RPC_FAR * ptr) {
957     free(ptr);
958     }
959
960 /* stubs */
961 extern "C" cc_int32
962 ccs_os_notify_cache_collection_changed (ccs_cache_collection_t cc)
963 {
964     return 0;
965 }
966
967 extern "C" cc_int32
968 ccs_os_notify_ccache_changed (ccs_cache_collection_t cc, const char *name)
969 {
970     return 0;
971 }