]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/jail/jail.c
As of sam's r175206, arp builds cleanly at WARNS level 6, but the Makefile
[FreeBSD/FreeBSD.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 #include <sys/uio.h>
36
37 #include <arpa/inet.h>
38 #include <netinet/in.h>
39
40 #include <ctype.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <grp.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 #define SJPARAM         "security.jail.param"
54 #define ERRMSG_SIZE     256
55
56 struct param {
57         struct iovec name;
58         struct iovec value;
59 };
60
61 static struct param *params;
62 static char **param_values;
63 static int nparams;
64
65 static char *ip4_addr;
66 #ifdef INET6
67 static char *ip6_addr;
68 #endif
69
70 static void add_ip_addr(char **addrp, char *newaddr);
71 #ifdef INET6
72 static void add_ip_addr46(char *newaddr);
73 #endif
74 static void add_ip_addrinfo(int ai_flags, char *value);
75 static void quoted_print(FILE *fp, char *str);
76 static void set_param(const char *name, char *value);
77 static void usage(void);
78
79 static const char *perm_sysctl[][3] = {
80         { "security.jail.set_hostname_allowed",
81           "allow.noset_hostname", "allow.set_hostname" },
82         { "security.jail.sysvipc_allowed",
83           "allow.nosysvipc", "allow.sysvipc" },
84         { "security.jail.allow_raw_sockets",
85           "allow.noraw_sockets", "allow.raw_sockets" },
86         { "security.jail.chflags_allowed",
87           "allow.nochflags", "allow.chflags" },
88         { "security.jail.mount_allowed",
89           "allow.nomount", "allow.mount" },
90         { "security.jail.socket_unixiproute_only",
91           "allow.socket_af", "allow.nosocket_af" },
92 };
93
94 extern char **environ;
95
96 #define GET_USER_INFO do {                                              \
97         pwd = getpwnam(username);                                       \
98         if (pwd == NULL) {                                              \
99                 if (errno)                                              \
100                         err(1, "getpwnam: %s", username);               \
101                 else                                                    \
102                         errx(1, "%s: no such user", username);          \
103         }                                                               \
104         lcap = login_getpwclass(pwd);                                   \
105         if (lcap == NULL)                                               \
106                 err(1, "getpwclass: %s", username);                     \
107         ngroups = NGROUPS;                                              \
108         if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0) \
109                 err(1, "getgrouplist: %s", username);                   \
110 } while (0)
111
112 int
113 main(int argc, char **argv)
114 {
115         login_cap_t *lcap = NULL;
116         struct iovec rparams[2];
117         struct passwd *pwd = NULL;
118         gid_t groups[NGROUPS];
119         size_t sysvallen;
120         int ch, cmdarg, i, jail_set_flags, jid, ngroups, sysval;
121         int hflag, iflag, Jflag, lflag, rflag, uflag, Uflag;
122         unsigned pi;
123         char *ep, *jailname, *securelevel, *username, *JidFile;
124         char errmsg[ERRMSG_SIZE], enforce_statfs[4];
125         static char *cleanenv;
126         const char *shell, *p = NULL;
127         FILE *fp;
128
129         hflag = iflag = Jflag = lflag = rflag = uflag = Uflag =
130             jail_set_flags = 0;
131         cmdarg = jid = -1;
132         jailname = securelevel = username = JidFile = cleanenv = NULL;
133         fp = NULL;
134
135         while ((ch = getopt(argc, argv, "cdhilmn:r:s:u:U:J:")) != -1) {
136                 switch (ch) {
137                 case 'd':
138                         jail_set_flags |= JAIL_DYING;
139                         break;
140                 case 'h':
141                         hflag = 1;
142                         break;
143                 case 'i':
144                         iflag = 1;
145                         break;
146                 case 'J':
147                         JidFile = optarg;
148                         Jflag = 1;
149                         break;
150                 case 'n':
151                         jailname = optarg;
152                         break;
153                 case 's':
154                         securelevel = optarg;
155                         break;
156                 case 'u':
157                         username = optarg;
158                         uflag = 1;
159                         break;
160                 case 'U':
161                         username = optarg;
162                         Uflag = 1;
163                         break;
164                 case 'l':
165                         lflag = 1;
166                         break;
167                 case 'c':
168                         jail_set_flags |= JAIL_CREATE;
169                         break;
170                 case 'm':
171                         jail_set_flags |= JAIL_UPDATE;
172                         break;
173                 case 'r':
174                         jid = strtoul(optarg, &ep, 10);
175                         if (!*optarg || *ep) {
176                                 *(const void **)&rparams[0].iov_base = "name";
177                                 rparams[0].iov_len = sizeof("name");
178                                 rparams[1].iov_base = optarg;
179                                 rparams[1].iov_len = strlen(optarg) + 1;
180                                 jid = jail_get(rparams, 2, 0);
181                                 if (jid < 0)
182                                         errx(1, "unknown jail: %s", optarg);
183                         }
184                         rflag = 1;
185                         break;
186                 default:
187                         usage();
188                 }
189         }
190         argc -= optind;
191         argv += optind;
192         if (rflag) {
193                 if (argc > 0 || iflag || Jflag || lflag || uflag || Uflag)
194                         usage();
195                 if (jail_remove(jid) < 0)
196                         err(1, "jail_remove");
197                 exit (0);
198         }
199         if (argc == 0)
200                 usage();
201         if (uflag && Uflag)
202                 usage();
203         if (lflag && username == NULL)
204                 usage();
205         if (uflag)
206                 GET_USER_INFO;
207
208         /*
209          * If the first argument (path) starts with a slash, and the third
210          * argument (IP address) starts with a digit, it is likely to be
211          * an old-style fixed-parameter command line.
212          */
213         if (jailname)
214                 set_param("name", jailname);
215         if (securelevel)
216                 set_param("securelevel", securelevel);
217         if (jail_set_flags) {
218                 for (i = 0; i < argc; i++) {
219                         if (!strncmp(argv[i], "command=", 8)) {
220                                 cmdarg = i;
221                                 argv[cmdarg] += 8;
222                                 jail_set_flags |= JAIL_ATTACH;
223                                 break;
224                         }
225                         if (hflag) {
226                                 if (!strncmp(argv[i], "ip4.addr=", 9)) {
227                                         add_ip_addr(&ip4_addr, argv[i] + 9);
228                                         break;
229                                 }
230 #ifdef INET6
231                                 if (!strncmp(argv[i], "ip6.addr=", 9)) {
232                                         add_ip_addr(&ip6_addr, argv[i] + 9);
233                                         break;
234                                 }
235 #endif
236                                 if (!strncmp(argv[i], "host.hostname=", 14))
237                                         add_ip_addrinfo(0, argv[i] + 14);
238                         }
239                         set_param(NULL, argv[i]);
240                 }
241         } else {
242                 if (argc < 4 || argv[0][0] != '/')
243                         errx(1, "%s\n%s",
244                            "no -c or -m, so this must be an old-style command.",
245                            "But it doesn't look like one.");
246                 set_param("path", argv[0]);
247                 set_param("host.hostname", argv[1]);
248                 if (hflag)
249                         add_ip_addrinfo(0, argv[1]);
250 #ifdef INET6
251                 add_ip_addr46(argv[2]);
252 #else
253                 add_ip_addr(&ip4_addr, argv[2]);
254 #endif
255                 cmdarg = 3;
256                 /* Emulate the defaults from security.jail.* sysctls */
257                 sysvallen = sizeof(sysval);
258                 if (sysctlbyname("security.jail.jailed", &sysval, &sysvallen,
259                     NULL, 0) == 0 && sysval == 0) {
260                         for (pi = 0; pi < sizeof(perm_sysctl) /
261                              sizeof(perm_sysctl[0]); pi++) {
262                                 sysvallen = sizeof(sysval);
263                                 if (sysctlbyname(perm_sysctl[pi][0],
264                                     &sysval, &sysvallen, NULL, 0) == 0)
265                                         set_param(perm_sysctl[pi]
266                                             [sysval ? 2 : 1], NULL);
267                         }
268                         sysvallen = sizeof(sysval);
269                         if (sysctlbyname("security.jail.enforce_statfs",
270                             &sysval, &sysvallen, NULL, 0) == 0) {
271                                 snprintf(enforce_statfs,
272                                     sizeof(enforce_statfs), "%d", sysval);
273                                 set_param("enforce_statfs", enforce_statfs);
274                         }
275                 }
276         }
277         if (ip4_addr != NULL)
278                 set_param("ip4.addr", ip4_addr);
279 #ifdef INET6
280         if (ip6_addr != NULL)
281                 set_param("ip6.addr", ip6_addr);
282 #endif
283         errmsg[0] = 0;
284         set_param("errmsg", errmsg);
285
286         if (Jflag) {
287                 fp = fopen(JidFile, "w");
288                 if (fp == NULL)
289                         errx(1, "Could not create JidFile: %s", JidFile);
290         }
291         jid = jail_set(&params->name, 2 * nparams,
292             jail_set_flags ? jail_set_flags : JAIL_CREATE | JAIL_ATTACH);
293         if (jid < 0) {
294                 if (errmsg[0] != '\0')
295                         errx(1, "%s", errmsg);
296                 err(1, "jail_set");
297         }
298         if (iflag) {
299                 printf("%d\n", jid);
300                 fflush(stdout);
301         }
302         if (Jflag) {
303                 if (jail_set_flags) {
304                         fprintf(fp, "jid=%d", jid);
305                         for (i = 0; i < nparams; i++)
306                                 if (strcmp(params[i].name.iov_base, "jid") &&
307                                     strcmp(params[i].name.iov_base, "errmsg")) {
308                                         fprintf(fp, " %s",
309                                             (char *)params[i].name.iov_base);
310                                         if (param_values[i]) {
311                                                 putc('=', fp);
312                                                 quoted_print(fp,
313                                                     param_values[i]);
314                                         }
315                                 }
316                         fprintf(fp, "\n");
317                 } else {
318                         for (i = 0; i < nparams; i++)
319                                 if (!strcmp(params[i].name.iov_base, "path"))
320                                         break;
321 #ifdef INET6
322                         fprintf(fp, "%d\t%s\t%s\t%s%s%s\t%s\n",
323                             jid, i < nparams
324                             ? (char *)params[i].value.iov_base : argv[0],
325                             argv[1], ip4_addr ? ip4_addr : "",
326                             ip4_addr && ip4_addr[0] && ip6_addr && ip6_addr[0]
327                             ? "," : "", ip6_addr ? ip6_addr : "", argv[3]);
328 #else
329                         fprintf(fp, "%d\t%s\t%s\t%s\t%s\n",
330                             jid, i < nparams
331                             ? (char *)params[i].value.iov_base : argv[0],
332                             argv[1], ip4_addr ? ip4_addr : "", argv[3]);
333 #endif
334                 }
335                 (void)fclose(fp);
336         }
337         if (cmdarg < 0)
338                 exit(0);
339         if (username != NULL) {
340                 if (Uflag)
341                         GET_USER_INFO;
342                 if (lflag) {
343                         p = getenv("TERM");
344                         environ = &cleanenv;
345                 }
346                 if (setgroups(ngroups, groups) != 0)
347                         err(1, "setgroups");
348                 if (setgid(pwd->pw_gid) != 0)
349                         err(1, "setgid");
350                 if (setusercontext(lcap, pwd, pwd->pw_uid,
351                     LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0)
352                         err(1, "setusercontext");
353                 login_close(lcap);
354         }
355         if (lflag) {
356                 if (*pwd->pw_shell)
357                         shell = pwd->pw_shell;
358                 else
359                         shell = _PATH_BSHELL;
360                 if (chdir(pwd->pw_dir) < 0)
361                         errx(1, "no home directory");
362                 setenv("HOME", pwd->pw_dir, 1);
363                 setenv("SHELL", shell, 1);
364                 setenv("USER", pwd->pw_name, 1);
365                 if (p)
366                         setenv("TERM", p, 1);
367         }
368         execvp(argv[cmdarg], argv + cmdarg);
369         err(1, "execvp: %s", argv[cmdarg]);
370 }
371
372 static void
373 add_ip_addr(char **addrp, char *value)
374 {
375         int addrlen;
376         char *addr;
377
378         if (!*addrp) {
379                 *addrp = strdup(value);
380                 if (!*addrp)
381                         err(1, "malloc");
382         } else if (value[0]) {
383                 addrlen = strlen(*addrp) + strlen(value) + 2;
384                 addr = malloc(addrlen);
385                 if (!addr)
386                         err(1, "malloc");
387                 snprintf(addr, addrlen, "%s,%s", *addrp, value);
388                 free(*addrp);
389                 *addrp = addr;
390         }
391 }
392
393 #ifdef INET6
394 static void
395 add_ip_addr46(char *value)
396 {
397         char *p, *np;
398
399         if (!value[0]) {
400                 add_ip_addr(&ip4_addr, value);
401                 add_ip_addr(&ip6_addr, value);
402                 return;
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         struct in_addr addr4;
421         int error;
422         char avalue4[INET_ADDRSTRLEN];
423 #ifdef INET6
424         struct in6_addr addr6;
425         char avalue6[INET6_ADDRSTRLEN];
426 #endif
427
428         /* Look up the hostname (or get the address) */
429         memset(&hints, 0, sizeof(hints));
430         hints.ai_socktype = SOCK_STREAM;
431 #ifdef INET6
432         hints.ai_family = PF_UNSPEC;
433 #else
434         hints.ai_family = PF_INET;
435 #endif
436         hints.ai_flags = ai_flags;
437         error = getaddrinfo(value, NULL, &hints, &ai0);
438         if (error != 0)
439                 errx(1, "hostname %s: %s", value, gai_strerror(error));
440         
441         /* Convert the addresses to ASCII so set_param can convert them back. */
442         for (ai = ai0; ai; ai = ai->ai_next)
443                 switch (ai->ai_family) {
444                 case AF_INET:
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                         memcpy(&addr6, &((struct sockaddr_in6 *)
455                             (void *)ai->ai_addr)->sin6_addr, sizeof(addr6));
456                         if (inet_ntop(AF_INET6, &addr6, avalue6,
457                             INET6_ADDRSTRLEN) == NULL)
458                                 err(1, "inet_ntop");
459                         add_ip_addr(&ip6_addr, avalue6);
460                         break;
461 #endif
462                 }
463         freeaddrinfo(ai0);
464 }
465
466 static void
467 quoted_print(FILE *fp, char *str)
468 {
469         int c, qc;
470         char *p = str;
471
472         /* An empty string needs quoting. */
473         if (!*p) {
474                 fputs("\"\"", fp);
475                 return;
476         }
477
478         /*
479          * The value will be surrounded by quotes if it contains spaces
480          * or quotes.
481          */
482         qc = strchr(p, '\'') ? '"'
483             : strchr(p, '"') ? '\''
484             : strchr(p, ' ') || strchr(p, '\t') ? '"'
485             : 0;
486         if (qc)
487                 putc(qc, fp);
488         while ((c = *p++)) {
489                 if (c == '\\' || c == qc)
490                         putc('\\', fp);
491                 putc(c, fp);
492         }
493         if (qc)
494                 putc(qc, fp);
495 }
496
497 static void
498 set_param(const char *name, char *value)
499 {
500         struct param *param;
501         char *ep, *p;
502         size_t buflen, mlen;
503         int i, nval, mib[CTL_MAXNAME];
504         struct {
505                 int i;
506                 char s[MAXPATHLEN];
507         } buf;
508
509         static int paramlistsize;
510
511         /* Separate the name from the value, if not done already. */
512         if (name == NULL) {
513                 name = value;
514                 if ((value = strchr(value, '=')))
515                         *value++ = '\0';
516         }
517
518         /* Check for repeat parameters */
519         for (i = 0; i < nparams; i++)
520                 if (!strcmp(name, params[i].name.iov_base)) {
521                         memcpy(params + i, params + i + 1,
522                             (--nparams - i) * sizeof(struct param));
523                         break;
524                 }
525
526         /* Make sure there is room for the new param record. */
527         if (!nparams) {
528                 paramlistsize = 32;
529                 params = malloc(paramlistsize * sizeof(*params));
530                 param_values = malloc(paramlistsize * sizeof(*param_values));
531                 if (params == NULL || param_values == NULL)
532                         err(1, "malloc");
533         } else if (nparams >= paramlistsize) {
534                 paramlistsize *= 2;
535                 params = realloc(params, paramlistsize * sizeof(*params));
536                 param_values = realloc(param_values,
537                     paramlistsize * sizeof(*param_values));
538                 if (params == NULL)
539                         err(1, "realloc");
540         }
541
542         /* Look up the paramter. */
543         param_values[nparams] = value;
544         param = params + nparams++;
545         *(const void **)&param->name.iov_base = name;
546         param->name.iov_len = strlen(name) + 1;
547         /* Trivial values - no value or errmsg. */
548         if (value == NULL) {
549                 param->value.iov_base = NULL;
550                 param->value.iov_len = 0;
551                 return;
552         }
553         if (!strcmp(name, "errmsg")) {
554                 param->value.iov_base = value;
555                 param->value.iov_len = ERRMSG_SIZE;
556                 return;
557         }
558         mib[0] = 0;
559         mib[1] = 3;
560         snprintf(buf.s, sizeof(buf.s), SJPARAM ".%s", name);
561         mlen = sizeof(mib) - 2 * sizeof(int);
562         if (sysctl(mib, 2, mib + 2, &mlen, buf.s, strlen(buf.s)) < 0)
563                 errx(1, "unknown parameter: %s", name);
564         mib[1] = 4;
565         buflen = sizeof(buf);
566         if (sysctl(mib, (mlen / sizeof(int)) + 2, &buf, &buflen, NULL, 0) < 0)
567                 err(1, "sysctl(0.4.%s)", name);
568         /*
569          * See if this is an array type.
570          * Treat non-arrays as an array of one.
571          */
572         p = strchr(buf.s, '\0');
573         nval = 1;
574         if (p - 2 >= buf.s && !strcmp(p - 2, ",a")) {
575                 if (value[0] == '\0' ||
576                     (value[0] == '-' && value[1] == '\0')) {
577                         param->value.iov_base = value;
578                         param->value.iov_len = 0;
579                         return;
580                 }
581                 p[-2] = 0;
582                 for (p = strchr(value, ','); p; p = strchr(p + 1, ',')) {
583                         *p = '\0';
584                         nval++;
585                 }
586         }
587         
588         /* Set the values according to the parameter type. */
589         switch (buf.i & CTLTYPE) {
590         case CTLTYPE_INT:
591         case CTLTYPE_UINT:
592                 param->value.iov_len = nval * sizeof(int);
593                 break;
594         case CTLTYPE_LONG:
595         case CTLTYPE_ULONG:
596                 param->value.iov_len = nval * sizeof(long);
597                 break;
598         case CTLTYPE_STRUCT:
599                 if (!strcmp(buf.s, "S,in_addr"))
600                         param->value.iov_len = nval * sizeof(struct in_addr);
601 #ifdef INET6
602                 else if (!strcmp(buf.s, "S,in6_addr"))
603                         param->value.iov_len = nval * sizeof(struct in6_addr);
604 #endif
605                 else
606                         errx(1, "%s: unknown parameter structure (%s)",
607                             name, buf.s);
608                 break;
609         case CTLTYPE_STRING:
610                 if (!strcmp(name, "path")) {
611                         param->value.iov_base = malloc(MAXPATHLEN);
612                         if (param->value.iov_base == NULL)
613                                 err(1, "malloc");
614                         if (realpath(value, param->value.iov_base) == NULL)
615                                 err(1, "%s: realpath(%s)", name, value);
616                         if (chdir(param->value.iov_base) != 0)
617                                 err(1, "chdir: %s",
618                                     (char *)param->value.iov_base);
619                 } else
620                         param->value.iov_base = value;
621                 param->value.iov_len = strlen(param->value.iov_base) + 1;
622                 return;
623         default:
624                 errx(1, "%s: unknown parameter type %d (%s)",
625                     name, buf.i, buf.s);
626         }
627         param->value.iov_base = malloc(param->value.iov_len);
628         for (i = 0; i < nval; i++) {
629                 switch (buf.i & CTLTYPE) {
630                 case CTLTYPE_INT:
631                         ((int *)param->value.iov_base)[i] =
632                             strtol(value, &ep, 10);
633                         if (ep[0] != '\0')
634                                 errx(1, "%s: non-integer value \"%s\"",
635                                     name, value);
636                         break;
637                 case CTLTYPE_UINT:
638                         ((unsigned *)param->value.iov_base)[i] =
639                             strtoul(value, &ep, 10);
640                         if (ep[0] != '\0')
641                                 errx(1, "%s: non-integer value \"%s\"",
642                                     name, value);
643                         break;
644                 case CTLTYPE_LONG:
645                         ((long *)param->value.iov_base)[i] =
646                             strtol(value, &ep, 10);
647                         if (ep[0] != '\0')
648                             errx(1, "%s: non-integer value \"%s\"",
649                                 name, value);
650                         break;
651                 case CTLTYPE_ULONG:
652                         ((unsigned long *)param->value.iov_base)[i] =
653                             strtoul(value, &ep, 10);
654                         if (ep[0] != '\0')
655                             errx(1, "%s: non-integer value \"%s\"",
656                                 name, value);
657                         break;
658                 case CTLTYPE_STRUCT:
659                         if (!strcmp(buf.s, "S,in_addr")) {
660                                 if (inet_pton(AF_INET, value,
661                                     &((struct in_addr *)
662                                     param->value.iov_base)[i]) != 1)
663                                         errx(1, "%s: not an IPv4 address: %s",
664                                             name, value);
665                         }
666 #ifdef INET6
667                         else if (!strcmp(buf.s, "S,in6_addr")) {
668                                 if (inet_pton(AF_INET6, value,
669                                     &((struct in6_addr *)
670                                     param->value.iov_base)[i]) != 1)
671                                         errx(1, "%s: not an IPv6 address: %s",
672                                             name, value);
673                         }
674 #endif
675                 }
676                 if (i > 0)
677                         value[-1] = ',';
678                 value = strchr(value, '\0') + 1;
679         }
680 }
681
682 static void
683 usage(void)
684 {
685
686         (void)fprintf(stderr,
687             "usage: jail [-d] [-h] [-i] [-J jid_file] "
688                         "[-l -u username | -U username]\n"
689             "            [-c | -m] param=value ... [command=command ...]\n"
690             "       jail [-r jail]\n");
691         exit(1);
692 }