Modify kern/makesyscall.sh to prefix all kernel system call procedures
[dragonfly.git] / sys / kern / kern_jail.c
CommitLineData
984263bc
MD
1/*
2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
5 * can do whatever you want with this stuff. If we meet some day, and you think
6 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
7 * ----------------------------------------------------------------------------
8 *
9 * $FreeBSD: src/sys/kern/kern_jail.c,v 1.6.2.3 2001/08/17 01:00:26 rwatson Exp $
753fd850 10 * $DragonFly: src/sys/kern/kern_jail.c,v 1.11 2006/06/05 07:26:10 dillon Exp $
984263bc
MD
11 *
12 */
13
14#include <sys/param.h>
15#include <sys/types.h>
16#include <sys/kernel.h>
b40e316c 17#include <sys/kinfo.h>
984263bc
MD
18#include <sys/systm.h>
19#include <sys/errno.h>
20#include <sys/sysproto.h>
21#include <sys/malloc.h>
b40e316c
JS
22#include <sys/nlookup.h>
23#include <sys/namecache.h>
984263bc
MD
24#include <sys/proc.h>
25#include <sys/jail.h>
26#include <sys/socket.h>
27#include <sys/sysctl.h>
b40e316c 28#include <sys/kern_syscall.h>
984263bc
MD
29#include <net/if.h>
30#include <netinet/in.h>
31
b40e316c
JS
32static struct prison *prison_find(int);
33
984263bc
MD
34MALLOC_DEFINE(M_PRISON, "prison", "Prison structures");
35
36SYSCTL_NODE(, OID_AUTO, jail, CTLFLAG_RW, 0,
37 "Jail rules");
38
39int jail_set_hostname_allowed = 1;
40SYSCTL_INT(_jail, OID_AUTO, set_hostname_allowed, CTLFLAG_RW,
41 &jail_set_hostname_allowed, 0,
42 "Processes in jail can set their hostnames");
43
44int jail_socket_unixiproute_only = 1;
45SYSCTL_INT(_jail, OID_AUTO, socket_unixiproute_only, CTLFLAG_RW,
46 &jail_socket_unixiproute_only, 0,
47 "Processes in jail are limited to creating UNIX/IPv4/route sockets only");
48
49int jail_sysvipc_allowed = 0;
50SYSCTL_INT(_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW,
51 &jail_sysvipc_allowed, 0,
52 "Processes in jail can use System V IPC primitives");
53
b70df062
MD
54int jail_chflags_allowed = 0;
55SYSCTL_INT(_jail, OID_AUTO, chflags_allowed, CTLFLAG_RW,
56 &jail_chflags_allowed, 0,
57 "Process in jail can set chflags(1)");
58
b40e316c
JS
59int lastprid = 0;
60int prisoncount = 0;
61
62LIST_HEAD(prisonlist, prison);
63struct prisonlist allprison = LIST_HEAD_INITIALIZER(&allprison);
64
65static int
66kern_jail_attach(int jid)
67{
68 struct proc *p = curthread->td_proc;
69 struct prison *pr;
70 int error;
71
72 pr = prison_find(jid);
73 if (pr == NULL)
74 return(EINVAL);
75
76 error = kern_chroot(pr->pr_root);
77 if (error)
78 return(error);
79
80 prison_hold(pr);
81 cratom(&p->p_ucred);
82 p->p_ucred->cr_prison = pr;
83 p->p_flag |= P_JAILED;
84
85 return(0);
86}
87
41c20dac
MD
88/*
89 * jail()
90 *
91 * jail_args(syscallarg(struct jail *) jail)
92 */
984263bc 93int
753fd850 94sys_jail(struct jail_args *uap)
984263bc 95{
b40e316c 96 struct prison *pr, *tpr;
984263bc 97 struct jail j;
dadab5e9 98 struct thread *td = curthread;
b40e316c
JS
99 int error, tryprid;
100 struct nlookupdata nd;
984263bc 101
dadab5e9 102 error = suser(td);
984263bc 103 if (error)
b40e316c 104 return(error);
984263bc
MD
105 error = copyin(uap->jail, &j, sizeof j);
106 if (error)
b40e316c 107 return(error);
984263bc 108 if (j.version != 0)
b40e316c
JS
109 return(EINVAL);
110 MALLOC(pr, struct prison *, sizeof *pr , M_PRISON, M_WAITOK | M_ZERO);
111
984263bc 112 error = copyinstr(j.hostname, &pr->pr_host, sizeof pr->pr_host, 0);
e713d50d 113 if (error)
984263bc 114 goto bail;
b40e316c
JS
115 error = nlookup_init(&nd, j.path, UIO_USERSPACE, NLC_FOLLOW);
116 if (error)
117 goto nlookup_init_clean;
118 error = nlookup(&nd);
119 if (error)
120 goto nlookup_init_clean;
121 pr->pr_root = cache_hold(nd.nl_ncp);
122
984263bc 123 pr->pr_ip = j.ip_number;
dbc282df 124 varsymset_init(&pr->pr_varsymset, NULL);
984263bc 125
b40e316c
JS
126 tryprid = lastprid + 1;
127 if (tryprid == JAIL_MAX)
128 tryprid = 1;
129next:
130 LIST_FOREACH(tpr, &allprison, pr_list) {
131 if (tpr->pr_id != tryprid)
132 continue;
133 tryprid++;
134 if (tryprid == JAIL_MAX) {
135 error = EAGAIN;
136 goto varsym_clean;
137 }
138 goto next;
139 }
140 pr->pr_id = lastprid = tryprid;
141 LIST_INSERT_HEAD(&allprison, pr, pr_list);
142 prisoncount++;
143
144 error = kern_jail_attach(pr->pr_id);
984263bc 145 if (error)
b40e316c 146 goto jail_attach_clean;
984263bc 147
b40e316c 148 nlookup_done(&nd);
984263bc
MD
149 return (0);
150
b40e316c
JS
151jail_attach_clean:
152 LIST_REMOVE(pr, pr_list);
153varsym_clean:
154 varsymset_clean(&pr->pr_varsymset);
155nlookup_init_clean:
156 nlookup_done(&nd);
984263bc
MD
157bail:
158 FREE(pr, M_PRISON);
b40e316c
JS
159 return(error);
160}
161
162/*
163 * int jail_attach(int jid);
164 */
165int
753fd850 166sys_jail_attach(struct jail_attach_args *uap)
b40e316c
JS
167{
168 struct thread *td = curthread;
169 int error;
170
171 error = suser(td);
172 if (error)
173 return(error);
174
175 return(kern_jail_attach(uap->jid));
984263bc
MD
176}
177
178int
dadab5e9 179prison_ip(struct thread *td, int flag, u_int32_t *ip)
984263bc
MD
180{
181 u_int32_t tmp;
41c20dac 182 struct prison *pr;
984263bc 183
dadab5e9
MD
184 if (td->td_proc == NULL)
185 return (0);
186 if ((pr = td->td_proc->p_ucred->cr_prison) == NULL)
984263bc 187 return (0);
e713d50d 188 if (flag)
984263bc
MD
189 tmp = *ip;
190 else
191 tmp = ntohl(*ip);
192 if (tmp == INADDR_ANY) {
e713d50d 193 if (flag)
41c20dac 194 *ip = pr->pr_ip;
984263bc 195 else
41c20dac 196 *ip = htonl(pr->pr_ip);
984263bc
MD
197 return (0);
198 }
199 if (tmp == INADDR_LOOPBACK) {
200 if (flag)
41c20dac 201 *ip = pr->pr_ip;
984263bc 202 else
41c20dac 203 *ip = htonl(pr->pr_ip);
984263bc
MD
204 return (0);
205 }
41c20dac 206 if (pr->pr_ip != tmp)
984263bc
MD
207 return (1);
208 return (0);
209}
210
211void
dadab5e9 212prison_remote_ip(struct thread *td, int flag, u_int32_t *ip)
984263bc
MD
213{
214 u_int32_t tmp;
41c20dac 215 struct prison *pr;
984263bc 216
dadab5e9 217 if (td == NULL || td->td_proc == NULL)
41c20dac 218 return;
dadab5e9 219 if ((pr = td->td_proc->p_ucred->cr_prison) == NULL)
984263bc
MD
220 return;
221 if (flag)
222 tmp = *ip;
223 else
224 tmp = ntohl(*ip);
225 if (tmp == INADDR_LOOPBACK) {
226 if (flag)
41c20dac 227 *ip = pr->pr_ip;
984263bc 228 else
41c20dac 229 *ip = htonl(pr->pr_ip);
984263bc
MD
230 }
231 return;
232}
233
234int
87de5057 235prison_if(struct ucred *cred, struct sockaddr *sa)
984263bc 236{
41c20dac 237 struct prison *pr;
984263bc
MD
238 struct sockaddr_in *sai = (struct sockaddr_in*) sa;
239 int ok;
240
87de5057 241 pr = cred->cr_prison;
41c20dac 242
984263bc
MD
243 if ((sai->sin_family != AF_INET) && jail_socket_unixiproute_only)
244 ok = 1;
245 else if (sai->sin_family != AF_INET)
246 ok = 0;
41c20dac 247 else if (pr->pr_ip != ntohl(sai->sin_addr.s_addr))
984263bc
MD
248 ok = 1;
249 else
250 ok = 0;
251 return (ok);
252}
b40e316c
JS
253
254/*
255 * Returns a prison instance, or NULL on failure.
256 */
257static struct prison *
258prison_find(int prid)
259{
260 struct prison *pr;
261
262 LIST_FOREACH(pr, &allprison, pr_list) {
263 if (pr->pr_id == prid)
264 break;
265 }
266 return(pr);
267}
268
269static int
270sysctl_jail_list(SYSCTL_HANDLER_ARGS)
271{
272 struct proc *p;
273 struct kinfo_prison *xp, *sxp;
274 struct prison *pr;
275 int count, error;
276
277 p = curthread->td_proc;
278
279 if (jailed(p->p_ucred))
280 return (0);
281retry:
282 count = prisoncount;
283
284 if (count == 0)
285 return(0);
286
287 sxp = xp = malloc(sizeof(*xp) * count, M_TEMP, M_WAITOK | M_ZERO);
288 if (count < prisoncount) {
289 free(sxp, M_TEMP);
290 goto retry;
291 }
292 count = prisoncount;
e713d50d 293
b40e316c
JS
294 LIST_FOREACH(pr, &allprison, pr_list) {
295 char *fullpath, *freepath;
296 xp->pr_version = KINFO_PRISON_VERSION;
297 xp->pr_id = pr->pr_id;
298 error = cache_fullpath(p, pr->pr_root, &fullpath, &freepath);
299 if (error == 0) {
300 strlcpy(xp->pr_path, fullpath, sizeof(xp->pr_path));
301 free(freepath, M_TEMP);
302 } else {
303 bzero(xp->pr_path, sizeof(xp->pr_path));
304 }
305 strlcpy(xp->pr_host, pr->pr_host, sizeof(xp->pr_host));
306 xp->pr_ip = pr->pr_ip;
307 xp++;
308 }
309
310 error = SYSCTL_OUT(req, sxp, sizeof(*sxp) * count);
311 free(sxp, M_TEMP);
312 return(error);
313}
314
315SYSCTL_OID(_jail, OID_AUTO, list, CTLTYPE_STRUCT | CTLFLAG_RD, NULL, 0,
316 sysctl_jail_list, "S", "List of active jails");
317
318void
319prison_hold(struct prison *pr)
320{
321 pr->pr_ref++;
322}
323
324void
325prison_free(struct prison *pr)
326{
327 KKASSERT(pr->pr_ref >= 1);
328
329 if (--pr->pr_ref > 0)
330 return;
331
332 LIST_REMOVE(pr, pr_list);
333 prisoncount--;
334
335 if (pr->pr_linux != NULL)
336 free(pr->pr_linux, M_PRISON);
337 varsymset_clean(&pr->pr_varsymset);
338 cache_drop(pr->pr_root);
339 free(pr, M_PRISON);
340}