[Libvir] libvirt daemon UNIX socket auth with PolicyKit

Currently our authentication model for local connections is using the basic UNIX file permissions, possibly with a setuid helper (in Xen case only). It can be summarized as - If app using libvirt is running as root => full access - Else => read only access The latter is enforced by fact that in Xen case libvirt_proxy only has impl for a handful of read only APIs, or in non-Xen case that the UNIX domain socket for the daemon /var/run/libvirt/libvirt-sock is mode 0700, while /var/run/libvirt/libvirt-sock-ro is 0777 & the daemon enforces based on which socket the client connects to. This is good because it allows non-root to at least monitor guest state while requiring root authentication for actually changing state. This is bad because it requires any app which wants to change state to run as root. ie we are required to launch virt-manager as root to gain ability to manage local guests. Problem with this include: - running the entire PyGTK & GTK & X codebase as root is undesirable - no integration with the DBus desktop session (gnome-vfs integartion) - no integration with the GNOME keyring (for VNC server passwords) - redundant (&dangerous) if all you want to do is manage remote libvirt hosts In summary what I really need for virt-manager is - Always run as non-root - Authenticate for local guest management (ie read+write) UNIX domain sockets already provide a way for each end to identify the PID and UID of the other end. This enables the libvirt daemon to determine the identity of the application on the other end. With this information the daemon merely needs to check this identity against some access control policy rules. Where to get/define these rules though ? Enter PolicyKit. http://lists.freedesktop.org/archives/hal/2006-March/004770.html http://lists.freedesktop.org/archives/hal/2007-June/008815.html PolicyKit is a freedesktop.org project to replace all the distro specific 'do admin stuff as root' hacks like consolehelper[1], sudo, gksudo with a standard mechanism. Most importantly this mechanism does not require that the application in question run as root, or ever gain root privileges. In addition this is completely configurable by the administrator in a application independant manner. How would the daemon use PolicyKit for authenticating clients on UNIX domain sockets - libvirtd defines two actions it can check called 'libvirt-local-monitor (read only monitoring of state), and 'libvirt-local-manage' (full read-write management). - Both libvirt-sock and libvirt-sock-ro are mode 0777 (ie all users) - A connection arrives on the UNIX domain socket either libvirt-sock-ro or libvirt-sock - libvirtd use SO_PEERCRED to get the PID of the client - If the connection is on libvirt-sock-ro 'action' is 'libvirt-local-monitor' Else if connection is on libvirt-sock 'action' is 'libvirt-local-manage' - Libvirt asks policy kit if PID is allowed to perform the 'action' -> Yes, connection proceeds -> No, connection is dropped NB, having two separate UNIX domain sockets is strictly speaking redundant now - the daemon could simply check each 'action' in turn - but keeping the separate sockets simplifies the code, because it minimises the diffs when PolicyKit is not enabled by the 'configure' script. How is policy determined by PolicyKit ? - libvirt RPM provides /usr/share/PolicyKit/policy/libvirt.policy - This file defines the two actions it supports 'libvirt-local-monitor' and 'libvirt-local-manage'. - For each action we define a default policy for an active desktop session, and an inactive desktop session. The sample policy is to allow 'libvirt-local-monitor' for any local desktop session, but only allow 'libvirt-local-manage' for the active session with the additional requirement that the user authenticate with PolicyKit using the root password. This basically replicates the existing security semantics, without requiring that we run virt-manager itself as root. This accomplishes the core goal. There are other benefits to PolicyKit though - the administrator can override the application provided default Policy. For example, could add a rule that says "if the user fred is logged into the local X session, allow them to do 'libvirt-local-manage' without any further prompts for root password" Since PolicyKit is a general framework, that rule could be generalized beyond just virt-manager "if the user fred is logged into the local X session, allow them to do any defined action without any further prompts for root password" Or going the other way the administrator can lock things down requiring a root password even for read only monitoring of VMs. Or a halfway house of requiring the user's own password (sudo equiv, instead of su equiva). If course this doesn't just apply to virt-manager either. This transparently 'just works' for virsh too & any other apps using libvirt - eg if policy grants 'libvirt-local-manage' then you can do full management whether virsh is root or not. This all talks about enforcement of policy from the daemon side. There is one outstanding question - if the policy so requires, where/how do we ask the user to enter their credentials. PolicyKit provides two mechanisms for this: - A command line tool polkit-grant which uses pam to authenticate you as root, or using your own user password. If successful you will have been granted the permission to perform that action for the remainder of the session. - A DBus service for triggering display of some UI for authenticating. There is a GNOME impl of this service & likely a KDE impl too. eg with virsh start off without any priveleges $ virsh --connect qemu:///system net-list libvir: Remote error : Connection reset by peer error: failed to connect to the hypervisor error: no valid connection $ polkit-grant --action libvirtd-local-manage2 Authentication is required. Password: Keep this privilege for the session? [no/session]? $ ./src/virsh --connect qemu:///system net-list --all Name State Autostart ----------------------------------------- default active yes One option is to have the libvirt library spawn polkit-grant if not finding any $DISPLAY, and call the DBus service (for UI prompt) if finding any. The other option is to punt this code upto the application - eg virt-manager always call the DBus service, while virsh always call polkit-grant. Finally, PolicyKit is new in Fedora 8 if you wish to try this out. The configure script is setup so that it automagically detects presence of PolicyKit and only turns it on at compile time if its available. The fallback is to continue current behaviour of restricting based on the socket permissions. http://fedoraproject.org/wiki/Releases/FeaturePolicyKit Finally, finally, if we enable PolicyKit support, then we should disable the Xen specific libvirt_proxy setuid binary, since it would subvert the policy for libvirt-local-monitor. IMHO we should just kill the setuid binary altogether, since we already require that the libvirt daemon be running for the networking APIs & this provides read-only access already which works for non-Xen drivers too. Attaching the patch implementing the proof of concept $ diffstat ~/libvirt-policykit.patch configure.in | 60 ++++++++++++++++++-- qemud/Makefile.am | 12 +++- qemud/internal.h | 23 ++++++- qemud/libvirtd.policy | 41 ++++++++++++++ qemud/qemud.c | 145 ++++++++++++++++++++++++++++++++++++++++++++------ qemud/remote.c | 6 +- 6 files changed, 258 insertions(+), 29 deletions(-) Oh, finally, finally, finally. PolicyKit is pretty new so I'd understand if there's some resistance to including it in an official libvirt release at this point. I think the patch is small enough that we could carry it as add-on in the Fedora RPMs only to start with untill its proved to be maintainable long term - I really need this for Fedora 8 to make virt-manager work sanely - ie not need to run as root. Regards, Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Wed, Aug 08, 2007 at 05:22:33AM +0100, Daniel P. Berrange wrote:
In summary what I really need for virt-manager is
- Always run as non-root - Authenticate for local guest management (ie read+write)
Okay, problem understood.
- libvirtd defines two actions it can check called 'libvirt-local-monitor (read only monitoring of state), and 'libvirt-local-manage' (full read-write management).
Would the model allow things like being able to manage domains run by the user, but only monitor domains from someone else. I understand it may not always be possible to store that information, but if available a finer grained control based on context would make sense. Things like PolicyKit are being added among other things for better user switching mechanism, and the domains run by an user are in my opinion an extension of the user session, and having per user control would make sense IMHO.
- Both libvirt-sock and libvirt-sock-ro are mode 0777 (ie all users)
- A connection arrives on the UNIX domain socket either libvirt-sock-ro or libvirt-sock
- libvirtd use SO_PEERCRED to get the PID of the client
I'm just wondering a bit about the portability, I remember that for gamin we had to use at least 2 different authentication mechanism to assert reliably the identity of the connecting process. Might be worth looking again at D-Bus code for this.
- If the connection is on libvirt-sock-ro 'action' is 'libvirt-local-monitor' Else if connection is on libvirt-sock 'action' is 'libvirt-local-manage'
- Libvirt asks policy kit if PID is allowed to perform the 'action' -> Yes, connection proceeds -> No, connection is dropped
NB, having two separate UNIX domain sockets is strictly speaking redundant now - the daemon could simply check each 'action' in turn - but keeping the separate sockets simplifies the code, because it minimises the diffs when PolicyKit is not enabled by the 'configure' script.
Okay, we clearly need to go progressively as PolicyKit being new, we can't rely on it being there, it may take years to propagate to most of our users.
How is policy determined by PolicyKit ?
- libvirt RPM provides /usr/share/PolicyKit/policy/libvirt.policy
- This file defines the two actions it supports 'libvirt-local-monitor' and 'libvirt-local-manage'.
- For each action we define a default policy for an active desktop session, and an inactive desktop session. The sample policy is to allow 'libvirt-local-monitor' for any local desktop session, but only allow 'libvirt-local-manage' for the active session with the additional requirement that the user authenticate with PolicyKit using the root password.
This basically replicates the existing security semantics, without requiring that we run virt-manager itself as root. This accomplishes the core goal.
Okay, sounds like a good first step,
If course this doesn't just apply to virt-manager either. This transparently 'just works' for virsh too & any other apps using libvirt - eg if policy grants 'libvirt-local-manage' then you can do full management whether virsh is root or not.
yeah it's very important that those be dealt at the libvirt level, in a sense the elevation of priviledge needed for virt-manager and the proxy are just a bad way to get around a lack of finer grained mechanism.
Finally, PolicyKit is new in Fedora 8 if you wish to try this out. The [...] Oh, finally, finally, finally. PolicyKit is pretty new so I'd understand if there's some resistance to including it in an official libvirt release at this point. I think the patch is small enough that we could carry it as add-on in the Fedora RPMs only to start with untill its proved to be maintainable long term - I really need this for Fedora 8 to make virt-manager work sanely - ie not need to run as root.
To me it's not a problem as long as users without PolicyKit don't see a regression. The idea of removing the suid binary of the proxy is to me sufficient to push it as the default mechanism when DBus/PolicyKit is available. We just need to be careful, but that doesn't mean being conservative. In a nutshell, this sounds cool, I wonder how much finer grained we can go with PolicyKit and as long as we don't make it a requirement it's fine to add to the default code base IMHO.
+enum libvirtd_auth { + LIBVIRTD_AUTH_NONE, + LIBVIRTD_AUTH_TLS, +#ifdef HAVE_POLICY_KIT + LIBVIRTD_AUTH_POLICYKIT, +#endif +};
Hum, I would keep the enum present even if it's not detected, the value won't be used but it should still be defined IMHO.
void qemudLog(int priority, const char *fmt, ...) Index: qemud/libvirtd.policy =================================================================== RCS file: qemud/libvirtd.policy diff -N qemud/libvirtd.policy --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ qemud/libvirtd.policy 8 Aug 2007 04:10:45 -0000 @@ -0,0 +1,41 @@ +<!DOCTYPE policyconfig PUBLIC + "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" + "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd"> + +<!-- +Policy definitions for libvirt daemon + +Copyright (c) 2007 Daniel P. Berrange <berrange@redhat.com> + +libvirt is licensed to you under the GNU Lesser General Public License +version 2. See COPYING for details. + +NOTE: If you make changes to this file, make sure to validate the file +using the polkit-policy-file-validate(1) tool. Changes made to this +file are instantly applied. +--> + +<policyconfig> + <group id="libvirtd"> + <description>Virtualization</description> + + <policy id="libvirtd-local-monitor"> + <description>Monitor local virtualized systems</description> + <message>System policy prevents monitoring local virtualized systems</message> + <defaults> + <allow_inactive>yes</allow_inactive> + <allow_active>yes</allow_active> + </defaults> + </policy> + + <policy id="libvirtd-local-manage"> + <description>Manage local virtualized systems</description> + <message>System policy prevents managing local virtualized systems</message> + <defaults> + <allow_inactive>no</allow_inactive> + <allow_active>auth_admin</allow_active> + </defaults> + </policy> + + </group> +</policyconfig>
Cool, more XML, we should actually check that PolicyKit install the http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd DTD in the XML catalog. I need to install F-8 test1 somewhere ! Okay here is the actual new authentication code,
- if (!client->tls) { + switch (client->auth) { +#ifdef HAVE_POLICY_KIT + case LIBVIRTD_AUTH_POLICYKIT:
I would keep the case out, add an error if HAVE_POLICY_KIT is not #defined which should not happen but...
+ { + PolKitCaller *pkcaller = NULL; + PolKitAction *pkaction = NULL; + PolKitContext *pkcontext = NULL; + PolKitError *pkerr; + PolKitResult pkresult; + const char *action = sock->readonly ? + "libvirtd-local-monitor" : + "libvirtd-local-manage"; + pid_t callerPid; + DBusError err; +#ifdef SO_PEERCRED + struct ucred cr; + unsigned int cr_len = sizeof (cr); + + if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) < 0) { + qemudLog(QEMUD_ERR, "Failed to verify client credentials: %s", strerror(errno)); + close(fd); + free(client); + return -1; + } + callerPid = cr.pid; +#else + /* XXX Many more OS support UNIX socket credentials we could port to ....*/
We should match with DBus code. If PolicyKit is defined, then that mean DBus is ported there and DBus do various socket creds checks last time I looked like CMSGCRED (but that's not as easy to use as SO_PEERCRED clearly).
+#error "UNIX socket credentials not supported/implemeneted on this platform" +#endif + + dbus_error_init(&err); + if (!(pkcaller = polkit_caller_new_from_pid(server->sysbus, callerPid, &err))) { + qemudLog(QEMUD_ERR, "Failed to lookup policy kit caller: %s", err.message); + dbus_error_free(&err); + close(fd); + free(client); + return -1; + } + + if (!(pkaction = polkit_action_new())) { + qemudLog(QEMUD_ERR, "Failed to create polkit action %s\n", strerror(errno)); + polkit_caller_unref(pkcaller); + close(fd); + free(client); + return -1; + } + polkit_action_set_action_id(pkaction, action); + + if (!(pkcontext = polkit_context_new()) || + !polkit_context_init(pkcontext, &pkerr)) { + qemudLog(QEMUD_ERR, "Failed to create polkit context %s\n", + pkerr ? polkit_error_get_error_message(pkerr) : strerror(errno)); + if (pkerr) + polkit_error_free(pkerr); + polkit_caller_unref(pkcaller); + polkit_action_unref(pkaction); + dbus_error_free(&err); + close(fd); + free(client); + return -1; + } + + pkresult = polkit_context_can_caller_do_action(pkcontext, pkaction, pkcaller); + polkit_context_unref(pkcontext); + polkit_caller_unref(pkcaller); + polkit_action_unref(pkaction); + if (pkresult != POLKIT_RESULT_YES) { + qemudLog(QEMUD_ERR, "Policy kit denied action %s from pid %d, result: %s\n", + action, callerPid, polkit_result_to_string_representation(pkresult)); + close(fd); + free(client); + return -1; + } + } + /* Allowed by policy kit, so fallthrough to generic setup... */ +#endif
The availability of the uid can be derived from the pid and even in the absence of PolicyKit I think we could embbed the same kind of policies as we do now and enforce them without relying on the filesystem attributes. One improvement I would like to see is to fallback to abstract sockets, not mapped on the filesystem, if we don't need the filesystem attributes to implement the policy, I find cleaner and safer to not map in the fs anymore (since libvirtd and libvirt are to be upgraded together I think the disruption would be rather minimal). Most of the other changes seems related to the tls->auth field change and looks fine.
dnl Allow to build without Xen, QEMU/KVM, test or remote driver AC_ARG_WITH(xen, -[ --with-xen add XEN support (on)]) +[ --with-xen add XEN support (on)],[],[with_xen=yes]) AC_ARG_WITH(qemu, -[ --with-qemu add QEMU/KVM support (on)]) +[ --with-qemu add QEMU/KVM support (on)],[],[with_qemu=yes]) AC_ARG_WITH(openvz, -[ --with-openvz add OpenVZ support (off)]) +[ --with-openvz add OpenVZ support (off)],[],[with_openvz=no]) AC_ARG_WITH(test, -[ --with-test add test driver support (on)]) +[ --with-test add test driver support (on)],[],[with_test=yes]) AC_ARG_WITH(remote, -[ --with-remote add remote driver support (on)]) +[ --with-remote add remote driver support (on)],[],[with_remote=yes])
dnl dnl specific tests to setup DV devel environments with debug etc ... @@ -95,7 +97,7 @@ AC_SUBST(STATIC_BINARIES) dnl --enable-debug=(yes|no) AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug=no/yes], - [enable debugging output])) + [enable debugging output]),[],[enable_debug=no]) if test x"$enable_debug" = x"yes"; then AC_DEFINE(ENABLE_DEBUG, [], [whether debugging is enabled]) fi
Hum, that's cleanups, commit separately this doesn't need to wait :-) [...]
+ +echo +echo "Drivers:" +echo " Xen: $with_xen" +echo " QEMU: $with_qemu" +echo " Test: $with_test" +echo " OpenVZ: $with_openvz" +echo " Remote: $with_remote" +echo +echo "General features:" +echo " Debug: $enable_debug" +if test "$with_remote" = "yes"; then + echo + echo "Remote driver features:"
and that too :-)
+ echo " PolicyKit: $with_policykit" +fi
In general this looks fairly cool to me. This certainly need also: - documentation - update of the spec file before being commited, but it seems this should not impact users not on F8 testing environment, and if it was the case it's best to detect it earlier than later. I still have some open questions about finer grained policied based on UID and owner of the domain when this could be asserted by libvirt. And if we can do a cleanup of the socket and unmap them from the filesystem in the process I would find this a good added property. Sounds excellent to me ! Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Wed, Aug 08, 2007 at 05:35:09AM -0400, Daniel Veillard wrote:
On Wed, Aug 08, 2007 at 05:22:33AM +0100, Daniel P. Berrange wrote:
In summary what I really need for virt-manager is
- Always run as non-root - Authenticate for local guest management (ie read+write)
Okay, problem understood.
- libvirtd defines two actions it can check called 'libvirt-local-monitor (read only monitoring of state), and 'libvirt-local-manage' (full read-write management).
Would the model allow things like being able to manage domains run by the user, but only monitor domains from someone else. I understand it may not always be possible to store that information, but if available a finer grained control based on context would make sense. Things like PolicyKit are being added among other things for better user switching mechanism, and the domains run by an user are in my opinion an extension of the user session, and having per user control would make sense IMHO.
Its not clear whether PolicyKit is going to be suitable for fine-grained per-object permissioning at this time. It is primarily concentrating on the coarse high level access to system resources at this time. We'll just have to watch what direction it evolves in. Personally I still think SELinux is probably the more likely long term bet for fine grained MAC on this at this time. Perhaps PolicyKit will end up being backed by/integrating with SELinux in the future.
- Both libvirt-sock and libvirt-sock-ro are mode 0777 (ie all users)
- A connection arrives on the UNIX domain socket either libvirt-sock-ro or libvirt-sock
- libvirtd use SO_PEERCRED to get the PID of the client
I'm just wondering a bit about the portability, I remember that for gamin we had to use at least 2 different authentication mechanism to assert reliably the identity of the connecting process. Might be worth looking again at D-Bus code for this.
Portability sucks. There's at least 4/5 os specific impls - as per my other reply to John I'd like to see this code isolated inside PolicyKit APIs themselves really.
+enum libvirtd_auth { + LIBVIRTD_AUTH_NONE, + LIBVIRTD_AUTH_TLS, +#ifdef HAVE_POLICY_KIT + LIBVIRTD_AUTH_POLICYKIT, +#endif +};
Hum, I would keep the enum present even if it's not detected, the value won't be used but it should still be defined IMHO.
I dunno - I preferred to keep it removed if not available so we don't accidentally make use of it elsewhere in the code.
void qemudLog(int priority, const char *fmt, ...) Index: qemud/libvirtd.policy =================================================================== RCS file: qemud/libvirtd.policy diff -N qemud/libvirtd.policy --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ qemud/libvirtd.policy 8 Aug 2007 04:10:45 -0000 @@ -0,0 +1,41 @@ +<!DOCTYPE policyconfig PUBLIC + "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" + "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd"> + +<!-- +Policy definitions for libvirt daemon + +Copyright (c) 2007 Daniel P. Berrange <berrange@redhat.com> + +libvirt is licensed to you under the GNU Lesser General Public License +version 2. See COPYING for details. + +NOTE: If you make changes to this file, make sure to validate the file +using the polkit-policy-file-validate(1) tool. Changes made to this +file are instantly applied. +--> + +<policyconfig> + <group id="libvirtd"> + <description>Virtualization</description> + + <policy id="libvirtd-local-monitor"> + <description>Monitor local virtualized systems</description> + <message>System policy prevents monitoring local virtualized systems</message> + <defaults> + <allow_inactive>yes</allow_inactive> + <allow_active>yes</allow_active> + </defaults> + </policy> + + <policy id="libvirtd-local-manage"> + <description>Manage local virtualized systems</description> + <message>System policy prevents managing local virtualized systems</message> + <defaults> + <allow_inactive>no</allow_inactive> + <allow_active>auth_admin</allow_active> + </defaults> + </policy> + + </group> +</policyconfig>
Cool, more XML, we should actually check that PolicyKit install the http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd DTD in the XML catalog. I need to install F-8 test1 somewhere !
Install it in a Xen guest ! The RPM puts the DTD in /usr/share/PolicyKit/config.dtd
- if (!client->tls) { + switch (client->auth) { +#ifdef HAVE_POLICY_KIT + case LIBVIRTD_AUTH_POLICYKIT:
I would keep the case out, add an error if HAVE_POLICY_KIT is not #defined which should not happen but...
Guess that would be safe enough.
+ { + PolKitCaller *pkcaller = NULL; + PolKitAction *pkaction = NULL; + PolKitContext *pkcontext = NULL; + PolKitError *pkerr; + PolKitResult pkresult; + const char *action = sock->readonly ? + "libvirtd-local-monitor" : + "libvirtd-local-manage"; + pid_t callerPid; + DBusError err; +#ifdef SO_PEERCRED + struct ucred cr; + unsigned int cr_len = sizeof (cr); + + if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) < 0) { + qemudLog(QEMUD_ERR, "Failed to verify client credentials: %s", strerror(errno)); + close(fd); + free(client); + return -1; + } + callerPid = cr.pid; +#else + /* XXX Many more OS support UNIX socket credentials we could port to ....*/
We should match with DBus code. If PolicyKit is defined, then that mean DBus is ported there and DBus do various socket creds checks last time I looked like CMSGCRED (but that's not as easy to use as SO_PEERCRED clearly).
Yep, I'd like to not have this code in libvirt at all really.
+#error "UNIX socket credentials not supported/implemeneted on this platform" +#endif + + dbus_error_init(&err); + if (!(pkcaller = polkit_caller_new_from_pid(server->sysbus, callerPid, &err))) { + qemudLog(QEMUD_ERR, "Failed to lookup policy kit caller: %s", err.message); + dbus_error_free(&err); + close(fd); + free(client); + return -1; + } + + if (!(pkaction = polkit_action_new())) { + qemudLog(QEMUD_ERR, "Failed to create polkit action %s\n", strerror(errno)); + polkit_caller_unref(pkcaller); + close(fd); + free(client); + return -1; + } + polkit_action_set_action_id(pkaction, action); + + if (!(pkcontext = polkit_context_new()) || + !polkit_context_init(pkcontext, &pkerr)) { + qemudLog(QEMUD_ERR, "Failed to create polkit context %s\n", + pkerr ? polkit_error_get_error_message(pkerr) : strerror(errno)); + if (pkerr) + polkit_error_free(pkerr); + polkit_caller_unref(pkcaller); + polkit_action_unref(pkaction); + dbus_error_free(&err); + close(fd); + free(client); + return -1; + } + + pkresult = polkit_context_can_caller_do_action(pkcontext, pkaction, pkcaller); + polkit_context_unref(pkcontext); + polkit_caller_unref(pkcaller); + polkit_action_unref(pkaction); + if (pkresult != POLKIT_RESULT_YES) { + qemudLog(QEMUD_ERR, "Policy kit denied action %s from pid %d, result: %s\n", + action, callerPid, polkit_result_to_string_representation(pkresult)); + close(fd); + free(client); + return -1; + } + } + /* Allowed by policy kit, so fallthrough to generic setup... */ +#endif
The availability of the uid can be derived from the pid and even in the absence of PolicyKit I think we could embbed the same kind of policies as we do now and enforce them without relying on the filesystem attributes.
Yes, in fact the 'struct ucred ' does have both a UID and PID field - I'm only using the UID in this code, but if we wanted to use this as a check even without PolicyKIt around we could do that.
dnl Allow to build without Xen, QEMU/KVM, test or remote driver AC_ARG_WITH(xen, -[ --with-xen add XEN support (on)]) +[ --with-xen add XEN support (on)],[],[with_xen=yes]) AC_ARG_WITH(qemu, -[ --with-qemu add QEMU/KVM support (on)]) +[ --with-qemu add QEMU/KVM support (on)],[],[with_qemu=yes]) AC_ARG_WITH(openvz, -[ --with-openvz add OpenVZ support (off)]) +[ --with-openvz add OpenVZ support (off)],[],[with_openvz=no]) AC_ARG_WITH(test, -[ --with-test add test driver support (on)]) +[ --with-test add test driver support (on)],[],[with_test=yes]) AC_ARG_WITH(remote, -[ --with-remote add remote driver support (on)]) +[ --with-remote add remote driver support (on)],[],[with_remote=yes])
dnl dnl specific tests to setup DV devel environments with debug etc ... @@ -95,7 +97,7 @@ AC_SUBST(STATIC_BINARIES) dnl --enable-debug=(yes|no) AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug=no/yes], - [enable debugging output])) + [enable debugging output]),[],[enable_debug=no]) if test x"$enable_debug" = x"yes"; then AC_DEFINE(ENABLE_DEBUG, [], [whether debugging is enabled]) fi
[...]
+ +echo +echo "Drivers:" +echo " Xen: $with_xen" +echo " QEMU: $with_qemu" +echo " Test: $with_test" +echo " OpenVZ: $with_openvz" +echo " Remote: $with_remote" +echo +echo "General features:" +echo " Debug: $enable_debug" +if test "$with_remote" = "yes"; then + echo + echo "Remote driver features:"
and that too :-)
Yes, I meant to separate this out for inclusion separately. In fact I want to make it so that the configure script checks whether Xen, QEMU, OpenVZ and GNUTLS are available and automatically enables the optional set of drivers based on this. The --with-XXX flags would then merely be to override the default - eg force a build without Xen even if its available. Regards, Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Wed, Aug 08, 2007 at 05:22:33AM +0100, Daniel P. Berrange wrote:
UNIX domain sockets already provide a way for each end to identify the PID and UID of the other end. This enables the libvirt daemon to determine the identity of the application on the other end. With this information the daemon merely needs to check this identity against some access control policy rules. Where to get/define these rules though ?
Enter PolicyKit.
http://lists.freedesktop.org/archives/hal/2006-March/004770.html http://lists.freedesktop.org/archives/hal/2007-June/008815.html
This is entirely new to me, but I suspect this doesn't have any Solaris integration support (yet? I'm asking around about this). Nonetheless the basic concept (allow all access, authenticate the peer's credentials against some kind of database) translates well on Solaris.
- libvirtd defines two actions it can check called 'libvirt-local-monitor (read only monitoring of state), and 'libvirt-local-manage' (full read-write management).
Good... but I think we need to consider true delegation as well, that is, allowing a certain credential to control only one named object. At least we need to make sure that's possible in the future without breaking anything here.
- libvirtd use SO_PEERCRED to get the PID of the client
Solaris doesn't have this, but the more powerful getpeerucred(): http://docs.sun.com/app/docs/doc/819-2243/6n4i09924?a=view http://docs.sun.com/app/docs/doc/819-2243/6n4i099nf?a=view Typically, we would then compare either the process's privilege set or the user id. Privileges will likely have to come later but the user ID will translate directly into RBAC: http://www.samag.com/documents/s=7667/sam0213c/0213c.htm Now, it may be the case that we can fit into the Policy Kit framework and that work is ongoing, which would make things simple from libvirt point of view (only need to replace SO_PEERCRED by getpeerucred for now). I will endeavour to find out for you... regards john

On Wed, Aug 08, 2007 at 01:55:15PM +0100, John Levon wrote:
On Wed, Aug 08, 2007 at 05:22:33AM +0100, Daniel P. Berrange wrote:
UNIX domain sockets already provide a way for each end to identify the PID and UID of the other end. This enables the libvirt daemon to determine the identity of the application on the other end. With this information the daemon merely needs to check this identity against some access control policy rules. Where to get/define these rules though ?
Enter PolicyKit.
http://lists.freedesktop.org/archives/hal/2006-March/004770.html http://lists.freedesktop.org/archives/hal/2007-June/008815.html
This is entirely new to me, but I suspect this doesn't have any Solaris integration support (yet? I'm asking around about this).
It has only recently had a 'useful' release. It is used by the current HAL and GNOME Volume Manager releases, so I imagine it'll be getting ported to Solaris in the near future if its not already under way.
- libvirtd defines two actions it can check called 'libvirt-local-monitor (read only monitoring of state), and 'libvirt-local-manage' (full read-write management).
Good... but I think we need to consider true delegation as well, that is, allowing a certain credential to control only one named object. At least we need to make sure that's possible in the future without breaking anything here.
Yep fine grained mandatory access control per VM/network is definitely something I want implemented. It is not clear whether this is something that would be done in PolicyKit in the future, or whether we'd end up using SELinux for it. The latter pretty compelling on Linux because its a unified MAC system usable across kernel & userspace (DBus, SE-Postgres, Oddjob to list some examples). Obvious downside is that there would need to be a different impl on non-Linux. Fortunatel all these details ought to be more or less hidden inside libvirt and not be exposed in the public API aside from a opaque 'token' for labelling the security context of an object.
- libvirtd use SO_PEERCRED to get the PID of the client
Solaris doesn't have this, but the more powerful getpeerucred():
http://docs.sun.com/app/docs/doc/819-2243/6n4i09924?a=view http://docs.sun.com/app/docs/doc/819-2243/6n4i099nf?a=view
There's at least 5 different impls of this general context across the various UNIX OS :-( I've just suggested to David Z that PolicyKit provide a 'polkit_caller_new_from_socket' API, so all the OS specific code for getting a UID from a socket can be isolated in polkicykit libraries rather than making each individual app re-implement the portability.
Typically, we would then compare either the process's privilege set or the user id. Privileges will likely have to come later but the user ID will translate directly into RBAC:
http://www.samag.com/documents/s=7667/sam0213c/0213c.htm
Now, it may be the case that we can fit into the Policy Kit framework and that work is ongoing, which would make things simple from libvirt point of view (only need to replace SO_PEERCRED by getpeerucred for now). I will endeavour to find out for you...
Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Wed, Aug 08, 2007 at 02:48:02PM +0100, Daniel P. Berrange wrote:
- libvirtd use SO_PEERCRED to get the PID of the client
Solaris doesn't have this, but the more powerful getpeerucred():
http://docs.sun.com/app/docs/doc/819-2243/6n4i09924?a=view http://docs.sun.com/app/docs/doc/819-2243/6n4i099nf?a=view
There's at least 5 different impls of this general context across the various UNIX OS :-( I've just suggested to David Z that PolicyKit provide a 'polkit_caller_new_from_socket' API, so all the OS specific code for getting a UID from a socket can be isolated in polkicykit libraries rather than making each individual app re-implement the portability.
I think the ability to get the UID of the caller would be useful even if polkicykit is not available though, that would allow to get rid of fs mapped sockets and the reliance on fs attributes. Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

Daniel P. Berrange wrote:
Currently our authentication model for local connections is using the basic UNIX file permissions, possibly with a setuid helper (in Xen case only). It can be summarized as
- If app using libvirt is running as root => full access - Else => read only access
The latter is enforced by fact that in Xen case libvirt_proxy only has impl for a handful of read only APIs, or in non-Xen case that the UNIX domain socket for the daemon /var/run/libvirt/libvirt-sock is mode 0700, while /var/run/libvirt/libvirt-sock-ro is 0777 & the daemon enforces based on which socket the client connects to.
This is good because it allows non-root to at least monitor guest state while requiring root authentication for actually changing state.
This is bad because it requires any app which wants to change state to run as root. ie we are required to launch virt-manager as root to gain ability to manage local guests. Problem with this include:
- running the entire PyGTK & GTK & X codebase as root is undesirable - no integration with the DBus desktop session (gnome-vfs integartion) - no integration with the GNOME keyring (for VNC server passwords) - redundant (&dangerous) if all you want to do is manage remote libvirt hosts
In summary what I really need for virt-manager is
- Always run as non-root - Authenticate for local guest management (ie read+write)
UNIX domain sockets already provide a way for each end to identify the PID and UID of the other end. This enables the libvirt daemon to determine the identity of the application on the other end. With this information the daemon merely needs to check this identity against some access control policy rules. Where to get/define these rules though ?
I'm unclear as to what problem this is solving that couldn't be solved using Unix users and groups. Add the users who need full access to a Unix group and change the permissions on the r/w socket: srw-rw---- 1 root virtstaff 0 2007-06-29 15:50 /var/run/libvirt/libvirt-sock I guess that'd be too simple for people who think XML configuration files are a good idea. Well, I checked the patch and it is not invasive, nor does it depend on PolicyKit / freedesktop.org crack being available in the future, so I guess we can carry it. Rich. -- Emerging Technologies, Red Hat - http://et.redhat.com/~rjones/ Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SL4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 03798903

On Wed, Aug 08, 2007 at 03:42:30PM +0100, Richard W.M. Jones wrote:
Daniel P. Berrange wrote:
Currently our authentication model for local connections is using the basic UNIX file permissions, possibly with a setuid helper (in Xen case only). It can be summarized as
- If app using libvirt is running as root => full access - Else => read only access
The latter is enforced by fact that in Xen case libvirt_proxy only has impl for a handful of read only APIs, or in non-Xen case that the UNIX domain socket for the daemon /var/run/libvirt/libvirt-sock is mode 0700, while /var/run/libvirt/libvirt-sock-ro is 0777 & the daemon enforces based on which socket the client connects to.
This is good because it allows non-root to at least monitor guest state while requiring root authentication for actually changing state.
This is bad because it requires any app which wants to change state to run as root. ie we are required to launch virt-manager as root to gain ability to manage local guests. Problem with this include:
- running the entire PyGTK & GTK & X codebase as root is undesirable - no integration with the DBus desktop session (gnome-vfs integartion) - no integration with the GNOME keyring (for VNC server passwords) - redundant (&dangerous) if all you want to do is manage remote libvirt hosts
In summary what I really need for virt-manager is
- Always run as non-root - Authenticate for local guest management (ie read+write)
UNIX domain sockets already provide a way for each end to identify the PID and UID of the other end. This enables the libvirt daemon to determine the identity of the application on the other end. With this information the daemon merely needs to check this identity against some access control policy rules. Where to get/define these rules though ?
I'm unclear as to what problem this is solving that couldn't be solved using Unix users and groups. Add the users who need full access to a Unix group and change the permissions on the r/w socket:
srw-rw---- 1 root virtstaff 0 2007-06-29 15:50 /var/run/libvirt/libvirt-sock
That either gives a user full access without requiring any password, or requires that the app run as root. That's just a mild tweaking of the status quo. It doesn't allow us to authenticate a non-root user to allow them access without the app itself being run as root. Regards, Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

Daniel P. Berrange wrote:
On Wed, Aug 08, 2007 at 03:42:30PM +0100, Richard W.M. Jones wrote:
Daniel P. Berrange wrote: srw-rw---- 1 root virtstaff 0 2007-06-29 15:50 /var/run/libvirt/libvirt-sock
That either gives a user full access without requiring any password, or requires that the app run as root. That's just a mild tweaking of the status quo. It doesn't allow us to authenticate a non-root user to allow them access without the app itself being run as root.
I wouldn't call it a "mild tweaking of the status quo". It lets an administrator designate staff who are permitted to manage virtualization (ie. by adding them to the virtstaff group), and then those staff can run management programs as themselves (non-root). If typing in a password is important because it proves that at the moment that the program was started, then the staff member was sitting in front of the computer (but not, like, later on or anything), then perhaps the administrators of these super secure systems should ensure their staff use screensavers. Anyhow isn't this something which SELinux was supposed to solve? Rich. -- Emerging Technologies, Red Hat - http://et.redhat.com/~rjones/ Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SL4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 03798903

On Wed, Aug 08, 2007 at 04:02:25PM +0100, Richard W.M. Jones wrote:
Daniel P. Berrange wrote:
On Wed, Aug 08, 2007 at 03:42:30PM +0100, Richard W.M. Jones wrote:
Daniel P. Berrange wrote: srw-rw---- 1 root virtstaff 0 2007-06-29 15:50 /var/run/libvirt/libvirt-sock
That either gives a user full access without requiring any password, or requires that the app run as root. That's just a mild tweaking of the status quo. It doesn't allow us to authenticate a non-root user to allow them access without the app itself being run as root.
I wouldn't call it a "mild tweaking of the status quo". It lets an administrator designate staff who are permitted to manage virtualization (ie. by adding them to the virtstaff group), and then those staff can run management programs as themselves (non-root). If typing in a password is important because it proves that at the moment that the program was started, then the staff member was sitting in front of the computer (but not, like, later on or anything), then perhaps the administrators of these super secure systems should ensure their staff use screensavers.
Anyhow isn't this something which SELinux was supposed to solve?
Yes - but with the caveat that it only solves it if running in 'strict' mode. In 'targetted' mode all user accounts are unconfined_t so can do pretty much anything they like. So we can't usefully leverage SELinux for this in most common deployements. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|
participants (4)
-
Daniel P. Berrange
-
Daniel Veillard
-
John Levon
-
Richard W.M. Jones