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 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 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 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 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 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 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 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 369 DWORD Client::Cleanup() { 370 CcAutoLock AL(Client::sLock); 371 SecureClient s; 372 return Client::Disconnect(); 373 } 374 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