summaryrefslogtreecommitdiffstats
path: root/src/lib/ccapi/windows/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/ccapi/windows/server.c')
-rw-r--r--src/lib/ccapi/windows/server.c638
1 files changed, 638 insertions, 0 deletions
diff --git a/src/lib/ccapi/windows/server.c b/src/lib/ccapi/windows/server.c
new file mode 100644
index 000000000..5ce8e6ebe
--- /dev/null
+++ b/src/lib/ccapi/windows/server.c
@@ -0,0 +1,638 @@
+
+
+#include <windows.h>
+#include "msg.h"
+#include "marshall.h"
+#include "serv_ops.h"
+#include "datastore.h"
+#include <stdio.h>
+#include <process.h>
+#include <tchar.h>
+#include <rpc.h>
+#include <rpcndr.h>
+#include "ntccrpc.h"
+#include <strsafe.h>
+
+#define SVCNAME "MIT_CCAPI_NT_Service"
+
+SERVICE_STATUS_HANDLE h_service_status = NULL;
+SERVICE_STATUS service_status;
+FILE * logfile = NULL;
+
+/* Log File */
+void begin_log(void) {
+ char temppath[512];
+
+ temppath[0] = L'\0';
+
+ GetTempPathA(sizeof(temppath), temppath);
+ StringCbCatA(temppath, sizeof(temppath), "mit_nt_ccapi.log");
+ logfile = fopen(temppath, "w");
+}
+
+void end_log(void) {
+ if (logfile) {
+ fclose(logfile);
+ logfile = NULL;
+ }
+}
+
+BOOL report_status(DWORD state,
+ DWORD exit_code,
+ DWORD wait_hint) {
+ static DWORD checkpoint = 1;
+ BOOL rv = TRUE;
+
+ if (state == SERVICE_START_PENDING)
+ service_status.dwControlsAccepted = 0;
+ else
+ service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+
+ service_status.dwCurrentState = state;
+ service_status.dwWin32ExitCode = exit_code;
+ service_status.dwWaitHint = wait_hint;
+
+ if (state == SERVICE_RUNNING ||
+ state == SERVICE_STOPPED)
+ service_status.dwCheckPoint = 0;
+ else
+ service_status.dwCheckPoint = checkpoint++;
+
+ rv = SetServiceStatus(h_service_status, &service_status);
+
+ return rv;
+}
+
+void service_start(DWORD argc, LPTSTR * argv) {
+ RPC_STATUS status;
+ RPC_BINDING_VECTOR * bv;
+
+ status = RpcServerUseProtseq("ncalrpc",
+ RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
+ NULL);
+
+ if (status != RPC_S_OK) {
+ return;
+ }
+
+ report_status(SERVICE_START_PENDING, NO_ERROR, 3000);
+
+ status = RpcServerRegisterIf(portable_ccapi_v1_0_s_ifspec,
+ 0, 0);
+
+ if (status != RPC_S_OK)
+ return;
+
+ report_status(SERVICE_START_PENDING, NO_ERROR, 3000);
+
+ status = RpcServerInqBindings(&bv);
+
+ if (status != RPC_S_OK)
+ return;
+
+ status = RpcEpRegister(portable_ccapi_v1_0_s_ifspec,
+ bv, 0, 0);
+
+ if (status != RPC_S_OK)
+ return;
+
+ report_status(SERVICE_START_PENDING, NO_ERROR, 3000);
+
+ status = RpcServerRegisterAuthInfo(NULL,
+ RPC_C_AUTHN_WINNT,
+ 0, 0);
+
+ if (status != RPC_S_OK)
+ return;
+
+ report_status(SERVICE_START_PENDING, NO_ERROR, 3000);
+
+ status = RpcServerListen(1,
+ RPC_C_LISTEN_MAX_CALLS_DEFAULT,
+ TRUE);
+
+ if (status != RPC_S_OK)
+ return;
+
+ report_status(SERVICE_RUNNING, NO_ERROR, 0);
+
+ begin_log();
+
+ status = RpcMgmtWaitServerListen();
+
+ end_log();
+
+ RpcEpUnregister(portable_ccapi_v1_0_s_ifspec, bv, 0);
+
+ RpcBindingVectorFree(&bv);
+}
+
+void service_stop(void) {
+ RpcMgmtStopServerListening(0);
+}
+
+void * __RPC_USER MIDL_user_allocate(size_t s) {
+ return malloc(s);
+}
+
+void __RPC_USER MIDL_user_free(void * p) {
+ free(p);
+}
+
+typedef struct tag_client_info {
+ char client_name[512];
+ LUID luid;
+} client_info_t;
+
+int obtain_auth_info(client_info_t * client_info, cc_auth_info_t ** pauth_info)
+{
+ *pauth_info = (cc_auth_info_t *)malloc(sizeof(cc_auth_info_t));
+ if ( !*pauth_info )
+ return ccErrNoMem;
+
+ (*pauth_info)->len = strlen(client_info->client_name) + 1;
+ (*pauth_info)->info = malloc((*pauth_info)->len);
+ if ( !(*pauth_info)->info ) {
+ free(*pauth_info);
+ return ccErrNoMem;
+ }
+
+ memcpy((*pauth_info)->info, client_info->client_name, (*pauth_info)->len);
+
+ return 0;
+}
+
+void destroy_auth_info(cc_auth_info_t *auth_info)
+{
+ free(auth_info->info);
+ free(auth_info);
+}
+
+int obtain_session_info(client_info_t * client_info, cc_session_info_t ** psession_info)
+{
+ *psession_info = (cc_session_info_t *)malloc(sizeof(cc_session_info_t));
+ if ( !*psession_info )
+ return ccErrNoMem;
+
+ (*psession_info)->len = sizeof(LUID);
+ (*psession_info)->info = malloc((*psession_info)->len);
+ if ( !(*psession_info)->info ) {
+ free(*psession_info);
+ return ccErrNoMem;
+ }
+
+ memcpy((*psession_info)->info, &client_info->luid, (*psession_info)->len);
+
+ return 0;
+}
+
+void destroy_session_info(cc_session_info_t *session_info)
+{
+ free(session_info->info);
+ free(session_info);
+}
+
+RPC_STATUS check_auth(handle_t h, client_info_t * client_info) {
+ RPC_BINDING_HANDLE bh = (RPC_BINDING_HANDLE) h;
+ RPC_STATUS status;
+ HANDLE htoken = NULL;
+ char name[256];
+ char domain[256];
+ DWORD name_len;
+ DWORD domain_len;
+ SID_NAME_USE snu = 0;
+
+ struct {
+ TOKEN_ORIGIN origin;
+ char pad[512];
+ } torigin;
+
+ struct {
+ TOKEN_OWNER owner;
+ char pad[4096];
+ } towner;
+
+ DWORD len;
+
+ status = RpcImpersonateClient(bh);
+
+ if (status != RPC_S_OK)
+ return status;
+
+ if (!OpenThreadToken(GetCurrentThread(),
+ TOKEN_READ | TOKEN_QUERY_SOURCE,
+ FALSE,
+ &htoken)) {
+ status = GetLastError();
+ goto _cleanup;
+ }
+
+ len = 0;
+
+ if (!GetTokenInformation(htoken,
+ TokenOrigin,
+ &torigin.origin,
+ sizeof(torigin),
+ &len)) {
+ status = GetLastError();
+ goto _cleanup;
+ }
+
+ if (!GetTokenInformation(htoken,
+ TokenOwner,
+ &towner.owner,
+ sizeof(towner),
+ &len)) {
+ status = GetLastError();
+ goto _cleanup;
+ }
+
+
+ name_len = sizeof(name)/sizeof(name[0]);
+ domain_len = sizeof(domain)/sizeof(domain[0]);
+
+ if (!LookupAccountSidA(NULL,
+ towner.owner.Owner,
+ name,
+ &name_len,
+ domain,
+ &domain_len,
+ &snu)) {
+ status = GetLastError();
+ goto _cleanup;
+ }
+
+ client_info->luid = torigin.origin.OriginatingLogonSession;
+ StringCbPrintfA(client_info->client_name,
+ sizeof(client_info->client_name),
+ "%s\\%s", domain, name);
+
+ status = 0;
+
+ _cleanup:
+
+ RpcRevertToSelf();
+
+ return status;
+}
+
+__int32 ccapi_Message(
+ /* [in] */ handle_t h,
+ /* [string][in] */ unsigned char *client_name,
+ /* [in] */ struct __LUID luid,
+ /* [size_is][length_is][in] */ unsigned char in_buf[],
+ /* [in] */ __int32 in_len,
+ /* [size_is][length_is][out] */ unsigned char out_buf[],
+ /* [out] */ __int32 *out_len)
+{
+ client_info_t client_info;
+ cc_msg_t * msg;
+ cc_msg_t * resp;
+ cc_auth_info_t * auth_info;
+ cc_session_info_t * session_info;
+ cc_int32 code;
+
+ if ( ccs_serv_initialize() != ccNoError ) {
+ code = ccErrServerUnavailable;
+ goto done;
+ }
+
+ code = check_auth(h, &client_info);
+ if (code == 0) {
+ if (!strcmp("SYSTEM",client_info.client_name) &&
+ client_info.luid.HighPart == 0 &&
+ client_info.luid.LowPart == 0 &&
+ client_name != NULL &&
+ client_name[0] != '\0') {
+ StringCbPrintfA(client_info.client_name,
+ sizeof(client_info.client_name),
+ "%s", client_name);
+ client_info.luid.HighPart = luid.HighPart;
+ client_info.luid.LowPart = luid.LowPart;
+ }
+ } else {
+ code = ccErrServerCantBecomeUID;
+ goto done;
+ }
+
+ /* allocate message */
+ msg = (cc_msg_t *)malloc(sizeof(cc_msg_t));
+ if (!msg) {
+ code = ccErrNoMem;
+ goto done;
+ }
+
+ /* unflatten message */
+ code = cci_msg_unflatten(in_buf, in_len, &msg);
+ if (code)
+ goto cleanup;
+
+ /* obtain auth info */
+ code = obtain_auth_info(&client_info, &auth_info);
+ if (code)
+ goto cleanup;
+
+ /* obtain session info */
+ code = obtain_session_info(&client_info, &session_info);
+ if (code)
+ goto cleanup;
+
+ /* process message */
+ code = ccs_serv_process_msg(msg, auth_info, session_info, &resp);
+ if (code)
+ goto cleanup;
+
+ /* flatten response */
+ code = cci_msg_flatten(resp, NULL);
+ if (code)
+ goto cleanup;
+
+ /* send response */
+ if (resp->flat_len > MAXMSGLEN) {
+ code = ccErrBadInternalMessage;
+ goto cleanup;
+ }
+ memcpy(out_buf, resp->flat, resp->flat_len);
+ *out_len = resp->flat_len;
+ code = ccNoError;
+
+ cleanup:
+ if (auth_info)
+ destroy_auth_info(auth_info);
+
+ if (session_info)
+ destroy_session_info(session_info);
+
+ /* free message */
+ if (msg)
+ cci_msg_destroy(msg);
+
+ /* free response */
+ if (resp)
+ cci_msg_destroy(resp);
+
+ done:
+ return code ? -1 : 0;
+}
+
+void WINAPI service_control(DWORD ctrl_code) {
+ switch(ctrl_code) {
+ case SERVICE_CONTROL_STOP:
+ report_status(SERVICE_STOP_PENDING, NO_ERROR, 0);
+ service_stop();
+ return;
+
+ /* everything else falls through */
+ }
+
+ report_status(service_status.dwCurrentState, NO_ERROR, 0);
+}
+
+void WINAPI service_main(DWORD argc, LPTSTR * argv) {
+
+ h_service_status = RegisterServiceCtrlHandler( _T(SVCNAME), service_control);
+
+ if (!h_service_status)
+ goto cleanup;
+
+ ZeroMemory(&service_status, sizeof(service_status));
+
+ service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ service_status.dwServiceSpecificExitCode = 0;
+
+ if (!report_status(SERVICE_START_PENDING,
+ NO_ERROR,
+ 3000))
+ goto cleanup;
+
+ service_start(argc, argv);
+
+ cleanup:
+
+ if (h_service_status) {
+ report_status(SERVICE_STOPPED, NO_ERROR, 0);
+ }
+}
+
+
+BOOL
+IsInstalled()
+{
+ BOOL bResult = FALSE;
+ SC_HANDLE hSCM;
+ SC_HANDLE hService;
+
+ // Open the Service Control Manager
+ hSCM = OpenSCManager( NULL, // local machine
+ NULL, // ServicesActive database
+ SC_MANAGER_ALL_ACCESS); // full access
+ if (hSCM) {
+
+ // Try to open the service
+ hService = OpenService( hSCM,
+ SVCNAME,
+ SERVICE_QUERY_CONFIG);
+ if (hService) {
+ bResult = TRUE;
+ CloseServiceHandle(hService);
+ }
+
+ CloseServiceHandle(hSCM);
+ }
+
+ return bResult;
+}
+
+BOOL
+Install()
+{
+ char szFilePath[_MAX_PATH];
+ SC_HANDLE hSCM;
+ SC_HANDLE hService;
+ TCHAR szKey[256];
+ HKEY hKey = NULL;
+ DWORD dwData;
+
+ // Open the Service Control Manager
+ hSCM = OpenSCManager( NULL, // local machine
+ NULL, // ServicesActive database
+ SC_MANAGER_ALL_ACCESS); // full access
+ if (!hSCM)
+ return FALSE;
+
+ // Get the executable file path
+ GetModuleFileName(NULL, szFilePath, sizeof(szFilePath));
+
+ // Create the service
+ hService = CreateService( hSCM,
+ SVCNAME,
+ SVCNAME,
+ SERVICE_ALL_ACCESS,
+ SERVICE_WIN32_OWN_PROCESS,
+ SERVICE_AUTO_START, // start condition
+ SERVICE_ERROR_NORMAL,
+ szFilePath,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (!hService) {
+ CloseServiceHandle(hSCM);
+ return FALSE;
+ }
+
+ // make registry entries to support logging messages
+ // Add the source name as a subkey under the Application
+ // key in the EventLog service portion of the registry.
+ StringCbCopyA(szKey, 256, "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\IKSD");
+ if (RegCreateKey(HKEY_LOCAL_MACHINE, szKey, &hKey) != ERROR_SUCCESS) {
+ CloseServiceHandle(hService);
+ CloseServiceHandle(hSCM);
+ return FALSE;
+ }
+
+ // Add the Event ID message-file name to the 'EventMessageFile' subkey.
+ RegSetValueEx( hKey,
+ "EventMessageFile",
+ 0,
+ REG_EXPAND_SZ,
+ (CONST BYTE*)szFilePath,
+ strlen(szFilePath) + 1);
+
+ // Set the supported types flags.
+ dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
+ RegSetValueEx( hKey,
+ "TypesSupported",
+ 0,
+ REG_DWORD,
+ (CONST BYTE*)&dwData,
+ sizeof(DWORD));
+ RegCloseKey(hKey);
+
+ // LogEvent(EVENTLOG_INFORMATION_TYPE, EVMSG_INSTALLED, SVCNAME);
+
+ // tidy up
+ CloseServiceHandle(hService);
+ CloseServiceHandle(hSCM);
+ return TRUE;
+}
+
+BOOL
+Uninstall()
+{
+ BOOL bResult = FALSE;
+ SC_HANDLE hService;
+ SC_HANDLE hSCM;
+
+ // Open the Service Control Manager
+ hSCM = OpenSCManager( NULL, // local machine
+ NULL, // ServicesActive database
+ SC_MANAGER_ALL_ACCESS); // full access
+ if (!hSCM)
+ return FALSE;
+
+ hService = OpenService( hSCM,
+ _T(SVCNAME),
+ DELETE);
+ if (hService) {
+ if (DeleteService(hService)) {
+ // LogEvent(EVENTLOG_INFORMATION_TYPE, EVMSG_REMOVED, SVCNAME);
+ bResult = TRUE;
+ } else {
+ // LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_NOTREMOVED, SVCNAME);
+ }
+ CloseServiceHandle(hService);
+ }
+
+ CloseServiceHandle(hSCM);
+ return bResult;
+}
+
+
+// Returns TRUE if it found an arg it recognised, FALSE if not
+// Note: processing some arguments causes output to stdout to be generated.
+BOOL
+ParseStandardArgs(int argc, char* argv[])
+{
+ char szFilePath[_MAX_PATH]="not a file name";
+
+ // See if we have any command line args we recognize
+ if (argc <= 1)
+ return FALSE;
+
+ if ( _stricmp(argv[1], "-h") == 0 ||
+ _stricmp(argv[1], "-?") == 0 ||
+ _stricmp(argv[1], "/h") == 0 ||
+ _stricmp(argv[1], "/?") == 0) {
+
+ //
+ GetModuleFileNameA(NULL, szFilePath, sizeof(szFilePath));
+ fprintf(stderr, "usage: %s [-v | -i | -u | -h]\r\n",szFilePath);
+ return TRUE;
+ } else if (_stricmp(argv[1], "-v") == 0 ||
+ _stricmp(argv[1], "/v") == 0 ) {
+
+ // Spit out version info
+ fprintf(stderr, "%s Version 0.1\n",_T(SVCNAME));
+ fprintf(stderr, "The service is %s installed\n",
+ IsInstalled() ? "currently" : "not");
+ return TRUE; // say we processed the argument
+
+ } else if (_stricmp(argv[1], "-i") == 0 ||
+ _stricmp(argv[1], "/i") == 0) {
+
+ // Request to install.
+ if (IsInstalled()) {
+ fprintf(stderr, "%s is already installed\n", _T(SVCNAME));
+ } else {
+ // Try and install the copy that's running
+ if (Install()) {
+ fprintf(stderr, "%s installed\n", _T(SVCNAME));
+ } else {
+ fprintf(stderr, "%s failed to install. Error %d\n", _T(SVCNAME), GetLastError());
+ }
+ }
+ return TRUE; // say we processed the argument
+
+ } else if (_stricmp(argv[1], "-u") == 0 ||
+ _stricmp(argv[1], "/u") == 0) {
+
+ // Request to uninstall.
+ if (!IsInstalled()) {
+ fprintf(stderr, "%s is not installed\n", _T(SVCNAME));
+ } else {
+ // Try and remove the copy that's installed
+ if (Uninstall()) {
+ // Get the executable file path
+ GetModuleFileNameA(NULL, szFilePath, sizeof(szFilePath));
+ fprintf(stderr, "%s removed. (You must delete the file (%s) yourself.)\n",
+ _T(SVCNAME), szFilePath);
+ } else {
+ fprintf(stderr, "Could not remove %s. Error %d\n", _T(SVCNAME), GetLastError());
+ }
+ }
+ return TRUE; // say we processed the argument
+
+ }
+
+ // Don't recognise the args
+ return FALSE;
+}
+
+int main(int argc, char ** argv) {
+
+ SERVICE_TABLE_ENTRY dispatch_table[] = {
+ { _T(SVCNAME), (LPSERVICE_MAIN_FUNCTION) service_main },
+ { NULL, NULL }
+ };
+
+ if ( ParseStandardArgs(argc, argv) )
+ return 0;
+
+ if (!StartServiceCtrlDispatcher(dispatch_table)) {
+ fprintf(stderr, "Can't start service control dispatcher\n");
+ }
+
+ return 0;
+}