[docs] Enable documentation for metadata.
[mono.git] / mono / metadata / attach.c
1 /**
2  * \file
3  * Support for attaching to the runtime from other processes.
4  *
5  * Author:
6  *   Zoltan Varga (vargaz@gmail.com)
7  *
8  * Copyright 2007-2009 Novell, Inc (http://www.novell.com)
9  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
10  */
11
12 #include <config.h>
13 #include <glib.h>
14
15 #ifdef HOST_WIN32
16 #define DISABLE_ATTACH
17 #endif
18 #ifndef DISABLE_ATTACH
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <sys/stat.h>
26 #include <sys/un.h>
27 #include <netinet/in.h>
28 #include <fcntl.h>
29 #include <inttypes.h>
30 #include <pwd.h>
31 #include <errno.h>
32 #include <netdb.h>
33 #include <unistd.h>
34
35 #include <mono/metadata/assembly-internals.h>
36 #include <mono/metadata/metadata.h>
37 #include <mono/metadata/class-internals.h>
38 #include <mono/metadata/object-internals.h>
39 #include <mono/metadata/threads-types.h>
40 #include <mono/metadata/gc-internals.h>
41 #include <mono/utils/mono-threads.h>
42 #include "attach.h"
43
44 #include <mono/utils/w32api.h>
45
46 /*
47  * This module enables other processes to attach to a running mono process and
48  * load agent assemblies. 
49  * Communication is done through a UNIX Domain Socket located at
50  * /tmp/mono-<USER>/.mono-<PID>.
51  * We use a simplified version of the .net remoting protocol.
52  * To increase security, and to avoid spinning up a listener thread on startup,
53  * we follow the java implementation, and only start up the attach mechanism
54  * when we receive a QUIT signal and there is a file named 
55  * '.mono_attach_pid<PID>' in /tmp.
56  *
57  * SECURITY:
58  * - This module allows loading of arbitrary code into a running mono runtime, so
59  *   it is security critical.
60  * - Security is based on controlling access to the unix file to which the unix 
61  *   domain socket is bound. Permissions/ownership are set such that only the owner 
62  *   of the process can access the socket.
63  * - As an additional measure, the socket is only created when the process receives
64  *   a SIGQUIT signal, which only its owner/root can send.
65  * - The socket is kept in a directory whose ownership is checked before creating
66  *   the socket. This could allow an attacker a kind of DOS attack by creating the 
67  *   directory with the wrong permissions/ownership. However, the only thing such
68  *   an attacker could prevent is the attaching of agents to the mono runtime.
69  */
70
71 typedef struct {
72         gboolean enabled;
73 } AgentConfig;
74
75 typedef struct {
76         int bytes_sent;
77 } AgentStats;
78
79 /*******************************************************************/
80 /* Remoting Protocol type definitions from [MS-NRBF] and [MS-NRTP] */
81 /*******************************************************************/
82
83 typedef enum {
84         PRIM_TYPE_INT32 = 8,
85         PRIM_TYPE_INT64 = 9,
86         PRIM_TYPE_NULL = 17,
87         PRIM_TYPE_STRING = 18
88 } PrimitiveType;
89
90 static AgentConfig config;
91
92 static int listen_fd, conn_fd;
93
94 static char *ipc_filename;
95
96 static char *server_uri;
97
98 static MonoThreadHandle *receiver_thread_handle;
99
100 static gboolean stop_receiver_thread;
101
102 static gboolean needs_to_start, started;
103
104 static void transport_connect (void);
105
106 static gsize WINAPI receiver_thread (void *arg);
107
108 static void transport_start_receive (void);
109
110 /*
111  * Functions to decode protocol data
112  */
113 static inline int
114 decode_byte (guint8 *buf, guint8 **endbuf, guint8 *limit)
115 {
116         *endbuf = buf + 1;
117         g_assert (*endbuf <= limit);
118         return buf [0];
119 }
120
121 static inline int
122 decode_int (guint8 *buf, guint8 **endbuf, guint8 *limit)
123 {
124         *endbuf = buf + 4;
125         g_assert (*endbuf <= limit);
126
127         return (((int)buf [0]) << 0) | (((int)buf [1]) << 8) | (((int)buf [2]) << 16) | (((int)buf [3]) << 24);
128 }
129
130 static char*
131 decode_string_value (guint8 *buf, guint8 **endbuf, guint8 *limit)
132 {
133         int type;
134     gint32 length;
135         guint8 *p = buf;
136         char *s;
137
138         type = decode_byte (p, &p, limit);
139         if (type == PRIM_TYPE_NULL) {
140                 *endbuf = p;
141                 return NULL;
142         }
143         g_assert (type == PRIM_TYPE_STRING);
144
145         length = 0;
146         while (TRUE) {
147                 guint8 b = decode_byte (p, &p, limit);
148                 
149                 length <<= 8;
150                 length += b;
151                 if (b <= 0x7f)
152                         break;
153         }
154
155         g_assert (length < (1 << 16));
156
157         s = (char *)g_malloc (length + 1);
158
159         g_assert (p + length <= limit);
160         memcpy (s, p, length);
161         s [length] = '\0';
162         p += length;
163
164         *endbuf = p;
165
166         return s;
167 }
168
169 /********************************/
170 /*    AGENT IMPLEMENTATION      */
171 /********************************/
172
173 void
174 mono_attach_parse_options (char *options)
175 {
176         if (!options)
177                 return;
178         if (!strcmp (options, "disable"))
179                 config.enabled = FALSE;
180 }
181
182 void
183 mono_attach_init (void)
184 {
185         config.enabled = TRUE;
186 }
187
188 /**
189  * mono_attach_start:
190  *
191  * Start the attach mechanism if needed.  This is called from a signal handler so it must be signal safe.
192  *
193  * Returns: whenever it was started.
194  */
195 gboolean
196 mono_attach_start (void)
197 {
198         char path [256];
199         int fd;
200
201         if (started)
202                 return FALSE;
203
204         /* Check for the existence of the trigger file */
205
206         /* 
207          * We don't do anything with this file, and the only thing an attacker can do
208          * by creating it is to enable the attach mechanism if the process receives a 
209          * SIGQUIT signal, which can only be sent by the owner/root.
210          */
211         snprintf (path, sizeof (path), "/tmp/.mono_attach_pid%"PRIdMAX"", (intmax_t) getpid ());
212         fd = open (path, O_RDONLY);
213         if (fd == -1)
214                 return FALSE;
215         close (fd);
216
217         if (!config.enabled)
218                 /* Act like we started */
219                 return TRUE;
220
221         if (started)
222                 return FALSE;
223
224         /*
225          * Our startup includes non signal-safe code, so ask the finalizer thread to 
226          * do the actual startup.
227          */
228         needs_to_start = TRUE;
229         mono_gc_finalize_notify ();
230
231         return TRUE;
232 }
233
234 /* Called by the finalizer thread when it is woken up */
235 void
236 mono_attach_maybe_start (void)
237 {
238         if (!needs_to_start)
239                 return;
240
241         needs_to_start = FALSE;
242         if (!started) {
243                 transport_start_receive ();
244
245                 started = TRUE;
246         }
247 }
248
249 void
250 mono_attach_cleanup (void)
251 {
252         if (listen_fd)
253                 close (listen_fd);
254         if (ipc_filename)
255                 unlink (ipc_filename);
256
257         stop_receiver_thread = TRUE;
258         if (conn_fd)
259                 /* This will cause receiver_thread () to break out of the read () call */
260                 close (conn_fd);
261
262         /* Wait for the receiver thread to exit */
263         if (receiver_thread_handle)
264                 mono_thread_info_wait_one_handle (receiver_thread_handle, 0, FALSE);
265 }
266
267 static int
268 mono_attach_load_agent (MonoDomain *domain, char *agent, char *args, MonoObject **exc)
269 {
270         MonoError error;
271         MonoAssembly *agent_assembly;
272         MonoImage *image;
273         MonoMethod *method;
274         guint32 entry;
275         MonoArray *main_args;
276         gpointer pa [1];
277         MonoImageOpenStatus open_status;
278
279         agent_assembly = mono_assembly_open_predicate (agent, FALSE, FALSE, NULL, NULL, &open_status);
280         if (!agent_assembly) {
281                 fprintf (stderr, "Cannot open agent assembly '%s': %s.\n", agent, mono_image_strerror (open_status));
282                 g_free (agent);
283                 return 2;
284         }
285
286         /* 
287          * Can't use mono_jit_exec (), as it sets things which might confuse the
288          * real Main method.
289          */
290         image = mono_assembly_get_image (agent_assembly);
291         entry = mono_image_get_entry_point (image);
292         if (!entry) {
293                 g_print ("Assembly '%s' doesn't have an entry point.\n", mono_image_get_filename (image));
294                 g_free (agent);
295                 return 1;
296         }
297
298         method = mono_get_method_checked (image, entry, NULL, NULL, &error);
299         if (method == NULL){
300                 g_print ("The entry point method of assembly '%s' could not be loaded due to %s\n", agent, mono_error_get_message (&error));
301                 mono_error_cleanup (&error);
302                 g_free (agent);
303                 return 1;
304         }
305         
306         
307         main_args = (MonoArray*)mono_array_new_checked (domain, mono_defaults.string_class, (args == NULL) ? 0 : 1, &error);
308         if (main_args == NULL) {
309                 g_print ("Could not allocate main method args due to %s\n", mono_error_get_message (&error));
310                 mono_error_cleanup (&error);
311                 g_free (agent);
312                 return 1;
313         }
314
315         if (args) {
316                 mono_array_set (main_args, MonoString*, 0, mono_string_new (domain, args));
317         }
318
319
320         pa [0] = main_args;
321         mono_runtime_try_invoke (method, NULL, pa, exc, &error);
322         if (!is_ok (&error)) {
323                 g_print ("The entry point method of assembly '%s' could not be executed due to %s\n", agent, mono_error_get_message (&error));
324                 mono_error_cleanup (&error);
325                 g_free (agent);
326                 return 1;
327         }
328
329         g_free (agent);
330
331         return 0;
332 }
333
334 /*
335  * ipc_connect:
336  *
337  *   Create a UNIX domain socket and bind it to a file in /tmp.
338  *
339  * SECURITY: This routine is _very_ security critical since we depend on the UNIX
340  * permissions system to prevent attackers from connecting to the socket.
341  */
342 static void
343 ipc_connect (void)
344 {
345         struct sockaddr_un name;
346         int sock, res;
347         size_t size;
348         char *filename, *directory;
349         struct stat stat;
350         struct passwd pwbuf;
351         char buf [1024];
352         struct passwd *pw;
353
354         if (getuid () != geteuid ()) {
355                 fprintf (stderr, "attach: disabled listening on an IPC socket when running in setuid mode.\n");
356                 return;
357         }
358
359         /* Create the socket.   */  
360         sock = socket (PF_UNIX, SOCK_STREAM, 0);
361         if (sock < 0) {
362                 perror ("attach: failed to create IPC socket");
363                 return;
364         }
365
366         /* 
367          * For security reasons, create a directory to hold the listening socket,
368          * since there is a race between bind () and chmod () below.
369          */
370         /* FIXME: Use TMP ? */
371         pw = NULL;
372 #ifdef HAVE_GETPWUID_R
373         res = getpwuid_r (getuid (), &pwbuf, buf, sizeof (buf), &pw);
374 #else
375         pw = getpwuid(getuid ());
376         res = pw != NULL ? 0 : 1;
377 #endif
378         if (res != 0) {
379                 fprintf (stderr, "attach: getpwuid_r () failed.\n");
380                 return;
381         }
382         g_assert (pw);
383         directory = g_strdup_printf ("/tmp/mono-%s", pw->pw_name);
384         res = mkdir (directory, S_IRUSR | S_IWUSR | S_IXUSR);
385         if (res != 0) {
386                 if (errno == EEXIST) {
387                         /* Check type and permissions */
388                         res = lstat (directory, &stat);
389                         if (res != 0) {
390                                 perror ("attach: lstat () failed");
391                                 return;
392                         }
393                         if (!S_ISDIR (stat.st_mode)) {
394                                 fprintf (stderr, "attach: path '%s' is not a directory.\n", directory);
395                                 return;
396                         }
397                         if (stat.st_uid != getuid ()) {
398                                 fprintf (stderr, "attach: directory '%s' is not owned by the current user.\n", directory);
399                                 return;
400                         }
401                         if ((stat.st_mode & S_IRWXG) != 0 || (stat.st_mode & S_IRWXO) || ((stat.st_mode & S_IRWXU) != (S_IRUSR | S_IWUSR | S_IXUSR))) {
402                                 fprintf (stderr, "attach: directory '%s' should have protection 0700.\n", directory);
403                                 return;
404                         }
405                 } else {
406                         perror ("attach: mkdir () failed");
407                         return;
408                 }
409         }
410
411         filename = g_strdup_printf ("%s/.mono-%"PRIdMAX"", directory, (intmax_t) getpid ());
412         unlink (filename);
413
414         /* Bind a name to the socket.   */
415         name.sun_family = AF_UNIX;
416         strcpy (name.sun_path, filename);
417
418         size = (offsetof (struct sockaddr_un, sun_path)
419                         + strlen (name.sun_path) + 1);
420
421         if (bind (sock, (struct sockaddr *) &name, size) < 0) {
422                 fprintf (stderr, "attach: failed to bind IPC socket '%s': %s\n", filename, strerror (errno));
423                 close (sock);
424                 return;
425         }
426
427         /* Set permissions */
428         res = chmod (filename, S_IRUSR | S_IWUSR);
429         if (res != 0) {
430                 perror ("attach: failed to set permissions on IPC socket");
431                 close (sock);
432                 unlink (filename);
433                 return;
434         }
435
436         res = listen (sock, 16);
437         if (res != 0) {
438                 fprintf (stderr, "attach: listen () failed: %s\n", strerror (errno));
439                 exit (1);
440         }
441
442         listen_fd = sock;
443
444         ipc_filename = g_strdup (filename);
445
446         server_uri = g_strdup_printf ("unix://%s/.mono-%"PRIdMAX"?/vm", directory, (intmax_t) getpid ());
447
448         g_free (filename);
449         g_free (directory);
450 }
451
452 static void
453 transport_connect (void)
454 {
455         ipc_connect ();
456 }
457
458 #if 0
459
460 static void
461 transport_send (int fd, guint8 *data, int len)
462 {
463         int res;
464
465         stats.bytes_sent += len;
466         //printf ("X: %d\n", stats.bytes_sent);
467
468         res = write (fd, data, len);
469         if (res != len) {
470                 /* FIXME: What to do here ? */
471         }
472 }
473
474 #endif
475
476 static void
477 transport_start_receive (void)
478 {
479         MonoError error;
480         MonoInternalThread *internal;
481
482         transport_connect ();
483
484         if (!listen_fd)
485                 return;
486
487         internal = mono_thread_create_internal (mono_get_root_domain (), receiver_thread, NULL, MONO_THREAD_CREATE_FLAGS_NONE, &error);
488         mono_error_assert_ok (&error);
489
490         receiver_thread_handle = mono_threads_open_thread_handle (internal->handle);
491         g_assert (receiver_thread_handle);
492 }
493
494 static gsize WINAPI
495 receiver_thread (void *arg)
496 {
497         MonoError error;
498         int res, content_len;
499         guint8 buffer [256];
500         guint8 *p, *p_end;
501         MonoObject *exc;
502         MonoInternalThread *internal;
503
504         internal = mono_thread_internal_current ();
505         mono_thread_set_name_internal (internal, mono_string_new (mono_domain_get (), "Attach receiver"), TRUE, FALSE, &error);
506         mono_error_assert_ok (&error);
507         /* Ask the runtime to not abort this thread */
508         //internal->flags |= MONO_THREAD_FLAG_DONT_MANAGE;
509         /* Ask the runtime to not wait for this thread */
510         internal->state |= ThreadState_Background;
511
512         printf ("attach: Listening on '%s'...\n", server_uri);
513
514         while (TRUE) {
515                 conn_fd = accept (listen_fd, NULL, NULL);
516                 if (conn_fd == -1)
517                         /* Probably closed by mono_attach_cleanup () */
518                         return 0;
519
520                 printf ("attach: Connected.\n");
521
522                 while (TRUE) {
523                         char *cmd, *agent_name, *agent_args;
524                         guint8 *body;
525
526                         /* Read Header */
527                         res = read (conn_fd, buffer, 6);
528
529                         if (res == -1 && errno == EINTR)
530                                 continue;
531
532                         if (res == -1 || stop_receiver_thread)
533                                 break;
534
535                         if (res != 6)
536                                 break;
537
538                         if ((strncmp ((char*)buffer, "MONO", 4) != 0) || buffer [4] != 1 || buffer [5] != 0) {
539                                 fprintf (stderr, "attach: message from server has unknown header.\n");
540                                 break;
541                         }
542
543                         /* Read content length */
544                         res = read (conn_fd, buffer, 4);
545                         if (res != 4)
546                                 break;
547
548                         p = buffer;
549                         p_end = p + 8;
550
551                         content_len = decode_int (p, &p, p_end);
552
553                         /* Read message body */
554                         body = (guint8 *)g_malloc (content_len);
555                         res = read (conn_fd, body, content_len);
556                         
557                         p = body;
558                         p_end = body + content_len;
559
560                         cmd = decode_string_value (p, &p, p_end);
561                         if (cmd == NULL)
562                                 break;
563                         g_assert (!strcmp (cmd, "attach"));
564
565                         agent_name = decode_string_value (p, &p, p_end);
566                         agent_args = decode_string_value (p, &p, p_end);
567
568                         printf ("attach: Loading agent '%s'.\n", agent_name);
569                         mono_attach_load_agent (mono_domain_get (), agent_name, agent_args, &exc);
570
571                         g_free (body);
572
573                         // FIXME: Send back a result
574                 }
575
576                 close (conn_fd);
577                 conn_fd = 0;
578
579                 printf ("attach: Disconnected.\n");
580
581                 if (stop_receiver_thread)
582                         break;
583         }
584
585         return 0;
586 }
587
588 #else /* DISABLE_ATTACH */
589
590 void
591 mono_attach_parse_options (char *options)
592 {
593 }
594
595 void
596 mono_attach_init (void)
597 {
598 }
599
600 gboolean
601 mono_attach_start (void)
602 {
603         return FALSE;
604 }
605
606 void
607 mono_attach_maybe_start (void)
608 {
609 }
610
611 void
612 mono_attach_cleanup (void)
613 {
614 }
615
616 #endif /* DISABLE_ATTACH */