Makefile.maint | 1 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 | 70 ++++++++++++++++ qemud/remote_dispatch_args.h | 1 qemud/remote_dispatch_prototypes.h | 14 +++ qemud/remote_dispatch_ret.h | 2 qemud/remote_dispatch_table.h | 10 ++ qemud/remote_protocol.c | 37 ++++++++ qemud/remote_protocol.h | 37 ++++++++ qemud/remote_protocol.x | 34 +++++++ src/Makefile.am | 26 +++++- src/capabilities.c | 10 ++ src/capabilities.h | 7 + src/domain_conf.c | 51 +++++++++++ src/domain_conf.h | 9 ++ src/driver.h | 8 + src/libvirt.c | 64 ++++++++++++++ src/libvirt_sym.version.in | 4 src/lxc_driver.c | 2 src/openvz_driver.c | 2 src/qemu_conf.h | 3 src/qemu_driver.c | 159 +++++++++++++++++++++++++++++++++++++ src/remote_internal.c | 63 ++++++++++++++ src/security.c | 133 ++++++++++++++++++++++++++++++ src/security.h | 72 ++++++++++++++++ src/security_selinux.c | 108 +++++++++++++++++++++++++ src/security_selinux.h | 18 ++++ src/storage_backend.c | 1 src/test.c | 2 src/uml_driver.c | 2 src/virsh.c | 26 ++++++ src/virterror.c | 9 ++ src/xml.c | 33 +++++++ src/xml.h | 4 tests/daemon-conf | 3 40 files changed, 1162 insertions(+), 4 deletions(-) diff --git a/Makefile.maint b/Makefile.maint index fda15db..156065f 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 += virSecurityReportError msg_gen_function += virReportErrorHelper msg_gen_function += lxcError msg_gen_function += umlError diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h index 97b4489..70f2657 100644 --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -111,6 +111,68 @@ typedef enum { } virDomainCreateFlags; /** + * VIR_SECURITY_LABEL_BUFLEN: + * + * Macro providing the maximum length of the virSecurityLabel label string. + * Note that this value is based on that used by Labeled NFS. + */ +#define VIR_SECURITY_LABEL_BUFLEN (4096 + 1) + +/** + * virSecurityLabel: + * + * a virSecurityLabel is a structure filled by virDomainGetSecurityLabel(), + * providing the security label and associated attributes for the specified + * domain. + * + */ +typedef struct _virSecurityLabel { + char label[VIR_SECURITY_LABEL_BUFLEN]; /* security label string */ + int enforcing; /* 1 if security policy is being enforced for domain */ +} virSecurityLabel; + +/** + * virSecurityLabelPtr: + * + * a virSecurityLabelPtr is a pointer to a virSecurityLabel. + */ +typedef virSecurityLabel *virSecurityLabelPtr; + +/** + * VIR_SECURITY_MODEL_BUFLEN: + * + * Macro providing the maximum length of the virSecurityModel model string. + */ +#define VIR_SECURITY_MODEL_BUFLEN (256 + 1) + +/** + * VIR_SECURITY_DOI_BUFLEN: + * + * Macro providing the maximum length of the virSecurityModel doi string. + */ +#define VIR_SECURITY_DOI_BUFLEN (256 + 1) + +/** + * virSecurityModel: + * + * a virSecurityModel is a structure filled by virNodeGetSecurityModel(), + * providing the per-hypervisor security model and DOI attributes for the + * specified domain. + * + */ +typedef struct _virSecurityModel { + char model[VIR_SECURITY_MODEL_BUFLEN]; /* security model string */ + char doi[VIR_SECURITY_DOI_BUFLEN]; /* domain of interpetation */ +} virSecurityModel; + +/** + * virSecurityModelPtr: + * + * a virSecurityModelPtr is a pointer to a virSecurityModel. + */ +typedef virSecurityModel *virSecurityModelPtr; + +/** * virNodeInfoPtr: * * a virNodeInfo is a structure filled by virNodeGetInfo() and providing @@ -416,6 +478,9 @@ char * virConnectGetCapabilities (virConnectPtr conn); unsigned long long virNodeGetFreeMemory (virConnectPtr conn); +int virNodeGetSecurityModel (virConnectPtr conn, + virSecurityModelPtr secmodel); + /* * Gather list of running domains */ @@ -504,6 +569,8 @@ int virDomainSetMaxMemory (virDomainPtr domain, int virDomainSetMemory (virDomainPtr domain, unsigned long memory); int virDomainGetMaxVcpus (virDomainPtr domain); +int virDomainGetSecurityLabel (virDomainPtr domain, + virSecurityLabelPtr seclabel); /* * XML domain description diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index fc322e6..ab50327 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -111,6 +111,68 @@ typedef enum { } virDomainCreateFlags; /** + * VIR_SECURITY_LABEL_BUFLEN: + * + * Macro providing the maximum length of the virSecurityLabel label string. + * Note that this value is based on that used by Labeled NFS. + */ +#define VIR_SECURITY_LABEL_BUFLEN (4096 + 1) + +/** + * virSecurityLabel: + * + * a virSecurityLabel is a structure filled by virDomainGetSecurityLabel(), + * providing the security label and associated attributes for the specified + * domain. + * + */ +typedef struct _virSecurityLabel { + char label[VIR_SECURITY_LABEL_BUFLEN]; /* security label string */ + int enforcing; /* 1 if security policy is being enforced for domain */ +} virSecurityLabel; + +/** + * virSecurityLabelPtr: + * + * a virSecurityLabelPtr is a pointer to a virSecurityLabel. + */ +typedef virSecurityLabel *virSecurityLabelPtr; + +/** + * VIR_SECURITY_MODEL_BUFLEN: + * + * Macro providing the maximum length of the virSecurityModel model string. + */ +#define VIR_SECURITY_MODEL_BUFLEN (256 + 1) + +/** + * VIR_SECURITY_DOI_BUFLEN: + * + * Macro providing the maximum length of the virSecurityModel doi string. + */ +#define VIR_SECURITY_DOI_BUFLEN (256 + 1) + +/** + * virSecurityModel: + * + * a virSecurityModel is a structure filled by virNodeGetSecurityModel(), + * providing the per-hypervisor security model and DOI attributes for the + * specified domain. + * + */ +typedef struct _virSecurityModel { + char model[VIR_SECURITY_MODEL_BUFLEN]; /* security model string */ + char doi[VIR_SECURITY_DOI_BUFLEN]; /* domain of interpetation */ +} virSecurityModel; + +/** + * virSecurityModelPtr: + * + * a virSecurityModelPtr is a pointer to a virSecurityModel. + */ +typedef virSecurityModel *virSecurityModelPtr; + +/** * virNodeInfoPtr: * * a virNodeInfo is a structure filled by virNodeGetInfo() and providing @@ -416,6 +478,9 @@ char * virConnectGetCapabilities (virConnectPtr conn); unsigned long long virNodeGetFreeMemory (virConnectPtr conn); +int virNodeGetSecurityModel (virConnectPtr conn, + virSecurityModelPtr secmodel); + /* * Gather list of running domains */ @@ -504,6 +569,8 @@ int virDomainSetMaxMemory (virDomainPtr domain, int virDomainSetMemory (virDomainPtr domain, unsigned long memory); int virDomainGetMaxVcpus (virDomainPtr domain); +int virDomainGetSecurityLabel (virDomainPtr domain, + virSecurityLabelPtr seclabel); /* * XML domain description diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 0f1f08c..5b94eb9 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_SECURITY, /* Error from security 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_SECURITY_MODEL, /* security model not found */ } virErrorNumber; /** diff --git a/po/POTFILES.in b/po/POTFILES.in index 26617a0..c63ab68 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/security.c +src/security_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..254193b 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 + 'virDomainGetSecurityLabel', # Needs investigation... + 'virNodeGetSecurityModel', # Needs investigation... 'virConnectDomainEventRegister', # overridden in virConnect.py 'virConnectDomainEventDeregister', # overridden in virConnect.py ) diff --git a/qemud/Makefile.am b/qemud/Makefile.am index b8dae88..9216db5 100644 --- a/qemud/Makefile.am +++ b/qemud/Makefile.am @@ -121,6 +121,7 @@ libvirtd_LDADD += ../src/libvirt_driver_nodedev.la endif endif +libvirtd_LDADD += ../src/libvirt_driver_security.la libvirtd_LDADD += ../src/libvirt.la if HAVE_POLKIT diff --git a/qemud/remote.c b/qemud/remote.c index 0488e6c..7849c8b 100644 --- a/qemud/remote.c +++ b/qemud/remote.c @@ -1319,6 +1319,76 @@ remoteDispatchDomainGetMaxVcpus (struct qemud_server *server ATTRIBUTE_UNUSED, } static int +remoteDispatchDomainGetSecurityLabel(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_error *rerr, + remote_domain_get_security_label_args *args, + remote_domain_get_security_label_ret *ret) +{ + virDomainPtr dom; + virSecurityLabel seclabel; + + dom = get_nonnull_domain(conn, args->dom); + if (dom == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + memset(&seclabel, 0, sizeof seclabel); + if (virDomainGetSecurityLabel(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); + remoteDispatchOOMError(rerr); + return -1; + } + strcpy(ret->label.label_val, seclabel.label); + ret->enforcing = seclabel.enforcing; + virDomainFree(dom); + + return 0; +} + +static int +remoteDispatchNodeGetSecurityModel(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_error *rerr, + void *args ATTRIBUTE_UNUSED, + remote_node_get_security_model_ret *ret) +{ + virSecurityModel secmodel; + + memset(&secmodel, 0, sizeof secmodel); + if (virNodeGetSecurityModel(conn, &secmodel) == -1) { + 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) { + remoteDispatchOOMError(rerr); + return -1; + } + 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) { + remoteDispatchOOMError(rerr); + return -1; + } + strcpy(ret->doi.doi_val, secmodel.doi); + + 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..dce14f1 100644 --- a/qemud/remote_dispatch_args.h +++ b/qemud/remote_dispatch_args.h @@ -99,3 +99,4 @@ 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_security_label_args val_remote_domain_get_security_label_args; diff --git a/qemud/remote_dispatch_prototypes.h b/qemud/remote_dispatch_prototypes.h index 3ffb164..bc8a899 100644 --- a/qemud/remote_dispatch_prototypes.h +++ b/qemud/remote_dispatch_prototypes.h @@ -184,6 +184,13 @@ static int remoteDispatchDomainGetSchedulerType( remote_error *err, remote_domain_get_scheduler_type_args *args, remote_domain_get_scheduler_type_ret *ret); +static int remoteDispatchDomainGetSecurityLabel( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_error *err, + remote_domain_get_security_label_args *args, + remote_domain_get_security_label_ret *ret); static int remoteDispatchDomainGetVcpus( struct qemud_server *server, struct qemud_client *client, @@ -576,6 +583,13 @@ static int remoteDispatchNodeGetInfo( remote_error *err, void *args, remote_node_get_info_ret *ret); +static int remoteDispatchNodeGetSecurityModel( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_error *err, + void *args, + remote_node_get_security_model_ret *ret); static int remoteDispatchNodeListDevices( struct qemud_server *server, struct qemud_client *client, diff --git a/qemud/remote_dispatch_ret.h b/qemud/remote_dispatch_ret.h index 563167f..136b1cc 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_security_label_ret val_remote_domain_get_security_label_ret; + remote_node_get_security_model_ret val_remote_node_get_security_model_ret; diff --git a/qemud/remote_dispatch_table.h b/qemud/remote_dispatch_table.h index 60f0e1c..6b7c9db 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, }, +{ /* DomainGetSecurityLabel => 118 */ + .fn = (dispatch_fn) remoteDispatchDomainGetSecurityLabel, + .args_filter = (xdrproc_t) xdr_remote_domain_get_security_label_args, + .ret_filter = (xdrproc_t) xdr_remote_domain_get_security_label_ret, +}, +{ /* NodeGetSecurityModel => 119 */ + .fn = (dispatch_fn) remoteDispatchNodeGetSecurityModel, + .args_filter = (xdrproc_t) xdr_void, + .ret_filter = (xdrproc_t) xdr_remote_node_get_security_model_ret, +}, diff --git a/qemud/remote_protocol.c b/qemud/remote_protocol.c index ec8e653..37b62dd 100644 --- a/qemud/remote_protocol.c +++ b/qemud/remote_protocol.c @@ -1166,6 +1166,43 @@ xdr_remote_domain_get_max_vcpus_ret (XDR *xdrs, remote_domain_get_max_vcpus_ret } bool_t +xdr_remote_domain_get_security_label_args (XDR *xdrs, remote_domain_get_security_label_args *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_get_security_label_ret (XDR *xdrs, remote_domain_get_security_label_ret *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->label.label_val; + + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->label.label_len, REMOTE_SECURITY_LABEL_MAX, + sizeof (char), (xdrproc_t) xdr_char)) + return FALSE; + if (!xdr_int (xdrs, &objp->enforcing)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_node_get_security_model_ret (XDR *xdrs, remote_node_get_security_model_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_SECURITY_MODEL_MAX, + sizeof (char), (xdrproc_t) xdr_char)) + return FALSE; + if (!xdr_array (xdrs, objp_cpp1, (u_int *) &objp->doi.doi_len, REMOTE_SECURITY_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..921f4a7 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_SECURITY_MODEL_MAX VIR_SECURITY_MODEL_BUFLEN +#define REMOTE_SECURITY_LABEL_MAX VIR_SECURITY_LABEL_BUFLEN +#define REMOTE_SECURITY_DOI_MAX VIR_SECURITY_DOI_BUFLEN typedef char remote_uuid[VIR_UUID_BUFLEN]; @@ -638,6 +641,32 @@ 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_security_label_args { + remote_nonnull_domain dom; +}; +typedef struct remote_domain_get_security_label_args remote_domain_get_security_label_args; + +struct remote_domain_get_security_label_ret { + struct { + u_int label_len; + char *label_val; + } label; + int enforcing; +}; +typedef struct remote_domain_get_security_label_ret remote_domain_get_security_label_ret; + +struct remote_node_get_security_model_ret { + struct { + u_int model_len; + char *model_val; + } model; + struct { + u_int doi_len; + char *doi_val; + } doi; +}; +typedef struct remote_node_get_security_model_ret remote_node_get_security_model_ret; + struct remote_domain_attach_device_args { remote_nonnull_domain dom; remote_nonnull_string xml; @@ -1349,6 +1378,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_SECURITY_LABEL = 118, + REMOTE_PROC_NODE_GET_SECURITY_MODEL = 119, }; typedef enum remote_procedure remote_procedure; @@ -1475,6 +1506,9 @@ 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_security_label_args (XDR *, remote_domain_get_security_label_args*); +extern bool_t xdr_remote_domain_get_security_label_ret (XDR *, remote_domain_get_security_label_ret*); +extern bool_t xdr_remote_node_get_security_model_ret (XDR *, remote_node_get_security_model_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 +1714,9 @@ 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_security_label_args (); +extern bool_t xdr_remote_domain_get_security_label_ret (); +extern bool_t xdr_remote_node_get_security_model_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..50ac063 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 model field. + */ +const REMOTE_SECURITY_MODEL_MAX = VIR_SECURITY_MODEL_BUFLEN; + +/* + * Maximum length of a security label field. + */ +const REMOTE_SECURITY_LABEL_MAX = VIR_SECURITY_LABEL_BUFLEN; + +/* + * Maximum length of a security DOI field. + */ +const REMOTE_SECURITY_DOI_MAX = VIR_SECURITY_DOI_BUFLEN; + /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -618,6 +633,20 @@ struct remote_domain_get_max_vcpus_ret { int num; }; +struct remote_domain_get_security_label_args { + remote_nonnull_domain dom; +}; + +struct remote_domain_get_security_label_ret { + char label; + int enforcing; +}; + +struct remote_node_get_security_model_ret { + char model; + char doi; +}; + struct remote_domain_attach_device_args { remote_nonnull_domain dom; remote_nonnull_string xml; @@ -1224,7 +1253,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_SECURITY_LABEL = 118, + REMOTE_PROC_NODE_GET_SECURITY_MODEL = 119 }; /* Custom RPC structure. */ diff --git a/src/Makefile.am b/src/Makefile.am index c32a1d4..97c1200 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 +SECURITY_DRIVER_SOURCES = \ + security.h security.c + +SECURITY_DRIVER_SELINUX_SOURCES = \ + security_selinux.h security_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_security_la_SOURCES = $(SECURITY_DRIVER_SOURCES) +if WITH_DRIVER_MODULES +mod_LTLIBRARIES += libvirt_driver_security.la +else +noinst_LTLIBRARIES += libvirt_driver_security.la +endif +if WITH_DRIVER_MODULES +libvirt_driver_security_la_LDFLAGS = -module -avoid-version +endif + +if HAVE_SELINUX +libvirt_driver_security_la_SOURCES += $(SECURITY_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) \ + $(SECURITY_DRIVER_SOURCES) \ + $(SECURITY_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 485ffb2..f007967 100644 --- a/src/domain_conf.c +++ b/src/domain_conf.c @@ -359,6 +359,16 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def) VIR_FREE(def); } +void virSecurityLabelDefFree(virDomainDefPtr def); + +void virSecurityLabelDefFree(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); + virSecurityLabelDefFree(def); + VIR_FREE(def); } @@ -1660,6 +1672,34 @@ static int virDomainLifecycleParseXML(virConnectPtr conn, return 0; } +static int +virSecurityLabelDefParseXML(virConnectPtr conn, + const virDomainDefPtr def, + xmlXPathContextPtr ctxt) +{ + char *p; + + if (virXPathNode(conn, "./seclabel", ctxt) == NULL) + return 0; + + p = virXPathStringLimit(conn, "string(./seclabel/label[1])", + VIR_SECURITY_LABEL_BUFLEN-1, ctxt); + if (p == NULL) + goto error; + def->seclabel.label = p; + + p = virXPathStringLimit(conn, "string(./seclabel/@model)", + VIR_SECURITY_MODEL_BUFLEN-1, ctxt); + if (p == NULL) + goto error; + def->seclabel.model = p; + + return 0; + +error: + virSecurityLabelDefFree(def); + return -1; +} virDomainDeviceDefPtr virDomainDeviceDefParse(virConnectPtr conn, virCapsPtr caps, @@ -2228,6 +2268,10 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn, } VIR_FREE(nodes); + /* analysis of security label */ + if (virSecurityLabelDefParseXML(conn, def, ctxt) == -1) + goto error; + return def; no_memory: @@ -3225,6 +3269,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 d6029ee..3419e50 100644 --- a/src/domain_conf.h +++ b/src/domain_conf.h @@ -392,6 +392,14 @@ struct _virDomainOSDef { char *bootloaderArgs; }; +/* Security configuration for domain */ +typedef struct _virSecurityLabelDef virSecurityLabelDef; +typedef virSecurityLabelDef *virSecurityLabelDefPtr; +struct _virSecurityLabelDef { + char *model; /* name of security model */ + char *label; /* security label string */ +}; + #define VIR_DOMAIN_CPUMASK_LEN 1024 /* Guest VM main configuration */ @@ -449,6 +457,7 @@ struct _virDomainDef { /* Only 1 */ virDomainChrDefPtr console; + virSecurityLabelDef seclabel; }; /* Guest VM runtime state */ diff --git a/src/driver.h b/src/driver.h index 8c394e2..ab6dddb 100644 --- a/src/driver.h +++ b/src/driver.h @@ -181,6 +181,12 @@ typedef int typedef int (*virDrvDomainGetMaxVcpus) (virDomainPtr domain); typedef int + (*virDrvDomainGetSecurityLabel) (virDomainPtr domain, + virSecurityLabelPtr seclabel); +typedef int + (*virDrvNodeGetSecurityModel) (virConnectPtr conn, + virSecurityModelPtr secmodel); +typedef int (*virDrvDomainAttachDevice) (virDomainPtr domain, const char *xml); typedef int @@ -361,6 +367,8 @@ struct _virDriver { virDrvDomainPinVcpu domainPinVcpu; virDrvDomainGetVcpus domainGetVcpus; virDrvDomainGetMaxVcpus domainGetMaxVcpus; + virDrvDomainGetSecurityLabel domainGetSecurityLabel; + virDrvNodeGetSecurityModel nodeGetSecurityModel; virDrvDomainDumpXML domainDumpXML; virDrvListDefinedDomains listDefinedDomains; virDrvNumOfDefinedDomains numOfDefinedDomains; diff --git a/src/libvirt.c b/src/libvirt.c index a4a0df5..ca881d8 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -3438,6 +3438,70 @@ virDomainGetMaxVcpus(virDomainPtr domain) return -1; } +/** + * virDomainGetSecurityLabel: + * @domain: a domain object + * @seclabel: pointer to a virSecurityLabel 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 +virDomainGetSecurityLabel(virDomainPtr domain, virSecurityLabelPtr 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->domainGetSecurityLabel) + return conn->driver->domainGetSecurityLabel(domain, seclabel); + + virLibConnWarning(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -2; +} + +/** + * virNodeGetSecurityModel: + * @conn: a connection object + * @secmodel: pointer to a virSecurityModel 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 +virNodeGetSecurityModel(virConnectPtr conn, virSecurityModelPtr secmodel) +{ + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return -1; + } + + if (secmodel == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return -1; + } + + if (conn->driver->nodeGetSecurityModel) + return conn->driver->nodeGetSecurityModel(conn, 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..c100a82 100644 --- a/src/libvirt_sym.version.in +++ b/src/libvirt_sym.version.in @@ -616,7 +616,11 @@ LIBVIRT_PRIVATE_@VERSION@ { virXPathNodeSet; virXPathString; virXMLPropString; + virXPathStringLimit; + /* TODO: move to public API before merge */ + virDomainGetSecurityLabel; + virNodeGetSecurityModel; /* Finally everything else is totally private */ local: diff --git a/src/lxc_driver.c b/src/lxc_driver.c index 12f6adc..e00aa1a 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, /* domainGetSecurityLabel */ + NULL, /* nodeGetSecurityModel */ lxcDomainDumpXML, /* domainDumpXML */ lxcListDefinedDomains, /* listDefinedDomains */ lxcNumDefinedDomains, /* numOfDefinedDomains */ diff --git a/src/openvz_driver.c b/src/openvz_driver.c index 95ac7dc..cccf1c6 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, /* domainGetSecurityLabel */ + NULL, /* nodeGetSecurityModel */ openvzDomainDumpXML, /* domainDumpXML */ openvzListDefinedDomains, /* listDomains */ openvzNumDefinedDomains, /* numOfDomains */ diff --git a/src/qemu_conf.h b/src/qemu_conf.h index 36d09d1..991253a 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 "security.h" #define qemudDebug(fmt, ...) do {} while(0) @@ -74,6 +75,8 @@ struct qemud_driver { virDomainEventQueuePtr domainEventQueue; int domainEventTimer; int domainEventDispatching; + + virSecurityDriverPtr securityDriver; }; /* Port numbers used for KVM migration. */ diff --git a/src/qemu_driver.c b/src/qemu_driver.c index 5f6fbd1..e282654 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 "security.h" /* For storing short-lived temporary files. */ #define TEMPDIR LOCAL_STATE_DIR "/cache/libvirt" @@ -182,6 +183,51 @@ qemudAutostartConfigs(struct qemud_driver *driver) { virConnectClose(conn); } +static int +qemudSecurityInit(struct qemud_driver *qemud_drv) +{ + int ret; + const char *doi, *model; + virCapsPtr caps; + virSecurityDriverPtr security_drv; + + ret = virSecurityDriverStartup(&security_drv); + if (ret == -1) { + qemudLog(QEMUD_ERR, _("Failed to start security driver")); + return -1; + } + /* No security driver wanted to be enabled: just return */ + if (ret == -2) + return 0; + + qemud_drv->securityDriver = security_drv; + doi = virSecurityDriverGetDOI(security_drv); + model = virSecurityDriverGetModel(security_drv); + + qemudLog(QEMUD_DEBUG, "Initialized security driver \"%s\" with " + "DOI \"%s\".\n", model, doi); + + /* + * Add security policy host caps now that the security 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 +299,11 @@ qemudStartup(void) { if ((qemu_driver->caps = qemudCapsInit()) == NULL) goto out_of_memory; + if (qemudSecurityInit(qemu_driver) < 0) { + qemudShutdown(); + return -1; + } + if (qemudLoadDriverConfig(qemu_driver, driverConf) < 0) { goto error; } @@ -824,6 +875,15 @@ static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) { return -1; } +static int qemudDomainSetSecurityLabel(virConnectPtr conn, struct qemud_driver *driver, virDomainObjPtr vm) +{ + if (vm->def->seclabel.label != NULL) + if (driver->securityDriver && driver->securityDriver->domainSetSecurityLabel) + return driver->securityDriver->domainSetSecurityLabel(conn, driver->securityDriver, + &vm->def->seclabel); + return 0; +} + static virDomainPtr qemudDomainLookupByName(virConnectPtr conn, const char *name); @@ -924,6 +984,16 @@ static int qemudStartVMDaemon(virConnectPtr conn, return -1; } + /* + * Set up the security label for the domain here, before doing + * too much else. + */ + if (qemudDomainSetSecurityLabel(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 +2523,94 @@ cleanup: return ret; } +static int qemudDomainGetSecurityLabel(virDomainPtr dom, virSecurityLabelPtr 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. + * + * Comment from Dan Berrange: + * + * Well the PID as stored in the virDomainObjPtr can't be changed + * because you've got a locked object. The OS level PID could have + * exited, though and in extreme circumstances have cycled through all + * PIDs back to ours. We could sanity check that our PID still exists + * after reading the label, by checking that our FD connecting to the + * QEMU monitor hasn't seen SIGHUP/ERR on poll(). + */ + if (virDomainIsActive(vm)) { + if (driver->securityDriver && driver->securityDriver->domainGetSecurityLabel) { + if (driver->securityDriver->domainGetSecurityLabel(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 qemudNodeGetSecurityModel(virConnectPtr conn, virSecurityModelPtr secmodel) +{ + struct qemud_driver *driver = (struct qemud_driver *)conn->privateData; + char *p; + + if (!driver->securityDriver) + return -2; + + p = driver->caps->host.secModel.model; + if (strlen(p) >= VIR_SECURITY_MODEL_BUFLEN-1) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("security model string exceeds max %d bytes"), + VIR_SECURITY_MODEL_BUFLEN-1); + return -1; + } + strcpy(secmodel->model, p); + + p = driver->caps->host.secModel.doi; + if (strlen(p) >= VIR_SECURITY_DOI_BUFLEN-1) { + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, + _("security DOI string exceeds max %d bytes"), + VIR_SECURITY_DOI_BUFLEN-1); + return -1; + } + strcpy(secmodel->doi, p); + return 0; +} +/* TODO: check seclabel restore */ static int qemudDomainRestore(virConnectPtr conn, const char *path) { struct qemud_driver *driver = conn->privateData; @@ -4203,6 +4360,8 @@ static virDriver qemuDriver = { NULL, /* domainGetVcpus */ #endif qemudDomainGetMaxVcpus, /* domainGetMaxVcpus */ + qemudDomainGetSecurityLabel, /* domainGetSecurityLabel */ + qemudNodeGetSecurityModel, /* nodeGetSecurityModel */ qemudDomainDumpXML, /* domainDumpXML */ qemudListDefinedDomains, /* listDomains */ qemudNumDefinedDomains, /* numOfDomains */ diff --git a/src/remote_internal.c b/src/remote_internal.c index 1d0c5ac..0cbede8 100644 --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -1983,6 +1983,67 @@ remoteDomainGetMaxVcpus (virDomainPtr domain) return ret.num; } +static int +remoteDomainGetSecurityLabel (virDomainPtr domain, virSecurityLabelPtr seclabel) +{ + remote_domain_get_security_label_args args; + remote_domain_get_security_label_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_SECURITY_LABEL, + (xdrproc_t) xdr_remote_domain_get_security_label_args, (char *)&args, + (xdrproc_t) xdr_remote_domain_get_security_label_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 +remoteNodeGetSecurityModel (virConnectPtr conn, virSecurityModelPtr secmodel) +{ + remote_node_get_security_model_ret ret; + GET_PRIVATE (conn, -1); + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_NODE_GET_SECURITY_MODEL, + (xdrproc_t) xdr_void, NULL, + (xdrproc_t) xdr_remote_node_get_security_model_ret, (char *)&ret) == -1) { + return -1; + } + + if (ret.model.model_val != NULL) { + if (strlen (ret.model.model_val) >= sizeof secmodel->model) { + errorf (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 (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 +5413,8 @@ static virDriver driver = { .domainPinVcpu = remoteDomainPinVcpu, .domainGetVcpus = remoteDomainGetVcpus, .domainGetMaxVcpus = remoteDomainGetMaxVcpus, + .domainGetSecurityLabel = remoteDomainGetSecurityLabel, + .nodeGetSecurityModel = remoteNodeGetSecurityModel, .domainDumpXML = remoteDomainDumpXML, .listDefinedDomains = remoteListDefinedDomains, .numOfDefinedDomains = remoteNumOfDefinedDomains, diff --git a/src/security.c b/src/security.c new file mode 100644 index 0000000..8dd2c9f --- /dev/null +++ b/src/security.c @@ -0,0 +1,133 @@ +/* + * 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 "security.h" + +#if HAVE_SELINUX +#include "security_selinux.h" +#endif + +static virSecurityDriverStatus testSecurityDriverProbe(void) +{ + return SECURITY_DRIVER_DISABLE; +} + +virSecurityDriver virTestSecurityDriver = { + .name = "test", + .probe = testSecurityDriverProbe, +}; + +static virSecurityDriverPtr security_drivers[] = { + &virTestSecurityDriver, +#ifdef HAVE_SELINUX + &virSELinuxSecurityDriver, +#endif +}; + +/* + * Probe each security 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, and -1 on error. If no security driver wanted to + * be enabled, then return -2 and let the caller determine what this really + * means. + */ +int +virSecurityDriverStartup(virSecurityDriverPtr * drv) +{ + unsigned int i; + + for (i = 0; i < (sizeof(security_drivers) / sizeof(security_drivers[0])); i++) { + virSecurityDriverPtr tmp = security_drivers[i]; + virSecurityDriverStatus ret = tmp->probe(); + + switch (ret) { + case SECURITY_DRIVER_ENABLE: + virSecurityDriverInit(tmp); + if (tmp->open(NULL, tmp) == -1) { + return -1; + } else { + *drv = tmp; + return 0; + } + break; + + case SECURITY_DRIVER_DISABLE: + break; + + default: + return -1; + } + } + return -2; +} + +void +virSecurityReportError(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_SECURITY, code, + VIR_ERR_ERROR, NULL, NULL, NULL, -1, -1, "%s", + errorMessage); +} + +/* + * Helpers + */ +void +virSecurityDriverInit(virSecurityDriverPtr drv) +{ + memset(&drv->_private, 0, sizeof drv->_private); +} + +int +virSecurityDriverSetDOI(virConnectPtr conn, + virSecurityDriverPtr drv, + const char *doi) +{ + if (strlen(doi) >= VIR_SECURITY_DOI_BUFLEN) { + virSecurityReportError(conn, VIR_ERR_ERROR, + _("%s: DOI \'%s\' is " + "longer than the maximum allowed length of %d"), + __func__, doi, VIR_SECURITY_DOI_BUFLEN - 1); + return -1; + } + strcpy(drv->_private.doi, doi); + return 0; +} + +const char * +virSecurityDriverGetDOI(virSecurityDriverPtr drv) +{ + return drv->_private.doi; +} + +const char * +virSecurityDriverGetModel(virSecurityDriverPtr drv) +{ + return drv->name; +} diff --git a/src/security.h b/src/security.h new file mode 100644 index 0000000..2ea9013 --- /dev/null +++ b/src/security.h @@ -0,0 +1,72 @@ +/* + * 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_SECURITY_H__ +#define __VIR_SECURITY_H__ + +#include "internal.h" +#include "domain_conf.h" + +/* + * Return values for security driver probing: the driver will determine + * whether it should be enabled or disabled. + */ +typedef enum { + SECURITY_DRIVER_ENABLE = 0, + SECURITY_DRIVER_ERROR = -1, + SECURITY_DRIVER_DISABLE = -2, +} virSecurityDriverStatus; + +typedef struct _virSecurityDriver virSecurityDriver; +typedef virSecurityDriver *virSecurityDriverPtr; +typedef virSecurityDriverStatus (*virSecurityDriverProbe) (void); +typedef int (*virSecurityDriverOpen) (virConnectPtr conn, + virSecurityDriverPtr drv); +typedef int (*virSecurityDomainGetLabel) (virConnectPtr conn, + virDomainObjPtr vm, + virSecurityLabelPtr sec); +typedef int (*virSecurityDomainSetLabel) (virConnectPtr conn, + virSecurityDriverPtr drv, + virSecurityLabelDefPtr secdef); + +struct _virSecurityDriver { + const char *name; + virSecurityDriverProbe probe; + virSecurityDriverOpen open; + virSecurityDomainGetLabel domainGetSecurityLabel; + virSecurityDomainSetLabel domainSetSecurityLabel; + + /* + * This is internally managed driver state and should only be accessed + * via helpers below. + */ + struct { + char doi[VIR_SECURITY_DOI_BUFLEN]; + } _private; +}; + +/* Global methods */ +int virSecurityDriverStartup(virSecurityDriverPtr * drv); + +void +virSecurityReportError(virConnectPtr conn, int code, const char *fmt, ...) + ATTRIBUTE_FORMAT(printf, 3, 4); + +/* Helpers */ +void virSecurityDriverInit(virSecurityDriverPtr drv); +int virSecurityDriverSetDOI(virConnectPtr conn, + virSecurityDriverPtr drv, + const char *doi); +const char *virSecurityDriverGetDOI(virSecurityDriverPtr drv); +const char *virSecurityDriverGetModel(virSecurityDriverPtr drv); + +#endif /* __VIR_SECURITY_H__ */ diff --git a/src/security_selinux.c b/src/security_selinux.c new file mode 100644 index 0000000..65fcde7 --- /dev/null +++ b/src/security_selinux.c @@ -0,0 +1,108 @@ +/* + * 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 security driver. + */ +#include +#include + +#include "security.h" +#include "security_selinux.h" + +#define SECURITY_SELINUX_VOID_DOI "0" + +static int +SELinuxSecurityDriverProbe(void) +{ + return is_selinux_enabled() ? SECURITY_DRIVER_ENABLE : SECURITY_DRIVER_DISABLE; +} + +static int +SELinuxSecurityDriverOpen(virConnectPtr conn, virSecurityDriverPtr drv) +{ + /* + * Where will the DOI come from? SELinux configuration, or qemu + * configuration? For the moment, we'll just set it to "0". + */ + virSecurityDriverSetDOI(conn, drv, SECURITY_SELINUX_VOID_DOI); + + return 0; +} + +static int +SELinuxSecurityDomainGetSecurityLabel(virConnectPtr conn, + virDomainObjPtr vm, + virSecurityLabelPtr sec) +{ + security_context_t ctx; + + if (getpidcon(vm->pid, &ctx) == -1) { + virSecurityReportError(conn, VIR_ERR_ERROR, _("%s: error calling " + "getpidcon(): %s"), __func__, + strerror(errno)); + return -1; + } + + if (strlen((char *) ctx) >= VIR_SECURITY_LABEL_BUFLEN) { + virSecurityReportError(conn, VIR_ERR_ERROR, + _("%s: security label exceeds " + "maximum length: %d"), __func__, + VIR_SECURITY_LABEL_BUFLEN - 1); + return -1; + } + + strcpy(sec->label, (char *) ctx); + free(ctx); + + sec->enforcing = security_getenforce(); + if (sec->enforcing == -1) { + virSecurityReportError(conn, VIR_ERR_ERROR, _("%s: error calling " + "security_getenforce(): %s"), __func__, + strerror(errno)); + return -1; + } + + return 0; +} + +static int +SELinuxSecurityDomainSetSecurityLabel(virConnectPtr conn, + virSecurityDriverPtr drv, + const virSecurityLabelDefPtr secdef) +{ + /* TODO: verify DOI */ + + if (!STREQ(drv->name, secdef->model)) { + virSecurityReportError(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) { + virSecurityReportError(conn, VIR_ERR_ERROR, + _("%s: unable to set security context " + "'\%s\': %s."), __func__, secdef->label, + strerror(errno)); + return -1; + } + return 0; +} + +virSecurityDriver virSELinuxSecurityDriver = { + .name = "selinux", + .probe = SELinuxSecurityDriverProbe, + .open = SELinuxSecurityDriverOpen, + .domainGetSecurityLabel = SELinuxSecurityDomainGetSecurityLabel, + .domainSetSecurityLabel = SELinuxSecurityDomainSetSecurityLabel, +}; diff --git a/src/security_selinux.h b/src/security_selinux.h new file mode 100644 index 0000000..1e32209 --- /dev/null +++ b/src/security_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_SECURITY_SELINUX_H__ +#define __VIR_SECURITY_SELINUX_H__ + +extern virSecurityDriver virSELinuxSecurityDriver; + +#endif /* __VIR_SECURITY_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 defa00f..ebf0c4c 100644 --- a/src/test.c +++ b/src/test.c @@ -3270,6 +3270,8 @@ static virDriver testDriver = { NULL, /* domainPinVcpu */ NULL, /* domainGetVcpus */ NULL, /* domainGetMaxVcpus */ + NULL, /* domainGetSecurityLabel */ + NULL, /* nodeGetSecurityModel */ testDomainDumpXML, /* domainDumpXML */ testListDefinedDomains, /* listDefinedDomains */ testNumOfDefinedDomains, /* numOfDefinedDomains */ diff --git a/src/uml_driver.c b/src/uml_driver.c index 408096e..cdb6de4 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, /* domainGetSecurityLabel */ + NULL, /* nodeGetSecurityModel */ umlDomainDumpXML, /* domainDumpXML */ umlListDefinedDomains, /* listDomains */ umlNumDefinedDomains, /* numOfDomains */ diff --git a/src/virsh.c b/src/virsh.c index f4a57f4..5131adc 100644 --- a/src/virsh.c +++ b/src/virsh.c @@ -955,6 +955,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) { @@ -1516,6 +1517,8 @@ cmdDominfo(vshControl *ctl, const vshCmd *cmd) { virDomainInfo info; virDomainPtr dom; + virSecurityModel secmodel; + virSecurityLabel seclabel; int ret = TRUE, autostart; unsigned int id; char *str, uuid[VIR_UUID_STRING_BUFLEN]; @@ -1574,6 +1577,29 @@ cmdDominfo(vshControl *ctl, const vshCmd *cmd) autostart ? _("enable") : _("disable") ); } + /* Security model and label information */ + memset(&secmodel, 0, sizeof secmodel); + if (virNodeGetSecurityModel(ctl->conn, &secmodel) == -1) { + virDomainFree(dom); + return FALSE; + } else { + /* Only print something if a security model is active */ + if (secmodel.model[0] != '\0') { + vshPrint(ctl, "%-15s %s\n", _("Security model:"), secmodel.model); + vshPrint(ctl, "%-15s %s\n", _("Security DOI:"), secmodel.doi); + + /* Security labels are only valid for active domains */ + memset(&seclabel, 0, sizeof seclabel); + if (virDomainGetSecurityLabel(dom, &seclabel) == -1) { + virDomainFree(dom); + return FALSE; + } else { + 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..1762a7a 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_SECURITY: + 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_SECURITY_MODEL: + if (info == NULL) + errmsg = _("Security model not found"); + else + errmsg = _("Security model not found: %s"); + break; } return (errmsg); } diff --git a/src/xml.c b/src/xml.c index a7ebe7c..186092d 100644 --- a/src/xml.c +++ b/src/xml.c @@ -75,6 +75,39 @@ virXPathString(virConnectPtr conn, } /** + * virXPathStringLimit: + * @xpath: the XPath string to evaluate + * @maxlen: maximum length permittred string + * @ctxt: an XPath context + * + * Wrapper for virXPathString, which validates the length of the returned + * string. + * + * Returns a new string which must be deallocated by the caller or NULL if + * the evaluation failed. + */ +char * +virXPathStringLimit(virConnectPtr conn, + const char *xpath, + size_t maxlen, + xmlXPathContextPtr ctxt) +{ + char *tmp = virXPathString(conn, xpath, ctxt); + + if (tmp != NULL) { + if (strlen(tmp) >= maxlen) { + virXMLError(conn, VIR_ERR_INTERNAL_ERROR, + _("\'%s\' value longer than %Zd bytes in virXPathStringLimit()"), + xpath, maxlen); + return NULL; + } + } else + virXMLError(conn, VIR_ERR_INTERNAL_ERROR, + _("\'%s\' missing in virXPathStringLimit()"), xpath); + return tmp; +} + +/** * virXPathNumber: * @xpath: the XPath string to evaluate * @ctxt: an XPath context diff --git a/src/xml.h b/src/xml.h index 0daa874..a1a9e41 100644 --- a/src/xml.h +++ b/src/xml.h @@ -17,6 +17,10 @@ int virXPathBoolean (virConnectPtr conn, char * virXPathString (virConnectPtr conn, const char *xpath, xmlXPathContextPtr ctxt); +char * virXPathStringLimit(virConnectPtr conn, + const char *xpath, + size_t maxlen, + xmlXPathContextPtr ctxt); int virXPathNumber (virConnectPtr conn, const char *xpath, xmlXPathContextPtr ctxt, diff --git a/tests/daemon-conf b/tests/daemon-conf index 65a9655..b55a379 100755 --- a/tests/daemon-conf +++ b/tests/daemon-conf @@ -62,6 +62,9 @@ while :; do -e '/^libnuma: Warning: .sys not mounted or no numa system/d' \ err > k && mv k err + # Filter out this diagnostic, too. + sed '/^Initialized security 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