From mboxrd@z Thu Jan 1 00:00:00 1970 From: David Gibson To: passt-dev@passt.top Subject: Re: [PATCH] conf: Add --runas option, changing to given UID and GID if started as root Date: Thu, 19 May 2022 13:53:08 +1000 Message-ID: In-Reply-To: <20220518171808.1986906-1-sbrivio@redhat.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============8056352821207432593==" --===============8056352821207432593== Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable On Wed, May 18, 2022 at 07:18:08PM +0200, Stefano Brivio wrote: > On some systems, user and group "nobody" might not be available. The > new --runas option allows to override the default "nobody" choice if > started as root. >=20 > Now that we allow this, drop the initgroups() call that was used to > add any additional groups for the given user, as that might now > grant unnecessarily broad permissions. For instance, several > distributions have a "kvm" group to allow regular user access to > /dev/kvm, and we don't need that in passt or pasta. >=20 > Signed-off-by: Stefano Brivio Reviewed-by: David Gibson > --- > conf.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > passt.1 | 7 ++++++ > passt.c | 36 ++++++++++++++++++------------ > passt.h | 5 +++++ > 4 files changed, 102 insertions(+), 14 deletions(-) >=20 > diff --git a/conf.c b/conf.c > index 0baf4fa..2e6e3c4 100644 > --- a/conf.c > +++ b/conf.c > @@ -22,6 +22,8 @@ > #include > #include > #include > +#include > +#include > #include > #include > #include > @@ -614,6 +616,9 @@ static void usage(const char *name) > info( " default: run in background if started from a TTY"); > info( " -e, --stderr Log to stderr too"); > info( " default: log to system logger only if started from a TTY"); > + info( " --runas UID|UID:GID Use given UID, GID if started as root" > + info( " UID and GID can be numeric, or login and group names"); > + info( " default: drop to user \"nobody\""); > info( " -h, --help Display this help message and exit"); > =20 > if (strstr(name, "pasta")) { > @@ -837,6 +842,57 @@ dns6: > } > } > =20 > +/** > + * conf_runas() - Handle --runas: look up desired UID and GID > + * @opt: Passed option value > + * @uid: User ID, set on return if valid > + * @gid: Group ID, set on return if valid > + * > + * Return: 0 on success, negative error code on failure > + */ > +static int conf_runas(const char *opt, unsigned int *uid, unsigned int *gi= d) > +{ > + char ubuf[LOGIN_NAME_MAX], gbuf[LOGIN_NAME_MAX], *endptr; > + struct passwd *pw; > + struct group *gr; > + > + /* NOLINTNEXTLINE(cert-err34-c): 2 if conversion succeeds */ > + if (sscanf(opt, "%u:%u", uid, gid) =3D=3D 2 && uid && gid) > + return 0; > + > + *uid =3D strtol(opt, &endptr, 0); > + if (!*endptr && (*gid =3D *uid)) > + return 0; > + > +#ifdef GLIBC_NO_STATIC_NSS > + (void)ubuf; > + (void)gbuf; > + (void)pw; > + > + return -EINVAL; > +#else > + /* NOLINTNEXTLINE(cert-err34-c): 2 if conversion succeeds */ > + if (sscanf(opt, "%" STR(LOGIN_NAME_MAX) "[^:]:" > + "%" STR(LOGIN_NAME_MAX) "s", ubuf, gbuf) =3D=3D 2) { > + pw =3D getpwnam(ubuf); > + if (!pw || !(*uid =3D pw->pw_uid)) > + return -ENOENT; > + > + gr =3D getgrnam(gbuf); > + if (!gr || !(*gid =3D gr->gr_gid)) > + return -ENOENT; > + > + return 0; > + } > + > + pw =3D getpwnam(ubuf); > + if (!pw || !(*uid =3D pw->pw_uid) || !(*gid =3D pw->pw_gid)) > + return -ENOENT; > + > + return 0; > +#endif /* !GLIBC_NO_STATIC_NSS */ > +} > + > /** > * conf() - Process command-line arguments and set configuration > * @c: Execution context > @@ -889,6 +945,7 @@ void conf(struct ctx *c, int argc, char **argv) > {"dns-forward", required_argument, NULL, 9 }, > {"no-netns-quit", no_argument, NULL, 10 }, > {"trace", no_argument, NULL, 11 }, > + {"runas", required_argument, NULL, 12 }, > { 0 }, > }; > struct get_bound_ports_ns_arg ns_ports_arg =3D { .c =3D c }; > @@ -1032,6 +1089,17 @@ void conf(struct ctx *c, int argc, char **argv) > =20 > c->trace =3D c->debug =3D c->foreground =3D 1; > break; > + case 12: > + if (c->uid || c->gid) { > + err("Multiple --runas options given"); > + usage(argv[0]); > + } > + > + if (conf_runas(optarg, &c->uid, &c->gid)) { > + err("Invalid --runas option: %s", optarg); > + usage(argv[0]); > + } > + break; > case 'd': > if (c->debug) { > err("Multiple --debug options given"); > diff --git a/passt.1 b/passt.1 > index cdca3e9..d3af916 100644 > --- a/passt.1 > +++ b/passt.1 > @@ -95,6 +95,13 @@ Log to standard error too. > Default is to log to system logger only, if started from an interactive > terminal, and to both system logger and standard error otherwise. > =20 > +.TP > +.BR \-\-runas " " \fIUID\fR|\fIUID:GID\fR|\fILOGIN\fR|\fILOGIN:GROUP\fR > +If started as root, change to given UID and corresponding group if UID is = given, > +or to given UID and given GID if both are given. Alternatively, login name= , or > +login name and group name can be passed. > +Default is to change to user \fInobody\fR if started as root. > + > .TP > .BR \-h ", " \-\-help > Display a help message and exit. > diff --git a/passt.c b/passt.c > index e5064f8..0732176 100644 > --- a/passt.c > +++ b/passt.c > @@ -191,9 +191,9 @@ static void seccomp(const struct ctx *c) > } > =20 > /** > - * check_root() - Warn if root in init, exit if we can't drop to nobody > + * check_root() - Check if root in init ns, exit if we can't drop to user > */ > -static void check_root(void) > +static void check_root(struct ctx *c) > { > const char root_uid_map[] =3D " 0 0 4294967295"; > struct passwd *pw; > @@ -214,22 +214,29 @@ static void check_root(void) > =20 > close(fd); > =20 > - fprintf(stderr, "Don't run this as root. Changing to nobody...\n"); > + if (!c->uid) { > + fprintf(stderr, "Don't run as root. Changing to nobody...\n"); > #ifndef GLIBC_NO_STATIC_NSS > - pw =3D getpwnam("nobody"); > - if (!pw) { > - perror("getpwnam"); > - exit(EXIT_FAILURE); > - } > + pw =3D getpwnam("nobody"); > + if (!pw) { > + perror("getpwnam"); > + exit(EXIT_FAILURE); > + } > =20 > - if (!initgroups(pw->pw_name, pw->pw_gid) && > - !setgid(pw->pw_gid) && !setuid(pw->pw_uid)) > - return; > + c->uid =3D pw->pw_uid; > + c->gid =3D pw->pw_gid; > #else > - (void)pw; > + (void)pw; > + > + /* Common value for 'nobody', not really specified */ > + c->uid =3D c->gid =3D 65534; > #endif > + } > =20 > - fprintf(stderr, "Can't change to user/group nobody, exiting"); > + if (!setgid(c->gid) && !setuid(c->uid)) > + return; > + > + fprintf(stderr, "Can't change user/group, exiting"); > exit(EXIT_FAILURE); > } > =20 > @@ -336,7 +343,6 @@ int main(int argc, char **argv) > =20 > arch_avx2_exec(argv); > =20 > - check_root(); > drop_caps(); > =20 > c.pasta_userns_fd =3D c.pasta_netns_fd =3D c.fd_tap =3D c.fd_tap_listen = =3D -1; > @@ -391,6 +397,8 @@ int main(int argc, char **argv) > conf(&c, argc, argv); > trace_init(c.trace); > =20 > + check_root(&c); > + > if (!c.debug && (c.stderr || isatty(fileno(stdout)))) > __openlog(log_name, LOG_PERROR, LOG_DAEMON); > =20 > diff --git a/passt.h b/passt.h > index 69e334d..e541341 100644 > --- a/passt.h > +++ b/passt.h > @@ -106,6 +106,8 @@ enum passt_modes { > * @sock_path: Path for UNIX domain socket > * @pcap: Path for packet capture file > * @pid_file: Path to PID file, empty string if not configured > + * @uid: UID we should drop to, if started as root > + * @gid: GID we should drop to, if started as root > * @pasta_netns_fd: File descriptor for network namespace in pasta mode > * @pasta_userns_fd: Descriptor for user namespace to join, -1 once joined > * @netns_only: In pasta mode, don't join or create a user namespace > @@ -170,6 +172,9 @@ struct ctx { > char pcap[PATH_MAX]; > char pid_file[PATH_MAX]; > =20 > + uid_t uid; > + uid_t gid; > + > int pasta_netns_fd; > int pasta_userns_fd; > int netns_only; --=20 David Gibson | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_ | _way_ _around_! http://www.ozlabs.org/~dgibson --===============8056352821207432593== Content-Type: application/pgp-signature Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="signature.asc" MIME-Version: 1.0 LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlRSXpCQUVCQ0FBZEZpRUVvVUx4V3U0L1dz MGRCK1h0Z3lwWTRnRXdZU0lGQW1LRnZ4NEFDZ2tRZ3lwWTRnRXcKWVNLNHpRLy9YeEh0ZUtmWGwv VnRPTGVnT2VLd004L0tzT2FQTU9qdUNaTVJBMEtPdGlOUkFHYUFQZjU2bEFPSgpCbG9CdldjaW8v T0syTk9UOGdQSEhqVlpObS9PUnNBVWVqVGF0NFVvaUpUcGxpNFpXbjFjYlBEMDZqRWI2TGlICnNi UmNOQWdYbGxNZ0tnbE5GdXgzSzRNbk5yWHlCMHZHRWJMS0JBaXE0WXBvdWZrdDJmOUZ6TFU0bXNj djBQbU4KZTBtdGtwSFk1MnN3L0owMFFoVUhyQjFFeUVqYTdRc2ptSWpwL0ZBV21ZT05nMms3dGxk MTQveGNZZHpiOUxhaAphakkvZXJQTjNpaHBUaytNd1REaEJTVWFBSkU0WnFLVmx3NmpZZG45dC9Q TDBRNGcyMWpBbjFTRWNlRjd1RFhICktCalFTYm9vWERrKzJmZmNmOTYxa1JXcWgyMlNieTdsc0hm a1VkUDZOQTdoZ0pJR1ZJL1J0QTZIV1p4RTU3ZUUKcFJkQ3pQdDZpK2VxdWREZTl0UVFDQ2xpODhm WTRWbzdHM0RQTGRDZEpROU5obHUyWUlPWEs1SEx1eFl2K1JTTApRaHNrZmZoVjhjRmZxdEttY29G aExNUDVHMTBXVG8zcTFoeU9GVms1MHZLSGI1TVpwWGdrYmxXNFRyQzQ4YmpMCk9kTE85cVBENzE5 bFFlWnN4VktJRTBhOHh5YmRFdVZqVncwSUVhcjhaeHNRQi9MdjY3cmhIQmc4TlMrMFFjRXEKRWdU akJjdVpQSWtQanR3bitoMUhIUWxLUjk0aUFIZW14TFFBYWJ6S2FsMXkxOE1DbDdTTy9pQlhXb1lK L0o4Vwo4K1dvcUFYQjVWbGpVQzFSU1FkZU54Nk1RVlN0QW1FTklVeW1qSkIwRXcwMHJmMmJPVkE9 Cj1qTnRNCi0tLS0tRU5EIFBHUCBTSUdOQVRVUkUtLS0tLQo= --===============8056352821207432593==--