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

« back to all changes in this revision

Viewing changes to trampoline/trampoline.c

Added module ivle.config, which takes care of some work interfacing with
    configobj, including searching for the file and opening the object.
ivle.conf.conf now uses this instead of having its own search.

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 data segment in memory */
322
 
        l.rlim_cur = 192 * 1024 * 1024; /* 192MiB */
323
 
        l.rlim_max = 256 * 1024 * 1024; /* 256MiB */
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
 
}