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