more updates
authorJeffrey Altman <jaltman@secure-endpoints.com>
Mon, 5 Jun 2006 04:30:35 +0000 (04:30 +0000)
committerJeffrey Altman <jaltman@secure-endpoints.com>
Mon, 5 Jun 2006 04:30:35 +0000 (04:30 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/branches/ccapi@18082 dc483132-0cff-0310-8789-dd5450dbe970

20 files changed:
src/lib/ccapi/NTMakefile [new file with mode: 0644]
src/lib/ccapi/client/NTMakefile
src/lib/ccapi/common/NTMakefile
src/lib/ccapi/doc/implementation-notes.txt [new file with mode: 0644]
src/lib/ccapi/include/CredentialsCache.h
src/lib/ccapi/include/CredentialsCache2.h
src/lib/ccapi/include/datastore.h [moved from src/lib/ccapi/server/datastore.h with 100% similarity]
src/lib/ccapi/include/rpc_auth.h [moved from src/lib/ccapi/server/rpc_auth.h with 100% similarity]
src/lib/ccapi/server/NTMakefile
src/lib/ccapi/unit-test/NTMakefile
src/lib/ccapi/unit-test/t_ccache.c
src/lib/ccapi/unit-test/t_context.c
src/lib/ccapi/unit-test/t_server.c
src/lib/ccapi/windows/NTMakefile [new file with mode: 0644]
src/lib/ccapi/windows/cacheapi.def [moved from src/lib/ccapi/client/cacheapi.def with 100% similarity]
src/lib/ccapi/windows/client.c [new file with mode: 0644]
src/lib/ccapi/windows/dllmain.c [moved from src/lib/ccapi/client/dllmain.c with 100% similarity]
src/lib/ccapi/windows/ntccrpc.acf [new file with mode: 0644]
src/lib/ccapi/windows/ntccrpc.idl [new file with mode: 0644]
src/lib/ccapi/windows/server.c [new file with mode: 0644]

diff --git a/src/lib/ccapi/NTMakefile b/src/lib/ccapi/NTMakefile
new file mode 100644 (file)
index 0000000..57c42e3
--- /dev/null
@@ -0,0 +1,25 @@
+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
index f9d85b463e315baddfcccf06e9f7131d52903a8b..2ce87c35076b335157d943c82dbce69ed490727f 100644 (file)
@@ -1,6 +1,6 @@
 !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
@@ -12,14 +12,7 @@ CC_COMMON_LIB = ..\common\cc_common.lib
 $(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
index d0d92fcbd635f45b2de10bcd2a6933fd889b1a0f..a43985af3a6ca8c905c510e33b4ad1ea8b90023e 100644 (file)
@@ -1,6 +1,6 @@
 !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
diff --git a/src/lib/ccapi/doc/implementation-notes.txt b/src/lib/ccapi/doc/implementation-notes.txt
new file mode 100644 (file)
index 0000000..e6150fb
--- /dev/null
@@ -0,0 +1,156 @@
+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
index 7143da4e255509cdf6ee13351905849c7f2bd347..12748bb9d40672333293e8f06367874cf825fe1a 100644 (file)
     #include <sys/types.h>
 #endif
 
+#if defined(_WIN32)
+#include <winsock.h>
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif /* __cplusplus */
index 214d93a02f0a74b0b8a32a880737ea154d982b10..706c86913311036aea5728039e001019e5d4a212 100644 (file)
@@ -87,10 +87,10 @@ typedef struct cc_credentials_v5_compat {
     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;
@@ -185,6 +185,13 @@ enum {
     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);
        
@@ -194,7 +201,7 @@ CCACHE_API cc_int32 cc_get_NC_info (
        
 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,
@@ -293,6 +300,11 @@ CCACHE_API cc_int32 cc_free_NC_info (
        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
index b221bcb9413eefa0dfca8eac8f2d8ecc11542ceb..564097c5344d01f4e8274ca459b2f76d1f3a393d 100644 (file)
@@ -1,18 +1,21 @@
-# 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
index eb2fd53e21b997a857d5f2975ce6a1b21d82e0ef..9bb486b6d3dc4bdfcec843f89ba69da0174c2715 100644 (file)
@@ -1,30 +1,48 @@
-# 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
index 6ef33ea23df5c7fec5b6e8225867f140b15beeee..175764e798a9f9dd86f73fc8dbd338f493a762ac 100644 (file)
@@ -59,8 +59,8 @@ int main() {
     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));
@@ -83,30 +83,30 @@ int main() {
     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);
         }
     }
index 9e35d9abf3a4d23f994352760ba1845d9d155d02..d76ff78d9a9d1fbda9f919179d3d3cfca8a4bd22 100644 (file)
@@ -68,10 +68,10 @@ int main() {
     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));
@@ -79,32 +79,32 @@ int main() {
         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, 
index d4d9984661f6f2a5565f80d15ca60fb1623868e2..d6093941ef5eb3125cbd2ed1f646b399d557de63 100644 (file)
@@ -143,7 +143,7 @@ main(void)
     cc_session_info_t * session_info;
     cc_int32            code;
 
-    if ( cci_serv_initialize() != ccNoError )
+    if ( ccs_serv_initialize() != ccNoError )
         return 1;
 
     while ( 1 ) {
@@ -163,7 +163,7 @@ main(void)
         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);
diff --git a/src/lib/ccapi/windows/NTMakefile b/src/lib/ccapi/windows/NTMakefile
new file mode 100644 (file)
index 0000000..f6fee6f
--- /dev/null
@@ -0,0 +1,35 @@
+!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
diff --git a/src/lib/ccapi/windows/client.c b/src/lib/ccapi/windows/client.c
new file mode 100644 (file)
index 0000000..db0b63a
--- /dev/null
@@ -0,0 +1,120 @@
+#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
diff --git a/src/lib/ccapi/windows/ntccrpc.acf b/src/lib/ccapi/windows/ntccrpc.acf
new file mode 100644 (file)
index 0000000..77216a9
--- /dev/null
@@ -0,0 +1,8 @@
+[\r
+    explicit_handle\r
+]\r
+\r
+interface portable_ccapi\r
+{\r
+\r
+}
\ No newline at end of file
diff --git a/src/lib/ccapi/windows/ntccrpc.idl b/src/lib/ccapi/windows/ntccrpc.idl
new file mode 100644 (file)
index 0000000..0dd038f
--- /dev/null
@@ -0,0 +1,31 @@
+[\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
diff --git a/src/lib/ccapi/windows/server.c b/src/lib/ccapi/windows/server.c
new file mode 100644 (file)
index 0000000..5ce8e6e
--- /dev/null
@@ -0,0 +1,638 @@
+\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