]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/jail/state.c
MFV r275696: file 5.21.
[FreeBSD/FreeBSD.git] / usr.sbin / jail / state.c
1 /*-
2  * Copyright (c) 2011 James Gritton
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/uio.h>
31
32 #include <err.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include "jailp.h"
37
38 struct cfjails ready = TAILQ_HEAD_INITIALIZER(ready);
39 struct cfjails depend = TAILQ_HEAD_INITIALIZER(depend);
40
41 static void dep_add(struct cfjail *from, struct cfjail *to, unsigned flags);
42 static int cmp_jailptr(const void *a, const void *b);
43 static int cmp_jailptr_name(const void *a, const void *b);
44 static struct cfjail *find_jail(const char *name);
45 static int running_jid(const char *name, int flags);
46
47 static struct cfjail **jails_byname;
48 static size_t njails;
49
50 /*
51  * Set up jail dependency lists.
52  */
53 void
54 dep_setup(int docf)
55 {
56         struct cfjail *j, *dj;
57         struct cfparam *p;
58         struct cfstring *s;
59         struct cfdepend *d;
60         const char *cs;
61         char *pname;
62         size_t plen;
63         int deps, ldeps;
64
65         if (!docf) {
66                 /*
67                  * With no config file, let "depend" for a single jail
68                  * look at currently running jails.
69                  */
70                 if ((j = TAILQ_FIRST(&cfjails)) &&
71                     (p = j->intparams[IP_DEPEND])) {
72                         TAILQ_FOREACH(s, &p->val, tq) {
73                                 if (running_jid(s->s, 0) < 0) {
74                                         warnx("depends on nonexistent jail "
75                                             "\"%s\"", s->s);
76                                         j->flags |= JF_FAILED;
77                                 }
78                         }
79                 }
80                 return;
81         }
82
83         njails = 0;
84         TAILQ_FOREACH(j, &cfjails, tq)
85                 njails++;
86         jails_byname = emalloc(njails * sizeof(struct cfjail *));
87         njails = 0;
88         TAILQ_FOREACH(j, &cfjails, tq)
89                 jails_byname[njails++] = j;
90         qsort(jails_byname, njails, sizeof(struct cfjail *), cmp_jailptr);
91         deps = 0;
92         ldeps = 0;
93         plen = 0;
94         pname = NULL;
95         TAILQ_FOREACH(j, &cfjails, tq) {
96                 if (j->flags & JF_FAILED)
97                         continue;
98                 if ((p = j->intparams[IP_DEPEND])) {
99                         TAILQ_FOREACH(s, &p->val, tq) {
100                                 dj = find_jail(s->s);
101                                 if (dj != NULL) {
102                                         deps++;
103                                         dep_add(j, dj, 0);
104                                 } else {
105                                         jail_warnx(j,
106                                             "depends on undefined jail \"%s\"",
107                                             s->s);
108                                         j->flags |= JF_FAILED;
109                                 }
110                         }
111                 }
112                 /* A jail has an implied dependency on its parent. */
113                 if ((cs = strrchr(j->name, '.')))
114                 {
115                         if (plen < (size_t)(cs - j->name + 1)) {
116                                 plen = (cs - j->name) + 1;
117                                 pname = erealloc(pname, plen);
118                         }
119                         strlcpy(pname, j->name, plen);
120                         dj = find_jail(pname);
121                         if (dj != NULL) {
122                                 ldeps++;
123                                 dep_add(j, dj, DF_LIGHT);
124                         }
125                 }
126         }
127
128         /* Look for dependency loops. */
129         if (deps && (deps > 1 || ldeps)) {
130                 (void)start_state(NULL, 0, 0, 0);
131                 while ((j = TAILQ_FIRST(&ready))) {
132                         requeue(j, &cfjails);
133                         dep_done(j, DF_NOFAIL);
134                 }
135                 while ((j = TAILQ_FIRST(&depend)) != NULL) {
136                         jail_warnx(j, "dependency loop");
137                         j->flags |= JF_FAILED;
138                         do {
139                                 requeue(j, &cfjails);
140                                 dep_done(j, DF_NOFAIL);
141                         } while ((j = TAILQ_FIRST(&ready)));
142                 }
143                 TAILQ_FOREACH(j, &cfjails, tq)
144                         STAILQ_FOREACH(d, &j->dep[DEP_FROM], tq[DEP_FROM])
145                                 d->flags &= ~DF_SEEN;
146         }
147         if (pname != NULL)
148                 free(pname);
149 }
150
151 /*
152  * Return if a jail has dependencies.
153  */
154 int
155 dep_check(struct cfjail *j)
156 {
157         int reset, depfrom, depto, ndeps, rev;
158         struct cfjail *dj;
159         struct cfdepend *d;
160
161         static int bits[] = { 0, 1, 1, 2, 1, 2, 2, 3 };
162
163         if (j->ndeps == 0)
164                 return 0;
165         ndeps = 0;
166         if ((rev = JF_DO_STOP(j->flags))) {
167                 depfrom = DEP_TO;
168                 depto = DEP_FROM;
169         } else {
170                 depfrom = DEP_FROM;
171                 depto = DEP_TO;
172         }
173         STAILQ_FOREACH(d, &j->dep[depfrom], tq[depfrom]) {
174                 if (d->flags & DF_SEEN)
175                         continue;
176                 dj = d->j[depto];
177                 if (dj->flags & JF_FAILED) {
178                         if (!(j->flags & (JF_DEPEND | JF_FAILED)) &&
179                             verbose >= 0)
180                                 jail_warnx(j, "skipped");
181                         j->flags |= JF_FAILED;
182                         continue;
183                 }
184                 /*
185                  * The dependee's state may be set (or changed) as a result of
186                  * being in a dependency it wasn't in earlier.
187                  */
188                 reset = 0;
189                 if (bits[dj->flags & JF_OP_MASK] <= 1) {
190                         if (!(dj->flags & JF_OP_MASK)) {
191                                 reset = 1;
192                                 dj->flags |= JF_DEPEND;
193                                 requeue(dj, &ready);
194                         }
195                         /* Set or change the dependee's state. */
196                         switch (j->flags & JF_OP_MASK) {
197                         case JF_START:
198                                 dj->flags |= JF_START;
199                                 break;
200                         case JF_SET:
201                                 if (!(dj->flags & JF_OP_MASK))
202                                         dj->flags |= JF_SET;
203                                 else if (dj->flags & JF_STOP)
204                                         dj->flags |= JF_START;
205                                 break;
206                         case JF_STOP:
207                         case JF_RESTART:
208                                 if (!(dj->flags & JF_STOP))
209                                         reset = 1;
210                                 dj->flags |= JF_STOP;
211                                 if (dj->flags & JF_SET)
212                                         dj->flags ^= (JF_START | JF_SET);
213                                 break;
214                         }
215                 }
216                 if (reset)
217                         dep_reset(dj);
218                 if (!((d->flags & DF_LIGHT) &&
219                     (rev ? dj->jid < 0 : dj->jid > 0)))
220                         ndeps++;
221         }
222         if (ndeps == 0)
223                 return 0;
224         requeue(j, &depend);
225         return 1;
226 }
227
228 /*
229  * Resolve any dependencies from a finished jail.
230  */
231 void
232 dep_done(struct cfjail *j, unsigned flags)
233 {
234         struct cfjail *dj;
235         struct cfdepend *d;
236         int depfrom, depto;
237
238         if (JF_DO_STOP(j->flags)) {
239                 depfrom = DEP_TO;
240                 depto = DEP_FROM;
241         } else {
242                 depfrom = DEP_FROM;
243                 depto = DEP_TO;
244         }
245         STAILQ_FOREACH(d, &j->dep[depto], tq[depto]) {
246                 if ((d->flags & DF_SEEN) | (flags & ~d->flags & DF_LIGHT))
247                         continue;
248                 d->flags |= DF_SEEN;
249                 dj = d->j[depfrom];
250                 if (!(flags & DF_NOFAIL) && (j->flags & JF_FAILED) &&
251                     (j->flags & (JF_OP_MASK | JF_DEPEND)) !=
252                     (JF_SET | JF_DEPEND)) {
253                         if (!(dj->flags & (JF_DEPEND | JF_FAILED)) &&
254                             verbose >= 0)
255                                 jail_warnx(dj, "skipped");
256                         dj->flags |= JF_FAILED;
257                 }
258                 if (!--dj->ndeps && dj->queue == &depend)
259                         requeue(dj, &ready);
260         }
261 }
262
263 /*
264  * Count a jail's dependencies and mark them as unseen.
265  */
266 void
267 dep_reset(struct cfjail *j)
268 {
269         int depfrom;
270         struct cfdepend *d;
271
272         depfrom = JF_DO_STOP(j->flags) ? DEP_TO : DEP_FROM;
273         j->ndeps = 0;
274         STAILQ_FOREACH(d, &j->dep[depfrom], tq[depfrom])
275                 j->ndeps++;
276 }
277
278 /*
279  * Find the next jail ready to do something.
280  */
281 struct cfjail *
282 next_jail(void)
283 {
284         struct cfjail *j;
285
286         if (!(j = next_proc(!TAILQ_EMPTY(&ready))) &&
287             (j = TAILQ_FIRST(&ready)) && JF_DO_STOP(j->flags) &&
288             (j = TAILQ_LAST(&ready, cfjails)) && !JF_DO_STOP(j->flags)) {
289                 TAILQ_FOREACH_REVERSE(j, &ready, cfjails, tq)
290                         if (JF_DO_STOP(j->flags))
291                                 break;
292         }
293         if (j != NULL)
294                 requeue(j, &cfjails);
295         return j;
296 }
297
298 /*
299  * Set jails to the proper start state.
300  */
301 int
302 start_state(const char *target, int docf, unsigned state, int running)
303 {
304         struct iovec jiov[6];
305         struct cfjail *j, *tj;
306         int jid;
307         char namebuf[MAXHOSTNAMELEN];
308
309         if (!target || (!docf && state != JF_STOP) ||
310             (!running && !strcmp(target, "*"))) {
311                 /*
312                  * For a global wildcard (including no target specified),
313                  * set the state on all jails and start with those that
314                  * have no dependencies.
315                  */
316                 TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
317                         j->flags = (j->flags & JF_FAILED) | state |
318                             (docf ? JF_WILD : 0);
319                         dep_reset(j);
320                         requeue(j, j->ndeps ? &depend : &ready);
321                 }
322         } else if (wild_jail_name(target)) {
323                 /*
324                  * For targets specified singly, or with a non-global wildcard,
325                  * set their state and call them ready (even if there are
326                  * dependencies).  Leave everything else unqueued for now.
327                  */
328                 if (running) {
329                         /*
330                          * -R matches its wildcards against currently running
331                          * jails, not against the config file.
332                          */
333                         jiov[0].iov_base = __DECONST(char *, "lastjid");
334                         jiov[0].iov_len = sizeof("lastjid");
335                         jiov[1].iov_base = &jid;
336                         jiov[1].iov_len = sizeof(jid);
337                         jiov[2].iov_base = __DECONST(char *, "jid");
338                         jiov[2].iov_len = sizeof("jid");
339                         jiov[3].iov_base = &jid;
340                         jiov[3].iov_len = sizeof(jid);
341                         jiov[4].iov_base = __DECONST(char *, "name");
342                         jiov[4].iov_len = sizeof("name");
343                         jiov[5].iov_base = &namebuf;
344                         jiov[5].iov_len = sizeof(namebuf);
345                         for (jid = 0; jail_get(jiov, 6, 0) > 0; ) {
346                                 if (wild_jail_match(namebuf, target)) {
347                                         j = add_jail();
348                                         j->name = estrdup(namebuf);
349                                         j->jid = jid;
350                                         j->flags = (j->flags & JF_FAILED) |
351                                             state | JF_WILD;
352                                         dep_reset(j);
353                                         requeue(j, &ready);
354                                 }
355                         }
356                 } else {
357                         TAILQ_FOREACH_SAFE(j, &cfjails, tq, tj) {
358                                 if (wild_jail_match(j->name, target)) {
359                                         j->flags = (j->flags & JF_FAILED) |
360                                             state | JF_WILD;
361                                         dep_reset(j);
362                                         requeue(j, &ready);
363                                 }
364                         }
365                 }
366         } else {
367                 j = find_jail(target);
368                 if (j == NULL && state == JF_STOP) {
369                         /* Allow -[rR] to specify a currently running jail. */
370                         if ((jid = running_jid(target, JAIL_DYING)) > 0) {
371                                 j = add_jail();
372                                 j->name = estrdup(target);
373                                 j->jid = jid;
374                         }
375                 }
376                 if (j == NULL) {
377                         warnx("\"%s\" not found", target);
378                         return -1;
379                 }
380                 j->flags = (j->flags & JF_FAILED) | state;
381                 dep_reset(j);
382                 requeue(j, &ready);
383         }
384         return 0;
385 }
386
387 /*
388  * Move a jail to a new list.
389  */
390 void
391 requeue(struct cfjail *j, struct cfjails *queue)
392 {
393         if (j->queue != queue) {
394                 TAILQ_REMOVE(j->queue, j, tq);
395                 TAILQ_INSERT_TAIL(queue, j, tq);
396                 j->queue = queue;
397         }
398 }
399
400 /*
401  * Add a dependency edge between two jails.
402  */
403 static void
404 dep_add(struct cfjail *from, struct cfjail *to, unsigned flags)
405 {
406         struct cfdepend *d;
407
408         d = emalloc(sizeof(struct cfdepend));
409         d->flags = flags;
410         d->j[DEP_FROM] = from;
411         d->j[DEP_TO] = to;
412         STAILQ_INSERT_TAIL(&from->dep[DEP_FROM], d, tq[DEP_FROM]);
413         STAILQ_INSERT_TAIL(&to->dep[DEP_TO], d, tq[DEP_TO]);
414 }
415
416 /*
417  * Compare jail pointers for qsort/bsearch.
418  */
419 static int
420 cmp_jailptr(const void *a, const void *b)
421 {
422         return strcmp((*((struct cfjail * const *)a))->name,
423             ((*(struct cfjail * const *)b))->name);
424 }
425
426 static int
427 cmp_jailptr_name(const void *a, const void *b)
428 {
429         return strcmp((const char *)a, ((*(struct cfjail * const *)b))->name);
430 }
431
432 /*
433  * Find a jail object by name.
434  */
435 static struct cfjail *
436 find_jail(const char *name)
437 {
438         struct cfjail **jp;
439
440         jp = bsearch(name, jails_byname, njails, sizeof(struct cfjail *),
441             cmp_jailptr_name);
442         return jp ? *jp : NULL;
443 }
444
445 /*
446  * Return the named jail's jid if it is running, and -1 if it isn't.
447  */
448 static int
449 running_jid(const char *name, int flags)
450 {
451         struct iovec jiov[2];
452         char *ep;
453         int jid;
454
455         if ((jid = strtol(name, &ep, 10)) && !*ep) {
456                 jiov[0].iov_base = __DECONST(char *, "jid");
457                 jiov[0].iov_len = sizeof("jid");
458                 jiov[1].iov_base = &jid;
459                 jiov[1].iov_len = sizeof(jid);
460         } else {
461                 jiov[0].iov_base = __DECONST(char *, "name");
462                 jiov[0].iov_len = sizeof("name");
463                 jiov[1].iov_len = strlen(name) + 1;
464                 jiov[1].iov_base = alloca(jiov[1].iov_len);
465                 strcpy(jiov[1].iov_base, name);
466         }
467         return jail_get(jiov, 2, flags);
468 }