[pacman-dev] [WIP 0/4] Dynamic Users
Packages with files owned by dynamically created users/groups have to create the user and chown the files in a post_install/post_upgrade install script. This makes pacman's database out of sync with the filesystem. See FS#43484. This patchset makes alpm/pacman aware of symbolic usernames and uses fakeuser (https://github.com/progandy/fakeuser) to provide a method for PKGBUILDs to add fake users. alpm will still fall back to the raw uid if it has not already been assigned, so packages that add users in post_install/post_upgrade will continue to work correctly, but should be changed to use pre_install/pre_upgrade instead. The makepkg portions should be complete, other than documentation. The alpm/pacman portions still need to be optimized to use a cache so that we don't have to consult /etc/{passwd,group} for every single file. *NOTE*: there is a bug in libarchive that prevents the symlink check from working with this patchset. This will be fixed in the next release. PKGBUILD ======== pkgname=dynamic-users-test pkgver=1 pkgrel=1 arch=(any) install=dynamic-users-test.install # entries are in passwd(5)/group(5) format, empty trailing fields can be omitted sysusers=('myuser') sysgroups=('mygroup') package() { touch "$pkgdir/testfile" chown myuser:mygroup "$pkgdir/testfile" } dynamic-users-test.install ========================== pre_install() { getent passwd myuser &>/dev/null || useradd --system myuser getent group mygroup &>/dev/null || groupadd --system mygroup } pre_upgrade() { pre_install } Andrew Gregory (4): use custom uid/gid lookup functions makepkg add sysusers during package() makepkg: store symbolic user/group names in mtree check_file_permissions: use symbolic user/group names if available lib/libalpm/add.c | 32 ++++++++++++++++++- scripts/Makefile.am | 2 ++ scripts/libmakepkg/.gitignore | 2 ++ scripts/libmakepkg/sysgroups.sh.in | 60 +++++++++++++++++++++++++++++++++++ scripts/libmakepkg/sysusers.sh.in | 65 ++++++++++++++++++++++++++++++++++++++ scripts/makepkg.sh.in | 6 ++-- src/pacman/check.c | 46 +++++++++++++++++++++------ 7 files changed, 200 insertions(+), 13 deletions(-) create mode 100644 scripts/libmakepkg/sysgroups.sh.in create mode 100644 scripts/libmakepkg/sysusers.sh.in -- 2.7.2
--- lib/libalpm/add.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/libalpm/add.c b/lib/libalpm/add.c index f5c9a95..e40ce27 100644 --- a/lib/libalpm/add.c +++ b/lib/libalpm/add.c @@ -106,6 +106,31 @@ int SYMEXPORT alpm_add_pkg(alpm_handle_t *handle, alpm_pkg_t *pkg) return 0; } +#include <pwd.h> +static int64_t lookup_uid(UNUSED void *ctx, const char *uname, int64_t uid) +{ + struct passwd *ent = getpwnam(uname); + if(ent) { + return ent->pw_uid; /* uname exists, return assigned uid */ + } else if((ent = getpwuid(uid))) { + return 0; /* uid is assigned to another user, fall back to root */ + } else { + return uid; /* uid is open, package may add user in post_install */ + } +} +#include <grp.h> +static int64_t lookup_gid(UNUSED void *ctx, const char *gname, int64_t gid) +{ + struct group *ent = getgrnam(gname); + if(ent) { + return ent->gr_gid; /* gname exists, return assigned gid */ + } else if((ent = getgrgid(gid))) { + return 0; /* gid is assigned to another group, fall back to root */ + } else { + return gid; /* gid is open, package may add group in post_install */ + } +} + static int perform_extraction(alpm_handle_t *handle, struct archive *archive, struct archive_entry *entry, const char *filename) { @@ -115,10 +140,15 @@ static int perform_extraction(alpm_handle_t *handle, struct archive *archive, ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_UNLINK | ARCHIVE_EXTRACT_SECURE_SYMLINKS; + struct archive *w = archive_write_disk_new(); + archive_write_disk_set_group_lookup(w, NULL, lookup_gid, NULL); + archive_write_disk_set_user_lookup(w, NULL, lookup_uid, NULL); + archive_write_disk_set_options(w, archive_flags); archive_entry_set_pathname(entry, filename); - ret = archive_read_extract(archive, entry, archive_flags); + ret = archive_read_extract2(archive, entry, w); + archive_write_free(w); if(ret == ARCHIVE_WARN && archive_errno(archive) != ENOSPC) { /* operation succeeded but a "non-critical" error was encountered */ _alpm_log(handle, ALPM_LOG_WARNING, _("warning given when extracting %s (%s)\n"), -- 2.7.2
--- scripts/Makefile.am | 2 ++ scripts/libmakepkg/.gitignore | 2 ++ scripts/libmakepkg/sysgroups.sh.in | 60 +++++++++++++++++++++++++++++++++++ scripts/libmakepkg/sysusers.sh.in | 65 ++++++++++++++++++++++++++++++++++++++ scripts/makepkg.sh.in | 4 ++- 5 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 scripts/libmakepkg/sysgroups.sh.in create mode 100644 scripts/libmakepkg/sysusers.sh.in diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 6f9abb8..a3c1050 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -81,6 +81,8 @@ LIBMAKEPKG_IN = \ libmakepkg/source/hg.sh \ libmakepkg/source/local.sh \ libmakepkg/source/svn.sh \ + libmakepkg/sysgroups.sh \ + libmakepkg/sysusers.sh \ libmakepkg/tidy.sh \ libmakepkg/tidy/docs.sh \ libmakepkg/tidy/emptydirs.sh \ diff --git a/scripts/libmakepkg/.gitignore b/scripts/libmakepkg/.gitignore index 2de91e7..7a784d0 100644 --- a/scripts/libmakepkg/.gitignore +++ b/scripts/libmakepkg/.gitignore @@ -4,6 +4,8 @@ lint_pkgbuild.sh lint_pkgbuild/*.sh source.sh source/*.sh +sysusers.sh +sysgroups.sh tidy.sh tidy/*.sh util.sh diff --git a/scripts/libmakepkg/sysgroups.sh.in b/scripts/libmakepkg/sysgroups.sh.in new file mode 100644 index 0000000..095d60e --- /dev/null +++ b/scripts/libmakepkg/sysgroups.sh.in @@ -0,0 +1,60 @@ +#!/bin/bash +# +# source.sh - functions for downloading and extracting sources +# +# Copyright (c) 2016 Pacman Development Team <pacman-dev@archlinux.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +[[ -n "$LIBMAKEPKG_SOURCE_SYSGROUPS" ]] && return +LIBMAKEPKG_SOURCE_SYSGROUPS=1 + +# field indices +gr_name=0 +gr_passwd=1 +gr_gid=2 +gr_users=3 + +dynamic_gid=101 + +gr_parse_ent() { + IFS=: read -a "$1" +} + +add_fake_group() { + local -a gr fakeuser_args + gr_parse_ent 'gr' <<< "$1" + + if [[ -n ${gr[$gr_gid]} ]]; then + fakeuser_args+=('-g' "${gr[$gr_gid]}") + else + fakeuser_args+=('-g' $(( dynamic_gid++ )) ) + fi + + [[ -n ${gr[$gr_name]} ]] && fakeuser_args+=('-n' "${gr[$gr_name]}") + [[ -n ${gr[$gr_passwd]} ]] && fakeuser_args+=('-p' "${gr[$gr_passwd]}") + [[ -n ${gr[$gr_gid]} ]] && fakeuser_args+=('-g' "${gr[$gr_gid]}") + [[ -n ${gr[$gr_users]} ]] && fakeuser_args+=('-m' "${gr[$gr_gecos]}") + + fakeadd -G "${fakeuser_args[@]}" +} + +add_fake_groups() { + for ent in "${sysgroups[@]}"; do + add_fake_group "$ent" + done +} + +# vim: set noet: diff --git a/scripts/libmakepkg/sysusers.sh.in b/scripts/libmakepkg/sysusers.sh.in new file mode 100644 index 0000000..fbb7328 --- /dev/null +++ b/scripts/libmakepkg/sysusers.sh.in @@ -0,0 +1,65 @@ +#!/bin/bash +# +# source.sh - functions for downloading and extracting sources +# +# Copyright (c) 2016 Pacman Development Team <pacman-dev@archlinux.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +[[ -n "$LIBMAKEPKG_SOURCE_SYSUSERS" ]] && return +LIBMAKEPKG_SOURCE_SYSUSERS=1 + +# field indices +pw_name=0 +pw_passwd=1 +pw_uid=2 +pw_gid=3 +pw_gecos=4 +pw_dir=5 +pw_shell=6 + +dynamic_uid=101 + +pw_parse_ent() { + IFS=: read -a "$1" +} + +add_fake_user() { + local -a pw fakeuser_args + pw_parse_ent 'pw' <<< "$1" + + if [[ -n ${pw[$pw_uid]} ]]; then + fakeuser_args+=('-u' "${pw[$pw_uid]}") + else + fakeuser_args+=('-u' $(( dynamic_uid++ )) ) + fi + + [[ -n ${pw[$pw_name]} ]] && fakeuser_args+=('-n' "${pw[$pw_name]}") + [[ -n ${pw[$pw_passwd]} ]] && fakeuser_args+=('-p' "${pw[$pw_passwd]}") + [[ -n ${pw[$pw_gid]} ]] && fakeuser_args+=('-g' "${pw[$pw_gid]}") + [[ -n ${pw[$pw_gecos]} ]] && fakeuser_args+=('-c' "${pw[$pw_gecos]}") + [[ -n ${pw[$pw_dir]} ]] && fakeuser_args+=('-d' "${pw[$pw_dir]}") + [[ -n ${pw[$pw_shell]} ]] && fakeuser_args+=('-s' "${pw[$pw_shell]}") + + fakeadd -U "${fakeuser_args[@]}" +} + +add_fake_users() { + for ent in "${sysusers[@]}"; do + add_fake_user "$ent" + done +} + +# vim: set noet: diff --git a/scripts/makepkg.sh.in b/scripts/makepkg.sh.in index 2efcc98..a0134f6 100644 --- a/scripts/makepkg.sh.in +++ b/scripts/makepkg.sh.in @@ -167,7 +167,7 @@ clean_up() { enter_fakeroot() { msg "$(gettext "Entering %s environment...")" "fakeroot" - fakeroot -- $0 -F "${ARGLIST[@]}" || exit $? + LD_PRELOAD=/usr/lib/libfakeuser.so fakeroot -- $0 -F "${ARGLIST[@]}" || exit $? } # Automatically update pkgver variable if a pkgver() function is provided @@ -2098,6 +2098,8 @@ if (( INFAKEROOT )); then fi chmod 755 "$pkgdirbase" + add_fake_users + add_fake_groups if (( ! SPLITPKG )); then pkgdir="$pkgdirbase/$pkgname" mkdir "$pkgdir" -- 2.7.2
On 19/03/16 23:50, Andrew Gregory wrote:
--- scripts/Makefile.am | 2 ++ scripts/libmakepkg/.gitignore | 2 ++ scripts/libmakepkg/sysgroups.sh.in | 60 +++++++++++++++++++++++++++++++++++ scripts/libmakepkg/sysusers.sh.in | 65 ++++++++++++++++++++++++++++++++++++++ scripts/makepkg.sh.in | 4 ++- 5 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 scripts/libmakepkg/sysgroups.sh.in create mode 100644 scripts/libmakepkg/sysusers.sh.in
diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 6f9abb8..a3c1050 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -81,6 +81,8 @@ LIBMAKEPKG_IN = \ libmakepkg/source/hg.sh \ libmakepkg/source/local.sh \ libmakepkg/source/svn.sh \ + libmakepkg/sysgroups.sh \ + libmakepkg/sysusers.sh \
libmakepkg/util/?
libmakepkg/tidy.sh \ libmakepkg/tidy/docs.sh \ libmakepkg/tidy/emptydirs.sh \ diff --git a/scripts/libmakepkg/.gitignore b/scripts/libmakepkg/.gitignore index 2de91e7..7a784d0 100644 --- a/scripts/libmakepkg/.gitignore +++ b/scripts/libmakepkg/.gitignore @@ -4,6 +4,8 @@ lint_pkgbuild.sh lint_pkgbuild/*.sh source.sh source/*.sh +sysusers.sh +sysgroups.sh tidy.sh tidy/*.sh util.sh diff --git a/scripts/libmakepkg/sysgroups.sh.in b/scripts/libmakepkg/sysgroups.sh.in new file mode 100644 index 0000000..095d60e --- /dev/null +++ b/scripts/libmakepkg/sysgroups.sh.in @@ -0,0 +1,60 @@ +#!/bin/bash +# +# source.sh - functions for downloading and extracting sources
Update
+# +# Copyright (c) 2016 Pacman Development Team <pacman-dev@archlinux.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +[[ -n "$LIBMAKEPKG_SOURCE_SYSGROUPS" ]] && return
LIBMAKEPKG_SYSGROUPS or LIBMAKEPKG_UTIL_SYSGROUPS
+LIBMAKEPKG_SOURCE_SYSGROUPS=1 + +# field indices +gr_name=0 +gr_passwd=1 +gr_gid=2 +gr_users=3 + +dynamic_gid=101 + +gr_parse_ent() { + IFS=: read -a "$1" +} + +add_fake_group() { + local -a gr fakeuser_args + gr_parse_ent 'gr' <<< "$1" + + if [[ -n ${gr[$gr_gid]} ]]; then + fakeuser_args+=('-g' "${gr[$gr_gid]}") + else + fakeuser_args+=('-g' $(( dynamic_gid++ )) ) + fi + + [[ -n ${gr[$gr_name]} ]] && fakeuser_args+=('-n' "${gr[$gr_name]}") + [[ -n ${gr[$gr_passwd]} ]] && fakeuser_args+=('-p' "${gr[$gr_passwd]}") + [[ -n ${gr[$gr_gid]} ]] && fakeuser_args+=('-g' "${gr[$gr_gid]}") + [[ -n ${gr[$gr_users]} ]] && fakeuser_args+=('-m' "${gr[$gr_gecos]}") + + fakeadd -G "${fakeuser_args[@]}" +} + +add_fake_groups() { + for ent in "${sysgroups[@]}"; do + add_fake_group "$ent" + done +} + +# vim: set noet:
There are no other vim lines in libmakepkg... mostly because I don't use vim! I dislike them due to applying to a single editor. We need to discuss this separately!
diff --git a/scripts/libmakepkg/sysusers.sh.in b/scripts/libmakepkg/sysusers.sh.in new file mode 100644 index 0000000..fbb7328 --- /dev/null +++ b/scripts/libmakepkg/sysusers.sh.in @@ -0,0 +1,65 @@ +#!/bin/bash +# +# source.sh - functions for downloading and extracting sources +# +# Copyright (c) 2016 Pacman Development Team <pacman-dev@archlinux.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +[[ -n "$LIBMAKEPKG_SOURCE_SYSUSERS" ]] && return +LIBMAKEPKG_SOURCE_SYSUSERS=1 + +# field indices +pw_name=0 +pw_passwd=1 +pw_uid=2 +pw_gid=3 +pw_gecos=4 +pw_dir=5 +pw_shell=6 + +dynamic_uid=101 + +pw_parse_ent() { + IFS=: read -a "$1" +} + +add_fake_user() { + local -a pw fakeuser_args + pw_parse_ent 'pw' <<< "$1" + + if [[ -n ${pw[$pw_uid]} ]]; then + fakeuser_args+=('-u' "${pw[$pw_uid]}") + else + fakeuser_args+=('-u' $(( dynamic_uid++ )) ) + fi + + [[ -n ${pw[$pw_name]} ]] && fakeuser_args+=('-n' "${pw[$pw_name]}") + [[ -n ${pw[$pw_passwd]} ]] && fakeuser_args+=('-p' "${pw[$pw_passwd]}") + [[ -n ${pw[$pw_gid]} ]] && fakeuser_args+=('-g' "${pw[$pw_gid]}") + [[ -n ${pw[$pw_gecos]} ]] && fakeuser_args+=('-c' "${pw[$pw_gecos]}") + [[ -n ${pw[$pw_dir]} ]] && fakeuser_args+=('-d' "${pw[$pw_dir]}") + [[ -n ${pw[$pw_shell]} ]] && fakeuser_args+=('-s' "${pw[$pw_shell]}") + + fakeadd -U "${fakeuser_args[@]}" +} + +add_fake_users() { + for ent in "${sysusers[@]}"; do + add_fake_user "$ent" + done +} + +# vim: set noet: diff --git a/scripts/makepkg.sh.in b/scripts/makepkg.sh.in index 2efcc98..a0134f6 100644 --- a/scripts/makepkg.sh.in +++ b/scripts/makepkg.sh.in @@ -167,7 +167,7 @@ clean_up() {
enter_fakeroot() { msg "$(gettext "Entering %s environment...")" "fakeroot" - fakeroot -- $0 -F "${ARGLIST[@]}" || exit $? + LD_PRELOAD=/usr/lib/libfakeuser.so fakeroot -- $0 -F "${ARGLIST[@]}" || exit $?
We should probably have a check that fakeuser is installed.
}
# Automatically update pkgver variable if a pkgver() function is provided @@ -2098,6 +2098,8 @@ if (( INFAKEROOT )); then fi
chmod 755 "$pkgdirbase" + add_fake_users + add_fake_groups if (( ! SPLITPKG )); then pkgdir="$pkgdirbase/$pkgname" mkdir "$pkgdir"
--- scripts/makepkg.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/makepkg.sh.in b/scripts/makepkg.sh.in index a0134f6..1729782 100644 --- a/scripts/makepkg.sh.in +++ b/scripts/makepkg.sh.in @@ -1220,7 +1220,7 @@ create_package() { msg2 "$(gettext "Generating .MTREE file...")" LANG=C bsdtar -czf .MTREE --format=mtree \ - --options='!all,use-set,type,uid,gid,mode,time,size,md5,sha256,link' \ + --options='!all,use-set,type,uid,uname,gid,gname,mode,time,size,md5,sha256,link' \ "${comp_files[@]}" * comp_files+=(".MTREE") -- 2.7.2
Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> --- src/pacman/check.c | 46 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/src/pacman/check.c b/src/pacman/check.c index 0fe74e8..2c2d4b2 100644 --- a/src/pacman/check.c +++ b/src/pacman/check.c @@ -20,6 +20,8 @@ #include <limits.h> #include <string.h> #include <errno.h> +#include <grp.h> +#include <pwd.h> /* pacman */ #include "check.h" @@ -74,22 +76,46 @@ static int check_file_permissions(const char *pkgname, const char *filepath, { int errors = 0; mode_t fsmode; + const char *uname = archive_entry_uname(entry); + const char *gname = archive_entry_gname(entry); /* uid */ - if(st->st_uid != archive_entry_uid(entry)) { - errors++; - if(!config->quiet) { - pm_printf(ALPM_LOG_WARNING, _("%s: %s (UID mismatch)\n"), - pkgname, filepath); + if(uname) { + struct passwd *pw = getpwuid(st->st_uid); + if(!pw || strcmp(uname, pw->pw_name) != 0) { + errors++; + if(!config->quiet) { + pm_printf(ALPM_LOG_WARNING, _("%s: %s (Owner mismatch)\n"), + pkgname, filepath); + } + } + } else { + if(st->st_uid != archive_entry_uid(entry)) { + errors++; + if(!config->quiet) { + pm_printf(ALPM_LOG_WARNING, _("%s: %s (UID mismatch)\n"), + pkgname, filepath); + } } } /* gid */ - if(st->st_gid != archive_entry_gid(entry)) { - errors++; - if(!config->quiet) { - pm_printf(ALPM_LOG_WARNING, _("%s: %s (GID mismatch)\n"), - pkgname, filepath); + if(gname) { + struct group *gr = getgrgid(st->st_gid); + if(!gr || strcmp(gname, gr->gr_name) != 0) { + errors++; + if(!config->quiet) { + pm_printf(ALPM_LOG_WARNING, _("%s: %s (Group mismatch)\n"), + pkgname, filepath); + } + } + } else { + if(st->st_gid != archive_entry_gid(entry)) { + errors++; + if(!config->quiet) { + pm_printf(ALPM_LOG_WARNING, _("%s: %s (GID mismatch)\n"), + pkgname, filepath); + } } } -- 2.7.2
On 19/03/16 23:50, Andrew Gregory wrote:
Packages with files owned by dynamically created users/groups have to create the user and chown the files in a post_install/post_upgrade install script. This makes pacman's database out of sync with the filesystem. See FS#43484.
This patchset makes alpm/pacman aware of symbolic usernames and uses fakeuser (https://github.com/progandy/fakeuser) to provide a method for PKGBUILDs to add fake users.
alpm will still fall back to the raw uid if it has not already been assigned, so packages that add users in post_install/post_upgrade will continue to work correctly, but should be changed to use pre_install/pre_upgrade instead.
The makepkg portions should be complete, other than documentation. The alpm/pacman portions still need to be optimized to use a cache so that we don't have to consult /etc/{passwd,group} for every single file.
Great! I have no substantial comments from a brief look at the patchset. Minor comments to follow.
*NOTE*: there is a bug in libarchive that prevents the symlink check from working with this patchset. This will be fixed in the next release.
So we will need to add a configure check either for the latest libarchive or for this fix. A
participants (2)
-
Allan McRae
-
Andrew Gregory