[libvirt] [PATCH 5/7] Add two new security label types

Eric Blake eblake at redhat.com
Tue Jan 24 00:04:41 UTC 2012


On 01/11/2012 09:33 AM, Daniel P. Berrange wrote:
> From: "Daniel P. Berrange" <berrange at redhat.com>
> 
> Curently security labels can be of type 'dynamic' or 'static'.
> If no security label is given, then 'dynamic' is assumed. The
> current code takes advantage of this default, and avoids even
> saving <seclabel> elements with type='dynamic' to disk. This
> means if you temporarily change security driver, the guests
> can all still start.
> 
> With the introduce of sVirt to LXC though, there needs to be

s/introduce/introduction/

> a new default of 'none' to allow unconfined LXC containers.
> 
> This patch introduces two new security label types
> 
>  - default:  the host configuration decides whether to run the
>              guest with type 'none' or 'dynamic' at guest start
>  - none:     the guest will run unconfined by security policy
> 
> The 'none' label type will obviously be undesirable for some
> deployments, so a new qemu.conf option allows a host admin to
> mandate confined guests. It is also possible to turn off default
> confinement
> 
>   security_default_confined = 1|0  (default == 1)
>   security_require_confined = 1|0  (default == 0)
> 
> * src/conf/domain_conf.c, src/conf/domain_conf.h: Add new
>   seclabel types
> * src/security/security_manager.c, src/security/security_manager.h:
>   Set default sec label types
> * src/security/security_selinux.c: Handle 'none' seclabel type
> * src/qemu/qemu.conf, src/qemu/qemu_conf.c, src/qemu/qemu_conf.h,
>   src/qemu/libvirtd_qemu.aug: New security config options
> * src/qemu/qemu_driver.c: Tell security driver about default
>   config

Fails 'make syntax-check':

po_check
--- ./po/POTFILES.in
+++ ./po/POTFILES.in
@@ -89,6 +89,7 @@
 src/security/security_apparmor.c
 src/security/security_dac.c
 src/security/security_driver.c
+src/security/security_manager.c
 src/security/security_selinux.c
 src/security/virt-aa-helper.c
 src/storage/parthelper.c
maint.mk: you have changed the set of files with translatable diagnostics;
 apply the above patch

Fails 'make check':

seclabeltest.c: In function 'main':
seclabeltest.c:16:5: error: too few arguments to function
'virSecurityManagerNew'
../src/security/security_manager.h:34:23: note: declared here

I had to squash this in to get tests to pass:

diff --git i/po/POTFILES.in w/po/POTFILES.in
index ca1db70..9b699bd 100644
--- i/po/POTFILES.in
+++ w/po/POTFILES.in
@@ -89,6 +89,7 @@ src/secret/secret_driver.c
 src/security/security_apparmor.c
 src/security/security_dac.c
 src/security/security_driver.c
+src/security/security_manager.c
 src/security/security_selinux.c
 src/security/virt-aa-helper.c
 src/storage/parthelper.c
diff --git i/src/conf/domain_conf.c w/src/conf/domain_conf.c
index 07080ce..91b95e2 100644
--- i/src/conf/domain_conf.c
+++ w/src/conf/domain_conf.c
@@ -9852,7 +9852,7 @@ virSecurityLabelDefFormat(virBufferPtr buf,
virSecurityLabelDefPtr def)
     virBufferAsprintf(buf, "<seclabel type='%s'",
                       sectype);
     if (def->model)
-        virBufferEscapeString(buf, " model='%s' ", def->model);
+        virBufferEscapeString(buf, " model='%s'", def->model);

     virBufferAsprintf(buf, " relabel='%s'",
                       def->norelabel ? "no" : "yes");
diff --git i/tests/seclabeltest.c w/tests/seclabeltest.c
index 5d87789..fca76b9 100644
--- i/tests/seclabeltest.c
+++ w/tests/seclabeltest.c
@@ -13,7 +13,7 @@ main (int argc ATTRIBUTE_UNUSED, char **argv
ATTRIBUTE_UNUSED)
     virSecurityManagerPtr mgr;
     const char *doi, *model;

-    mgr = virSecurityManagerNew(NULL, false);
+    mgr = virSecurityManagerNew(NULL, false, true, false);
     if (mgr == NULL) {
         fprintf (stderr, "Failed to start security driver");
         exit (-1);


> ---
>  src/conf/domain_conf.c          |   81 +++++++++++++++++++++++----------------
>  src/conf/domain_conf.h          |    2 +
>  src/qemu/libvirtd_qemu.aug      |    2 +
>  src/qemu/qemu.conf              |    8 ++++
>  src/qemu/qemu_conf.c            |   11 +++++
>  src/qemu/qemu_conf.h            |    2 +
>  src/qemu/qemu_driver.c          |    7 +++-
>  src/security/security_manager.c |   51 ++++++++++++++++++++++--
>  src/security/security_manager.h |    8 +++-
>  src/security/security_selinux.c |   28 ++++++++++----
>  10 files changed, 152 insertions(+), 48 deletions(-)

You also need to list the new XML in docs/schemas/domaincommon.rng,
document it in docs/formatdomain.html.in, and add tests for the new XML
being parsed correctly.  I don't feel comfortable acking with that much
missing, so I'll need a v2; but I can proceed to give code review.

> @@ -2547,13 +2549,15 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def,
>                               "%s", _("missing security type"));
>          goto error;
>      }
> +
>      def->type = virDomainSeclabelTypeFromString(p);
>      VIR_FREE(p);
> -    if (def->type < 0) {
> +    if (def->type <= 0) {
>          virDomainReportError(VIR_ERR_XML_ERROR,
>                               "%s", _("invalid security type"));

This explicitly rejects type='default', was that intentional (where you
_want_ the user to omit the attribute to get the default)?  It matches
what we do elsewhere, but that does mean that you have to be careful in
documenting it this way (type='none|dynamic|static' or omitted type to
form the four possibilities).

> @@ -2574,13 +2578,23 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def,
>                                   "%s", _("dynamic label type must use resource relabeling"));
>              goto error;
>          }
> +        if (def->type == VIR_DOMAIN_SECLABEL_NONE &&
> +            !def->norelabel) {
> +            virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> +                                 "%s", _("dynamic label type must use not resource relabeling"));

Bad grammar in the error message, but even with that fixed, it still
doesn't make sense compared to the if conditional.  I think you meant
something more like:

"resource relabeling not compatible with 'none' label type"

> +            goto error;
> +        }
>      } else {
> -        if (def->type == VIR_DOMAIN_SECLABEL_STATIC)
> +        if (def->type == VIR_DOMAIN_SECLABEL_STATIC ||
> +            def->type == VIR_DOMAIN_SECLABEL_NONE)
>              def->norelabel = true;
>          else
>              def->norelabel = false;
>      }
>  
> +    if (def->type == VIR_DOMAIN_SECLABEL_NONE)
> +        return 0;

So we explicitly ignore any <label> sub-elements with type of none; I
can live with that.

> @@ -9810,37 +9824,41 @@ virDomainLifecycleDefFormat(virBufferPtr buf,
>  }
>  
>  
> -static int
> -virSecurityLabelDefFormat(virBufferPtr buf, virSecurityLabelDefPtr def,
> -                          unsigned int flags)
> +static void
> +virSecurityLabelDefFormat(virBufferPtr buf, virSecurityLabelDefPtr def)

I like that you are dropping dependency on the flags argument.

I will note that this means that 'virsh save-image-edit' might end up
changing a save image file created pre-patch into a new form post-patch
 even when there are no changes made by the user (but that's not the
first time we've done that).  But as long as we aren't changing the
output from the input _from the same version of libvirt_, I'm okay if
upgrading the libvirt version causes a rewrite when editing an older
save image.

>  {
>      const char *sectype = virDomainSeclabelTypeToString(def->type);
> -    int ret = -1;
>  
>      if (!sectype)
> -        goto cleanup;
> +        return;
>  
> -    if (def->type == VIR_DOMAIN_SECLABEL_DYNAMIC &&
> -        !def->baselabel &&
> -        (flags & VIR_DOMAIN_XML_INACTIVE)) {
> -        /* This is the default for inactive xml, so nothing to output.  */

The old way omitted output if you use the defaults, but still generated
output if you do this as short-hand input:

<seclabel>
  <baselabel>...</baselabel>
</seclabel>

for the intended:

<seclabel type='dynamic' model='selinux' relabel='yes'>
  <baselabel>...</baselabel>
</seclabel>

> -    } else {
> -        virBufferAsprintf(buf, "<seclabel type='%s' model='%s' relabel='%s'>\n",
> -                          sectype, def->model,
> -                          def->norelabel ? "no" : "yes");
> -        virBufferEscapeString(buf, "  <label>%s</label>\n",
> -                              def->label);
> -        if (!def->norelabel)
> -            virBufferEscapeString(buf, "  <imagelabel>%s</imagelabel>\n",
> -                                  def->imagelabel);
> -        if (def->type == VIR_DOMAIN_SECLABEL_DYNAMIC)
> -            virBufferEscapeString(buf, "  <baselabel>%s</baselabel>\n",
> -                                  def->baselabel);
> -        virBufferAddLit(buf, "</seclabel>\n");
> +    if (def->type == VIR_DOMAIN_SECLABEL_DEFAULT)
> +        return;

...whereas your new code fails to look for the presence of <baselabel>,
and would silently lose the user's input.  I argue that the bug is in
your parse routine, and not your output routine.  That is, if the user
omitted type, but then provides a <baselabel>, you need to explicitly
set type to dynamic rather than leaving it at default, so that the
output routine can blindly omit output when type is default precisely
because <baselabel> no longer maps to default.

> +
> +    virBufferAsprintf(buf, "<seclabel type='%s'",
> +                      sectype);
> +    if (def->model)
> +        virBufferEscapeString(buf, " model='%s' ", def->model);

Lose the trailing space.  Also, you can exploit the fact that
virBufferEscapeString does a NULL check for you, and lose the 'if
(def->model)' with no change in output.

> +
> +    virBufferAsprintf(buf, " relabel='%s'",
> +                      def->norelabel ? "no" : "yes");
> +
> +    if (def->type == VIR_DOMAIN_SECLABEL_NONE) {
> +        virBufferAddLit(buf, "/>\n");
> +        return;
>      }
> -    ret = 0;
> -cleanup:
> -    return ret;
> +
> +    virBufferAddLit(buf, ">\n");
> +
> +    virBufferEscapeString(buf, "  <label>%s</label>\n",
> +                          def->label);
> +    if (!def->norelabel)
> +        virBufferEscapeString(buf, "  <imagelabel>%s</imagelabel>\n",
> +                              def->imagelabel);
> +    if (def->type == VIR_DOMAIN_SECLABEL_DYNAMIC)
> +        virBufferEscapeString(buf, "  <baselabel>%s</baselabel>\n",
> +                              def->baselabel);
> +    virBufferAddLit(buf, "</seclabel>\n");

This can result in:

<seclabel type='dynamic' model='selinux' relabel='yes'>
</seclabel>

for an inactive domain.  Do we want to go to any effort to shorten this to:

<seclabel type='dynamic' model='selinux' relabel='yes'/>

> +++ b/src/qemu/qemu.conf
> @@ -138,6 +138,14 @@
>  #
>  # security_driver = "selinux"
>  
> +# If set to non-zero, then the default security labelling

Do we want the US spelling of labeling here?

> +# will make guests confined. If set to zero, then guests
> +# will be unconfined by default. Defaults to 1
> +# security_default_confined = 1
> +
> +# If set to non-zero, then attempts to create unconfined
> +# guests will be blocked. Defaults to zero.
> +# security_require_confined = 1

Consistency argues that we should use the digit rather than the name of
the number, and end with a full stop, as in:

Defaults to 1.
Defaults to 0.

(that is, both variable descriptions need a tweak, but for different
reasons)

> @@ -195,6 +197,15 @@ int qemudLoadDriverConfig(struct qemud_driver *driver,
>          }
>      }
>  
> +    p = virConfGetValue (conf, "security_default_confined");
> +    CHECK_TYPE ("security_default_confined", VIR_CONF_LONG);
> +    if (p) driver->securityDefaultConfined = p->l;
> +
> +    p = virConfGetValue (conf, "security_require_confined");
> +    CHECK_TYPE ("security_require_confined", VIR_CONF_LONG);
> +    if (p) driver->securityRequireConfined = p->l;

Conversion of long to boolean.  I guess this is copy-and-paste from
other places doing this, and also that since we require C99 it will be
safe.  But in C89 projects, the gnulib replacement for <stdbool.h> means
that this is non-portable, even though it should work just fine with C99
bool.  (That is, the gnulib replacement bool can compile (bool)256L to
false instead of the desired true, depending on your platform and
compiler).  No need to fix it in your patch (if we do anything, it
should be a global style scrub of all offenders in this file in one go).

> @@ -246,12 +250,20 @@ SELinuxGenSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED,
>                                     _("cannot generate selinux context for %s"), mcs);
>              goto cleanup;
>          }
> +        break;
> +
> +    case VIR_DOMAIN_SECLABEL_NONE:
> +        /* no op */
> +        break;

Might be safer to put a default case that errors out, so that we catch
any future additions.

-- 
Eric Blake   eblake at redhat.com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 620 bytes
Desc: OpenPGP digital signature
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20120123/22841a52/attachment-0001.sig>


More information about the libvir-list mailing list