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 introduction of sVirt to LXC though, there needs to be
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
<p>
The <code>seclabel</code> element allows control over the
- operation of the security drivers. There are two basic
- modes of operation, dynamic where libvirt automatically
- generates a unique security label, or static where the
- application/administrator chooses the labels. With dynamic
+ operation of the security drivers. There are three basic
+ modes of operation, 'dynamic' where libvirt automatically
+ generates a unique security label, 'static' where the
+ application/administrator chooses the labels, or 'none'
+ where confinement is disabled. With dynamic
label generation, libvirt will always automatically
relabel any resources associated with the virtual machine.
With static label assignment, by default, the administrator
or application must ensure labels are set correctly on any
resources, however, automatic relabeling can be enabled
- if desired.
+ if desired. <span class="since">'dynamic' since 0.6.1, 'static'
+ since 0.6.2, and 'none' since 0.9.10.</span>
</p>
<p>
<seclabel type='static' model='selinux' relabel='yes'>
<label>system_u:system_r:svirt_t:s0:c392,c662</label>
</seclabel>
+
+ <seclabel type='none'/>
</pre>
+ <p>
+ If no 'type' attribute is provided in the input XML, then
+ the security driver default setting will be used, which
+ may be either 'none' or 'dynamic'. If a 'baselabel' is set
+ but no 'type' is set, then the type is presumed to be 'dynamic'
+ </p>
+
<p>
When viewing the XML for a running guest with automatic
resource relabeling active, an additional XML element,
</p>
<dl>
<dt><code>type</code></dt>
- <dd>Either <code>static</code> or <code>dynamic</code> to determine
- whether libvirt automatically generates a unique security label
- or not.
+ <dd>Either <code>static</code>, <code>dynamic</code> or <code>none</code>
+ to determine whether libvirt automatically generates a unique security
+ label or not.
</dd>
<dt><code>model</code></dt>
<dd>A valid security model name, matching the currently
</optional>
</interleave>
</group>
+ <group>
+ <attribute name='type'>
+ <value>none</value>
+ </attribute>
+ </group>
</choice>
</element>
</define>
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
"unknown")
VIR_ENUM_IMPL(virDomainSeclabel, VIR_DOMAIN_SECLABEL_LAST,
+ "default",
+ "none",
"dynamic",
"static")
"%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"));
goto error;
}
+
p = virXPathStringLimit("string(./seclabel/@relabel)",
VIR_SECURITY_LABEL_BUFLEN-1, ctxt);
if (p != NULL) {
"%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", _("resource relabeling is 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;
def->imagelabel = p;
}
- /* Only parse baselabel, for dynamic label */
- if (def->type == VIR_DOMAIN_SECLABEL_DYNAMIC) {
+ /* Only parse baselabel, for dynamic or none label types */
+ if (def->type == VIR_DOMAIN_SECLABEL_DYNAMIC ||
+ def->type == VIR_DOMAIN_SECLABEL_NONE) {
p = virXPathStringLimit("string(./seclabel/baselabel[1])",
VIR_SECURITY_LABEL_BUFLEN-1, ctxt);
- if (p != NULL)
+ if (p != NULL) {
def->baselabel = p;
+ /* Forces none type to dynamic for back compat */
+ def->type = VIR_DOMAIN_SECLABEL_DYNAMIC;
+ }
}
/* Only parse model, if static labelling, or a base
}
-static int
-virSecurityLabelDefFormat(virBufferPtr buf, virSecurityLabelDefPtr def,
- unsigned int flags)
+static void
+virSecurityLabelDefFormat(virBufferPtr buf, virSecurityLabelDefPtr def)
{
const char *sectype = virDomainSeclabelTypeToString(def->type);
- int ret = -1;
if (!sectype)
- goto cleanup;
+ return;
+
+ if (def->type == VIR_DOMAIN_SECLABEL_DEFAULT)
+ return;
+
+ virBufferAsprintf(buf, "<seclabel type='%s'",
+ sectype);
+ virBufferEscapeString(buf, " model='%s'", def->model);
+
+ virBufferAsprintf(buf, " relabel='%s'",
+ def->norelabel ? "no" : "yes");
+
+ if (def->type == VIR_DOMAIN_SECLABEL_NONE) {
+ virBufferAddLit(buf, "/>\n");
+ return;
+ }
+
+ if (def->label || def->imagelabel || def->baselabel) {
+ virBufferAddLit(buf, ">\n");
- 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. */
- } 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, " <baselabel>%s</baselabel>\n",
def->baselabel);
virBufferAddLit(buf, "</seclabel>\n");
+ } else {
+ virBufferAddLit(buf, "/>\n");
}
- ret = 0;
-cleanup:
- return ret;
}
virBufferAddLit(buf, " </devices>\n");
- if (def->seclabel.model) {
- virBufferAdjustIndent(buf, 2);
- if (virSecurityLabelDefFormat(buf, &def->seclabel, flags) < 0)
- goto cleanup;
- virBufferAdjustIndent(buf, -2);
- }
+ virBufferAdjustIndent(buf, 2);
+ virSecurityLabelDefFormat(buf, &def->seclabel);
+ virBufferAdjustIndent(buf, -2);
if (def->namespaceData && def->ns.format) {
if ((def->ns.format)(buf, def->namespaceData) < 0)
};
enum virDomainSeclabelType {
+ VIR_DOMAIN_SECLABEL_DEFAULT,
+ VIR_DOMAIN_SECLABEL_NONE,
VIR_DOMAIN_SECLABEL_DYNAMIC,
VIR_DOMAIN_SECLABEL_STATIC,
| bool_entry "vnc_sasl"
| str_entry "vnc_sasl_dir"
| str_entry "security_driver"
+ | bool_entry "security_default_confined"
+ | bool_entry "security_require_confined"
| str_entry "user"
| str_entry "group"
| bool_entry "dynamic_ownership"
#
# security_driver = "selinux"
+# If set to non-zero, then the default security labeling
+# 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 0.
+# security_require_confined = 1
# The user ID for QEMU processes run by the system instance.
#user = "root"
int i;
/* Setup critical defaults */
+ driver->securityDefaultConfined = true;
+ driver->securityRequireConfined = false;
driver->dynamicOwnership = 1;
driver->clearEmulatorCapabilities = 1;
}
}
+ 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;
+
+
p = virConfGetValue (conf, "vnc_sasl");
CHECK_TYPE ("vnc_sasl", VIR_CONF_LONG);
if (p) driver->vncSASL = p->l;
virDomainEventStatePtr domainEventState;
char *securityDriverName;
+ bool securityDefaultConfined;
+ bool securityRequireConfined;
virSecurityManagerPtr securityManager;
char *saveImageFormat;
qemuSecurityInit(struct qemud_driver *driver)
{
virSecurityManagerPtr mgr = virSecurityManagerNew(driver->securityDriverName,
- driver->allowDiskFormatProbing);
+ driver->allowDiskFormatProbing,
+ driver->securityDefaultConfined,
+ driver->securityRequireConfined);
+
if (!mgr)
goto error;
virSecurityManagerPtr dac = virSecurityManagerNewDAC(driver->user,
driver->group,
driver->allowDiskFormatProbing,
+ driver->securityDefaultConfined,
+ driver->securityRequireConfined,
driver->dynamicOwnership);
if (!dac)
goto error;
struct _virSecurityManager {
virSecurityDriverPtr drv;
bool allowDiskFormatProbing;
+ bool defaultConfined;
+ bool requireConfined;
};
static virSecurityManagerPtr virSecurityManagerNewDriver(virSecurityDriverPtr drv,
- bool allowDiskFormatProbing)
+ bool allowDiskFormatProbing,
+ bool defaultConfined,
+ bool requireConfined)
{
virSecurityManagerPtr mgr;
mgr->drv = drv;
mgr->allowDiskFormatProbing = allowDiskFormatProbing;
+ mgr->defaultConfined = defaultConfined;
+ mgr->requireConfined = requireConfined;
if (drv->open(mgr) < 0) {
virSecurityManagerFree(mgr);
{
virSecurityManagerPtr mgr =
virSecurityManagerNewDriver(&virSecurityDriverStack,
- virSecurityManagerGetAllowDiskFormatProbing(primary));
+ virSecurityManagerGetAllowDiskFormatProbing(primary),
+ virSecurityManagerGetDefaultConfined(primary),
+ virSecurityManagerGetRequireConfined(primary));
if (!mgr)
return NULL;
virSecurityManagerPtr virSecurityManagerNewDAC(uid_t user,
gid_t group,
bool allowDiskFormatProbing,
+ bool defaultConfined,
+ bool requireConfined,
bool dynamicOwnership)
{
virSecurityManagerPtr mgr =
virSecurityManagerNewDriver(&virSecurityDriverDAC,
- allowDiskFormatProbing);
+ allowDiskFormatProbing,
+ defaultConfined,
+ requireConfined);
if (!mgr)
return NULL;
}
virSecurityManagerPtr virSecurityManagerNew(const char *name,
- bool allowDiskFormatProbing)
+ bool allowDiskFormatProbing,
+ bool defaultConfined,
+ bool requireConfined)
{
virSecurityDriverPtr drv = virSecurityDriverLookup(name);
if (!drv)
return NULL;
- return virSecurityManagerNewDriver(drv, allowDiskFormatProbing);
+ return virSecurityManagerNewDriver(drv,
+ allowDiskFormatProbing,
+ defaultConfined,
+ requireConfined);
}
return mgr->allowDiskFormatProbing;
}
+bool virSecurityManagerGetDefaultConfined(virSecurityManagerPtr mgr)
+{
+ return mgr->defaultConfined;
+}
+
+bool virSecurityManagerGetRequireConfined(virSecurityManagerPtr mgr)
+{
+ return mgr->requireConfined;
+}
+
int virSecurityManagerRestoreImageLabel(virSecurityManagerPtr mgr,
virDomainDefPtr vm,
virDomainDiskDefPtr disk)
int virSecurityManagerGenLabel(virSecurityManagerPtr mgr,
virDomainDefPtr vm)
{
+ if (vm->seclabel.type == VIR_DOMAIN_SECLABEL_DEFAULT) {
+ if (mgr->defaultConfined)
+ vm->seclabel.type = VIR_DOMAIN_SECLABEL_DYNAMIC;
+ else
+ vm->seclabel.type = VIR_DOMAIN_SECLABEL_NONE;
+ }
+
+ if ((vm->seclabel.type == VIR_DOMAIN_SECLABEL_NONE) &&
+ mgr->requireConfined) {
+ virSecurityReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Unconfined guests are not allowed on this host"));
+ return -1;
+ }
+
if (mgr->drv->domainGenSecurityLabel)
return mgr->drv->domainGenSecurityLabel(mgr, vm);
typedef virSecurityManager *virSecurityManagerPtr;
virSecurityManagerPtr virSecurityManagerNew(const char *name,
- bool allowDiskFormatProbing);
+ bool allowDiskFormatProbing,
+ bool defaultConfined,
+ bool requireConfined);
virSecurityManagerPtr virSecurityManagerNewStack(virSecurityManagerPtr primary,
virSecurityManagerPtr secondary);
virSecurityManagerPtr virSecurityManagerNewDAC(uid_t user,
gid_t group,
bool allowDiskFormatProbing,
+ bool defaultConfined,
+ bool requireConfined,
bool dynamicOwnership);
void *virSecurityManagerGetPrivateData(virSecurityManagerPtr mgr);
const char *virSecurityManagerGetDOI(virSecurityManagerPtr mgr);
const char *virSecurityManagerGetModel(virSecurityManagerPtr mgr);
bool virSecurityManagerGetAllowDiskFormatProbing(virSecurityManagerPtr mgr);
+bool virSecurityManagerGetDefaultConfined(virSecurityManagerPtr mgr);
+bool virSecurityManagerGetRequireConfined(virSecurityManagerPtr mgr);
int virSecurityManagerRestoreImageLabel(virSecurityManagerPtr mgr,
virDomainDefPtr def,
int c1 = 0;
int c2 = 0;
context_t ctx = NULL;
+ const char *range;
if ((def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC) &&
!def->seclabel.baselabel &&
return rc;
}
- if (def->seclabel.type == VIR_DOMAIN_SECLABEL_STATIC) {
+ switch (def->seclabel.type) {
+ case VIR_DOMAIN_SECLABEL_STATIC:
if (!(ctx = context_new(def->seclabel.label)) ) {
virReportSystemError(errno,
_("unable to allocate socket security context '%s'"),
return rc;
}
- const char *range = context_range_get(ctx);
+ range = context_range_get(ctx);
if (!range ||
!(mcs = strdup(range))) {
virReportOOMError();
goto cleanup;
}
- } else {
+ break;
+
+ case VIR_DOMAIN_SECLABEL_DYNAMIC:
do {
c1 = virRandomBits(10);
c2 = virRandomBits(10);
_("cannot generate selinux context for %s"), mcs);
goto cleanup;
}
- }
- def->seclabel.imagelabel = SELinuxGenNewContext(default_image_context, mcs);
- if (!def->seclabel.imagelabel) {
+ break;
+
+ case VIR_DOMAIN_SECLABEL_NONE:
+ /* no op */
+ break;
+
+ default:
virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
- _("cannot generate selinux context for %s"), mcs);
+ _("unexpected security label type '%s'"),
+ virDomainSeclabelTypeToString(def->seclabel.type));
goto cleanup;
}
+ if (!def->seclabel.norelabel) {
+ def->seclabel.imagelabel = SELinuxGenNewContext(default_image_context, mcs);
+ if (!def->seclabel.imagelabel) {
+ virSecurityReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot generate selinux context for %s"), mcs);
+ goto cleanup;
+ }
+ }
+
if (!def->seclabel.model &&
!(def->seclabel.model = strdup(SECURITY_SELINUX_NAME))) {
virReportOOMError();
virSecurityManagerPtr mgr;
const char *doi, *model;
- mgr = virSecurityManagerNew(NULL, false);
+ mgr = virSecurityManagerNew(NULL, false, true, true);
if (mgr == NULL) {
fprintf (stderr, "Failed to start security driver");
exit (-1);