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

Last change on this file since 64294 was 64294, checked in by jmr@…, 11 years ago

error checking, sprintf -> snprintf, strcpy -> strncpy

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 13.9 KB
Line 
1/*
2 * tracelib.c
3 * $Id: tracelib.c 64294 2010-02-28 21:59:12Z jmr@macports.org $
4 *
5 * Copyright (c) 2007 Eugene Pimenov (GSoC), The MacPorts Project.
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 the MacPorts 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 <limits.h>
48#include "tracelib.h"
49
50static char * name;
51static char * sandbox;
52static char * filemap, * filemap_end;
53static char * depends; 
54static int sock=-1;
55static int enable_fence=0;
56static Tcl_Interp * interp;
57static pthread_mutex_t sock_mutex=PTHREAD_MUTEX_INITIALIZER;
58static int cleanuping=0;
59static char * sdk=
60#ifdef TRACE_SDK
61        /*"MacOSX10.4u.sdk"*/
62        TRACE_SDK
63#else
64        0
65#endif
66;
67
68static void send_file_map(int sock);
69static void dep_check(int sock, const char * path);
70static void sandbox_violation(int sock, const char * path);
71static void ui_warn(const char * format, ...);
72static void ui_info(const char * format, ...);
73
74#define MAX_SOCKETS ((FD_SETSIZE)-1)
75
76static int TracelibSetNameCmd(Tcl_Interp * interp, int objc, Tcl_Obj *CONST objv[])
77{
78        if (objc != 3)
79        {
80                Tcl_WrongNumArgs(interp, 2, objv, "number of arguments should be exactly 3");
81                return TCL_ERROR;
82        }
83       
84        name=strdup(Tcl_GetString(objv[2]));
85        if (!name) {
86            Tcl_SetResult(interp, "memory allocation failed", TCL_STATIC);
87            return TCL_ERROR;
88        }
89       
90        return TCL_OK;
91}
92
93/*
94 * Save sandbox path into memory and prepare it for checks.
95 * For now it just change : to \0, and add last \0
96 * Input:
97 *  /dev/null:/dev/tty:/tmp
98 * In variable;
99 * /dev/null\0/dev/tty\0/tmp\0\0
100 */
101static int TracelibSetSandboxCmd(Tcl_Interp * interp, int objc, Tcl_Obj *CONST objv[])
102{
103        int len;
104        char * t;
105       
106        if (objc != 3)
107        {
108                Tcl_WrongNumArgs(interp, 2, objv, "number of arguments should be exactly 3");
109                return TCL_ERROR;
110        }
111       
112        len=strlen(Tcl_GetString(objv[2]))+2;
113        sandbox=(char*)malloc(len);
114        if (!sandbox) {
115            Tcl_SetResult(interp, "memory allocation failed", TCL_STATIC);
116            return TCL_ERROR;
117        }
118        memset(sandbox, 0, len);
119        strlcpy(sandbox, Tcl_GetString(objv[2]), len);
120        for(t=sandbox;(t=strchr(t+1, ':'));)
121        {
122                /* : -> \0 */
123                if(t[-1]!='\\')
124                        *t=0;
125                else
126                        /* \: -> : */
127                        /* TODO \\: -> \: */
128                        memmove(t-1, t, strlen(t));
129        }
130       
131        return TCL_OK;
132}
133
134/*
135 * Is there more data? (return 1 if more data in socket, 0 otherwise)
136 */
137static char can_I_recv_more(int sock)
138{
139        struct timeval tv;
140        fd_set fdr;
141        tv.tv_sec  = 0;
142        tv.tv_usec = 0;
143
144        FD_ZERO(&fdr);
145        FD_SET(sock, &fdr);
146        return select(sock+1, &fdr, 0, 0, &tv) == 1;
147}
148
149/*
150 * receive line from socket, parse it and send answer
151 */
152static char process_line(int sock)
153{
154        char * t, buf[1024]={0}, *f, *next_t;
155        int len;
156       
157        if((len=recv(sock, buf, sizeof(buf) - 1, 0))==-1)
158                return 0;
159        if(!len)
160                return 0;
161       
162        buf[len] = '\0';
163       
164        for(t=buf;*t&&t-buf<(int)sizeof(buf);t=next_t)
165        {
166                next_t = t+strlen(t)+1;
167                if(next_t == buf + sizeof(buf) && len == sizeof(buf) - 1)
168                {
169                        memmove(buf, t, next_t - t);
170                        t = buf;
171                        {
172                                char * end_of_t = t + strlen(t);
173                                *end_of_t = ' ';
174                                for(;can_I_recv_more(sock);)
175                                {
176                                        if(recv(sock, end_of_t, 1, 0) != 1)
177                                        {
178                                                ui_warn("recv failed");
179                                                return 0;
180                                        }
181                                        if(*end_of_t++ == '\0')
182                                                break;
183                                }
184                        }
185                }
186   
187                f=strchr(t, '\t');
188                if(!f)
189                {
190                        ui_warn("malformed command %s", t);
191                        break;
192                }
193
194                /* Replace \t with \0 */
195                *f = '\0';
196                /* Advance pointer to arguments */
197                f++;
198
199                if(!strcmp(t, "filemap"))
200                {
201                        send_file_map(sock);
202                }else if(!strcmp(t, "sandbox_violation"))
203                {
204                        sandbox_violation(sock, f);
205                }else if(!strcmp(t, "dep_check"))
206                {
207                        dep_check(sock, f);
208                }else if(!strcmp(t, "execve"))
209                {
210                        /* ====================== */
211                        /* = TODO: do something = */
212                        /* ====================== */
213                }else
214                {
215                        ui_warn("unknown command %s (%s)", t, f);
216                }
217        }
218        return 1;
219}
220
221static void send_file_map(int sock)
222{
223        if(!filemap)
224        {
225                char * t, * _;
226               
227                size_t remaining = 1024;
228                filemap=(char*)malloc(remaining);
229                if (!filemap) {
230                    ui_warn("send_file_map: memory allocation failed");
231                return;
232                }
233                t=filemap;
234               
235                #define append_allow(path, resolution) do { strlcpy(t, path, remaining); \
236                                                            if (remaining < (strlen(t)+3)) \
237                                                                remaining=0; \
238                                                            else \
239                                                                remaining-=strlen(t)+3; \
240                                                            t+=strlen(t)+1; \
241                                                            *t++=resolution; \
242                                                            *t++=0; \
243                                                          } while(0);
244                if(enable_fence)
245                {
246                        for(_=sandbox; *_; _+=strlen(_)+1)
247                                append_allow(_, 0);
248                       
249                        append_allow("/bin", 0);
250                        append_allow("/sbin", 0);
251                        append_allow("/dev", 0);
252                        append_allow(Tcl_GetVar(interp, "macports::prefix", TCL_GLOBAL_ONLY), 2);
253                        /* If there is no SDK we will allow everything in /usr /System/Library etc, else add binaries to allow, and redirect root to SDK. */
254                        if(sdk&&*sdk)
255                        {
256                                char buf[260];
257                                buf[0] = '\0';
258                                strlcat(buf, Tcl_GetVar(interp, "macports::developer_dir", TCL_GLOBAL_ONLY), 260);
259                                strlcat(buf, "/SDKs/", 260);
260                                strlcat(buf, sdk, 260);
261                       
262                                append_allow("/usr/bin", 0);
263                                append_allow("/usr/sbin", 0);
264                                append_allow("/usr/libexec/gcc", 0);
265                                append_allow("/System/Library/Perl", 0);
266                                append_allow("/", 1);
267                                strlcpy(t-1, buf, remaining);
268                                t+=strlen(t)+1;
269                        }else
270                        {
271                                append_allow("/usr", 0);
272                                append_allow("/System/Library", 0);
273                                append_allow("/Library", 0);
274                                append_allow(Tcl_GetVar(interp, "macports::developer_dir", TCL_GLOBAL_ONLY), 0);
275                        }
276                }else
277                        append_allow("/", 0);
278                filemap_end=t;
279                #undef append_allow
280        }
281       
282        {
283                size_t s=filemap_end-filemap;
284                send(sock, &s, sizeof(s), 0);
285                send(sock, filemap, s, 0);
286        }
287}
288
289static void sandbox_violation(int sock UNUSED, const char * path)
290{
291        Tcl_SetVar(interp, "path", path, 0);
292        Tcl_Eval(interp, "slave_add_sandbox_violation $path");
293        Tcl_UnsetVar(interp, "path", 0);
294}
295
296static void dep_check(int sock, const char * path)
297{
298        char * port=0;
299        size_t len=1;
300        char resolution='!';
301               
302        Tcl_SetVar(interp, "path", path, 0);
303        Tcl_Eval(interp, "registry::file_registered $path");
304        port=strdup(Tcl_GetStringResult(interp));
305        if (!port) {
306                ui_warn("dep_check: memory allocation failed");
307            return;
308        }
309        Tcl_UnsetVar(interp, "path", 0);
310       
311        if(*port!='0'||port[1])
312        {
313                char * t;
314       
315                t=depends;
316                for(;*t;t+=strlen(t)+1)
317                {
318                        if(!strcmp(t, port))
319                        {
320                                resolution='+';
321                                break;
322                        }
323                }
324        }
325       
326        if(resolution!='+') {
327            if(*port=='0'&&!port[1])
328                    ui_info("trace: access denied to %s (*unknown*)", path);
329                else
330                    ui_info("trace: access denied to %s (%s)", path, port);
331    }
332
333        free(port);
334       
335        if(send(sock, &len, sizeof(len), 0)==-1)
336                ui_warn("tracelib send failed");
337        if(send(sock, &resolution, 1, 0)==-1)
338                ui_warn("tracelib send failed");
339}
340
341static void ui_msg(const char * severity, const char * format, va_list va)
342{
343        char buf[1024], tclcmd[32];
344       
345        vsnprintf(buf, sizeof(buf), format, va);
346       
347        snprintf(tclcmd, sizeof(tclcmd), "ui_%s $warn", severity);
348       
349        Tcl_SetVar(interp, "warn", buf, 0);
350       
351        Tcl_Eval(interp, tclcmd);
352        Tcl_UnsetVar(interp, "warn", 0);
353       
354}
355
356static void ui_warn(const char * format, ...)
357{
358        va_list va;
359       
360        va_start(va, format);
361                ui_msg("warn", format, va);
362        va_end(va);
363}
364
365static void ui_info(const char * format, ...)
366{
367        va_list va;
368       
369        va_start(va, format);
370                ui_msg("msg", format, va);
371        va_end(va);
372}
373
374static int TracelibRunCmd(Tcl_Interp * in)
375{
376        struct sockaddr_un sun;
377        fd_set fdr;
378        int i;
379        int max_fd, max_used, socks[MAX_SOCKETS];
380        struct rlimit rl;
381       
382        pthread_mutex_lock(&sock_mutex);
383        if(cleanuping)
384        {
385                pthread_mutex_unlock(&sock_mutex);
386                return 0;
387        }
388        sock=socket(AF_UNIX, SOCK_STREAM, 0);
389        if (sock == -1) {
390                Tcl_SetErrno(errno);
391                Tcl_ResetResult(interp);
392                Tcl_AppendResult(interp, "socket: ", (char *) Tcl_PosixError(interp), NULL);
393                pthread_mutex_unlock(&sock_mutex);
394                return TCL_ERROR;
395        }
396        pthread_mutex_unlock(&sock_mutex);
397       
398        interp=in;
399       
400        rl.rlim_cur=rl.rlim_max=RLIM_INFINITY;
401#if defined(__APPLE__) && defined(OPEN_MAX)
402        if (OPEN_MAX < rl.rlim_cur)
403                rl.rlim_cur = OPEN_MAX;
404#endif
405        if(setrlimit(RLIMIT_NOFILE, &rl)==-1)
406        {
407                ui_warn("setrlimit failed (%d)", errno);
408        }
409
410       
411        sun.sun_family=AF_UNIX;
412        strlcpy(sun.sun_path, name, sizeof(sun.sun_path));
413        if (bind(sock, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
414                Tcl_SetErrno(errno);
415                Tcl_ResetResult(interp);
416                Tcl_AppendResult(interp, "bind: ", (char *) Tcl_PosixError(interp), NULL);
417                return TCL_ERROR;
418        }
419       
420        if (listen(sock, 5) == -1) {
421                Tcl_SetErrno(errno);
422                Tcl_ResetResult(interp);
423                Tcl_AppendResult(interp, "listen: ", (char *) Tcl_PosixError(interp), NULL);
424                return TCL_ERROR;
425        }
426        max_used=0;
427        max_fd=sock;
428       
429        for(;sock!=-1&&!cleanuping;)
430        {
431                FD_ZERO(&fdr);
432                FD_SET(sock, &fdr);
433                for(i=0;i<max_used;++i)
434                        FD_SET(socks[i], &fdr);
435                               
436                if(select(max_fd+1, &fdr, 0, 0, 0)<1)
437                {
438                        continue;
439                }
440                if(sock==-1)
441                {
442                        break;
443                }
444                if(FD_ISSET(sock, &fdr))
445                {
446                        int s;
447                        s=accept(sock, 0, 0);
448                       
449                        if(s==-1)
450                        {
451                                if(cleanuping)
452                                        break;
453                                else
454                                        ui_warn("tracelib: accept return -1 (errno: %d)", errno);
455                                /* failed sometimes and i dunno why*/
456                                continue;
457                        }
458                        /* Temporary solution, it's better to regenerate this variable in each iteration, because when closing socket we'll get it too high */                         
459                        if(s>max_fd)
460                                max_fd=s;
461                        for(i=0;i<max_used;++i)
462                                if(!socks[i])
463                                {
464                                        socks[i]=s;
465                                        break;
466                                }
467                        if(i==max_used)
468                        {
469                                if(max_used==MAX_SOCKETS-1)
470                                {
471                                        ui_warn("There is no place to store socket");
472                                        close(s);
473                                }
474                                else
475                                        socks[max_used++]=s;
476                        }
477                }
478               
479                for(i=0;i<max_used;++i)
480                {
481                        if(!socks[i])
482                                continue;
483                        if(FD_ISSET(socks[i], &fdr))
484                        {
485                                if(!process_line(socks[i]))
486                                {
487                                        close(socks[i]);
488                                        socks[i]=0;
489                                        continue;
490                                }
491                        }
492                }
493        }
494       
495        for(i=0;i<max_used;++i)
496        {
497                if(socks[i])
498                {
499                        close(socks[i]);
500                        socks[i]=0;
501                }
502        }
503       
504        return TCL_OK;
505}
506
507static int TracelibCleanCmd(Tcl_Interp * interp)
508{
509        #define safe_free(x) do{free(x); x=0;}while(0);
510        cleanuping=1;
511        pthread_mutex_lock(&sock_mutex);
512        if(sock!=-1)
513        {
514                /* shutdown(sock, SHUT_RDWR);*/
515                close(sock);
516                sock=-1;
517        }
518        pthread_mutex_unlock(&sock_mutex);
519        if(name)
520        {
521                unlink(name);
522                safe_free(name);
523        }
524        if(filemap)
525                safe_free(filemap);
526        if(depends)
527                safe_free(depends);
528        enable_fence=0;
529        #undef safe_free
530        cleanuping=0;
531        Tcl_Eval(interp, "registry::close_file_map");
532        return TCL_OK;
533}
534
535static int TracelibCloseSocketCmd(Tcl_Interp * interp UNUSED)
536{
537        cleanuping=1;
538        pthread_mutex_lock(&sock_mutex);
539        if(sock!=-1)
540        {
541                /*shutdown(sock, SHUT_RDWR);*/
542                close(sock);
543                sock=-1;
544        }
545        pthread_mutex_unlock(&sock_mutex);
546        return TCL_OK;
547}
548
549static int TracelibSetDeps(Tcl_Interp * interp UNUSED, int objc, Tcl_Obj* CONST objv[])
550{
551        char * t, * d;
552        size_t l;
553        if(objc!=3)
554        {
555                Tcl_WrongNumArgs(interp, 2, objv, "number of arguments should be exactly 3");
556                return TCL_ERROR;
557        }
558       
559        d=Tcl_GetString(objv[2]);
560        l=strlen(d);
561        depends=malloc(l+2);
562        if (!depends) {
563            Tcl_SetResult(interp, "memory allocation failed", TCL_STATIC);
564            return TCL_ERROR;
565        }
566        depends[l+1]=0;
567        strlcpy(depends, d, l+2);
568        for(t=depends;*t;++t)
569                if(*t==' ')
570                        *t++=0;
571       
572        return TCL_OK;
573}
574
575static int TracelibEnableFence(Tcl_Interp * interp UNUSED)
576{
577        enable_fence=1;
578        if(filemap)
579                free(filemap);
580        filemap=0;
581        return TCL_OK;
582}
583
584int TracelibCmd(ClientData clientData UNUSED, Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[])
585{
586        int result=TCL_OK;
587        static const char * options[]={"setname", "run", "clean", "setsandbox", "closesocket", "setdeps", "enablefence", 0};
588        typedef enum 
589        {
590                kSetName,
591                kRun,
592                kClean,
593                kSetSandbox,
594                kCloseSocket,
595                kSetDeps,
596                kEnableFence
597        } EOptions;
598        EOptions current_option;
599       
600        /* There is no args for commands now. */
601        if (objc <2)
602        {
603                Tcl_WrongNumArgs(interp, 1, objv, "option");
604                return TCL_ERROR;
605        }
606       
607        result=Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0, (int*)&current_option);
608        if(result==TCL_OK)
609        {
610                switch(current_option)
611                {
612                case kSetName:
613                        result=TracelibSetNameCmd(interp, objc, objv);
614                        break;
615                case kRun:
616                        result=TracelibRunCmd(interp);
617                        break;
618                case kClean:
619                        result=TracelibCleanCmd(interp);
620                        break;
621                case kCloseSocket:
622                        result=TracelibCloseSocketCmd(interp);
623                        break;
624                case kSetSandbox:
625                        result=TracelibSetSandboxCmd(interp, objc, objv);
626                        break;
627                case kSetDeps:
628                        result=TracelibSetDeps(interp, objc, objv);
629                        break;
630                case kEnableFence:
631                        result=TracelibEnableFence(interp);
632                        break;
633                }
634        }
635       
636        return result;
637}
Note: See TracBrowser for help on using the repository browser.