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; + int enforcing; +}; + +struct remote_domain_get_secmodel_args { + remote_nonnull_domain dom; +}; + +struct remote_domain_get_secmodel_ret { + char model; + char doi; +}; + 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, " \n"); virBufferAddLit(&xml, " \n"); } + + if (caps->host.secModel.model) { + virBufferAddLit(&xml, " \n"); + virBufferVSprintf(&xml, " %s\n", caps->host.secModel.model); + virBufferVSprintf(&xml, " %s\n", caps->host.secModel.doi); + virBufferAddLit(&xml, " \n"); + } + virBufferAddLit(&xml, " \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, " \n"); + + if (def->seclabel.model) { + virBufferEscapeString(&buf, " \n", def->seclabel.model); + virBufferEscapeString(&buf, " \n", def->seclabel.label); + virBufferAddLit(&buf, " \n"); + } + virBufferAddLit(&buf, "\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 + * + */ +#include +#include + +#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 + * + */ +#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 + * + * SELinux seclabel driver. + */ +#include +#include + +#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 + * + */ +#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