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 #include "stdio.h" // KPKDBG
28
29 #include "ccs_request.h"
30
31 #include "ccapi.h"
32 #include "util.h"
33
34 extern "C" {
35 #include "cci_debugging.h"
36 #include "tls.h" // KPKDBG
37 }
38
39 #include "client.h"
40 #include "init.hxx"
41 #include "name.h"
42 #include "secure.hxx"
43
44 #define SECONDS_TO_WAIT 10
45
46 #define STARTUP "CLIENT STARTUP: "
47 #define DISCONNECT "CLIENT DISCONNECT: "
48
49 bool Client::s_init = false;
50 CcOsLock Client::sLock;
51
bind_client(char * ep OPTIONAL,Init::InitInfo & info,LPSTR * endpoint)52 static DWORD bind_client(char* ep OPTIONAL, Init::InitInfo& info, LPSTR* endpoint) {
53 DWORD status = 0;
54 unsigned char * pszStringBinding = NULL;
55
56 if (!ep) {
57 status = alloc_name(endpoint, "ep", isNT());
58 }
59 else {
60 *endpoint = ep;
61 }
62
63 if (!status) {
64 /* Use a convenience function to concatenate the elements of */
65 /* the string binding into the proper sequence. */
66 status = RpcStringBindingCompose(0, // uuid
67 (unsigned char*)"ncalrpc", // protseq
68 0, // address
69 (unsigned char*)(*endpoint), // endpoint
70 0, // options
71 &pszStringBinding);
72 cci_check_error(status);
73 }
74
75 if (!status) {
76 /* Set the binding handle that will be used to bind to the server. */
77 status = RpcBindingFromStringBinding(pszStringBinding, &ccs_request_IfHandle);
78 cci_check_error(status);
79 }
80
81 if (!status) {
82 // Win9x might call RpcBindingSetAuthInfo (not Ex), but it does not
83 // quite work on Win9x...
84 if (isNT()) {
85 RPC_SECURITY_QOS qos;
86 qos.Version = RPC_C_SECURITY_QOS_VERSION;
87 qos.Capabilities = RPC_C_QOS_CAPABILITIES_DEFAULT;
88 qos.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC;
89 qos.ImpersonationType = RPC_C_IMP_LEVEL_IDENTIFY;
90
91 status = info.fRpcBindingSetAuthInfoEx(ccs_request_IfHandle,
92 0, // principal
93 RPC_C_AUTHN_LEVEL_CONNECT,
94 RPC_C_AUTHN_WINNT,
95 0, // current address space
96 RPC_C_AUTHZ_NAME,
97 &qos);
98 cci_check_error(status);
99 }
100 }
101
102 if (pszStringBinding) {
103 DWORD status = RpcStringFree(&pszStringBinding);
104 cci_check_error(status);
105 }
106 return cci_check_error(status);
107 }
108
find_server(Init::InitInfo & info,LPSTR endpoint)109 DWORD find_server(Init::InitInfo& info, LPSTR endpoint) {
110 DWORD status = 0;
111 LPSTR event_name = 0;
112 HANDLE hEvent = 0;
113 SECURITY_ATTRIBUTES sa = { 0 };
114 PSECURITY_ATTRIBUTES psa = 0;
115 STARTUPINFO si = { 0 };
116 PROCESS_INFORMATION pi = { 0 };
117 char* szExe = 0;
118 char* szDir = 0;
119 BOOL bRes = FALSE;
120 char* cmdline = NULL;
121
122 psa = isNT() ? &sa : 0;
123
124 cci_debug_printf("%s Looking for server; ccs_request_IfHandle:0x%X", __FUNCTION__, ccs_request_IfHandle);
125 status = cci_check_error(RpcMgmtIsServerListening(ccs_request_IfHandle));
126 if (status == RPC_S_NOT_LISTENING) {
127 cci_debug_printf(" Server *NOT* found!");
128 si.cb = sizeof(si);
129
130 status = alloc_module_dir_name(CCAPI_DLL, &szDir);
131
132 if (!status) {
133 status = alloc_module_dir_name_with_file(CCAPI_DLL, CCAPI_EXE, &szExe);
134 }
135
136 if (!status) {
137 status = alloc_name(&event_name, "startup", isNT());
138 cci_check_error(status);
139 }
140
141 if (!status) {
142 if (isNT()) {
143 sa.nLength = sizeof(sa);
144 status = alloc_own_security_descriptor_NT(&sa.lpSecurityDescriptor);
145 cci_check_error(status);
146 }
147 }
148
149 if (!status) {
150 hEvent = CreateEvent(psa, FALSE, FALSE, event_name);
151 cci_debug_printf(" CreateEvent(... %s) returned hEvent 0x%X", event_name, hEvent);
152 if (!hEvent) status = GetLastError();
153 }
154
155 if (!status) {
156 alloc_cmdline_2_args(szExe, endpoint, "-D", &cmdline);
157 bRes = CreateProcess( szExe, // app name
158 NULL, //cmdline, // cmd line is <server endpoint -[DC]>
159 psa, // SA
160 psa, // SA
161 FALSE,
162 CREATE_NEW_PROCESS_GROUP |
163 NORMAL_PRIORITY_CLASS |
164 #ifdef CCAPI_LAUNCH_SERVER_WITH_CONSOLE
165 CREATE_NEW_CONSOLE |
166 #else
167 DETACHED_PROCESS |
168 #endif
169 0,
170 NULL, // environment
171 szDir, // current dir
172 &si,
173 &pi);
174 if (!bRes) {
175 status = GetLastError();
176 cci_debug_printf(" CreateProcess returned %d; LastError: %d", bRes, status);
177 }
178 cci_debug_printf(" Waiting...");
179 }
180 cci_check_error(status);
181
182 if (!status) {
183 status = WaitForSingleObject(hEvent, (SECONDS_TO_WAIT)*1000);
184 status = RpcMgmtIsServerListening(ccs_request_IfHandle);
185 }
186 }
187 else if (status) {
188 cci_debug_printf(" unexpected error while looking for server: 0D%d / 0U%u / 0X%X", status, status, status);
189 }
190
191 if (szDir) free_alloc_p(&szDir);
192 if (szExe) free_alloc_p(&szExe);
193 if (hEvent) CloseHandle(hEvent);
194 if (pi.hThread) CloseHandle(pi.hThread);
195 if (pi.hProcess) CloseHandle(pi.hProcess);
196 if (sa.lpSecurityDescriptor) free_alloc_p(&sa.lpSecurityDescriptor);
197 return cci_check_error(status);
198
199 }
200
201 static
202 DWORD
make_random_challenge(DWORD * challenge_out)203 make_random_challenge(DWORD *challenge_out) {
204 HCRYPTPROV provider;
205 DWORD status = 0;
206 *challenge_out = 0;
207 if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL,
208 CRYPT_VERIFYCONTEXT)) {
209 status = GetLastError();
210 cci_check_error(status);
211 return status;
212 }
213 if (!CryptGenRandom(provider, sizeof(*challenge_out),
214 (BYTE *)challenge_out)) {
215 status = GetLastError();
216 cci_check_error(status);
217 return status;
218 }
219 if (!CryptReleaseContext(provider, 0)) {
220 /*
221 * Note: even though CryptReleaseContext() failed, we don't really
222 * care since a) we've already successfully obtained our challenge
223 * anyway and b) at least one of the potential errors, "ERROR_BUSY"
224 * does not really seem to be an error at all. So GetLastError() is
225 * logged for informational purposes only and should not be returned.
226 */
227 cci_check_error(GetLastError());
228 }
229 return status;
230 }
231
232 static
233 DWORD
authenticate_server(Init::InitInfo & info)234 authenticate_server(Init::InitInfo& info) {
235 DWORD challenge, desired_response;
236 HANDLE hMap = 0;
237 LPSTR mem_name = 0;
238 PDWORD pvalue = 0;
239 CC_UINT32 response = 0;
240 SECURITY_ATTRIBUTES sa = { 0 };
241 DWORD status = 0;
242
243 cci_debug_printf("%s entry", __FUNCTION__);
244
245 status = alloc_name(&mem_name, "auth", isNT());
246 cci_check_error(status);
247
248 if (!status) {
249 status = make_random_challenge(&challenge);
250 desired_response = challenge + 1;
251 cci_check_error(status);
252 }
253
254 if (!status) {
255 if (isNT()) {
256 sa.nLength = sizeof(sa);
257 status = alloc_own_security_descriptor_NT(&sa.lpSecurityDescriptor);
258 }
259 }
260 cci_check_error(status);
261
262 if (!status) {
263 hMap = CreateFileMapping(INVALID_HANDLE_VALUE, isNT() ? &sa : 0,
264 PAGE_READWRITE, 0, sizeof(DWORD), mem_name);
265 if (!hMap)
266 status = GetLastError();
267 }
268 cci_check_error(status);
269
270 if (!status) {
271 pvalue = (PDWORD)MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
272 if (!pvalue) status = GetLastError();
273 }
274 cci_check_error(status);
275
276 if (!status) {
277 *pvalue = challenge;
278
279 RpcTryExcept {
280 response = ccs_authenticate( (CC_CHAR*)mem_name );
281 }
282 RpcExcept(1) {
283 status = RpcExceptionCode();
284 cci_check_error(status);
285 }
286 RpcEndExcept;
287 }
288 cci_check_error(status);
289
290 if (!status) {
291 // Check response
292 if ((response != desired_response) && (*pvalue != desired_response)) {
293 cci_debug_printf(" Could not authenticate server.");
294 status = ERROR_ACCESS_DENIED; // XXX - CO_E_NOMATCHINGSIDFOUND?
295 }
296 else {
297 cci_debug_printf(" Server authenticated!");
298 }
299 cci_check_error(status);
300 }
301
302 free_alloc_p(&mem_name);
303 free_alloc_p(&sa.lpSecurityDescriptor);
304 if (pvalue) {
305 BOOL ok = UnmapViewOfFile(pvalue);
306 // DEBUG_ASSERT(ok);
307 }
308 if (hMap) CloseHandle(hMap);
309 return status;
310 }
311
312 DWORD
Disconnect()313 Client::Disconnect() {
314 DWORD status = 0;
315 if (ccs_request_IfHandle) {
316 /* The calls to the remote procedures are complete. */
317 /* Free the binding handle */
318 status = RpcBindingFree(&ccs_request_IfHandle);
319 }
320 s_init = false;
321 return status;
322 }
323
324 DWORD
Connect(char * ep OPTIONAL)325 Client::Connect(char* ep OPTIONAL) {
326 LPSTR endpoint = 0;
327 DWORD status = 0;
328
329 if (!ccs_request_IfHandle) {
330 Init::InitInfo info;
331
332 status = Init::Info(info);
333 cci_check_error(status);
334
335 if (!status) {
336 status = bind_client(ep, info, &endpoint);
337 cci_check_error(status);
338 }
339
340 if (!status) {
341 status = find_server(info, endpoint);
342 cci_check_error(status);
343 }
344
345 if (!status) {
346 status = authenticate_server(info);
347 cci_check_error(status);
348 }
349 }
350
351
352 if (endpoint && (endpoint != ep)) free_alloc_p(&endpoint);
353
354 if (status) Client::Disconnect();
355 return status;
356 }
357
Initialize(char * ep OPTIONAL)358 DWORD Client::Initialize(char* ep OPTIONAL) {
359 CcAutoTryLock AL(Client::sLock);
360 if (!AL.IsLocked() || s_init)
361 return 0;
362 SecureClient s;
363 ccs_request_IfHandle = NULL;
364 DWORD status = Client::Connect(ep);
365 if (!status) s_init = true;
366 return status;
367 }
368
Cleanup()369 DWORD Client::Cleanup() {
370 CcAutoLock AL(Client::sLock);
371 SecureClient s;
372 return Client::Disconnect();
373 }
374
Reconnect(char * ep OPTIONAL)375 DWORD Client::Reconnect(char* ep OPTIONAL) {
376 CcAutoLock AL(Client::sLock);
377 SecureClient s;
378 DWORD status = 0;
379
380 if (Initialized()) {
381 DWORD status = Client::Cleanup();
382 }
383 if ( (!status) ) {
384 status = Client::Initialize(ep);
385 }
386
387 return status;
388 }
389