varnish-cache/bin/varnishd/mgt/mgt_jail_unix.c
1
/*-
2
 * Copyright (c) 2006-2015 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
 * SUCH DAMAGE.
27
 *
28
 * Jailing processes the UNIX way, using setuid(2) etc.
29
 */
30
31
#include "config.h"
32
33
#include <errno.h>
34
#include <fcntl.h>
35
#include <grp.h>
36
#include <pwd.h>
37
#include <stdio.h>
38
#include <stdlib.h>
39
#include <string.h>
40
#include <unistd.h>
41
#include <sys/stat.h>
42
43
#include "mgt/mgt.h"
44
#include "common/heritage.h"
45
46
#ifdef __linux__
47
#include <sys/prctl.h>
48
#endif
49
50
static gid_t vju_mgt_gid;
51
static uid_t vju_uid;
52
static gid_t vju_gid;
53
static const char *vju_user;
54
55
static uid_t vju_wrkuid;
56
static gid_t vju_wrkgid;
57
static const char *vju_wrkuser;
58
59
static gid_t vju_cc_gid;
60
static int vju_cc_gid_set;
61
62
#ifndef VARNISH_USER
63
#define VARNISH_USER "varnish"
64
#endif
65
66
#ifndef VCACHE_USER
67
#define VCACHE_USER "vcache"
68
#endif
69
70
#ifndef NGID
71
#define NGID 2000
72
#endif
73
74
static int
75 66
vju_getuid(const char *arg)
76
{
77
        struct passwd *pw;
78
79 66
        pw = getpwnam(arg);
80 66
        if (pw != NULL) {
81 63
                vju_user = strdup(arg);
82 63
                AN(vju_user);
83 63
                vju_uid = pw->pw_uid;
84 63
                vju_gid = pw->pw_gid;
85
        }
86 66
        endpwent();
87 66
        return (pw == NULL ? -1 : 0);
88
}
89
90
static int
91 66
vju_getwrkuid(const char *arg)
92
{
93
        struct passwd *pw;
94
95 66
        pw = getpwnam(arg);
96 66
        if (pw != NULL) {
97 63
                vju_wrkuser = strdup(arg);
98 63
                AN(vju_wrkuser);
99 63
                vju_wrkuid = pw->pw_uid;
100 63
                vju_wrkgid = pw->pw_gid;
101
        }
102 66
        endpwent();
103 66
        return (pw == NULL ? -1 : 0);
104
}
105
106
static int
107 15
vju_getccgid(const char *arg)
108
{
109
        struct group *gr;
110
111 15
        gr = getgrnam(arg);
112 15
        if (gr != NULL) {
113 12
                vju_cc_gid_set = 1;
114 12
                vju_cc_gid = gr->gr_gid;
115
        }
116 15
        endgrent();
117 15
        return (gr == NULL ? -1 : 0);
118
}
119
120
/**********************************************************************
121
 */
122
123
static int v_matchproto_(jail_init_f)
124 2310
vju_init(char **args)
125
{
126 2310
        if (args == NULL) {
127
                /* Autoconfig */
128 2283
                if (geteuid() != 0)
129 2235
                        return (1);
130 48
                if (vju_getuid(VARNISH_USER))
131 0
                        return (1);
132
        } else {
133
134 27
                if (geteuid() != 0)
135 0
                        ARGV_ERR("Unix Jail: Must be root.\n");
136
137 57
                for (;*args != NULL; args++) {
138 42
                        if (!strncmp(*args, "user=", 5)) {
139 15
                                if (vju_getuid((*args) + 5))
140 3
                                        ARGV_ERR(
141
                                            "Unix jail: %s user not found.\n",
142
                                            (*args) + 5);
143 12
                                continue;
144
                        }
145 27
                        if (!strncmp(*args, "workuser=", 9)) {
146 9
                                if (vju_getwrkuid((*args) + 9))
147 3
                                        ARGV_ERR(
148
                                            "Unix jail: %s user not found.\n",
149
                                            (*args) + 9);
150 6
                                continue;
151
                        }
152 18
                        if (!strncmp(*args, "ccgroup=", 8)) {
153 15
                                if (vju_getccgid((*args) + 8))
154 3
                                        ARGV_ERR(
155
                                            "Unix jail: %s group not found.\n",
156
                                            (*args) + 8);
157 12
                                continue;
158
                        }
159 3
                        ARGV_ERR("Unix jail: unknown sub-argument '%s'\n",
160
                            *args);
161
                }
162
163 15
                if (vju_user == NULL && vju_getuid(VARNISH_USER))
164 0
                        ARGV_ERR("Unix jail: %s user not found.\n",
165
                            VARNISH_USER);
166
        }
167
168 63
        AN(vju_user);
169
170 63
        vju_mgt_gid = getgid();
171
172 63
        if (vju_wrkuser == NULL && vju_getwrkuid(VCACHE_USER)) {
173 0
                vju_wrkuid = vju_uid;
174 0
                vju_wrkgid = vju_gid;
175
        }
176
177 63
        if (vju_wrkuser != NULL && vju_wrkgid != vju_gid)
178 3
                ARGV_ERR("Unix jail: user %s and %s have "
179
                    "different login groups\n", vju_user, vju_wrkuser);
180
181
        /* Do an explicit JAIL_MASTER_LOW */
182 60
        AZ(setegid(vju_gid));
183 60
        AZ(seteuid(vju_uid));
184 60
        return (0);
185
}
186
187
static void v_matchproto_(jail_master_f)
188 1245
vju_master(enum jail_master_e jme)
189
{
190 1245
        if (jme == JAIL_MASTER_LOW) {
191 621
                AZ(setegid(vju_gid));
192 621
                AZ(seteuid(vju_uid));
193
        } else {
194 624
                AZ(seteuid(0));
195 624
                AZ(setegid(vju_mgt_gid));
196
        }
197 1245
}
198
199
static void v_matchproto_(jail_subproc_f)
200 93
vju_subproc(enum jail_subproc_e jse)
201
{
202
        int i;
203
        gid_t gid_list[NGID];
204
205 93
        AZ(seteuid(0));
206 93
        if (vju_wrkuser != NULL &&
207 93
            (jse == JAIL_SUBPROC_VCLLOAD || jse == JAIL_SUBPROC_WORKER)) {
208 0
                AZ(setgid(vju_wrkgid));
209 0
                AZ(initgroups(vju_wrkuser, vju_wrkgid));
210
        } else {
211 93
                AZ(setgid(vju_gid));
212 93
                AZ(initgroups(vju_user, vju_gid));
213
        }
214
215 93
        if (jse == JAIL_SUBPROC_CC && vju_cc_gid_set) {
216
                /* Add the optional extra group for the C-compiler access */
217 12
                i = getgroups(NGID, gid_list);
218 12
                assert(i >= 0);
219 12
                gid_list[i++] = vju_cc_gid;
220 12
                AZ(setgroups(i, gid_list));
221
        }
222
223 93
        if (vju_wrkuser != NULL &&
224 93
            (jse == JAIL_SUBPROC_VCLLOAD || jse == JAIL_SUBPROC_WORKER)) {
225 0
                AZ(setuid(vju_wrkuid));
226
        } else {
227 93
                AZ(setuid(vju_uid));
228
        }
229
230
#ifdef __linux__
231
        /*
232
         * On linux mucking about with uid/gid disables core-dumps,
233
         * reenable them again.
234
         */
235
        if (prctl(PR_SET_DUMPABLE, 1) != 0) {
236
                MGT_Complain(C_INFO,
237
                    "Could not set dumpable bit.  Core dumps turned off");
238
        }
239
#endif
240 93
}
241
242
static int v_matchproto_(jail_make_dir_f)
243 96
vju_make_subdir(const char *dname, const char *what, struct vsb *vsb)
244
{
245
        int e;
246
247 96
        AN(dname);
248 96
        AN(what);
249 96
        AZ(seteuid(0));
250
251 96
        if (mkdir(dname, 0755) < 0 && errno != EEXIST) {
252 0
                e = errno;
253 0
                if (vsb != NULL) {
254 0
                        VSB_printf(vsb,
255
                            "Cannot create %s directory '%s': %s\n",
256
                            what, dname, strerror(e));
257
                } else {
258 0
                        MGT_Complain(C_ERR,
259
                            "Cannot create %s directory '%s': %s",
260
                            what, dname, strerror(e));
261
                }
262 0
                return (1);
263
        }
264 96
        AZ(chown(dname, vju_uid, vju_gid));
265 96
        AZ(seteuid(vju_uid));
266 96
        return (0);
267
}
268
269
static int v_matchproto_(jail_make_dir_f)
270 48
vju_make_workdir(const char *dname, const char *what, struct vsb *vsb)
271
{
272
273 48
        AN(dname);
274 48
        AZ(what);
275 48
        AZ(vsb);
276 48
        AZ(seteuid(0));
277
278 48
        if (mkdir(dname, 0755) < 0 && errno != EEXIST) {
279 0
                MGT_Complain(C_ERR, "Cannot create working directory '%s': %s",
280 0
                    dname, strerror(errno));
281 0
                return (1);
282
        }
283 48
        AZ(chown(dname, -1, vju_gid));
284 48
        AZ(seteuid(vju_uid));
285 48
        return (0);
286
}
287
288
static void v_matchproto_(jail_fixfd_f)
289 96
vju_fixfd(int fd, enum jail_fixfd_e what)
290
{
291
        /* Called under JAIL_MASTER_FILE */
292
293 96
        switch (what) {
294
        case JAIL_FIXFD_FILE:
295 3
                AZ(fchmod(fd, 0750));
296 3
                AZ(fchown(fd, vju_wrkuid, vju_wrkgid));
297 3
                break;
298
        case JAIL_FIXFD_VSMMGT:
299 42
                AZ(fchmod(fd, 0750));
300 42
                AZ(fchown(fd, vju_uid, vju_gid));
301 42
                break;
302
        case JAIL_FIXFD_VSMWRK:
303 51
                AZ(fchmod(fd, 0750));
304 51
                AZ(fchown(fd, vju_wrkuid, vju_wrkgid));
305 51
                break;
306
        default:
307 0
                WRONG("Ain't Fixin'");
308
        }
309 96
}
310
311
const struct jail_tech jail_tech_unix = {
312
        .magic =        JAIL_TECH_MAGIC,
313
        .name =         "unix",
314
        .init =         vju_init,
315
        .master =       vju_master,
316
        .make_subdir =  vju_make_subdir,
317
        .make_workdir = vju_make_workdir,
318
        .fixfd =        vju_fixfd,
319
        .subproc =      vju_subproc,
320
};