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