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

« back to all changes in this revision

Viewing changes to bin/trampoline/trampoline.c

  • Committer: mattgiuca
  • Date: 2008-07-15 07:19:34 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:875
Added "migrations" directory, which contains incremental database update
    scripts.
Updated users.sql, uniqueness key on offering table.
Added migration matching this update to the migrations directory. Mm handy!

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* IVLE - Informatics Virtual Learning Environment
2
 
 * Copyright (C) 2007-2008 The University of Melbourne
3
 
 *
4
 
 * This program is free software; you can redistribute it and/or modify
5
 
 * it under the terms of the GNU General Public License as published by
6
 
 * the Free Software Foundation; either version 2 of the License, or
7
 
 * (at your option) any later version.
8
 
 *
9
 
 * This program is distributed in the hope that it will be useful,
10
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 
 * GNU General Public License for more details.
13
 
 *
14
 
 * You should have received a copy of the GNU General Public License
15
 
 * along with this program; if not, write to the Free Software
16
 
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
 
 *
18
 
 * Program: Trampoline
19
 
 * Author:  Tom Conway, Matt Giuca
20
 
 * Date:    20/12/2007
21
 
 *
22
 
 * This program runs a given program in a given working dir.
23
 
 * First, it chroots to a jail path and setuids to a given user ID.
24
 
 * This is intented to provide a safe execution environment for arbitrary
25
 
 * programs and services.
26
 
 *
27
 
 * Scripts (such as Python programs) should be executed by supplying
28
 
 * "/usr/bin/python" as the program, and the script as the first argument.
29
 
 *
30
 
 * Must run as root. Will safely setuid to the supplied uid, checking that it
31
 
 * is not root. Recommended that the file is set up as follows:
32
 
 *  sudo chown root:root trampoline; sudo chroot +s trampoline
33
 
 */
34
 
 
35
 
#define _XOPEN_SOURCE
36
 
#define _BSD_SOURCE
37
 
 
38
 
#include <stdio.h>
39
 
#include <stdlib.h>
40
 
#include <string.h>
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 <grp.h>
49
 
#include <limits.h>
50
 
#include <signal.h>
51
 
 
52
 
/* conf.h is admin-configured by the setup process.
53
 
 * It defines jail_base.
54
 
 */
55
 
#include "conf.h"
56
 
 
57
 
#include "norm.h"
58
 
 
59
 
/* Returns TRUE if the given uid is allowed to execute trampoline.
60
 
 * Only root or the web server should be allowed to execute.
61
 
 * This is determined by the whitelist allowed_uids in conf.h.
62
 
 */
63
 
int uid_allowed(int uid)
64
 
{
65
 
    int i;
66
 
    /* root is always allowed to execute trampoline */
67
 
    if (uid == 0)
68
 
        return 1;
69
 
    /* loop over all allowed_uids */
70
 
    for (i=(sizeof(allowed_uids)/sizeof(*allowed_uids))-1; i>=0; i--)
71
 
    {
72
 
        if (allowed_uids[i] == uid)
73
 
            return 1;
74
 
    }
75
 
    /* default to disallowing */
76
 
    return 0;
77
 
}
78
 
 
79
 
/* Turn the process into a daemon using the standard
80
 
 * 2-fork technique.
81
 
 */
82
 
void daemonize(void)
83
 
{
84
 
    pid_t pid, sid;
85
 
 
86
 
    /* already a daemon */
87
 
    if ( getppid() == 1 ) return;
88
 
 
89
 
    /* Fork off the parent process */
90
 
    pid = fork();
91
 
    if (pid < 0) {
92
 
        exit(1);
93
 
    }
94
 
    /* If we got a good PID, then we can exit the parent process. */
95
 
    if (pid > 0) {
96
 
        exit(0);
97
 
    }
98
 
 
99
 
    /* At this point we are executing as the child process */
100
 
 
101
 
    /* Change the file mode mask */
102
 
    umask(022);
103
 
 
104
 
    /* Create a new SID for the child process */
105
 
    sid = setsid();
106
 
    if (sid < 0) {
107
 
        exit(1);
108
 
    }
109
 
 
110
 
    /* Change the current working directory.  This prevents the current
111
 
       directory from being locked; hence not being able to remove it. */
112
 
    if ((chdir("/")) < 0) {
113
 
        exit(1);
114
 
    }
115
 
 
116
 
    /* Redirect standard files to /dev/null */
117
 
    freopen( "/dev/null", "r", stdin);
118
 
    freopen( "/dev/null", "w", stdout);
119
 
    freopen( "/dev/null", "w", stderr);
120
 
}
121
 
 
122
 
static void usage(const char* nm)
123
 
{
124
 
    fprintf(stderr,
125
 
        "usage: %s [-d] [-u] <uid> <base> <src> <system> <jail> <cwd> <program> [args...]\n", nm);
126
 
    fprintf(stderr, "  -d       \tDaemonize\n");
127
 
    fprintf(stderr, "  -u       \tPrint usage\n");
128
 
    fprintf(stderr, "  <uid>    \tUID to run program as\n");
129
 
    fprintf(stderr, "  <base>   \tDirectory that jails will be mounted in\n");
130
 
    fprintf(stderr, "  <src>    \tDirectory containing users jail data\n");
131
 
    fprintf(stderr, "  <system> \tDirectory containing main jail file system\n");
132
 
    fprintf(stderr, "  <jail>   \tDirectory of users mounted jail\n");
133
 
    fprintf(stderr, "  <cwd>    \tDirectory inside the jail to change dir to\n");
134
 
    fprintf(stderr, "  <program>\tProgram inside jail to execute\n");
135
 
    exit(1);
136
 
}
137
 
 
138
 
#ifdef IVLE_AUFS_JAILS
139
 
/* Die more pleasantly if mallocs fail. */
140
 
void *die_if_null(void *ptr)
141
 
{
142
 
    if (ptr == NULL)
143
 
    {
144
 
        perror("not enough memory");
145
 
        exit(1);
146
 
    }
147
 
    return ptr;
148
 
}
149
 
 
150
 
int checked_mount(const char *source, const char *target,
151
 
                  const char *filesystemtype, unsigned long mountflags,
152
 
                  const void *data)
153
 
{
154
 
    int result = mount(source, target, filesystemtype, mountflags, data);
155
 
    if (result)
156
 
    {
157
 
        syslog(LOG_ERR, "could not mount %s on %s\n", source, target);
158
 
        perror("could not mount");
159
 
        exit(1);
160
 
    }
161
 
 
162
 
    return result;
163
 
}
164
 
 
165
 
 
166
 
/* Find the path of the user components of a jail, given a mountpoint. */
167
 
char *jail_src(const char *jail_src_base, const char *jail_base,
168
 
               const char *jailpath)
169
 
{
170
 
    char* src;
171
 
    int srclen;
172
 
    int dstlen;
173
 
 
174
 
    srclen = strlen(jail_src_base);
175
 
    dstlen = strlen(jail_base);
176
 
 
177
 
    src = die_if_null(malloc(strlen(jailpath) + (srclen - dstlen) + 1));
178
 
    strcpy(src, jail_src_base);
179
 
    strcat(src, jailpath+dstlen);
180
 
 
181
 
    return src;
182
 
}
183
 
 
184
 
/* Check for the validity of a jail in the given path, mounting it if it looks
185
 
 * empty.
186
 
 * TODO: Updating /etc/mtab would be nice. */
187
 
void mount_if_needed(const char *jail_src_base, const char *jail_base,
188
 
                     const char *jail_system, const char *jailpath)
189
 
{
190
 
    char *jailsrc;
191
 
    char *jaillib;
192
 
    char *source_bits;
193
 
    char *target_bits;
194
 
 
195
 
    /* Check if there is something useful in the jail. If not, it's probably
196
 
     * not mounted. */
197
 
    jaillib = die_if_null(malloc(strlen(jailpath) + 5));
198
 
    sprintf(jaillib, "%s/lib", jailpath);
199
 
 
200
 
    if (access(jaillib, F_OK))
201
 
    {
202
 
        /* No /lib? Mustn't be mounted. Mount it, creating the dir if needed. */
203
 
        if (access(jailpath, F_OK))
204
 
        {
205
 
             if(mkdir(jailpath, 0755))
206
 
             {
207
 
                 syslog(LOG_ERR, "could not create mountpoint %s\n", jailpath);
208
 
                 perror("could not create jail mountpoint");
209
 
                 exit(1);
210
 
             }
211
 
             syslog(LOG_NOTICE, "created mountpoint %s\n", jailpath);
212
 
        }
213
 
 
214
 
        jailsrc = jail_src(jail_src_base, jail_base, jailpath);
215
 
        checked_mount(jail_system, jailpath, NULL, MS_BIND | MS_RDONLY, NULL);
216
 
 
217
 
        source_bits = die_if_null(malloc(strlen(jailsrc) + 5 + 1));
218
 
        target_bits = die_if_null(malloc(strlen(jailpath) + 5 + 1));
219
 
        sprintf(source_bits, "%s/home", jailsrc);
220
 
        sprintf(target_bits, "%s/home", jailpath);
221
 
 
222
 
        checked_mount(source_bits, target_bits, NULL, MS_BIND, NULL);
223
 
 
224
 
        sprintf(source_bits, "%s/tmp", jailsrc);
225
 
        sprintf(target_bits, "%s/tmp", jailpath);
226
 
 
227
 
        checked_mount(source_bits, target_bits, NULL, MS_BIND, NULL);
228
 
 
229
 
        syslog(LOG_INFO, "mounted %s\n", jailpath);
230
 
 
231
 
        free(jailsrc);
232
 
        free(source_bits);
233
 
        free(target_bits);
234
 
    }
235
 
 
236
 
    free(jaillib);
237
 
}
238
 
#endif /* IVLE_AUFS_JAILS */
239
 
 
240
 
/* Unsets any signal mask applied by the parent process */
241
 
int unmask_signals(void)
242
 
{
243
 
    int result;
244
 
    sigset_t* sigset;
245
 
    sigset = die_if_null(malloc(sizeof(sigset_t)));
246
 
    sigemptyset(sigset);
247
 
    result = sigprocmask(SIG_SETMASK, sigset, NULL);
248
 
    free(sigset);
249
 
    printf("%d", result);
250
 
    return result;
251
 
}
252
 
 
253
 
int main(int argc, char* const argv[])
254
 
{
255
 
    char* jail_base;
256
 
    char* jail_src_base;
257
 
    char* jail_system;
258
 
    char* jailpath;
259
 
    char* work_dir;
260
 
    char* prog;
261
 
    char* const * args;
262
 
    int uid;
263
 
    gid_t groups[1];
264
 
    int arg_num = 1;
265
 
    int daemon_mode = 0;
266
 
    int unlimited = 0;
267
 
    char canonical_jailpath[PATH_MAX];
268
 
 
269
 
    /* Disallow execution from all users but the whitelisted ones, and root */
270
 
    if (!uid_allowed(getuid()))
271
 
    {
272
 
        fprintf(stderr, "only the web server may execute trampoline\n");
273
 
        exit(1);
274
 
    }
275
 
 
276
 
    /* Args check and usage */
277
 
    if (argc < 5)
278
 
    {
279
 
        usage(argv[0]);
280
 
    }
281
 
 
282
 
    if (strcmp(argv[arg_num], "-d") == 0)
283
 
    {
284
 
        if (argc < 6)
285
 
        {
286
 
            usage(argv[0]);
287
 
        }
288
 
        daemon_mode = 1;
289
 
        arg_num++;
290
 
    }
291
 
 
292
 
    if (strcmp(argv[arg_num], "-u") == 0)
293
 
    {
294
 
        if (argc < 6)
295
 
        {
296
 
            usage(argv[0]);
297
 
        }
298
 
        unlimited = 1;
299
 
        arg_num++;
300
 
    }
301
 
    uid = atoi(argv[arg_num++]);
302
 
    jail_base = argv[arg_num++];
303
 
    jail_src_base = argv[arg_num++];
304
 
    jail_system = argv[arg_num++];
305
 
    jailpath = argv[arg_num++];
306
 
    work_dir = argv[arg_num++];
307
 
    prog = argv[arg_num];
308
 
    args = argv + arg_num;
309
 
 
310
 
    /* Disallow suiding to the root user */
311
 
    if (uid == 0)
312
 
    {
313
 
        fprintf(stderr, "cannot set up a jail as root\n");
314
 
        exit(1);
315
 
    }
316
 
 
317
 
    /* Jail path must be an absolute path,
318
 
     * and it must begin with jail_base.
319
 
     */
320
 
    if (norm(canonical_jailpath, PATH_MAX, jailpath) != 0)
321
 
    {
322
 
        fprintf(stderr, "bad jail path: %s\n", jailpath);
323
 
        exit(1);
324
 
    }
325
 
    if (strncmp(canonical_jailpath, jail_base, strlen(jail_base)))
326
 
    {
327
 
        fprintf(stderr, "bad jail path: %s\n", jailpath);
328
 
        exit(1);
329
 
    }
330
 
 
331
 
    openlog("trampoline", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);
332
 
 
333
 
    #ifdef IVLE_AUFS_JAILS
334
 
    mount_if_needed(jail_src_base, jail_base, jail_system, canonical_jailpath);
335
 
    #endif /* IVLE_AUFS_JAILS */
336
 
 
337
 
    /* chroot into the jail.
338
 
     * Henceforth this process, and its children, cannot see anything above
339
 
     * canoncial_jailpath. */
340
 
    if (chroot(canonical_jailpath))
341
 
    {
342
 
        syslog(LOG_ERR, "chroot to %s failed\n", canonical_jailpath);
343
 
 
344
 
        perror("could not chroot");
345
 
        exit(1);
346
 
    }
347
 
 
348
 
    /* chdir into the specified working directory */
349
 
    if (chdir(work_dir))
350
 
    {
351
 
        perror("could not chdir");
352
 
        exit(1);
353
 
    }
354
 
 
355
 
    /* setuid to the given user ID.
356
 
     * Henceforth we will be running as this user instead of root.
357
 
     */
358
 
    if (setgid(uid))
359
 
    {
360
 
        perror("could not setgid");
361
 
        exit(1);
362
 
    }
363
 
    
364
 
    groups[0] = uid;
365
 
    if (setgroups(1, groups))
366
 
    {
367
 
        perror("could not setgroups");
368
 
        exit(1);
369
 
    }
370
 
 
371
 
    if (setuid(uid))
372
 
    {
373
 
        perror("could not setuid");
374
 
        exit(1);
375
 
    }
376
 
 
377
 
    /* set user resource limits */
378
 
    if (!unlimited)
379
 
    {
380
 
        struct rlimit l;
381
 
        /* Process adress space in memory */
382
 
        l.rlim_cur = 448 * 1024 * 1024; /* 512MiB - 64MiB */
383
 
        l.rlim_max = 512 * 1024 * 1024; /* 512MiB */
384
 
        if (setrlimit(RLIMIT_AS, &l))
385
 
        {
386
 
            perror("could not setrlimit/RLIMIT_AS");
387
 
            exit(1);
388
 
        }
389
 
        
390
 
        /* Process data segment in memory
391
 
         * Note: This requires a kernel patch to work correctly otherwise it is  
392
 
         * ineffective (thus you are only limited by RLIMIT_AS)
393
 
         */
394
 
        l.rlim_cur = 448 * 1024 * 1024; /* 512MiB - 64MiB */
395
 
        l.rlim_max = 512 * 1024 * 1024; /* 512MiB */
396
 
        if (setrlimit(RLIMIT_DATA, &l))
397
 
        {
398
 
            perror("could not setrlimit/RLIMIT_DATA");
399
 
            exit(1);
400
 
        }
401
 
 
402
 
        /* Core */
403
 
        l.rlim_cur = 0; /* No core files */
404
 
        l.rlim_max = 0; /* No core files */
405
 
        if (setrlimit(RLIMIT_CORE, &l))
406
 
        {
407
 
            perror("could not setrlimit/RLIMIT_CORE");
408
 
            exit(1);
409
 
        }
410
 
 
411
 
        /* CPU */
412
 
        l.rlim_cur = 25; /* 25 Seconds */
413
 
        l.rlim_max = 30; /* 30 Seconds */
414
 
        if (setrlimit(RLIMIT_CPU, &l))
415
 
        {
416
 
            perror("could not setrlimit/RLIMIT_CPU");
417
 
            exit(1);
418
 
        }
419
 
 
420
 
        /* File Size */
421
 
        l.rlim_cur = 64 * 1024 * 1024; /* 64MiB */
422
 
        l.rlim_max = 72 * 1024 * 1024; /* 72MiB */
423
 
        if (setrlimit(RLIMIT_FSIZE, &l))
424
 
        {
425
 
            perror("could not setrlimit/RLIMIT_FSIZE");
426
 
            exit(1);
427
 
        }
428
 
        
429
 
        /* Number of Processes */
430
 
        l.rlim_cur = 50;
431
 
        l.rlim_max = 50;
432
 
        if (setrlimit(RLIMIT_NPROC, &l))
433
 
        {
434
 
            perror("could not setrlimit/RLIMIT_NPROC");
435
 
            exit(1);
436
 
        }
437
 
    }
438
 
 
439
 
    /* Remove any signal handler masks so we can send signals to the child */
440
 
    if(unmask_signals())
441
 
    {
442
 
        perror("could not unmask signals");
443
 
        exit(1);
444
 
    }
445
 
 
446
 
    /* If everything was OK daemonize (if required) */
447
 
    if (daemon_mode)
448
 
    {
449
 
        daemonize();
450
 
    }
451
 
 
452
 
    /* exec (replace this process with the a new instance of the target
453
 
     * program). Pass along all the arguments.
454
 
     * Note that for script execution, the "program" will be the interpreter,
455
 
     * and the first argument will be the script. */
456
 
    execv(prog, args);
457
 
 
458
 
    /* nb exec won't return unless there was an error */
459
 
    syslog(LOG_ERR, "exec of %s in %s failed", prog, canonical_jailpath);
460
 
 
461
 
    perror("could not exec");
462
 
    closelog();
463
 
    return 1;
464
 
}