]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.sbin/jail/jail.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / usr.sbin / jail / jail.c
1 /*-
2  * Copyright (c) 1999 Poul-Henning Kamp.
3  * Copyright (c) 2009 James Gritton
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/jail.h>
33 #include <sys/socket.h>
34 #include <sys/sysctl.h>
35
36 #include <arpa/inet.h>
37 #include <netinet/in.h>
38
39 #include <ctype.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <grp.h>
43 #include <jail.h>
44 #include <login_cap.h>
45 #include <netdb.h>
46 #include <paths.h>
47 #include <pwd.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52
53 static struct jailparam *params;
54 static char **param_values;
55 static int nparams;
56
57 static char *ip4_addr;
58 #ifdef INET6
59 static char *ip6_addr;
60 #endif
61
62 static void add_ip_addr(char **addrp, char *newaddr);
63 #ifdef INET6
64 static void add_ip_addr46(char *newaddr);
65 #endif
66 static void add_ip_addrinfo(int ai_flags, char *value);
67 static void quoted_print(FILE *fp, char *str);
68 static void set_param(const char *name, char *value);
69 static void usage(void);
70
71 static const char *perm_sysctl[][3] = {
72         { "security.jail.set_hostname_allowed",
73           "allow.noset_hostname", "allow.set_hostname" },
74         { "security.jail.sysvipc_allowed",
75           "allow.nosysvipc", "allow.sysvipc" },
76         { "security.jail.allow_raw_sockets",
77           "allow.noraw_sockets", "allow.raw_sockets" },
78         { "security.jail.chflags_allowed",
79           "allow.nochflags", "allow.chflags" },
80         { "security.jail.mount_allowed",
81           "allow.nomount", "allow.mount" },
82         { "security.jail.socket_unixiproute_only",
83           "allow.socket_af", "allow.nosocket_af" },
84 };
85
86 extern char **environ;
87
88 #define GET_USER_INFO do {                                              \
89         pwd = getpwnam(username);                                       \
90         if (pwd == NULL) {                                              \
91                 if (errno)                                              \
92                         err(1, "getpwnam: %s", username);               \
93                 else                                                    \
94                         errx(1, "%s: no such user", username);          \
95         }                                                               \
96         lcap = login_getpwclass(pwd);                                   \
97         if (lcap == NULL)                                               \
98                 err(1, "getpwclass: %s", username);                     \
99         ngroups = ngroups_max;                                          \
100         if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0) \
101                 err(1, "getgrouplist: %s", username);                   \
102 } while (0)
103
104 int
105 main(int argc, char **argv)
106 {
107         login_cap_t *lcap = NULL;
108         struct passwd *pwd = NULL;
109         gid_t *groups;
110         size_t sysvallen;
111         int ch, cmdarg, i, jail_set_flags, jid, ngroups, sysval;
112         int hflag, iflag, Jflag, lflag, rflag, uflag, Uflag;
113         long ngroups_max;
114         unsigned pi;
115         char *jailname, *securelevel, *username, *JidFile;
116         char enforce_statfs[4];
117         static char *cleanenv;
118         const char *shell, *p = NULL;
119         FILE *fp;
120
121         hflag = iflag = Jflag = lflag = rflag = uflag = Uflag =
122             jail_set_flags = 0;
123         cmdarg = jid = -1;
124         jailname = securelevel = username = JidFile = cleanenv = NULL;
125         fp = NULL;
126
127         ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1;     
128         if ((groups = malloc(sizeof(gid_t) * ngroups_max)) == NULL)
129                 err(1, "malloc");
130
131         while ((ch = getopt(argc, argv, "cdhilmn:r:s:u:U:J:")) != -1) {
132                 switch (ch) {
133                 case 'd':
134                         jail_set_flags |= JAIL_DYING;
135                         break;
136                 case 'h':
137                         hflag = 1;
138                         break;
139                 case 'i':
140                         iflag = 1;
141                         break;
142                 case 'J':
143                         JidFile = optarg;
144                         Jflag = 1;
145                         break;
146                 case 'n':
147                         jailname = optarg;
148                         break;
149                 case 's':
150                         securelevel = optarg;
151                         break;
152                 case 'u':
153                         username = optarg;
154                         uflag = 1;
155                         break;
156                 case 'U':
157                         username = optarg;
158                         Uflag = 1;
159                         break;
160                 case 'l':
161                         lflag = 1;
162                         break;
163                 case 'c':
164                         jail_set_flags |= JAIL_CREATE;
165                         break;
166                 case 'm':
167                         jail_set_flags |= JAIL_UPDATE;
168                         break;
169                 case 'r':
170                         jid = jail_getid(optarg);
171                         if (jid < 0)
172                                 errx(1, "%s", jail_errmsg);
173                         rflag = 1;
174                         break;
175                 default:
176                         usage();
177                 }
178         }
179         argc -= optind;
180         argv += optind;
181         if (rflag) {
182                 if (argc > 0 || iflag || Jflag || lflag || uflag || Uflag)
183                         usage();
184                 if (jail_remove(jid) < 0)
185                         err(1, "jail_remove");
186                 exit (0);
187         }
188         if (argc == 0)
189                 usage();
190         if (uflag && Uflag)
191                 usage();
192         if (lflag && username == NULL)
193                 usage();
194         if (uflag)
195                 GET_USER_INFO;
196
197         if (jailname)
198                 set_param("name", jailname);
199         if (securelevel)
200                 set_param("securelevel", securelevel);
201         if (jail_set_flags) {
202                 for (i = 0; i < argc; i++) {
203                         if (!strncmp(argv[i], "command=", 8)) {
204                                 cmdarg = i;
205                                 argv[cmdarg] += 8;
206                                 jail_set_flags |= JAIL_ATTACH;
207                                 break;
208                         }
209                         if (hflag) {
210                                 if (!strncmp(argv[i], "ip4.addr=", 9)) {
211                                         add_ip_addr(&ip4_addr, argv[i] + 9);
212                                         break;
213                                 }
214 #ifdef INET6
215                                 if (!strncmp(argv[i], "ip6.addr=", 9)) {
216                                         add_ip_addr(&ip6_addr, argv[i] + 9);
217                                         break;
218                                 }
219 #endif
220                                 if (!strncmp(argv[i], "host.hostname=", 14))
221                                         add_ip_addrinfo(0, argv[i] + 14);
222                         }
223                         set_param(NULL, argv[i]);
224                 }
225         } else {
226                 if (argc < 4 || argv[0][0] != '/')
227                         errx(1, "%s\n%s",
228                            "no -c or -m, so this must be an old-style command.",
229                            "But it doesn't look like one.");
230                 set_param("path", argv[0]);
231                 set_param("host.hostname", argv[1]);
232                 if (hflag)
233                         add_ip_addrinfo(0, argv[1]);
234                 if (argv[2][0] != '\0')
235 #ifdef INET6
236                         add_ip_addr46(argv[2]);
237 #else
238                         add_ip_addr(&ip4_addr, argv[2]);
239 #endif
240                 cmdarg = 3;
241                 /* Emulate the defaults from security.jail.* sysctls */
242                 sysvallen = sizeof(sysval);
243                 if (sysctlbyname("security.jail.jailed", &sysval, &sysvallen,
244                     NULL, 0) == 0 && sysval == 0) {
245                         for (pi = 0; pi < sizeof(perm_sysctl) /
246                              sizeof(perm_sysctl[0]); pi++) {
247                                 sysvallen = sizeof(sysval);
248                                 if (sysctlbyname(perm_sysctl[pi][0],
249                                     &sysval, &sysvallen, NULL, 0) == 0)
250                                         set_param(perm_sysctl[pi]
251                                             [sysval ? 2 : 1], NULL);
252                         }
253                         sysvallen = sizeof(sysval);
254                         if (sysctlbyname("security.jail.enforce_statfs",
255                             &sysval, &sysvallen, NULL, 0) == 0) {
256                                 snprintf(enforce_statfs,
257                                     sizeof(enforce_statfs), "%d", sysval);
258                                 set_param("enforce_statfs", enforce_statfs);
259                         }
260                 }
261         }
262         if (ip4_addr != NULL)
263                 set_param("ip4.addr", ip4_addr);
264 #ifdef INET6
265         if (ip6_addr != NULL)
266                 set_param("ip6.addr", ip6_addr);
267 #endif
268
269         if (Jflag) {
270                 fp = fopen(JidFile, "w");
271                 if (fp == NULL)
272                         errx(1, "Could not create JidFile: %s", JidFile);
273         }
274         jid = jailparam_set(params, nparams, 
275             jail_set_flags ? jail_set_flags : JAIL_CREATE | JAIL_ATTACH);
276         if (jid < 0)
277                 errx(1, "%s", jail_errmsg);
278         if (iflag) {
279                 printf("%d\n", jid);
280                 fflush(stdout);
281         }
282         if (Jflag) {
283                 if (jail_set_flags) {
284                         fprintf(fp, "jid=%d", jid);
285                         for (i = 0; i < nparams; i++)
286                                 if (strcmp(params[i].jp_name, "jid")) {
287                                         fprintf(fp, " %s",
288                                             (char *)params[i].jp_name);
289                                         if (param_values[i]) {
290                                                 putc('=', fp);
291                                                 quoted_print(fp,
292                                                     param_values[i]);
293                                         }
294                                 }
295                         fprintf(fp, "\n");
296                 } else {
297                         for (i = 0; i < nparams; i++)
298                                 if (!strcmp(params[i].jp_name, "path"))
299                                         break;
300 #ifdef INET6
301                         fprintf(fp, "%d\t%s\t%s\t%s%s%s\t%s\n",
302                             jid, i < nparams
303                             ? (char *)params[i].jp_value : argv[0],
304                             argv[1], ip4_addr ? ip4_addr : "",
305                             ip4_addr && ip4_addr[0] && ip6_addr && ip6_addr[0]
306                             ? "," : "", ip6_addr ? ip6_addr : "", argv[3]);
307 #else
308                         fprintf(fp, "%d\t%s\t%s\t%s\t%s\n",
309                             jid, i < nparams
310                             ? (char *)params[i].jp_value : argv[0],
311                             argv[1], ip4_addr ? ip4_addr : "", argv[3]);
312 #endif
313                 }
314                 (void)fclose(fp);
315         }
316         if (cmdarg < 0)
317                 exit(0);
318         if (username != NULL) {
319                 if (Uflag)
320                         GET_USER_INFO;
321                 if (lflag) {
322                         p = getenv("TERM");
323                         environ = &cleanenv;
324                 }
325                 if (setgroups(ngroups, groups) != 0)
326                         err(1, "setgroups");
327                 if (setgid(pwd->pw_gid) != 0)
328                         err(1, "setgid");
329                 if (setusercontext(lcap, pwd, pwd->pw_uid,
330                     LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0)
331                         err(1, "setusercontext");
332                 login_close(lcap);
333         }
334         if (lflag) {
335                 if (*pwd->pw_shell)
336                         shell = pwd->pw_shell;
337                 else
338                         shell = _PATH_BSHELL;
339                 if (chdir(pwd->pw_dir) < 0)
340                         errx(1, "no home directory");
341                 setenv("HOME", pwd->pw_dir, 1);
342                 setenv("SHELL", shell, 1);
343                 setenv("USER", pwd->pw_name, 1);
344                 if (p)
345                         setenv("TERM", p, 1);
346         }
347         execvp(argv[cmdarg], argv + cmdarg);
348         err(1, "execvp: %s", argv[cmdarg]);
349 }
350
351 static void
352 add_ip_addr(char **addrp, char *value)
353 {
354         int addrlen;
355         char *addr;
356
357         if (!*addrp) {
358                 *addrp = strdup(value);
359                 if (!*addrp)
360                         err(1, "malloc");
361         } else if (value[0]) {
362                 addrlen = strlen(*addrp) + strlen(value) + 2;
363                 addr = malloc(addrlen);
364                 if (!addr)
365                         err(1, "malloc");
366                 snprintf(addr, addrlen, "%s,%s", *addrp, value);
367                 free(*addrp);
368                 *addrp = addr;
369         }
370 }
371
372 #ifdef INET6
373 static void
374 add_ip_addr46(char *value)
375 {
376         char *p, *np;
377
378         for (p = value;; p = np + 1)
379         {
380                 np = strchr(p, ',');
381                 if (np)
382                         *np = '\0';
383                 add_ip_addrinfo(AI_NUMERICHOST, p);
384                 if (!np)
385                         break;
386         }
387 }
388 #endif
389
390 static void
391 add_ip_addrinfo(int ai_flags, char *value)
392 {
393         struct addrinfo hints, *ai0, *ai;
394         struct in_addr addr4;
395         size_t size;
396         int error, ip4ok;
397         int mib[4];
398         char avalue4[INET_ADDRSTRLEN];
399 #ifdef INET6
400         struct in6_addr addr6;
401         int ip6ok;
402         char avalue6[INET6_ADDRSTRLEN];
403 #endif
404
405         /* Look up the hostname (or get the address) */
406         memset(&hints, 0, sizeof(hints));
407         hints.ai_socktype = SOCK_STREAM;
408 #ifdef INET6
409         hints.ai_family = PF_UNSPEC;
410 #else
411         hints.ai_family = PF_INET;
412 #endif
413         hints.ai_flags = ai_flags;
414         error = getaddrinfo(value, NULL, &hints, &ai0);
415         if (error != 0)
416                 errx(1, "hostname %s: %s", value, gai_strerror(error));
417
418         /*
419          * Silently ignore unsupported address families from DNS lookups.
420          * But if this is a numeric address, let the kernel give the error.
421          */
422         if (ai_flags & AI_NUMERICHOST)
423                 ip4ok =
424 #ifdef INET6
425                     ip6ok =
426 #endif
427                     1;
428         else {
429                 size = 4;
430                 ip4ok = (sysctlnametomib("security.jail.param.ip4", mib,
431                     &size) == 0);
432 #ifdef INET6
433                 size = 4;
434                 ip6ok = (sysctlnametomib("security.jail.param.ip6", mib,
435                     &size) == 0);
436 #endif
437         }
438         
439         /* Convert the addresses to ASCII so set_param can convert them back. */
440         for (ai = ai0; ai; ai = ai->ai_next)
441                 switch (ai->ai_family) {
442                 case AF_INET:
443                         if (!ip4ok)
444                                 break;
445                         memcpy(&addr4, &((struct sockaddr_in *)
446                             (void *)ai->ai_addr)->sin_addr, sizeof(addr4));
447                         if (inet_ntop(AF_INET, &addr4, avalue4,
448                             INET_ADDRSTRLEN) == NULL)
449                                 err(1, "inet_ntop");
450                         add_ip_addr(&ip4_addr, avalue4);
451                         break;
452 #ifdef INET6
453                 case AF_INET6:
454                         if (!ip6ok)
455                                 break;
456                         memcpy(&addr6, &((struct sockaddr_in6 *)
457                             (void *)ai->ai_addr)->sin6_addr, sizeof(addr6));
458                         if (inet_ntop(AF_INET6, &addr6, avalue6,
459                             INET6_ADDRSTRLEN) == NULL)
460                                 err(1, "inet_ntop");
461                         add_ip_addr(&ip6_addr, avalue6);
462                         break;
463 #endif
464                 }
465         freeaddrinfo(ai0);
466 }
467
468 static void
469 quoted_print(FILE *fp, char *str)
470 {
471         int c, qc;
472         char *p = str;
473
474         /* An empty string needs quoting. */
475         if (!*p) {
476                 fputs("\"\"", fp);
477                 return;
478         }
479
480         /*
481          * The value will be surrounded by quotes if it contains spaces
482          * or quotes.
483          */
484         qc = strchr(p, '\'') ? '"'
485             : strchr(p, '"') ? '\''
486             : strchr(p, ' ') || strchr(p, '\t') ? '"'
487             : 0;
488         if (qc)
489                 putc(qc, fp);
490         while ((c = *p++)) {
491                 if (c == '\\' || c == qc)
492                         putc('\\', fp);
493                 putc(c, fp);
494         }
495         if (qc)
496                 putc(qc, fp);
497 }
498
499 static void
500 set_param(const char *name, char *value)
501 {
502         struct jailparam *param;
503         int i;
504
505         static int paramlistsize;
506
507         /* Separate the name from the value, if not done already. */
508         if (name == NULL) {
509                 name = value;
510                 if ((value = strchr(value, '=')))
511                         *value++ = '\0';
512         }
513
514         /* jail_set won't chdir along with its chroot, so do it here. */
515         if (!strcmp(name, "path") && chdir(value) < 0)
516                 err(1, "chdir: %s", value);
517
518         /* Check for repeat parameters */
519         for (i = 0; i < nparams; i++)
520                 if (!strcmp(name, params[i].jp_name)) {
521                         jailparam_free(params + i, 1);
522                         memcpy(params + i, params + i + 1,
523                             (--nparams - i) * sizeof(struct jailparam));
524                         break;
525                 }
526
527         /* Make sure there is room for the new param record. */
528         if (!nparams) {
529                 paramlistsize = 32;
530                 params = malloc(paramlistsize * sizeof(*params));
531                 param_values = malloc(paramlistsize * sizeof(*param_values));
532                 if (params == NULL || param_values == NULL)
533                         err(1, "malloc");
534         } else if (nparams >= paramlistsize) {
535                 paramlistsize *= 2;
536                 params = realloc(params, paramlistsize * sizeof(*params));
537                 param_values = realloc(param_values,
538                     paramlistsize * sizeof(*param_values));
539                 if (params == NULL)
540                         err(1, "realloc");
541         }
542
543         /* Look up the paramter. */
544         param_values[nparams] = value;
545         param = params + nparams++;
546         if (jailparam_init(param, name) < 0 ||
547             jailparam_import(param, value) < 0)
548                 errx(1, "%s", jail_errmsg);
549 }
550
551 static void
552 usage(void)
553 {
554
555         (void)fprintf(stderr,
556             "usage: jail [-d] [-h] [-i] [-J jid_file] "
557                         "[-l -u username | -U username]\n"
558             "            [-c | -m] param=value ... [command=command ...]\n"
559             "       jail [-r jail]\n");
560         exit(1);
561 }