Пример #1
0
int main(int argc, char**argv) {
    Tsocketlink *sl[MAX_SOCKETS];

    /*	struct timeval startround, endround;*/
    unsigned int numsockets = 0;
    int outsocket;
    unsigned int i;
    unsigned short int nodetach = 0;
    char *m_socket = NULL;
    char *pidfile = NULL;
    FILE *pidfilefd = NULL;
    unsigned int m_base=511, m_peak=2048;
    float m_interval=10.0;
    /*	signal(SIGINT, sigterm_handler);
    	signal(SIGTERM, sigterm_handler);*/

    {
        int c;
        while (1) {
            int option_index = 0;
            static struct option long_options[] = {
                {"pidfile", required_argument, NULL, 0},
                {"nodetach", no_argument, NULL, 0},
                {"help", no_argument, NULL, 0},
                {"socket", required_argument, NULL, 0},
                {"base", required_argument, NULL, 0},
                {"interval", required_argument, NULL, 0},
                {"peak", required_argument, NULL, 0},
                {"peek", required_argument, NULL, 0},
                {NULL, 0, NULL, 0}
            };
            c = getopt_long(argc, argv, "p:nh",long_options, &option_index);
            if (c == -1)
                break;
            switch (c) {
            case 0:
                switch (option_index) {
                case 0:
                    pidfile = strdup(optarg);
                    break;
                case 1:
                    nodetach = 1;
                    break;
                case 2:
                    usage();
                    exit(0);
                case 3:
                    m_socket = strdup(optarg);
                    break;
                case 4:
                    m_base = atoi(optarg);
                    break;
                case 5:
                    m_interval = (float)atof(optarg);
                    break;
                case 6:
                    m_peak = atoi(optarg);
                    break;
                case 7: /* for backwards compatibility */
                    m_peak = atoi(optarg);
                    break;
                }
                break;
            case 'p':
                pidfile = strdup(optarg);
                break;
            case 'n':
                nodetach = 1;
                break;
            case 'h':
                usage();
                exit(0);
            }
        }
    }
    openlog(PROGRAMNAME, LOG_PID, LOG_DAEMON);

    outsocket = socket(AF_UNIX, SOCK_DGRAM, 0);
    DEBUG_MSG("outsocket at %d\n", outsocket);
    {
        struct sockaddr client_addr;
        strncpy(client_addr.sa_data, "/dev/log", sizeof(client_addr.sa_data));
        client_addr.sa_family = AF_UNIX;
        if (connect(outsocket, &client_addr, sizeof(client_addr)) != 0) {
            /*DEBUG_MSG("connect returned erno %d: %s\n",errno, strerror(errno));*/
            outsocket = socket(AF_UNIX, SOCK_STREAM, 0);
            if (connect(outsocket, &client_addr, sizeof(client_addr)) != 0) {
                syslog(LOG_CRIT, "version "VERSION", while connecting to /dev/log: %s", strerror(errno));
                if (nodetach) printf("version "VERSION", while connecting to /dev/log: %s\n",strerror(errno) );
                exit(1);
            }
        }
        if (nodetach) printf("opened /dev/log\n");
    }

    if (!m_socket)
    {
        char buf[1024], *tmp;
        Tiniparser *ip = new_iniparser(CONFIGFILE);
        if (!ip) {
            syslog(LOG_CRIT, "version "VERSION", abort, could not parse configfile "CONFIGFILE);
            if (nodetach) printf("version "VERSION", abort, could not parse configfile "CONFIGFILE"\n");
            exit(11);
        }
        while ((tmp = iniparser_next_section(ip, buf, 1024))) {
            if (!have_socket(tmp, sl, numsockets)) {
                unsigned int base=511, peak=2048;
                float interval=5.0;
                long prevpos, secpos;

                if (numsockets == MAX_SOCKETS) {
                    syslog(LOG_NOTICE, "Warning: jk_socketd is compiled to support maximum %d sockets and more sockets are requested, not all sockets are opened!",MAX_SOCKETS);
                    if (nodetach) printf("Warning: jk_socketd is compiled to support maximum %d sockets and more sockets are requested, not all sockets are opened!\n",MAX_SOCKETS);
                    break;
                }

                prevpos = iniparser_get_position(ip);
                secpos = prevpos - strlen(tmp)-4;
                DEBUG_MSG("secpos=%ld, prevpos=%ld\n",secpos,prevpos);
                base = iniparser_get_int_at_position(ip, tmp, "base", secpos);
                peak = iniparser_get_int_at_position(ip, tmp, "peak", secpos);
                interval = iniparser_get_float_at_position(ip, tmp, "interval", secpos);
                iniparser_set_position(ip, secpos);
                if (10 > base || base >  1000000) base = 511;
                if (100 > peak || peak > 10000000 || peak < base) {
                    /* for backwards compatibility we check 'peek' */
                    peak = iniparser_get_int_at_position(ip, tmp, "peek", secpos);
                    if (100 > peak || peak > 10000000 || peak < base) {
                        peak = 2048;
                    }
                }
                if (0.01 > interval || interval > 60.0) interval = 5.0;
                sl[numsockets] = new_socketlink(outsocket, tmp, base, peak, (int)(interval*1000000.0), nodetach);
                if (sl[numsockets]) {
                    syslog(LOG_NOTICE, "version "VERSION", listening on socket %s with rates [%d:%d]/%f",tmp,base,peak,interval);
                    if (nodetach) printf("version "VERSION", listening on socket %s with rates [%d:%d]/%f\n",tmp,base,peak,interval);
                    numsockets++;
                } else {
                    if (nodetach) printf("version "VERSION", failed to create socket %s\n",tmp);
                }
                DEBUG_MSG("setting position to %ld\n",prevpos);
                iniparser_set_position(ip, prevpos);
            } else {
                syslog(LOG_NOTICE, "version "VERSION", socket %s is mentioned multiple times in config file",tmp);
                if (nodetach) printf("version "VERSION", socket %s is mentioned multiple times in config file\n",tmp);
            }

        }
    }
    else
    {
        unsigned int base=m_base, peak=m_peak;
        float interval=m_interval;
        if (10 > base || base >  1000000) base = 511;
        if (100 > peak || peak > 10000000 || peak < base) peak = 2048;
        if (0.01 > interval || m_interval > 60.0) interval = 5.0;
        sl[numsockets] = new_socketlink(outsocket, m_socket, base, peak, (int)(interval*1000000.0), nodetach);
        if (sl[numsockets]) {
            syslog(LOG_NOTICE, "version "VERSION", listening on socket %s with rates [%d:%d]/%f",m_socket,base,peak,interval);
            if (nodetach) printf("version "VERSION", listening on socket %s with rates [%d:%d]/%f\n",m_socket,base,peak,interval);
            numsockets++;
        } else {
            if (nodetach) printf("version "VERSION",failed to create socket %s\n",m_socket);
        }
    }

    if (numsockets == 0) {
        printf("version "VERSION", no sockets specified in configfile "CONFIGFILE" or on commandline, nothing to do, exiting...\n");
        syslog(LOG_ERR,"version "VERSION", no sockets specified in configfile "CONFIGFILE" or on commandline, nothing to do, exiting...");
        exit(1);
    }

    if (pidfile) pidfilefd = fopen(pidfile, "w");

    /* now chroot() to some root:root dir without binaries, and change to nobody:nogroup */

    {
        struct passwd *pw = getpwnam("nobody");
        int ret;
        char *path = INIPREFIX;
        if (!pw) {
            syslog(LOG_ERR, "cannot get UID and GID for user nobody");
            if (nodetach) printf("cannot get UID and GID for user nobody");
        }
        ret = testsafepath(path, 0,0);
        if (ret != 0) {
            syslog(LOG_ERR, "abort, path %s is not owned root:root or does not have 0644 permissions\n",path);
            exit(53);
        }

        if (!(chdir(path)==0 && chroot(path)==0)) {
            syslog(LOG_ERR, "failed to chroot to "INIPREFIX);
            if (nodetach) printf("failed to chroot to "INIPREFIX);
        }
        if (pw) {
            if (setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0) {
                syslog(LOG_ERR, "failed to change to user nobody (uid=%d, gid=%d)", pw->pw_uid, pw->pw_gid);
                if (nodetach) printf("failed to change to user nobody (uid=%d, gid=%d)\n", pw->pw_uid, pw->pw_gid);
            }
        }
    }

    if (!nodetach) {
        /* detach and set the detached process as the new process group leader */
        if (fork() != 0) {
            DEBUG_MSG("exit process %d\n", getpid());
            exit(0);
        }
        DEBUG_MSG("after fork(), process id %d continues\n", getpid());
        setsid();
    }
    if (pidfile) {
        if (pidfilefd) {
            /* we should do this using fscanf(pidfilefd,"%d", getpid()) */
            char buf[32];
            unsigned int size;
            size = snprintf(buf, 32, "%d", getpid());
            fwrite(buf, size, sizeof(char), pidfilefd);
            fclose(pidfilefd);
        } else {
            syslog(LOG_NOTICE, "failed to write pid to %s", pidfile);
        }
    }
    /* use sl[0] for the main process */
    for (i=1; i<numsockets; i++) {
        pthread_create(&sl[i]->thread, NULL,(void*)&socketlink_handle, (void*) sl[i]);
        DEBUG_MSG("created thread %i for %s\n",i,sl[i]->inpath);
    }
    DEBUG_MSG("main thread starting work on socket\n");
    socketlink_handle(sl[0]);
    /*	DEBUG_MSG("pause() for process %d\n",getpid());
    	pause();
    	DEBUG_MSG("before clean_exit\n");
    	clean_exit(numsockets,sl);
    	DEBUG_MSG("after clean_exit\n");
    	if (nodetach) printf("caught signal, exiting\n");*/
    exit(0);
}
Пример #2
0
int main (int argc, char **argv) {
	unsigned int i;
	unsigned int use_capabilities=0;
	int ret;
	char **newargv;
	char *user=NULL, *tmp=NULL;
	
	struct passwd *pw=NULL;
	struct group *gr=NULL;
	unsigned long ngroups_max=NGROUPS_MAX;
	unsigned long ngroups=0;
	gid_t *gids;
	struct passwd *intpw=NULL; /* for internal_getpwuid() */
	char *jaildir=NULL, *newhome=NULL, *shell=NULL;
	Tiniparser *parser=NULL;
	char **envs=NULL;
	unsigned int relax_home_group_permissions=0;
	unsigned int relax_home_other_permissions=0;
	unsigned int relax_home_group=0;
	char *injail_shell=NULL;
	unsigned int skip_injail_passwd_check=0;

	DEBUG_MSG(PROGRAMNAME", started\n");
	/* open the log facility */
	openlog(PROGRAMNAME, LOG_PID, LOG_AUTH);
	
	/* attach signal handler */
	signal(SIGILL,signal_handler);
	signal(SIGSEGV,signal_handler);
	signal(SIGTERM,signal_handler);
	signal(SIGFPE,signal_handler);
	
	/* check if it PRORAMNAME that the user wants */
	tmp = strrchr(argv[0], '/');
	if (!tmp) {
		tmp = argv[0];
	} else {
		tmp++;
	}
	
	if (strcmp(tmp, PROGRAMNAME) != 0 && strcmp(tmp, "su")!= 0 && (tmp[0] != '-' || strcmp(&tmp[1], PROGRAMNAME))) {
		DEBUG_MSG("wrong name, tmp=%s, &tmp[1]=%s\n", tmp, &tmp[1]);
		syslog(LOG_ERR, "abort, "PROGRAMNAME" is called as %s", argv[0]);
		exit(1);
	}

	/* now test if we are setuid root (the effective user id must be 0, and the real user id > 0 */
	if (geteuid() != 0) {
		if (have_capabilities()) {
			use_capabilities=1;
		} else {
			syslog(LOG_ERR, "abort, effective user ID is not 0, possibly "PROGRAMNAME" is not setuid root");
			exit(11);
		}
	}
	if (getuid() == 0) {
		syslog(LOG_ERR, "abort, "PROGRAMNAME" is run by root, which does not make sense because user root can break out of a jail anyway");
		exit(12);
	}

	DEBUG_MSG("get user info\n");
	/* get user info based on the users name and not on the uid. this enables support
	for systems with multiple users with the same user id */ 
	tmp = getenv("USER");
	if (tmp && strlen(tmp)) {
		user = strdup(tmp);
	}
	if (user) {
		pw = getpwnam(user);
	} else {
		pw = getpwuid(getuid());
	}
	if (!pw) {
		syslog(LOG_ERR, "abort, failed to get user information for user ID %d: %s, check /etc/passwd", getuid(), strerror(errno));
		exit(13);
	}
	if (!pw->pw_name || strlen(pw->pw_name)==0) {
		syslog(LOG_ERR, "abort, got an empty username for user ID %d: %s, check /etc/passwd", getuid(), strerror(errno));
		exit(13);
	}
	if (user && strcmp(user,pw->pw_name)!=0) {
		syslog(LOG_ERR, "abort, asked for user %s, got user info for %s", user, pw->pw_name);
		exit(13);
	}
	if (pw->pw_uid != getuid()) {
		syslog(LOG_ERR, "abort, started by user ID %d, got user info %s with user ID %d,", getuid(), pw->pw_name, pw->pw_uid);
		exit(13);
	}
	DEBUG_MSG("got user %s\nget group info\n",pw->pw_name);
	gr = getgrgid(getgid());
	if (!gr) {
		syslog(LOG_ERR, "abort, failed to get group information for group ID %d: %s, check /etc/group", getgid(), strerror(errno));
		exit(13);
	}
	DEBUG_MSG("get additional groups\n");
	/* ngroups_max = sysconf(_SC_NGROUPS_MAX);*/
	gids = malloc(ngroups_max * sizeof(gid_t));
	ngroups = getgroups(ngroups_max,gids);
	if (ngroups == -1) {
		syslog(LOG_ERR, "abort, failed to get additional group information: %s, check /etc/group", strerror(errno));
		exit(13);
	}
#ifdef DEBUG
	printf("got additional groups ");
	for (i=0;i<ngroups;i++) {
		printf("%d, ",gids[i]);
	}
	printf("\n");
#endif

	/* make sure the jailkit config directory is owned root:root and not writable for others */
	if ( (testsafepath(INIPREFIX, 0, 0) &~TESTPATH_GROUPW) != 0 ) {
		syslog(LOG_ERR, "abort, jailkit configuration directory "INIPREFIX" is not safe; it should be owned 0:0 and not writable for others");
		exit(14);
	}
	parser = new_iniparser(CONFIGFILE);
	if (parser) {
		char *groupsec, *section=NULL, buffer[1024]; /* openbsd complains if this is <1024 */
		groupsec = strcat(strcpy(malloc0(strlen(gr->gr_name)+7), "group "), gr->gr_name);
		if (iniparser_has_section(parser, pw->pw_name)) {
			section = strdup(pw->pw_name);
		} else if (iniparser_has_section(parser, groupsec)) {
			section = groupsec;
		} else if (iniparser_has_section(parser, "DEFAULT")) {
			section = strdup("DEFAULT");
		}
		if (section != groupsec) free(groupsec);
		if (section) {
			unsigned int pos = iniparser_get_position(parser) - strlen(section) - 2;
			
			if (iniparser_get_string_at_position(parser, section, "env", pos, buffer, 1024) > 0) {
				envs = explode_string(buffer, ',');
			}
			relax_home_group_permissions = iniparser_get_int_at_position(parser, section, "relax_home_group_permissions", pos);
			relax_home_other_permissions = iniparser_get_int_at_position(parser, section, "relax_home_other_permissions", pos);
			relax_home_group = iniparser_get_int_at_position(parser, section, "relax_home_group", pos);
			if (iniparser_get_string_at_position(parser, section, "injail_shell", pos, buffer, 1024) > 0) {
				injail_shell = strdup(buffer);
			}
			if (injail_shell) {
				skip_injail_passwd_check = iniparser_get_int_at_position(parser, section, "skip_injail_passwd_check", pos);
			}
			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",
					section, relax_home_group_permissions, relax_home_other_permissions, 
					relax_home_group, injail_shell, skip_injail_passwd_check);
			free(section);
		} else {
			DEBUG_MSG("no relevant section found in configfile\n");
		}
		iniparser_close(parser);
	} else {
		DEBUG_MSG("no configfile "CONFIGFILE" ??\n");
	}

	DEBUG_MSG("close filedescriptors\n");
	/* open file descriptors can be used to break out of a chroot, so we close all of them, except for stdin,stdout and stderr */
#ifdef OPEN_MAX
    i = OPEN_MAX;
#elif defined(NOFILE)
    i = NOFILE;
#else
    i = getdtablesize();
#endif
	while (--i > 2) {
		/*printf("closing file descriptor %d\n",i);*/
		while (close(i) != 0 && errno == EINTR);
	}
	/* now make sure file descriptors 0 1 and 2 are valid before we (or a child) starts writing to it */
	while (1) {
		int fd;
		fd = open("/dev/null", O_RDWR);
		if (fd < 0)
			exit(10);
		if (fd > 2) {
			close(fd);
			break;
		} else {
			DEBUG_MSG("re-opening file descriptor %d\n",fd);
		}
	}

	/* now we clear the environment, except for values allowed in /etc/jailkit/jk_chrootsh.ini */	
	unset_environ_except(envs);
	if (envs) {
		free_array(envs);
	}
	
	if (pw->pw_gid != getgid()) {
		syslog(LOG_ERR, "abort, the group ID from /etc/passwd (%d) does not match the group ID we run with (%d)", pw->pw_gid, getgid());
		exit(15);
	}
	if (!pw->pw_dir || strlen(pw->pw_dir) ==0) {
		syslog(LOG_ERR, "abort, got an empty home directory for user %s (%d)", pw->pw_name, getuid());
		exit(16);
	}
	if (strstr(pw->pw_dir, "/./") == NULL) {
		syslog(LOG_ERR, "abort, homedir '%s' for user %s (%d) does not contain the jail separator <jail>/./<home>", pw->pw_dir, pw->pw_name, getuid());
		exit(17);
	}
	DEBUG_MSG("get jaildir\n");
	if (!getjaildir(pw->pw_dir, &jaildir, &newhome)) {
		syslog(LOG_ERR, "abort, failed to read the jail and the home from %s for user %s (%d)",pw->pw_dir, pw->pw_name, getuid());
		exit(17);
	}
	DEBUG_MSG("dir=%s,jaildir=%s,newhome=%s\n",pw->pw_dir, jaildir, newhome);
	DEBUG_MSG("get chdir()\n");
	if (chdir(jaildir) != 0) {
		syslog(LOG_ERR, "abort, chdir(%s) failed: %s, check the permissions for %s",jaildir,strerror(errno),jaildir);
		exit(19);
	} else {
		char test[1024];
		/* test if it really succeeded */
		if (getcwd(test, 1024)==NULL || !dirs_equal(jaildir, test)) {
			syslog(LOG_ERR, "abort, the current dir is %s after chdir(%s), but it should be %s",test,jaildir,jaildir);
			exit(21);
		}		
	}		
	
	/* here do test the ownership of the jail and the homedir and such
	the function testsafepath doe exit itself on any failure */
	if (!basicjailissafe(jaildir)) {
		syslog(LOG_ERR, "abort, %s is not a safe chroot jail.", jaildir);
		exit(53);
	} 
	ret = testsafepath(pw->pw_dir, getuid(), getgid());
	if ((ret & TESTPATH_NOREGPATH) ) {
		syslog(LOG_ERR, "abort, path %s is not a directory", pw->pw_dir);
		exit(53);	
	}
	if ((ret & TESTPATH_OWNER) ) {
		syslog(LOG_ERR, "abort, path %s is not owned by %d", pw->pw_dir,getuid());
		exit(53);
	}
	if (!relax_home_group && (ret & TESTPATH_GROUP)) {
		syslog(LOG_ERR, "abort, path %s does not have group owner %d, set option 'relax_home_group' to relax this check", pw->pw_dir,getgid());
		exit(53);
	}
	if (!relax_home_group_permissions && (ret & TESTPATH_GROUPW)) {
		syslog(LOG_ERR, "abort, path %s is group writable, set option 'relax_home_group_permissions' to relax this check", pw->pw_dir);
		exit(53);
	}
	if (!relax_home_other_permissions && (ret & TESTPATH_OTHERW)) {
		syslog(LOG_ERR, "abort, path %s is writable for other, set option 'relax_home_other_permissions' to relax this check", pw->pw_dir);
		exit(53);
	}
	/* do a final log message */
	tmp = implode_array(&argv[1], argc-1, " ");
	syslog(LOG_INFO, "now entering jail %s for user %s (%d) with arguments %s", jaildir, pw->pw_name, getuid(), tmp);
	free(tmp);
	
	DEBUG_MSG("chroot()\n");
	/* do the chroot() call */
	if (chroot(jaildir)) {
		syslog(LOG_ERR, "abort, chroot(%s) failed: %s, check the permissions for %s", jaildir, strerror(errno), jaildir);
		exit(33);
	}
	
	if (use_capabilities) {
#ifdef HAVE_CAP_GET_PROC
		cap_t caps;
		cap_value_t capv[1];
		/* drop chroot capability, should we drop all other capabilities that may be used to escape from the jail too ?  */
		if ((caps = cap_get_proc()) == NULL) {
			syslog(LOG_ERR, "abort, failed to retrieve current capabilities: %s", strerror(errno));
			exit(101);
		}
		capv[0] = CAP_SYS_CHROOT;
		/* other capabilities that should/could be dropped:
		CAP_SETPCAP, CAP_SYS_MODULE, CAP_SYS_RAWIO, CAP_SYS_PTRACE, CAP_SYS_ADMIN */
		if (cap_set_flag(caps, CAP_PERMITTED, 1, capv, CAP_CLEAR)) {
			syslog(LOG_ERR, "abort, failed to set PERMITTED capabilities: %s", strerror(errno));
			exit(102);
		}
		if (cap_set_flag(caps, CAP_EFFECTIVE, 1, capv, CAP_CLEAR)) {
			syslog(LOG_ERR, "abort, failed to set effective capabilities: %s", strerror(errno));
			exit(103);
		}
		if (cap_set_flag(caps, CAP_INHERITABLE, 1, capv, CAP_CLEAR)) {
			syslog(LOG_ERR, "abort, failed to set INHERITABLE capabilities: %s", strerror(errno));
			exit(104);
		}
		if (cap_set_proc(caps)) {
			syslog(LOG_ERR, "abort, failed to apply new capabilities: %s", strerror(errno));
			exit(105);
		}
#else
		/* we should never get here */
		exit(333);
#endif
	} else {
		/* drop all privileges, it seems that we first have to setgid(), 
			then we have to call initgroups(), 
			then we call setuid() */
		if (setgid(getgid())) {
			syslog(LOG_ERR, "abort, failed to set effective group ID %d: %s", getgid(), strerror(errno));
			exit(34);
		}
		if (setgroups(ngroups, gids)==-1) {
			syslog(LOG_ERR, "abort, failed to set additional groups: %s", strerror(errno));
			exit(35);
		}
		free(gids);
	/*	if (initgroups(pw->pw_name, getgid())) {
			syslog(LOG_ERR, "abort, failed to init groups for user %s (%d), check %s/etc/group", pw->pw_name,getuid(),jaildir);
			exit(35);
		}*/
		if (setuid(getuid())) {
			syslog(LOG_ERR, "abort, failed to set effective user ID %d: %s", getuid(), strerror(errno));
			exit(36);
		}
	}
	/* test for user and group info, is it the same? checks username, groupname and home */
	if (!skip_injail_passwd_check){
		char *oldpw_name,*oldgr_name;
		oldpw_name = strdup(pw->pw_name);
		oldgr_name = strdup(gr->gr_name);
		
		if (user) {
			pw = getpwnam(user);
		} else {
			pw = getpwuid(getuid());
		}
		if (!pw) {
			syslog(LOG_ERR, "abort, failed to get user information in the jail for user ID %d: %s, check %s/etc/passwd",getuid(),strerror(errno),jaildir);
			exit(35);
		}
		if (pw->pw_uid != getuid()) {
			syslog(LOG_ERR, "abort, got user information in the jail for user ID %d instead of user ID %d, check %s/etc/passwd",pw->pw_uid,getuid(),jaildir);
			exit(35);
		}
		DEBUG_MSG("got %s as pw_dir\n",pw->pw_dir);
		gr = getgrgid(getgid());
		if (!gr) {
			syslog(LOG_ERR, "abort, failed to get group information in the jail for group ID %d: %s, check %s/etc/group",getgid(),strerror(errno),jaildir);
			exit(35);
		}
		if (strcmp(pw->pw_name, oldpw_name)!=0) {
			syslog(LOG_ERR, "abort, username %s differs from jail username %s for user ID %d, check /etc/passwd and %s/etc/passwd", oldpw_name, pw->pw_name, getuid(), jaildir);
			exit(37);
		}
		if (strcmp(gr->gr_name, oldgr_name)!=0) {
			syslog(LOG_ERR, "abort, groupname %s differs from jail groupname %s for group ID %d, check /etc/passwd and %s/etc/passwd", oldgr_name, gr->gr_name, getgid(), jaildir);
			exit(37);
		}
		if (strcmp(pw->pw_dir, newhome)!=0) {
			DEBUG_MSG("%s!=%s\n",pw->pw_dir, newhome);
			/* if these are different, it could be that getpwuid() gets the real user 
			info (from for example ldap or nscd), and not the info inside the jail, lets 
			test that, and if true, we should use the	shell from the internal function as well*/
			intpw = internal_getpwuid("/etc/passwd", getuid());
			if (!intpw) {
				DEBUG_MSG("%s!=%s\n",intpw->pw_dir, newhome);
				syslog(LOG_ERR, "abort, failed to find user %d in %s/etc/passwd", getuid(), jaildir);
				exit(39);
			}
			if (!dirs_equal(intpw->pw_dir, newhome)) {
				DEBUG_MSG("%s!=%s\n",intpw->pw_dir, newhome);
				syslog(LOG_ERR, "abort, home directory %s differs from jail home directory %s for user %s (%d), check /etc/passwd and %s/etc/passwd", newhome, pw->pw_dir, pw->pw_name, getuid(), jaildir);
				exit(39);
			}
		}
		free(oldpw_name);
		free(oldgr_name);
	}
	if (injail_shell) {
		shell = injail_shell;
	} else if (intpw) {
		shell = intpw->pw_shell;
	} else {
		shell = pw->pw_shell;
	}
	/* test the shell in the jail, it is not allowed to be setuid() root */
	testsafepath(shell,0,0);

	/* prepare the new environment */
	setenv("HOME",newhome,1);
	setenv("USER",pw->pw_name,1);
	setenv("USERNAME",pw->pw_name,1);
	setenv("SHELL",shell,1);
	if (chdir(newhome) != 0) {
		syslog(LOG_ERR, "abort, chdir(%s) failed inside the jail %s: %s, check the permissions for %s/%s",newhome,jaildir,strerror(errno),jaildir,newhome);
		exit(41);
	}

	/* cleanup before execution */
	free(newhome);
	
	/* now execute the jailed shell */
	/*execl(pw->pw_shell, pw->pw_shell, NULL);*/
	newargv = malloc0((argc+1)*sizeof(char *));
	newargv[0] = shell;
	for (i=1;i<argc;i++) {
		newargv[i] = argv[i];
	}
	execv(shell, newargv);
	DEBUG_MSG(strerror(errno));
	syslog(LOG_ERR, "ERROR: failed to execute shell %s for user %s (%d), check the permissions and libraries of %s/%s",shell,pw->pw_name,getuid(),jaildir,shell);

	free(jaildir);
	exit(111);
}