summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPanu Matilainen <pmatilai@redhat.com>2023-03-15 11:35:40 +0200
committerPanu Matilainen <pmatilai@redhat.com>2023-03-30 13:59:28 +0300
commit009d1397331a89413e2c5eead163cadb47ccdb4b (patch)
tree4bfcaf38f4b1a0bb02abf2c2d42df66a543fcf6d
parentfc09ee9d60cce879e521ac522b24817de32b3611 (diff)
downloadrpm-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/Dockerfile1
-rw-r--r--docs/man/rpm.8.md4
-rw-r--r--include/rpm/rpmts.h2
-rw-r--r--lib/poptI.c4
-rw-r--r--lib/psm.c173
-rw-r--r--lib/rpmscript.c3
-rw-r--r--macros.in3
-rw-r--r--tests/atlocal.in2
-rwxr-xr-xtests/populate2
-rw-r--r--tests/rpmi.at34
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)"),
diff --git a/lib/psm.c b/lib/psm.c
index da82355b8..8903df266 100644
--- a/lib/psm.c
+++ b/lib/psm.c
@@ -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, }
diff --git a/macros.in b/macros.in
index b3460b03b..3a1306bcb 100644
--- a/macros.in
+++ b/macros.in
@@ -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