-
-
Notifications
You must be signed in to change notification settings - Fork 125
Implement optional admin authorization via qrexec #613
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've looked at the PAM module implementation for now, but not much at the PAM configs. On the first sight looks like there is quite a bit of duplication in the PAM configs, but that's probably unavoidable to make it work with any authselect profile...
all: 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 $< -o $@ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Include standard CFLAGS var too (both debian and fedora add preferred hardening options here)
gcc -O2 -Wall -Wextra -Werror -fPIC -pie $< -o $@ | ||
|
||
pam_qubes_admin_authz.so: pam_qubes_admin_authz.c qubes-admin-authz-common.h | ||
gcc -O2 -Wall -Wextra -Werror -fPIC -pie -shared $< -o $@ -lpam |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and here too
passwordless-root/README.md
Outdated
dom0. For example: | ||
|
||
``` | ||
qubes.AuthorizeInVMAdminAccess * * @default target=dom0 default_target=dom0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"ask" is missing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. I'm wondering if we should use "VM" in the service name or change it to "qube"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder about even simpler name like qubes.AuthorizeAdminAccess
? But it might be confusing where that admin is (whether it gives dom0 admin access for example)... But OTOH that concern should be covered by the custom prompt already, right? (at least for the "ask" case)
@marmarta any opinion? Past related discussion starting at this comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think AuthorizeAdminAccess is ok, within context of the custom prompt.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As in, if we got a prompt that just had the service name I'm with @HW42 , but if there's a custom prompt, it's fine to omit it. If we do keep it, lets make it InQube, because from what I understand in can apply to things like remote qubes which might not be vms..
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). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe make it configurable via qvm-service (too?) so it's more compatible with disposable qubes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. Since services are boolean we need 3. Any preferences regarding naming?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe root-access-deny
, root-access-prompt
and root-access-allow
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whonix would like for this to be configurable in Qube Manager, via a tri-select dropdown with a default setting. Is qvm-service
the best tool for this job, or would something like qvm-prefs
or qvm-features
work better here? (I would assume a property settable via qvm-prefs
and readable via qubesdb would be best.)
if (!(strcmp(service, "su") == 0 || | ||
strcmp(service, "su-l") == 0 || | ||
strcmp(service, "sudo") == 0 || | ||
strcmp(service, "sudo-i") == 0 || | ||
strcmp(service, "polkit-1") == 0)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this necessary, instead of just adding it to only relevant PAM configuration files? If it's necessary, better have the list in the module parameter, so it can be adjusted without rebuilding (for example to use it for other services too, like doas
or login
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think yes, given how Debian and Fedora structure their PAM configurations. They have common stacks (like /etc/pam.d/common-auth
) and those are then included by the service specific configs (like /etc/pam.d/su
). Since the later are owned by those packages they are not so easy to update, unlike the former, for with their is authselect
in Fedora and pam-auth-update
in Debian.
My original plan was to configure it like this:
auth [default=1 success=ignore] pam_succeed_if.so use_uid user ingroup qubes service in su:su-l:sudo:sudo-i:polkit-1
auth sufficient pam_qubes_admin_authz.so
This works under Debian but the PAM stack under Fedora is broken because how of how it interacts with the relative jump (in default=1
) works:
jump over the next N modules in the stack. Note that N equal to 0 is not allowed, it would be treated as ignore in such case. The side effect depends on the PAM function call: for pam_authenticate, pam_acct_mgmt, pam_chauthtok, and pam_open_session it is ignore; for pam_setcred and pam_close_session it is one of ignore, ok, or bad depending on the module's return value.
This means that for non matching requests pam_setcred
get a failed state for non-matching requests and this is propagated through. This even breaks login on the console!
This can be worked around by negating the logic:
auth [default=ignore success=1] pam_succeed_if.so use_uid user ingroup qubes service in su:su-l:sudo:sudo-i:polkit-1
auth [default=1] pam_permit.so
auth sufficient pam_qubes_admin_authz.so
But I disliked this relatively complex jumping around.
But making it configurable via a module parameter is easy. In the end you will need to re-build the package anyway. But it would be easier for experimenting or other unusual usage of the module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
log to stderr instead of stdout?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tend to use stdout if it's the only and main output of a program, like in this case, but fine with me. Will update it together with the other changes.
return 1; | ||
} | ||
|
||
execlp("qrexec-client-vm", "qrexec-client-vm", "@default", "qubes.AuthorizeInVMAdminAccess", NULL); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just an idea: add PAM service name as the call argument? This way you could for example allow polkit but not sudo. Or do you think it it's not really useful?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure about a use case, since you get full admin access in all cases. But if you think it's useful, I would not object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not really sure. It could be useful if there are cases where it allows some limited root access, but even for polkit it would allow pkexec (right?) so a full shell anyway... So, maybe not that useful?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes currently the polkit config allows full access. But yes, in the future their might be an PAM using program that doesn't give full access.
sudo has an pam_service
option, so you might build something based on that. Since polkit is scriptable there probably too, but I didn't looked into that.
56960e0
to
e7b9577
Compare
See included README for details. Issue: QubesOS/qubes-issues#2695
e7b9577
to
b7decf6
Compare
Those are direct copies of the Fedora configs, just with our line added. And yes AFAIU that's how you are supposed to use authselect: You can only define a full profile not a small addition like with pam-auth-config. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly looks good and I like the mechanism. The main caveat is that privilege escalation via various Xen devices is likely still possible. This is because libvchan does not have kernel support.
if (read_ret < 0) { | ||
pam_syslog(pamh, LOG_ERR, "failed to read from socket: %i", errno); | ||
rc = PAM_SYSTEM_ERR; | ||
goto ret; | ||
} | ||
|
||
// Since the other side is trusted this isn't strictly necessary. But it's | ||
// probably still nicer to ensure that we don't put unexpected bytes into | ||
// the log. | ||
for (size_t i = 0; i < sizeof(res) - 1; i += 1) { | ||
if (res[i] == '\0') { | ||
break; | ||
} | ||
if (res[i] < 0x20 || res[i] > 0x7e) { | ||
res[i] = '.'; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe check that the other side didn’t send a NUL byte in the string?
# keeps working on upgrade. (But still allow the user to override the preset, | ||
# if they really want. | ||
|
||
if [[ -e /run/systemd/system ]]; then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[[
is not part of POSIX and isn't needed here.
Offtopic for this PR... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple of notes and a few typo fixes. I didn't review the code thoroughly, but I skimmed part of it and read the documentation page.
# 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 |
There was a problem hiding this comment.
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 ...
.
- `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 qrexex policy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
usage is to have a trivial qrexec service in dom0 and use the qrexex policy | |
usage is to have a trivial qrexec service in dom0 and use the qrexec policy |
setting or more common via /etc/qubes/admin-authzd.conf in a template (setting | ||
in /usr/local has precendce). | ||
|
||
There config file have a very simpley syntax. The first line needs to contain |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There config file have a very simpley syntax. The first line needs to contain | |
There config file have a very simple syntax. The first line needs to contain |
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). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whonix would like for this to be configurable in Qube Manager, via a tri-select dropdown with a default setting. Is qvm-service
the best tool for this job, or would something like qvm-prefs
or qvm-features
work better here? (I would assume a property settable via qvm-prefs
and readable via qubesdb would be best.)
} else if (strcmp(argv[i], "quiet") == 0) { | ||
quiet = true; | ||
} else { | ||
pam_syslog(pamh, LOG_ERR, "unkown option: %s", argv[i]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pam_syslog(pamh, LOG_ERR, "unkown option: %s", argv[i]); | |
pam_syslog(pamh, LOG_ERR, "unknown option: %s", argv[i]); |
Related: |
See included README for details.
Issue: QubesOS/qubes-issues#2695