]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - usr.sbin/jexec/jexec.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / usr.sbin / jexec / jexec.c
1 /*-
2  * Copyright (c) 2003 Mike Barcroft <mike@FreeBSD.org>
3  * Copyright (c) 2008 Bjoern A. Zeeb <bz@FreeBSD.org>
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  * $FreeBSD$
28  */
29
30 #include <sys/param.h>
31 #include <sys/jail.h>
32 #include <sys/sysctl.h>
33
34 #include <netinet/in.h>
35
36 #include <err.h>
37 #include <errno.h>
38 #include <login_cap.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <pwd.h>
43 #include <unistd.h>
44
45 static void     usage(void);
46
47 #ifdef SUPPORT_OLD_XPRISON
48 static
49 char *lookup_xprison_v1(void *p, char *end, int *id)
50 {
51         struct xprison_v1 *xp;
52
53         if (id == NULL)
54                 errx(1, "Internal error. Invalid ID pointer.");
55
56         if ((char *)p + sizeof(struct xprison_v1) > end)
57                 errx(1, "Invalid length for jail");
58
59         xp = (struct xprison_v1 *)p;
60
61         *id = xp->pr_id;
62         return ((char *)(xp + 1));
63 }
64 #endif
65
66 static
67 char *lookup_xprison_v3(void *p, char *end, int *id, char *jailname)
68 {
69         struct xprison *xp;
70         char *q;
71         int ok;
72
73         if (id == NULL)
74                 errx(1, "Internal error. Invalid ID pointer.");
75
76         if ((char *)p + sizeof(struct xprison) > end)
77                 errx(1, "Invalid length for jail");
78
79         xp = (struct xprison *)p;
80         ok = 1;
81
82         /* Jail state and name. */
83         if (xp->pr_state < 0 || xp->pr_state >=
84             (int)((sizeof(prison_states) / sizeof(struct prison_state))))
85                 errx(1, "Invalid jail state.");
86         else if (xp->pr_state != PRISON_STATE_ALIVE)
87                 ok = 0;
88         if (jailname != NULL) {
89                 if (xp->pr_name[0] == '\0')
90                         ok = 0;
91                 else if (strcmp(jailname, xp->pr_name) != 0)
92                         ok = 0;
93         }
94
95         q = (char *)(xp + 1);
96         /* IPv4 addresses. */
97         q += (xp->pr_ip4s * sizeof(struct in_addr));
98         if ((char *)q > end)
99                 errx(1, "Invalid length for jail");
100         /* IPv6 addresses. */
101         q += (xp->pr_ip6s * sizeof(struct in6_addr));
102         if ((char *)q > end)
103                 errx(1, "Invalid length for jail");
104
105         if (ok)
106                 *id = xp->pr_id;
107         return (q);
108 }
109
110 static int
111 lookup_jail(int jid, char *jailname)
112 {
113         size_t i, j, len;
114         void *p, *q;
115         int version, id, xid, count;
116
117         if (sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1)
118                 err(1, "sysctlbyname(): security.jail.list");
119
120         j = len;
121         for (i = 0; i < 4; i++) {
122                 if (len == 0)
123                         return (-1);
124                 p = q = malloc(len);
125                 if (p == NULL)
126                         err(1, "malloc()");
127
128                 if (sysctlbyname("security.jail.list", q, &len, NULL, 0) == -1) {
129                         if (errno == ENOMEM) {
130                                 free(p);
131                                 p = NULL;
132                                 len += j;
133                                 continue;
134                         }
135                         err(1, "sysctlbyname(): security.jail.list");
136                 }
137                 break;
138         }
139         if (p == NULL)
140                 err(1, "sysctlbyname(): security.jail.list");
141         if (len < sizeof(int))
142                 errx(1, "This is no prison. Kernel and userland out of sync?");
143         version = *(int *)p;
144         if (version > XPRISON_VERSION)
145                 errx(1, "Sci-Fi prison. Kernel/userland out of sync?");
146
147         count = 0;
148         xid = -1;
149         for (; q != NULL && (char *)q + sizeof(int) < (char *)p + len;) {
150                 version = *(int *)q;
151                 if (version > XPRISON_VERSION)
152                         errx(1, "Sci-Fi prison. Kernel/userland out of sync?");
153                 id = -1;
154                 switch (version) {
155 #ifdef SUPPORT_OLD_XPRISON
156                 case 1:
157                         if (jailname != NULL)
158                                 errx(1, "Version 1 prisons did not "
159                                     "support jail names.");
160                         q = lookup_xprison_v1(q, (char *)p + len, &id);
161                         break;
162                 case 2:
163                         errx(1, "Version 2 was used by multi-IPv4 jail "
164                             "implementations that never made it into the "
165                             "official kernel.");
166                         /* NOTREACHED */
167                         break;
168 #endif
169                 case 3:
170                         q = lookup_xprison_v3(q, (char *)p + len, &id, jailname);
171                         break;
172                 default:
173                         errx(1, "Prison unknown. Kernel/userland out of sync?");
174                         /* NOTREACHED */
175                         break;
176                 }
177                 /* Possible match; see if we have a jail ID to match as well.  */
178                 if (id > 0 && (jid <= 0 || id == jid)) {
179                         xid = id;
180                         count++;
181                 }
182         }
183
184         free(p);
185
186         if (count == 1)
187                 return (xid);
188         else if (count > 1)
189                 errx(1, "Could not uniquely identify the jail.");
190         else
191                 return (-1);
192 }
193
194 #define GET_USER_INFO do {                                              \
195         pwd = getpwnam(username);                                       \
196         if (pwd == NULL) {                                              \
197                 if (errno)                                              \
198                         err(1, "getpwnam: %s", username);               \
199                 else                                                    \
200                         errx(1, "%s: no such user", username);          \
201         }                                                               \
202         lcap = login_getpwclass(pwd);                                   \
203         if (lcap == NULL)                                               \
204                 err(1, "getpwclass: %s", username);                     \
205         ngroups = NGROUPS;                                              \
206         if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0) \
207                 err(1, "getgrouplist: %s", username);                   \
208 } while (0)
209
210 int
211 main(int argc, char *argv[])
212 {
213         int jid;
214         login_cap_t *lcap = NULL;
215         struct passwd *pwd = NULL;
216         gid_t groups[NGROUPS];
217         int ch, ngroups, uflag, Uflag;
218         char *jailname, *username;
219
220         ch = uflag = Uflag = 0;
221         jailname = username = NULL;
222         jid = -1;
223
224         while ((ch = getopt(argc, argv, "i:n:u:U:")) != -1) {
225                 switch (ch) {
226                 case 'n':
227                         jailname = optarg;
228                         break;
229                 case 'u':
230                         username = optarg;
231                         uflag = 1;
232                         break;
233                 case 'U':
234                         username = optarg;
235                         Uflag = 1;
236                         break;
237                 default:
238                         usage();
239                 }
240         }
241         argc -= optind;
242         argv += optind;
243         if (argc < 2)
244                 usage();
245         if (strlen(argv[0]) > 0) {
246                 jid = (int)strtol(argv[0], NULL, 10);
247                 if (errno)
248                         err(1, "Unable to parse jail ID.");
249         }
250         if (jid <= 0 && jailname == NULL) {
251                 fprintf(stderr, "Neither jail ID nor jail name given.\n");
252                 usage();
253         }
254         if (uflag && Uflag)
255                 usage();
256         if (uflag)
257                 GET_USER_INFO;
258         jid = lookup_jail(jid, jailname);
259         if (jid <= 0)
260                 errx(1, "Cannot identify jail.");
261         if (jail_attach(jid) == -1)
262                 err(1, "jail_attach(): %d", jid);
263         if (chdir("/") == -1)
264                 err(1, "chdir(): /");
265         if (username != NULL) {
266                 if (Uflag)
267                         GET_USER_INFO;
268                 if (setgroups(ngroups, groups) != 0)
269                         err(1, "setgroups");
270                 if (setgid(pwd->pw_gid) != 0)
271                         err(1, "setgid");
272                 if (setusercontext(lcap, pwd, pwd->pw_uid,
273                     LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0)
274                         err(1, "setusercontext");
275                 login_close(lcap);
276         }
277         if (execvp(argv[1], argv + 1) == -1)
278                 err(1, "execvp(): %s", argv[1]);
279         exit(0);
280 }
281
282 static void
283 usage(void)
284 {
285
286         fprintf(stderr, "%s%s\n",
287                 "usage: jexec [-u username | -U username]",
288                 " [-n jailname] jid command ...");
289         exit(1); 
290 }