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