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
cci_os_ipc_process_init(void)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
cci_os_ipc_thread_init(void)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
cci_os_ipc(cc_int32 in_launch_server,k5_ipc_stream in_request_stream,k5_ipc_stream * out_reply_stream)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
cci_os_ipc_msg(cc_int32 in_launch_server,k5_ipc_stream in_request_stream,cc_int32 in_msg,k5_ipc_stream * out_reply_stream)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
handle_exception(DWORD code,struct tspdata * ptspdata)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 */
ccapi_connect(const struct tspdata * tsp)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