source: trunk/base/src/pextlib1.0/tracelib.c @ 29781

Last change on this file since 29781 was 29781, checked in by epimenov@…, 12 years ago

tracelib: /Applications/MacPorts should be in sandbox

  • Property svn:keywords set to Id
File size: 11.6 KB
Line 
1/*
2 * tracelib.c
3 * $Id: tracelib.c 29781 2007-10-09 23:50:26Z epimenov@macports.org $
4 *
5 * Copyright (c) 2007 Eugene Pimenov (GSoC), MacPorts team
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of Darwinports Team nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <config.h>
34#include <string.h>
35#include <sys/time.h>
36#include <sys/resource.h>
37#include <unistd.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <sys/socket.h>
41#include <sys/types.h>
42#include <sys/select.h>
43#include <sys/un.h>
44#include <stdarg.h>
45#include <errno.h>
46#include <pthread.h>
47#include "tracelib.h"
48
49static char * name;
50static char * sandbox;
51static char * filemap, * filemap_end;
52static char * depends; 
53static int sock=-1;
54static int enable_fence=0;
55static Tcl_Interp * interp;
56static pthread_mutex_t sock_mutex=PTHREAD_MUTEX_INITIALIZER;
57static int cleanuping=0;
58static char * sdk=
59#ifdef TRACE_SDK
60        /*"MacOSX10.4u.sdk"*/
61        TRACE_SDK
62#else
63        0
64#endif
65;
66
67static void send_file_map(int sock);
68static void dep_check(int sock, const char * path);
69static void sandbox_violation(int sock, const char * path);
70static void ui_warn(const char * format, ...);
71static void ui_info(const char * format, ...);
72
73#define MAX_SOCKETS ((FD_SETSIZE)-1)
74
75static int TracelibSetNameCmd(Tcl_Interp * interp, int objc, Tcl_Obj *CONST objv[])
76{
77        if (objc != 3)
78        {
79                Tcl_WrongNumArgs(interp, 2, objv, "number of arguments should be exactly 3");
80                return TCL_ERROR;
81        }
82       
83        name=strdup(Tcl_GetString(objv[2]));
84       
85        return TCL_OK;
86}
87
88/*
89 * Save sandbox path into memory and prepare it for checks.
90 * For now it just change : to \0, and add last \0
91 * Input:
92 *  /dev/null:/dev/tty:/tmp
93 * In variable;
94 * /dev/null\0/dev/tty\0/tmp\0\0
95 */
96static int TracelibSetSandboxCmd(Tcl_Interp * interp, int objc, Tcl_Obj *CONST objv[])
97{
98        int len;
99        char * t;
100       
101        if (objc != 3)
102        {
103                Tcl_WrongNumArgs(interp, 2, objv, "number of arguments should be exactly 3");
104                return TCL_ERROR;
105        }
106       
107        len=strlen(Tcl_GetString(objv[2]))+2;
108        sandbox=(char*)malloc(len);
109        memset(sandbox, 0, len);
110        strcpy(sandbox, Tcl_GetString(objv[2]));
111        for(t=sandbox;(t=strchr(t+1, ':'));)
112        {
113                /* : -> \0 */
114                if(t[-1]!='\\')
115                        *t=0;
116                else
117                        /* \: -> : */
118                        /* TODO \\: -> \: */
119                        memmove(t-1, t, strlen(t));
120        }
121       
122        return TCL_OK;
123}
124
125/*
126 * receive line from socket, parse it and send answer
127 */
128static char process_line(int sock)
129{
130        char * t, buf[1024]={0}, *f;
131        int len;
132       
133        if((len=recv(sock, buf, sizeof(buf), 0))==-1)
134                return 0;
135        if(!len)
136                return 0;
137        buf[len]=0;
138        /* sometimes two messages come in one recv.. I ain't caring about it now, but it can be a problem */
139        for(t=buf;*t&&t-buf<(int)sizeof(buf);t=f+strlen(f)+1)
140        {
141                f=strchr(t, '\t');
142                if(!f)
143                {
144                        ui_warn("malformed command %s", t);
145                        break;
146                }
147                *f++=0;
148                if(!strcmp(t, "filemap"))
149                {
150                        send_file_map(sock);
151                }else if(!strcmp(t, "sandbox_violation"))
152                {
153                        sandbox_violation(sock, f);
154                }else if(!strcmp(t, "dep_check"))
155                {
156                        dep_check(sock, f);
157                }else if(!strcmp(t, "execve"))
158                {
159                        /* ====================== */
160                        /* = TODO: do something = */
161                        /* ====================== */
162                }else
163                {
164                        ui_warn("unknown command %s (%s)", t, f);
165                }
166        }
167        return 1;
168}
169
170static void send_file_map(int sock)
171{
172        if(!filemap)
173        {
174                char * t, * _;
175               
176                filemap=(char*)malloc(1024);
177                t=filemap;
178               
179                #define append_allow(path, resolution) do{strcpy(t, path); t+=strlen(t)+1; *t++=resolution; *t++=0;}while(0);
180                if(enable_fence)
181                {
182                        for(_=sandbox; *_; _+=strlen(_)+1)
183                                append_allow(_, 0);
184                       
185                        append_allow("/bin", 0);
186                        append_allow("/sbin", 0);
187                        append_allow("/dev", 0);
188                        append_allow(Tcl_GetVar(interp, "macports::prefix", TCL_GLOBAL_ONLY), 2);
189                        append_allow("/Applications/MacPorts", 0);
190                        /* If there is no SDK we will allow everything in /usr /System/Library etc, else add binaries to allow, and redirect root to SDK. */
191                        if(sdk&&*sdk)
192                        {
193                                char buf[260]="/Developer/SDKs/";
194                                strcat(buf, sdk);
195                       
196                                append_allow("/usr/bin", 0);
197                                append_allow("/usr/sbin", 0);
198                                append_allow("/usr/libexec/gcc", 0);
199                                append_allow("/System/Library/Perl", 0);
200                                append_allow("/usr/X11R6/bin", 0);
201                                append_allow("/", 1);
202                                strcpy(t-1, buf);
203                                t+=strlen(t)+1;
204                        }else
205                        {
206                                append_allow("/usr", 0);
207                                append_allow("/System/Library", 0);
208                                append_allow("/Library", 0);
209                                append_allow("/Developer/Headers", 0);
210                        }
211                }else
212                        append_allow("/", 0);
213                filemap_end=t;
214                #undef append_allow
215        }
216       
217        {
218                size_t s=filemap_end-filemap;
219                send(sock, &s, sizeof(s), 0);
220                send(sock, filemap, s, 0);
221        }
222}
223
224static void sandbox_violation(int sock UNUSED, const char * path)
225{
226        Tcl_SetVar(interp, "path", path, 0);
227        Tcl_Eval(interp, "slave_add_sandbox_violation $path");
228        Tcl_UnsetVar(interp, "path", 0);
229}
230
231static void dep_check(int sock, const char * path)
232{
233        char * port=0;
234        size_t len=1;
235        char resolution; 
236       
237        /* If there aren't deps then allow anything. (Useful for extract) */
238        if(!depends)
239                resolution='+';
240        else
241        {
242                resolution='!';
243               
244                Tcl_SetVar(interp, "path", path, 0);
245                Tcl_Eval(interp, "registry::file_registered $path");
246                port=strdup(Tcl_GetStringResult(interp));
247                Tcl_UnsetVar(interp, "path", 0);
248       
249                if(*port!='0'||port[1])
250                {
251                        char * t;
252               
253                        t=depends;
254                        for(;*t;t+=strlen(t)+1)
255                        {
256                                if(!strcmp(t, port))
257                                {
258                                        resolution='+';
259                                        break;
260                                }
261                        }
262                }else if(*port=='0'&&!port[1])
263                        strcpy(port, "*unknown*");
264        }
265       
266        if(resolution!='+')
267                ui_info("trace: access denied to %s (%s)", path, port);
268
269        if(port)
270                free(port);
271       
272        if(send(sock, &len, sizeof(len), 0)==-1)
273                ui_warn("tracelib send failed");
274        if(send(sock, &resolution, 1, 0)==-1)
275                ui_warn("tracelib send failed");
276}
277
278static void ui_msg(const char * severity, const char * format, va_list va)
279{
280        char buf[1024], tclcmd[32];
281       
282        vsprintf(buf, format, va);
283       
284        sprintf(tclcmd, "ui_%s $warn", severity);
285       
286        Tcl_SetVar(interp, "warn", buf, 0);
287       
288        Tcl_Eval(interp, tclcmd);
289        Tcl_UnsetVar(interp, "warn", 0);
290       
291}
292
293static void ui_warn(const char * format, ...)
294{
295        va_list va;
296       
297        va_start(va, format);
298                ui_msg("warn", format, va);
299        va_end(va);
300}
301
302static void ui_info(const char * format, ...)
303{
304        va_list va;
305       
306        va_start(va, format);
307                ui_msg("msg", format, va);
308        va_end(va);
309}
310
311static int TracelibRunCmd(Tcl_Interp * in)
312{
313        struct sockaddr_un sun;
314        fd_set fdr;
315        int i;
316        int max_fd, max_used, socks[MAX_SOCKETS];
317        struct rlimit rl;
318       
319        pthread_mutex_lock(&sock_mutex);
320        if(cleanuping)
321        {
322                pthread_mutex_unlock(&sock_mutex);
323                return 0;
324        }
325        sock=socket(AF_UNIX, SOCK_STREAM, 0);
326        pthread_mutex_unlock(&sock_mutex);
327       
328        interp=in;
329       
330        rl.rlim_cur=rl.rlim_max=RLIM_INFINITY;
331        if(setrlimit(RLIMIT_NOFILE, &rl)==-1)
332        {
333                ui_warn("setrlimit failed (%d)", errno);
334        }
335
336       
337        sun.sun_family=AF_UNIX;
338        strcpy(sun.sun_path, name);
339        if(bind(sock, (struct sockaddr*)&sun, sizeof(sun))==-1)
340        {
341                Tcl_SetResult(interp, "Cannot bind socket", TCL_STATIC);
342                return TCL_ERROR;
343        }
344       
345        listen(sock, 5);
346        max_used=0;
347        max_fd=sock;
348       
349        for(;sock!=-1&&!cleanuping;)
350        {
351                FD_ZERO(&fdr);
352                FD_SET(sock, &fdr);
353                for(i=0;i<max_used;++i)
354                        FD_SET(socks[i], &fdr);
355                               
356                if(select(max_fd+1, &fdr, 0, 0, 0)<1)
357                {
358                        continue;
359                }
360                if(sock==-1)
361                {
362                        break;
363                }
364                if(FD_ISSET(sock, &fdr))
365                {
366                        int s;
367                        s=accept(sock, 0, 0);
368                       
369                        if(s==-1)
370                        {
371                                if(cleanuping)
372                                        break;
373                                else
374                                        ui_warn("tracelib: accept return -1 (errno: %d)", errno);
375                                /* failed sometimes and i dunno why*/
376                                continue;
377                        }
378                        /* Temporary solution, it's better to regenerate this variable in each iteration, because when closing socket we'll get it too high */                         
379                        if(s>max_fd)
380                                max_fd=s;
381                        for(i=0;i<max_used;++i)
382                                if(!socks[i])
383                                {
384                                        socks[i]=s;
385                                        break;
386                                }
387                        if(i==max_used)
388                        {
389                                if(max_used==MAX_SOCKETS-1)
390                                {
391                                        ui_warn("There is no place to store socket");
392                                        close(s);
393                                }
394                                else
395                                        socks[max_used++]=s;
396                        }
397                }
398               
399                for(i=0;i<max_used;++i)
400                {
401                        if(!socks[i])
402                                continue;
403                        if(FD_ISSET(socks[i], &fdr))
404                        {
405                                if(!process_line(socks[i]))
406                                {
407                                        close(socks[i]);
408                                        socks[i]=0;
409                                        continue;
410                                }
411                        }
412                }
413        }
414       
415        for(i=0;i<max_used;++i)
416        {
417                if(socks[i])
418                {
419                        close(socks[i]);
420                        socks[i]=0;
421                }
422        }
423       
424        return TCL_OK;
425}
426
427static int TracelibCleanCmd(Tcl_Interp * interp UNUSED)
428{
429        #define safe_free(x) do{free(x); x=0;}while(0);
430        cleanuping=1;
431        pthread_mutex_lock(&sock_mutex);
432        if(sock!=-1)
433        {
434                /* shutdown(sock, SHUT_RDWR);*/
435                close(sock);
436                sock=-1;
437        }
438        pthread_mutex_unlock(&sock_mutex);
439        if(name)
440        {
441                unlink(name);
442                safe_free(name);
443        }
444        if(filemap)
445                safe_free(filemap);
446        if(depends)
447                safe_free(depends);
448        enable_fence=0;
449        #undef safe_free
450        cleanuping=0;
451        return TCL_OK;
452}
453
454static int TracelibCloseSocketCmd(Tcl_Interp * interp UNUSED)
455{
456        cleanuping=1;
457        pthread_mutex_lock(&sock_mutex);
458        if(sock!=-1)
459        {
460                /*shutdown(sock, SHUT_RDWR);*/
461                close(sock);
462                sock=-1;
463        }
464        pthread_mutex_unlock(&sock_mutex);
465        return TCL_OK;
466}
467
468static int TracelibSetDeps(Tcl_Interp * interp UNUSED, int objc, Tcl_Obj* CONST objv[])
469{
470        char * t, * d;
471        size_t l;
472        if(objc!=3)
473        {
474                Tcl_WrongNumArgs(interp, 2, objv, "number of arguments should be exactly 3");
475                return TCL_ERROR;
476        }
477       
478        d=Tcl_GetString(objv[2]);
479        l=strlen(d);
480        depends=malloc(l+2);
481        depends[l+1]=0;
482        strcpy(depends, d);
483        for(t=depends;*t;++t)
484                if(*t==' ')
485                        *t++=0;
486       
487        return TCL_OK;
488}
489
490static int TracelibEnableFence(Tcl_Interp * interp UNUSED)
491{
492        enable_fence=1;
493        if(filemap)
494                free(filemap);
495        filemap=0;
496        return TCL_OK;
497}
498
499int TracelibCmd(ClientData clientData UNUSED, Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[])
500{
501        int result=TCL_OK;
502        static const char * options[]={"setname", "run", "clean", "setsandbox", "closesocket", "setdeps", "enablefence", 0};
503        typedef enum 
504        {
505                kSetName,
506                kRun,
507                kClean,
508                kSetSandbox,
509                kCloseSocket,
510                kSetDeps,
511                kEnableFence
512        } EOptions;
513        EOptions current_option;
514       
515        /* There is no args for commands now. */
516        if (objc <2)
517        {
518                Tcl_WrongNumArgs(interp, 1, objv, "option");
519                return TCL_ERROR;
520        }
521       
522        result=Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0, (int*)&current_option);
523        if(result==TCL_OK)
524        {
525                switch(current_option)
526                {
527                case kSetName:
528                        result=TracelibSetNameCmd(interp, objc, objv);
529                        break;
530                case kRun:
531                        result=TracelibRunCmd(interp);
532                        break;
533                case kClean:
534                        result=TracelibCleanCmd(interp);
535                        break;
536                case kCloseSocket:
537                        result=TracelibCloseSocketCmd(interp);
538                        break;
539                case kSetSandbox:
540                        result=TracelibSetSandboxCmd(interp, objc, objv);
541                        break;
542                case kSetDeps:
543                        result=TracelibSetDeps(interp, objc, objv);
544                        break;
545                case kEnableFence:
546                        result=TracelibEnableFence(interp);
547                        break;
548                }
549        }
550       
551        return result;
552}
Note: See TracBrowser for help on using the repository browser.