Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 11 Aug 2011 00:14:52 +0200
From:      joris dedieu <joris.dedieu@gmail.com>
To:        freebsd-jail@freebsd.org
Subject:   Re: Jexec and access to tty
Message-ID:  <CAPd55qDCpF6%2BNJTKCNUZKEVp3JRnrF8VLKUgFVtLc7fxQODA=w@mail.gmail.com>
In-Reply-To: <CAPd55qBGAkQDxQ6cy7=M%2BxxX%2BHnAoR%2B8_iCS6QMxxSi-iNnniA@mail.gmail.com>
References:  <20110809124543.GA22077@psconsult.nl> <CAPd55qBGAkQDxQ6cy7=M%2BxxX%2BHnAoR%2B8_iCS6QMxxSi-iNnniA@mail.gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help
2011/8/10 joris dedieu <joris.dedieu@gmail.com>:
> 2011/8/9 Paul Schenkeveld <freebsd@psconsult.nl>:
>> Hi,
>>
>> There have been several threads about this issue, some people have come
>> up with work arounds but I think that the issue is more fundamental,
>> that's why I wanted to start this new thread.
>>
>> When using jexec to do interactive work inside an existing jail, people
>> find out that they no longer have access to their tty device. =A0As a
>> result, programs requiring input of passwords or passphrases behave
>> unexpectedly in one of several ways.
>>
>> Ssh says "Host key verification failed." and refuses to log in to
>> another system (unless pubkey authentication is user in combination with
>> an agent of course). =A0Some programs fall back to using stdin/stdout
>> and echo the password as it is typed (the mysql clients are popular
>> examples).
>>
>> Work-arounds that have been suggested are
>> =A01. Run a sshd inside the jail and log in using ssh
>> =A02. Start tmux inside the jail so you get a new pseudo tty slave insid=
e
>> =A0 =A0the jail. =A0People trying screen find that it won't work unlike =
tmux.
>> =A03. I tried using 'script -q /dev/null' inside the jail because it is
>> =A0 =A0part op the base system and it doesn't change your terminal type
>> =A0 =A0and interpret keyboard input and screen output. =A0I found out th=
at I
>> =A0 =A0failed when I resized my window :-(
>
> An other way is to use chroot(5) to enter the jail.
> Maybe chroot /jail/root login -f $USER should be acceptable in some situa=
tions.
>
>>
>> I don't like 1 on a machine with many jails, especially if some of them
>> share the same IP address (e.g. sometimes I have to run a mail server on
>> the same IP adress as a webserver but in a distinct jail).
>>
>> 2 is not ideal either because tmux emulates a different terminal on
>> the inside than the terminal on the outside that it runs on.
>>
>> 3 is really a kludge and causes problems when you resize your window.
>>
>> I thought that I found a solution by rewriting jexec such that it will
>> open a pseudo tty and does the passing of data between the jailed pts
>> and the tty from where jexec was started but that's not going to work as
>> the pseudo tty most be opened by the child process inside the jail but
>> the parent outside the jail must have access to the master side of the
>> pseudo tty.
>>
>> So far we are still talking about work-arounds. =A0Why not look at the
>> root cause. =A0Unfortunately I'm not familiar with kernel sources so if
>> I'm wrong, please forgive me, I write this with the best intentions.
>>
>> The root cause of th problem appears to be that pseudo ttys opened
>> outside a jail are not visible nor accessible inside a jail, pseudo ttys
>> created inside a jail are visible and accessible though.
>
> As far as I understand, sys/fs/devfs/devfs_vnops.c uses
> prison_check(9) too see if an item as been build in the same jail or
> in a child.
> The tty exists inside the jail but you can't use it (and so you can't
> escape the jail).
>
>>
>> Would it be conceivable that by using jexec the controlling tty of jexec
>> magically becomes visible and accessible inside the jail? =A0Preferrable
>> only until jexec dies.
>
> I'm not sure. The only way should be =A0to temporary disable the check
> with a =A0variable. But it will also brake jail security during jexec
> excution.
> Using chroot(2) instead of jail(2) should be an option (but it's =A0non
> trivial to affect jail context for all other subsystems).
>
> I think the only right way is to open a new tty while entering the
> jail (this is what tmux or a jailed ssh does).
> But it should be difficult to make things like echo ps | jexec 1 sh works=
.

I gave this way a try and quickly produced a dirty patch. I did not
yet investigate on the right way, but I already know that I'm wrong on
several items so sorry for your eyes :)

Basically this patch  introduced  -t option that forks jexec inside
the jail, open a new pts and tries to establish a communication with
the old one.
It mostly works has expected but breaks some console inputs like
control commands, completion and so on.
Before spending more time  trying to produce something acceptable,
please let  me know if it should be the expected feature or sounds
like a nasty workaround.

--- usr.sbin/jexec/jexec.c.orig 2009-08-03 10:13:06.000000000 +0200
+++ usr.sbin/jexec/jexec.c      2011-08-10 23:33:44.000000000 +0200
@@ -37,16 +37,19 @@

 #include <err.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <jail.h>
 #include <limits.h>
 #include <login_cap.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <termios.h>
 #include <pwd.h>
 #include <unistd.h>

 static void    usage(void);
+static int     ntty_exec(const char *file, char *const argv[]);

 #define GET_USER_INFO do {                                             \
        pwd =3D getpwnam(username);                                       \
@@ -64,6 +67,8 @@
                err(1, "getgrouplist: %s", username);                   \
 } while (0)

+#define MAXREAD 256
+
 int
 main(int argc, char *argv[])
 {
@@ -71,17 +76,17 @@
        login_cap_t *lcap =3D NULL;
        struct passwd *pwd =3D NULL;
        gid_t *groups =3D NULL;
-       int ch, ngroups, uflag, Uflag;
+       int ch, ngroups, uflag, Uflag, tflag;
        long ngroups_max;
        char *username;

-       ch =3D uflag =3D Uflag =3D 0;
+       ch =3D uflag =3D Uflag =3D tflag =3D 0;
        username =3D NULL;
        ngroups_max =3D sysconf(_SC_NGROUPS_MAX) + 1;
        if ((groups =3D malloc(sizeof(gid_t) * ngroups_max)) =3D=3D NULL)
                err(1, "malloc");

-       while ((ch =3D getopt(argc, argv, "nu:U:")) !=3D -1) {
+       while ((ch =3D getopt(argc, argv, "nu:U:t")) !=3D -1) {
                switch (ch) {
                case 'n':
                        /* Specified name, now unused */
@@ -94,6 +99,9 @@
                        username =3D optarg;
                        Uflag =3D 1;
                        break;
+               case 't':
+                       tflag =3D 1;
+                       break;
                default:
                        usage();
                }
@@ -125,8 +133,14 @@
                        err(1, "setusercontext");
                login_close(lcap);
        }
-       if (execvp(argv[1], argv + 1) =3D=3D -1)
-               err(1, "execvp(): %s", argv[1]);
+       if (tflag) {
+               if(ntty_exec(argv[1], argv + 1) =3D=3D -1)
+                       err(1, "ntty_exec(): %s", argv[1]);
+       }
+       else {
+               if (execvp(argv[1], argv + 1) =3D=3D -1)
+                       err(1, "execvp(): %s", argv[1]);
+       }
        exit(0);
 }

@@ -138,3 +152,77 @@
                "usage: jexec [-u username | -U username] jail command ..."=
);
        exit(1);
 }
+
+static int
+ntty_exec(const char *file, char *const argv[])
+{
+       int pseudoterm, pseudoterm_fd, res;
+       struct fd_set in_fd;
+       struct termios termset;
+       char input[MAXREAD];
+
+       if ((pseudoterm =3D posix_openpt(O_RDWR)) =3D=3D -1)
+               err(1, "posix_openpt");
+       if (grantpt(pseudoterm) !=3D 0)
+               err(1, "grantpt");
+       if (unlockpt(pseudoterm) !=3D 0)
+               err(1, "grantpt");
+       if ((pseudoterm_fd =3D open(ptsname(pseudoterm), O_RDWR)) =3D=3D -1=
)
+               err(1, "open");
+
+       switch (fork()) {
+       case -1 :
+               err(1, "fork");
+       case 0  :
+               close(pseudoterm);
+               if (tcgetattr(pseudoterm_fd, &termset) =3D=3D -1)
+                       err(1, "tcgetattr");
+               cfmakeraw(&termset);
+               tcsetattr(pseudoterm_fd, TCSANOW, &termset);
+               fclose(stdin);
+               fclose(stdout);
+               fclose(stderr);
+               dup(pseudoterm_fd);
+               dup(pseudoterm_fd);
+               dup(pseudoterm_fd);
+               close(pseudoterm_fd);
+               setsid();
+               ioctl(0, TIOCSCTTY, 1);
+               return execvp(file, argv);
+       default :
+               close(pseudoterm_fd);
+               while(1) {
+                       FD_ZERO(&in_fd);
+                       FD_SET(0, &in_fd);
+                       FD_SET(pseudoterm, &in_fd);
+                       if (select(pseudoterm + 1, &in_fd, NULL, NULL,
NULL) =3D=3D -1)
+                               err(1, "select");
+                       if (FD_ISSET(0, &in_fd)) {
+                               /* stdin */
+                               if ((res =3D read(0, input, sizeof(input)))=
 > 0 )
+                                       write(pseudoterm, input, res);
+                               else {
+                                       if (errno =3D=3D ENOENT)
+                                               return 0;
+                                       else
+                                               err(1, "read");
+                               }
+                       }
+                       if (FD_ISSET(pseudoterm, &in_fd)) {
+                               if ((res =3D read(pseudoterm, input,
+                                   sizeof(input))) > 0 )
+                                       /* stdout */
+                                       write(1, input, res);
+                               else {
+                                       if (errno =3D=3D ENOENT)
+                                               return 0;
+                                       else
+                                               err(1, "write");
+                                       }
+                       }
+               }
+       }
+
+       return 0;
+}
+

regards
Joris

>
> regards
> Joris
>
>>
>> I understand that this is not trivial but given the number of threads
>> about this problem, it's a real issue to many people. =A0To me it's wort=
h
>> some $ or EUR to solve this in a clean way.
>>
>> Kind regards,
>>
>> Paul Schenkeveld
>> _______________________________________________
>> freebsd-jail@freebsd.org mailing list
>> http://lists.freebsd.org/mailman/listinfo/freebsd-jail
>> To unsubscribe, send any mail to "freebsd-jail-unsubscribe@freebsd.org"
>>
>



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAPd55qDCpF6%2BNJTKCNUZKEVp3JRnrF8VLKUgFVtLc7fxQODA=w>