This is to announce the release of v0.20 of sVirt, a project to add security labeling support to Linux-based virtualization. Project page: http://www.selinuxproject.org/page/SVirt A patch against libvirt is attached; and also included in a release tarball at http://namei.org/svirt/. See 'readme.txt' there for more details on building and running the code. This release is an update in response to feedback received on the v0.10 prototype release, per the discussion thread at: https://www.redhat.com/archives/libvir-list/2008-October/msg00478.html Changes are as follows: ----------------------------------------------------------------------------- v0.20 - 11/Dec/2008 ----------------------------------------------------------------------------- * Published TODO list: http://selinuxproject.org/page/SVirt/TODO * Rebased to current upstream: converted to new build system, locking etc. * Changed DOI format to an integer value, represented via a string, defaulting to "0". Ongoing general discussion on DOI formats and semantics may be found at: http://mail.opensolaris.org/mailman/listinfo/doi-discuss * Introduced the concept of a "security model", to more easily distinguish between security models and labels in the API. * The security model and DOI attributes are now properties of the hypervisor (instead of the domain label), and included in its host capabilities, e.g.: <capabilities> <host> <cpu> <arch>x86_64</arch> </cpu> <secmodel> <model>selinux</model> <doi>0</doi> </secmodel> </host> .... </capabilities> Implicit here is the assumption that each hypervisor may only be associated with one security model. * Integrated security model support into "virsh capabilities". * The domain configuration label is now of the form: <domain> .... <seclabel model='selinux'> <label>system_u:system_r:virtd_t:s0</label> </seclabel> </domain> * The model attribute of the seclabel element above is validated against the host security model at runtime. * The output of "virsh dominfo" for a running labeled domain is now as follows: # dominfo sys1 Id: 1 Name: sys1 UUID: fa3c8e06-0877-2a08-06fd-f2479b7bacb0 OS Type: hvm Security model: selinux Security DOI: 0 State: running CPU(s): 1 CPU time: 24.9s Max memory: 524288 kB Used memory: 524288 kB Autostart: disable Security label: system_u:system_r:virtd_t:s0 (enforcing) * The security policy enforcing is a dynamic property of the domain security label, as it may be applied on a per-domain basis. * The main aspects to security labeling support in the library and associated data structures are as follows: Domain configuration: virDomainSecLabelDef Host capabilities: virDomainSecModel Active domain state: virDomainSecLabel ----------------------------------------------------------------------------- I'm hoping to be able to propose an initial version for upstream merge within the next few minor releases, tasks for which are being scoped out in the new TODO list: http://selinuxproject.org/page/SVirt/TODO If the current release passes review, the next major task will be to add dynamic MCS labeling of domains and disk images for simple isolation. Feedback is welcome. - James -- James Morris <jmorris@xxxxxxxxx>
Makefile.maint | 1 autobuild.sh | 10 +- include/libvirt/libvirt.h | 67 ++++++++++++++ include/libvirt/libvirt.h.in | 67 ++++++++++++++ include/libvirt/virterror.h | 2 po/POTFILES.in | 2 python/generator.py | 2 qemud/Makefile.am | 1 qemud/remote.c | 82 +++++++++++++++++ qemud/remote_dispatch_args.h | 2 qemud/remote_dispatch_prototypes.h | 14 ++ qemud/remote_dispatch_ret.h | 2 qemud/remote_dispatch_table.h | 10 ++ qemud/remote_protocol.c | 46 +++++++++ qemud/remote_protocol.h | 44 +++++++++ qemud/remote_protocol.x | 38 +++++++- src/Makefile.am | 26 ++++- src/capabilities.c | 10 ++ src/capabilities.h | 7 + src/domain_conf.c | 73 +++++++++++++++ src/domain_conf.h | 9 + src/driver.h | 8 + src/libvirt.c | 69 ++++++++++++++ src/libvirt_sym.version.in | 3 src/lxc_driver.c | 2 src/openvz_driver.c | 2 src/qemu_conf.h | 3 src/qemu_driver.c | 175 +++++++++++++++++++++++++++++++++++++ src/remote_internal.c | 66 +++++++++++++ src/seclabel.c | 114 ++++++++++++++++++++++++ src/seclabel.h | 62 +++++++++++++ src/seclabel_selinux.c | 106 ++++++++++++++++++++++ src/seclabel_selinux.h | 18 +++ src/storage_backend.c | 1 src/test.c | 2 src/uml_driver.c | 2 src/virsh.c | 22 ++++ src/virterror.c | 9 + tests/daemon-conf | 3 39 files changed, 1177 insertions(+), 5 deletions(-) diff --git a/Makefile.maint b/Makefile.maint index 10d481b..0dcdcf8 100644 --- a/Makefile.maint +++ b/Makefile.maint @@ -353,6 +353,7 @@ msg_gen_function += qemudReportError msg_gen_function += openvzLog msg_gen_function += openvzError msg_gen_function += virDomainReportError +msg_gen_function += virSecLabelReportError msg_gen_function += virReportErrorHelper msg_gen_function += lxcError msg_gen_function += umlError diff --git a/autobuild.sh b/autobuild.sh index e5d8554..bee3f34 100755 --- a/autobuild.sh +++ b/autobuild.sh @@ -14,10 +14,18 @@ rm -rf coverage #mkdir build #cd build +SELINUXENABLED=/usr/sbin/selinuxenabled + +if [ -x $SELINUXENABLED ] && $SELINUXENABLED ; then + WITH_SELINUX="--with-selinux=yes" +else + WITH_SELINUX="" +fi + ./autogen.sh --prefix="$AUTOBUILD_INSTALL_ROOT" \ --enable-test-coverage \ --enable-compile-warnings=error \ - --with-xen-proxy + --with-xen-proxy $WITH_SELINUX # If the MAKEFLAGS envvar does not yet include a -j option, # add -jN where N depends on the number of processors. diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h index 97b4489..46cd3f4 100644 --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -111,6 +111,69 @@ typedef enum { } virDomainCreateFlags; /** + * VIR_SECLABEL_LABEL_BUFLEN: + * + * Macro providing the maximum length of the virDomainSecLabel + * label string. Note that this value is based on that used + * by Labeled NFS. + */ +#define VIR_SECLABEL_LABEL_BUFLEN (4096 + 1) + +/** + * virDomainSecLabel: + * + * a virDomainSecLabel is a structure filled by virDomainGetSecLabel(), + * providing the security label and associated attributes for the specified + * domain. + * + */ +typedef struct _virDomainSecLabel { + char label[VIR_SECLABEL_LABEL_BUFLEN]; /* security label string */ + int enforcing; /* 1 if security policy is being enforced for domain */ +} virDomainSecLabel; + +/** + * virDomainSecLabelPtr: + * + * a virDomainSecLabelPtr is a pointer to a virDomainSecLabel. + */ +typedef virDomainSecLabel *virDomainSecLabelPtr; + +/** + * VIR_SECLABEL_MODEL_BUFLEN: + * + * Macro providing the maximum length of the virDomainSecModel model string. + */ +#define VIR_SECLABEL_MODEL_BUFLEN (256 + 1) + +/** + * VIR_SECLABEL_DOI_BUFLEN: + * + * Macro providing the maximum length of the virDomainSecModel doi string. + */ +#define VIR_SECLABEL_DOI_BUFLEN (256 + 1) + +/** + * virDomainSecModel: + * + * a virDomainSecModel is a structure filled by virDomainGetSecModel(), + * providing the per-hypervisor security model and DOI attributes for the + * specified domain. + * + */ +typedef struct _virDomainSecModel { + char model[VIR_SECLABEL_MODEL_BUFLEN]; /* security model string */ + char doi[VIR_SECLABEL_DOI_BUFLEN]; /* domain of interpetation */ +} virDomainSecModel; + +/** + * virDomainSecModelPtr: + * + * a virDomainSecModelPtr is a pointer to a virDomainSecModel. + */ +typedef virDomainSecModel *virDomainSecModelPtr; + +/** * virNodeInfoPtr: * * a virNodeInfo is a structure filled by virNodeGetInfo() and providing @@ -504,6 +567,10 @@ int virDomainSetMaxMemory (virDomainPtr domain, int virDomainSetMemory (virDomainPtr domain, unsigned long memory); int virDomainGetMaxVcpus (virDomainPtr domain); +int virDomainGetSecLabel (virDomainPtr domain, + virDomainSecLabelPtr seclabel); +int virDomainGetSecModel (virDomainPtr domain, + virDomainSecModelPtr secmodel); /* * XML domain description diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index fc322e6..63cf21d 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -111,6 +111,69 @@ typedef enum { } virDomainCreateFlags; /** + * VIR_SECLABEL_LABEL_BUFLEN: + * + * Macro providing the maximum length of the virDomainSecLabel + * label string. Note that this value is based on that used + * by Labeled NFS. + */ +#define VIR_SECLABEL_LABEL_BUFLEN (4096 + 1) + +/** + * virDomainSecLabel: + * + * a virDomainSecLabel is a structure filled by virDomainGetSecLabel(), + * providing the security label and associated attributes for the specified + * domain. + * + */ +typedef struct _virDomainSecLabel { + char label[VIR_SECLABEL_LABEL_BUFLEN]; /* security label string */ + int enforcing; /* 1 if security policy is being enforced for domain */ +} virDomainSecLabel; + +/** + * virDomainSecLabelPtr: + * + * a virDomainSecLabelPtr is a pointer to a virDomainSecLabel. + */ +typedef virDomainSecLabel *virDomainSecLabelPtr; + +/** + * VIR_SECLABEL_MODEL_BUFLEN: + * + * Macro providing the maximum length of the virDomainSecModel model string. + */ +#define VIR_SECLABEL_MODEL_BUFLEN (256 + 1) + +/** + * VIR_SECLABEL_DOI_BUFLEN: + * + * Macro providing the maximum length of the virDomainSecModel doi string. + */ +#define VIR_SECLABEL_DOI_BUFLEN (256 + 1) + +/** + * virDomainSecModel: + * + * a virDomainSecModel is a structure filled by virDomainGetSecModel(), + * providing the per-hypervisor security model and DOI attributes for the + * specified domain. + * + */ +typedef struct _virDomainSecModel { + char model[VIR_SECLABEL_MODEL_BUFLEN]; /* security model string */ + char doi[VIR_SECLABEL_DOI_BUFLEN]; /* domain of interpetation */ +} virDomainSecModel; + +/** + * virDomainSecModelPtr: + * + * a virDomainSecModelPtr is a pointer to a virDomainSecModel. + */ +typedef virDomainSecModel *virDomainSecModelPtr; + +/** * virNodeInfoPtr: * * a virNodeInfo is a structure filled by virNodeGetInfo() and providing @@ -504,6 +567,10 @@ int virDomainSetMaxMemory (virDomainPtr domain, int virDomainSetMemory (virDomainPtr domain, unsigned long memory); int virDomainGetMaxVcpus (virDomainPtr domain); +int virDomainGetSecLabel (virDomainPtr domain, + virDomainSecLabelPtr seclabel); +int virDomainGetSecModel (virDomainPtr domain, + virDomainSecModelPtr secmodel); /* * XML domain description diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 0f1f08c..e987d9e 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -61,6 +61,7 @@ typedef enum { VIR_FROM_UML, /* Error at the UML driver */ VIR_FROM_NODEDEV, /* Error from node device monitor */ VIR_FROM_XEN_INOTIFY, /* Error from xen inotify layer */ + VIR_FROM_SECLABEL, /* Error from security labeling framework */ } virErrorDomain; @@ -154,6 +155,7 @@ typedef enum { VIR_WAR_NO_NODE, /* failed to start node driver */ VIR_ERR_INVALID_NODE_DEVICE,/* invalid node device object */ VIR_ERR_NO_NODE_DEVICE,/* node device not found */ + VIR_ERR_NO_SECLABEL_MODEL, /* security labeling model not found */ } virErrorNumber; /** diff --git a/po/POTFILES.in b/po/POTFILES.in index d33d24b..8dab48e 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -20,6 +20,8 @@ src/proxy_internal.c src/qemu_conf.c src/qemu_driver.c src/remote_internal.c +src/seclabel.c +src/seclabel_selinux.c src/sexpr.c src/storage_backend.c src/storage_backend_disk.c diff --git a/python/generator.py b/python/generator.py index 9c71c05..7a99eb3 100755 --- a/python/generator.py +++ b/python/generator.py @@ -342,6 +342,8 @@ skip_function = ( 'virCopyLastError', # Python API is called virGetLastError instead 'virConnectOpenAuth', # Python C code is manually written 'virDefaultErrorFunc', # Python virErrorFuncHandler impl calls this from C + 'virDomainGetSecLabel', # Needs investigation... + 'virDomainGetSecModel', # Needs investigation... 'virConnectDomainEventRegister', # overridden in virConnect.py 'virConnectDomainEventDeregister', # overridden in virConnect.py ) diff --git a/qemud/Makefile.am b/qemud/Makefile.am index 8cb0847..9db9ee8 100644 --- a/qemud/Makefile.am +++ b/qemud/Makefile.am @@ -122,6 +122,7 @@ libvirtd_LDADD += ../src/libvirt_driver_nodedev.la endif endif +libvirtd_LDADD += ../src/libvirt_driver_seclabel.la libvirtd_LDADD += ../src/libvirt.la if HAVE_POLKIT diff --git a/qemud/remote.c b/qemud/remote.c index 0488e6c..ab2d754 100644 --- a/qemud/remote.c +++ b/qemud/remote.c @@ -1319,6 +1319,88 @@ remoteDispatchDomainGetMaxVcpus (struct qemud_server *server ATTRIBUTE_UNUSED, } static int +remoteDispatchDomainGetSeclabel(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_error *rerr, + remote_domain_get_seclabel_args *args, + remote_domain_get_seclabel_ret *ret) +{ + virDomainPtr dom; + virDomainSecLabel seclabel; + + dom = get_nonnull_domain(conn, args->dom); + if (dom == NULL) { + remoteDispatchFormatError(rerr, "%s", _("domain not found")); + return -2; + } + + memset(&seclabel, 0, sizeof seclabel); + + if (virDomainGetSecLabel(dom, &seclabel) == -1) { + virDomainFree(dom); + remoteDispatchFormatError(rerr, "%s", _("unable to get security label")); + return -1; + } + + ret->label.label_len = strlen(seclabel.label) + 1; + if (VIR_ALLOC_N(ret->label.label_val, ret->label.label_len) < 0) { + virDomainFree (dom); + remoteDispatchFormatError(rerr, "%s", strerror (errno)); + return -2; + } + strcpy(ret->label.label_val, seclabel.label); + ret->enforcing = seclabel.enforcing; + + virDomainFree(dom); + return 0; +} + +static int +remoteDispatchDomainGetSecmodel(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_error *rerr, + remote_domain_get_secmodel_args *args, + remote_domain_get_secmodel_ret *ret) +{ + virDomainPtr dom; + virDomainSecModel secmodel; + + dom = get_nonnull_domain(conn, args->dom); + if (dom == NULL) { + remoteDispatchFormatError(rerr, "%s", _("domain not found")); + return -2; + } + + memset(&secmodel, 0, sizeof secmodel); + if (virDomainGetSecModel(dom, &secmodel) == -1) { + virDomainFree(dom); + remoteDispatchFormatError(rerr, "%s", _("unable to get security model")); + return -1; + } + + ret->model.model_len = strlen(secmodel.model) + 1; + if (VIR_ALLOC_N(ret->model.model_val, ret->model.model_len) < 0) { + virDomainFree(dom); + remoteDispatchFormatError(rerr, "%s", strerror (errno)); + return -2; + } + strcpy(ret->model.model_val, secmodel.model); + + ret->doi.doi_len = strlen(secmodel.doi) + 1; + if (VIR_ALLOC_N(ret->doi.doi_val, ret->doi.doi_len) < 0) { + virDomainFree(dom); + remoteDispatchFormatError(rerr, "%s", strerror (errno)); + return -2; + } + strcpy(ret->doi.doi_val, secmodel.doi); + + virDomainFree(dom); + return 0; +} + +static int remoteDispatchDomainGetOsType (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, diff --git a/qemud/remote_dispatch_args.h b/qemud/remote_dispatch_args.h index a19ab79..ec5e346 100644 --- a/qemud/remote_dispatch_args.h +++ b/qemud/remote_dispatch_args.h @@ -99,3 +99,5 @@ remote_node_device_get_parent_args val_remote_node_device_get_parent_args; remote_node_device_num_of_caps_args val_remote_node_device_num_of_caps_args; remote_node_device_list_caps_args val_remote_node_device_list_caps_args; + remote_domain_get_seclabel_args val_remote_domain_get_seclabel_args; + remote_domain_get_secmodel_args val_remote_domain_get_secmodel_args; diff --git a/qemud/remote_dispatch_prototypes.h b/qemud/remote_dispatch_prototypes.h index 3ffb164..13fbf6a 100644 --- a/qemud/remote_dispatch_prototypes.h +++ b/qemud/remote_dispatch_prototypes.h @@ -184,6 +184,20 @@ static int remoteDispatchDomainGetSchedulerType( remote_error *err, remote_domain_get_scheduler_type_args *args, remote_domain_get_scheduler_type_ret *ret); +static int remoteDispatchDomainGetSeclabel( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_error *err, + remote_domain_get_seclabel_args *args, + remote_domain_get_seclabel_ret *ret); +static int remoteDispatchDomainGetSecmodel( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_error *err, + remote_domain_get_secmodel_args *args, + remote_domain_get_secmodel_ret *ret); static int remoteDispatchDomainGetVcpus( struct qemud_server *server, struct qemud_client *client, diff --git a/qemud/remote_dispatch_ret.h b/qemud/remote_dispatch_ret.h index 563167f..1b4779f 100644 --- a/qemud/remote_dispatch_ret.h +++ b/qemud/remote_dispatch_ret.h @@ -86,3 +86,5 @@ remote_node_device_get_parent_ret val_remote_node_device_get_parent_ret; remote_node_device_num_of_caps_ret val_remote_node_device_num_of_caps_ret; remote_node_device_list_caps_ret val_remote_node_device_list_caps_ret; + remote_domain_get_seclabel_ret val_remote_domain_get_seclabel_ret; + remote_domain_get_secmodel_ret val_remote_domain_get_secmodel_ret; diff --git a/qemud/remote_dispatch_table.h b/qemud/remote_dispatch_table.h index 60f0e1c..4e6921f 100644 --- a/qemud/remote_dispatch_table.h +++ b/qemud/remote_dispatch_table.h @@ -592,3 +592,13 @@ .args_filter = (xdrproc_t) xdr_remote_node_device_list_caps_args, .ret_filter = (xdrproc_t) xdr_remote_node_device_list_caps_ret, }, +{ /* DomainGetSeclabel => 118 */ + .fn = (dispatch_fn) remoteDispatchDomainGetSeclabel, + .args_filter = (xdrproc_t) xdr_remote_domain_get_seclabel_args, + .ret_filter = (xdrproc_t) xdr_remote_domain_get_seclabel_ret, +}, +{ /* DomainGetSecmodel => 119 */ + .fn = (dispatch_fn) remoteDispatchDomainGetSecmodel, + .args_filter = (xdrproc_t) xdr_remote_domain_get_secmodel_args, + .ret_filter = (xdrproc_t) xdr_remote_domain_get_secmodel_ret, +}, diff --git a/qemud/remote_protocol.c b/qemud/remote_protocol.c index ec8e653..44aba3f 100644 --- a/qemud/remote_protocol.c +++ b/qemud/remote_protocol.c @@ -1166,6 +1166,52 @@ xdr_remote_domain_get_max_vcpus_ret (XDR *xdrs, remote_domain_get_max_vcpus_ret } bool_t +xdr_remote_domain_get_seclabel_args (XDR *xdrs, remote_domain_get_seclabel_args *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_get_seclabel_ret (XDR *xdrs, remote_domain_get_seclabel_ret *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->label.label_val; + + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->label.label_len, REMOTE_SECLABEL_LABEL_MAX, + sizeof (char), (xdrproc_t) xdr_char)) + return FALSE; + if (!xdr_int (xdrs, &objp->enforcing)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_get_secmodel_args (XDR *xdrs, remote_domain_get_secmodel_args *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_get_secmodel_ret (XDR *xdrs, remote_domain_get_secmodel_ret *objp) +{ + char **objp_cpp1 = (char **) (void *) &objp->doi.doi_val; + char **objp_cpp0 = (char **) (void *) &objp->model.model_val; + + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->model.model_len, REMOTE_SECLABEL_MODEL_MAX, + sizeof (char), (xdrproc_t) xdr_char)) + return FALSE; + if (!xdr_array (xdrs, objp_cpp1, (u_int *) &objp->doi.doi_len, REMOTE_SECLABEL_DOI_MAX, + sizeof (char), (xdrproc_t) xdr_char)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_domain_attach_device_args (XDR *xdrs, remote_domain_attach_device_args *objp) { diff --git a/qemud/remote_protocol.h b/qemud/remote_protocol.h index bf107ae..a13e234 100644 --- a/qemud/remote_protocol.h +++ b/qemud/remote_protocol.h @@ -39,6 +39,9 @@ typedef remote_nonnull_string *remote_string; #define REMOTE_AUTH_TYPE_LIST_MAX 20 #define REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX 65536 #define REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX 65536 +#define REMOTE_SECLABEL_MODEL_MAX VIR_SECLABEL_MODEL_BUFLEN +#define REMOTE_SECLABEL_LABEL_MAX VIR_SECLABEL_LABEL_BUFLEN +#define REMOTE_SECLABEL_DOI_MAX VIR_SECLABEL_DOI_BUFLEN typedef char remote_uuid[VIR_UUID_BUFLEN]; @@ -638,6 +641,37 @@ struct remote_domain_get_max_vcpus_ret { }; typedef struct remote_domain_get_max_vcpus_ret remote_domain_get_max_vcpus_ret; +struct remote_domain_get_seclabel_args { + remote_nonnull_domain dom; +}; +typedef struct remote_domain_get_seclabel_args remote_domain_get_seclabel_args; + +struct remote_domain_get_seclabel_ret { + struct { + u_int label_len; + char *label_val; + } label; + int enforcing; +}; +typedef struct remote_domain_get_seclabel_ret remote_domain_get_seclabel_ret; + +struct remote_domain_get_secmodel_args { + remote_nonnull_domain dom; +}; +typedef struct remote_domain_get_secmodel_args remote_domain_get_secmodel_args; + +struct remote_domain_get_secmodel_ret { + struct { + u_int model_len; + char *model_val; + } model; + struct { + u_int doi_len; + char *doi_val; + } doi; +}; +typedef struct remote_domain_get_secmodel_ret remote_domain_get_secmodel_ret; + struct remote_domain_attach_device_args { remote_nonnull_domain dom; remote_nonnull_string xml; @@ -1349,6 +1383,8 @@ enum remote_procedure { REMOTE_PROC_NODE_DEVICE_GET_PARENT = 115, REMOTE_PROC_NODE_DEVICE_NUM_OF_CAPS = 116, REMOTE_PROC_NODE_DEVICE_LIST_CAPS = 117, + REMOTE_PROC_DOMAIN_GET_SECLABEL = 118, + REMOTE_PROC_DOMAIN_GET_SECMODEL = 119, }; typedef enum remote_procedure remote_procedure; @@ -1475,6 +1511,10 @@ extern bool_t xdr_remote_domain_get_vcpus_args (XDR *, remote_domain_get_vcpus_ extern bool_t xdr_remote_domain_get_vcpus_ret (XDR *, remote_domain_get_vcpus_ret*); extern bool_t xdr_remote_domain_get_max_vcpus_args (XDR *, remote_domain_get_max_vcpus_args*); extern bool_t xdr_remote_domain_get_max_vcpus_ret (XDR *, remote_domain_get_max_vcpus_ret*); +extern bool_t xdr_remote_domain_get_seclabel_args (XDR *, remote_domain_get_seclabel_args*); +extern bool_t xdr_remote_domain_get_seclabel_ret (XDR *, remote_domain_get_seclabel_ret*); +extern bool_t xdr_remote_domain_get_secmodel_args (XDR *, remote_domain_get_secmodel_args*); +extern bool_t xdr_remote_domain_get_secmodel_ret (XDR *, remote_domain_get_secmodel_ret*); extern bool_t xdr_remote_domain_attach_device_args (XDR *, remote_domain_attach_device_args*); extern bool_t xdr_remote_domain_detach_device_args (XDR *, remote_domain_detach_device_args*); extern bool_t xdr_remote_domain_get_autostart_args (XDR *, remote_domain_get_autostart_args*); @@ -1680,6 +1720,10 @@ extern bool_t xdr_remote_domain_get_vcpus_args (); extern bool_t xdr_remote_domain_get_vcpus_ret (); extern bool_t xdr_remote_domain_get_max_vcpus_args (); extern bool_t xdr_remote_domain_get_max_vcpus_ret (); +extern bool_t xdr_remote_domain_get_seclabel_args (); +extern bool_t xdr_remote_domain_get_seclabel_ret (); +extern bool_t xdr_remote_domain_get_secmodel_args (); +extern bool_t xdr_remote_domain_get_secmodel_ret (); extern bool_t xdr_remote_domain_attach_device_args (); extern bool_t xdr_remote_domain_detach_device_args (); extern bool_t xdr_remote_domain_get_autostart_args (); diff --git a/qemud/remote_protocol.x b/qemud/remote_protocol.x index 22327fd..c88fd35 100644 --- a/qemud/remote_protocol.x +++ b/qemud/remote_protocol.x @@ -116,6 +116,21 @@ const REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX = 65536; */ const REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX = 65536; +/* + * Maximum length of a security label model field. + */ +const REMOTE_SECLABEL_MODEL_MAX = VIR_SECLABEL_MODEL_BUFLEN; + +/* + * Maximum length of a security label field. + */ +const REMOTE_SECLABEL_LABEL_MAX = VIR_SECLABEL_LABEL_BUFLEN; + +/* + * Maximum length of a security label DOI field. + */ +const REMOTE_SECLABEL_DOI_MAX = VIR_SECLABEL_DOI_BUFLEN; + /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -618,6 +633,24 @@ struct remote_domain_get_max_vcpus_ret { int num; }; +struct remote_domain_get_seclabel_args { + remote_nonnull_domain dom; +}; + +struct remote_domain_get_seclabel_ret { + char label<REMOTE_SECLABEL_LABEL_MAX>; + int enforcing; +}; + +struct remote_domain_get_secmodel_args { + remote_nonnull_domain dom; +}; + +struct remote_domain_get_secmodel_ret { + char model<REMOTE_SECLABEL_MODEL_MAX>; + char doi<REMOTE_SECLABEL_DOI_MAX>; +}; + struct remote_domain_attach_device_args { remote_nonnull_domain dom; remote_nonnull_string xml; @@ -1224,7 +1257,10 @@ enum remote_procedure { REMOTE_PROC_NODE_DEVICE_DUMP_XML = 114, REMOTE_PROC_NODE_DEVICE_GET_PARENT = 115, REMOTE_PROC_NODE_DEVICE_NUM_OF_CAPS = 116, - REMOTE_PROC_NODE_DEVICE_LIST_CAPS = 117 + REMOTE_PROC_NODE_DEVICE_LIST_CAPS = 117, + + REMOTE_PROC_DOMAIN_GET_SECLABEL = 118, + REMOTE_PROC_DOMAIN_GET_SECMODEL = 119 }; /* Custom RPC structure. */ diff --git a/src/Makefile.am b/src/Makefile.am index c32a1d4..076ef85 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -134,7 +134,7 @@ UML_DRIVER_SOURCES = \ NETWORK_DRIVER_SOURCES = \ network_driver.h network_driver.c -# And finally storage backend specific impls +# Storage backend specific impls STORAGE_DRIVER_SOURCES = \ storage_driver.h storage_driver.c \ storage_backend.h storage_backend.c @@ -159,6 +159,12 @@ STORAGE_DRIVER_DISK_SOURCES = \ STORAGE_HELPER_DISK_SOURCES = \ parthelper.c +# Security framework and drivers for various models +SECLABEL_DRIVER_SOURCES = \ + seclabel.h seclabel.c + +SECLABEL_DRIVER_SELINUX_SOURCES = \ + seclabel_selinux.h seclabel_selinux.c NODE_DEVICE_DRIVER_SOURCES = \ node_device.c node_device.h @@ -370,6 +376,19 @@ libvirt_driver_nodedev_la_LDFLAGS += -module -avoid-version endif endif +libvirt_driver_seclabel_la_SOURCES = $(SECLABEL_DRIVER_SOURCES) +if WITH_DRIVER_MODULES +mod_LTLIBRARIES += libvirt_driver_seclabel.la +else +noinst_LTLIBRARIES += libvirt_driver_seclabel.la +endif +if WITH_DRIVER_MODULES +libvirt_driver_seclabel_la_LDFLAGS = -module -avoid-version +endif +# XXX fixme +# if WITH_SELINUX +libvirt_driver_seclabel_la_SOURCES += $(SECLABEL_DRIVER_SELINUX_SOURCES) +# endif # Add all conditional sources just in case... EXTRA_DIST += \ @@ -388,8 +407,9 @@ EXTRA_DIST += \ $(STORAGE_DRIVER_DISK_SOURCES) \ $(NODE_DEVICE_DRIVER_SOURCES) \ $(NODE_DEVICE_DRIVER_HAL_SOURCES) \ - $(NODE_DEVICE_DRIVER_DEVKIT_SOURCES) - + $(NODE_DEVICE_DRIVER_DEVKIT_SOURCES) \ + $(SECLABEL_DRIVER_SOURCES) \ + $(SECLABEL_DRIVER_SELINUX_SOURCES) # Empty source list - it merely links a bunch of convenience libs together libvirt_la_SOURCES = diff --git a/src/capabilities.c b/src/capabilities.c index 158873c..6a09f36 100644 --- a/src/capabilities.c +++ b/src/capabilities.c @@ -150,6 +150,8 @@ virCapabilitiesFree(virCapsPtr caps) { VIR_FREE(caps->host.migrateTrans); VIR_FREE(caps->host.arch); + VIR_FREE(caps->host.secModel.model); + VIR_FREE(caps->host.secModel.doi); VIR_FREE(caps); } @@ -575,6 +577,14 @@ virCapabilitiesFormatXML(virCapsPtr caps) virBufferAddLit(&xml, " </cells>\n"); virBufferAddLit(&xml, " </topology>\n"); } + + if (caps->host.secModel.model) { + virBufferAddLit(&xml, " <secmodel>\n"); + virBufferVSprintf(&xml, " <model>%s</model>\n", caps->host.secModel.model); + virBufferVSprintf(&xml, " <doi>%s</doi>\n", caps->host.secModel.doi); + virBufferAddLit(&xml, " </secmodel>\n"); + } + virBufferAddLit(&xml, " </host>\n\n"); diff --git a/src/capabilities.h b/src/capabilities.h index c991ea1..6257ac3 100644 --- a/src/capabilities.h +++ b/src/capabilities.h @@ -78,6 +78,12 @@ struct _virCapsHostNUMACell { int *cpus; }; +typedef struct _virCapsHostSecModel virCapsHostSecModel; +struct _virCapsHostSecModel { + char *model; + char *doi; +}; + typedef struct _virCapsHost virCapsHost; typedef virCapsHost *virCapsHostPtr; struct _virCapsHost { @@ -90,6 +96,7 @@ struct _virCapsHost { char **migrateTrans; int nnumaCell; virCapsHostNUMACellPtr *numaCell; + virCapsHostSecModel secModel; }; typedef struct _virCaps virCaps; diff --git a/src/domain_conf.c b/src/domain_conf.c index 32ed59f..5a0d4de 100644 --- a/src/domain_conf.c +++ b/src/domain_conf.c @@ -359,6 +359,16 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def) VIR_FREE(def); } +void virDomainSecLabelDefFree(virDomainDefPtr def); + +void virDomainSecLabelDefFree(virDomainDefPtr def) +{ + if (def->seclabel.model) + VIR_FREE(def->seclabel.model); + if (def->seclabel.label) + VIR_FREE(def->seclabel.label); +} + void virDomainDefFree(virDomainDefPtr def) { unsigned int i; @@ -417,6 +427,8 @@ void virDomainDefFree(virDomainDefPtr def) VIR_FREE(def->cpumask); VIR_FREE(def->emulator); + virDomainSecLabelDefFree(def); + VIR_FREE(def); } @@ -1644,6 +1656,56 @@ static int virDomainLifecycleParseXML(virConnectPtr conn, return 0; } +static int +virDomainSecLabelDefParseXMLString(virConnectPtr conn, + const char *str, + int maxlen, + const char *name, + char **to, + xmlXPathContextPtr ctxt) +{ + char *tmp = virXPathString(conn, str, ctxt); + + if (tmp != NULL) { + if (strlen(tmp) >= maxlen) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("\'%s\' longer than %d characters"), + name, maxlen); + return -1; + } + *to = tmp; + } else { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("\'%s\' missing"), name); + return -1; + } + return 0; +} + +static int +virDomainSecLabelDefParseXML(virConnectPtr conn, + const virDomainDefPtr def, + xmlXPathContextPtr ctxt) +{ + if (virXPathNode(conn, "./seclabel", ctxt) == NULL) + return 0; + + if (virDomainSecLabelDefParseXMLString(conn, "string(./seclabel/label[1])", + VIR_SECLABEL_LABEL_BUFLEN-1, "label", + &def->seclabel.label, ctxt) == -1) + goto error; + + if (virDomainSecLabelDefParseXMLString(conn, "string(./seclabel/@model)", + VIR_SECLABEL_MODEL_BUFLEN-1, "model", + &def->seclabel.model, ctxt) == -1) + goto error; + + return 0; + +error: + virDomainSecLabelDefFree(def); + return -1; +} virDomainDeviceDefPtr virDomainDeviceDefParse(virConnectPtr conn, virCapsPtr caps, @@ -2212,6 +2274,10 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn, } VIR_FREE(nodes); + /* analysis of security label */ + if (virDomainSecLabelDefParseXML(conn, def, ctxt) == -1) + goto error; + return def; no_memory: @@ -3206,6 +3272,13 @@ char *virDomainDefFormat(virConnectPtr conn, goto cleanup; virBufferAddLit(&buf, " </devices>\n"); + + if (def->seclabel.model) { + virBufferEscapeString(&buf, " <seclabel model='%s'>\n", def->seclabel.model); + virBufferEscapeString(&buf, " <label>%s</label>\n", def->seclabel.label); + virBufferAddLit(&buf, " </seclabel>\n"); + } + virBufferAddLit(&buf, "</domain>\n"); if (virBufferError(&buf)) diff --git a/src/domain_conf.h b/src/domain_conf.h index 51cf6d5..aee6459 100644 --- a/src/domain_conf.h +++ b/src/domain_conf.h @@ -391,6 +391,14 @@ struct _virDomainOSDef { char *bootloaderArgs; }; +/* Security label configuration for domain */ +typedef struct _virDomainSecLabelDef virDomainSecLabelDef; +typedef virDomainSecLabelDef *virDomainSecLabelDefPtr; +struct _virDomainSecLabelDef { + char *model; /* name of security labeling model */ + char *label; /* security label string */ +}; + #define VIR_DOMAIN_CPUMASK_LEN 1024 /* Guest VM main configuration */ @@ -448,6 +456,7 @@ struct _virDomainDef { /* Only 1 */ virDomainChrDefPtr console; + virDomainSecLabelDef seclabel; }; /* Guest VM runtime state */ diff --git a/src/driver.h b/src/driver.h index 8c394e2..393621f 100644 --- a/src/driver.h +++ b/src/driver.h @@ -181,6 +181,12 @@ typedef int typedef int (*virDrvDomainGetMaxVcpus) (virDomainPtr domain); typedef int + (*virDrvDomainGetSecLabel) (virDomainPtr domain, + virDomainSecLabelPtr seclabel); +typedef int + (*virDrvDomainGetSecModel) (virDomainPtr domain, + virDomainSecModelPtr secmodel); +typedef int (*virDrvDomainAttachDevice) (virDomainPtr domain, const char *xml); typedef int @@ -361,6 +367,8 @@ struct _virDriver { virDrvDomainPinVcpu domainPinVcpu; virDrvDomainGetVcpus domainGetVcpus; virDrvDomainGetMaxVcpus domainGetMaxVcpus; + virDrvDomainGetSecLabel domainGetSecLabel; + virDrvDomainGetSecModel domainGetSecModel; virDrvDomainDumpXML domainDumpXML; virDrvListDefinedDomains listDefinedDomains; virDrvNumOfDefinedDomains numOfDefinedDomains; diff --git a/src/libvirt.c b/src/libvirt.c index a4a0df5..9b127e3 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -3438,6 +3438,75 @@ virDomainGetMaxVcpus(virDomainPtr domain) return -1; } +/** + * virDomainGetSecLabel: + * @domain: a domain object + * @seclabel: pointer to a virDomainSecLabel structure + * + * Extract security label of an active domain. + * + * Returns 0 in case of success, -1 in case of failure, and -2 + * if the operation is not supported (caller decides if that's + * an error). + */ +int +virDomainGetSecLabel(virDomainPtr domain, virDomainSecLabelPtr seclabel) +{ + virConnectPtr conn; + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return -1; + } + + if (seclabel == NULL) { + virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__); + return -1; + } + + conn = domain->conn; + + if (conn->driver->domainGetSecLabel) + return conn->driver->domainGetSecLabel(domain, seclabel); + + virLibConnWarning(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -2; +} + +/** + * virDomainGetSecModel: + * @domain: a domain object + * @secmodel: pointer to a virDomainSecModel structure + * + * Extract the security model of a hypervisor. + * + * Returns 0 in case of success, -1 in case of failure, and -2 + * if the operation is not supported (caller decides if that's + * an error). + */ +int +virDomainGetSecModel(virDomainPtr domain, virDomainSecModelPtr secmodel) +{ + virConnectPtr conn; + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return -1; + } + + if (secmodel == NULL) { + virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__); + return -1; + } + + conn = domain->conn; + + if (conn->driver->domainGetSecModel) + return conn->driver->domainGetSecModel(domain, secmodel); + + virLibConnWarning(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -2; +} /** * virDomainAttachDevice: diff --git a/src/libvirt_sym.version.in b/src/libvirt_sym.version.in index de0bc4a..f533c17 100644 --- a/src/libvirt_sym.version.in +++ b/src/libvirt_sym.version.in @@ -617,6 +617,9 @@ LIBVIRT_PRIVATE_@VERSION@ { virXPathString; virXMLPropString; + /* WIP */ + virDomainGetSecLabel; + virDomainGetSecModel; /* Finally everything else is totally private */ local: diff --git a/src/lxc_driver.c b/src/lxc_driver.c index 12f6adc..6cd8001 100644 --- a/src/lxc_driver.c +++ b/src/lxc_driver.c @@ -1423,6 +1423,8 @@ static virDriver lxcDriver = { NULL, /* domainPinVcpu */ NULL, /* domainGetVcpus */ NULL, /* domainGetMaxVcpus */ + NULL, /* domainGetSecLabel */ + NULL, /* domainGetSecModel */ lxcDomainDumpXML, /* domainDumpXML */ lxcListDefinedDomains, /* listDefinedDomains */ lxcNumDefinedDomains, /* numOfDefinedDomains */ diff --git a/src/openvz_driver.c b/src/openvz_driver.c index 95ac7dc..bee7471 100644 --- a/src/openvz_driver.c +++ b/src/openvz_driver.c @@ -1279,6 +1279,8 @@ static virDriver openvzDriver = { NULL, /* domainPinVcpu */ NULL, /* domainGetVcpus */ openvzDomainGetMaxVcpus, /* domainGetMaxVcpus */ + NULL, /* domainGetSecLabel */ + NULL, /* domainGetSecModel */ openvzDomainDumpXML, /* domainDumpXML */ openvzListDefinedDomains, /* listDomains */ openvzNumDefinedDomains, /* numOfDomains */ diff --git a/src/qemu_conf.h b/src/qemu_conf.h index 36d09d1..736b21c 100644 --- a/src/qemu_conf.h +++ b/src/qemu_conf.h @@ -32,6 +32,7 @@ #include "network_conf.h" #include "domain_conf.h" #include "domain_event.h" +#include "seclabel.h" #define qemudDebug(fmt, ...) do {} while(0) @@ -74,6 +75,8 @@ struct qemud_driver { virDomainEventQueuePtr domainEventQueue; int domainEventTimer; int domainEventDispatching; + + virSecLabelDriverPtr secLabelDriver; }; /* Port numbers used for KVM migration. */ diff --git a/src/qemu_driver.c b/src/qemu_driver.c index 5f6fbd1..fa04fa3 100644 --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -68,6 +68,7 @@ #include "memory.h" #include "uuid.h" #include "domain_conf.h" +#include "seclabel.h" /* For storing short-lived temporary files. */ #define TEMPDIR LOCAL_STATE_DIR "/cache/libvirt" @@ -182,6 +183,50 @@ qemudAutostartConfigs(struct qemud_driver *driver) { virConnectClose(conn); } +static int +qemudSecLabelInit(struct qemud_driver *qemud_drv) +{ + int ret; + const char *doi, *model; + virCapsPtr caps; + virSecLabelDriverPtr seclabel_drv; + + ret = virSecLabelDriverStartup(&seclabel_drv); + if (ret == -1) { + qemudLog(QEMUD_ERR, _("Failed to start security labeling driver")); + return -1; + } + if (ret == -2) + return 0; + + qemud_drv->secLabelDriver = seclabel_drv; + doi = virSecLabelDriverGetDOI(seclabel_drv); + model = virSecLabelDriverGetModel(seclabel_drv); + + qemudLog(QEMUD_DEBUG, "Initialized security labeling driver \"%s\" with " + "DOI \"%s\".\n", model, doi); + + /* + * Add security policy host caps now that the labeling driver is + * initialized. + */ + caps = qemud_drv->caps; + + caps->host.secModel.model = strdup(model); + if (!caps->host.secModel.model) { + qemudLog(QEMUD_ERR, _("Failed to copy secModel model: %s"), strerror(errno)); + return -1; + } + + caps->host.secModel.doi = strdup(doi); + if (!caps->host.secModel.doi) { + qemudLog(QEMUD_ERR, _("Failed to copy secModel DOI: %s"), strerror(errno)); + return -1; + } + + return 0; +} + /** * qemudStartup: * @@ -253,6 +298,11 @@ qemudStartup(void) { if ((qemu_driver->caps = qemudCapsInit()) == NULL) goto out_of_memory; + if (qemudSecLabelInit(qemu_driver) < 0) { + qemudShutdown(); + return -1; + } + if (qemudLoadDriverConfig(qemu_driver, driverConf) < 0) { goto error; } @@ -824,6 +874,15 @@ static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) { return -1; } +static int qemudDomainSetSecLabel(virConnectPtr conn, struct qemud_driver *driver, virDomainObjPtr vm) +{ + if (vm->def->seclabel.label != NULL) + if (driver->secLabelDriver && driver->secLabelDriver->domainSetLabel) + return driver->secLabelDriver->domainSetLabel(conn, driver->secLabelDriver, + &vm->def->seclabel); + return 0; +} + static virDomainPtr qemudDomainLookupByName(virConnectPtr conn, const char *name); @@ -924,6 +983,16 @@ static int qemudStartVMDaemon(virConnectPtr conn, return -1; } + /* + * Set up the security label for the domain here, before doing + * too much else. + */ + if (qemudDomainSetSecLabel(conn, driver, vm) < 0) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("Failed to set security label")); + return -1; + } + if (qemudExtractVersionInfo(emulator, NULL, &qemuCmdFlags) < 0) { @@ -2453,7 +2522,111 @@ cleanup: return ret; } +static int qemudDomainGetSecLabel(virDomainPtr dom, virDomainSecLabelPtr seclabel) +{ + struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData; + virDomainObjPtr vm; + const char *type; + int ret = -1; + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + qemuDriverUnlock(driver); + + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + virUUIDFormat(dom->uuid, uuidstr); + qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (!(type = virDomainVirtTypeToString(vm->def->virtType))) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, + _("unknown virt type in domain definition '%d'"), + vm->def->virtType); + goto cleanup; + } + + /* + * Theoretically, the pid can be replaced during this operation and + * return the label of a different process. If atomicity is needed, + * further validation will be required. + */ + if (virDomainIsActive(vm)) { + if (driver->secLabelDriver && driver->secLabelDriver->domainGetLabel) { + if (driver->secLabelDriver->domainGetLabel(dom->conn, vm, seclabel) == -1) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, + _("Failed to get security label")); + goto cleanup; + } + } + } + + ret = 0; + +cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} + +static int qemudDomainGetSecModel(virDomainPtr dom, virDomainSecModelPtr secmodel) +{ + struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData; + virDomainObjPtr vm; + const char *type; + char *p; + int ret = -1; + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + qemuDriverUnlock(driver); + + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + virUUIDFormat(dom->uuid, uuidstr); + qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (!(type = virDomainVirtTypeToString(vm->def->virtType))) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, + _("unknown virt type in domain definition '%d'"), + vm->def->virtType); + goto cleanup; + } + + p = driver->caps->host.secModel.model; + if (strlen(p) >= VIR_SECLABEL_MODEL_BUFLEN-1) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, + _("security model string exceeds max %d bytes"), + VIR_SECLABEL_MODEL_BUFLEN-1); + goto cleanup; + } + strcpy(secmodel->model, p); + + p = driver->caps->host.secModel.doi; + if (strlen(p) >= VIR_SECLABEL_DOI_BUFLEN-1) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR, + _("security DOI string exceeds max %d bytes"), + VIR_SECLABEL_DOI_BUFLEN-1); + goto cleanup; + } + strcpy(secmodel->doi, p); + + ret = 0; + +cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} +/* TODO: check seclabel restore */ static int qemudDomainRestore(virConnectPtr conn, const char *path) { struct qemud_driver *driver = conn->privateData; @@ -4203,6 +4376,8 @@ static virDriver qemuDriver = { NULL, /* domainGetVcpus */ #endif qemudDomainGetMaxVcpus, /* domainGetMaxVcpus */ + qemudDomainGetSecLabel, /* domainGetSecLabel */ + qemudDomainGetSecModel, /* domainGetSecModel */ qemudDomainDumpXML, /* domainDumpXML */ qemudListDefinedDomains, /* listDomains */ qemudNumDefinedDomains, /* numOfDomains */ diff --git a/src/remote_internal.c b/src/remote_internal.c index 1d0c5ac..99e2926 100644 --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -1983,6 +1983,70 @@ remoteDomainGetMaxVcpus (virDomainPtr domain) return ret.num; } +static int +remoteDomainGetSecLabel (virDomainPtr domain, virDomainSecLabelPtr seclabel) +{ + remote_domain_get_seclabel_args args; + remote_domain_get_seclabel_ret ret; + GET_PRIVATE (domain->conn, -1); + + make_nonnull_domain (&args.dom, domain); + memset (&ret, 0, sizeof ret); + if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_SECLABEL, + (xdrproc_t) xdr_remote_domain_get_seclabel_args, (char *)&args, + (xdrproc_t) xdr_remote_domain_get_seclabel_ret, (char *)&ret) == -1) { + return -1; + } + + if (ret.label.label_val != NULL) { + if (strlen (ret.label.label_val) >= sizeof seclabel->label) { + errorf (domain->conn, VIR_ERR_RPC, _("security label exceeds maximum: %zd"), + sizeof seclabel->label - 1); + return -1; + } + strcpy (seclabel->label, ret.label.label_val); + seclabel->enforcing = ret.enforcing; + } + + return 0; +} + +static int +remoteDomainGetSecModel (virDomainPtr domain, virDomainSecModelPtr secmodel) +{ + remote_domain_get_secmodel_args args; + remote_domain_get_secmodel_ret ret; + GET_PRIVATE (domain->conn, -1); + + make_nonnull_domain (&args.dom, domain); + memset (&ret, 0, sizeof ret); + if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_SECMODEL, + (xdrproc_t) xdr_remote_domain_get_secmodel_args, (char *)&args, + (xdrproc_t) xdr_remote_domain_get_secmodel_ret, (char *)&ret) == -1) { + return -1; + } + + if (ret.model.model_val != NULL) { + if (strlen (ret.model.model_val) >= sizeof secmodel->model) { + errorf (domain->conn, VIR_ERR_RPC, _("security model exceeds maximum: %zd"), + sizeof secmodel->model - 1); + return -1; + } + strcpy (secmodel->model, ret.model.model_val); + } + + if (ret.doi.doi_val != NULL) { + if (strlen (ret.doi.doi_val) >= sizeof secmodel->doi) { + errorf (domain->conn, VIR_ERR_RPC, _("security doi exceeds maximum: %zd"), + sizeof secmodel->doi - 1); + return -1; + } + strcpy (secmodel->doi, ret.doi.doi_val); + } + + return 0; +} + static char * remoteDomainDumpXML (virDomainPtr domain, int flags) { @@ -5352,6 +5416,8 @@ static virDriver driver = { .domainPinVcpu = remoteDomainPinVcpu, .domainGetVcpus = remoteDomainGetVcpus, .domainGetMaxVcpus = remoteDomainGetMaxVcpus, + .domainGetSecLabel = remoteDomainGetSecLabel, + .domainGetSecModel = remoteDomainGetSecModel, .domainDumpXML = remoteDomainDumpXML, .listDefinedDomains = remoteListDefinedDomains, .numOfDefinedDomains = remoteNumOfDefinedDomains, diff --git a/src/seclabel.c b/src/seclabel.c new file mode 100644 index 0000000..854e81e --- /dev/null +++ b/src/seclabel.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Authors: + * James Morris <jmorris@xxxxxxxxx> + * + */ +#include <config.h> +#include <string.h> + +#include "virterror_internal.h" + +#include "seclabel.h" + +#if HAVE_SELINUX +#include "seclabel_selinux.h" +#endif + +static virSecLabelDriverPtr seclabel_drivers[] = { +#ifdef HAVE_SELINUX + &virSELinuxSecLabelDriver, +#endif +}; + +/* + * Probe each security labeling driver: each should perform a test to see if + * it should be loaded, e.g. if the currently active host security mechanism + * matches. If the probe succeeds, initialize the driver and return it. + * + * Returns 0 on success, -1 on error, and -2 on no driver found (caller + * to determine if that's an error). + */ +int +virSecLabelDriverStartup(virSecLabelDriverPtr * drv) +{ + unsigned int i; + + /* FIXME: gcc warning when no drivers enabled */ + for (i = 0; i < (sizeof(seclabel_drivers) / sizeof(seclabel_drivers[0])); i++) { + virSecLabelDriverPtr tmp = seclabel_drivers[i]; + + if (tmp->probe()) { + virSecLabelDriverInit(tmp); + if (tmp->open(NULL, tmp) == -1) { + return -1; + } else { + *drv = tmp; + break; + } + } else + return -2; + } + return 0; +} + +void +virSecLabelReportError(virConnectPtr conn, int code, const char *fmt, ...) +{ + va_list args; + char errorMessage[1024]; + + if (fmt) { + va_start(args, fmt); + vsnprintf(errorMessage, sizeof(errorMessage) - 1, fmt, args); + va_end(args); + } else + errorMessage[0] = '\0'; + + virRaiseError(conn, NULL, NULL, VIR_FROM_SECLABEL, code, + VIR_ERR_ERROR, NULL, NULL, NULL, -1, -1, "%s", + errorMessage); +} + +/* + * Helpers + */ +void +virSecLabelDriverInit(virSecLabelDriverPtr drv) +{ + memset(&drv->_private, 0, sizeof drv->_private); +} + +int +virSecLabelDriverSetDOI(virConnectPtr conn, + virSecLabelDriverPtr drv, + const char *doi) +{ + if (strlen(doi) >= VIR_SECLABEL_DOI_BUFLEN) { + virSecLabelReportError(conn, VIR_ERR_ERROR, + _("%s: DOI \'%s\' is " + "longer than the maximum allowed length of %d"), + __func__, doi, VIR_SECLABEL_DOI_BUFLEN - 1); + return -1; + } + strcpy(drv->_private.doi, doi); + return 0; +} + +const char * +virSecLabelDriverGetDOI(virSecLabelDriverPtr drv) +{ + return drv->_private.doi; +} + +const char * +virSecLabelDriverGetModel(virSecLabelDriverPtr drv) +{ + return drv->name; +} diff --git a/src/seclabel.h b/src/seclabel.h new file mode 100644 index 0000000..0eead78 --- /dev/null +++ b/src/seclabel.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Authors: + * James Morris <jmorris@xxxxxxxxx> + * + */ +#ifndef __VIR_SECLABEL_H__ +#define __VIR_SECLABEL_H__ + +#include "internal.h" +#include "domain_conf.h" + +typedef struct _virSecLabelDriver virSecLabelDriver; +typedef virSecLabelDriver *virSecLabelDriverPtr; +typedef int (*virSecLabelDriverProbe) (void); +typedef int (*virSecLabelDriverOpen) (virConnectPtr conn, + virSecLabelDriverPtr drv); +typedef int (*virSecLabelDomainGetLabel) (virConnectPtr conn, + virDomainObjPtr vm, + virDomainSecLabelPtr sec); +typedef int (*virSecLabelDomainSetLabel) (virConnectPtr conn, + virSecLabelDriverPtr drv, + virDomainSecLabelDefPtr secdef); + +struct _virSecLabelDriver { + const char *name; + virSecLabelDriverProbe probe; + virSecLabelDriverOpen open; + virSecLabelDomainGetLabel domainGetLabel; + virSecLabelDomainSetLabel domainSetLabel; + + /* + * This is internally managed driver state and should only be accessed + * via helpers below. + */ + struct { + char doi[VIR_SECLABEL_DOI_BUFLEN]; + } _private; +}; + +/* Global methods */ +int virSecLabelDriverStartup(virSecLabelDriverPtr * drv); + +void +virSecLabelReportError(virConnectPtr conn, int code, const char *fmt, ...) + ATTRIBUTE_FORMAT(printf, 3, 4); + +/* Helpers */ +void virSecLabelDriverInit(virSecLabelDriverPtr drv); +int virSecLabelDriverSetDOI(virConnectPtr conn, + virSecLabelDriverPtr drv, + const char *doi); +const char *virSecLabelDriverGetDOI(virSecLabelDriverPtr drv); +const char *virSecLabelDriverGetModel(virSecLabelDriverPtr drv); + +#endif /* __VIR_SECLABEL_H__ */ diff --git a/src/seclabel_selinux.c b/src/seclabel_selinux.c new file mode 100644 index 0000000..f3dbb8d --- /dev/null +++ b/src/seclabel_selinux.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Authors: + * James Morris <jmorris@xxxxxxxxx> + * + * SELinux seclabel driver. + */ +#include <config.h> +#include <selinux/selinux.h> + +#include "seclabel.h" +#include "seclabel_selinux.h" + +#define SECLABEL_SELINUX_VOID_DOI "0" + +static int +SELinuxSecLabelDriverProbe(void) +{ + return is_selinux_enabled(); +} + +static int +SELinuxSecLabelDriverOpen(virConnectPtr conn, virSecLabelDriverPtr drv) +{ + /* + * Where will the DOI come from? SELinux configuration, or qemu + * configuration? For the moment, we'll just set it to "0". + */ + virSecLabelDriverSetDOI(conn, drv, SECLABEL_SELINUX_VOID_DOI); + + return 0; +} + +static int +SELinuxSecLabelDomainGetLabel(virConnectPtr conn, + virDomainObjPtr vm, virDomainSecLabelPtr sec) +{ + security_context_t ctx; + + if (getpidcon(vm->pid, &ctx) == -1) { + virSecLabelReportError(conn, VIR_ERR_ERROR, _("%s: error calling " + "getpidcon(): %s"), __func__, + strerror(errno)); + return -1; + } + + if (strlen((char *) ctx) >= VIR_SECLABEL_LABEL_BUFLEN) { + virSecLabelReportError(conn, VIR_ERR_ERROR, + _("%s: security label exceeds " + "maximum length: %d"), __func__, + VIR_SECLABEL_LABEL_BUFLEN - 1); + return -1; + } + + strcpy(sec->label, (char *) ctx); + free(ctx); + + sec->enforcing = security_getenforce(); + if (sec->enforcing == -1) { + virSecLabelReportError(conn, VIR_ERR_ERROR, _("%s: error calling " + "security_getenforce(): %s"), __func__, + strerror(errno)); + return -1; + } + + return 0; +} + +static int +SELinuxSecLabelDomainSetLabel(virConnectPtr conn, + virSecLabelDriverPtr drv, const virDomainSecLabelDefPtr secdef) +{ + /* TODO: verify DOI */ + + if (!STREQ(drv->name, secdef->model)) { + virSecLabelReportError(conn, VIR_ERR_ERROR, + _("%s: security label driver mismatch: " + "\'%s\' model configured for domain, but " + "hypervisor driver is \'%s\'."), + __func__, secdef->model, drv->name); + return -1; + } + + if (setexeccon(secdef->label) == -1) { + virSecLabelReportError(conn, VIR_ERR_ERROR, + _("%s: unable to set security context " + "'\%s\': %s."), __func__, secdef->label, + strerror(errno)); + return -1; + } + return 0; +} + +virSecLabelDriver virSELinuxSecLabelDriver = { + .name = "selinux", + .probe = SELinuxSecLabelDriverProbe, + .open = SELinuxSecLabelDriverOpen, + .domainGetLabel = SELinuxSecLabelDomainGetLabel, + .domainSetLabel = SELinuxSecLabelDomainSetLabel, +}; diff --git a/src/seclabel_selinux.h b/src/seclabel_selinux.h new file mode 100644 index 0000000..20e575d --- /dev/null +++ b/src/seclabel_selinux.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Authors: + * James Morris <jmorris@xxxxxxxxx> + * + */ +#ifndef __VIR_SECLABEL_SELINUX_H__ +#define __VIR_SECLABEL_SELINUX_H__ + +extern virSecLabelDriver virSELinuxSecLabelDriver; + +#endif /* __VIR_SECLABEL_SELINUX_H__ */ diff --git a/src/storage_backend.c b/src/storage_backend.c index bb24727..227817a 100644 --- a/src/storage_backend.c +++ b/src/storage_backend.c @@ -246,6 +246,7 @@ virStorageBackendUpdateVolInfoFD(virConnectPtr conn, VIR_FREE(vol->target.perms.label); #if HAVE_SELINUX +/* XXX: make this a security driver call */ if (fgetfilecon(fd, &filecon) == -1) { if (errno != ENODATA && errno != ENOTSUP) { virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, diff --git a/src/test.c b/src/test.c index 257fc8a..0e27789 100644 --- a/src/test.c +++ b/src/test.c @@ -3270,6 +3270,8 @@ static virDriver testDriver = { NULL, /* domainPinVcpu */ NULL, /* domainGetVcpus */ NULL, /* domainGetMaxVcpus */ + NULL, /* domainGetSecLabel */ + NULL, /* domainGetSecModel */ testDomainDumpXML, /* domainDumpXML */ testListDefinedDomains, /* listDefinedDomains */ testNumOfDefinedDomains, /* numOfDefinedDomains */ diff --git a/src/uml_driver.c b/src/uml_driver.c index 408096e..eb56687 100644 --- a/src/uml_driver.c +++ b/src/uml_driver.c @@ -1855,6 +1855,8 @@ static virDriver umlDriver = { NULL, /* domainPinVcpu */ NULL, /* domainGetVcpus */ NULL, /* domainGetMaxVcpus */ + NULL, /* domainGetSecLabel */ + NULL, /* domainGetSecModel */ umlDomainDumpXML, /* domainDumpXML */ umlListDefinedDomains, /* listDomains */ umlNumDefinedDomains, /* numOfDomains */ diff --git a/src/virsh.c b/src/virsh.c index 1a5b42f..afadc08 100644 --- a/src/virsh.c +++ b/src/virsh.c @@ -958,6 +958,7 @@ static const vshCmdOptDef opts_undefine[] = { {NULL, 0, 0, NULL} }; +/* XXX MAC policy for defining & undefining domains ?? */ static int cmdUndefine(vshControl *ctl, const vshCmd *cmd) { @@ -1519,6 +1520,8 @@ cmdDominfo(vshControl *ctl, const vshCmd *cmd) { virDomainInfo info; virDomainPtr dom; + virDomainSecModel secmodel; + virDomainSecLabel seclabel; int ret = TRUE, autostart; unsigned int id; char *str, uuid[VIR_UUID_STRING_BUFLEN]; @@ -1544,6 +1547,12 @@ cmdDominfo(vshControl *ctl, const vshCmd *cmd) free(str); } + memset(&secmodel, 0, sizeof secmodel); + if (virDomainGetSecModel(dom, &secmodel) == 0) { + vshPrint(ctl, "%-15s %s\n", _("Security model:"), secmodel.model); + vshPrint(ctl, "%-15s %s\n", _("Security DOI:"), secmodel.doi); + } + if (virDomainGetInfo(dom, &info) == 0) { vshPrint(ctl, "%-15s %s\n", _("State:"), N_(vshDomainStateToString(info.state))); @@ -1577,6 +1586,19 @@ cmdDominfo(vshControl *ctl, const vshCmd *cmd) autostart ? _("enable") : _("disable") ); } + memset(&seclabel, 0, sizeof seclabel); + if (virDomainGetSecLabel(dom, &seclabel) == -1) + ret = FALSE; + else { + /* + * Security labels are only valid for active + * domains. + */ + if (seclabel.label[0] != '\0') + vshPrint(ctl, "%-15s %s (%s)\n", + _("Security label:"), seclabel.label, + seclabel.enforcing ? "enforcing" : "permissive"); + } virDomainFree(dom); return ret; } diff --git a/src/virterror.c b/src/virterror.c index 5a0b827..88b7e18 100644 --- a/src/virterror.c +++ b/src/virterror.c @@ -319,6 +319,9 @@ virDefaultErrorFunc(virErrorPtr err) case VIR_FROM_UML: dom = "UML "; break; + case VIR_FROM_SECLABEL: + dom = "Security Labeling "; + break; } if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) { domain = err->dom->name; @@ -745,6 +748,12 @@ virErrorMsg(virErrorNumber error, const char *info) else errmsg = _("Node device not found: %s"); break; + case VIR_ERR_NO_SECLABEL_MODEL: + if (info == NULL) + errmsg = _("Security labeling model not found"); + else + errmsg = _("Security labeling model not found: %s"); + break; } return (errmsg); } diff --git a/tests/daemon-conf b/tests/daemon-conf index 03189d5..e492846 100755 --- a/tests/daemon-conf +++ b/tests/daemon-conf @@ -59,6 +59,9 @@ while :; do # Filter out this diagnostic. sed '/^Cannot set group when not running as root$/d' err > k && mv k err + # Filter out this diagnostic, too. + sed '/^Initialized security labeling driver/d' err > k && mv k err + printf '%s\n\n' "remoteReadConfigFile: $f: $param_name: $msg" > expected-err diff -u expected-err err || fail=1
-- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list