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

« back to all changes in this revision

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