Re: Need advice: Better Jail integration into ps/top, setpwfile gone forever?

From: Alan Somers <asomers_at_freebsd.org>
Date: Thu, 26 Aug 2021 01:43:19 UTC
On Mon, Aug 23, 2021 at 4:03 AM antranigv <antranigv@freebsd.am> wrote:

> Greetings all,
>
> I am trying to have better integration of top(1) and ps(1) with FreeBSD
> Jails.
>
> The main problem that I am trying to solve is displaying the correct UID
> username. Here's an example.
>
> I have a host (srv0), it is running a Jail named "fsoc", The Jail "fsoc"
> has a user named "romero" with the UID 1001.
>
> If I run `ps auxd` in the Jail, I get the following,
>
> romero@fsoc:~ $ ps auxd
> USER    PID %CPU %MEM   VSZ  RSS TT  STAT STARTED    TIME COMMAND
> root   4377  0.0  0.0 11376  956  -  SsJ  14:15   0:00.38
> /usr/sbin/syslogd -ss
> root   5758  0.0  0.1 13128 1352  1  IJ   18:24   0:00.02 /bin/tcsh -i
> root   5763  0.0  0.0 12048  960  1  IJ   18:24   0:00.01 - su - romero
> romero 5764  0.0  0.1 12120 2268  1  SJ   18:24   0:00.02 `-- -su (sh)
> romero 9625  0.0  0.1 11684 2576  1  R+J  09:41   0:00.01   `-- ps auxd
>
> Good!
>
> However, if I try to run it on the host, here's what I get,
>
> root@srv0:~ # ps auxd -J fsoc
> USER  PID %CPU %MEM   VSZ  RSS TT  STAT STARTED    TIME COMMAND
> root 4377  0.0  0.0 11376  956  -  SsJ  14:15   0:00.38 /usr/sbin/syslogd
> -ss
> root 5758  0.0  0.1 13128 1352  1  IJ   18:24   0:00.02 /bin/tcsh -i
> root 5763  0.0  0.0 12048  960  1  IJ   18:24   0:00.01 - su - romero
> 1001 5764  0.0  0.1 12124 2436  1  I+J  18:24   0:00.02 `-- -su (sh)
>
> As you can see, in the User field it says 1001, because the host does not
> have a user with that UID.
>
> This seems fine, but it becomes an issue when you have multiple Jail and a
> large host running.
>
> Here's an example if the host had a user with UID 1001,
>
> root@pingvinashen:~ # ps auxd -J oragir
> USER        PID %CPU %MEM    VSZ   RSS TT  STAT STARTED    TIME COMMAND
> root        949  0.0  0.0  11344  2584  -  IsJ  Mon19   0:01.13
> /usr/sbin/cron -s
> root       1962  0.0  0.0  11428  2796  -  SsJ  Mon19   0:01.83
> /usr/sbin/syslogd -ss
> antranigv 95342  0.0  0.0  11004  2424  -  IsJ  Mon19   0:00.48 daemon:
> /usr/home/oragir/writefreely/writefreely[9992] (daemon)
> antranigv  9992  0.0  0.4 767244 58336  -  IJ   Mon19   2:58.87 -
> /usr/home/oragir/writefreely/writefreely
>
> Now, you would think that this is good, however, if you run this in the
> jail,
>
> root@oragir:~ # ps auxd
> USER      PID %CPU %MEM    VSZ   RSS TT  STAT STARTED    TIME COMMAND
> root      949  0.0  0.0  11344  2584  -  SsJ  Mon15   0:01.13
> /usr/sbin/cron -s
> root     1962  0.0  0.0  11428  2796  -  SsJ  Mon15   0:01.83
> /usr/sbin/syslogd -ss
> oragir  95342  0.0  0.0  11004  2424  -  IsJ  Mon15   0:00.48 daemon:
> /usr/home/oragir/writefreely/writefreely[9992] (daemon)
> oragir   9992  0.0  0.4 767244 58336  -  IJ   Mon15   2:58.88 -
> /usr/home/oragir/writefreely/writefreely
> root    88228  0.0  0.0  13336  4004  8  SJ   09:45   0:00.01 /bin/csh -i
> root    99502  0.0  0.0  11824  3140  8  R+J  09:45   0:00.00 - ps auxd
>
> As you can see, the UID 1001 was not `antranigv`, instead it was `oragir`.
>
> This has been an issue for me, so I tried writing some code to implement
> the following.
>
> If the process is in a Jail, then change the passwd db from /etc to
> /path/of/the/jail/etc.
>
> I thought it would be an easy thing to do, but not so much.
>
> Here's what I've tried.
>
> 1) Call jail_attach and run ps inside the Jail. Oh yeah, it's a jail!
> after attaching to it there is no way to deattach :-) silly me!
>
> 2) Change the passwd file for getpwuid/getpwnam. I wanted to use
> setpwfile(3) but turns out that \
> COMPATIBILITY
>      The historic function setpwfile(3), which allowed the specification of
>      alternate password databases, has been deprecated and is no longer
>      available.
>
> Okay, So I look into how other tools like pwd_mkdb is written and I see
> that everything is defined (pun intended) the following way,
>
> in /usr/include/pwd.h
>
> #define _PATH_PWD               "/etc"
> #define _PATH_PASSWD            "/etc/passwd"
> #define _PASSWD                 "passwd"
> #define _PATH_MASTERPASSWD      "/etc/master.passwd"
> #define _MASTERPASSWD           "master.passwd"
>
> #define _PATH_MP_DB             "/etc/pwd.db"
> #define _MP_DB                  "pwd.db"
> #define _PATH_SMP_DB            "/etc/spwd.db"
> #define _SMP_DB                 "spwd.db"
>
> #define _PATH_PWD_MKDB          "/usr/sbin/pwd_mkdb"
>
> and pwd_mkdb does the following
>
> ...
> strcpy(prefix, _PATH_PWD);
> ...
>                 case 'd':
>                         dflag++;
>                         strlcpy(prefix, optarg, sizeof(prefix));
>                         break;
> ...
>
> Tuns out it parses the DB file, but I don't want to do that in ps/top! :-)
>
> 3) Just for fun, I played with chroot. I tried the following code.
> # cat getpw.c
>
> #define MAXHOSTNAMELEN  255
> #define MAXPATHLEN      255
> #include <pwd.h>
> //#include <jail.h>
> #include <stdio.h>
> #include <unistd.h>
> #include <sys/uio.h>
> #include <sys/jail.h>
> #include <sys/param.h>
> #include <sys/types.h>
>
> int main(){
>         // Just get root!
>         struct passwd *pwd;
>         printf("just root: %s\n", (getpwuid(0))->pw_name);
>
>         // let's try with undef/define
> #undef _PATH_PWD
> #undef _PATH_PASSWD
> #undef _PASSWD
> #undef _PATH_MASTERPASSWD
> #undef _MASTERPASSWD
>
> #undef _PATH_MP_DB
> #undef _MP_DB
> #undef _PATH_SMP_DB
> #undef _SMP_DB
>
> #define _PATH_PWD               "/zdata/jails/fsoc/etc"
> #define _PATH_PASSWD            "/zdata/jails/fsoc/etc/passwd"
> #define _PASSWD                 "passwd"
> #define _PATH_MASTERPASSWD      "/zdata/jails/fsoc/etc/master.passwd"
> #define _MASTERPASSWD           "master.passwd"
>
> #define _PATH_MP_DB             "/zdata/jails/fsoc/etc/pwd.db"
> #define _MP_DB                  "pwd.db"
> #define _PATH_SMP_DB            "/zdata/jails/fsoc/etc/spwd.db"
> #define _SMP_DB                 "spwd.db"
>         pwd = getpwuid(1001);
>         if (pwd == NULL) {
>                 printf("using undef/define: no user found\n");
>         } else {
>                 printf("using undef/define: %s\n", pwd->pw_name);
>         }
>
>         // let's try with chroot!
>         chroot("/zdata/jails/fsoc");
>         pwd = getpwuid(1001);
>         if (pwd == NULL) {
>                 printf("after chroot: no user found\n");
>         } else {
>                 printf("after chroot: %s\n", pwd->pw_name);
>         }
>
>         // escape back the chroot ;-)
>         chroot("../../../../");
>         pwd = getpwuid(1001);
>         if (pwd == NULL) {
>                 printf("after unchroot: no user found\n");
>         } else {
>                 printf("after chroot: %s\n", pwd->pw_name);
>         }
>
>         return 42;
> }
>
> And I get the following:
>
> # ./getpw
> just root: root
> using undef/define: no user found
> after chroot: romero
> after unchroot: no user found
>
> So, any advice? should I do chroot in ps? (no I don't think that's a good
> idea), should I add a new call that implements setpwfile(3)? But I really
> want to know why it was removed, I'm sure there's a story there. Or is
> there a better way?
>
> Kind regards, have a nice day!
>
> --
> antranigv
> https://antranigv.am/
>

Oof, that's a hard problem.  But in fact, it's worse than you think.
/etc/passwd isn't the only place that user information is stored.   It
could be in NIS, or LDAP, or Heimdal, or who knows where.  The only
reliable way to look up a user is to actually do it from within the jail.
You could create a socketpair, then fork a child process and attach that to
the jail, and use it to lookup jailed UIDs.  It's complicated, but I can't
imagine anything simpler that would work.
-Alan