Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ all:
ifeq ($(ENABLE_SELINUX),1)
$(MAKE) -C selinux -f /usr/share/selinux/devel/Makefile -- $(selinux_policies)
endif
$(MAKE) -C passwordless-root

clean:
make -C misc clean
make -C qubes-rpc clean
make -C doc clean
make -C passwordless-root clean
rm -rf qubesagent/*.pyc qubesagent/__pycache__
rm -rf test-packages/__pycache__
rm -rf test-packages/qubesagent.egg-info
Expand Down
2 changes: 1 addition & 1 deletion archlinux/PKGBUILD.in
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ build() {
# Fix for archlinux sbindir
sed 's:/usr/sbin/ntpdate:/usr/bin/ntpdate:g' -i qubes-rpc/sync-ntp-clock

for dir in qubes-rpc misc; do
for dir in qubes-rpc misc passwordless-root; do
make -C "$dir" VERSION=${pkgver}
done
}
Expand Down

This file was deleted.

8 changes: 6 additions & 2 deletions debian/qubes-core-agent-passwordless-root.install
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
etc/polkit-1/rules.d/00-qubes-allow-all.rules
etc/qubes/admin-authzd.conf
etc/sudoers.d/qubes
usr/share/pam-configs/su.qubes
usr/share/pam-configs/qubes-admin-authz
usr/bin/qubes-admin-authzd
usr/lib/*/security/pam_qubes_admin_authz.so
lib/systemd/system/qubes-admin-authzd.service
usr/share/doc/qubes-core-agent-passwordless-root/README.md
12 changes: 12 additions & 0 deletions debian/qubes-core-agent-passwordless-root.postinst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ pam-auth-update --package

#DEBHELPER#

# If systemd is running try starting qubes-admin-authzd even if the user
# blocked the normal start by dh (above) via policy-rc.d. Not running it will
# break passwordless auth, so this is most likely not what the user wanted. But
# if they really want to they still can mask the service.

if [ -e /run/systemd/system ] &&
! systemctl is-active --quiet qubes-admin-authzd.service &&
systemctl is-enabled --quiet qubes-admin-authzd.service
then
systemctl start qubes-admin-authzd.service || true
fi
Comment on lines +29 to +39

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a concrete scenario where policy-rc.d blocks startup of something incorrectly? I'm a bit worried about assuming the user's intentions here, since Kicksecure's build system uses a "deny everything" policy-rc.d to prevent everything from starting, because of the fallout it can have on the rest of the build. Bypassing this mechanism seems dangerous at best. If a user does end up blocked, they can always open a DispVM console and log in as root, or use qvm-run -u root ....

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have this in (some of) my Debian templates as a stopgap solution to allow disabling services of newly installed packages before they are run (Debian's default policy).

since Kicksecure's build system uses a "deny everything" policy-rc.d to prevent everything from starting

But in that case you don't have a running systemd, right? So this is a nop.

That being said I don't feel too strongly about handling this corner case. So would be fine with dropping it, if preferred.


exit 0

# vim: set ts=4 sw=4 sts=4 et :
3 changes: 2 additions & 1 deletion debian/rules
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ override_dh_fixperms:
dh_fixperms -a -Xqfile-unpacker

override_dh_systemd_start:
dh_systemd_start --no-restart-on-upgrade
dh_systemd_start --package=qubes-core-agent-passwordless-root --restart-after-upgrade
dh_systemd_start --remaining-packages --no-restart-after-upgrade

override_dh_install:
if [ "$(DISTRIBUTION)" = "Ubuntu" ]; then \
Expand Down
2 changes: 2 additions & 0 deletions passwordless-root/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pam_qubes_admin_authz.so
qubes-admin-authzd
4 changes: 4 additions & 0 deletions passwordless-root/75-qubes-admin-authz.preset
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# This will be re-preset on package upgrade. If you really want you can
# override this with a higher-priority preset file but that is probably a bad
# idea since it will break the pam module.
enable qubes-admin-authzd.service
55 changes: 49 additions & 6 deletions passwordless-root/Makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
BINDIR ?= /usr/bin
SYSCONFDIR ?= /etc
SUDOERSDIR = $(SYSCONFDIR)/sudoers.d
POLKIT1DIR = $(SYSCONFDIR)/polkit-1
PAMDIR = $(SYSCONFDIR)/pam.d
PAMCONFIGSDIR = /usr/share/pam-configs/
SYSLIBDIR ?= /lib

.PHONY: install install-debian install-rh
ifneq ($(DEB_HOST_MULTIARCH),)
PAM_MOD_DIR = /usr/lib/$(DEB_HOST_MULTIARCH)/security
else
PAM_MOD_DIR = /usr/lib64/security
endif

.PHONY: install install-debian install-rh all clean

all: qubes-admin-authzd pam_qubes_admin_authz.so

clean:
rm -f qubes-admin-authzd pam_qubes_admin_authz.so

qubes-admin-authzd: qubes-admin-authzd.c qubes-admin-authz-common.h
gcc -O2 -Wall -Wextra -Werror -fPIC -pie $(CFLAGS) $(LDFLAGS) $< -o $@ -lqubesdb

pam_qubes_admin_authz.so: pam_qubes_admin_authz.c qubes-admin-authz-common.h
gcc -O2 -Wall -Wextra -Werror -fPIC -pie -shared $(CFLAGS) $(LDFLAGS) $< -o $@ -lpam

install:
install -d -m 0750 $(DESTDIR)$(SUDOERSDIR)
Expand All @@ -14,11 +32,36 @@ install:
sed -E '/^[^#]/s/\<(ROLE|TYPE)=[A-Za-z0-9_]+[[:space:]]+//g' qubes.sudoers | \
install -D -m 0440 /dev/stdin $(DESTDIR)$(SUDOERSDIR)/qubes; \
fi
install -d -m 0750 $(DESTDIR)$(POLKIT1DIR)/rules.d
install -D -m 0644 polkit-1-qubes-allow-all.rules $(DESTDIR)$(POLKIT1DIR)/rules.d/00-qubes-allow-all.rules
install -D -t $(DESTDIR)$(PAM_MOD_DIR) pam_qubes_admin_authz.so
install -D -t $(DESTDIR)$(BINDIR) qubes-admin-authzd
install -D -m 0644 -t $(DESTDIR)$(SYSLIBDIR)/systemd/system qubes-admin-authzd.service
install -D -m 0644 -t $(DESTDIR)$(SYSCONFDIR)/qubes/ admin-authzd.conf
install -D -m 0644 -t $(DESTDIR)/usr/share/doc/qubes-core-agent-passwordless-root README.md

install-rh:
install -D -m 0644 pam.d_su.qubes $(DESTDIR)$(PAMDIR)/su.qubes
install -D -m 0644 -t $(DESTDIR)$(SYSLIBDIR)/systemd/system-preset 75-qubes-admin-authz.preset
install -d $(DESTDIR)/usr/share/authselect/vendor
for i in \
local \
nis \
sssd \
winbind \
; do \
install -D -t $(DESTDIR)/usr/share/authselect/vendor/$$i \
authselect/$$i/system-auth authselect/$$i/password-auth || exit 1; \
for j in \
README \
REQUIREMENTS \
dconf-db \
dconf-locks \
fingerprint-auth \
nsswitch.conf \
postlogin \
smartcard-auth \
; do \
ln -s ../../default/$$i/$$j $(DESTDIR)/usr/share/authselect/vendor/$$i/$$j || exit 1; \
done; \
done

install-debian:
install -D -m 0644 pam-configs_su.qubes $(DESTDIR)$(PAMCONFIGSDIR)/su.qubes
install -D -m 0644 pam-configs-qubes-admin-authz $(DESTDIR)$(PAMCONFIGSDIR)/qubes-admin-authz
88 changes: 88 additions & 0 deletions passwordless-root/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Passwordless in-qube admin authorization

By default all users in the `qubes` group (usually only the standard `user`
user) are allowed to execute things as root via sudo/su/pkexec. This is allowed
without a password prompt.

Separating the user isn't that meaningfull since Qubes' primary
compartimentalization layer are qubes and `user` in a qube has access most
things there anyway (user data, GUI I/O, etc). But some advanced users want
more options therefore this mechanism has been extended.

## Modes

There are 3 modes:

- `allow`: Always allow admin access.
- `deny`: Always deny admin access.
- `qrexec`: Make a qrexec call and use it's result to allow/deny. The main
usage is to have a trivial qrexec service in dom0 and use the qrexec policy
with the ask action to allow only after prompting.

"Admin access" here means being able to run things as another user (including
root), via sudo, su and polkit. This is always limited to users in the `qubes`
group.


## Config

When the `qubes-core-agent-passwordless-root` is installed the
`pam_qubes_admin_authz.so` PAM module is always enabled. This module asks the
`qubes-admin-authzd` daemon which does the actual logic (see below for
technical details).

The mode can be set via /usr/local/etc/qubes/admin-authzd.conf for per-qube
setting or more common via /etc/qubes/admin-authzd.conf in a template (setting
in /usr/local has precendce).
Comment thread
marmarek marked this conversation as resolved.

There config file have a very simple syntax. The first line needs to contain
one of the listed mode above without anythings else. Currently the rest of the
file is ignored. Please prefix comments with `#` to allow extension of this
configuration file should the need arise.

Additionally the mode can be set by setting the `admin-authz-mode` feature on a
qube or it's template. This setting override the above config files (internally
the feature sets a qubesdb entry, which is read on boot and written to
/run/qubes-admin-authzd.conf).


## qrexec policy

After setting the mode to qrexec, you need to configure the qrexec policy in
dom0. For example:

```
qubes.AuthorizeAdminAccess * * @default ask target=dom0 default_target=dom0
```

asks for requests from any VM.

Note that in dom0 only a trivial service is run that returns a fixed string
such that the qube knows the result of the policy evaluation. The idea here is
that this way the existing qrexec policy can be reused, including it's ask
prompt.


## Limitaions

Keep in mind that if you have allowed admin access and the qube was compromised
after that point persistence is trivial.


## Technical details

We implement a PAM module named `pam_qubes_admin_authz.so` to permit the
access. Since PAM modules can be run in a setuid context (when called by
sudo/su) we want to keep the code simple there. Therefore we use just some
existing PAM helper functions to check for the requesting users group
membership and check the PAM "service" (`sudo`, `su-l`, etc.) and if they match
we make a connection to an abstract Unix socket. With `SO_PEERCRED` we check
that this socket has been opened by root. The rest, including config file
handling and invoking qrexec-client-vm is then handled by the
`qubes-admin-authzd` at the other end of the socket (started by
qubes-admin-authzd.service).

## Recovery

Should you have locked you self out you should still be able to use
`qvm-console-dispvm` and login as root there without a password.
4 changes: 4 additions & 0 deletions passwordless-root/admin-authzd.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
allow

# The first line of this file needs to be 'deny', 'allow' or 'qrexec'.
# For more details see /usr/share/doc/qubes-core-agent-passwordless-root/README
33 changes: 33 additions & 0 deletions passwordless-root/authselect/local/password-auth
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
auth required pam_env.so
auth required pam_faildelay.so delay=2000000
auth required pam_faillock.so preauth silent {include if "with-faillock"}
auth sufficient pam_qubes_admin_authz.so group=qubes services=su,su-l,sudo,sudo-i,polkit-1
auth sufficient pam_u2f.so cue {include if "with-pam-u2f"}
auth required pam_u2f.so cue {if not "without-pam-u2f-nouserok":nouserok} {include if "with-pam-u2f-2fa"}
auth sufficient pam_unix.so {if not "without-nullok":nullok}
auth sufficient pam_systemd_home.so {include if "with-systemd-homed"}
auth required pam_faillock.so authfail {include if "with-faillock"}
auth optional pam_gnome_keyring.so only_if=login auto_start {include if "with-pam-gnome-keyring"}
auth required pam_deny.so

account required pam_access.so {include if "with-pamaccess"}
account required pam_faillock.so {include if "with-faillock"}
account sufficient pam_systemd_home.so {include if "with-systemd-homed"}
account required pam_unix.so

password sufficient pam_systemd_home.so {include if "with-systemd-homed"}
password requisite pam_pwquality.so
password [default=1 ignore=ignore success=ok] pam_localuser.so {include if "with-pwhistory"}
password requisite pam_pwhistory.so use_authtok {include if "with-pwhistory"}
password sufficient pam_unix.so yescrypt shadow {if not "without-nullok":nullok} use_authtok
password required pam_deny.so

session optional pam_keyinit.so revoke
session required pam_limits.so
session optional pam_ecryptfs.so unwrap {include if "with-ecryptfs"}
session optional pam_systemd_home.so {include if "with-systemd-homed"}
-session optional pam_systemd.so
session optional pam_oddjob_mkhomedir.so {include if "with-mkhomedir"}
session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session required pam_unix.so
session optional pam_gnome_keyring.so only_if=login auto_start {include if "with-pam-gnome-keyring"}
34 changes: 34 additions & 0 deletions passwordless-root/authselect/local/system-auth
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
auth required pam_env.so
auth required pam_faildelay.so delay=2000000
auth required pam_faillock.so preauth silent {include if "with-faillock"}
auth sufficient pam_qubes_admin_authz.so group=qubes services=su,su-l,sudo,sudo-i,polkit-1
auth sufficient pam_fprintd.so {include if "with-fingerprint"}
auth sufficient pam_u2f.so cue {include if "with-pam-u2f"}
auth required pam_u2f.so cue {if not "without-pam-u2f-nouserok":nouserok} {include if "with-pam-u2f-2fa"}
auth sufficient pam_unix.so {if not "without-nullok":nullok}
auth sufficient pam_systemd_home.so {include if "with-systemd-homed"}
auth required pam_faillock.so authfail {include if "with-faillock"}
auth optional pam_gnome_keyring.so only_if=login auto_start {include if "with-pam-gnome-keyring"}
auth required pam_deny.so

account required pam_access.so {include if "with-pamaccess"}
account required pam_faillock.so {include if "with-faillock"}
account sufficient pam_systemd_home.so {include if "with-systemd-homed"}
account required pam_unix.so

password sufficient pam_systemd_home.so {include if "with-systemd-homed"}
password requisite pam_pwquality.so
password [default=1 ignore=ignore success=ok] pam_localuser.so {include if "with-pwhistory"}
password requisite pam_pwhistory.so use_authtok {include if "with-pwhistory"}
password sufficient pam_unix.so yescrypt shadow {if not "without-nullok":nullok} use_authtok
password required pam_deny.so

session optional pam_keyinit.so revoke
session required pam_limits.so
session optional pam_ecryptfs.so unwrap {include if "with-ecryptfs"}
session optional pam_systemd_home.so {include if "with-systemd-homed"}
-session optional pam_systemd.so
session optional pam_oddjob_mkhomedir.so {include if "with-mkhomedir"}
session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session required pam_unix.so
session optional pam_gnome_keyring.so only_if=login auto_start {include if "with-pam-gnome-keyring"}
33 changes: 33 additions & 0 deletions passwordless-root/authselect/nis/password-auth
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
auth required pam_env.so
auth required pam_faildelay.so delay=2000000
auth required pam_faillock.so preauth silent {include if "with-faillock"}
auth sufficient pam_qubes_admin_authz.so group=qubes services=su,su-l,sudo,sudo-i,polkit-1
auth sufficient pam_u2f.so cue {include if "with-pam-u2f"}
auth required pam_u2f.so cue {if not "without-pam-u2f-nouserok":nouserok} {include if "with-pam-u2f-2fa"}
auth sufficient pam_unix.so {if not "without-nullok":nullok}
auth sufficient pam_systemd_home.so {include if "with-systemd-homed"}
auth required pam_faillock.so authfail {include if "with-faillock"}
auth optional pam_gnome_keyring.so only_if=login auto_start {include if "with-pam-gnome-keyring"}
auth required pam_deny.so

account required pam_access.so {include if "with-pamaccess"}
account required pam_faillock.so {include if "with-faillock"}
account sufficient pam_systemd_home.so {include if "with-systemd-homed"}
account required pam_unix.so broken_shadow

password sufficient pam_systemd_home.so {include if "with-systemd-homed"}
password requisite pam_pwquality.so {if not "with-nispwquality":local_users_only}
password [default=1 ignore=ignore success=ok] pam_localuser.so {include if "with-pwhistory"}
password requisite pam_pwhistory.so use_authtok {include if "with-pwhistory"}
password sufficient pam_unix.so yescrypt shadow {if not "without-nullok":nullok} use_authtok nis
password required pam_deny.so

session optional pam_keyinit.so revoke
session required pam_limits.so
session optional pam_ecryptfs.so unwrap {include if "with-ecryptfs"}
session optional pam_systemd_home.so {include if "with-systemd-homed"}
-session optional pam_systemd.so
session optional pam_oddjob_mkhomedir.so {include if "with-mkhomedir"}
session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session required pam_unix.so
session optional pam_gnome_keyring.so only_if=login auto_start {include if "with-pam-gnome-keyring"}
Loading