Path: blob/main/crypto/krb5/src/ccapi/lib/win/OldCC/client.cxx
106841 views
/*
* $Header$
*
* Copyright 2008 Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. Furthermore if you modify this software you must label
* your software as modified software and not distribute it in such a
* fashion that it might be confused with the original M.I.T. software.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*/
#include "stdio.h" // KPKDBG
#include "ccs_request.h"
#include "ccapi.h"
#include "util.h"
extern "C" {
#include "cci_debugging.h"
#include "tls.h" // KPKDBG
}
#include "client.h"
#include "init.hxx"
#include "name.h"
#include "secure.hxx"
#define SECONDS_TO_WAIT 10
#define STARTUP "CLIENT STARTUP: "
#define DISCONNECT "CLIENT DISCONNECT: "
bool Client::s_init = false;
CcOsLock Client::sLock;
static DWORD bind_client(char* ep OPTIONAL, Init::InitInfo& info, LPSTR* endpoint) {
DWORD status = 0;
unsigned char * pszStringBinding = NULL;
if (!ep) {
status = alloc_name(endpoint, "ep", isNT());
}
else {
*endpoint = ep;
}
if (!status) {
/* Use a convenience function to concatenate the elements of */
/* the string binding into the proper sequence. */
status = RpcStringBindingCompose(0, // uuid
(unsigned char*)"ncalrpc", // protseq
0, // address
(unsigned char*)(*endpoint), // endpoint
0, // options
&pszStringBinding);
cci_check_error(status);
}
if (!status) {
/* Set the binding handle that will be used to bind to the server. */
status = RpcBindingFromStringBinding(pszStringBinding, &ccs_request_IfHandle);
cci_check_error(status);
}
if (!status) {
// Win9x might call RpcBindingSetAuthInfo (not Ex), but it does not
// quite work on Win9x...
if (isNT()) {
RPC_SECURITY_QOS qos;
qos.Version = RPC_C_SECURITY_QOS_VERSION;
qos.Capabilities = RPC_C_QOS_CAPABILITIES_DEFAULT;
qos.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC;
qos.ImpersonationType = RPC_C_IMP_LEVEL_IDENTIFY;
status = info.fRpcBindingSetAuthInfoEx(ccs_request_IfHandle,
0, // principal
RPC_C_AUTHN_LEVEL_CONNECT,
RPC_C_AUTHN_WINNT,
0, // current address space
RPC_C_AUTHZ_NAME,
&qos);
cci_check_error(status);
}
}
if (pszStringBinding) {
DWORD status = RpcStringFree(&pszStringBinding);
cci_check_error(status);
}
return cci_check_error(status);
}
DWORD find_server(Init::InitInfo& info, LPSTR endpoint) {
DWORD status = 0;
LPSTR event_name = 0;
HANDLE hEvent = 0;
SECURITY_ATTRIBUTES sa = { 0 };
PSECURITY_ATTRIBUTES psa = 0;
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
char* szExe = 0;
char* szDir = 0;
BOOL bRes = FALSE;
char* cmdline = NULL;
psa = isNT() ? &sa : 0;
cci_debug_printf("%s Looking for server; ccs_request_IfHandle:0x%X", __FUNCTION__, ccs_request_IfHandle);
status = cci_check_error(RpcMgmtIsServerListening(ccs_request_IfHandle));
if (status == RPC_S_NOT_LISTENING) {
cci_debug_printf(" Server *NOT* found!");
si.cb = sizeof(si);
status = alloc_module_dir_name(CCAPI_DLL, &szDir);
if (!status) {
status = alloc_module_dir_name_with_file(CCAPI_DLL, CCAPI_EXE, &szExe);
}
if (!status) {
status = alloc_name(&event_name, "startup", isNT());
cci_check_error(status);
}
if (!status) {
if (isNT()) {
sa.nLength = sizeof(sa);
status = alloc_own_security_descriptor_NT(&sa.lpSecurityDescriptor);
cci_check_error(status);
}
}
if (!status) {
hEvent = CreateEvent(psa, FALSE, FALSE, event_name);
cci_debug_printf(" CreateEvent(... %s) returned hEvent 0x%X", event_name, hEvent);
if (!hEvent) status = GetLastError();
}
if (!status) {
alloc_cmdline_2_args(szExe, endpoint, "-D", &cmdline);
bRes = CreateProcess( szExe, // app name
NULL, //cmdline, // cmd line is <server endpoint -[DC]>
psa, // SA
psa, // SA
FALSE,
CREATE_NEW_PROCESS_GROUP |
NORMAL_PRIORITY_CLASS |
#ifdef CCAPI_LAUNCH_SERVER_WITH_CONSOLE
CREATE_NEW_CONSOLE |
#else
DETACHED_PROCESS |
#endif
0,
NULL, // environment
szDir, // current dir
&si,
&pi);
if (!bRes) {
status = GetLastError();
cci_debug_printf(" CreateProcess returned %d; LastError: %d", bRes, status);
}
cci_debug_printf(" Waiting...");
}
cci_check_error(status);
if (!status) {
status = WaitForSingleObject(hEvent, (SECONDS_TO_WAIT)*1000);
status = RpcMgmtIsServerListening(ccs_request_IfHandle);
}
}
else if (status) {
cci_debug_printf(" unexpected error while looking for server: 0D%d / 0U%u / 0X%X", status, status, status);
}
if (szDir) free_alloc_p(&szDir);
if (szExe) free_alloc_p(&szExe);
if (hEvent) CloseHandle(hEvent);
if (pi.hThread) CloseHandle(pi.hThread);
if (pi.hProcess) CloseHandle(pi.hProcess);
if (sa.lpSecurityDescriptor) free_alloc_p(&sa.lpSecurityDescriptor);
return cci_check_error(status);
}
static
DWORD
make_random_challenge(DWORD *challenge_out) {
HCRYPTPROV provider;
DWORD status = 0;
*challenge_out = 0;
if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)) {
status = GetLastError();
cci_check_error(status);
return status;
}
if (!CryptGenRandom(provider, sizeof(*challenge_out),
(BYTE *)challenge_out)) {
status = GetLastError();
cci_check_error(status);
return status;
}
if (!CryptReleaseContext(provider, 0)) {
/*
* Note: even though CryptReleaseContext() failed, we don't really
* care since a) we've already successfully obtained our challenge
* anyway and b) at least one of the potential errors, "ERROR_BUSY"
* does not really seem to be an error at all. So GetLastError() is
* logged for informational purposes only and should not be returned.
*/
cci_check_error(GetLastError());
}
return status;
}
static
DWORD
authenticate_server(Init::InitInfo& info) {
DWORD challenge, desired_response;
HANDLE hMap = 0;
LPSTR mem_name = 0;
PDWORD pvalue = 0;
CC_UINT32 response = 0;
SECURITY_ATTRIBUTES sa = { 0 };
DWORD status = 0;
cci_debug_printf("%s entry", __FUNCTION__);
status = alloc_name(&mem_name, "auth", isNT());
cci_check_error(status);
if (!status) {
status = make_random_challenge(&challenge);
desired_response = challenge + 1;
cci_check_error(status);
}
if (!status) {
if (isNT()) {
sa.nLength = sizeof(sa);
status = alloc_own_security_descriptor_NT(&sa.lpSecurityDescriptor);
}
}
cci_check_error(status);
if (!status) {
hMap = CreateFileMapping(INVALID_HANDLE_VALUE, isNT() ? &sa : 0,
PAGE_READWRITE, 0, sizeof(DWORD), mem_name);
if (!hMap)
status = GetLastError();
}
cci_check_error(status);
if (!status) {
pvalue = (PDWORD)MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (!pvalue) status = GetLastError();
}
cci_check_error(status);
if (!status) {
*pvalue = challenge;
RpcTryExcept {
response = ccs_authenticate( (CC_CHAR*)mem_name );
}
RpcExcept(1) {
status = RpcExceptionCode();
cci_check_error(status);
}
RpcEndExcept;
}
cci_check_error(status);
if (!status) {
// Check response
if ((response != desired_response) && (*pvalue != desired_response)) {
cci_debug_printf(" Could not authenticate server.");
status = ERROR_ACCESS_DENIED; // XXX - CO_E_NOMATCHINGSIDFOUND?
}
else {
cci_debug_printf(" Server authenticated!");
}
cci_check_error(status);
}
free_alloc_p(&mem_name);
free_alloc_p(&sa.lpSecurityDescriptor);
if (pvalue) {
BOOL ok = UnmapViewOfFile(pvalue);
// DEBUG_ASSERT(ok);
}
if (hMap) CloseHandle(hMap);
return status;
}
DWORD
Client::Disconnect() {
DWORD status = 0;
if (ccs_request_IfHandle) {
/* The calls to the remote procedures are complete. */
/* Free the binding handle */
status = RpcBindingFree(&ccs_request_IfHandle);
}
s_init = false;
return status;
}
DWORD
Client::Connect(char* ep OPTIONAL) {
LPSTR endpoint = 0;
DWORD status = 0;
if (!ccs_request_IfHandle) {
Init::InitInfo info;
status = Init::Info(info);
cci_check_error(status);
if (!status) {
status = bind_client(ep, info, &endpoint);
cci_check_error(status);
}
if (!status) {
status = find_server(info, endpoint);
cci_check_error(status);
}
if (!status) {
status = authenticate_server(info);
cci_check_error(status);
}
}
if (endpoint && (endpoint != ep)) free_alloc_p(&endpoint);
if (status) Client::Disconnect();
return status;
}
DWORD Client::Initialize(char* ep OPTIONAL) {
CcAutoTryLock AL(Client::sLock);
if (!AL.IsLocked() || s_init)
return 0;
SecureClient s;
ccs_request_IfHandle = NULL;
DWORD status = Client::Connect(ep);
if (!status) s_init = true;
return status;
}
DWORD Client::Cleanup() {
CcAutoLock AL(Client::sLock);
SecureClient s;
return Client::Disconnect();
}
DWORD Client::Reconnect(char* ep OPTIONAL) {
CcAutoLock AL(Client::sLock);
SecureClient s;
DWORD status = 0;
if (Initialized()) {
DWORD status = Client::Cleanup();
}
if ( (!status) ) {
status = Client::Initialize(ep);
}
return status;
}