#include "process.h"
#include "windows.h"
extern "C" {
#include "ccs_common.h"
#include "ccs_os_server.h"
#include <syslog.h>
#include "ccs_reply.h"
#include "ccs_request.h"
#include "win-utils.h"
#include "ccutils.h"
#include "cci_stream.h"
}
#include "WorkQueue.h"
#include "util.h"
#include "opts.hxx"
#include "init.hxx"
#pragma warning (disable : 4996)
BOOL bListen = TRUE;
const char* sessID = NULL;
time_t _sst = 0;
unsigned char* pszNetworkAddress = NULL;
unsigned char* pszStringBinding = NULL;
BOOL bRpcHandleInited = FALSE;
_RPC_ASYNC_STATE* rpcState = NULL;
struct RpcRcvArgs {
char* networkAddress;
unsigned char* protocolSequence;
unsigned char* sessID;
unsigned char* uuid;
ParseOpts::Opts* opts;
RPC_STATUS status;
} rpcargs = { NULL,
(unsigned char*)"ncalrpc",
NULL,
NULL,
NULL };
#define N_FIXED_ARGS 3
#define SERVER_REPLY_RPC_HANDLE ccs_reply_IfHandle
void receiveLoop(void* rpcargs);
void connectionListener(void* rpcargs);
void Usage(const char* argv0);
void printError(TCHAR* msg);
void setMySST() {_sst = time(&_sst);}
time_t getMySST() {return _sst;}
RPC_STATUS send_connection_reply(ccs_pipe_t in_pipe);
void RPC_ENTRY clientListener( _RPC_ASYNC_STATE*,
void* Context,
RPC_ASYNC_EVENT Event);
RPC_STATUS RPC_ENTRY sec_callback( IN RPC_IF_ID *Interface,
IN void *Context);
RPC_STATUS send_init(char* clientUUID);
cc_int32 ccs_os_server_initialize (int argc, const char *argv[]) {
cc_int32 err = 0;
ParseOpts::Opts opts = { 0 };
ParseOpts PO;
BOOL bAdjustedShutdown = FALSE;
HMODULE hKernel32 = GetModuleHandle("kernel32");
if (!err) {
sessID = argv[1];
setMySST();
opts.cMinCalls = 1;
opts.cMaxCalls = 20;
opts.fDontWait = TRUE;
#ifdef CCAPI_TEST_OPTIONS
PO.SetValidOpts("kemnfubc");
#else
PO.SetValidOpts("kc");
#endif
PO.Parse(opts, argc, (char**)argv);
if (hKernel32) {
typedef BOOL (WINAPI *FP_SetProcessShutdownParameters)(DWORD, DWORD);
FP_SetProcessShutdownParameters pSetProcessShutdownParameters =
(FP_SetProcessShutdownParameters)
GetProcAddress(hKernel32, "SetProcessShutdownParameters");
if (pSetProcessShutdownParameters) {
bAdjustedShutdown = pSetProcessShutdownParameters(100, 0);
}
}
cci_debug_printf("%s Shutdown Parameters",
bAdjustedShutdown ? "Adjusted" : "Did not adjust");
err = Init::Initialize();
}
if (err) {
Init::Cleanup();
fprintf( stderr, "An error occured while %s the server (%u)\n",
opts.bShutdown ? "shutting down" : "starting/running",
err);
exit(cci_check_error (err));
}
return cci_check_error (err);
}
cc_int32 ccs_os_server_cleanup (int argc, const char *argv[]) {
cc_int32 err = 0;
cci_debug_printf("%s for user <%s> shutting down.", argv[0], argv[1]);
return cci_check_error (err);
}
cc_int32 ccs_os_server_listen_loop (int argc, const char *argv[]) {
cc_int32 err = 0;
uintptr_t threadStatus;
unsigned int loopCounter = 0;
ParseOpts::Opts opts = { 0 };
ParseOpts PO;
opts.cMinCalls = 1;
opts.cMaxCalls = 20;
opts.fDontWait = TRUE;
#ifdef CCAPI_TEST_OPTIONS
PO.SetValidOpts("kemnfubc");
#else
PO.SetValidOpts("kc");
#endif
PO.Parse(opts, argc, (char**)argv);
#define INFO_BUFFER_SIZE 32767
TCHAR infoBuf[INFO_BUFFER_SIZE];
DWORD bufCharCount = INFO_BUFFER_SIZE;
bufCharCount = INFO_BUFFER_SIZE;
if( !GetUserName( infoBuf, &bufCharCount ) ) printError( TEXT("GetUserName") );
rpcargs.sessID = (unsigned char*)sessID;
rpcargs.opts = &opts;
threadStatus = _beginthread(receiveLoop, 0, (void*)&rpcargs);
while (TRUE) {
loopCounter++;
if (worklist_isEmpty() & 1) {
SleepEx(1000, TRUE);
}
else if (TRUE) { k5_ipc_stream buf = NULL;
long rpcmsg = CCMSG_INVALID;
time_t serverStartTime = 0xDEADDEAD;
RPC_STATUS status = 0;
char* uuid = NULL;
k5_ipc_stream stream = NULL;
ccs_pipe_t pipe = NULL;
ccs_pipe_t pipe2 = NULL;
if (worklist_remove(&rpcmsg, &pipe, &buf, &serverStartTime)) {
uuid = ccs_win_pipe_getUuid(pipe);
#if 0
cci_debug_printf("%s: processing WorkItem msg:%ld pipeUUID:<%s> pipeHandle:0x%X SST:%ld",
__FUNCTION__, rpcmsg, uuid, ccs_win_pipe_getHandle(pipe), serverStartTime);
#endif
if (serverStartTime <= getMySST()) {
switch (rpcmsg) {
case CCMSG_CONNECT: {
cci_debug_printf(" Processing CONNECT");
rpcargs.uuid = (unsigned char*)uuid;
connectionListener((void*)&rpcargs);
status = rpcargs.status;
if (!status) {
status = ccs_server_add_client(pipe);
}
if (!status) {status = send_connection_reply(pipe);}
break;
}
case CCMSG_DISCONNECT: {
cci_debug_printf(" Processing DISCONNECT");
if (!status) {
status = ccs_server_remove_client(pipe);
}
break;
}
case CCMSG_REQUEST:
cci_debug_printf(" Processing REQUEST");
ccs_pipe_copy(&pipe2, pipe);
err = ccs_server_handle_request (pipe, pipe2, buf);
break;
case CCMSG_PING:
cci_debug_printf(" Processing PING");
err = k5_ipc_stream_new (&stream);
err = k5_ipc_stream_write(stream, "This is a test of the emergency broadcasting system", 52);
err = ccs_os_server_send_reply(pipe, stream);
break;
default:
cci_debug_printf("Huh? Received invalid message type %ld from UUID:<%s>",
rpcmsg, uuid);
break;
}
if (buf) k5_ipc_stream_release(buf);
}
else {
cci_debug_printf("Whoops! Server has rebooted since client established connection.");
}
}
else {cci_debug_printf("Huh? Queue not empty but no item to remove.");}
}
}
return cci_check_error (err);
}
cc_int32 ccs_os_server_send_reply (ccs_pipe_t in_pipe,
k5_ipc_stream in_reply_stream) {
cc_int32 err = 0;
char* uuid = ccs_win_pipe_getUuid(in_pipe);
HANDLE h = ccs_win_pipe_getHandle(in_pipe);
if (!err) {
err = send_init(uuid); }
if (!err) {
RpcTryExcept {
long status;
ccs_rpc_request_reply( CCMSG_REQUEST_REPLY,
(unsigned char*)&h,
(unsigned char*)uuid,
getMySST(),
k5_ipc_stream_size(in_reply_stream),
(const unsigned char*)k5_ipc_stream_data(in_reply_stream),
&status );
}
RpcExcept(1) {
cci_check_error(RpcExceptionCode());
}
RpcEndExcept
}
err = RpcBindingFree(&SERVER_REPLY_RPC_HANDLE);
return cci_check_error (err);
}
void Usage(const char* argv0) {
printf("Usage:\n");
printf("%s [m maxcalls] [n mincalls] [f dontwait] [h|?]]\n", argv0);
printf(" CCAPI server process.\n");
printf(" h|? whow usage message. <\n");
}
void receiveLoop(void* rpcargs) {
struct RpcRcvArgs* rcvargs = (struct RpcRcvArgs*)rpcargs;
RPC_STATUS status = FALSE;
unsigned char* pszSecurity = NULL;
LPSTR endpoint = NULL;
LPSTR event_name = NULL;
PSECURITY_DESCRIPTOR psd = NULL;
HANDLE hEvent = 0;
Init::InitInfo info;
cci_debug_printf("THREAD BEGIN: %s", __FUNCTION__);
status = Init::Info(info);
if (!status) {
if (!rcvargs->opts->pszEndpoint) {
if (!status) {
status = alloc_name(&endpoint, "ep", isNT());
}
if (!status) {
status = alloc_name(&event_name, "startup", isNT());
}
if (!status) {
hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, event_name);
}
}
else {
endpoint = rcvargs->opts->pszEndpoint;
}
}
cci_debug_printf("%s Registering endpoint %s", __FUNCTION__, endpoint);
if (!status && isNT()) {
status = alloc_own_security_descriptor_NT(&psd);
}
if (!status) {
status = RpcServerUseProtseqEp(rcvargs->protocolSequence,
rcvargs->opts->cMaxCalls,
(RPC_CSTR)endpoint,
rcvargs->opts->bDontProtect ? 0 : psd); }
if (!status) {
status = RpcServerRegisterAuthInfo(0, RPC_C_AUTHN_WINNT,
0,
0);
}
while (bListen && !status) {
cci_debug_printf("%s is listening ...", __FUNCTION__);
if (!info.isNT) {
status = RpcServerRegisterIf(ccs_request_ServerIfHandle, NULL, NULL); }
else {
status = info.fRpcServerRegisterIfEx(ccs_request_ServerIfHandle, NULL, NULL, RPC_IF_ALLOW_SECURE_ONLY,
rcvargs->opts->cMaxCalls,
rcvargs->opts->bSecCallback ?
(RPC_IF_CALLBACK_FN*)sec_callback : 0 );
}
if (!status) {
status = RpcServerListen(rcvargs->opts->cMinCalls,
rcvargs->opts->cMaxCalls,
rcvargs->opts->fDontWait);
}
if (!status) {
if (rcvargs->opts->fDontWait) {
if (hEvent) SetEvent(hEvent); status = RpcMgmtWaitServerListen();
}
}
}
if (status) { if (hEvent) CloseHandle(hEvent);
free_alloc_p(&event_name);
free_alloc_p(&psd);
if (endpoint && (endpoint != rcvargs->opts->pszEndpoint))
free_alloc_p(&endpoint);
}
_endthread();
}
#if 0
return status;
}
#endif
void connectionListener(void* rpcargs) {
struct RpcRcvArgs* rcvargs = (struct RpcRcvArgs*)rpcargs;
RPC_STATUS status = FALSE;
char* endpoint;
unsigned char* pszOptions = NULL;
unsigned char * pszUuid = NULL;
endpoint = clientEndpoint((char*)rcvargs->uuid);
rpcState = (RPC_ASYNC_STATE*)malloc(sizeof(RPC_ASYNC_STATE));
status = RpcAsyncInitializeHandle(rpcState, sizeof(RPC_ASYNC_STATE));
cci_debug_printf("");
cci_debug_printf("%s About to LISTEN to <%s>", __FUNCTION__, endpoint);
rpcState->UserInfo = rcvargs->uuid;
rpcState->NotificationType = RpcNotificationTypeApc;
rpcState->u.APC.NotificationRoutine = clientListener;
rpcState->u.APC.hThread = 0;
if (bRpcHandleInited) {
RpcStringFree(&pszStringBinding);
RpcBindingFree(&SERVER_REPLY_RPC_HANDLE);
bRpcHandleInited = FALSE;
}
if (!status) {
status = RpcStringBindingCompose(
pszUuid,
pszProtocolSequence,
pszNetworkAddress,
(RPC_CSTR)endpoint,
pszOptions,
&pszStringBinding);
}
if (!status) {
status = RpcBindingFromStringBinding(pszStringBinding, &SERVER_REPLY_RPC_HANDLE);
}
if (!status) {bRpcHandleInited = TRUE;}
RpcTryExcept {
cci_debug_printf(" Calling remote procedure ccapi_listen");
ccapi_listen(rpcState, SERVER_REPLY_RPC_HANDLE, CCMSG_LISTEN, &status);
}
RpcExcept(1) {
status = cci_check_error(RpcExceptionCode());
}
RpcEndExcept
rcvargs->status = status;
}
void RPC_ENTRY clientListener(
_RPC_ASYNC_STATE* pAsync,
void* Context,
RPC_ASYNC_EVENT Event
) {
ccs_pipe_t pipe = ccs_win_pipe_new((char*)pAsync->UserInfo, NULL);
cci_debug_printf("%s(0x%X, ...) async routine for <0x%X:%s>!",
__FUNCTION__, pAsync, pAsync->UserInfo, pAsync->UserInfo);
worklist_add( CCMSG_DISCONNECT,
pipe,
NULL,
(const time_t)0 );
}
void printError( TCHAR* msg ) {
DWORD eNum;
TCHAR sysMsg[256];
TCHAR* p;
eNum = GetLastError( );
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, eNum,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
sysMsg, 256, NULL );
p = sysMsg;
while( ( *p > 31 ) || ( *p == 9 ) )
++p;
do { *p-- = 0; } while( ( p >= sysMsg ) &&
( ( *p == '.' ) || ( *p < 33 ) ) );
cci_debug_printf("%s failed with error %d (%s)", msg, eNum, sysMsg);
}
RPC_STATUS send_init(char* clientUUID) {
RPC_STATUS status;
unsigned char * pszUuid = NULL;
unsigned char * pszOptions = NULL;
status = RpcStringBindingCompose(pszUuid,
pszProtocolSequence,
pszNetworkAddress,
(unsigned char*)clientEndpoint(clientUUID),
pszOptions,
&pszStringBinding);
if (status) {return (status);}
status = RpcBindingFromStringBinding(pszStringBinding, &SERVER_REPLY_RPC_HANDLE);
return (status);
}
RPC_STATUS send_finish() {
RPC_STATUS status;
status = RpcStringFree(&pszStringBinding); if (status) {return (status);}
status = RpcBindingFree(&SERVER_REPLY_RPC_HANDLE);
return (status);
}
RPC_STATUS send_connection_reply(ccs_pipe_t in_pipe) {
char* uuid = ccs_win_pipe_getUuid (in_pipe);
HANDLE h = ccs_win_pipe_getHandle(in_pipe);
RPC_STATUS status = send_init(uuid);
RpcTryExcept {
ccs_rpc_connect_reply( CCMSG_CONNECT_REPLY,
(unsigned char*)&h,
(unsigned char*)uuid,
getMySST(),
&status );
}
RpcExcept(1) {
cci_check_error(RpcExceptionCode());
}
RpcEndExcept
status = send_finish();
return (status);
}
#if 0
DWORD alloc_name(LPSTR* pname, LPSTR postfix) {
DWORD len = strlen(sessID) + 1 + strlen(postfix) + 1;
*pname = (LPSTR)malloc(len);
if (!*pname) return GetLastError();
_snprintf(*pname, len, "%s.%s", sessID, postfix);
return 0;
}
#endif
RPC_STATUS GetPeerName( RPC_BINDING_HANDLE hClient,
LPTSTR pszClientName,
int iMaxLen) {
RPC_STATUS Status = RPC_S_OK;
RPC_BINDING_HANDLE hServer = NULL;
PTBYTE pszStringBinding = NULL;
PTBYTE pszClientNetAddr = NULL;
PTBYTE pszProtSequence = NULL;
memset(pszClientName, 0, iMaxLen * sizeof(TCHAR));
__try {
Status = RpcBindingServerFromClient (hClient, &hServer);
if (Status != RPC_S_OK) __leave;
Status = RpcBindingToStringBinding (hServer,
&pszStringBinding);
if (Status != RPC_S_OK) __leave;
Status = RpcStringBindingParse (pszStringBinding, NULL,
&pszProtSequence, &pszClientNetAddr,
NULL, NULL);
if (Status != RPC_S_OK)
__leave;
int iLen = lstrlen(pszClientName) + 1;
if (iMaxLen < iLen)
Status = RPC_S_BUFFER_TOO_SMALL;
lstrcpyn(pszClientName, (LPCTSTR)pszClientNetAddr, iMaxLen);
}
__finally {
if (pszProtSequence)
RpcStringFree (&pszProtSequence);
if (pszClientNetAddr)
RpcStringFree (&pszClientNetAddr);
if (pszStringBinding)
RpcStringFree (&pszStringBinding);
if (hServer)
RpcBindingFree (&hServer);
}
return Status;
}
struct client_auth_info {
RPC_AUTHZ_HANDLE authz_handle;
unsigned char* server_principal; ULONG authn_level;
ULONG authn_svc;
ULONG authz_svc;
};
RPC_STATUS
GetClientId(
RPC_BINDING_HANDLE hClient,
char* client_id,
int max_len,
client_auth_info* info
)
{
RPC_AUTHZ_HANDLE authz_handle = 0;
unsigned char* server_principal = 0;
ULONG authn_level = 0;
ULONG authn_svc = 0;
ULONG authz_svc = 0;
RPC_STATUS status = 0;
memset(client_id, 0, max_len);
if (info) {
memset(info, 0, sizeof(client_auth_info));
}
status = RpcBindingInqAuthClient(hClient, &authz_handle,
info ? &server_principal : 0,
&authn_level, &authn_svc, &authz_svc);
if (status == RPC_S_OK)
{
if (info) {
info->server_principal = server_principal;
info->authz_handle = authz_handle;
info->authn_level = authn_level;
info->authn_svc = authn_svc;
info->authz_svc = authz_svc;
}
if (authn_svc == RPC_C_AUTHN_WINNT) {
WCHAR* username = (WCHAR*)authz_handle;
int len = lstrlenW(username) + 1;
if (max_len < len)
status = RPC_S_BUFFER_TOO_SMALL;
_snprintf(client_id, max_len, "%S", username);
} else {
status = RPC_S_UNKNOWN_AUTHN_SERVICE;
}
}
return status;
}
char*
rpc_error_to_string(
RPC_STATUS status
)
{
switch(status) {
case RPC_S_OK:
return "OK";
case RPC_S_INVALID_BINDING:
return "Invalid binding";
case RPC_S_WRONG_KIND_OF_BINDING:
return "Wrong binding";
case RPC_S_BINDING_HAS_NO_AUTH:
RpcRaiseException(RPC_S_BINDING_HAS_NO_AUTH);
return "Binding has no auth";
default:
return "BUG: I am confused";
}
}
void
print_client_info(
RPC_STATUS peer_status,
const char* peer_name,
RPC_STATUS client_status,
const char* client_id,
client_auth_info* info
)
{
if (peer_status == RPC_S_OK || peer_status == RPC_S_BUFFER_TOO_SMALL) {
cci_debug_printf("%s Peer Name is \"%s\"", __FUNCTION__, peer_name);
} else {
cci_debug_printf("%s Error %u getting Peer Name (%s)",
__FUNCTION__, peer_status, rpc_error_to_string(peer_status));
}
if (client_status == RPC_S_OK || client_status == RPC_S_BUFFER_TOO_SMALL) {
if (info) {
cci_debug_printf("%s Client Auth Info"
"\tServer Principal: %s\n"
"\tAuthentication Level: %d\n"
"\tAuthentication Service: %d\n"
"\tAuthorization Service: %d\n",
__FUNCTION__,
info->server_principal,
info->authn_level,
info->authn_svc,
info->authz_svc);
}
cci_debug_printf("%s Client ID is \"%s\"", __FUNCTION__, client_id);
} else {
cci_debug_printf("%s Error getting Client Info (%u = %s)",
__FUNCTION__, client_status, rpc_error_to_string(client_status));
}
}
DWORD sid_check() {
DWORD status = 0;
HANDLE hToken_c = 0;
HANDLE hToken_s = 0;
PTOKEN_USER ptu_c = 0;
PTOKEN_USER ptu_s = 0;
DWORD len = 0;
BOOL bImpersonate = FALSE;
status = RpcImpersonateClient(0);
if (!status) {
bImpersonate = TRUE;
if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken_c))
status = GetLastError();
}
if (!status) {
status = RpcRevertToSelf();
}
if (!status) {
bImpersonate = FALSE;
len = 0;
GetTokenInformation(hToken_c, TokenUser, ptu_c, 0, &len);
if (len == 0) status = 1;
}
if (!status) {
if (!(ptu_c = (PTOKEN_USER)LocalAlloc(0, len)))
status = GetLastError();
}
if (!status) {
if (!GetTokenInformation(hToken_c, TokenUser, ptu_c, len, &len))
status = GetLastError();
}
if (!status) {
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken_s))
status = GetLastError();
}
if (!status) {
len = 0;
GetTokenInformation(hToken_s, TokenUser, ptu_s, 0, &len);
if (len == 0) status = GetLastError();
}
if (!status) {
if (!(ptu_s = (PTOKEN_USER)LocalAlloc(0, len)))
status = GetLastError();
}
if (!status) {
if (!GetTokenInformation(hToken_s, TokenUser, ptu_s, len, &len))
status = GetLastError();
}
if (!EqualSid(ptu_s->User.Sid, ptu_c->User.Sid))
status = RPC_S_ACCESS_DENIED;
if (!hToken_c && !bImpersonate)
cci_debug_printf("%s Cannot impersonate (%u)", __FUNCTION__, status);
else if (!hToken_c)
cci_debug_printf("%s Failed to open client token (%u)", __FUNCTION__, status);
else if (bImpersonate)
cci_debug_printf("%s Failed to revert (%u)", __FUNCTION__, status);
else if (!ptu_c)
cci_debug_printf("%s Failed to get client token user info (%u)",
__FUNCTION__, status);
else if (!hToken_s)
cci_debug_printf("%s Failed to open server token (%u)", __FUNCTION__, status);
else if (!ptu_s)
cci_debug_printf("%s Failed to get server token user info (%u)",
__FUNCTION__, status);
else if (status == RPC_S_ACCESS_DENIED)
cci_debug_printf("%s SID **does not** match!", __FUNCTION__);
else if (status == RPC_S_OK)
cci_debug_printf("%s SID matches!", __FUNCTION__);
else
if (status) {
cci_debug_printf("%s unrecognized error %u", __FUNCTION__, status);
abort();
}
if (bImpersonate) RpcRevertToSelf();
if (hToken_c && hToken_c != INVALID_HANDLE_VALUE)
CloseHandle(hToken_c);
if (ptu_c) LocalFree(ptu_c);
if (hToken_s && hToken_s != INVALID_HANDLE_VALUE)
CloseHandle(hToken_s);
if (ptu_s) LocalFree(ptu_s);
if (status) cci_debug_printf("%s returning %u", __FUNCTION__, status);
return status;
}
RPC_STATUS RPC_ENTRY sec_callback( IN RPC_IF_ID *Interface,
IN void *Context) {
char peer_name[1024];
char client_name[1024];
RPC_STATUS peer_status;
RPC_STATUS client_status;
cci_debug_printf("%s", __FUNCTION__);
peer_status = GetPeerName(Context, peer_name, sizeof(peer_name));
client_status = GetClientId(Context, client_name, sizeof(client_name), 0);
print_client_info(peer_status, peer_name, client_status, client_name, 0);
DWORD sid_status = sid_check();
cci_debug_printf("%s returning (%u)", __FUNCTION__, sid_status);
return sid_status;
}
extern "C" void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len) {
return(malloc(len));
}
extern "C" void __RPC_USER midl_user_free(void __RPC_FAR * ptr) {
free(ptr);
}