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

« back to all changes in this revision

Viewing changes to bin/trampoline/trampoline.c

  • Committer: dcoles
  • Date: 2008-07-03 04:20:54 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:803
Setup: Modularised setup.py so it is now no longer over 1000 lines. This should 
allow us to get in there and tidy up each module much easier. Also removed 
updatejails since this functionality seems to be duplicated with remakeuser.py 
and remakealluser.py scripts.

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