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

« back to all changes in this revision

Viewing changes to bin/trampoline/trampoline.c

Dispatch now generates an index for each plugin type, allowing plugins to
be written which are aware of other plugins, and other plugin types.

All view plugins now subclass from ivle.webapp.base.plugins.ViewPlugin,
as opposed to subclassing BasePlugin directly. This will allow us to
easily re-write console as an OverlayPlugin, and allow future new
plugins types to be created.

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
 * Usage: trampoline uid jail-path working-path program [args...]
 
31
 * Must run as root. Will safely setuid to the supplied uid, checking that it
 
32
 * is not root. Recommended that the file is set up as follows:
 
33
 *  sudo chown root:root trampoline; sudo chroot +s trampoline
 
34
 */
 
35
 
 
36
#define _XOPEN_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 <limits.h>
 
49
#include <signal.h>
 
50
 
 
51
/* conf.h is admin-configured by the setup process.
 
52
 * It defines jail_base.
 
53
 */
 
54
#include "conf.h"
 
55
 
 
56
#include "norm.h"
 
57
 
 
58
/* Returns TRUE if the given uid is allowed to execute trampoline.
 
59
 * Only root or the web server should be allowed to execute.
 
60
 * This is determined by the whitelist allowed_uids in conf.h.
 
61
 */
 
62
int uid_allowed(int uid)
 
63
{
 
64
    int i;
 
65
    /* root is always allowed to execute trampoline */
 
66
    if (uid == 0)
 
67
        return 1;
 
68
    /* loop over all allowed_uids */
 
69
    for (i=(sizeof(allowed_uids)/sizeof(*allowed_uids))-1; i>=0; i--)
 
70
    {
 
71
        if (allowed_uids[i] == uid)
 
72
            return 1;
 
73
    }
 
74
    /* default to disallowing */
 
75
    return 0;
 
76
}
 
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
 
 
218
int main(int argc, char* const argv[])
 
219
{
 
220
    char* jailpath;
 
221
    char* work_dir;
 
222
    char* prog;
 
223
    char* const * args;
 
224
    int uid;
 
225
    int arg_num = 1;
 
226
    int daemon_mode = 0;
 
227
    int unlimited = 0;
 
228
    char canonical_jailpath[PATH_MAX];
 
229
 
 
230
    /* Disallow execution from all users but the whitelisted ones, and root */
 
231
    if (!uid_allowed(getuid()))
 
232
    {
 
233
        fprintf(stderr, "only the web server may execute trampoline\n");
 
234
        exit(1);
 
235
    }
 
236
 
 
237
    /* Args check and usage */
 
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;
 
267
 
 
268
    /* Disallow suiding to the root user */
 
269
    if (uid == 0)
 
270
    {
 
271
        fprintf(stderr, "cannot set up a jail as root\n");
 
272
        exit(1);
 
273
    }
 
274
 
 
275
    /* Jail path must be an absolute path,
 
276
     * and it must begin with jail_base.
 
277
     */
 
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 */
 
294
 
 
295
    /* chroot into the jail.
 
296
     * Henceforth this process, and its children, cannot see anything above
 
297
     * canoncial_jailpath. */
 
298
    if (chroot(canonical_jailpath))
 
299
    {
 
300
        syslog(LOG_ERR, "chroot to %s failed\n", canonical_jailpath);
 
301
 
 
302
        perror("could not chroot");
 
303
        exit(1);
 
304
    }
 
305
 
 
306
    /* chdir into the specified working directory */
 
307
    if (chdir(work_dir))
 
308
    {
 
309
        perror("could not chdir");
 
310
        exit(1);
 
311
    }
 
312
 
 
313
    /* setuid to the given user ID.
 
314
     * Henceforth we will be running as this user instead of root.
 
315
     */
 
316
    if (setgid(uid))
 
317
    {
 
318
        perror("could not setgid");
 
319
        exit(1);
 
320
    }
 
321
 
 
322
    if (setuid(uid))
 
323
    {
 
324
        perror("could not setuid");
 
325
        exit(1);
 
326
    }
 
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
 
 
394
    /* exec (replace this process with the a new instance of the target
 
395
     * program). Pass along all the arguments.
 
396
     * Note that for script execution, the "program" will be the interpreter,
 
397
     * and the first argument will be the script. */
 
398
    execv(prog, args);
 
399
 
 
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
 
 
403
    perror("could not exec");
 
404
    closelog();
 
405
    return 1;
 
406
}