diff options
| author | Bjoern A. Zeeb <bz@FreeBSD.org> | 2008-11-29 14:32:14 +0000 |
|---|---|---|
| committer | Bjoern A. Zeeb <bz@FreeBSD.org> | 2008-11-29 14:32:14 +0000 |
| commit | 413628a7e3d23a897cd959638d325395e4c9691b (patch) | |
| tree | 2b8f4835032d12a0e61cc40dad151e279cf7a49f /usr.sbin/jexec | |
| parent | f0f4475a13fe3545c10e9b3e8c69679c4abfb1b3 (diff) | |
Notes
Diffstat (limited to 'usr.sbin/jexec')
| -rw-r--r-- | usr.sbin/jexec/Makefile | 2 | ||||
| -rw-r--r-- | usr.sbin/jexec/jexec.8 | 43 | ||||
| -rw-r--r-- | usr.sbin/jexec/jexec.c | 245 |
3 files changed, 213 insertions, 77 deletions
diff --git a/usr.sbin/jexec/Makefile b/usr.sbin/jexec/Makefile index 2bf817cb84b6b..049ccd46e2e28 100644 --- a/usr.sbin/jexec/Makefile +++ b/usr.sbin/jexec/Makefile @@ -6,4 +6,6 @@ DPADD= ${LIBUTIL} LDADD= -lutil WARNS?= 6 +CFLAGS+= -DSUPPORT_OLD_XPRISON + .include <bsd.prog.mk> diff --git a/usr.sbin/jexec/jexec.8 b/usr.sbin/jexec/jexec.8 index 40c4979ad494a..bdda23d0209b3 100644 --- a/usr.sbin/jexec/jexec.8 +++ b/usr.sbin/jexec/jexec.8 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 26, 2008 +.Dd November 29, 2008 .Dt JEXEC 8 .Os .Sh NAME @@ -34,22 +34,36 @@ .Sh SYNOPSIS .Nm .Op Fl u Ar username | Fl U Ar username -.Op Fl h Ar hostname | Fl h Ar ip-number | Ar jid -.Ar command ... +.Op Fl n Ar jailname +.Ar jid command ... .Sh DESCRIPTION The .Nm utility executes .Ar command -inside the jail identified by -.Ar hostname +inside the jail identified by either +.Ar jailname or -.Ar ip-number -or -.Ar jid . +.Ar jid +or both. +.Pp +If the jail cannot be identified uniquely by the given parameters, +an error message is printed. +.Nm +will also check the state of the jail (once supported) to be +.Dv ALIVE +and ignore jails in other states. +The mandatory argument +.Ar jid +is the unique jail identifier as given by +.Xr jls 8 . +In case you only want to match on other criteria, give an empty string. .Pp The following options are available: .Bl -tag -width indent +.It Fl n Ar jailname +The name of the jail, if given upon creation of the jail. +This is not the hostname of the jail. .It Fl u Ar username The user name from host environment as whom the .Ar command @@ -59,9 +73,6 @@ The user name from jailed environment as whom the .Ar command should run. .El -.Sh "CAUTIONS" -Only jid is guaranteed to uniquely identify a jail. -Hostname or ip-number only work here if matched to one unique jail. .Sh SEE ALSO .Xr jail_attach 2 , .Xr jail 8 , @@ -71,3 +82,13 @@ The .Nm utility was added in .Fx 5.1 . +.Sh BUGS +If the jail is not identified by +.Ar jid +there is a possible race in between the lookup of the jail +and executing the command inside the jail. +Giving a +.Ar jid +has a similar race as another process can stop the jail and +start another one after the user looked up the +.Ar jid . diff --git a/usr.sbin/jexec/jexec.c b/usr.sbin/jexec/jexec.c index 137eec310a8d7..69bc8f02448a0 100644 --- a/usr.sbin/jexec/jexec.c +++ b/usr.sbin/jexec/jexec.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2003 Mike Barcroft <mike@FreeBSD.org> + * Copyright (c) 2008 Bjoern A. Zeeb <bz@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,20 +31,171 @@ #include <sys/jail.h> #include <sys/sysctl.h> -#include <arpa/inet.h> +#include <netinet/in.h> #include <err.h> #include <errno.h> -#include <limits.h> #include <login_cap.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <pwd.h> #include <unistd.h> -#include <string.h> static void usage(void); -static int addr2jid(const char *addr); + +#ifdef SUPPORT_OLD_XPRISON +static +char *lookup_xprison_v1(void *p, char *end, int *id) +{ + struct xprison_v1 *xp; + + if (id == NULL) + errx(1, "Internal error. Invalid ID pointer."); + + if ((char *)p + sizeof(struct xprison_v1) > end) + errx(1, "Invalid length for jail"); + + xp = (struct xprison_v1 *)p; + + *id = xp->pr_id; + return ((char *)(xp + 1)); +} +#endif + +static +char *lookup_xprison_v3(void *p, char *end, int *id, char *jailname) +{ + struct xprison *xp; + char *q; + int ok; + + if (id == NULL) + errx(1, "Internal error. Invalid ID pointer."); + + if ((char *)p + sizeof(struct xprison) > end) + errx(1, "Invalid length for jail"); + + xp = (struct xprison *)p; + ok = 1; + + /* Jail state and name. */ + if (xp->pr_state < 0 || xp->pr_state > + (int)((sizeof(prison_states) / sizeof(struct prison_state)))) + errx(1, "Invalid jail state."); + else if (xp->pr_state != PRISON_STATE_ALIVE) + ok = 0; + if (jailname != NULL) { + if (xp->pr_name == NULL) + ok = 0; + else if (strcmp(jailname, xp->pr_name) != 0) + ok = 0; + } + + q = (char *)(xp + 1); + /* IPv4 addresses. */ + q += (xp->pr_ip4s * sizeof(struct in_addr)); + if ((char *)q > end) + errx(1, "Invalid length for jail"); + /* IPv6 addresses. */ + q += (xp->pr_ip6s * sizeof(struct in6_addr)); + if ((char *)q > end) + errx(1, "Invalid length for jail"); + + if (ok) + *id = xp->pr_id; + return (q); +} + +static int +lookup_jail(int jid, char *jailname) +{ + size_t i, j, len; + void *p, *q; + int version, id, xid, count; + + if (sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1) + err(1, "sysctlbyname(): security.jail.list"); + + j = len; + for (i = 0; i < 4; i++) { + if (len <= 0) + exit(0); + p = q = malloc(len); + if (p == NULL) + err(1, "malloc()"); + + if (sysctlbyname("security.jail.list", q, &len, NULL, 0) == -1) { + if (errno == ENOMEM) { + free(p); + p = NULL; + len += j; + continue; + } + err(1, "sysctlbyname(): security.jail.list"); + } + break; + } + if (p == NULL) + err(1, "sysctlbyname(): security.jail.list"); + if (len < sizeof(int)) + errx(1, "This is no prison. Kernel and userland out of sync?"); + version = *(int *)p; + if (version > XPRISON_VERSION) + errx(1, "Sci-Fi prison. Kernel/userland out of sync?"); + + count = 0; + xid = -1; + for (; q != NULL && (char *)q + sizeof(int) < (char *)p + len;) { + version = *(int *)q; + if (version > XPRISON_VERSION) + errx(1, "Sci-Fi prison. Kernel/userland out of sync?"); + id = -1; + switch (version) { +#ifdef SUPPORT_OLD_XPRISON + case 1: + if (jailname != NULL) + errx(1, "Version 1 prisons did not " + "support jail names."); + q = lookup_xprison_v1(q, (char *)p + len, &id); + break; + case 2: + errx(1, "Version 2 was used by multi-IPv4 jail " + "implementations that never made it into the " + "official kernel."); + /* NOTREACHED */ + break; +#endif + case 3: + q = lookup_xprison_v3(q, (char *)p + len, &id, jailname); + break; + default: + errx(1, "Prison unknown. Kernel/userland out of sync?"); + /* NOTREACHED */ + break; + } + /* Possible match. */ + if (id > 0) { + /* Do we have a jail ID to match as well? */ + if (jid > 0) { + if (jid == id) { + xid = id; + count++; + } + } else { + xid = id; + count++; + } + } + } + + free(p); + + if (count != 1) + errx(1, "Could not uniquely identify the jail."); + + return (xid); +} #define GET_USER_INFO do { \ pwd = getpwnam(username); \ @@ -68,13 +220,18 @@ main(int argc, char *argv[]) login_cap_t *lcap = NULL; struct passwd *pwd = NULL; gid_t groups[NGROUPS]; - int ch, ngroups, uflag, Uflag, hflag; - char *username; - ch = uflag = Uflag = hflag = 0; - username = NULL; + int ch, ngroups, uflag, Uflag; + char *jailname, *username; + + ch = uflag = Uflag = 0; + jailname = username = NULL; + jid = -1; - while ((ch = getopt(argc, argv, "u:U:h")) != -1) { + while ((ch = getopt(argc, argv, "i:n:u:U:")) != -1) { switch (ch) { + case 'n': + jailname = optarg; + break; case 'u': username = optarg; uflag = 1; @@ -83,9 +240,6 @@ main(int argc, char *argv[]) username = optarg; Uflag = 1; break; - case 'h': - hflag = 1; - break; default: usage(); } @@ -94,15 +248,22 @@ main(int argc, char *argv[]) argv += optind; if (argc < 2) usage(); + if (strlen(argv[0]) > 0) { + jid = (int)strtol(argv[0], NULL, 10); + if (errno) + err(1, "Unable to parse jail ID."); + } + if (jid <= 0 && jailname == NULL) { + fprintf(stderr, "Neither jail ID nor jail name given.\n"); + usage(); + } if (uflag && Uflag) usage(); if (uflag) GET_USER_INFO; - if (hflag) { - if ((jid = addr2jid(argv[0])) == 0) - errx(1, "jail_attach(): Cannot convert %s to jid", argv[0]); - } else - jid = (int)strtol(argv[0], NULL, 10); + jid = lookup_jail(jid, jailname); + if (jid <= 0) + errx(1, "Cannot identify jail."); if (jail_attach(jid) == -1) err(1, "jail_attach(): %d", jid); if (chdir("/") == -1) @@ -130,54 +291,6 @@ usage(void) fprintf(stderr, "%s%s\n", "usage: jexec [-u username | -U username]", - " [-h hostname | -h ip-number | jid] command ..."); + " [-n jailname] jid command ..."); exit(1); } - -static int -addr2jid(const char *addr) -{ - struct xprison *sxp, *xp; - struct in_addr in; - size_t i, len; - int jid, cnt; - jid = cnt = 0; - - if (sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1) - err(1, "sysctlbyname(): security.jail.list"); - for (i = 0; i < 4; i++) { - if (len <= 0) - err(1, "sysctlbyname(): len <=0"); - sxp = xp = malloc(len); - if (sxp == NULL) - err(1, "malloc()"); - if (sysctlbyname("security.jail.list", xp, &len, NULL, 0) == -1) { - if (errno == ENOMEM) { - free(sxp); - sxp = NULL; - continue; - } - err(1, "sysctlbyname(): security.jail.list"); - } - break; - } - if (sxp == NULL) - err(1, "sysctlbyname(): security.jail.list"); - if (len < sizeof(*xp) || len % sizeof(*xp) || - xp->pr_version != XPRISON_VERSION) - errx(1, "Kernel and userland out of sync"); - for (i = 0; i < len / sizeof(*xp); i++) { - in.s_addr = ntohl(xp->pr_ip); - if ((strcmp(inet_ntoa(in), addr) == 0) || - (strcmp(xp->pr_host, addr) == 0)) { - jid = xp->pr_id; - cnt++; - } - xp++; - } - free(sxp); - if (cnt == 1) - return (jid); - else - return(0); -} |
