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