1 /*
2 * the jailkit chroot() shell
3 * this program does a safe chroot() and then executes the shell
4 * that the user has within that new root (according to newroot/etc/passwd)
5 *
6 * I tried to merge some of the ideas from chrsh by Aaron D. Gifford,
7 * start-stop-daemon from Marek Michalkiewicz and suexec by the Apache
8 * group in this shell
9 *
10
11 Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2015, 2016 Olivier Sessink
12 All rights reserved.
13
14 Redistribution and use in source and binary forms, with or without
15 modification, are permitted provided that the following conditions
16 are met:
17 * Redistributions of source code must retain the above copyright
18 notice, this list of conditions and the following disclaimer.
19 * Redistributions in binary form must reproduce the above
20 copyright notice, this list of conditions and the following
21 disclaimer in the documentation and/or other materials provided
22 with the distribution.
23 * The names of its contributors may not be used to endorse or
24 promote products derived from this software without specific
25 prior written permission.
26
27 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
30 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
31 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
32 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
33 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
34 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
35 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
37 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38 POSSIBILITY OF SUCH DAMAGE.
39 */
40
41 #include "config.h"
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <fcntl.h>
47 #include <string.h>
48 #include <pwd.h>
49 #include <grp.h>
50 #include <sys/stat.h>
51 #include <sys/types.h>
52 #include <errno.h>
53 #include <syslog.h>
54 #include <limits.h>
55 #include <signal.h>
56
57 #ifdef HAVE_SYS_CAPABILITY_H
58 #include <sys/capability.h>
59 #endif
60
61 /* #define DEBUG */
62
63 #ifdef DEBUG
64 #define DEBUG_MSG printf
65 #else
66 #define DEBUG_MSG(args...)
67 /**/
68 #endif
69
70 #define PROGRAMNAME "jk_chrootsh"
71 #define CONFIGFILE INIPREFIX"/jk_chrootsh.ini"
72
73 #include "jk_lib.h"
74 #include "utils.h"
75 #include "iniparser.h"
76 #include "passwdparser.h"
77
78 /* doesn't compile on FreeBSD without this */
79 extern char **environ;
80
81 /*
82 typedef struct {
83 char *key;
84 char *value;
85 } Tsavedenv;
86
87 static Tsavedenv *savedenv_new(const char *key) {
88 Tsavedenv *savedenv;
89 char *val = getenv(key);
90 if (!val) return NULL;
91 savedenv = malloc(sizeof(Tsavedenv));
92 savedenv->key = strdup(key);
93 savedenv->value = strdup(val);
94 return savedenv;
95 }
96
97 static void savedenv_restore(Tsavedenv *savedenv) {
98 if (savedenv) {
99 setenv(savedenv->key, savedenv->value, 1);
100 DEBUG_MSG("restored %s=%s\n",savedenv->key, savedenv->value);
101 }
102 }
103
104 static void savedenv_free(Tsavedenv *savedenv) {
105 if (savedenv) {
106 free(savedenv->key);
107 free(savedenv->value);
108 free(savedenv);
109 }
110 }
111 */
112
in_array(char ** haystack,char * needle,int needlelen)113 static int in_array(char **haystack, char * needle, int needlelen) {
114 if (haystack && needle) {
115 char **tmp = haystack;
116 while (*tmp) {
117 if (strlen(*tmp)==needlelen && strncmp(*tmp, needle, needlelen)==0) return 1;
118 tmp++;
119 }
120 }
121 return 0;
122 }
123
unset_environ_except(char ** except)124 static void unset_environ_except(char **except) {
125 char **tmp = environ;
126 if (environ == NULL || *tmp==NULL || except==NULL || *except==NULL) {
127 clearenv();
128 return;
129 }
130 while (*tmp) {
131 char* pos = strchr(*tmp, '=');
132 if (pos == NULL) {
133 /* invalid environment variable, are we being hacked? clear them all! */
134 clearenv();
135 return;
136 }
137 if (!in_array(except, *tmp, pos-*tmp)) {
138 char *key = strndup(*tmp, pos-*tmp);
139 unsetenv(key);
140 free(key);
141 tmp = environ;
142 continue;
143 }
144 tmp++;
145 }
146 }
147
have_capabilities(void)148 static int have_capabilities(void) {
149 #ifdef HAVE_CAP_GET_PROC
150 cap_t caps = cap_get_proc();
151 if (caps) {
152 cap_flag_value_t value_p;
153 cap_get_flag(caps, CAP_SYS_CHROOT, CAP_EFFECTIVE,&value_p);
154 cap_free(caps);
155 return (value_p);
156 }
157 #endif /*HAVE_CAP_GET_PROC*/
158 return 0;
159 }
160
signal_handler(int signum)161 void signal_handler(int signum) {
162 syslog(LOG_ERR, "abort, received signal %s (%d)", strsignal(signum),signum);
163 exit(666);
164 }
165
main(int argc,char ** argv)166 int main (int argc, char **argv) {
167 unsigned int i;
168 unsigned int use_capabilities=0;
169 int ret;
170 char **newargv;
171 char *user=NULL, *tmp=NULL;
172
173 struct passwd *pw=NULL;
174 struct group *gr=NULL;
175 unsigned long ngroups_max=NGROUPS_MAX;
176 unsigned long ngroups=0;
177 gid_t *gids;
178 struct passwd *intpw=NULL; /* for internal_getpwuid() */
179 char *jaildir=NULL, *newhome=NULL, *shell=NULL;
180 Tiniparser *parser=NULL;
181 char **envs=NULL;
182 unsigned int relax_home_group_permissions=0;
183 unsigned int relax_home_other_permissions=0;
184 unsigned int relax_home_group=0;
185 unsigned int relax_home_owner=0;
186 unsigned int injail_login_shell=0;
187 char *injail_shell=NULL;
188 unsigned int skip_injail_passwd_check=0;
189
190 DEBUG_MSG(PROGRAMNAME", started\n");
191 /* open the log facility */
192 openlog(PROGRAMNAME, LOG_PID, LOG_AUTH);
193
194 /* attach signal handler */
195 signal(SIGILL,signal_handler);
196 signal(SIGSEGV,signal_handler);
197 signal(SIGTERM,signal_handler);
198 signal(SIGFPE,signal_handler);
199
200 /* check if it PRORAMNAME that the user wants */
201 tmp = strrchr(argv[0], '/');
202 if (!tmp) {
203 tmp = argv[0];
204 } else {
205 tmp++;
206 }
207
208 if (strcmp(tmp, PROGRAMNAME) != 0 && strcmp(tmp, "su")!= 0 && strcmp(tmp, "-su")!= 0 && strcmp(tmp, "-"PROGRAMNAME)!=0) {
209 DEBUG_MSG("wrong name, tmp=%s, &tmp[1]=%s\n", tmp, &tmp[1]);
210 syslog(LOG_ERR, "abort, "PROGRAMNAME" is called as %s", argv[0]);
211 exit(1);
212 }
213
214 /* now test if we are setuid root (the effective user id must be 0, and the real user id > 0 */
215 if (geteuid() != 0) {
216 if (have_capabilities()) {
217 use_capabilities=1;
218 } else {
219 syslog(LOG_ERR, "abort, effective user ID is not 0, possibly "PROGRAMNAME" is not setuid root");
220 exit(11);
221 }
222 }
223 if (getuid() == 0) {
224 syslog(LOG_ERR, "abort, "PROGRAMNAME" is run by root, which does not make sense because user root can break out of a jail anyway");
225 exit(12);
226 }
227
228 DEBUG_MSG("get user info\n");
229 /* get user info based on the users name and not on the uid. this enables support
230 for systems with multiple users with the same user id */
231 tmp = getenv("USER");
232 if (tmp && strlen(tmp)) {
233 user = strdup(tmp);
234 }
235 if (user) {
236 pw = getpwnam(user);
237 } else {
238 pw = getpwuid(getuid());
239 }
240 if (!pw) {
241 syslog(LOG_ERR, "abort, failed to get user information for user ID %u: %s, check /etc/passwd", getuid(), strerror(errno));
242 exit(13);
243 }
244 if (!pw->pw_name || strlen(pw->pw_name)==0) {
245 syslog(LOG_ERR, "abort, got an empty username for user ID %u: %s, check /etc/passwd", getuid(), strerror(errno));
246 exit(13);
247 }
248 if (user && strcmp(user,pw->pw_name)!=0) {
249 syslog(LOG_ERR, "abort, asked for user %s, got user info for %s", user, pw->pw_name);
250 exit(13);
251 }
252 if (pw->pw_uid != getuid()) {
253 syslog(LOG_ERR, "abort, started by user ID %u, got user info %s with user ID %d,", getuid(), pw->pw_name, pw->pw_uid);
254 exit(13);
255 }
256 DEBUG_MSG("got user %s\nget group info\n",pw->pw_name);
257 gr = getgrgid(getgid());
258 if (!gr) {
259 syslog(LOG_ERR, "abort, failed to get group information for group ID %u: %s, check /etc/group", getgid(), strerror(errno));
260 exit(13);
261 }
262 DEBUG_MSG("get additional groups\n");
263 /* ngroups_max = sysconf(_SC_NGROUPS_MAX);*/
264 gids = malloc(ngroups_max * sizeof(gid_t));
265 ngroups = getgroups(ngroups_max,gids);
266 if (ngroups == -1) {
267 syslog(LOG_ERR, "abort, failed to get additional group information: %s, check /etc/group", strerror(errno));
268 exit(13);
269 }
270 #ifdef DEBUG
271 printf("got additional groups ");
272 for (i=0;i<ngroups;i++) {
273 printf("%u, ",gids[i]);
274 }
275 printf("\n");
276 #endif
277
278 /* make sure the jailkit config directory is owned root:root and not writable for others */
279 if ( (testsafepath(INIPREFIX, 0, 0) &~TESTPATH_GROUPW) != 0 ) {
280 syslog(LOG_ERR, "abort, jailkit configuration directory "INIPREFIX" is not safe; it should be owned 0:0 and not writable for others");
281 exit(14);
282 }
283 parser = new_iniparser(CONFIGFILE);
284 if (parser) {
285 char *groupsec, *section=NULL, buffer[1024]; /* openbsd complains if this is <1024 */
286 groupsec = strcat(strcpy(malloc0(strlen(gr->gr_name)+7), "group "), gr->gr_name);
287 if (iniparser_has_section(parser, pw->pw_name)) {
288 section = strdup(pw->pw_name);
289 } else if (iniparser_has_section(parser, groupsec)) {
290 section = groupsec;
291 } else if (iniparser_has_section(parser, "DEFAULT")) {
292 section = strdup("DEFAULT");
293 }
294 if (section != groupsec) free(groupsec);
295 if (section) {
296 unsigned int pos = iniparser_get_position(parser) - strlen(section) - 2;
297
298 if (iniparser_get_string_at_position(parser, section, "env", pos, buffer, 1024) > 0) {
299 envs = explode_string(buffer, ',');
300 }
301 relax_home_group_permissions = iniparser_get_int_at_position(parser, section, "relax_home_group_permissions", pos, 0);
302 relax_home_other_permissions = iniparser_get_int_at_position(parser, section, "relax_home_other_permissions", pos, 0);
303 relax_home_group = iniparser_get_int_at_position(parser, section, "relax_home_group", pos, 0);
304 relax_home_owner = iniparser_get_int_at_position(parser, section, "relax_home_owner", pos, 0);
305 if (iniparser_get_string_at_position(parser, section, "injail_shell", pos, buffer, 1024) > 0) {
306 injail_shell = strdup(buffer);
307 }
308 if (injail_shell) {
309 skip_injail_passwd_check = iniparser_get_int_at_position(parser, section, "skip_injail_passwd_check", pos, 0);
310 }
311 injail_login_shell = iniparser_get_int_at_position(parser, section, "injail_login_shell", pos, 0);
312 DEBUG_MSG("section %s: relax_home_group_permissions=%d, relax_home_other_permissions=%d, relax_home_group=%d, injail_shell=%s, skip_injail_passwd_check=%d\n",
313 section, relax_home_group_permissions, relax_home_other_permissions,
314 relax_home_group, injail_shell, skip_injail_passwd_check);
315 free(section);
316 } else {
317 DEBUG_MSG("no relevant section found in configfile\n");
318 }
319 iniparser_close(parser);
320 } else {
321 DEBUG_MSG("no configfile "CONFIGFILE" ??\n");
322 }
323
324 DEBUG_MSG("close filedescriptors\n");
325 /* open file descriptors can be used to break out of a chroot, so we close all of them, except for stdin,stdout and stderr */
326 #ifdef OPEN_MAX
327 i = OPEN_MAX;
328 #elif defined(NOFILE)
329 i = NOFILE;
330 #else
331 i = getdtablesize();
332 #endif
333 while (--i > 2) {
334 /*printf("closing file descriptor %d\n",i);*/
335 while (close(i) != 0 && errno == EINTR);
336 }
337 /* now make sure file descriptors 0 1 and 2 are valid before we (or a child) starts writing to it */
338 while (1) {
339 int fd;
340 fd = open("/dev/null", O_RDWR);
341 if (fd < 0)
342 exit(10);
343 if (fd > 2) {
344 close(fd);
345 break;
346 } else {
347 DEBUG_MSG("re-opening file descriptor %d\n",fd);
348 }
349 }
350
351 /* now we clear the environment, except for values allowed in /etc/jailkit/jk_chrootsh.ini */
352 unset_environ_except(envs);
353 if (envs) {
354 free_array(envs);
355 }
356
357 if (pw->pw_gid != getgid()) {
358 syslog(LOG_ERR, "abort, the group ID from /etc/passwd (%u) does not match the group ID we run with (%u)", pw->pw_gid, getgid());
359 exit(15);
360 }
361 if (!pw->pw_dir || strlen(pw->pw_dir) ==0) {
362 syslog(LOG_ERR, "abort, got an empty home directory for user %s (%u)", pw->pw_name, getuid());
363 exit(16);
364 }
365 if (strstr(pw->pw_dir, "/./") == NULL) {
366 syslog(LOG_ERR, "abort, homedir '%s' for user %s (%u) does not contain the jail separator <jail>/./<home>", pw->pw_dir, pw->pw_name, getuid());
367 exit(17);
368 }
369 DEBUG_MSG("get jaildir\n");
370 if (!getjaildir(pw->pw_dir, &jaildir, &newhome)) {
371 syslog(LOG_ERR, "abort, failed to read the jail and the home from %s for user %s (%u)",pw->pw_dir, pw->pw_name, getuid());
372 exit(17);
373 }
374 DEBUG_MSG("dir=%s,jaildir=%s,newhome=%s\n",pw->pw_dir, jaildir, newhome);
375 DEBUG_MSG("get chdir()\n");
376 if (chdir(jaildir) != 0) {
377 syslog(LOG_ERR, "abort, chdir(%s) failed: %s, check the permissions for %s",jaildir,strerror(errno),jaildir);
378 exit(19);
379 } else {
380 char test[1024];
381 /* test if it really succeeded */
382 if (getcwd(test, 1024)==NULL || !dirs_equal(jaildir, test)) {
383 syslog(LOG_ERR, "abort, the current dir is %s after chdir(%s), but it should be %s",test,jaildir,jaildir);
384 exit(21);
385 }
386 }
387
388 /* here do test the ownership of the jail and the homedir and such
389 the function testsafepath doe exit itself on any failure */
390 if (!basicjailissafe(jaildir)) {
391 syslog(LOG_ERR, "abort, %s is not a safe jail, check ownership and permissions.", jaildir);
392 exit(53);
393 }
394 ret = testsafepath(pw->pw_dir, getuid(), getgid());
395 if ((ret & TESTPATH_NOREGPATH) ) {
396 syslog(LOG_ERR, "abort, path %s is not a directory", pw->pw_dir);
397 exit(53);
398 }
399 if (!relax_home_owner && (ret & TESTPATH_OWNER) ) {
400 syslog(LOG_ERR, "abort, path %s is not owned by %u", pw->pw_dir,getuid());
401 exit(53);
402 }
403 if (!relax_home_group && (ret & TESTPATH_GROUP)) {
404 syslog(LOG_ERR, "abort, path %s does not have group owner %u, set option 'relax_home_group' to relax this check", pw->pw_dir,getgid());
405 exit(53);
406 }
407 if (!relax_home_group_permissions && (ret & TESTPATH_GROUPW)) {
408 syslog(LOG_ERR, "abort, path %s is group writable, set option 'relax_home_group_permissions' to relax this check", pw->pw_dir);
409 exit(53);
410 }
411 if (!relax_home_other_permissions && (ret & TESTPATH_OTHERW)) {
412 syslog(LOG_ERR, "abort, path %s is writable for other, set option 'relax_home_other_permissions' to relax this check", pw->pw_dir);
413 exit(53);
414 }
415 /* do a final log message */
416 tmp = implode_array(&argv[1], argc-1, " ");
417 syslog(LOG_INFO, "now entering jail %s for user %s (%u) with arguments %s", jaildir, pw->pw_name, getuid(), tmp);
418 free(tmp);
419
420 DEBUG_MSG("chroot()\n");
421 /* do the chroot() call */
422 if (chroot(jaildir)) {
423 syslog(LOG_ERR, "abort, chroot(%s) failed: %s, check the permissions for %s", jaildir, strerror(errno), jaildir);
424 exit(33);
425 }
426
427 if (use_capabilities) {
428 #ifdef HAVE_CAP_GET_PROC
429 cap_t caps;
430 cap_value_t capv[1];
431 /* drop chroot capability, should we drop all other capabilities that may be used to escape from the jail too ? */
432 if ((caps = cap_get_proc()) == NULL) {
433 syslog(LOG_ERR, "abort, failed to retrieve current capabilities: %s", strerror(errno));
434 exit(101);
435 }
436 capv[0] = CAP_SYS_CHROOT;
437 /* other capabilities that should/could be dropped:
438 CAP_SETPCAP, CAP_SYS_MODULE, CAP_SYS_RAWIO, CAP_SYS_PTRACE, CAP_SYS_ADMIN */
439 if (cap_set_flag(caps, CAP_PERMITTED, 1, capv, CAP_CLEAR)) {
440 syslog(LOG_ERR, "abort, failed to set PERMITTED capabilities: %s", strerror(errno));
441 exit(102);
442 }
443 if (cap_set_flag(caps, CAP_EFFECTIVE, 1, capv, CAP_CLEAR)) {
444 syslog(LOG_ERR, "abort, failed to set effective capabilities: %s", strerror(errno));
445 exit(103);
446 }
447 if (cap_set_flag(caps, CAP_INHERITABLE, 1, capv, CAP_CLEAR)) {
448 syslog(LOG_ERR, "abort, failed to set INHERITABLE capabilities: %s", strerror(errno));
449 exit(104);
450 }
451 if (cap_set_proc(caps)) {
452 syslog(LOG_ERR, "abort, failed to apply new capabilities: %s", strerror(errno));
453 exit(105);
454 }
455 #else
456 /* we should never get here */
457 exit(333);
458 #endif
459 } else {
460 /* drop all privileges, it seems that we first have to setgid(),
461 then we have to call initgroups(),
462 then we call setuid() */
463 if (setgid(getgid())) {
464 syslog(LOG_ERR, "abort, failed to set effective group ID %u: %s", getgid(), strerror(errno));
465 exit(34);
466 }
467 if (setgroups(ngroups, gids)==-1) {
468 syslog(LOG_ERR, "abort, failed to set additional groups: %s", strerror(errno));
469 exit(35);
470 }
471 free(gids);
472 /* if (initgroups(pw->pw_name, getgid())) {
473 syslog(LOG_ERR, "abort, failed to init groups for user %s (%d), check %s/etc/group", pw->pw_name,getuid(),jaildir);
474 exit(35);
475 }*/
476 if (setuid(getuid())) {
477 syslog(LOG_ERR, "abort, failed to set effective user ID %u: %s", getuid(), strerror(errno));
478 exit(36);
479 }
480 }
481 /* test for user and group info, is it the same? checks username, groupname and home */
482 if (!skip_injail_passwd_check){
483 char *oldpw_name,*oldgr_name;
484 oldpw_name = strdup(pw->pw_name);
485 oldgr_name = strdup(gr->gr_name);
486
487 if (user) {
488 pw = getpwnam(user);
489 } else {
490 pw = getpwuid(getuid());
491 }
492 if (!pw) {
493 syslog(LOG_ERR, "abort, failed to get user information in the jail for user ID %u: %s, check %s/etc/passwd",getuid(),strerror(errno),jaildir);
494 exit(35);
495 }
496 if (pw->pw_uid != getuid()) {
497 syslog(LOG_ERR, "abort, got user information in the jail for user ID %u instead of user ID %u, check %s/etc/passwd",pw->pw_uid,getuid(),jaildir);
498 exit(35);
499 }
500 DEBUG_MSG("got %s as pw_dir\n",pw->pw_dir);
501 gr = getgrgid(getgid());
502 if (!gr) {
503 syslog(LOG_ERR, "abort, failed to get group information in the jail for group ID %u: %s, check %s/etc/group",getgid(),strerror(errno),jaildir);
504 exit(35);
505 }
506 if (strcmp(pw->pw_name, oldpw_name)!=0) {
507 syslog(LOG_ERR, "abort, username %s differs from jail username %s for user ID %u, check /etc/passwd and %s/etc/passwd", oldpw_name, pw->pw_name, getuid(), jaildir);
508 exit(37);
509 }
510 if (strcmp(gr->gr_name, oldgr_name)!=0) {
511 syslog(LOG_ERR, "abort, groupname %s differs from jail groupname %s for group ID %u, check /etc/passwd and %s/etc/passwd", oldgr_name, gr->gr_name, getgid(), jaildir);
512 exit(37);
513 }
514 if (strcmp(pw->pw_dir, newhome)!=0) {
515 DEBUG_MSG("%s!=%s\n",pw->pw_dir, newhome);
516 /* if these are different, it could be that getpwuid() gets the real user
517 info (from for example ldap or nscd), and not the info inside the jail, lets
518 test that, and if true, we should use the shell from the internal function as well*/
519 intpw = internal_getpwuid("/etc/passwd", getuid());
520 if (!intpw) {
521 DEBUG_MSG("%s!=%s\n",intpw->pw_dir, newhome);
522 syslog(LOG_ERR, "abort, failed to find user %u in %s/etc/passwd", getuid(), jaildir);
523 exit(39);
524 }
525 if (!dirs_equal(intpw->pw_dir, newhome)) {
526 DEBUG_MSG("%s!=%s\n",intpw->pw_dir, newhome);
527 syslog(LOG_ERR, "abort, home directory %s differs from jail home directory %s for user %s (%u), check /etc/passwd and %s/etc/passwd", newhome, pw->pw_dir, pw->pw_name, getuid(), jaildir);
528 exit(39);
529 }
530 }
531 free(oldpw_name);
532 free(oldgr_name);
533 }
534 if (injail_shell) {
535 shell = injail_shell;
536 } else if (intpw) {
537 shell = intpw->pw_shell;
538 } else {
539 shell = pw->pw_shell;
540 }
541 /* test the shell in the jail, it is not allowed to be setuid() root */
542 testsafepath(shell,0,0);
543
544 /* prepare the new environment */
545 setenv("HOME",newhome,1);
546 setenv("USER",pw->pw_name,1);
547 setenv("USERNAME",pw->pw_name,1);
548 setenv("SHELL",shell,1);
549 if (chdir(newhome) != 0) {
550 syslog(LOG_ERR, "abort, chdir(%s) failed inside the jail %s: %s, check the permissions for %s/%s",newhome,jaildir,strerror(errno),jaildir,newhome);
551 exit(41);
552 }
553
554 /* cleanup before execution */
555 free(newhome);
556
557 /* now execute the jailed shell */
558 if (injail_login_shell) {
559 argc = 2;
560 newargv = malloc0((argc+1)*sizeof(char *));
561 newargv[0] = shell;
562 newargv[1] = "--login";
563 } else {
564 newargv = malloc0((argc+1)*sizeof(char *));
565 newargv[0] = shell;
566 for (i=1;i<argc;i++) {
567 newargv[i] = argv[i];
568 }
569 }
570 execv(shell, newargv);
571 DEBUG_MSG(strerror(errno));
572 syslog(LOG_ERR, "ERROR: failed to execute shell %s for user %s (%u), check the permissions and libraries of %s/%s",shell,pw->pw_name,getuid(),jaildir,shell);
573
574 free(jaildir);
575 exit(111);
576 }
577