~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to trampoline/trampoline.c

  • Committer: matt.giuca
  • Date: 2009-01-12 01:03:59 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:1077
Reverted revisions 1059 and 1069 - renaming the DB user from 'postgres' to
    'ivleuser'.
    Heart was in the right place, but currently doesn't work because postgres
    owns the relations and we've no way to correct that yet (since we manually
    run the .sql script as postgres).

    This will be reverted back again once that's solved.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
 * This program runs a given program in a given working dir.
23
23
 * First, it chroots to a jail path and setuids to a given user ID.
24
24
 * This is intented to provide a safe execution environment for arbitrary
25
 
 * programs and scripts.
 
25
 * programs and services.
26
26
 *
27
27
 * Scripts (such as Python programs) should be executed by supplying
28
28
 * "/usr/bin/python" as the program, and the script as the first argument.
33
33
 *  sudo chown root:root trampoline; sudo chroot +s trampoline
34
34
 */
35
35
 
 
36
#define _XOPEN_SOURCE
 
37
 
36
38
#include <stdio.h>
37
39
#include <stdlib.h>
38
40
#include <string.h>
39
41
#include <unistd.h>
 
42
#include <syslog.h>
 
43
#include <sys/mount.h>
 
44
#include <sys/types.h>
 
45
#include <sys/stat.h>
 
46
#include <sys/time.h>
 
47
#include <sys/resource.h>
 
48
#include <limits.h>
 
49
#include <signal.h>
40
50
 
41
51
/* conf.h is admin-configured by the setup process.
42
52
 * It defines jail_base.
43
53
 */
44
54
#include "conf.h"
45
55
 
46
 
/* Argument names */
47
 
#define ARG_UID         1
48
 
#define ARG_JAILPATH    2
49
 
#define ARG_CWD         3
50
 
#define ARG_PROG        4
51
 
 
52
 
#define MIN_ARGC        5
53
 
 
54
 
#define UID_ROOT        0
 
56
#include "norm.h"
55
57
 
56
58
/* Returns TRUE if the given uid is allowed to execute trampoline.
57
59
 * Only root or the web server should be allowed to execute.
73
75
    return 0;
74
76
}
75
77
 
 
78
/* Turn the process into a daemon using the standard
 
79
 * 2-fork technique.
 
80
 */
 
81
void daemonize(void)
 
82
{
 
83
    pid_t pid, sid;
 
84
 
 
85
    /* already a daemon */
 
86
    if ( getppid() == 1 ) return;
 
87
 
 
88
    /* Fork off the parent process */
 
89
    pid = fork();
 
90
    if (pid < 0) {
 
91
        exit(1);
 
92
    }
 
93
    /* If we got a good PID, then we can exit the parent process. */
 
94
    if (pid > 0) {
 
95
        exit(0);
 
96
    }
 
97
 
 
98
    /* At this point we are executing as the child process */
 
99
 
 
100
    /* Change the file mode mask */
 
101
    umask(022);
 
102
 
 
103
    /* Create a new SID for the child process */
 
104
    sid = setsid();
 
105
    if (sid < 0) {
 
106
        exit(1);
 
107
    }
 
108
 
 
109
    /* Change the current working directory.  This prevents the current
 
110
       directory from being locked; hence not being able to remove it. */
 
111
    if ((chdir("/")) < 0) {
 
112
        exit(1);
 
113
    }
 
114
 
 
115
    /* Redirect standard files to /dev/null */
 
116
    freopen( "/dev/null", "r", stdin);
 
117
    freopen( "/dev/null", "w", stdout);
 
118
    freopen( "/dev/null", "w", stderr);
 
119
}
 
120
 
 
121
static void usage(const char* nm)
 
122
{
 
123
    fprintf(stderr,
 
124
        "usage: %s [-d] [-u] <uid> <jail> <cwd> <program> [args...]\n", nm);
 
125
    exit(1);
 
126
}
 
127
 
 
128
#ifdef IVLE_AUFS_JAILS
 
129
/* Die more pleasantly if mallocs fail. */
 
130
void *die_if_null(void *ptr)
 
131
{
 
132
    if (ptr == NULL)
 
133
    {
 
134
        perror("not enough memory");
 
135
        exit(1);
 
136
    }
 
137
    return ptr;
 
138
}
 
139
 
 
140
/* Find the path of the user components of a jail, given a mountpoint. */
 
141
char *jail_src(const char* jailpath)
 
142
{
 
143
    char* src;
 
144
    int srclen;
 
145
    int dstlen;
 
146
 
 
147
    srclen = strlen(jail_src_base);
 
148
    dstlen = strlen(jail_base);
 
149
    
 
150
    src = die_if_null(malloc(strlen(jailpath) + (srclen - dstlen) + 1));
 
151
    strcpy(src, jail_src_base);
 
152
    strcat(src, jailpath+dstlen);
 
153
 
 
154
    return src;
 
155
}
 
156
 
 
157
/* Check for the validity of a jail in the given path, mounting it if it looks
 
158
 * empty.
 
159
 * TODO: Updating /etc/mtab would be nice. */
 
160
void mount_if_needed(const char* jailpath)
 
161
{
 
162
    char *jailsrc;
 
163
    char *jaillib;
 
164
    char *mountdata;
 
165
 
 
166
    /* Check if there is something useful in the jail. If not, it's probably
 
167
     * not mounted. */
 
168
    jaillib = die_if_null(malloc(strlen(jailpath) + 5));
 
169
    sprintf(jaillib, "%s/lib", jailpath);
 
170
 
 
171
    if (access(jaillib, F_OK))
 
172
    {
 
173
        /* No /lib? Mustn't be mounted. Mount it, creating the dir if needed. */
 
174
        if (access(jailpath, F_OK))
 
175
        {
 
176
             if(mkdir(jailpath, 0755))
 
177
             {
 
178
                 syslog(LOG_ERR, "could not create mountpoint %s\n", jailpath);
 
179
                 perror("could not create jail mountpoint");
 
180
                 exit(1);
 
181
             }
 
182
             syslog(LOG_NOTICE, "created mountpoint %s\n", jailpath);
 
183
        }
 
184
       
 
185
        jailsrc = jail_src(jailpath);
 
186
        mountdata = die_if_null(malloc(3 + strlen(jailsrc) + 4 + strlen(jail_system) + 3 + 1));
 
187
        sprintf(mountdata, "br:%s=rw:%s=ro", jailsrc, jail_system);
 
188
        if (mount("none", jailpath, "aufs", 0, mountdata))
 
189
        {
 
190
            syslog(LOG_ERR, "could not mount %s\n", jailpath);
 
191
            perror("could not mount");
 
192
            exit(1);
 
193
        } 
 
194
 
 
195
        syslog(LOG_INFO, "mounted %s\n", jailpath);
 
196
 
 
197
        free(jailsrc);
 
198
        free(mountdata);
 
199
    }
 
200
 
 
201
    free(jaillib);
 
202
}
 
203
#endif /* IVLE_AUFS_JAILS */
 
204
 
 
205
/* Unsets any signal mask applied by the parent process */
 
206
int unmask_signals(void)
 
207
{
 
208
    int result;
 
209
    sigset_t* sigset;
 
210
    sigset = die_if_null(malloc(sizeof(sigset_t)));
 
211
    sigemptyset(sigset);
 
212
    result = sigprocmask(SIG_SETMASK, sigset, NULL);
 
213
    free(sigset);
 
214
    printf("%d", result);
 
215
    return result;
 
216
}
 
217
 
76
218
int main(int argc, char* const argv[])
77
219
{
78
220
    char* jailpath;
 
221
    char* work_dir;
 
222
    char* prog;
 
223
    char* const * args;
79
224
    int uid;
 
225
    int arg_num = 1;
 
226
    int daemon_mode = 0;
 
227
    int unlimited = 0;
 
228
    char canonical_jailpath[PATH_MAX];
80
229
 
81
230
    /* Disallow execution from all users but the whitelisted ones, and root */
82
231
    if (!uid_allowed(getuid()))
86
235
    }
87
236
 
88
237
    /* Args check and usage */
89
 
    if (argc < MIN_ARGC)
90
 
    {
91
 
        fprintf(stderr, "usage: %s <uid> <jail> <cwd> <program> [args...]\n",
92
 
            argv[0]);
93
 
        exit(EXIT_FAILURE);
94
 
    }
 
238
    if (argc < 5)
 
239
    {
 
240
        usage(argv[0]);
 
241
    }
 
242
 
 
243
    if (strcmp(argv[arg_num], "-d") == 0)
 
244
    {
 
245
        if (argc < 6)
 
246
        {
 
247
            usage(argv[0]);
 
248
        }
 
249
        daemon_mode = 1;
 
250
        arg_num++;
 
251
    }
 
252
 
 
253
    if (strcmp(argv[arg_num], "-u") == 0)
 
254
    {
 
255
        if (argc < 6)
 
256
        {
 
257
            usage(argv[0]);
 
258
        }
 
259
        unlimited = 1;
 
260
        arg_num++;
 
261
    }
 
262
    uid = atoi(argv[arg_num++]);
 
263
    jailpath = argv[arg_num++];
 
264
    work_dir = argv[arg_num++];
 
265
    prog = argv[arg_num];
 
266
    args = argv + arg_num;
95
267
 
96
268
    /* Disallow suiding to the root user */
97
 
    uid = atoi(argv[ARG_UID]);
98
 
    if (uid == UID_ROOT)
 
269
    if (uid == 0)
99
270
    {
100
271
        fprintf(stderr, "cannot set up a jail as root\n");
101
272
        exit(1);
102
273
    }
103
274
 
104
 
    /* Jail path must:
105
 
     * Be non-empty
106
 
     * Start with a '/'
107
 
     * Not contain "/.."
108
 
     * Begin with jail_base
 
275
    /* Jail path must be an absolute path,
 
276
     * and it must begin with jail_base.
109
277
     */
110
 
    jailpath = argv[ARG_JAILPATH];
111
 
    if (strlen(jailpath) < 1 || jailpath[0] != '/'
112
 
            || strstr(jailpath, "/..")
113
 
            || strncmp(jailpath, jail_base, strlen(jail_base)))
114
 
    {
115
 
        fprintf(stderr, "bad jail path: %s\n", jailpath);
116
 
        exit(1);
117
 
    }
 
278
    if (norm(canonical_jailpath, PATH_MAX, jailpath) != 0)
 
279
    {
 
280
        fprintf(stderr, "bad jail path: %s\n", jailpath);
 
281
        exit(1);
 
282
    }
 
283
    if (strncmp(canonical_jailpath, jail_base, strlen(jail_base)))
 
284
    {
 
285
        fprintf(stderr, "bad jail path: %s\n", jailpath);
 
286
        exit(1);
 
287
    }
 
288
 
 
289
    openlog("trampoline", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);
 
290
 
 
291
    #ifdef IVLE_AUFS_JAILS
 
292
    mount_if_needed(canonical_jailpath);
 
293
    #endif /* IVLE_AUFS_JAILS */
118
294
 
119
295
    /* chroot into the jail.
120
296
     * Henceforth this process, and its children, cannot see anything above
121
 
     * jailpath. */
122
 
    if (chroot(jailpath))
 
297
     * canoncial_jailpath. */
 
298
    if (chroot(canonical_jailpath))
123
299
    {
 
300
        syslog(LOG_ERR, "chroot to %s failed\n", canonical_jailpath);
 
301
 
124
302
        perror("could not chroot");
125
303
        exit(1);
126
304
    }
127
305
 
128
306
    /* chdir into the specified working directory */
129
 
    if (chdir(argv[ARG_CWD]))
 
307
    if (chdir(work_dir))
130
308
    {
131
309
        perror("could not chdir");
132
310
        exit(1);
135
313
    /* setuid to the given user ID.
136
314
     * Henceforth we will be running as this user instead of root.
137
315
     */
 
316
    if (setgid(uid))
 
317
    {
 
318
        perror("could not setgid");
 
319
        exit(1);
 
320
    }
 
321
 
138
322
    if (setuid(uid))
139
323
    {
140
324
        perror("could not setuid");
141
325
        exit(1);
142
326
    }
143
327
 
 
328
    /* set user resource limits */
 
329
    if (!unlimited)
 
330
    {
 
331
        struct rlimit l;
 
332
        /* Process adress space in memory */
 
333
        l.rlim_cur = 192 * 1024 * 1024; /* 192MiB */
 
334
        l.rlim_max = 256 * 1024 * 1024; /* 256MiB */
 
335
        if (setrlimit(RLIMIT_AS, &l))
 
336
        {
 
337
            perror("could not setrlimit/RLIMIT_AS");
 
338
            exit(1);
 
339
        }
 
340
        
 
341
        /* Process data segment in memory
 
342
         * Note: This requires a kernel patch to work correctly otherwise it is  
 
343
         * ineffective (thus you are only limited by RLIMIT_AS)
 
344
         */
 
345
        l.rlim_cur = 192 * 1024 * 1024; /* 192MiB */
 
346
        l.rlim_max = 256 * 1024 * 1024; /* 256MiB */
 
347
        if (setrlimit(RLIMIT_DATA, &l))
 
348
        {
 
349
            perror("could not setrlimit/RLIMIT_DATA");
 
350
            exit(1);
 
351
        }
 
352
 
 
353
        /* Core */
 
354
        l.rlim_cur = 0; /* No core files */
 
355
        l.rlim_max = 0; /* No core files */
 
356
        if (setrlimit(RLIMIT_CORE, &l))
 
357
        {
 
358
            perror("could not setrlimit/RLIMIT_CORE");
 
359
            exit(1);
 
360
        }
 
361
 
 
362
        /* CPU */
 
363
        l.rlim_cur = 25; /* 25 Seconds */
 
364
        l.rlim_max = 30; /* 30 Seconds */
 
365
        if (setrlimit(RLIMIT_CPU, &l))
 
366
        {
 
367
            perror("could not setrlimit/RLIMIT_CPU");
 
368
            exit(1);
 
369
        }
 
370
 
 
371
        /* File Size */
 
372
        l.rlim_cur = 64 * 1024 * 1024; /* 64MiB */
 
373
        l.rlim_max = 72 * 1024 * 1024; /* 72MiB */
 
374
if (setrlimit(RLIMIT_FSIZE, &l))
 
375
        {
 
376
            perror("could not setrlimit/RLIMIT_FSIZE");
 
377
            exit(1);
 
378
        }
 
379
    }
 
380
 
 
381
    /* Remove any signal handler masks so we can send signals to the child */
 
382
    if(unmask_signals())
 
383
    {
 
384
        perror("could not unmask signals");
 
385
        exit(1);
 
386
    }
 
387
 
 
388
    /* If everything was OK daemonize (if required) */
 
389
    if (daemon_mode)
 
390
    {
 
391
        daemonize();
 
392
    }
 
393
 
144
394
    /* exec (replace this process with the a new instance of the target
145
395
     * program). Pass along all the arguments.
146
396
     * Note that for script execution, the "program" will be the interpreter,
147
397
     * and the first argument will be the script. */
148
 
    execv(argv[ARG_PROG], argv + ARG_PROG);
 
398
    execv(prog, args);
149
399
 
150
400
    /* nb exec won't return unless there was an error */
 
401
    syslog(LOG_ERR, "exec of %s in %s failed", prog, canonical_jailpath);
 
402
 
151
403
    perror("could not exec");
152
 
    return EXIT_FAILURE;
 
404
    closelog();
 
405
    return 1;
153
406
}