xref: /freebsd/crypto/krb5/src/ccapi/lib/win/ccapi_os_ipc.cxx (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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