~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
 
 * 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
 
}