44d1ef5a1e2401ddcd828c68f67f035f2bf2fe31
[moonshot-ui.git] / libmoonshot / libmoonshot-msrpc.c
1 /* libmoonshot - Moonshot client library
2  * Copyright (c) 2011, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * Author: Sam Thursfield <samthursfield@codethink.co.uk>
33  */
34
35 #include <windows.h>
36 //#include <rpc.h>
37 #include <msrpc-mingw.h>
38
39 #include "libmoonshot.h"
40 #include "libmoonshot-common.h"
41 #include "moonshot-msrpc.h"
42
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <string.h>
46
47 #define MOONSHOT_ENDPOINT_NAME "/org/janet/Moonshot"
48 #define MOONSHOT_INSTALL_PATH_KEY "Software\\Moonshot"
49
50
51 static MoonshotError *moonshot_error_new_from_status (MoonshotErrorCode code,
52                                                       DWORD             status)
53 {
54     MoonshotError *error = malloc (sizeof (MoonshotError));
55     error->code = code;
56     error->message = malloc (256);
57
58     FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, (LPSTR)error->message, 255, NULL);
59     return error;
60 }
61
62 static MoonshotError *moonshot_error_new_with_status (MoonshotErrorCode code,
63                                                       DWORD             status,
64                                                       const char        *format, ...)
65 {
66     MoonshotError *error = malloc (sizeof (MoonshotError));
67     char *buffer;
68     va_list args;
69     int length;
70
71     va_start (args, format);
72
73     error->code = code;
74
75     buffer = malloc (strlen (format) + 256 + 3);
76     strcpy (buffer, format);
77     strcat (buffer, ": ");
78
79     FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
80                    NULL,
81                    status,
82                    0,
83                    (LPSTR)buffer + strlen (format) + 3,
84                    255,
85                    NULL);
86
87     length = _vscprintf (buffer, args);
88     error->message = malloc (length + 1);
89     _vsnprintf (error->message, length, buffer, args);
90     free (buffer);
91
92     va_end (args);
93
94     return error;
95 }
96
97 static void launch_server (MoonshotError **error) {
98     HKEY key = NULL;
99     STARTUPINFO startup_info = { 0 };
100     PROCESS_INFORMATION process_info = { 0 };
101     LONG status;
102     BOOL success;
103     DWORD value_type;
104     DWORD length;
105     char exe_path[1024];
106
107     status = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
108                            MOONSHOT_INSTALL_PATH_KEY,
109                            0,
110                            KEY_READ,
111                            &key);
112
113     if (status == ERROR_FILE_NOT_FOUND) {
114         *error = moonshot_error_new
115                    (MOONSHOT_ERROR_INSTALLATION_ERROR,
116                     "Moonshot is not installed correctly on this system. "
117                     "(Registry key HKLM\\%s was not found).",
118                     MOONSHOT_INSTALL_PATH_KEY);
119         return;
120     } else if (status != 0) {
121         *error = moonshot_error_new_with_status
122                    (MOONSHOT_ERROR_OS_ERROR,
123                     status,
124                     "Unable to read registry key HKLM\\%s",
125                     MOONSHOT_INSTALL_PATH_KEY);
126         return;
127     }
128
129     length = 1023;
130     status = RegQueryValueEx (key, NULL, NULL, &value_type, exe_path, &length);
131
132     if (value_type != REG_SZ) {
133         *error = moonshot_error_new_with_status
134                    (MOONSHOT_ERROR_INSTALLATION_ERROR,
135                     status,
136                     "Value of registry key HKLM\\%s is invalid. Please set it "
137                     "to point to the location of moonshot.exe",
138                     MOONSHOT_INSTALL_PATH_KEY);
139         return;
140     }
141
142
143     if (status != 0) {
144         *error = moonshot_error_new_with_status
145                    (MOONSHOT_ERROR_OS_ERROR,
146                     status,
147                     "Unable to read value of registry key HKLM\\%s",
148                     MOONSHOT_INSTALL_PATH_KEY);
149         return;
150     }
151
152     startup_info.cb = sizeof (startup_info);
153
154     success = CreateProcess (exe_path,
155                              NULL,
156                              NULL,
157                              NULL,
158                              TRUE,
159                              DETACHED_PROCESS,
160                              NULL,
161                              NULL,
162                              &startup_info, &process_info);
163
164     if (! success) {
165         *error = moonshot_error_new_with_status
166                    (MOONSHOT_ERROR_UNABLE_TO_START_SERVICE,
167                     GetLastError (),
168                     "Unable to spawn the moonshot server at '%s'",
169                     exe_path);
170         return;
171     }
172 }
173
174 static void bind_rpc (MoonshotError **error)
175 {
176     DWORD status;
177     int   i;
178
179     status = rpc_client_bind (&moonshot_binding_handle,
180                               MOONSHOT_ENDPOINT_NAME,
181                               RPC_PER_USER);
182
183     if (status != RPC_S_OK) {
184         *error = moonshot_error_new_from_status (MOONSHOT_ERROR_IPC_ERROR,
185                                                  status);
186         return;
187     }
188
189     status = RpcMgmtIsServerListening (moonshot_binding_handle);
190
191     if (status == RPC_S_NOT_LISTENING) {
192         launch_server (error);
193
194         if (*error != NULL)
195             return;
196
197         /* Allow 1 minute for the server to launch before we time out */
198         for (i=0; i<600; i++) {
199             Sleep (100); /* ms */
200
201             status = RpcMgmtIsServerListening (moonshot_binding_handle);
202
203             if (status == RPC_S_OK)
204                 return;
205
206             if (status != RPC_S_NOT_LISTENING)
207                 break;
208         }
209     }
210
211     if (status != RPC_S_OK)
212         *error = moonshot_error_new_from_status (MOONSHOT_ERROR_IPC_ERROR,
213                                                  status);
214 }
215
216 static void init_rpc (MoonshotError **error)
217 {
218     static volatile LONG binding_init_flag = 2;
219     int status;
220
221     /* Hack to avoid requiring a moonshot_init() function. Windows does not
222      * provide any synchronisation primitives that can be statically init'ed,
223      * but we can use its atomic variable access functions to achieve the same.
224      * See: http://msdn.microsoft.com/en-us/library/ms684122%28v=vs.85%29.aspx
225      */
226
227     if (binding_init_flag == 0)
228         return;
229
230     if (InterlockedCompareExchange (&binding_init_flag, 1, 2) == 2) {
231         bind_rpc (error);
232
233         /* We'll handle all exceptions locally to avoid interfering with any
234          * other RPC/other exception handling that goes on in the process,
235          * and so we can store the problem in a MooshotError instead of
236          * aborting.
237          */
238         rpc_set_global_exception_handler_enable (FALSE);
239
240         if (InterlockedCompareExchange (&binding_init_flag, 0, 1) != 1) {
241             /* This should never happen */
242             fprintf (stderr, "moonshot: Internal synchronisation error");
243         }
244     } else {
245         while (binding_init_flag != 0)
246             Sleep (100); /* ms */
247     }
248 }
249
250
251 int moonshot_get_identity (const char     *nai,
252                            const char     *password,
253                            const char     *service,
254                            char          **nai_out,
255                            char          **password_out,
256                            char          **server_certificate_hash_out,
257                            char          **ca_certificate_out,
258                            char          **subject_name_constraint_out,
259                            char          **subject_alt_name_constraint_out,
260                            MoonshotError **error)
261 {
262     int success;
263     RpcAsyncCall call;
264
265     init_rpc (error);
266
267     if (*error != NULL)
268         return FALSE;
269
270     rpc_async_call_init (&call);
271
272     *nai_out = NULL;
273     *password_out = NULL;
274     *server_certificate_hash_out = NULL;
275     *ca_certificate_out = NULL;
276     *subject_name_constraint_out = NULL;
277     *subject_alt_name_constraint_out = NULL;
278
279     RPC_TRY_EXCEPT {
280         moonshot_get_identity_rpc (&call,
281                                    nai,
282                                    password,
283                                    service,
284                                    nai_out,
285                                    password_out,
286                                    server_certificate_hash_out,
287                                    ca_certificate_out,
288                                    subject_name_constraint_out,
289                                    subject_alt_name_constraint_out);
290
291         success = rpc_async_call_complete_int (&call);
292     }
293     RPC_EXCEPT {
294         *error = moonshot_error_new_from_status (MOONSHOT_ERROR_IPC_ERROR,
295                                                  RPC_GET_EXCEPTION_CODE ());
296     }
297     RPC_END_EXCEPT
298
299     if (*error != NULL)
300         return FALSE;
301
302     if (success == FALSE) {
303         *error = moonshot_error_new (MOONSHOT_ERROR_NO_IDENTITY_SELECTED,
304                                      "No identity was returned by the Moonshot "
305                                      "user interface.");
306         return FALSE;
307     }
308
309     return TRUE;
310 }
311
312
313 int moonshot_get_default_identity (char          **nai_out,
314                                    char          **password_out,
315                                    char          **server_certificate_hash_out,
316                                    char          **ca_certificate_out,
317                                    char          **subject_name_constraint_out,
318                                    char          **subject_alt_name_constraint_out,
319                                    MoonshotError **error)
320 {
321     int success;
322     RpcAsyncCall call;
323
324     init_rpc (error);
325
326     if (*error != NULL)
327         return FALSE;
328
329     rpc_async_call_init (&call);
330
331     *nai_out = NULL;
332     *password_out = NULL;
333     *server_certificate_hash_out = NULL;
334     *ca_certificate_out = NULL;
335     *subject_name_constraint_out = NULL;
336     *subject_alt_name_constraint_out = NULL;
337
338     RPC_TRY_EXCEPT {
339         moonshot_get_default_identity_rpc (&call,
340                                            nai_out,
341                                            password_out,
342                                            server_certificate_hash_out,
343                                            ca_certificate_out,
344                                            subject_name_constraint_out,
345                                            subject_alt_name_constraint_out);
346
347         success = rpc_async_call_complete_int (&call);
348     }
349     RPC_EXCEPT {
350         *error = moonshot_error_new_from_status (MOONSHOT_ERROR_IPC_ERROR,
351                                                  RPC_GET_EXCEPTION_CODE ());
352     }
353     RPC_END_EXCEPT
354
355     if (*error != NULL)
356         return FALSE;
357
358     if (success == FALSE) {
359         *error = moonshot_error_new (MOONSHOT_ERROR_NO_IDENTITY_SELECTED,
360                                      "No identity was returned by the Moonshot "
361                                      "user interface.");
362         return FALSE;
363     }
364
365     return TRUE;
366 };