Imported Upstream version 1.15.1
[platform/upstream/krb5.git] / src / ccapi / lib / win / ccapi_os_ipc.cxx
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 extern "C" {
28 #include "k5-thread.h"
29 #include "ccapi_os_ipc.h"
30 #include "cci_debugging.h"
31 #include "ccs_reply.h"
32 #include "ccs_request.h"
33 #include "ccutils.h"
34 #include "tls.h"
35 #include "util.h"
36 #include "win-utils.h"
37     }
38
39 #include "autolock.hxx"
40 #include "CredentialsCache.h"
41 #include "secure.hxx"
42 #include "opts.hxx"
43 #include "client.h"
44
45 extern "C" DWORD GetTlsIndex();
46
47 #define SECONDS_TO_WAIT 10
48 #define CLIENT_REQUEST_RPC_HANDLE ccs_request_IfHandle
49
50 extern HANDLE           hCCAPIv2Mutex;
51 ParseOpts::Opts         opts                = { 0 };
52 PSECURITY_ATTRIBUTES    psa                 = 0;
53 SECURITY_ATTRIBUTES     sa                  = { 0 };
54
55 /* The layout of the rest of this module:  
56
57    The entrypoints defined in ccs_os_ipc.h:
58     cci_os_ipc_thread_init
59     cci_os_ipc
60
61    Other routines needed by those four.
62     cci_os_connect
63     handle_exception
64  */
65
66 cc_int32        ccapi_connect(const struct tspdata* tsp);
67 static DWORD    handle_exception(DWORD code, struct tspdata* ptspdata);
68
69 extern "C" {
70 cc_int32        cci_os_ipc_msg( cc_int32        in_launch_server,
71                                 k5_ipc_stream    in_request_stream,
72                                 cc_int32        in_msg,
73                                 k5_ipc_stream*   out_reply_stream);
74     }
75
76 /* ------------------------------------------------------------------------ */
77
78 extern "C" cc_int32 cci_os_ipc_process_init (void) {
79     RPC_STATUS status;
80
81     if (!isNT()) {
82         status = RpcServerRegisterIf(ccs_reply_ServerIfHandle,  // interface
83                                      NULL,                      // MgrTypeUuid
84                                      NULL);                     // MgrEpv; null means use default
85         }
86     else {
87         status = RpcServerRegisterIfEx(ccs_reply_ServerIfHandle,  // interface
88                                        NULL,                      // MgrTypeUuid
89                                        NULL,                      // MgrEpv; 0 means default
90                                        RPC_IF_ALLOW_SECURE_ONLY,
91                                        RPC_C_LISTEN_MAX_CALLS_DEFAULT,
92                                        NULL);                     // No security callback.
93         }
94     cci_check_error(status);
95
96     if (!status) {
97         status = RpcServerRegisterAuthInfo(0, // server principal
98                                            RPC_C_AUTHN_WINNT,
99                                            0,
100                                            0 );
101         cci_check_error(status);
102         }
103
104     return status; // ugh. needs translation
105 }
106
107 /* ------------------------------------------------------------------------ */
108
109 extern "C" cc_int32 cci_os_ipc_thread_init (void) {
110     cc_int32                    err         = ccNoError;
111     struct tspdata*             ptspdata;
112     HANDLE                      replyEvent  = NULL;
113     UUID __RPC_FAR              uuid;
114     RPC_CSTR __RPC_FAR          uuidString  = NULL;
115     char*                       endpoint    = NULL;
116
117     if (!GetTspData(GetTlsIndex(), &ptspdata)) return ccErrNoMem;
118
119     err   = cci_check_error(UuidCreate(&uuid)); // Get a UUID
120     if (err == RPC_S_OK) {                      // Convert to string
121         err = UuidToString(&uuid, &uuidString);
122         cci_check_error(err);
123         }
124     if (!err) {                                 // Save in thread local storage
125         tspdata_setUUID(ptspdata, uuidString);
126         endpoint = clientEndpoint((const char *)uuidString);
127         err = RpcServerUseProtseqEp((RPC_CSTR)"ncalrpc",
128                                     RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
129                                     (RPC_CSTR)endpoint,
130                                     sa.lpSecurityDescriptor);  // SD
131         free(endpoint);
132         cci_check_error(err);
133         }
134
135 #if 0
136     cci_debug_printf("%s UUID:<%s>", __FUNCTION__, tspdata_getUUID(ptspdata));
137 #endif
138     // Initialize old CCAPI if necessary:
139     if (!err) if (!Init::  Initialized()) err = Init::  Initialize( );
140     if (!err) if (!Client::Initialized()) err = Client::Initialize(0);
141
142     if (!err) {
143         /* Whenever a reply to an RPC request is received, the RPC caller needs to
144            know when the reply has been received.  It does that by waiting for a 
145            client-specific event to be set.  Define the event name to be <UUID>_reply:  */
146         replyEvent = createThreadEvent((char*)uuidString, REPLY_SUFFIX);
147         }
148
149     if (!err) {
150         static bool bListening = false;
151         if (!bListening) {
152             err = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
153             cci_check_error(err);
154             }
155             bListening = err == 0;
156         }
157
158     if (replyEvent) tspdata_setReplyEvent(ptspdata, replyEvent);
159     else            err = cci_check_error(GetLastError());
160
161     if (uuidString) RpcStringFree(&uuidString);
162
163     return cci_check_error(err);
164     }
165
166
167 /* ------------------------------------------------------------------------ */
168
169 cc_int32 cci_os_ipc (cc_int32      in_launch_server,
170                      k5_ipc_stream in_request_stream,
171                      k5_ipc_stream* out_reply_stream) {
172     return cci_os_ipc_msg(  in_launch_server, 
173                             in_request_stream, 
174                             CCMSG_REQUEST, 
175                             out_reply_stream);
176     }
177
178 extern "C" cc_int32 cci_os_ipc_msg( cc_int32        in_launch_server,
179                                     k5_ipc_stream    in_request_stream,
180                                     cc_int32        in_msg,
181                                     k5_ipc_stream*   out_reply_stream) {
182
183     cc_int32        err             = ccNoError;
184     cc_int32        done            = FALSE;
185     cc_int32        try_count       = 0;
186     cc_int32        server_died     = FALSE;
187     TCHAR*          pszStringBinding= NULL;
188     struct tspdata* ptspdata        = NULL;
189     char*           uuid            = NULL;
190     int             lenUUID         = 0;
191     unsigned int    trycount        = 0;
192     time_t          sst             = 0;
193     STARTUPINFO             si      = { 0 };
194     PROCESS_INFORMATION     pi      = { 0 };
195     HANDLE          replyEvent      = 0;
196     BOOL            bCCAPI_Connected= FALSE;
197     BOOL            bListening      = FALSE;
198     unsigned char tspdata_handle[8] = { 0 };
199
200     if (!in_request_stream) { err = cci_check_error (ccErrBadParam); }
201     if (!out_reply_stream ) { err = cci_check_error (ccErrBadParam); }
202     
203     if (!GetTspData(GetTlsIndex(), &ptspdata)) {return ccErrBadParam;}
204     bListening = tspdata_getListening(ptspdata);
205     if (!bListening) {
206         err = cci_check_error(cci_os_ipc_thread_init());
207         bListening = !err;
208         tspdata_setListening(ptspdata, bListening);
209         }
210
211     bCCAPI_Connected = tspdata_getConnected  (ptspdata);
212     replyEvent       = tspdata_getReplyEvent (ptspdata);
213     sst              = tspdata_getSST (ptspdata);
214     uuid             = tspdata_getUUID(ptspdata);
215
216     // The lazy connection to the server has been put off as long as possible!
217     // ccapi_connect starts listening for replies as an RPC server and then
218     //   calls ccs_rpc_connect.
219     if (!err && !bCCAPI_Connected) {
220         err                 = cci_check_error(ccapi_connect(ptspdata));
221         bCCAPI_Connected    = !err;
222         tspdata_setConnected(ptspdata, bCCAPI_Connected);
223         }
224
225     // Clear replyEvent so we can detect when a reply to our request has been received:
226     ResetEvent(replyEvent);
227     
228     //++ Use the old CCAPI implementation to try to talk to the server:
229     // It has all the code to use the RPC in a thread-safe way, make the endpoint, 
230     //   (re)connect and (re)start the server.
231     // Note:  the old implementation wrapped the thread-safety stuff in a macro.
232     //   Here it is expanded and thus duplicated for each RPC call.  The new code has
233     //   a very limited number of RPC calls, unlike the older code.
234     WaitForSingleObject( hCCAPIv2Mutex, INFINITE );
235     SecureClient*   s = 0;
236     SecureClient::Start(s);
237     CcAutoLock*     a = 0;
238     CcAutoLock::Start(a, Client::sLock);
239
240     // New code using new RPC procedures for sending the data and receiving a reply:
241     if (!err) {
242         RpcTryExcept {
243             if (!GetTspData(GetTlsIndex(), &ptspdata)) {return ccErrBadParam;}
244             uuid    = tspdata_getUUID(ptspdata);
245             lenUUID = 1 + strlen(uuid);     /* 1+ includes terminating \0. */
246 #if 0
247             cci_debug_printf("%s calling remote ccs_rpc_request tsp*:0x%X", __FUNCTION__, ptspdata);
248             cci_debug_printf("  rpcmsg:%d; UUID[%d]:<%s> SST:%ld", in_msg, lenUUID, uuid, sst);
249 #endif
250             /* copy ptr into handle; ptr may be 4 or 8 bytes, depending on platform; handle is always 8 */
251             memcpy(tspdata_handle, &ptspdata, sizeof(ptspdata));
252             ccs_rpc_request(                    /* make call with user message: */
253                 in_msg,                         /* Message type */
254                 tspdata_handle,                 /* Our tspdata* will be sent back to the reply proc. */
255                 (unsigned char*)uuid,
256                 krb5int_ipc_stream_size(in_request_stream),
257                 (unsigned char*)krb5int_ipc_stream_data(in_request_stream), /* Data buffer */
258                 sst,                            /* session start time */
259                 (long*)(&err) );                /* Return code */
260             }
261         RpcExcept(1) {
262             err = handle_exception(RpcExceptionCode(), ptspdata);
263             }
264         RpcEndExcept;
265         }
266
267     cci_check_error(err);
268     CcAutoLock::Stop(a);
269     SecureClient::Stop(s);
270     ReleaseMutex(hCCAPIv2Mutex);       
271     //-- Use the old CCAPI implementation to try to talk to the server.
272
273     // Wait for reply handler to set event:
274     if (!err) {
275         err = cci_check_error(WaitForSingleObject(replyEvent, INFINITE));//(SECONDS_TO_WAIT)*1000));
276         }
277
278     if (!err) {
279         err = cci_check_error(RpcMgmtIsServerListening(CLIENT_REQUEST_RPC_HANDLE));
280         }
281
282     if (!err && server_died) {
283         err = cci_check_error (ccErrServerUnavailable);
284         }
285 #if 0    
286     if (err == BOOTSTRAP_UNKNOWN_SERVICE && !in_launch_server) {
287         err = ccNoError;  /* If the server is not running just return an empty stream. */
288         }
289 #endif
290
291     if (!err) {
292         *out_reply_stream = tspdata_getStream(ptspdata);
293         }
294
295     return cci_check_error (err);    
296     }
297
298
299
300 static DWORD handle_exception(DWORD code, struct tspdata* ptspdata) {
301     cci_debug_printf("%s code %u; ccs_request_IfHandle:0x%X", __FUNCTION__, code, ccs_request_IfHandle);
302     if ( (code == RPC_S_SERVER_UNAVAILABLE) || (code == RPC_S_INVALID_BINDING) ) {
303         Client::Cleanup();
304         tspdata_setConnected(ptspdata, FALSE);
305         }
306     return code;
307     }
308
309
310 /* Establish a CCAPI connection with the server.
311  * The connect logic here is identical to the logic in the send request code.
312  * TODO:  merge this connect code with that request code.
313  */
314 cc_int32 ccapi_connect(const struct tspdata* tsp) {
315     BOOL                    bListen     = TRUE;
316     HANDLE                  replyEvent  = 0;
317     RPC_STATUS              status      = FALSE;
318     char*                   uuid        = NULL;
319     unsigned char           tspdata_handle[8] = {0};
320
321     /* Start listening to our uuid before establishing the connection,
322      *  so that when the server tries to call ccapi_listen, we will be ready.
323      */
324
325     /* Build complete RPC uuid using previous CCAPI implementation: */
326     replyEvent      = tspdata_getReplyEvent(tsp);
327     uuid            = tspdata_getUUID(tsp);
328
329     cci_debug_printf("%s is listening ...", __FUNCTION__);
330
331     // Clear replyEvent so we can detect when a reply to our connect request has been received:
332     ResetEvent(replyEvent);
333
334     // We use the old CCAPI implementation to try to talk to the server.  
335     // It has all the code to make the uuid, (re)connect and (re)start the server.
336     WaitForSingleObject( hCCAPIv2Mutex, INFINITE );
337     SecureClient*   s = 0;
338     SecureClient::Start(s);
339     CcAutoLock*     a = 0;
340     CcAutoLock::Start(a, Client::sLock);
341
342     // Initialize old CCAPI if necessary:
343     if (!status) if (!Init::  Initialized()) status = Init::  Initialize( );
344     if (!status) if (!Client::Initialized()) status = Client::Initialize(0);
345
346     // New code using new RPC procedures for sending the data and receiving a reply:
347     if (!status) {
348         memcpy(tspdata_handle, &tsp, sizeof(tsp));
349         RpcTryExcept {
350             ccs_rpc_connect(                /* make call with user message: */
351                 CCMSG_CONNECT,              /* Message type */
352                 tspdata_handle,             /* Our tspdata* will be sent back to the reply proc. */
353                 (unsigned char*)uuid,
354                 (long*)(&status) );         /* Return code */
355             }
356         RpcExcept(1) {
357             cci_check_error(RpcExceptionCode());
358             status  = ccErrBadInternalMessage;
359             }
360         RpcEndExcept;
361         }
362
363     CcAutoLock::Stop(a);
364     SecureClient::Stop(s);
365     ReleaseMutex(hCCAPIv2Mutex);       
366
367     if (!status) {
368 #if 0
369         cci_debug_printf("%s Waiting for replyEvent.", __FUNCTION__);
370 #endif
371         status = WaitForSingleObject(replyEvent, INFINITE);//(SECONDS_TO_WAIT)*1000);
372         status = cci_check_error(RpcMgmtIsServerListening(CLIENT_REQUEST_RPC_HANDLE));
373         cci_debug_printf("  Server %sFOUND!", (status) ? "NOT " : "");
374         }
375     if (status) {
376         cci_debug_printf("  unexpected error while looking for server... (%u)", status);
377         } 
378     
379     return status;
380     }