msrpc fixes for libmoonshot abi change
[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 void *__RPC_USER MIDL_user_allocate (size_t size) {
51     return malloc (size);
52 }
53
54 void __RPC_USER MIDL_user_free (void *data) {
55     if (data == NULL)
56         return;
57
58     free (data);
59 }
60
61 void moonshot_free (void *data)
62 {
63     free (data);
64 }
65
66 static MoonshotError *moonshot_error_new_from_status (MoonshotErrorCode code,
67                                                       DWORD             status)
68 {
69     MoonshotError *error = malloc (sizeof (MoonshotError));
70     error->code = code;
71     error->message = malloc (256);
72
73     FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, (LPSTR)error->message, 255, NULL);
74     return error;
75 }
76
77 static MoonshotError *moonshot_error_new_with_status (MoonshotErrorCode code,
78                                                       DWORD             status,
79                                                       const char        *format, ...)
80 {
81     MoonshotError *error = malloc (sizeof (MoonshotError));
82     char *buffer;
83     va_list args;
84     int length;
85
86     va_start (args, format);
87
88     error->code = code;
89
90     buffer = malloc (strlen (format) + 256 + 3);
91     strcpy (buffer, format);
92     strcat (buffer, ": ");
93
94     FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
95                    NULL,
96                    status,
97                    0,
98                    (LPSTR)buffer + strlen (format) + 3,
99                    255,
100                    NULL);
101
102     length = _vscprintf (buffer, args);
103     error->message = malloc (length + 1);
104     _vsnprintf (error->message, length, buffer, args);
105     free (buffer);
106
107     va_end (args);
108
109     return error;
110 }
111
112 static void launch_server (MoonshotError **error) {
113     HKEY key = NULL;
114     STARTUPINFO startup_info = { 0 };
115     PROCESS_INFORMATION process_info = { 0 };
116     LONG status;
117     BOOL success;
118     DWORD value_type;
119     DWORD length;
120     char exe_path[1024];
121
122     status = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
123                            MOONSHOT_INSTALL_PATH_KEY,
124                            0,
125                            KEY_READ,
126                            &key);
127
128     if (status == ERROR_FILE_NOT_FOUND) {
129         *error = moonshot_error_new
130                    (MOONSHOT_ERROR_INSTALLATION_ERROR,
131                     "Moonshot is not installed correctly on this system. "
132                     "(Registry key HKLM\\%s was not found).",
133                     MOONSHOT_INSTALL_PATH_KEY);
134         return;
135     } else if (status != 0) {
136         *error = moonshot_error_new_with_status
137                    (MOONSHOT_ERROR_OS_ERROR,
138                     status,
139                     "Unable to read registry key HKLM\\%s",
140                     MOONSHOT_INSTALL_PATH_KEY);
141         return;
142     }
143
144     length = 1023;
145     status = RegQueryValueEx (key, NULL, NULL, &value_type, exe_path, &length);
146
147     if (value_type != REG_SZ) {
148         *error = moonshot_error_new_with_status
149                    (MOONSHOT_ERROR_INSTALLATION_ERROR,
150                     status,
151                     "Value of registry key HKLM\\%s is invalid. Please set it "
152                     "to point to the location of moonshot.exe",
153                     MOONSHOT_INSTALL_PATH_KEY);
154         return;
155     }
156
157
158     if (status != 0) {
159         *error = moonshot_error_new_with_status
160                    (MOONSHOT_ERROR_OS_ERROR,
161                     status,
162                     "Unable to read value of registry key HKLM\\%s",
163                     MOONSHOT_INSTALL_PATH_KEY);
164         return;
165     }
166
167     startup_info.cb = sizeof (startup_info);
168
169     success = CreateProcess (exe_path,
170                              NULL,
171                              NULL,
172                              NULL,
173                              TRUE,
174                              DETACHED_PROCESS,
175                              NULL,
176                              NULL,
177                              &startup_info, &process_info);
178
179     if (! success) {
180         *error = moonshot_error_new_with_status
181                    (MOONSHOT_ERROR_UNABLE_TO_START_SERVICE,
182                     GetLastError (),
183                     "Unable to spawn the moonshot server at '%s'",
184                     exe_path);
185         return;
186     }
187 }
188
189 static void bind_rpc (MoonshotError **error)
190 {
191     DWORD status;
192     int   i;
193
194     status = rpc_client_bind (&moonshot_binding_handle,
195                               MOONSHOT_ENDPOINT_NAME,
196                               RPC_PER_USER);
197
198     if (status != RPC_S_OK) {
199         *error = moonshot_error_new_from_status (MOONSHOT_ERROR_IPC_ERROR,
200                                                  status);
201         return;
202     }
203
204     status = RpcMgmtIsServerListening (moonshot_binding_handle);
205
206     if (status == RPC_S_NOT_LISTENING) {
207         launch_server (error);
208
209         if (*error != NULL)
210             return;
211
212         /* Allow 1 minute for the server to launch before we time out */
213         for (i=0; i<600; i++) {
214             Sleep (100); /* ms */
215
216             status = RpcMgmtIsServerListening (moonshot_binding_handle);
217
218             if (status == RPC_S_OK)
219                 return;
220
221             if (status != RPC_S_NOT_LISTENING)
222                 break;
223         }
224     }
225
226     if (status != RPC_S_OK)
227         *error = moonshot_error_new_from_status (MOONSHOT_ERROR_IPC_ERROR,
228                                                  status);
229 }
230
231 static void init_rpc (MoonshotError **error)
232 {
233     static volatile LONG binding_init_flag = 2;
234     int status;
235
236     /* Hack to avoid requiring a moonshot_init() function. Windows does not
237      * provide any synchronisation primitives that can be statically init'ed,
238      * but we can use its atomic variable access functions to achieve the same.
239      * See: http://msdn.microsoft.com/en-us/library/ms684122%28v=vs.85%29.aspx
240      */
241
242     if (binding_init_flag == 0)
243         return;
244
245     if (InterlockedCompareExchange (&binding_init_flag, 1, 2) == 2) {
246         bind_rpc (error);
247
248         /* We'll handle all exceptions locally to avoid interfering with any
249          * other RPC/other exception handling that goes on in the process,
250          * and so we can store the problem in a MooshotError instead of
251          * aborting.
252          */
253         rpc_set_global_exception_handler_enable (FALSE);
254
255         if (InterlockedCompareExchange (&binding_init_flag, 0, 1) != 1) {
256             /* This should never happen */
257             fprintf (stderr, "moonshot: Internal synchronisation error");
258         }
259     } else {
260         while (binding_init_flag != 0)
261             Sleep (100); /* ms */
262     }
263 }
264
265
266 int moonshot_get_identity (const char     *nai,
267                            const char     *password,
268                            const char     *service,
269                            char          **nai_out,
270                            char          **password_out,
271                            char          **server_certificate_hash_out,
272                            char          **ca_certificate_out,
273                            char          **subject_name_constraint_out,
274                            char          **subject_alt_name_constraint_out,
275                            MoonshotError **error)
276 {
277     int success;
278     RpcAsyncCall call;
279
280     init_rpc (error);
281
282     if (*error != NULL)
283         return FALSE;
284
285     rpc_async_call_init (&call);
286
287     if (nai == NULL) nai = "";
288     if (password == NULL) password = "";
289     if (service == NULL) service = "";
290
291     *nai_out = NULL;
292     *password_out = NULL;
293     *server_certificate_hash_out = NULL;
294     *ca_certificate_out = NULL;
295     *subject_name_constraint_out = NULL;
296     *subject_alt_name_constraint_out = NULL;
297
298     RPC_TRY_EXCEPT {
299         moonshot_get_identity_rpc (&call,
300                                    nai,
301                                    password,
302                                    service,
303                                    nai_out,
304                                    password_out,
305                                    server_certificate_hash_out,
306                                    ca_certificate_out,
307                                    subject_name_constraint_out,
308                                    subject_alt_name_constraint_out);
309
310         success = rpc_async_call_complete_int (&call);
311     }
312     RPC_EXCEPT {
313         *error = moonshot_error_new_from_status (MOONSHOT_ERROR_IPC_ERROR,
314                                                  RPC_GET_EXCEPTION_CODE ());
315     }
316     RPC_END_EXCEPT
317
318     if (*error != NULL)
319         return FALSE;
320
321     if (success == FALSE) {
322         *error = moonshot_error_new (MOONSHOT_ERROR_NO_IDENTITY_SELECTED,
323                                      "No identity was returned by the Moonshot "
324                                      "user interface.");
325         return FALSE;
326     }
327
328     return TRUE;
329 }
330
331
332 int moonshot_get_default_identity (char          **nai_out,
333                                    char          **password_out,
334                                    char          **server_certificate_hash_out,
335                                    char          **ca_certificate_out,
336                                    char          **subject_name_constraint_out,
337                                    char          **subject_alt_name_constraint_out,
338                                    MoonshotError **error)
339 {
340     int success;
341     RpcAsyncCall call;
342
343     init_rpc (error);
344
345     if (*error != NULL)
346         return FALSE;
347
348     rpc_async_call_init (&call);
349
350     *nai_out = NULL;
351     *password_out = NULL;
352     *server_certificate_hash_out = NULL;
353     *ca_certificate_out = NULL;
354     *subject_name_constraint_out = NULL;
355     *subject_alt_name_constraint_out = NULL;
356
357     RPC_TRY_EXCEPT {
358         moonshot_get_default_identity_rpc (&call,
359                                            nai_out,
360                                            password_out,
361                                            server_certificate_hash_out,
362                                            ca_certificate_out,
363                                            subject_name_constraint_out,
364                                            subject_alt_name_constraint_out);
365
366         success = rpc_async_call_complete_int (&call);
367     }
368     RPC_EXCEPT {
369         *error = moonshot_error_new_from_status (MOONSHOT_ERROR_IPC_ERROR,
370                                                  RPC_GET_EXCEPTION_CODE ());
371     }
372     RPC_END_EXCEPT
373
374     if (*error != NULL)
375         return FALSE;
376
377     if (success == FALSE) {
378         *error = moonshot_error_new (MOONSHOT_ERROR_NO_IDENTITY_SELECTED,
379                                      "No identity was returned by the Moonshot "
380                                      "user interface.");
381         return FALSE;
382     }
383
384     return TRUE;
385 };
386
387 int moonshot_install_id_card (const char     *display_name,
388                               const char     *user_name,
389                               const char     *password,
390                               const char     *realm,
391                               char           *rules_patterns[],
392                               int             rules_patterns_length,
393                               char           *rules_always_confirm[],
394                               int             rules_always_confirm_length,
395                               char           *services[],
396                               int             services_length,
397                               const char     *ca_cert,
398                               const char     *subject,
399                               const char     *subject_alt,
400                               const char     *server_cert,
401                               int             force_flat_file_store,
402                               MoonshotError **error)
403 {
404     int success = FALSE;
405
406     init_rpc (error);
407
408     if (user_name == NULL) user_name = "";
409     if (password == NULL) password = "";
410     if (realm == NULL) realm = "";
411     if (ca_cert == NULL) ca_cert = "";
412     if (subject == NULL) subject = "";
413     if (subject_alt == NULL) subject_alt = "";
414     if (server_cert == NULL) server_cert = "";
415
416     RPC_TRY_EXCEPT {
417         success = moonshot_install_id_card_rpc (display_name,
418                                                 user_name,
419                                                 password,
420                                                 realm,
421                                                 rules_patterns,
422                                                 rules_patterns_length,
423                                                 rules_always_confirm,
424                                                 rules_always_confirm_length,
425                                                 services,
426                                                 services_length,
427                                                 ca_cert,
428                                                 subject,
429                                                 subject_alt,
430                                                 server_cert,
431                                                 force_flat_file_store);
432     }
433     RPC_EXCEPT {
434         *error = moonshot_error_new_from_status (MOONSHOT_ERROR_IPC_ERROR,
435                                                  RPC_GET_EXCEPTION_CODE ());
436     }
437     RPC_END_EXCEPT
438
439     return success;
440 }
441
442 BOOL WINAPI DllMain (HINSTANCE  hinst,
443                      DWORD      reason,
444                      void      *reserved)
445 {
446     if (reason == DLL_PROCESS_DETACH) {
447         /* Process exiting/DLL being unloaded. This is a good
448          * opportunity to free the RPC binding.
449          *
450          * FIXME: we can't use the msrpc-mingw routine for this in case
451          * it was already unloaded. I'd love to work out how to link
452          * that library statically into libmoonshot-0.dll.
453          */
454         RpcBindingFree (&moonshot_binding_handle);
455     }
456
457     return TRUE;
458 }