2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * See the COPYING file for license information.
18 * Copyright (c) 2006, 2007 Guillaume Chazarain <guichaz@yahoo.fr>
22 #include <sys/resource.h>
36 * Copied from linux-2.6/include/linux/sched.h
37 * Compile on old libc, run on new kernel :-)
39 #define CLONE_NEWNS 0x00020000 /* New namespace */
40 #define CLONE_NEWUTS 0x04000000 /* New utsname */
41 #define CLONE_NEWIPC 0x08000000 /* New ipcs */
46 * We find out the UID/GID infos before the chroot as we may need /etc/passwd.
47 * The results are stored in this struct.
57 * The temporary stack for the cloned process
59 #define STACK_SIZE (1 << 20)
60 static char stack
[STACK_SIZE
];
63 * Get the clone(2) flags from the command line arguments
65 static int get_flags(struct gengetopt_args_info
*args_info
)
70 if (args_info
->mount_flag
)
73 if (args_info
->uname_flag
)
74 flags
|= CLONE_NEWUTS
;
76 if (args_info
->ipc_flag
)
77 flags
|= CLONE_NEWIPC
;
79 for (i
= 0; i
< args_info
->mask_given
; i
++)
80 flags
|= args_info
->mask_arg
[i
];
86 * Wrapper for malloc, exit instead of returning NULL
88 static void *xmalloc(int size
)
90 void *res
= malloc(size
);
99 * Do a chroot() if requested. Do also a chdir() to prevent the simplest
100 * form a chroot escape.
102 static void perform_chroot(struct gengetopt_args_info
*ggo
)
104 if (ggo
->chroot_given
) {
105 if (chroot(ggo
->chroot_arg
) < 0) {
110 if (chdir("/") < 0) {
111 perror("chdir(\"/\")");
118 * Find the UID associated to a username
120 static uid_t
name_to_uid(const char *name
)
125 pass
= getpwnam(name
);
131 fprintf(stderr
, "%s: user not found\n", name
);
139 * Find the GID associated to a group
141 static gid_t
name_to_gid(const char *name
)
146 grp
= getgrnam(name
);
152 fprintf(stderr
, "%s: group not found\n", name
);
160 * Transform the command line args (ggo) into our struct (user)
162 static void get_user_infos(struct user_info
*user
,
163 struct gengetopt_args_info
*ggo
)
165 size_t nr_groups
= ggo
->gid_given
+ ggo
->groupname_given
;
167 if (ggo
->uid_given
&& ggo
->username_given
) {
168 fputs("--uid and --username are mutually exclusive\n", stderr
);
173 user
->uid
= ggo
->uid_arg
;
174 else if (ggo
->username_given
)
175 user
->uid
= name_to_uid(ggo
->username_arg
);
180 user
->groups
= xmalloc(nr_groups
* sizeof(gid_t
));
185 if (ggo
->gid_given
) {
186 memcpy(user
->groups
, ggo
->gid_arg
,
187 ggo
->gid_given
* sizeof(gid_t
));
188 user
->nr_groups
= ggo
->gid_given
;
191 if (ggo
->groupname_given
) {
193 for (i
= 0; i
< ggo
->groupname_given
; i
++) {
194 gid_t gid
= name_to_gid(ggo
->groupname_arg
[i
]);
195 user
->groups
[user
->nr_groups
] = gid
;
201 user
->gid
= user
->groups
[0];
203 user
->gid
= user
->uid
;
204 if (user
->uid
!= -1) {
206 user
->groups
= &user
->uid
;
212 * Change the UID and GID if requested.
214 static void change_id(struct user_info
*user
)
216 if (user
->nr_groups
) {
217 if (setgroups(user
->nr_groups
, user
->groups
) < 0) {
223 if (user
->gid
!= -1) {
224 if (setgid(user
->gid
) < 0) {
230 if (user
->uid
!= -1) {
231 if (setuid(user
->uid
) < 0) {
241 static void daemonize(void)
259 perror("chdir(\"/\")");
261 fd
= open("/dev/null", O_RDWR
, 0);
263 if (dup2(fd
, STDIN_FILENO
) < 0)
264 perror("dup2(fd, STDIN_FILENO)");
265 if (dup2(fd
, STDOUT_FILENO
) < 0)
266 perror("dup2(fd, STDOUT_FILENO)");
267 if (dup2(fd
, STDERR_FILENO
) < 0)
268 perror("dup2(fd, STDERR_FILENO)");
269 if (fd
> STDERR_FILENO
)
272 perror("open(\"/dev/null\")");
276 * The new process in its own namespace can now execute the command passed as
277 * argument or a shell
279 static int child_process(void *ggo_arg
)
281 struct gengetopt_args_info
*ggo
= ggo_arg
;
282 struct user_info user
;
285 if (setpriority(PRIO_PROCESS
, 0, ggo
->nice_arg
) < 0) {
286 perror("setpriority");
290 get_user_infos(&user
, ggo
);
294 if (ggo
->daemonize_flag
)
297 if (ggo
->alarm_given
)
298 alarm(ggo
->alarm_arg
);
300 if (ggo
->inputs_num
) {
301 char **argv
= xmalloc(sizeof(char *) * (ggo
->inputs_num
+ 1));
302 memcpy(argv
, ggo
->inputs
, ggo
->inputs_num
* sizeof(char *));
303 argv
[ggo
->inputs_num
] = NULL
;
304 execvp(argv
[0], argv
);
307 char *argv
[] = { NULL
, NULL
};
308 char *shell
= getenv("SHELL");
312 execv(argv
[0], argv
);
319 int main(int argc
, char *argv
[])
321 struct gengetopt_args_info args_info
;
326 ret
= cmdline_parser(argc
, argv
, &args_info
);
330 flags
= get_flags(&args_info
);
331 if (argc
- args_info
.inputs_num
<= 1) {
332 fputs("No flag specified, exiting\n", stderr
);
336 if (args_info
.alarm_given
&&
337 (args_info
.alarm_arg
< 0 || args_info
.alarm_arg
> 65535)) {
339 "The alarm value must be between 0 and 65535 got: %d\n",
340 args_info
.alarm_arg
);
345 ret
= clone(child_process
, &stack
[STACK_SIZE
- 1], flags
, &args_info
);
351 ret
= waitpid(ret
, &status
, 0);
357 ret
= WEXITSTATUS(status
);