diff options
author | Panu Matilainen <pmatilai@redhat.com> | 2023-03-15 11:35:40 +0200 |
---|---|---|
committer | Panu Matilainen <pmatilai@redhat.com> | 2023-03-30 13:59:28 +0300 |
commit | 009d1397331a89413e2c5eead163cadb47ccdb4b (patch) | |
tree | 4bfcaf38f4b1a0bb02abf2c2d42df66a543fcf6d | |
parent | fc09ee9d60cce879e521ac522b24817de32b3611 (diff) | |
download | rpm-009d1397331a89413e2c5eead163cadb47ccdb4b.tar.gz |
Automatically create sysuser users and groups on install
Add a new built-in %sysusers scriptlet that executes before unpacking
files and creates any sysuser users and groups provided by this package.
This scriptlet gets called with the same arguments as %pre, and
sysusers.d(5) lines are fed into the stdin of the script (ie similar to
file triggers).
systemd-sysusers is the native solution, but compatibility with
non-systemd distros can be achieved by pointing %__systemd_sysusers to a
script that translates the sysusers.d(5) lines into adduser/addgroup
calls or similar.
Unlike all the other package-related scripts, this runs from the outside
of a possible chroot to have access to the user-creation utilities
from the start of the transaction even in case of initial installation.
Users and groups are never deleted, on purpose, as that would lead to
uid/gid reuse and consequently, data leak and general weirdness.
-rw-r--r-- | ci/Dockerfile | 1 | ||||
-rw-r--r-- | docs/man/rpm.8.md | 4 | ||||
-rw-r--r-- | include/rpm/rpmts.h | 2 | ||||
-rw-r--r-- | lib/poptI.c | 4 | ||||
-rw-r--r-- | lib/psm.c | 173 | ||||
-rw-r--r-- | lib/rpmscript.c | 3 | ||||
-rw-r--r-- | macros.in | 3 | ||||
-rw-r--r-- | tests/atlocal.in | 2 | ||||
-rwxr-xr-x | tests/populate | 2 | ||||
-rw-r--r-- | tests/rpmi.at | 34 |
10 files changed, 226 insertions, 2 deletions
diff --git a/ci/Dockerfile b/ci/Dockerfile index 0c2bd08c3..bb119eb52 100644 --- a/ci/Dockerfile +++ b/ci/Dockerfile @@ -43,6 +43,7 @@ RUN dnf -y install \ findutils sed grep gawk diffutils file patch \ tar unzip gzip bzip2 cpio xz \ pkgconfig \ + /usr/bin/systemd-sysusers \ /usr/bin/gdb-add-index \ dwz \ fsverity-utils fsverity-utils-devel \ diff --git a/docs/man/rpm.8.md b/docs/man/rpm.8.md index 450694f17..6fab2d968 100644 --- a/docs/man/rpm.8.md +++ b/docs/man/rpm.8.md @@ -374,6 +374,10 @@ and **%postuntrans** scriptlet(s). and turns off execution of the corresponding **%triggerprein**, **%triggerin**, **%triggerun**, and **%triggerpostun** scriptlet(s). +**\--nosysusers** + +: Don't create sysusers from packages + **\--oldpackage** : Allow an upgrade to replace a newer package with an older one. diff --git a/include/rpm/rpmts.h b/include/rpm/rpmts.h index b9eb6daa0..7b60338d7 100644 --- a/include/rpm/rpmts.h +++ b/include/rpm/rpmts.h @@ -50,7 +50,7 @@ enum rpmtransFlags_e { RPMTRANS_FLAG_NOTRIGGERPOSTUN = (1 << 23), /*!< from --notriggerpostun */ RPMTRANS_FLAG_NOPRETRANS = (1 << 24), /*!< from --nopretrans */ RPMTRANS_FLAG_NOPOSTTRANS = (1 << 25), /*!< from --noposttrans */ - /* bit 26 unused */ + RPMTRANS_FLAG_NOSYSUSERS = (1 << 26), /*!< from --nosysusers */ RPMTRANS_FLAG_NOMD5 = (1 << 27), /*!< from --nomd5 */ RPMTRANS_FLAG_NOFILEDIGEST = (1 << 27), /*!< from --nofiledigest (alias to --nomd5) */ /* bit 28 unused */ diff --git a/lib/poptI.c b/lib/poptI.c index c13e83a94..9c71cc1de 100644 --- a/lib/poptI.c +++ b/lib/poptI.c @@ -246,6 +246,10 @@ struct poptOption rpmInstallPoptTable[] = { &rpmIArgs.transFlags, RPMTRANS_FLAG_NOTRIGGERPOSTUN, N_("do not execute any %%triggerpostun scriptlet(s)"), NULL}, + { "nosysusers", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN, + &rpmIArgs.transFlags, RPMTRANS_FLAG_NOSYSUSERS, + N_("do not create %%sysusers users and groups"), NULL }, + { "oldpackage", '\0', POPT_BIT_SET, &rpmIArgs.probFilter, RPMPROB_FILTER_OLDPACKAGE, N_("upgrade to an old version of the package (--force on upgrades does this automatically)"), @@ -16,6 +16,7 @@ #include <rpm/rpmlog.h> #include <rpm/rpmstring.h> #include <rpm/argv.h> +#include <rpm/rpmbase64.h> #include "lib/fsm.h" /* XXX CPIO_FOO/FSM_FOO constants */ #include "lib/rpmchroot.h" @@ -261,6 +262,173 @@ static rpmRC runInstScript(rpmpsm psm, rpmTagVal scriptTag) return rc; } +struct ARGVi_s { + ARGV_t argv; + int argc; + int ix; +}; + +static const char *nextarg(void *data) +{ + const char *l = NULL; + struct ARGVi_s *avi = data; + if (avi->ix < avi->argc) { + l = avi->argv[avi->ix]; + avi->ix++; + } + return l; +} + +/* Execute all systemd-sysusers separately for all sources */ +static rpmRC execSysusers(rpmpsm psm, Header h, const char *cmd, + ARGV_t *sysusers, int nsysusers) +{ + rpmRC rc = RPMRC_OK; + + for (int i = 0; i < nsysusers; i++) { + const char *path = sysusers[i][0]; + rpmScript script = NULL; + ARGV_t argv = NULL; + + argvAdd(&argv, cmd); + if (*path) { + argvAdd(&argv, "--replace"); + argvAdd(&argv, path); + } + if (!rstreq(rpmtsRootDir(psm->ts), "/")) { + argvAdd(&argv, "--root"); + argvAdd(&argv, rpmtsRootDir(psm->ts)); + } + argvAdd(&argv, "-"); + + script = rpmScriptFromArgv(h, RPMTAG_SYSUSERS, argv, 0, 0); + if (script) { + struct rpmtd_s pfx; + struct ARGVi_s avi = { + .argv = sysusers[i], + .argc = argvCount(sysusers[i]), + .ix = 1, + }; + + rpmScriptSetNextFileFunc(script, nextarg, &avi); + headerGet(h, RPMTAG_INSTPREFIXES, &pfx, + HEADERGET_ALLOC|HEADERGET_ARGV); + rc = runScript(psm->ts, psm->te, h, pfx.data, script, + psm->scriptArg, -1); + rpmtdFreeData(&pfx); + } + rpmScriptFree(script); + argvFree(argv); + + if (rc) + break; + } + return rc; +} + +/* Return providing file path for provides index px, "" on not found. */ +static char *findProviderFile(rpmfiles files, int px) +{ + int fc = rpmfilesFC(files); + char *fn = NULL; + + for (int i = 0; i < fc; i++) { + const uint32_t *ddict = NULL; + uint32_t ndict = rpmfilesFDepends(files, i, &ddict); + for (int j = 0; j < ndict; j++) { + unsigned int dict = ddict[j]; + unsigned int dt = (dict >> 24) & 0xff; + unsigned int dx = dict & 0x00ffffff; + if (dt == 'P' && dx == px) { + fn = rpmfilesFN(files, i); + goto exit; + } + } + } + +exit: + return (fn != NULL) ? fn : xstrdup(""); +} + +static rpmRC runSysusers(rpmpsm psm) +{ + rpmRC rc = RPMRC_OK; + Header h = NULL; + rpmds provides = NULL; + ARGV_t *sysusers = NULL; + int nsysusers = 0; + int dx; + char *cmd = rpmExpand("%{?__systemd_sysusers}", NULL); + + /* Skip all this if disabled */ + if (*cmd == '\0') + goto exit; + + h = rpmteHeader(psm->te); + provides = rpmdsNew(h, RPMTAG_PROVIDENAME, 0); + + /* + * Decode sysusers provides from this header into per-source argv's, + * providing file as the first element ("" for non-file based provides), + * followed by decoded sysusers lines. + */ + while ((dx = rpmdsNext(provides)) >= 0) { + const char *name = rpmdsN(provides); + char *fn = NULL; + char *line = NULL; + size_t llen = 0; + int px = -1; + + if (!(rstreqn(name, "user(", 5) || rstreqn(name, "group(", 6))) + continue; + if (!(rpmdsFlags(provides) & RPMSENSE_EQUAL)) + continue; + if (rpmBase64Decode(rpmdsEVR(provides), (void **)&line, &llen)) + continue; + + if (sysusers == NULL) + sysusers = xcalloc(rpmdsCount(provides), sizeof(*sysusers)); + + /* Find the providing file (if any) */ + fn = findProviderFile(psm->files, dx); + + /* Do we already know this provider? */ + for (int i = 0; i < nsysusers; i++) { + if (rstreq(sysusers[i][0], fn)) { + px = i; + break; + } + } + + /* If not, allocate one */ + if (px == -1) { + px = nsysusers; + argvAdd(&sysusers[px], fn); + nsysusers++; + } + + argvAddN(&sysusers[px], line, llen); + + free(fn); + free(line); + } + + if (sysusers) { + rc = execSysusers(psm, h, cmd, sysusers, nsysusers); + + for (int i = 0; i < nsysusers; i++) + argvFree(sysusers[i]); + free(sysusers); + } + +exit: + rpmdsFree(provides); + headerFree(h); + free(cmd); + + return rc; +} + /** * Execute triggers. * @todo Trigger on any provides, not just package NVR. @@ -671,6 +839,11 @@ static rpmRC rpmPackageInstall(rpmts ts, rpmpsm psm) if (rpmtsFilterFlags(psm->ts) & RPMPROB_FILTER_REPLACEPKG) markReplacedInstance(ts, psm->te); + /* Create sysusers.d(5) users and groups provided by this package */ + if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOSYSUSERS)) { + rc = runSysusers(psm); + if (rc) break; + } if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERPREIN)) { /* Run triggers in other package(s) this package sets off. */ diff --git a/lib/rpmscript.c b/lib/rpmscript.c index 9bcdbb0f2..b3cd67a09 100644 --- a/lib/rpmscript.c +++ b/lib/rpmscript.c @@ -90,6 +90,9 @@ static const struct scriptInfo_s scriptInfo[] = { { RPMSCRIPT_VERIFY, "verify", 0, RPMTAG_VERIFYSCRIPT, RPMTAG_VERIFYSCRIPTPROG, RPMTAG_VERIFYSCRIPTFLAGS, RPMSCRIPT_FLAG_CRITICAL, }, + { RPMSCRIPT_SYSUSERS, "sysusers", 0, + RPMTAG_SYSUSERS, 0, 0, + RPMSCRIPT_FLAG_CRITICAL, }, { 0, "unknown", 0, RPMTAG_NOT_FOUND, RPMTAG_NOT_FOUND, RPMTAG_NOT_FOUND, 0, } @@ -132,6 +132,9 @@ %_keyringpath %{_dbpath}/pubkeys/ +# sysusers helper binary or script, uncomment to disable +%__systemd_sysusers %{_bindir}/systemd-sysusers + # # Path to script that creates debug symbols in a /usr/lib/debug # shadow tree. diff --git a/tests/atlocal.in b/tests/atlocal.in index 3559080bd..cb051570b 100644 --- a/tests/atlocal.in +++ b/tests/atlocal.in @@ -69,6 +69,8 @@ if mknod foodev c 123 123; then rm -f foodev else MKNOD_DISABLED=true + # not related to mknod but close enough for a "am I privileged" test + VERIFYOPTS="--nouser --nogroup" fi MALLOC_DEBUG=libc_malloc_debug.so.0 diff --git a/tests/populate b/tests/populate index cd965553c..87c0c7f4f 100755 --- a/tests/populate +++ b/tests/populate @@ -35,7 +35,7 @@ for cf in hosts resolv.conf passwd group mtab ; do [ -f /etc/${cf} ] && cp /etc/${cf} testing/etc/${cf} done touch testing/etc/{shadow,gshadow} -for prog in gzip cat cp patch tar sh bash ln chmod rm mkdir uname grep sed find file ionice mktemp nice cut sort diff touch install wc coreutils xargs mknod locale; do +for prog in gzip cat cp patch tar sh bash ln chmod rm mkdir uname grep sed find file ionice mktemp nice cut sort diff touch install wc coreutils xargs mknod locale systemd-sysusers; do p=`which ${prog}` if [ "${p}" != "" ]; then ln -s ${p} testing/${bindir}/ diff --git a/tests/rpmi.at b/tests/rpmi.at index 9cfc32187..747008493 100644 --- a/tests/rpmi.at +++ b/tests/rpmi.at @@ -1440,3 +1440,37 @@ deptest-test-obsoletes-1.0-1.noarch ], []) AT_CLEANUP + +AT_SETUP([rpm -i create user]) +AT_KEYWORDS([install]) +AT_CHECK([ +RPMDB_INIT + +runroot rpmbuild -bb --quiet --define "pkg user" --define "provs %{add_sysuser u myuser 876 - /home/myuser /bin/sh}"\ + /data/SPECS/deptest.spec +runroot rpm -U /build/RPMS/noarch/deptest-user-1.0-1.noarch.rpm 2> /dev/null +runroot_other tail -1 /etc/passwd +runroot rpm -V ${VERIFYOPTS} deptest-user +], +[0], +[myuser:x:876:876::/home/myuser:/bin/sh +], +[]) +AT_CLEANUP + +AT_SETUP([rpm -i create group]) +AT_KEYWORDS([install]) +AT_CHECK([ +RPMDB_INIT + +runroot rpmbuild -bb --quiet --define "pkg user" --define "provs %{add_sysuser g mygroup 678}"\ + /data/SPECS/deptest.spec +runroot rpm -i /build/RPMS/noarch/deptest-user-1.0-1.noarch.rpm 2> /dev/null +runroot_other tail -1 /etc/group +runroot rpm -V ${VERIFYOPTS} deptest-user +], +[0], +[mygroup:x:678: +], +[]) +AT_CLEANUP |