aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/jexec
diff options
context:
space:
mode:
authorBjoern A. Zeeb <bz@FreeBSD.org>2008-11-29 14:32:14 +0000
committerBjoern A. Zeeb <bz@FreeBSD.org>2008-11-29 14:32:14 +0000
commit413628a7e3d23a897cd959638d325395e4c9691b (patch)
tree2b8f4835032d12a0e61cc40dad151e279cf7a49f /usr.sbin/jexec
parentf0f4475a13fe3545c10e9b3e8c69679c4abfb1b3 (diff)
Notes
Diffstat (limited to 'usr.sbin/jexec')
-rw-r--r--usr.sbin/jexec/Makefile2
-rw-r--r--usr.sbin/jexec/jexec.843
-rw-r--r--usr.sbin/jexec/jexec.c245
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);
-}