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 // Initialize old CCAPI if necessary: 136 if (!err) if (!Init:: Initialized()) err = Init:: Initialize( ); 137 if (!err) if (!Client::Initialized()) err = Client::Initialize(0); 138 139 if (!err) { 140 /* Whenever a reply to an RPC request is received, the RPC caller needs to 141 know when the reply has been received. It does that by waiting for a 142 client-specific event to be set. Define the event name to be <UUID>_reply: */ 143 replyEvent = createThreadEvent((char*)uuidString, REPLY_SUFFIX); 144 } 145 146 if (!err) { 147 static bool bListening = false; 148 if (!bListening) { 149 err = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE); 150 cci_check_error(err); 151 } 152 bListening = err == 0; 153 } 154 155 if (replyEvent) tspdata_setReplyEvent(ptspdata, replyEvent); 156 else err = cci_check_error(GetLastError()); 157 158 if (uuidString) RpcStringFree(&uuidString); 159 160 return cci_check_error(err); 161 } 162 163 164 /* ------------------------------------------------------------------------ */ 165 166 cc_int32 cci_os_ipc (cc_int32 in_launch_server, 167 k5_ipc_stream in_request_stream, 168 k5_ipc_stream* out_reply_stream) { 169 return cci_os_ipc_msg( in_launch_server, 170 in_request_stream, 171 CCMSG_REQUEST, 172 out_reply_stream); 173 } 174 175 extern "C" cc_int32 cci_os_ipc_msg( cc_int32 in_launch_server, 176 k5_ipc_stream in_request_stream, 177 cc_int32 in_msg, 178 k5_ipc_stream* out_reply_stream) { 179 180 cc_int32 err = ccNoError; 181 cc_int32 done = FALSE; 182 cc_int32 try_count = 0; 183 cc_int32 server_died = FALSE; 184 TCHAR* pszStringBinding= NULL; 185 struct tspdata* ptspdata = NULL; 186 char* uuid = NULL; 187 int lenUUID = 0; 188 unsigned int trycount = 0; 189 time_t sst = 0; 190 STARTUPINFO si = { 0 }; 191 PROCESS_INFORMATION pi = { 0 }; 192 HANDLE replyEvent = 0; 193 BOOL bCCAPI_Connected= FALSE; 194 BOOL bListening = FALSE; 195 unsigned char tspdata_handle[8] = { 0 }; 196 197 if (!in_request_stream) { err = cci_check_error (ccErrBadParam); } 198 if (!out_reply_stream ) { err = cci_check_error (ccErrBadParam); } 199 200 if (!GetTspData(GetTlsIndex(), &ptspdata)) {return ccErrBadParam;} 201 bListening = tspdata_getListening(ptspdata); 202 if (!bListening) { 203 err = cci_check_error(cci_os_ipc_thread_init()); 204 bListening = !err; 205 tspdata_setListening(ptspdata, bListening); 206 } 207 208 bCCAPI_Connected = tspdata_getConnected (ptspdata); 209 replyEvent = tspdata_getReplyEvent (ptspdata); 210 sst = tspdata_getSST (ptspdata); 211 uuid = tspdata_getUUID(ptspdata); 212 213 // The lazy connection to the server has been put off as long as possible! 214 // ccapi_connect starts listening for replies as an RPC server and then 215 // calls ccs_rpc_connect. 216 if (!err && !bCCAPI_Connected) { 217 err = cci_check_error(ccapi_connect(ptspdata)); 218 bCCAPI_Connected = !err; 219 tspdata_setConnected(ptspdata, bCCAPI_Connected); 220 } 221 222 // Clear replyEvent so we can detect when a reply to our request has been received: 223 ResetEvent(replyEvent); 224 225 //++ Use the old CCAPI implementation to try to talk to the server: 226 // It has all the code to use the RPC in a thread-safe way, make the endpoint, 227 // (re)connect and (re)start the server. 228 // Note: the old implementation wrapped the thread-safety stuff in a macro. 229 // Here it is expanded and thus duplicated for each RPC call. The new code has 230 // a very limited number of RPC calls, unlike the older code. 231 WaitForSingleObject( hCCAPIv2Mutex, INFINITE ); 232 SecureClient* s = 0; 233 SecureClient::Start(s); 234 CcAutoLock* a = 0; 235 CcAutoLock::Start(a, Client::sLock); 236 237 // New code using new RPC procedures for sending the data and receiving a reply: 238 if (!err) { 239 RpcTryExcept { 240 if (!GetTspData(GetTlsIndex(), &ptspdata)) {return ccErrBadParam;} 241 uuid = tspdata_getUUID(ptspdata); 242 lenUUID = 1 + strlen(uuid); /* 1+ includes terminating \0. */ 243 /* copy ptr into handle; ptr may be 4 or 8 bytes, depending on platform; handle is always 8 */ 244 memcpy(tspdata_handle, &ptspdata, sizeof(ptspdata)); 245 ccs_rpc_request( /* make call with user message: */ 246 in_msg, /* Message type */ 247 tspdata_handle, /* Our tspdata* will be sent back to the reply proc. */ 248 (unsigned char*)uuid, 249 krb5int_ipc_stream_size(in_request_stream), 250 (unsigned char*)krb5int_ipc_stream_data(in_request_stream), /* Data buffer */ 251 sst, /* session start time */ 252 (long*)(&err) ); /* Return code */ 253 } 254 RpcExcept(1) { 255 err = handle_exception(RpcExceptionCode(), ptspdata); 256 } 257 RpcEndExcept; 258 } 259 260 cci_check_error(err); 261 CcAutoLock::Stop(a); 262 SecureClient::Stop(s); 263 ReleaseMutex(hCCAPIv2Mutex); 264 //-- Use the old CCAPI implementation to try to talk to the server. 265 266 // Wait for reply handler to set event: 267 if (!err) { 268 err = cci_check_error(WaitForSingleObject(replyEvent, INFINITE));//(SECONDS_TO_WAIT)*1000)); 269 } 270 271 if (!err) { 272 err = cci_check_error(RpcMgmtIsServerListening(CLIENT_REQUEST_RPC_HANDLE)); 273 } 274 275 if (!err && server_died) { 276 err = cci_check_error (ccErrServerUnavailable); 277 } 278 279 if (!err) { 280 *out_reply_stream = tspdata_getStream(ptspdata); 281 } 282 283 return cci_check_error (err); 284 } 285 286 287 288 static DWORD handle_exception(DWORD code, struct tspdata* ptspdata) { 289 cci_debug_printf("%s code %u; ccs_request_IfHandle:0x%X", __FUNCTION__, code, ccs_request_IfHandle); 290 if ( (code == RPC_S_SERVER_UNAVAILABLE) || (code == RPC_S_INVALID_BINDING) ) { 291 Client::Cleanup(); 292 tspdata_setConnected(ptspdata, FALSE); 293 } 294 return code; 295 } 296 297 298 /* Establish a CCAPI connection with the server. 299 * The connect logic here is identical to the logic in the send request code. 300 * TODO: merge this connect code with that request code. 301 */ 302 cc_int32 ccapi_connect(const struct tspdata* tsp) { 303 BOOL bListen = TRUE; 304 HANDLE replyEvent = 0; 305 RPC_STATUS status = FALSE; 306 char* uuid = NULL; 307 unsigned char tspdata_handle[8] = {0}; 308 309 /* Start listening to our uuid before establishing the connection, 310 * so that when the server tries to call ccapi_listen, we will be ready. 311 */ 312 313 /* Build complete RPC uuid using previous CCAPI implementation: */ 314 replyEvent = tspdata_getReplyEvent(tsp); 315 uuid = tspdata_getUUID(tsp); 316 317 cci_debug_printf("%s is listening ...", __FUNCTION__); 318 319 // Clear replyEvent so we can detect when a reply to our connect request has been received: 320 ResetEvent(replyEvent); 321 322 // We use the old CCAPI implementation to try to talk to the server. 323 // It has all the code to make the uuid, (re)connect and (re)start the server. 324 WaitForSingleObject( hCCAPIv2Mutex, INFINITE ); 325 SecureClient* s = 0; 326 SecureClient::Start(s); 327 CcAutoLock* a = 0; 328 CcAutoLock::Start(a, Client::sLock); 329 330 // Initialize old CCAPI if necessary: 331 if (!status) if (!Init:: Initialized()) status = Init:: Initialize( ); 332 if (!status) if (!Client::Initialized()) status = Client::Initialize(0); 333 334 // New code using new RPC procedures for sending the data and receiving a reply: 335 if (!status) { 336 memcpy(tspdata_handle, &tsp, sizeof(tsp)); 337 RpcTryExcept { 338 ccs_rpc_connect( /* make call with user message: */ 339 CCMSG_CONNECT, /* Message type */ 340 tspdata_handle, /* Our tspdata* will be sent back to the reply proc. */ 341 (unsigned char*)uuid, 342 (long*)(&status) ); /* Return code */ 343 } 344 RpcExcept(1) { 345 cci_check_error(RpcExceptionCode()); 346 status = ccErrBadInternalMessage; 347 } 348 RpcEndExcept; 349 } 350 351 CcAutoLock::Stop(a); 352 SecureClient::Stop(s); 353 ReleaseMutex(hCCAPIv2Mutex); 354 355 if (!status) { 356 status = WaitForSingleObject(replyEvent, INFINITE);//(SECONDS_TO_WAIT)*1000); 357 status = cci_check_error(RpcMgmtIsServerListening(CLIENT_REQUEST_RPC_HANDLE)); 358 cci_debug_printf(" Server %sFOUND!", (status) ? "NOT " : ""); 359 } 360 if (status) { 361 cci_debug_printf(" unexpected error while looking for server... (%u)", status); 362 } 363 364 return status; 365 } 366