]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - usr.sbin/jail/jail.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.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 #ifdef INET6
58 static int ip6_ok;
59 static char *ip6_addr;
60 #endif
61 #ifdef INET
62 static int ip4_ok;
63 static char *ip4_addr;
64 #endif
65
66 #if defined(INET6) || defined(INET)
67 static void add_ip_addr(char **addrp, char *newaddr);
68 #endif
69 #ifdef INET6
70 static void add_ip_addr46(char *newaddr);
71 #endif
72 static void add_ip_addrinfo(int ai_flags, char *value);
73 static void quoted_print(FILE *fp, char *str);
74 static void set_param(const char *name, char *value);
75 static void usage(void);
76
77 static const char *perm_sysctl[][3] = {
78         { "security.jail.set_hostname_allowed",
79           "allow.noset_hostname", "allow.set_hostname" },
80         { "security.jail.sysvipc_allowed",
81           "allow.nosysvipc", "allow.sysvipc" },
82         { "security.jail.allow_raw_sockets",
83           "allow.noraw_sockets", "allow.raw_sockets" },
84         { "security.jail.chflags_allowed",
85           "allow.nochflags", "allow.chflags" },
86         { "security.jail.mount_allowed",
87           "allow.nomount", "allow.mount" },
88         { "security.jail.socket_unixiproute_only",
89           "allow.socket_af", "allow.nosocket_af" },
90 };
91
92 extern char **environ;
93
94 #define GET_USER_INFO do {                                              \
95         pwd = getpwnam(username);                                       \
96         if (pwd == NULL) {                                              \
97                 if (errno)                                              \
98                         err(1, "getpwnam: %s", username);               \
99                 else                                                    \
100                         errx(1, "%s: no such user", username);          \
101         }                                                               \
102         lcap = login_getpwclass(pwd);                                   \
103         if (lcap == NULL)                                               \
104                 err(1, "getpwclass: %s", username);                     \
105         ngroups = ngroups_max;                                          \
106         if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0) \
107                 err(1, "getgrouplist: %s", username);                   \
108 } while (0)
109
110 int
111 main(int argc, char **argv)
112 {
113         login_cap_t *lcap = NULL;
114         struct passwd *pwd = NULL;
115         gid_t *groups;
116         size_t sysvallen;
117         int ch, cmdarg, i, jail_set_flags, jid, ngroups, sysval;
118         int hflag, iflag, Jflag, lflag, rflag, uflag, Uflag;
119         long ngroups_max;
120         unsigned pi;
121         char *jailname, *securelevel, *username, *JidFile;
122         char enforce_statfs[4];
123         static char *cleanenv;
124         const char *shell, *p = NULL;
125         FILE *fp;
126
127         hflag = iflag = Jflag = lflag = rflag = uflag = Uflag =
128             jail_set_flags = 0;
129         cmdarg = jid = -1;
130         jailname = securelevel = username = JidFile = cleanenv = NULL;
131         fp = NULL;
132
133         ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1;     
134         if ((groups = malloc(sizeof(gid_t) * ngroups_max)) == NULL)
135                 err(1, "malloc");
136
137         while ((ch = getopt(argc, argv, "cdhilmn:r:s:u:U:J:")) != -1) {
138                 switch (ch) {
139                 case 'd':
140                         jail_set_flags |= JAIL_DYING;
141                         break;
142                 case 'h':
143                         hflag = 1;
144                         break;
145                 case 'i':
146                         iflag = 1;
147                         break;
148                 case 'J':
149                         JidFile = optarg;
150                         Jflag = 1;
151                         break;
152                 case 'n':
153                         jailname = optarg;
154                         break;
155                 case 's':
156                         securelevel = optarg;
157                         break;
158                 case 'u':
159                         username = optarg;
160                         uflag = 1;
161                         break;
162                 case 'U':
163                         username = optarg;
164                         Uflag = 1;
165                         break;
166                 case 'l':
167                         lflag = 1;
168                         break;
169                 case 'c':
170                         jail_set_flags |= JAIL_CREATE;
171                         break;
172                 case 'm':
173                         jail_set_flags |= JAIL_UPDATE;
174                         break;
175                 case 'r':
176                         jid = jail_getid(optarg);
177                         if (jid < 0)
178                                 errx(1, "%s", jail_errmsg);
179                         rflag = 1;
180                         break;
181                 default:
182                         usage();
183                 }
184         }
185         argc -= optind;
186         argv += optind;
187         if (rflag) {
188                 if (argc > 0 || iflag || Jflag || lflag || uflag || Uflag)
189                         usage();
190                 if (jail_remove(jid) < 0)
191                         err(1, "jail_remove");
192                 exit (0);
193         }
194         if (argc == 0)
195                 usage();
196         if (uflag && Uflag)
197                 usage();
198         if (lflag && username == NULL)
199                 usage();
200         if (uflag)
201                 GET_USER_INFO;
202
203 #ifdef INET6
204         ip6_ok = feature_present("inet6");
205 #endif
206 #ifdef INET
207         ip4_ok = feature_present("inet");
208 #endif
209
210         if (jailname)
211                 set_param("name", jailname);
212         if (securelevel)
213                 set_param("securelevel", securelevel);
214         if (jail_set_flags) {
215                 for (i = 0; i < argc; i++) {
216                         if (!strncmp(argv[i], "command=", 8)) {
217                                 cmdarg = i;
218                                 argv[cmdarg] += 8;
219                                 jail_set_flags |= JAIL_ATTACH;
220                                 break;
221                         }
222                         if (hflag) {
223 #ifdef INET
224                                 if (!strncmp(argv[i], "ip4.addr=", 9)) {
225                                         add_ip_addr(&ip4_addr, argv[i] + 9);
226                                         break;
227                                 }
228 #endif
229 #ifdef INET6
230                                 if (!strncmp(argv[i], "ip6.addr=", 9)) {
231                                         add_ip_addr(&ip6_addr, argv[i] + 9);
232                                         break;
233                                 }
234 #endif
235                                 if (!strncmp(argv[i], "host.hostname=", 14))
236                                         add_ip_addrinfo(0, argv[i] + 14);
237                         }
238                         set_param(NULL, argv[i]);
239                 }
240         } else {
241                 if (argc < 4 || argv[0][0] != '/')
242                         errx(1, "%s\n%s",
243                            "no -c or -m, so this must be an old-style command.",
244                            "But it doesn't look like one.");
245                 set_param("path", argv[0]);
246                 set_param("host.hostname", argv[1]);
247                 if (hflag)
248                         add_ip_addrinfo(0, argv[1]);
249 #if defined(INET6) || defined(INET)
250                 if (argv[2][0] != '\0')
251 #ifdef INET6
252                         add_ip_addr46(argv[2]);
253 #else
254                         add_ip_addr(&ip4_addr, argv[2]);
255 #endif
256 #endif
257                 cmdarg = 3;
258                 /* Emulate the defaults from security.jail.* sysctls */
259                 sysvallen = sizeof(sysval);
260                 if (sysctlbyname("security.jail.jailed", &sysval, &sysvallen,
261                     NULL, 0) == 0 && sysval == 0) {
262                         for (pi = 0; pi < sizeof(perm_sysctl) /
263                              sizeof(perm_sysctl[0]); pi++) {
264                                 sysvallen = sizeof(sysval);
265                                 if (sysctlbyname(perm_sysctl[pi][0],
266                                     &sysval, &sysvallen, NULL, 0) == 0)
267                                         set_param(perm_sysctl[pi]
268                                             [sysval ? 2 : 1], NULL);
269                         }
270                         sysvallen = sizeof(sysval);
271                         if (sysctlbyname("security.jail.enforce_statfs",
272                             &sysval, &sysvallen, NULL, 0) == 0) {
273                                 snprintf(enforce_statfs,
274                                     sizeof(enforce_statfs), "%d", sysval);
275                                 set_param("enforce_statfs", enforce_statfs);
276                         }
277                 }
278         }
279 #ifdef INET
280         if (ip4_addr != NULL)
281                 set_param("ip4.addr", ip4_addr);
282 #endif
283 #ifdef INET6
284         if (ip6_addr != NULL)
285                 set_param("ip6.addr", ip6_addr);
286 #endif
287
288         if (Jflag) {
289                 fp = fopen(JidFile, "w");
290                 if (fp == NULL)
291                         errx(1, "Could not create JidFile: %s", JidFile);
292         }
293         jid = jailparam_set(params, nparams, 
294             jail_set_flags ? jail_set_flags : JAIL_CREATE | JAIL_ATTACH);
295         if (jid < 0)
296                 errx(1, "%s", jail_errmsg);
297         if (iflag) {
298                 printf("%d\n", jid);
299                 fflush(stdout);
300         }
301         if (Jflag) {
302                 if (jail_set_flags) {
303                         fprintf(fp, "jid=%d", jid);
304                         for (i = 0; i < nparams; i++)
305                                 if (strcmp(params[i].jp_name, "jid")) {
306                                         fprintf(fp, " %s",
307                                             (char *)params[i].jp_name);
308                                         if (param_values[i]) {
309                                                 putc('=', fp);
310                                                 quoted_print(fp,
311                                                     param_values[i]);
312                                         }
313                                 }
314                         fprintf(fp, "\n");
315                 } else {
316                         for (i = 0; i < nparams; i++)
317                                 if (!strcmp(params[i].jp_name, "path"))
318                                         break;
319 #if defined(INET6) && defined(INET)
320                         fprintf(fp, "%d\t%s\t%s\t%s%s%s\t%s\n",
321                             jid, i < nparams
322                             ? (char *)params[i].jp_value : argv[0],
323                             argv[1], ip4_addr ? ip4_addr : "",
324                             ip4_addr && ip4_addr[0] && ip6_addr && ip6_addr[0]
325                             ? "," : "", ip6_addr ? ip6_addr : "", argv[3]);
326 #elif defined(INET6)
327                         fprintf(fp, "%d\t%s\t%s\t%s\t%s\n",
328                             jid, i < nparams
329                             ?  (char *)params[i].jp_value : argv[0],
330                             argv[1], ip6_addr ? ip6_addr : "", argv[3]);
331 #elif defined(INET)
332                         fprintf(fp, "%d\t%s\t%s\t%s\t%s\n",
333                             jid, i < nparams
334                             ? (char *)params[i].jp_value : argv[0],
335                             argv[1], ip4_addr ? ip4_addr : "", argv[3]);
336 #endif
337                 }
338                 (void)fclose(fp);
339         }
340         if (cmdarg < 0)
341                 exit(0);
342         if (username != NULL) {
343                 if (Uflag)
344                         GET_USER_INFO;
345                 if (lflag) {
346                         p = getenv("TERM");
347                         environ = &cleanenv;
348                 }
349                 if (setgroups(ngroups, groups) != 0)
350                         err(1, "setgroups");
351                 if (setgid(pwd->pw_gid) != 0)
352                         err(1, "setgid");
353                 if (setusercontext(lcap, pwd, pwd->pw_uid,
354                     LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0)
355                         err(1, "setusercontext");
356                 login_close(lcap);
357         }
358         if (lflag) {
359                 if (*pwd->pw_shell)
360                         shell = pwd->pw_shell;
361                 else
362                         shell = _PATH_BSHELL;
363                 if (chdir(pwd->pw_dir) < 0)
364                         errx(1, "no home directory");
365                 setenv("HOME", pwd->pw_dir, 1);
366                 setenv("SHELL", shell, 1);
367                 setenv("USER", pwd->pw_name, 1);
368                 if (p)
369                         setenv("TERM", p, 1);
370         }
371         execvp(argv[cmdarg], argv + cmdarg);
372         err(1, "execvp: %s", argv[cmdarg]);
373 }
374
375 #if defined(INET6) || defined(INET)
376 static void
377 add_ip_addr(char **addrp, char *value)
378 {
379         int addrlen;
380         char *addr;
381
382         if (!*addrp) {
383                 *addrp = strdup(value);
384                 if (!*addrp)
385                         err(1, "malloc");
386         } else if (value[0]) {
387                 addrlen = strlen(*addrp) + strlen(value) + 2;
388                 addr = malloc(addrlen);
389                 if (!addr)
390                         err(1, "malloc");
391                 snprintf(addr, addrlen, "%s,%s", *addrp, value);
392                 free(*addrp);
393                 *addrp = addr;
394         }
395 }
396 #endif
397
398 #ifdef INET6
399 static void
400 add_ip_addr46(char *value)
401 {
402         char *p, *np;
403
404         for (p = value;; p = np + 1)
405         {
406                 np = strchr(p, ',');
407                 if (np)
408                         *np = '\0';
409                 add_ip_addrinfo(AI_NUMERICHOST, p);
410                 if (!np)
411                         break;
412         }
413 }
414 #endif
415
416 static void
417 add_ip_addrinfo(int ai_flags, char *value)
418 {
419         struct addrinfo hints, *ai0, *ai;
420         int error;
421 #ifdef INET
422         char avalue4[INET_ADDRSTRLEN];
423         struct in_addr addr4;
424 #endif
425 #ifdef INET6
426         char avalue6[INET6_ADDRSTRLEN];
427         struct in6_addr addr6;
428 #endif
429
430         /* Look up the hostname (or get the address) */
431         memset(&hints, 0, sizeof(hints));
432         hints.ai_socktype = SOCK_STREAM;
433 #if defined(INET6) && defined(INET)
434         hints.ai_family = PF_UNSPEC;
435 #elif defined(INET6)
436         hints.ai_family = PF_INET6;
437 #elif defined(INET)
438         hints.ai_family = PF_INET;
439 #endif
440         hints.ai_flags = ai_flags;
441         error = getaddrinfo(value, NULL, &hints, &ai0);
442         if (error != 0)
443                 errx(1, "hostname %s: %s", value, gai_strerror(error));
444
445         /* Convert the addresses to ASCII so set_param can convert them back. */
446         for (ai = ai0; ai; ai = ai->ai_next)
447                 switch (ai->ai_family) {
448 #ifdef INET
449                 case AF_INET:
450                         if (!ip4_ok && (ai_flags & AI_NUMERICHOST) == 0)
451                                 break;
452                         memcpy(&addr4, &((struct sockaddr_in *)
453                             (void *)ai->ai_addr)->sin_addr, sizeof(addr4));
454                         if (inet_ntop(AF_INET, &addr4, avalue4,
455                             INET_ADDRSTRLEN) == NULL)
456                                 err(1, "inet_ntop");
457                         add_ip_addr(&ip4_addr, avalue4);
458                         break;
459 #endif
460 #ifdef INET6
461                 case AF_INET6:
462                         if (!ip6_ok && (ai_flags & AI_NUMERICHOST) == 0)
463                                 break;
464                         memcpy(&addr6, &((struct sockaddr_in6 *)
465                             (void *)ai->ai_addr)->sin6_addr, sizeof(addr6));
466                         if (inet_ntop(AF_INET6, &addr6, avalue6,
467                             INET6_ADDRSTRLEN) == NULL)
468                                 err(1, "inet_ntop");
469                         add_ip_addr(&ip6_addr, avalue6);
470                         break;
471 #endif
472                 }
473         freeaddrinfo(ai0);
474 }
475
476 static void
477 quoted_print(FILE *fp, char *str)
478 {
479         int c, qc;
480         char *p = str;
481
482         /* An empty string needs quoting. */
483         if (!*p) {
484                 fputs("\"\"", fp);
485                 return;
486         }
487
488         /*
489          * The value will be surrounded by quotes if it contains spaces
490          * or quotes.
491          */
492         qc = strchr(p, '\'') ? '"'
493             : strchr(p, '"') ? '\''
494             : strchr(p, ' ') || strchr(p, '\t') ? '"'
495             : 0;
496         if (qc)
497                 putc(qc, fp);
498         while ((c = *p++)) {
499                 if (c == '\\' || c == qc)
500                         putc('\\', fp);
501                 putc(c, fp);
502         }
503         if (qc)
504                 putc(qc, fp);
505 }
506
507 static void
508 set_param(const char *name, char *value)
509 {
510         struct jailparam *param;
511         int i;
512
513         static int paramlistsize;
514
515         /* Separate the name from the value, if not done already. */
516         if (name == NULL) {
517                 name = value;
518                 if ((value = strchr(value, '=')))
519                         *value++ = '\0';
520         }
521
522         /* jail_set won't chdir along with its chroot, so do it here. */
523         if (!strcmp(name, "path") && chdir(value) < 0)
524                 err(1, "chdir: %s", value);
525
526         /* Check for repeat parameters */
527         for (i = 0; i < nparams; i++)
528                 if (!strcmp(name, params[i].jp_name)) {
529                         jailparam_free(params + i, 1);
530                         memcpy(params + i, params + i + 1,
531                             (--nparams - i) * sizeof(struct jailparam));
532                         break;
533                 }
534
535         /* Make sure there is room for the new param record. */
536         if (!nparams) {
537                 paramlistsize = 32;
538                 params = malloc(paramlistsize * sizeof(*params));
539                 param_values = malloc(paramlistsize * sizeof(*param_values));
540                 if (params == NULL || param_values == NULL)
541                         err(1, "malloc");
542         } else if (nparams >= paramlistsize) {
543                 paramlistsize *= 2;
544                 params = realloc(params, paramlistsize * sizeof(*params));
545                 param_values = realloc(param_values,
546                     paramlistsize * sizeof(*param_values));
547                 if (params == NULL)
548                         err(1, "realloc");
549         }
550
551         /* Look up the paramter. */
552         param_values[nparams] = value;
553         param = params + nparams++;
554         if (jailparam_init(param, name) < 0 ||
555             jailparam_import(param, value) < 0)
556                 errx(1, "%s", jail_errmsg);
557 }
558
559 static void
560 usage(void)
561 {
562
563         (void)fprintf(stderr,
564             "usage: jail [-d] [-h] [-i] [-J jid_file] "
565                         "[-l -u username | -U username]\n"
566             "            [-c | -m] param=value ... [command=command ...]\n"
567             "       jail [-r jail]\n");
568         exit(1);
569 }