From nobody Fri Aug 27 09:34:48 2021 X-Original-To: freebsd-hackers@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4F3DF178EE9F for ; Fri, 27 Aug 2021 09:35:02 +0000 (UTC) (envelope-from antranigv@freebsd.am) Received: from evncert.am (evncert.am [212.42.214.164]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 4Gwvh434jBz3MY7; Fri, 27 Aug 2021 09:34:59 +0000 (UTC) (envelope-from antranigv@freebsd.am) Received: from evncert.am (localhost [127.0.0.1]) by evncert.am (OpenSMTPD) with ESMTP id 39982816; Fri, 27 Aug 2021 13:45:55 +0400 (+04) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=freebsd.am; h=content-type :mime-version:subject:from:in-reply-to:date:cc :content-transfer-encoding:message-id:references:to; s= selector0; bh=IhMibG94qpeUkGFlLqQDjji6uSM=; b=bbJ2b4cpy1oqo1C7jG XpBRrvgkqqVw330pYktdS2V5DQ0YMFHfqpz86g6aNz6A9eEzMI6Ufc7VaxeaMNwl XSa0hwkVYRjGV82e65xh3V+CB4p3LQTb2+MkTkZ9RufzCk+KJ4vjL0d0bJyQUJTs BqdNLefa19v2JY7YW2T0J+v/w= DomainKey-Signature: a=rsa-sha1; c=nofws; d=freebsd.am; h=content-type :mime-version:subject:from:in-reply-to:date:cc :content-transfer-encoding:message-id:references:to; q=dns; s= selector0; b=Exp24oGmc5ZY3Pkfl8jRTglfDFZbMW/j3PT/AzDtB6/FDNE8HdJ bY2gWNksfgNST/l2XNTIAsfali0A/1OgjjUDRYvxH7jLgVp3NBwrxoeyzthS0n2r 5cL0eTMKxYHmC7/ukcInh9Suke8qgSkrTLbIa1HkMV2vrUmBSoZPMZR4= Received: by post.evncert.am (OpenSMTPD) with ESMTPSA id e55cd752 (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256:NO); Fri, 27 Aug 2021 13:45:55 +0400 (+04) Content-Type: text/plain; charset=us-ascii List-Id: Technical discussions relating to FreeBSD List-Archive: https://lists.freebsd.org/archives/freebsd-hackers List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-freebsd-hackers@freebsd.org Mime-Version: 1.0 (Mac OS X Mail 14.0 \(3654.120.0.1.13\)) Subject: Re: Need advice: Better Jail integration into ps/top, setpwfile gone forever? From: antranigv In-Reply-To: Date: Fri, 27 Aug 2021 13:34:48 +0400 Cc: "freebsd-hackers@FreeBSD.org" Content-Transfer-Encoding: quoted-printable Message-Id: <5DB070E0-2602-4C15-B950-920B56506E15@freebsd.am> References: <1B45F065-DC9D-40C9-958F-7D4D64DE8993@freebsd.am> To: Alan Somers , Jamie Landeg-Jones X-Mailer: Apple Mail (2.3654.120.0.1.13) X-Rspamd-Queue-Id: 4Gwvh434jBz3MY7 X-Spamd-Bar: -- Authentication-Results: mx1.freebsd.org; dkim=none ("invalid DKIM record") header.d=freebsd.am header.s=selector0 header.b=bbJ2b4cp; dmarc=pass (policy=none) header.from=freebsd.am; spf=pass (mx1.freebsd.org: domain of antranigv@freebsd.am designates 212.42.214.164 as permitted sender) smtp.mailfrom=antranigv@freebsd.am X-Spamd-Result: default: False [-2.30 / 15.00]; TO_DN_EQ_ADDR_SOME(0.00)[]; ARC_NA(0.00)[]; NEURAL_HAM_MEDIUM(-1.00)[-1.000]; RCPT_COUNT_THREE(0.00)[3]; TO_DN_SOME(0.00)[]; MV_CASE(0.50)[]; FROM_HAS_DN(0.00)[]; MIME_GOOD(-0.10)[text/plain]; RCVD_TLS_LAST(0.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_SPF_ALLOW(-0.20)[+mx]; TO_MATCH_ENVRCPT_SOME(0.00)[]; DKIM_TRACE(0.00)[freebsd.am:~]; DMARC_POLICY_ALLOW(-0.50)[freebsd.am,none]; NEURAL_HAM_SHORT(-1.00)[-1.000]; R_DKIM_PERMFAIL(0.00)[freebsd.am:s=selector0]; FROM_EQ_ENVFROM(0.00)[]; MIME_TRACE(0.00)[0:+]; SUBJECT_ENDS_QUESTION(1.00)[]; ASN(0.00)[asn:49800, ipnet:212.42.192.0/19, country:AM]; RCVD_COUNT_TWO(0.00)[2]; MID_RHS_MATCH_FROM(0.00)[] X-Spam: Yes X-ThisMailContainsUnwantedMimeParts: N Dear Jamie and Alan, Thank you both for your inputs. Jamie, I totally understand your point, for an issue like this to be = fixed, we=20 need to patch some subsystem. I'm not sure I have enough knowledge to do = that,=20 but what you point is called UID Virtualization. If I am not mistaken = illumos=20 systems do that for their Zones. I have to get into it, hopefully = something=20 similar can be done with FreeBSD. So, 1) Yes, patching only ps/top will not solve the issue, what about htop?=20= sockstat? procstat? etc. 2) The ideal way is to make a major change. Look into what other systems = are=20 doing and try to implement it here. Alan, I got a PoC working! #include #include #include #include #include #include #include #include #include int main(int argc, char *argv[]){ struct passwd *pwd; int sp[2], uid; pid_t pid; if (argc < 2 ) { printf("usage: psjail UID\n"); exit(1); } uid =3D atoi(argv[1]); printf("Got UID: %d\n", uid); socketpair(PF_LOCAL, SOCK_STREAM, 0, sp); pid =3D fork(); if (pid =3D=3D 0){ printf("In the child process\n"); printf("Going into Jail\n"); jail_attach(35); pwd =3D getpwuid(uid); if (pwd =3D=3D NULL) { printf("In the Jail: no user found\n"); write(sp[1], "", sizeof(pwd)); } else { printf("In the Jail: %s\n", pwd->pw_name); write(sp[1], pwd->pw_name, = sizeof(pwd->pw_name)); printf("Message sent\n"); } _Exit(0); } else { char buf[sizeof(pwd->pw_name)]; printf("I'm the parent\n"); int n =3D read(sp[0], buf, sizeof(pwd->pw_name)); printf("got %d bytes\n", n); printf("parent got : '%s'\n", buf); pwd =3D getpwuid(uid); if (pwd =3D=3D NULL) { printf("In the parent: no user found\n"); } else { printf("In the parent: %s\n", pwd->pw_name); } } printf("Done executing\n"); return 42; } Here's a sample output, root@srv0:~/src # ./spjail 1001 Got UID: 1001 I'm the parent In the child process Going into Jail In the Jail: romero Message sent got 8 bytes parent got : 'romero' In the parent: no user found Done executing root@srv0:~/src # ./spjail 1000 Got UID: 1000 I'm the parent In the child process Going into Jail In the Jail: no user found got 8 bytes parent got : '' In the parent: no user found Done executing Now, as Jamie said, this is not a good idea, it's basically a "Hack." I = will=20 try to find a proper way to virtualize the UID table, write a PoC for = that and=20 see what the community thinks about that. I like Jamie's approach, but I would not want it to be manual. We need = some=20 automated way. Sure, I can integrate that into my Jail Orchestrator, but = some=20 people want to use jail(8) with jail.conf, and it should be simple for = them as=20 well. Thank you all. Any more input will be appreciated. Kind regards, -- antranigv https://antranigv.am/ > On 26 Aug 2021, at 5:43 AM, Alan Somers wrote: >=20 > On Mon, Aug 23, 2021 at 4:03 AM antranigv = wrote: >=20 >> Greetings all, >>=20 >> I am trying to have better integration of top(1) and ps(1) with = FreeBSD >> Jails. >>=20 >> The main problem that I am trying to solve is displaying the correct = UID >> username. Here's an example. >>=20 >> I have a host (srv0), it is running a Jail named "fsoc", The Jail = "fsoc" >> has a user named "romero" with the UID 1001. >>=20 >> If I run `ps auxd` in the Jail, I get the following, >>=20 >> 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 >>=20 >> Good! >>=20 >> However, if I try to run it on the host, here's what I get, >>=20 >> 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) >>=20 >> As you can see, in the User field it says 1001, because the host does = not >> have a user with that UID. >>=20 >> This seems fine, but it becomes an issue when you have multiple Jail = and a >> large host running. >>=20 >> Here's an example if the host had a user with UID 1001, >>=20 >> 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 >>=20 >> Now, you would think that this is good, however, if you run this in = the >> jail, >>=20 >> 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 >>=20 >> As you can see, the UID 1001 was not `antranigv`, instead it was = `oragir`. >>=20 >> This has been an issue for me, so I tried writing some code to = implement >> the following. >>=20 >> If the process is in a Jail, then change the passwd db from /etc to >> /path/of/the/jail/etc. >>=20 >> I thought it would be an easy thing to do, but not so much. >>=20 >> Here's what I've tried. >>=20 >> 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! >>=20 >> 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. >>=20 >> 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, >>=20 >> in /usr/include/pwd.h >>=20 >> #define _PATH_PWD "/etc" >> #define _PATH_PASSWD "/etc/passwd" >> #define _PASSWD "passwd" >> #define _PATH_MASTERPASSWD "/etc/master.passwd" >> #define _MASTERPASSWD "master.passwd" >>=20 >> #define _PATH_MP_DB "/etc/pwd.db" >> #define _MP_DB "pwd.db" >> #define _PATH_SMP_DB "/etc/spwd.db" >> #define _SMP_DB "spwd.db" >>=20 >> #define _PATH_PWD_MKDB "/usr/sbin/pwd_mkdb" >>=20 >> and pwd_mkdb does the following >>=20 >> ... >> strcpy(prefix, _PATH_PWD); >> ... >> case 'd': >> dflag++; >> strlcpy(prefix, optarg, sizeof(prefix)); >> break; >> ... >>=20 >> Tuns out it parses the DB file, but I don't want to do that in = ps/top! :-) >>=20 >> 3) Just for fun, I played with chroot. I tried the following code. >> # cat getpw.c >>=20 >> #define MAXHOSTNAMELEN 255 >> #define MAXPATHLEN 255 >> #include >> //#include >> #include >> #include >> #include >> #include >> #include >> #include >>=20 >> int main(){ >> // Just get root! >> struct passwd *pwd; >> printf("just root: %s\n", (getpwuid(0))->pw_name); >>=20 >> // let's try with undef/define >> #undef _PATH_PWD >> #undef _PATH_PASSWD >> #undef _PASSWD >> #undef _PATH_MASTERPASSWD >> #undef _MASTERPASSWD >>=20 >> #undef _PATH_MP_DB >> #undef _MP_DB >> #undef _PATH_SMP_DB >> #undef _SMP_DB >>=20 >> #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" >>=20 >> #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 =3D getpwuid(1001); >> if (pwd =3D=3D NULL) { >> printf("using undef/define: no user found\n"); >> } else { >> printf("using undef/define: %s\n", pwd->pw_name); >> } >>=20 >> // let's try with chroot! >> chroot("/zdata/jails/fsoc"); >> pwd =3D getpwuid(1001); >> if (pwd =3D=3D NULL) { >> printf("after chroot: no user found\n"); >> } else { >> printf("after chroot: %s\n", pwd->pw_name); >> } >>=20 >> // escape back the chroot ;-) >> chroot("../../../../"); >> pwd =3D getpwuid(1001); >> if (pwd =3D=3D NULL) { >> printf("after unchroot: no user found\n"); >> } else { >> printf("after chroot: %s\n", pwd->pw_name); >> } >>=20 >> return 42; >> } >>=20 >> And I get the following: >>=20 >> # ./getpw >> just root: root >> using undef/define: no user found >> after chroot: romero >> after unchroot: no user found >>=20 >> 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? >>=20 >> Kind regards, have a nice day! >>=20 >> -- >> antranigv >> https://antranigv.am/ >>=20 >=20 > 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