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

« back to all changes in this revision

Viewing changes to bin/trampoline/trampoline.c

  • Committer: William Grant
  • Date: 2009-02-23 23:47:02 UTC
  • mfrom: (1099.1.211 new-dispatch)
  • Revision ID: grantw@unimelb.edu.au-20090223234702-db4b1llly46ignwo
Merge from lp:~ivle-dev/ivle/new-dispatch.

Pretty much everything changes. Reread the setup docs. Backup your databases.
Every file is now in a different installed location, the configuration system
is rewritten, the dispatch system is rewritten, URLs are different, the
database is different, worksheets and exercises are no longer on the
filesystem, we use a templating engine, jail service protocols are rewritten,
we don't repeat ourselves, we have authorization rewritten, phpBB is gone,
and probably lots of other things that I cannot remember.

This is certainly the biggest commit I have ever made, and hopefully
the largest I ever will.

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> <base> <src> <system> <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 *jail_src_base, const char *jail_base,
 
142
               const char *jailpath)
 
143
{
 
144
    char* src;
 
145
    int srclen;
 
146
    int dstlen;
 
147
 
 
148
    srclen = strlen(jail_src_base);
 
149
    dstlen = strlen(jail_base);
 
150
    
 
151
    src = die_if_null(malloc(strlen(jailpath) + (srclen - dstlen) + 1));
 
152
    strcpy(src, jail_src_base);
 
153
    strcat(src, jailpath+dstlen);
 
154
 
 
155
    return src;
 
156
}
 
157
 
 
158
/* Check for the validity of a jail in the given path, mounting it if it looks
 
159
 * empty.
 
160
 * TODO: Updating /etc/mtab would be nice. */
 
161
void mount_if_needed(const char *jail_src_base, const char *jail_base,
 
162
                     const char *jail_system, const char *jailpath)
 
163
{
 
164
    char *jailsrc;
 
165
    char *jaillib;
 
166
    char *mountdata;
 
167
 
 
168
    /* Check if there is something useful in the jail. If not, it's probably
 
169
     * not mounted. */
 
170
    jaillib = die_if_null(malloc(strlen(jailpath) + 5));
 
171
    sprintf(jaillib, "%s/lib", jailpath);
 
172
 
 
173
    if (access(jaillib, F_OK))
 
174
    {
 
175
        /* No /lib? Mustn't be mounted. Mount it, creating the dir if needed. */
 
176
        if (access(jailpath, F_OK))
 
177
        {
 
178
             if(mkdir(jailpath, 0755))
 
179
             {
 
180
                 syslog(LOG_ERR, "could not create mountpoint %s\n", jailpath);
 
181
                 perror("could not create jail mountpoint");
 
182
                 exit(1);
 
183
             }
 
184
             syslog(LOG_NOTICE, "created mountpoint %s\n", jailpath);
 
185
        }
 
186
       
 
187
        jailsrc = jail_src(jail_src_base, jail_base, jailpath);
 
188
        mountdata = die_if_null(malloc(3 + strlen(jailsrc) + 4 + strlen(jail_system) + 3 + 1));
 
189
        sprintf(mountdata, "br:%s=rw:%s=ro", jailsrc, jail_system);
 
190
        if (mount("none", jailpath, "aufs", 0, mountdata))
 
191
        {
 
192
            syslog(LOG_ERR, "could not mount %s\n", jailpath);
 
193
            perror("could not mount");
 
194
            exit(1);
 
195
        } 
 
196
 
 
197
        syslog(LOG_INFO, "mounted %s\n", jailpath);
 
198
 
 
199
        free(jailsrc);
 
200
        free(mountdata);
 
201
    }
 
202
 
 
203
    free(jaillib);
 
204
}
 
205
#endif /* IVLE_AUFS_JAILS */
 
206
 
 
207
/* Unsets any signal mask applied by the parent process */
 
208
int unmask_signals(void)
 
209
{
 
210
    int result;
 
211
    sigset_t* sigset;
 
212
    sigset = die_if_null(malloc(sizeof(sigset_t)));
 
213
    sigemptyset(sigset);
 
214
    result = sigprocmask(SIG_SETMASK, sigset, NULL);
 
215
    free(sigset);
 
216
    printf("%d", result);
 
217
    return result;
 
218
}
 
219
 
 
220
int main(int argc, char* const argv[])
 
221
{
 
222
    char* jail_base;
 
223
    char* jail_src_base;
 
224
    char* jail_system;
 
225
    char* jailpath;
 
226
    char* work_dir;
 
227
    char* prog;
 
228
    char* const * args;
 
229
    int uid;
 
230
    int arg_num = 1;
 
231
    int daemon_mode = 0;
 
232
    int unlimited = 0;
 
233
    char canonical_jailpath[PATH_MAX];
 
234
 
 
235
    /* Disallow execution from all users but the whitelisted ones, and root */
 
236
    if (!uid_allowed(getuid()))
 
237
    {
 
238
        fprintf(stderr, "only the web server may execute trampoline\n");
 
239
        exit(1);
 
240
    }
 
241
 
 
242
    /* Args check and usage */
 
243
    if (argc < 5)
 
244
    {
 
245
        usage(argv[0]);
 
246
    }
 
247
 
 
248
    if (strcmp(argv[arg_num], "-d") == 0)
 
249
    {
 
250
        if (argc < 6)
 
251
        {
 
252
            usage(argv[0]);
 
253
        }
 
254
        daemon_mode = 1;
 
255
        arg_num++;
 
256
    }
 
257
 
 
258
    if (strcmp(argv[arg_num], "-u") == 0)
 
259
    {
 
260
        if (argc < 6)
 
261
        {
 
262
            usage(argv[0]);
 
263
        }
 
264
        unlimited = 1;
 
265
        arg_num++;
 
266
    }
 
267
    uid = atoi(argv[arg_num++]);
 
268
    jail_base = argv[arg_num++];
 
269
    jail_src_base = argv[arg_num++];
 
270
    jail_system = argv[arg_num++];
 
271
    jailpath = argv[arg_num++];
 
272
    work_dir = argv[arg_num++];
 
273
    prog = argv[arg_num];
 
274
    args = argv + arg_num;
 
275
 
 
276
    /* Disallow suiding to the root user */
 
277
    if (uid == 0)
 
278
    {
 
279
        fprintf(stderr, "cannot set up a jail as root\n");
 
280
        exit(1);
 
281
    }
 
282
 
 
283
    /* Jail path must be an absolute path,
 
284
     * and it must begin with jail_base.
 
285
     */
 
286
    if (norm(canonical_jailpath, PATH_MAX, jailpath) != 0)
 
287
    {
 
288
        fprintf(stderr, "bad jail path: %s\n", jailpath);
 
289
        exit(1);
 
290
    }
 
291
    if (strncmp(canonical_jailpath, jail_base, strlen(jail_base)))
 
292
    {
 
293
        fprintf(stderr, "bad jail path: %s\n", jailpath);
 
294
        exit(1);
 
295
    }
 
296
 
 
297
    openlog("trampoline", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);
 
298
 
 
299
    #ifdef IVLE_AUFS_JAILS
 
300
    mount_if_needed(jail_src_base, jail_base, jail_system, canonical_jailpath);
 
301
    #endif /* IVLE_AUFS_JAILS */
 
302
 
 
303
    /* chroot into the jail.
 
304
     * Henceforth this process, and its children, cannot see anything above
 
305
     * canoncial_jailpath. */
 
306
    if (chroot(canonical_jailpath))
 
307
    {
 
308
        syslog(LOG_ERR, "chroot to %s failed\n", canonical_jailpath);
 
309
 
 
310
        perror("could not chroot");
 
311
        exit(1);
 
312
    }
 
313
 
 
314
    /* chdir into the specified working directory */
 
315
    if (chdir(work_dir))
 
316
    {
 
317
        perror("could not chdir");
 
318
        exit(1);
 
319
    }
 
320
 
 
321
    /* setuid to the given user ID.
 
322
     * Henceforth we will be running as this user instead of root.
 
323
     */
 
324
    if (setgid(uid))
 
325
    {
 
326
        perror("could not setgid");
 
327
        exit(1);
 
328
    }
 
329
 
 
330
    if (setuid(uid))
 
331
    {
 
332
        perror("could not setuid");
 
333
        exit(1);
 
334
    }
 
335
 
 
336
    /* set user resource limits */
 
337
    if (!unlimited)
 
338
    {
 
339
        struct rlimit l;
 
340
        /* Process adress space in memory */
 
341
        l.rlim_cur = 192 * 1024 * 1024; /* 192MiB */
 
342
        l.rlim_max = 256 * 1024 * 1024; /* 256MiB */
 
343
        if (setrlimit(RLIMIT_AS, &l))
 
344
        {
 
345
            perror("could not setrlimit/RLIMIT_AS");
 
346
            exit(1);
 
347
        }
 
348
        
 
349
        /* Process data segment in memory
 
350
         * Note: This requires a kernel patch to work correctly otherwise it is  
 
351
         * ineffective (thus you are only limited by RLIMIT_AS)
 
352
         */
 
353
        l.rlim_cur = 192 * 1024 * 1024; /* 192MiB */
 
354
        l.rlim_max = 256 * 1024 * 1024; /* 256MiB */
 
355
        if (setrlimit(RLIMIT_DATA, &l))
 
356
        {
 
357
            perror("could not setrlimit/RLIMIT_DATA");
 
358
            exit(1);
 
359
        }
 
360
 
 
361
        /* Core */
 
362
        l.rlim_cur = 0; /* No core files */
 
363
        l.rlim_max = 0; /* No core files */
 
364
        if (setrlimit(RLIMIT_CORE, &l))
 
365
        {
 
366
            perror("could not setrlimit/RLIMIT_CORE");
 
367
            exit(1);
 
368
        }
 
369
 
 
370
        /* CPU */
 
371
        l.rlim_cur = 25; /* 25 Seconds */
 
372
        l.rlim_max = 30; /* 30 Seconds */
 
373
        if (setrlimit(RLIMIT_CPU, &l))
 
374
        {
 
375
            perror("could not setrlimit/RLIMIT_CPU");
 
376
            exit(1);
 
377
        }
 
378
 
 
379
        /* File Size */
 
380
        l.rlim_cur = 64 * 1024 * 1024; /* 64MiB */
 
381
        l.rlim_max = 72 * 1024 * 1024; /* 72MiB */
 
382
if (setrlimit(RLIMIT_FSIZE, &l))
 
383
        {
 
384
            perror("could not setrlimit/RLIMIT_FSIZE");
 
385
            exit(1);
 
386
        }
 
387
    }
 
388
 
 
389
    /* Remove any signal handler masks so we can send signals to the child */
 
390
    if(unmask_signals())
 
391
    {
 
392
        perror("could not unmask signals");
 
393
        exit(1);
 
394
    }
 
395
 
 
396
    /* If everything was OK daemonize (if required) */
 
397
    if (daemon_mode)
 
398
    {
 
399
        daemonize();
 
400
    }
 
401
 
 
402
    /* exec (replace this process with the a new instance of the target
 
403
     * program). Pass along all the arguments.
 
404
     * Note that for script execution, the "program" will be the interpreter,
 
405
     * and the first argument will be the script. */
 
406
    execv(prog, args);
 
407
 
 
408
    /* nb exec won't return unless there was an error */
 
409
    syslog(LOG_ERR, "exec of %s in %s failed", prog, canonical_jailpath);
 
410
 
 
411
    perror("could not exec");
 
412
    closelog();
 
413
    return 1;
 
414
}