--- /dev/null
+CD = cd\r
+\r
+all:\r
+ $(CD) common\r
+ $(MAKE) -f NTMakefile all\r
+ $(CD) ../client\r
+ $(MAKE) -f NTMakefile all\r
+ $(CD) ../server\r
+ $(MAKE) -f NTMakefile all\r
+ $(CD) ../windows\r
+ $(MAKE) -f NTMakefile all\r
+ $(CD) ..\r
+\r
+clean:\r
+ $(CD) common\r
+ $(MAKE) -f NTMakefile clean\r
+ $(CD) ../client\r
+ $(MAKE) -f NTMakefile clean\r
+ $(CD) ../server\r
+ $(MAKE) -f NTMakefile clean\r
+ $(CD) ../windows\r
+ $(MAKE) -f NTMakefile clean\r
+ $(CD) ..\r
+\r
+\r
!INCLUDE <WIN32.MAK>
-CFLAGS = -I../include $(cdebug) $(cflags) $(cvarsdll)
+CFLAGS = -I../include $(cdebug) $(cflags) $(cvarsmt)
CC_CLIENT_OBJS = cacheapi.obj context.obj ccache.obj credentials.obj ccache_iterator.obj \
credentials_iterator.obj ccstring.obj ccapiv2.obj
$(CC_CLIENT_LIB): $(CC_CLIENT_OBJS)
$(implib) /NOLOGO /OUT:$@ $**
-CCAPI_DLLFILE = krbcc32.dll
-
-WINLIBS = ws2_32.lib $(guilibsdll) msvcrt.lib
-
-$(CCAPI_DLLFILE): dllmain.obj $(CC_CLIENT_LIB) $(CC_COMMON_LIB)
- $(link) /OUT:$@ $(ldebug) $(dlllflags) /NODEFAULTLIB:libcmt.lib -def:cacheapi.def $** $(WINLIBS)
-
-all: $(CCAPI_DLLFILE)
+all: $(CC_CLIENT_LIB)
clean:
- del *.obj *.lib *.dll *.exp
+ del *.obj *.lib
!INCLUDE <WIN32.MAK>\r
\r
-CFLAGS = -I../include $(cdebug) $(cflags) $(cvarsdll)\r
+CFLAGS = -I../include $(cdebug) $(cflags) $(cvarsmt)\r
\r
CC_COMMON_OBJS = marshall.obj msg.obj generic_lists.obj\r
\r
--- /dev/null
+The following are notes describing the requirements of the Platform\r
+Specific code necessary for constructing a Portable CCAPI.\r
+\r
+Directory structure:\r
+\r
+ lib/ccapi/client - platform independent client library\r
+ lib/ccapi/common - platform independent common library\r
+ lib/ccapi/include - platform independent header files\r
+ lib/ccapi/mac - macosx specific headers, libraries, executables\r
+ lib/ccapi/server - platform independent server library\r
+ lib/ccapi/windows - windows specific headers, libraries, executables\r
+\r
+\r
+Platform Independent Design:\r
+\r
+The functionality of the Portable CCAPI is implemented in the platform\r
+independent libraries. The common library encapsulates the functions\r
+for managing generic lists, iterators, and messages as well as routines\r
+formarshalling and unmarshalling. The client library provides the\r
+client side routines for issuing requests to the ccapi server minus the\r
+platform dependent glue required for shared library initialization,\r
+cleanup, and interprocess communications. The server library provides\r
+server side functionality for managing credential cache collections,\r
+caches, credentials, iterators, and their handles minus the platform\r
+dependent glue for process initialization, interprocess communication,\r
+session security, and critical section enforcement.\r
+\r
+\r
+Platform Dependent Design Requirements:\r
+\r
+The platform dependent code is responsible for producing a shared\r
+client library:\r
+\r
+ + the shared library is built from cc_client.lib and cc_common.lib plus\r
+ platform dependent glue\r
+\r
+ - [windows] link cc_client.lib and cc_common.lib with platform\r
+ dependent routines and export list (.def) to produce\r
+ krbcc32.{lib,dll}\r
+\r
+ + initialization and cleanup\r
+\r
+ - [windows] provide DllMain entry point providing Process and Thread\r
+ attachment and detachment routines\r
+\r
+ + implement cci_perform_rpc() function used by cc_client.lib\r
+ cc_int32 cci_perform_rpc(cc_msg_t *request, cc_msg_t **response)\r
+\r
+ - cci_perform_rpc() takes an input request cc_msg_t object, flattens\r
+ it with cci_msg_flatten() and sends the contents of unsigned char\r
+ buffer request->flat of length request->flat_len to the server\r
+ utilizing a platform specific interprocess communication method.\r
+\r
+ - upon IPC success, cci_perform_rpc() unflattens the response buffer\r
+ with cci_msg_unflatten() and returns the new cc_msg_t response\r
+ object to the caller.\r
+\r
+ - cci_perform_rpc() is responsible for performing any necessary\r
+ session security management. For example, on Windows the Logon\r
+ Provider executes under the local machine's "SYSTEM" account within\r
+ session 0 and not under the account of the user that is logging in\r
+ nor within the session the user's desktop and applications will be\r
+ running within. It is the responsibility of cci_perform_rpc() and\r
+ the platform dependent IPC mechanism to communicate the user's\r
+ security identifiers to the server.\r
+\r
+ For Windows, this means that the platform specific IPC messaging\r
+ allows a username and session identifier to be sent separate from\r
+ the username and session identifier that will be obtained via the\r
+ use of Local RPC. If the Local RPC authenticates the user as\r
+ "SYSTEM" and session 0, then the communicated values (if provided)\r
+ will be used instead.\r
+\r
+ + implement client side of IPC routine.\r
+\r
+ - [windows] the client side IPC routine is produced by compiling a\r
+ IDL file. The IDL defines an interface with a single function:\r
+\r
+ __int32 ccapi_Message (\r
+ [in] handle_t h,\r
+ [in, string] unsigned char * client_name,\r
+ [in] struct _LUID luid,\r
+ [in] __int32 in_len,\r
+ [in, string, size_is(in_len)] unsigned char * in_buf,\r
+ [in] __int32 out_size,\r
+ [out] __int32 * out_len,\r
+ [out, string, size_is(out_size)] unsigned char\r
+ out_buf[*]);\r
+\r
+ The handle is a Local RPC specific handle used to identify the\r
+ request. The client_name and luid are the override values for the\r
+ username and session identifier for use during Windows login. The\r
+ rest of the parameters provide the input and output buffers as well\r
+ as allow communication of the actual length of the message data\r
+ that is required by cci_msg_unflatten().\r
+\r
+ + if the CCAPI server is per-session, the shared client library is\r
+ responsible for ensuring that an instance of the server is running in\r
+ the current session. If not, the library must initiate an instance\r
+ of the CCAPI server prior to performing any IPC requests.\r
+\r
+The platform dependent code is responsible for producing a server\r
+executable:\r
+\r
+ + The server executable is built from cc_server.lib and cc_common.lib\r
+ plus platform dependent glue.\r
+\r
+ - [windows] The Windows CCAPI Server is being built using the\r
+ per-system model. The platform specific code is responsible for\r
+ providing NT Service Management routines for installation and\r
+ removal as well as the NT Service Entry Points used when the\r
+ process is started as an NT Service.\r
+\r
+ link cc_server.lib and cc_common.lib with platform dependent\r
+ routines to produce krbcc32s.exe.\r
+\r
+ + Based upon the platform requirements, the server may be constructed\r
+ to be per-session or per-system. The selected IPC mechanism must\r
+ enforce the appropriate scoping.\r
+\r
+ + The platform dependent startup routines will perform platform\r
+ specific initialization including the IPC engine and call the\r
+ platform independent initialization routine ccs_serv_initialize()\r
+\r
+ + The platform dependent shutdown routines will perform platform\r
+ specific cleanup including the IPC engine and call the platform\r
+ independent function ccs_serv_cleanup() prior to process termination.\r
+\r
+ + For each inbound CCAPI request, the server will unmarshall the\r
+ request using cci_msg_unflatten() to produce a cc_msg_t object,\r
+ construct cc_auth_info_t and cc_session_info_t objects to represent\r
+ the platform dependent authentication and session data, call\r
+ ccs_serv_process_msg() to process the request, call cci_msg_flatten()\r
+ to marhall the response, transmit the response to the caller, and\r
+ then cleanup the request and response cc_msg_t objects with\r
+ cci_msg_destroy().\r
+\r
+ + The cc_auth_info_t and cc_session_info_t objects are structures\r
+ storing opaque binary (data, length) representations of the\r
+ authentication and session data. These are stored as part of ccache\r
+ collection and ccaches. ccs_serv_process_msg() will perform a binary\r
+ comparison of the stored data with the data provided in the current\r
+ request. If they do not match, the request will be denied. It is\r
+ necessary that the data generated data always be the same. If\r
+ username strings are not case-sensitive, they should be normalized\r
+ before being passed to ccs_serv_process_msg().\r
+\r
+ + The current cc_server.lib routines assume that one request at a time\r
+ is being processed. If the IPC engine allows for more than one\r
+ request to be simultaneously received in separate threads, then the\r
+ call to ccs_serv_process_msg() must be wrapped by a critical section.\r
+ Future enhancements to cc_server.lib will allow for per-object\r
+ mutexes. When available the platform specific glue must provide\r
+ functions to create, obtain, release, and destroy mutex objects.\r
+\r
+\r
#include <sys/types.h>
#endif
+#if defined(_WIN32)
+#include <winsock.h>
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
char* client;
char* server;
cc_data_compat keyblock;
- cc_time_t authtime;
- cc_time_t starttime;
- cc_time_t endtime;
- cc_time_t renew_till;
+ cc_time authtime;
+ cc_time starttime;
+ cc_time endtime;
+ cc_time renew_till;
cc_uint32 is_skey;
cc_uint32 ticket_flags;
cc_data_compat** addresses;
CC_CRED_MAX
};
+enum {
+ CC_LOCK_UNLOCK = 1,
+ CC_LOCK_READER = 2,
+ CC_LOCK_WRITER = 3,
+ CC_LOCK_NOBLOCK = 16
+};
+
CCACHE_API cc_int32 cc_shutdown (
apiCB** ioContext);
CCACHE_API cc_int32 cc_get_change_time (
apiCB* inContext,
- cc_time_t* outTime);
+ cc_time* outTime);
CCACHE_API cc_int32 cc_open (
apiCB* inContext,
apiCB* inContext,
infoNC*** ioInfo);
+CCACHE_API cc_int32 cc_lock_request(
+ apiCB* inContext,
+ const ccache_p* inCCache,
+ const cc_int32 lock_type);
+
#if TARGET_OS_MAC
#if defined(__MWERKS__)
#pragma enumsalwaysint reset
-# Makefile for the CCAPI Generic Server
+# Makefile for the CCAPI Server Library
!INCLUDE <WIN32.MAK>
-CFLAGS = -I../include
+CFLAGS = -I../include $(cdebug) $(cflags) $(cvarsmt)
-CCAPI_LIB = ../client/ccapi.lib
-WINLIBS = user32.lib advapi32.lib
-CCSOBJS = ccs_context.obj ccs_ccache.obj ccs_lists.obj rpc_auth.obj serv_ops.obj
+CC_SERVER_OBJS = ccs_context.obj ccs_ccache.obj ccs_lists.obj rpc_auth.obj serv_ops.obj
-all: ccapi_server.exe
+CC_SERVER_LIB = cc_server.lib
-ccapi_server.exe: main.obj $(CCSOBJS) $(CCAPI_LIB)
- link -out:$@ main.obj $(CCSOBJS) $(CCAPI_LIB) $(WINLIBS)
+CC_COMMON_LIB = ../common/cc_common.lib
+
+$(CC_SERVER_LIB): $(CC_SERVER_OBJS)
+ $(implib) /NOLOGO /OUT:$@ $**
+
+all: $(CC_SERVER_LIB)
clean:
- del *.obj *.exe
+ del *.obj *.lib
+
\ No newline at end of file
-# Makefile for the CCAPI Generic Server\r
+!include <win32.mak>\r
\r
-!INCLUDE <WIN32.MAK>\r
+CFLAGS = -I../include $(cdebug) $(cflags) $(cvarsmt)\r
\r
-CFLAGS = -I../include\r
+WINLIBS = ws2_32.lib rpcrt4.lib $(guilibsdll)\r
\r
-CCAPI_LIB = ../lib/ccapi.lib\r
-WINLIBS = user32.lib advapi32.lib\r
-CCSOBJS = context.obj ccache.obj lists.obj rpc_auth.obj serv_ops.obj\r
+T_CCACHE = t_ccache.exe\r
\r
-all: t_lists.exe t_msg.exe t_ccache.exe t_context.exe ccapi_server.exe\r
+T_CONTEXT = t_context.exe\r
\r
-t_lists.exe: t_lists.obj $(CCSOBJS) $(CCAPI_LIB)\r
- link -out:$@ t_lists.obj $(CCSOBJS) $(CCAPI_LIB) $(WINLIBS)\r
+T_LISTS = t_lists.exe\r
\r
-t_msg.exe: t_msg.obj $(CCSOBJS) $(CCAPI_LIB)\r
- link -out:$@ t_msg.obj $(CCSOBJS) $(CCAPI_LIB) $(WINLIBS)\r
+T_MSG = t_msg.exe\r
\r
-t_ccache.exe: t_ccache.obj $(CCSOBJS) $(CCAPI_LIB)\r
- link -out:$@ t_ccache.obj $(CCSOBJS) $(CCAPI_LIB) $(WINLIBS)\r
+T_SERVER = t_server.exe\r
\r
-t_context.exe: t_context.obj $(CCSOBJS) $(CCAPI_LIB)\r
- link -out:$@ t_context.obj $(CCSOBJS) $(CCAPI_LIB) $(WINLIBS)\r
+all: $(T_CCACHE) $(T_CONTEXT) $(T_LISTS) $(T_MSG) $(T_SERVER)\r
\r
-ccapi_server.exe: main.obj $(CCSOBJS) $(CCAPI_LIB)\r
- link -out:$@ main.obj $(CCSOBJS) $(CCAPI_LIB) $(WINLIBS)\r
+ntccrpc_c.c ntccrpc_s.c ntccrpc.h: ntccrpc.idl ntccrpc.acf\r
+ midl ntccrpc.idl /acf ntccrpc.acf\r
+\r
+CC_CLIENT_LIB = ..\client\cc_client.lib\r
+\r
+CC_COMMON_LIB = ..\common\cc_common.lib\r
+\r
+CC_SERVER_LIB = ..\server\cc_server.lib\r
+\r
+CC_API_LIB = ..\windows\krbcc32.lib\r
+\r
+$(T_CCACHE): t_ccache.obj $(CC_SERVER_LIB) $(CC_COMMON_LIB)\r
+ $(link) /NOLOGO $(conlibsmt) $(ldebug) $(conlflags) /OUT:$@ $** $(WINLIBS)\r
+\r
+$(T_CONTEXT): t_context.obj $(CC_SERVER_LIB) $(CC_COMMON_LIB)\r
+ $(link) /NOLOGO $(conlibsmt) $(ldebug) $(conlflags) /OUT:$@ $** $(WINLIBS)\r
+\r
+$(T_LISTS): t_lists.obj $(CC_SERVER_LIB) $(CC_COMMON_LIB)\r
+ $(link) /NOLOGO $(conlibsmt) $(ldebug) $(conlflags) /OUT:$@ $** $(WINLIBS)\r
+\r
+$(T_MSG): t_msg.obj $(CC_SERVER_LIB) $(CC_COMMON_LIB)\r
+ $(link) /NOLOGO $(conlibsmt) $(ldebug) $(conlflags) /OUT:$@ $** $(WINLIBS)\r
+\r
+$(T_SERVER): t_server.obj $(CC_SERVER_LIB) $(CC_COMMON_LIB)\r
+ $(link) /NOLOGO $(conlibsmt) $(ldebug) $(conlflags) /OUT:$@ $** $(WINLIBS)\r
+\r
+clean:\r
+ del *.exe *.dll *.lib *.exp *.obj ntccrpc_c.c ntccrpc_s.c ntccrpc.h\r
\r
-clean: \r
- del *.obj *.exe\r
\r
int i;
cc_int32 code;
- code = cci_ccache_new("The first", p1, cc_credentials_v4_v5, &c1);
- code = cci_ccache_new("The 2nd", p2, cc_credentials_v4_v5, &c2);
+ code = ccs_ccache_new("The first", p1, cc_credentials_v4_v5, &c1);
+ code = ccs_ccache_new("The 2nd", p2, cc_credentials_v4_v5, &c2);
cred1 = (cc_server_credentials_t*)malloc(sizeof(cc_server_credentials_t));
memset(cred1,0,sizeof(cc_server_credentials_t));
strncpy(cred2->creds.credentials.credentials_v4->principal, p1, strlen(p1));
cred3->creds.credentials.credentials_v5->client = p1;
- code = cci_ccache_store_creds(c1, &cred1->creds);
+ code = ccs_ccache_store_creds(c1, &cred1->creds);
printf("(c1, cred1) -> %d\n",code);
- code = cci_ccache_store_creds(c1, &cred2->creds);
+ code = ccs_ccache_store_creds(c1, &cred2->creds);
printf("(c1, cred2) -> %d\n",code);
- code = cci_ccache_store_creds(c2, &cred3->creds);
+ code = ccs_ccache_store_creds(c2, &cred3->creds);
printf("(c2, cred3) -> %d\n",code);
- code = cci_ccache_store_creds(c1, &cred3->creds);
+ code = ccs_ccache_store_creds(c1, &cred3->creds);
printf("(c1, cred3) -> %d\n",code);
i = 0;
- code = cci_ccache_move(c1, c2);
- code = cci_ccache_destroy(c1);
- code = cci_ccache_new_iterator(c2, &iterator);
- while (cci_credentials_iterate_has_next(iterator)) {
+ code = ccs_ccache_move(c1, c2);
+ code = ccs_ccache_destroy(c1);
+ code = ccs_ccache_new_iterator(c2, &iterator);
+ while (ccs_credentials_iterate_has_next(iterator)) {
i++;
- code = cci_credentials_iterate_next(iterator, &node);
+ code = ccs_credentials_iterate_next(iterator, &node);
stored_cred = (cc_server_credentials_t *)node->data;
printf("%d %d %s\n", stored_cred->is_default, stored_cred->creds.version, stored_cred->creds.credentials.credentials_v4->principal);
if (i == 1) {
- code = cci_ccache_rem_creds(c2,&cred2->creds);
+ code = ccs_ccache_rem_creds(c2,&cred2->creds);
printf("(c2 rem cred2) -> %d\n",code);
}
}
int i;
cc_int32 code;
- code = cci_context_new(5, auth_info, session_info, &ctx);
- code = cci_context_create_default_ccache(ctx, cc_credentials_v4, "Spike", &ccache);
- code = cci_context_get_default_ccache_name(ctx, &name);
- code = cci_context_open_ccache(ctx, name, &ccache);
+ code = ccs_context_new(5, auth_info, session_info, &ctx);
+ code = ccs_context_create_default_ccache(ctx, cc_credentials_v4, "Spike", &ccache);
+ code = ccs_context_get_default_ccache_name(ctx, &name);
+ code = ccs_context_open_ccache(ctx, name, &ccache);
for (i = 0; i < 5; i++) {
creds = (cc_credentials_union*)malloc(sizeof(cc_credentials_union));
creds->credentials.credentials_v4 = (cc_credentials_v4_t*)malloc(sizeof(cc_credentials_v4_t));
strcpy(creds->credentials.credentials_v4->principal, "Spike");
- code = cci_ccache_store_creds(ccache, creds);
+ code = ccs_ccache_store_creds(ccache, creds);
}
- code = cci_context_create_ccache(ctx, "ccache 2", cc_credentials_v4, "Jeff", &ccache);
- code = cci_context_open_ccache(ctx, "ccache 2", &ccache);
+ code = ccs_context_create_ccache(ctx, "ccache 2", cc_credentials_v4_v5, "Jeff", &ccache);
+ code = ccs_context_open_ccache(ctx, "ccache 2", &ccache);
for (i = 0; i < 5; i++) {
creds = (cc_credentials_union*)malloc(sizeof(cc_credentials_union));
- creds->version = cc_credentials_v4;
- creds->credentials.credentials_v4 = (cc_credentials_v4_t*)malloc(sizeof(cc_credentials_v4_t));
- strcpy(creds->credentials.credentials_v4->principal, "Jeff");
+ creds->version = cc_credentials_v5;
+ creds->credentials.credentials_v5 = (cc_credentials_v5_t*)malloc(sizeof(cc_credentials_v5_t));
+ strcpy(creds->credentials.credentials_v5->principal, "Jeff");
- cci_ccache_store_creds(ccache, creds);
+ ccs_ccache_store_creds(ccache, creds);
}
- code = cci_context_ccache_iterator(ctx, &ccache_iterator);
- while (cci_ccache_iterate_has_next(ccache_iterator)) {
- code = cci_ccache_iterate_next(ccache_iterator, &ccache_node);
+ code = ccs_context_ccache_iterator(ctx, &ccache_iterator);
+ while (ccs_ccache_iterate_has_next(ccache_iterator)) {
+ code = ccs_ccache_iterate_next(ccache_iterator, &ccache_node);
ccache = (cc_server_ccache_t *)ccache_node->data;
printf("%x for %s %s default = %d v %d\n",
ccache, ccache->principal_v4, ccache->principal_v5,
ccache->is_default, ccache->versions);
- code = cci_ccache_new_iterator(ccache, &creds_iterator);
- while (cci_credentials_iterate_has_next(creds_iterator)) {
- code = cci_credentials_iterate_next(creds_iterator, &creds_node);
+ code = ccs_ccache_new_iterator(ccache, &creds_iterator);
+ while (ccs_credentials_iterate_has_next(creds_iterator)) {
+ code = ccs_credentials_iterate_next(creds_iterator, &creds_node);
server_creds = (cc_server_credentials_t *)creds_node->data;
printf("\t%s %d\n",
server_creds->creds.credentials.credentials_v4->principal,
cc_session_info_t * session_info;
cc_int32 code;
- if ( cci_serv_initialize() != ccNoError )
+ if ( ccs_serv_initialize() != ccNoError )
return 1;
while ( 1 ) {
code = obtain_session_info(&session_info);
/* process message */
- code = cci_serv_process_msg(msg, auth_info, session_info, &resp);
+ code = ccs_serv_process_msg(msg, auth_info, session_info, &resp);
/* flatten response */
code = cci_msg_flatten(resp, NULL);
--- /dev/null
+!include <win32.mak>\r
+\r
+CFLAGS = -I../include $(cdebug) $(cflags) $(cvarsmt)\r
+\r
+CCAPI_SERVER = ccapi_server.exe\r
+\r
+CCAPI_DLLFILE = krbcc32.dll\r
+\r
+WINLIBS = ws2_32.lib rpcrt4.lib $(guilibsdll)\r
+\r
+all: $(CCAPI_DLLFILE) $(CCAPI_SERVER)\r
+\r
+ntccrpc_c.c ntccrpc_s.c ntccrpc.h: ntccrpc.idl ntccrpc.acf\r
+ midl ntccrpc.idl /acf ntccrpc.acf\r
+\r
+CLIENT_OBJS = ntccrpc_c.obj client.obj dllmain.obj\r
+\r
+SERVER_OBJS = ntccrpc_s.obj server.obj\r
+\r
+CC_CLIENT_LIB = ..\client\cc_client.lib\r
+\r
+CC_COMMON_LIB = ..\common\cc_common.lib\r
+\r
+CC_SERVER_LIB = ..\server\cc_server.lib\r
+\r
+$(CCAPI_DLLFILE): $(CLIENT_OBJS) $(CC_CLIENT_LIB) $(CC_COMMON_LIB)\r
+ $(link) /NOLOGO /OUT:$@ $(ldebug) $(dlllflags) $(guilibsmt) -def:cacheapi.def $** $(WINLIBS)\r
+\r
+$(CCAPI_SERVER): $(SERVER_OBJS) $(CC_SERVER_LIB) $(CC_COMMON_LIB)\r
+ $(link) /NOLOGO $(conlibsmt) $(ldebug) $(conlflags) /OUT:$@ $** $(WINLIBS)\r
+\r
+clean:\r
+ del *.exe *.dll *.lib *.exp *.obj ntccrpc_c.c ntccrpc_s.c ntccrpc.h\r
+\r
+\r
--- /dev/null
+#include <windows.h>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <tchar.h>\r
+#include "ntccrpc.h"\r
+#include <strsafe.h>\r
+#include "CredentialsCache.h"\r
+#include "msg.h"\r
+\r
+static RPC_BINDING_HANDLE hRpcBinding;\r
+\r
+void * __RPC_USER MIDL_user_allocate(size_t s) {\r
+ return malloc(s);\r
+}\r
+\r
+void __RPC_USER MIDL_user_free(void * p) {\r
+ free(p);\r
+}\r
+\r
+int cc_rpc_init(void) {\r
+ RPC_STATUS status;\r
+ TCHAR * bindstring = NULL;\r
+ RPC_SECURITY_QOS sqos;\r
+\r
+ status = RpcStringBindingCompose(NULL,\r
+ _T("ncalrpc"),\r
+ NULL,\r
+ NULL,\r
+ NULL,\r
+ &bindstring);\r
+\r
+ if (status != RPC_S_OK) {\r
+ fprintf(stderr, "RpcStringBindingCompose failed: %d\n",\r
+ status);\r
+ return 1;\r
+ }\r
+\r
+ status = RpcBindingFromStringBinding(bindstring,\r
+ &hRpcBinding);\r
+\r
+ if (status != RPC_S_OK) {\r
+ fprintf(stderr, "RpcBindingFromStringBinding failed: %d\n",\r
+ status);\r
+ return 1;\r
+ }\r
+\r
+ status = RpcStringFree(&bindstring);\r
+\r
+ ZeroMemory(&sqos, sizeof(sqos));\r
+\r
+ sqos.Version = 1;\r
+ sqos.Capabilities = RPC_C_QOS_CAPABILITIES_DEFAULT;\r
+ sqos.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC;\r
+ sqos.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE;\r
+\r
+ status = RpcBindingSetAuthInfoEx(hRpcBinding,\r
+ NULL,\r
+ RPC_C_AUTHN_LEVEL_CALL,\r
+ RPC_C_AUTHN_WINNT,\r
+ NULL,\r
+ 0,\r
+ &sqos);\r
+ if (status != RPC_S_OK) {\r
+ fprintf(stderr, "RpcBindingSetAuthInfoEx failed: %d\n",\r
+ status);\r
+ return 1;\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+int cc_rpc_cleanup(void) {\r
+ RPC_STATUS status;\r
+\r
+ status = RpcBindingFree(&hRpcBinding);\r
+\r
+ return 0;\r
+}\r
+\r
+cc_int32 cci_set_thread_session_id(unsigned char * client_name, LUID luid) {\r
+\r
+}\r
+\r
+void cci_get_thread_session_id(unsigned char * client_name, int len, LUID *pluid) {\r
+\r
+}\r
+\r
+\r
+/* __int32 ccapi_Message(\r
+ * [in] handle_t h,\r
+ * [string][in] unsigned char *client_name,\r
+ * [in] struct _LUID luid,\r
+ * [in] __int32 cb_buffer,\r
+ * [out] __int32 *cb_len,\r
+ * [size_is][string][out] unsigned char buffer[ ]);\r
+ */\r
+\r
+cc_int32 cci_perform_rpc(cc_msg_t *request, cc_msg_t **response)\r
+{\r
+ __int32 rpc_code;\r
+ unsigned char client_name[256];\r
+ LUID luid;\r
+ struct __LUID __luid;\r
+ unsigned char out_buf[MAXMSGLEN];\r
+ __int32 out_len = MAXMSGLEN;\r
+\r
+ if (!cc_rpc_init())\r
+ return -1;\r
+\r
+ cci_get_thread_session_id(client_name, sizeof(client_name), &luid);\r
+\r
+ __luid.HighPart = luid.HighPart;\r
+ __luid.LowPart = luid.LowPart;\r
+\r
+ rpc_code = ccapi_Message(hRpcBinding, client_name, __luid, \r
+ request->flat, request->flat_len, \r
+ out_buf, &out_len);\r
+\r
+ return rpc_code;\r
+}\r
--- /dev/null
+[\r
+ explicit_handle\r
+]\r
+\r
+interface portable_ccapi\r
+{\r
+\r
+}
\ No newline at end of file
--- /dev/null
+[\r
+ uuid(c8b4a635-e9e4-4650-a073-b25610324950),\r
+ version(1.0),\r
+ endpoint("ncalrpc:[mit_nt_ccapi]"),\r
+ pointer_default(unique)\r
+]\r
+\r
+interface portable_ccapi\r
+{\r
+ const short MAXMSGLEN = 65536;\r
+\r
+ // Locally Unique Identifier\r
+ //\r
+\r
+ struct __LUID {\r
+ __int32 LowPart;\r
+ long HighPart;\r
+ };\r
+\r
+\r
+ // The Generic CCAPI Message RPC\r
+\r
+ __int32 ccapi_Message (\r
+ [in] handle_t h,\r
+ [in, string] unsigned char * client_name,\r
+ [in] struct __LUID luid,\r
+ [in, length_is(in_len), size_is(MAXMSGLEN)] unsigned char in_buf[],\r
+ [in] __int32 in_len,\r
+ [out, length_is(*out_len), size_is(MAXMSGLEN)] unsigned char out_buf[],\r
+ [out] __int32 * out_len);\r
+}\r
--- /dev/null
+\r
+\r
+#include <windows.h>\r
+#include "msg.h"\r
+#include "marshall.h"\r
+#include "serv_ops.h"\r
+#include "datastore.h"\r
+#include <stdio.h>\r
+#include <process.h>\r
+#include <tchar.h>\r
+#include <rpc.h>\r
+#include <rpcndr.h>\r
+#include "ntccrpc.h"\r
+#include <strsafe.h>\r
+\r
+#define SVCNAME "MIT_CCAPI_NT_Service"\r
+\r
+SERVICE_STATUS_HANDLE h_service_status = NULL;\r
+SERVICE_STATUS service_status;\r
+FILE * logfile = NULL;\r
+\r
+/* Log File */\r
+void begin_log(void) {\r
+ char temppath[512];\r
+\r
+ temppath[0] = L'\0';\r
+\r
+ GetTempPathA(sizeof(temppath), temppath);\r
+ StringCbCatA(temppath, sizeof(temppath), "mit_nt_ccapi.log");\r
+ logfile = fopen(temppath, "w");\r
+}\r
+\r
+void end_log(void) {\r
+ if (logfile) {\r
+ fclose(logfile);\r
+ logfile = NULL;\r
+ }\r
+}\r
+\r
+BOOL report_status(DWORD state,\r
+ DWORD exit_code,\r
+ DWORD wait_hint) {\r
+ static DWORD checkpoint = 1;\r
+ BOOL rv = TRUE;\r
+\r
+ if (state == SERVICE_START_PENDING)\r
+ service_status.dwControlsAccepted = 0;\r
+ else\r
+ service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;\r
+\r
+ service_status.dwCurrentState = state;\r
+ service_status.dwWin32ExitCode = exit_code;\r
+ service_status.dwWaitHint = wait_hint;\r
+\r
+ if (state == SERVICE_RUNNING ||\r
+ state == SERVICE_STOPPED)\r
+ service_status.dwCheckPoint = 0;\r
+ else\r
+ service_status.dwCheckPoint = checkpoint++;\r
+\r
+ rv = SetServiceStatus(h_service_status, &service_status);\r
+\r
+ return rv;\r
+}\r
+\r
+void service_start(DWORD argc, LPTSTR * argv) {\r
+ RPC_STATUS status;\r
+ RPC_BINDING_VECTOR * bv;\r
+\r
+ status = RpcServerUseProtseq("ncalrpc",\r
+ RPC_C_PROTSEQ_MAX_REQS_DEFAULT,\r
+ NULL);\r
+\r
+ if (status != RPC_S_OK) {\r
+ return;\r
+ }\r
+\r
+ report_status(SERVICE_START_PENDING, NO_ERROR, 3000);\r
+\r
+ status = RpcServerRegisterIf(portable_ccapi_v1_0_s_ifspec,\r
+ 0, 0);\r
+\r
+ if (status != RPC_S_OK)\r
+ return;\r
+\r
+ report_status(SERVICE_START_PENDING, NO_ERROR, 3000);\r
+\r
+ status = RpcServerInqBindings(&bv);\r
+\r
+ if (status != RPC_S_OK)\r
+ return;\r
+\r
+ status = RpcEpRegister(portable_ccapi_v1_0_s_ifspec,\r
+ bv, 0, 0);\r
+\r
+ if (status != RPC_S_OK)\r
+ return;\r
+\r
+ report_status(SERVICE_START_PENDING, NO_ERROR, 3000);\r
+\r
+ status = RpcServerRegisterAuthInfo(NULL,\r
+ RPC_C_AUTHN_WINNT,\r
+ 0, 0);\r
+\r
+ if (status != RPC_S_OK)\r
+ return;\r
+\r
+ report_status(SERVICE_START_PENDING, NO_ERROR, 3000);\r
+\r
+ status = RpcServerListen(1,\r
+ RPC_C_LISTEN_MAX_CALLS_DEFAULT,\r
+ TRUE);\r
+\r
+ if (status != RPC_S_OK)\r
+ return;\r
+\r
+ report_status(SERVICE_RUNNING, NO_ERROR, 0);\r
+\r
+ begin_log();\r
+\r
+ status = RpcMgmtWaitServerListen();\r
+\r
+ end_log();\r
+\r
+ RpcEpUnregister(portable_ccapi_v1_0_s_ifspec, bv, 0);\r
+\r
+ RpcBindingVectorFree(&bv);\r
+}\r
+\r
+void service_stop(void) {\r
+ RpcMgmtStopServerListening(0);\r
+}\r
+\r
+void * __RPC_USER MIDL_user_allocate(size_t s) {\r
+ return malloc(s);\r
+}\r
+\r
+void __RPC_USER MIDL_user_free(void * p) {\r
+ free(p);\r
+}\r
+\r
+typedef struct tag_client_info {\r
+ char client_name[512];\r
+ LUID luid; \r
+} client_info_t;\r
+\r
+int obtain_auth_info(client_info_t * client_info, cc_auth_info_t ** pauth_info)\r
+{\r
+ *pauth_info = (cc_auth_info_t *)malloc(sizeof(cc_auth_info_t));\r
+ if ( !*pauth_info )\r
+ return ccErrNoMem;\r
+ \r
+ (*pauth_info)->len = strlen(client_info->client_name) + 1;\r
+ (*pauth_info)->info = malloc((*pauth_info)->len);\r
+ if ( !(*pauth_info)->info ) {\r
+ free(*pauth_info);\r
+ return ccErrNoMem;\r
+ }\r
+\r
+ memcpy((*pauth_info)->info, client_info->client_name, (*pauth_info)->len);\r
+ \r
+ return 0;\r
+}\r
+\r
+void destroy_auth_info(cc_auth_info_t *auth_info)\r
+{\r
+ free(auth_info->info);\r
+ free(auth_info);\r
+}\r
+\r
+int obtain_session_info(client_info_t * client_info, cc_session_info_t ** psession_info)\r
+{\r
+ *psession_info = (cc_session_info_t *)malloc(sizeof(cc_session_info_t));\r
+ if ( !*psession_info )\r
+ return ccErrNoMem;\r
+ \r
+ (*psession_info)->len = sizeof(LUID);\r
+ (*psession_info)->info = malloc((*psession_info)->len);\r
+ if ( !(*psession_info)->info ) {\r
+ free(*psession_info);\r
+ return ccErrNoMem;\r
+ }\r
+\r
+ memcpy((*psession_info)->info, &client_info->luid, (*psession_info)->len);\r
+ \r
+ return 0;\r
+}\r
+\r
+void destroy_session_info(cc_session_info_t *session_info)\r
+{\r
+ free(session_info->info);\r
+ free(session_info);\r
+}\r
+\r
+RPC_STATUS check_auth(handle_t h, client_info_t * client_info) {\r
+ RPC_BINDING_HANDLE bh = (RPC_BINDING_HANDLE) h;\r
+ RPC_STATUS status;\r
+ HANDLE htoken = NULL;\r
+ char name[256];\r
+ char domain[256];\r
+ DWORD name_len;\r
+ DWORD domain_len;\r
+ SID_NAME_USE snu = 0;\r
+\r
+ struct {\r
+ TOKEN_ORIGIN origin;\r
+ char pad[512];\r
+ } torigin;\r
+\r
+ struct {\r
+ TOKEN_OWNER owner;\r
+ char pad[4096];\r
+ } towner;\r
+\r
+ DWORD len;\r
+\r
+ status = RpcImpersonateClient(bh);\r
+\r
+ if (status != RPC_S_OK)\r
+ return status;\r
+\r
+ if (!OpenThreadToken(GetCurrentThread(),\r
+ TOKEN_READ | TOKEN_QUERY_SOURCE,\r
+ FALSE,\r
+ &htoken)) {\r
+ status = GetLastError();\r
+ goto _cleanup;\r
+ }\r
+\r
+ len = 0;\r
+\r
+ if (!GetTokenInformation(htoken,\r
+ TokenOrigin,\r
+ &torigin.origin,\r
+ sizeof(torigin),\r
+ &len)) {\r
+ status = GetLastError();\r
+ goto _cleanup;\r
+ }\r
+\r
+ if (!GetTokenInformation(htoken,\r
+ TokenOwner,\r
+ &towner.owner,\r
+ sizeof(towner),\r
+ &len)) {\r
+ status = GetLastError();\r
+ goto _cleanup;\r
+ }\r
+\r
+\r
+ name_len = sizeof(name)/sizeof(name[0]);\r
+ domain_len = sizeof(domain)/sizeof(domain[0]);\r
+\r
+ if (!LookupAccountSidA(NULL,\r
+ towner.owner.Owner,\r
+ name,\r
+ &name_len,\r
+ domain,\r
+ &domain_len,\r
+ &snu)) {\r
+ status = GetLastError();\r
+ goto _cleanup;\r
+ }\r
+\r
+ client_info->luid = torigin.origin.OriginatingLogonSession;\r
+ StringCbPrintfA(client_info->client_name,\r
+ sizeof(client_info->client_name),\r
+ "%s\\%s", domain, name);\r
+\r
+ status = 0;\r
+\r
+ _cleanup:\r
+\r
+ RpcRevertToSelf();\r
+\r
+ return status;\r
+}\r
+\r
+__int32 ccapi_Message( \r
+ /* [in] */ handle_t h,\r
+ /* [string][in] */ unsigned char *client_name,\r
+ /* [in] */ struct __LUID luid,\r
+ /* [size_is][length_is][in] */ unsigned char in_buf[],\r
+ /* [in] */ __int32 in_len,\r
+ /* [size_is][length_is][out] */ unsigned char out_buf[],\r
+ /* [out] */ __int32 *out_len)\r
+{\r
+ client_info_t client_info;\r
+ cc_msg_t * msg;\r
+ cc_msg_t * resp;\r
+ cc_auth_info_t * auth_info;\r
+ cc_session_info_t * session_info;\r
+ cc_int32 code;\r
+\r
+ if ( ccs_serv_initialize() != ccNoError ) {\r
+ code = ccErrServerUnavailable;\r
+ goto done;\r
+ }\r
+\r
+ code = check_auth(h, &client_info);\r
+ if (code == 0) {\r
+ if (!strcmp("SYSTEM",client_info.client_name) &&\r
+ client_info.luid.HighPart == 0 &&\r
+ client_info.luid.LowPart == 0 &&\r
+ client_name != NULL &&\r
+ client_name[0] != '\0') {\r
+ StringCbPrintfA(client_info.client_name,\r
+ sizeof(client_info.client_name),\r
+ "%s", client_name);\r
+ client_info.luid.HighPart = luid.HighPart;\r
+ client_info.luid.LowPart = luid.LowPart;\r
+ }\r
+ } else {\r
+ code = ccErrServerCantBecomeUID;\r
+ goto done;\r
+ }\r
+\r
+ /* allocate message */\r
+ msg = (cc_msg_t *)malloc(sizeof(cc_msg_t));\r
+ if (!msg) {\r
+ code = ccErrNoMem;\r
+ goto done;\r
+ }\r
+\r
+ /* unflatten message */\r
+ code = cci_msg_unflatten(in_buf, in_len, &msg);\r
+ if (code)\r
+ goto cleanup;\r
+\r
+ /* obtain auth info */\r
+ code = obtain_auth_info(&client_info, &auth_info);\r
+ if (code)\r
+ goto cleanup;\r
+\r
+ /* obtain session info */\r
+ code = obtain_session_info(&client_info, &session_info);\r
+ if (code)\r
+ goto cleanup;\r
+\r
+ /* process message */\r
+ code = ccs_serv_process_msg(msg, auth_info, session_info, &resp);\r
+ if (code)\r
+ goto cleanup;\r
+\r
+ /* flatten response */\r
+ code = cci_msg_flatten(resp, NULL);\r
+ if (code)\r
+ goto cleanup;\r
+\r
+ /* send response */\r
+ if (resp->flat_len > MAXMSGLEN) {\r
+ code = ccErrBadInternalMessage;\r
+ goto cleanup;\r
+ }\r
+ memcpy(out_buf, resp->flat, resp->flat_len);\r
+ *out_len = resp->flat_len;\r
+ code = ccNoError;\r
+\r
+ cleanup:\r
+ if (auth_info)\r
+ destroy_auth_info(auth_info);\r
+\r
+ if (session_info)\r
+ destroy_session_info(session_info);\r
+\r
+ /* free message */\r
+ if (msg)\r
+ cci_msg_destroy(msg);\r
+\r
+ /* free response */\r
+ if (resp)\r
+ cci_msg_destroy(resp);\r
+\r
+ done:\r
+ return code ? -1 : 0;\r
+}\r
+\r
+void WINAPI service_control(DWORD ctrl_code) {\r
+ switch(ctrl_code) {\r
+ case SERVICE_CONTROL_STOP:\r
+ report_status(SERVICE_STOP_PENDING, NO_ERROR, 0);\r
+ service_stop();\r
+ return;\r
+\r
+ /* everything else falls through */\r
+ }\r
+\r
+ report_status(service_status.dwCurrentState, NO_ERROR, 0);\r
+}\r
+\r
+void WINAPI service_main(DWORD argc, LPTSTR * argv) {\r
+\r
+ h_service_status = RegisterServiceCtrlHandler( _T(SVCNAME), service_control);\r
+\r
+ if (!h_service_status)\r
+ goto cleanup;\r
+\r
+ ZeroMemory(&service_status, sizeof(service_status));\r
+\r
+ service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;\r
+ service_status.dwServiceSpecificExitCode = 0;\r
+\r
+ if (!report_status(SERVICE_START_PENDING,\r
+ NO_ERROR,\r
+ 3000))\r
+ goto cleanup;\r
+\r
+ service_start(argc, argv);\r
+\r
+ cleanup:\r
+\r
+ if (h_service_status) {\r
+ report_status(SERVICE_STOPPED, NO_ERROR, 0);\r
+ }\r
+}\r
+\r
+\r
+BOOL\r
+IsInstalled()\r
+{\r
+ BOOL bResult = FALSE;\r
+ SC_HANDLE hSCM;\r
+ SC_HANDLE hService;\r
+\r
+ // Open the Service Control Manager\r
+ hSCM = OpenSCManager( NULL, // local machine\r
+ NULL, // ServicesActive database\r
+ SC_MANAGER_ALL_ACCESS); // full access\r
+ if (hSCM) {\r
+\r
+ // Try to open the service\r
+ hService = OpenService( hSCM,\r
+ SVCNAME,\r
+ SERVICE_QUERY_CONFIG);\r
+ if (hService) {\r
+ bResult = TRUE;\r
+ CloseServiceHandle(hService);\r
+ }\r
+\r
+ CloseServiceHandle(hSCM);\r
+ }\r
+\r
+ return bResult;\r
+}\r
+\r
+BOOL\r
+Install()\r
+{\r
+ char szFilePath[_MAX_PATH];\r
+ SC_HANDLE hSCM;\r
+ SC_HANDLE hService;\r
+ TCHAR szKey[256];\r
+ HKEY hKey = NULL;\r
+ DWORD dwData;\r
+\r
+ // Open the Service Control Manager\r
+ hSCM = OpenSCManager( NULL, // local machine\r
+ NULL, // ServicesActive database\r
+ SC_MANAGER_ALL_ACCESS); // full access\r
+ if (!hSCM)\r
+ return FALSE;\r
+\r
+ // Get the executable file path\r
+ GetModuleFileName(NULL, szFilePath, sizeof(szFilePath));\r
+\r
+ // Create the service\r
+ hService = CreateService( hSCM,\r
+ SVCNAME,\r
+ SVCNAME,\r
+ SERVICE_ALL_ACCESS,\r
+ SERVICE_WIN32_OWN_PROCESS,\r
+ SERVICE_AUTO_START, // start condition\r
+ SERVICE_ERROR_NORMAL,\r
+ szFilePath,\r
+ NULL,\r
+ NULL,\r
+ NULL,\r
+ NULL,\r
+ NULL);\r
+ if (!hService) {\r
+ CloseServiceHandle(hSCM);\r
+ return FALSE;\r
+ }\r
+\r
+ // make registry entries to support logging messages\r
+ // Add the source name as a subkey under the Application\r
+ // key in the EventLog service portion of the registry.\r
+ StringCbCopyA(szKey, 256, "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\IKSD");\r
+ if (RegCreateKey(HKEY_LOCAL_MACHINE, szKey, &hKey) != ERROR_SUCCESS) {\r
+ CloseServiceHandle(hService);\r
+ CloseServiceHandle(hSCM);\r
+ return FALSE;\r
+ }\r
+\r
+ // Add the Event ID message-file name to the 'EventMessageFile' subkey.\r
+ RegSetValueEx( hKey,\r
+ "EventMessageFile",\r
+ 0,\r
+ REG_EXPAND_SZ,\r
+ (CONST BYTE*)szFilePath,\r
+ strlen(szFilePath) + 1);\r
+\r
+ // Set the supported types flags.\r
+ dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;\r
+ RegSetValueEx( hKey,\r
+ "TypesSupported",\r
+ 0,\r
+ REG_DWORD,\r
+ (CONST BYTE*)&dwData,\r
+ sizeof(DWORD));\r
+ RegCloseKey(hKey);\r
+\r
+ // LogEvent(EVENTLOG_INFORMATION_TYPE, EVMSG_INSTALLED, SVCNAME);\r
+\r
+ // tidy up\r
+ CloseServiceHandle(hService);\r
+ CloseServiceHandle(hSCM);\r
+ return TRUE;\r
+}\r
+\r
+BOOL\r
+Uninstall()\r
+{\r
+ BOOL bResult = FALSE;\r
+ SC_HANDLE hService;\r
+ SC_HANDLE hSCM;\r
+\r
+ // Open the Service Control Manager\r
+ hSCM = OpenSCManager( NULL, // local machine\r
+ NULL, // ServicesActive database\r
+ SC_MANAGER_ALL_ACCESS); // full access\r
+ if (!hSCM)\r
+ return FALSE;\r
+\r
+ hService = OpenService( hSCM,\r
+ _T(SVCNAME),\r
+ DELETE);\r
+ if (hService) {\r
+ if (DeleteService(hService)) {\r
+ // LogEvent(EVENTLOG_INFORMATION_TYPE, EVMSG_REMOVED, SVCNAME);\r
+ bResult = TRUE;\r
+ } else {\r
+ // LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_NOTREMOVED, SVCNAME);\r
+ }\r
+ CloseServiceHandle(hService);\r
+ }\r
+\r
+ CloseServiceHandle(hSCM);\r
+ return bResult;\r
+}\r
+\r
+\r
+// Returns TRUE if it found an arg it recognised, FALSE if not\r
+// Note: processing some arguments causes output to stdout to be generated.\r
+BOOL\r
+ParseStandardArgs(int argc, char* argv[])\r
+{\r
+ char szFilePath[_MAX_PATH]="not a file name";\r
+\r
+ // See if we have any command line args we recognize\r
+ if (argc <= 1)\r
+ return FALSE;\r
+\r
+ if ( _stricmp(argv[1], "-h") == 0 ||\r
+ _stricmp(argv[1], "-?") == 0 ||\r
+ _stricmp(argv[1], "/h") == 0 ||\r
+ _stricmp(argv[1], "/?") == 0) {\r
+\r
+ //\r
+ GetModuleFileNameA(NULL, szFilePath, sizeof(szFilePath));\r
+ fprintf(stderr, "usage: %s [-v | -i | -u | -h]\r\n",szFilePath);\r
+ return TRUE;\r
+ } else if (_stricmp(argv[1], "-v") == 0 ||\r
+ _stricmp(argv[1], "/v") == 0 ) {\r
+\r
+ // Spit out version info\r
+ fprintf(stderr, "%s Version 0.1\n",_T(SVCNAME));\r
+ fprintf(stderr, "The service is %s installed\n",\r
+ IsInstalled() ? "currently" : "not");\r
+ return TRUE; // say we processed the argument\r
+\r
+ } else if (_stricmp(argv[1], "-i") == 0 ||\r
+ _stricmp(argv[1], "/i") == 0) {\r
+\r
+ // Request to install.\r
+ if (IsInstalled()) {\r
+ fprintf(stderr, "%s is already installed\n", _T(SVCNAME));\r
+ } else {\r
+ // Try and install the copy that's running\r
+ if (Install()) {\r
+ fprintf(stderr, "%s installed\n", _T(SVCNAME));\r
+ } else {\r
+ fprintf(stderr, "%s failed to install. Error %d\n", _T(SVCNAME), GetLastError());\r
+ }\r
+ }\r
+ return TRUE; // say we processed the argument\r
+\r
+ } else if (_stricmp(argv[1], "-u") == 0 ||\r
+ _stricmp(argv[1], "/u") == 0) {\r
+\r
+ // Request to uninstall.\r
+ if (!IsInstalled()) {\r
+ fprintf(stderr, "%s is not installed\n", _T(SVCNAME));\r
+ } else {\r
+ // Try and remove the copy that's installed\r
+ if (Uninstall()) {\r
+ // Get the executable file path\r
+ GetModuleFileNameA(NULL, szFilePath, sizeof(szFilePath));\r
+ fprintf(stderr, "%s removed. (You must delete the file (%s) yourself.)\n",\r
+ _T(SVCNAME), szFilePath);\r
+ } else {\r
+ fprintf(stderr, "Could not remove %s. Error %d\n", _T(SVCNAME), GetLastError());\r
+ }\r
+ }\r
+ return TRUE; // say we processed the argument\r
+\r
+ }\r
+\r
+ // Don't recognise the args\r
+ return FALSE;\r
+}\r
+\r
+int main(int argc, char ** argv) {\r
+\r
+ SERVICE_TABLE_ENTRY dispatch_table[] = {\r
+ { _T(SVCNAME), (LPSERVICE_MAIN_FUNCTION) service_main },\r
+ { NULL, NULL }\r
+ };\r
+\r
+ if ( ParseStandardArgs(argc, argv) )\r
+ return 0;\r
+\r
+ if (!StartServiceCtrlDispatcher(dispatch_table)) {\r
+ fprintf(stderr, "Can't start service control dispatcher\n");\r
+ }\r
+\r
+ return 0;\r
+}\r