4 * Copyright 2008 Massachusetts Institute of Technology.
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.
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.
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"
36 #include "win-utils.h"
39 #include "autolock.hxx"
40 #include "CredentialsCache.h"
45 extern "C" DWORD GetTlsIndex();
47 #define SECONDS_TO_WAIT 10
48 #define CLIENT_REQUEST_RPC_HANDLE ccs_request_IfHandle
50 extern HANDLE hCCAPIv2Mutex;
51 ParseOpts::Opts opts = { 0 };
52 PSECURITY_ATTRIBUTES psa = 0;
53 SECURITY_ATTRIBUTES sa = { 0 };
55 /* The layout of the rest of this module:
57 The entrypoints defined in ccs_os_ipc.h:
58 cci_os_ipc_thread_init
61 Other routines needed by those four.
66 cc_int32 ccapi_connect(const struct tspdata* tsp);
67 static DWORD handle_exception(DWORD code, struct tspdata* ptspdata);
70 cc_int32 cci_os_ipc_msg( cc_int32 in_launch_server,
71 k5_ipc_stream in_request_stream,
73 k5_ipc_stream* out_reply_stream);
76 /* ------------------------------------------------------------------------ */
78 extern "C" cc_int32 cci_os_ipc_process_init (void) {
82 status = RpcServerRegisterIf(ccs_reply_ServerIfHandle, // interface
84 NULL); // MgrEpv; null means use default
87 status = RpcServerRegisterIfEx(ccs_reply_ServerIfHandle, // interface
89 NULL, // MgrEpv; 0 means default
90 RPC_IF_ALLOW_SECURE_ONLY,
91 RPC_C_LISTEN_MAX_CALLS_DEFAULT,
92 NULL); // No security callback.
94 cci_check_error(status);
97 status = RpcServerRegisterAuthInfo(0, // server principal
101 cci_check_error(status);
104 return status; // ugh. needs translation
107 /* ------------------------------------------------------------------------ */
109 extern "C" cc_int32 cci_os_ipc_thread_init (void) {
110 cc_int32 err = ccNoError;
111 struct tspdata* ptspdata;
112 HANDLE replyEvent = NULL;
114 RPC_CSTR __RPC_FAR uuidString = NULL;
115 char* endpoint = NULL;
117 if (!GetTspData(GetTlsIndex(), &ptspdata)) return ccErrNoMem;
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);
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,
130 sa.lpSecurityDescriptor); // SD
132 cci_check_error(err);
136 cci_debug_printf("%s UUID:<%s>", __FUNCTION__, tspdata_getUUID(ptspdata));
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);
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);
150 static bool bListening = false;
152 err = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
153 cci_check_error(err);
155 bListening = err == 0;
158 if (replyEvent) tspdata_setReplyEvent(ptspdata, replyEvent);
159 else err = cci_check_error(GetLastError());
161 if (uuidString) RpcStringFree(&uuidString);
163 return cci_check_error(err);
167 /* ------------------------------------------------------------------------ */
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,
178 extern "C" cc_int32 cci_os_ipc_msg( cc_int32 in_launch_server,
179 k5_ipc_stream in_request_stream,
181 k5_ipc_stream* out_reply_stream) {
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;
191 unsigned int trycount = 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 };
200 if (!in_request_stream) { err = cci_check_error (ccErrBadParam); }
201 if (!out_reply_stream ) { err = cci_check_error (ccErrBadParam); }
203 if (!GetTspData(GetTlsIndex(), &ptspdata)) {return ccErrBadParam;}
204 bListening = tspdata_getListening(ptspdata);
206 err = cci_check_error(cci_os_ipc_thread_init());
208 tspdata_setListening(ptspdata, bListening);
211 bCCAPI_Connected = tspdata_getConnected (ptspdata);
212 replyEvent = tspdata_getReplyEvent (ptspdata);
213 sst = tspdata_getSST (ptspdata);
214 uuid = tspdata_getUUID(ptspdata);
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);
225 // Clear replyEvent so we can detect when a reply to our request has been received:
226 ResetEvent(replyEvent);
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 );
236 SecureClient::Start(s);
238 CcAutoLock::Start(a, Client::sLock);
240 // New code using new RPC procedures for sending the data and receiving a reply:
243 if (!GetTspData(GetTlsIndex(), &ptspdata)) {return ccErrBadParam;}
244 uuid = tspdata_getUUID(ptspdata);
245 lenUUID = 1 + strlen(uuid); /* 1+ includes terminating \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);
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 */
262 err = handle_exception(RpcExceptionCode(), ptspdata);
267 cci_check_error(err);
269 SecureClient::Stop(s);
270 ReleaseMutex(hCCAPIv2Mutex);
271 //-- Use the old CCAPI implementation to try to talk to the server.
273 // Wait for reply handler to set event:
275 err = cci_check_error(WaitForSingleObject(replyEvent, INFINITE));//(SECONDS_TO_WAIT)*1000));
279 err = cci_check_error(RpcMgmtIsServerListening(CLIENT_REQUEST_RPC_HANDLE));
282 if (!err && server_died) {
283 err = cci_check_error (ccErrServerUnavailable);
286 if (err == BOOTSTRAP_UNKNOWN_SERVICE && !in_launch_server) {
287 err = ccNoError; /* If the server is not running just return an empty stream. */
292 *out_reply_stream = tspdata_getStream(ptspdata);
295 return cci_check_error (err);
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) ) {
304 tspdata_setConnected(ptspdata, FALSE);
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.
314 cc_int32 ccapi_connect(const struct tspdata* tsp) {
316 HANDLE replyEvent = 0;
317 RPC_STATUS status = FALSE;
319 unsigned char tspdata_handle[8] = {0};
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.
325 /* Build complete RPC uuid using previous CCAPI implementation: */
326 replyEvent = tspdata_getReplyEvent(tsp);
327 uuid = tspdata_getUUID(tsp);
329 cci_debug_printf("%s is listening ...", __FUNCTION__);
331 // Clear replyEvent so we can detect when a reply to our connect request has been received:
332 ResetEvent(replyEvent);
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 );
338 SecureClient::Start(s);
340 CcAutoLock::Start(a, Client::sLock);
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);
346 // New code using new RPC procedures for sending the data and receiving a reply:
348 memcpy(tspdata_handle, &tsp, sizeof(tsp));
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 */
357 cci_check_error(RpcExceptionCode());
358 status = ccErrBadInternalMessage;
364 SecureClient::Stop(s);
365 ReleaseMutex(hCCAPIv2Mutex);
369 cci_debug_printf("%s Waiting for replyEvent.", __FUNCTION__);
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 " : "");
376 cci_debug_printf(" unexpected error while looking for server... (%u)", status);