Currently, if we label a file to match qemu process DAC label, we do not store the original owner anywhere. So when relabeling back, the only option we have is to relabel to root:root which is obviously wrong. However, bare remembering is not enough. We need to keep track of how many times we labeled a file so only the last restore chown()-s file back to the original owner. In order to not pollute domain XML, this info is kept in driver's private data in a hash table with path being key and pair <oldLabel, refcount> being value. --- src/security/security_dac.c | 351 ++++++++++++++++++++++++++++++++++------- src/security/security_driver.h | 3 + 2 files changed, 296 insertions(+), 58 deletions(-) diff --git a/src/security/security_dac.c b/src/security/security_dac.c index 0b274b7..4b8f0a2 100644 --- a/src/security/security_dac.c +++ b/src/security/security_dac.c @@ -42,8 +42,121 @@ struct _virSecurityDACData { uid_t user; gid_t group; bool dynamicOwnership; + virHashTablePtr oldOwners; /* to hold pairs <path, virOldLabelPtr> */ }; +struct _virOldLabel { + char *owner; + int refCount; +}; + +static void virOldLabelFree(virOldLabelPtr label) +{ + if (!label) + return; + + VIR_FREE(label->owner); + VIR_FREE(label); +} + +static void +hashDataFree(void *payload, const void *name ATTRIBUTE_UNUSED) +{ + virOldLabelFree(payload); +} + +/** + * virSecurityDACRememberLabel: + * @priv: private DAC driver data + * @path: path which is about to be relabelled + * + * Store the original DAC label of @path. + * Returns: number of references of @path, or -1 on error + */ +static int +virSecurityDACRememberLabel(virSecurityDACDataPtr priv, + const char *path) +{ + struct stat sb; + virOldLabelPtr oldLabel = NULL; + char *user = NULL, *group = NULL; + + oldLabel = virHashLookup(priv->oldOwners, path); + + if (oldLabel) { + /* just increment ref counter */ + oldLabel->refCount++; + goto cleanup; + } + + if (stat(path, &sb) < 0) { + virReportSystemError(errno, _("Unable to stat %s"), path); + goto cleanup; + } + + user = virGetUserName(sb.st_uid); + group = virGetGroupName(sb.st_gid); + if ((!user && (virAsprintf(&user, "+%ld", (long) sb.st_uid) < 0)) || + (!group && (virAsprintf(&group, "+%ld", (long) sb.st_gid) < 0)) || + (VIR_ALLOC(oldLabel) < 0) || + (virAsprintf(&oldLabel->owner, "%s:%s", user, group) < 0)) { + virReportOOMError(); + VIR_FREE(oldLabel); + goto cleanup; + } + + oldLabel->refCount = 1; + if (virHashUpdateEntry(priv->oldOwners, path, oldLabel) < 0) { + virOldLabelFree(oldLabel); + oldLabel = NULL; + } + +cleanup: + VIR_FREE(user); + VIR_FREE(group); + return oldLabel ? oldLabel->refCount : -1; +} + +static int parseIds(const char *label, uid_t *uidPtr, gid_t *gidPtr); + +/** + * virSecurityDACGetRememberedLabel: + * @priv: private DAC driver data + * @path: path which we want to restore label on + * @user: original owner of @path + * @group: original owner of @path + * + * Decrements reference counter on @path. If this was the last + * reference, we need to restore the original label, in which + * case @user and @group are updated. + * Returns: the number of references of @path + * 0 if we need to restore the label + * -1 on error + */ +static int +virSecurityDACGetRememberedLabel(virSecurityDACDataPtr priv, + const char *path, + uid_t *user, + gid_t *group) +{ + int ret = -1; + virOldLabelPtr oldLabel = NULL; + + oldLabel = virHashLookup(priv->oldOwners, path); + if (!oldLabel) + goto cleanup; + + ret = --oldLabel->refCount; + + if (!ret) { + ret = parseIds(oldLabel->owner, user, group); + virHashRemoveEntry(priv->oldOwners, path); + } + +cleanup: + return ret; +} + void virSecurityDACSetUser(virSecurityManagerPtr mgr, uid_t user) { @@ -242,14 +355,20 @@ virSecurityDACProbe(const char *virtDriver ATTRIBUTE_UNUSED) } static int -virSecurityDACOpen(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +virSecurityDACOpen(virSecurityManagerPtr mgr) { - return 0; + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + + priv->oldOwners = virHashCreate(0, hashDataFree); + return priv->oldOwners ? 0 : -1; } static int -virSecurityDACClose(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +virSecurityDACClose(virSecurityManagerPtr mgr) { + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + + virHashFree(priv->oldOwners); return 0; } @@ -306,7 +425,9 @@ virSecurityDACSetOwnership(const char *path, uid_t uid, gid_t gid) } static int -virSecurityDACRestoreSecurityFileLabel(const char *path) +virSecurityDACRestoreSecurityFileLabel(const char *path, + uid_t user, + gid_t group) { struct stat buf; int rc = -1; @@ -323,8 +444,7 @@ virSecurityDACRestoreSecurityFileLabel(const char *path) if (stat(newpath, &buf) != 0) goto err; - /* XXX record previous ownership */ - rc = virSecurityDACSetOwnership(newpath, 0, 0); + rc = virSecurityDACSetOwnership(newpath, user, group); err: VIR_FREE(newpath); @@ -345,7 +465,8 @@ virSecurityDACSetSecurityFileLabel(virDomainDiskDefPtr disk ATTRIBUTE_UNUSED, uid_t user; gid_t group; - if (virSecurityDACGetImageIds(def, priv, &user, &group)) + if (virSecurityDACGetImageIds(def, priv, &user, &group) || + (virSecurityDACRememberLabel(priv, path) < 0)) return -1; return virSecurityDACSetOwnership(path, user, group); @@ -382,26 +503,14 @@ virSecurityDACRestoreSecurityImageLabelInt(virSecurityManagerPtr mgr, virDomainDiskDefPtr disk, int migrated) { + int ret; virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + uid_t user = 0; + gid_t group = 0; - if (!priv->dynamicOwnership) - return 0; - - if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) - return 0; - - /* Don't restore labels on readoly/shared disks, because - * other VMs may still be accessing these - * Alternatively we could iterate over all running - * domains and try to figure out if it is in use, but - * this would not work for clustered filesystems, since - * we can't see running VMs using the file on other nodes - * Safest bet is thus to skip the restore step. - */ - if (disk->readonly || disk->shared) - return 0; - - if (!disk->src) + if (!priv->dynamicOwnership || + (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) || + !disk->src) return 0; /* If we have a shared FS & doing migrated, we must not @@ -411,7 +520,7 @@ virSecurityDACRestoreSecurityImageLabelInt(virSecurityManagerPtr mgr, */ if (migrated) { int rc = virStorageFileIsSharedFS(disk->src); - if (rc < 0) + if (rc > 0) return -1; if (rc == 1) { VIR_DEBUG("Skipping image label restore on %s because FS is shared", @@ -420,7 +529,15 @@ virSecurityDACRestoreSecurityImageLabelInt(virSecurityManagerPtr mgr, } } - return virSecurityDACRestoreSecurityFileLabel(disk->src); + ret = virSecurityDACGetRememberedLabel(priv, disk->src, &user, &group); + if (ret < 0) + return -1; + else if (ret > 0) { + VIR_DEBUG("Skipping image label restore on %s " + "because the image is still in use", disk->src); + return 0; + } + return virSecurityDACRestoreSecurityFileLabel(disk->src, user, group); } @@ -445,7 +562,8 @@ virSecurityDACSetSecurityPCILabel(virPCIDevicePtr dev ATTRIBUTE_UNUSED, uid_t user; gid_t group; - if (virSecurityDACGetIds(def, priv, &user, &group)) + if (virSecurityDACGetIds(def, priv, &user, &group) || + (virSecurityDACRememberLabel(priv, file) < 0)) return -1; return virSecurityDACSetOwnership(file, user, group); @@ -464,7 +582,8 @@ virSecurityDACSetSecurityUSBLabel(virUSBDevicePtr dev ATTRIBUTE_UNUSED, uid_t user; gid_t group; - if (virSecurityDACGetIds(def, priv, &user, &group)) + if (virSecurityDACGetIds(def, priv, &user, &group) || + (virSecurityDACRememberLabel(priv, file) < 0)) return -1; return virSecurityDACSetOwnership(file, user, group); @@ -536,18 +655,46 @@ done: static int virSecurityDACRestoreSecurityPCILabel(virPCIDevicePtr dev ATTRIBUTE_UNUSED, const char *file, - void *opaque ATTRIBUTE_UNUSED) + void *opaque) { - return virSecurityDACRestoreSecurityFileLabel(file); + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(opaque); + uid_t user = 0; + gid_t group = 0; + int ret; + + ret = virSecurityDACGetRememberedLabel(priv, file, &user, &group); + if (ret < 0) + return -1; + else if (ret > 0) { + VIR_DEBUG("Skipping security label restore on %s " + "because the PCI device is still in use", file); + return 0; + } + + return virSecurityDACRestoreSecurityFileLabel(file, user, group); } static int virSecurityDACRestoreSecurityUSBLabel(virUSBDevicePtr dev ATTRIBUTE_UNUSED, const char *file, - void *opaque ATTRIBUTE_UNUSED) + void *opaque) { - return virSecurityDACRestoreSecurityFileLabel(file); + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(opaque); + uid_t user = 0; + gid_t group = 0; + int ret; + + ret = virSecurityDACGetRememberedLabel(priv, file, &user, &group); + if (ret < 0) + return -1; + else if (ret > 0) { + VIR_DEBUG("Skipping security label restore on %s " + "because the USB device is still in use", file); + return 0; + } + + return virSecurityDACRestoreSecurityFileLabel(file, user, group); } @@ -630,6 +777,8 @@ virSecurityDACSetChardevLabel(virSecurityManagerPtr mgr, switch (dev->type) { case VIR_DOMAIN_CHR_TYPE_DEV: case VIR_DOMAIN_CHR_TYPE_FILE: + if (virSecurityDACRememberLabel(priv, dev->data.file.path) < 0) + goto cleanup; ret = virSecurityDACSetOwnership(dev->data.file.path, user, group); break; @@ -637,16 +786,22 @@ virSecurityDACSetChardevLabel(virSecurityManagerPtr mgr, if ((virAsprintf(&in, "%s.in", dev->data.file.path) < 0) || (virAsprintf(&out, "%s.out", dev->data.file.path) < 0)) { virReportOOMError(); - goto done; + goto cleanup; } if (virFileExists(in) && virFileExists(out)) { + if ((virSecurityDACRememberLabel(priv, in) < 0) || + (virSecurityDACRememberLabel(priv, out) < 0)) + goto cleanup; if ((virSecurityDACSetOwnership(in, user, group) < 0) || (virSecurityDACSetOwnership(out, user, group) < 0)) { - goto done; + goto cleanup; } - } else if (virSecurityDACSetOwnership(dev->data.file.path, - user, group) < 0) { - goto done; + } else { + if (virSecurityDACRememberLabel(priv, dev->data.file.path) < 0) + goto cleanup; + if (virSecurityDACSetOwnership(dev->data.file.path, + user, group) < 0) + goto cleanup; } ret = 0; break; @@ -656,38 +811,85 @@ virSecurityDACSetChardevLabel(virSecurityManagerPtr mgr, break; } -done: +cleanup: VIR_FREE(in); VIR_FREE(out); return ret; } static int -virSecurityDACRestoreChardevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, +virSecurityDACRestoreChardevLabel(virSecurityManagerPtr mgr, virDomainChrSourceDefPtr dev) { + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); char *in = NULL, *out = NULL; int ret = -1; + uid_t user = 0; + gid_t group = 0; switch (dev->type) { case VIR_DOMAIN_CHR_TYPE_DEV: case VIR_DOMAIN_CHR_TYPE_FILE: - ret = virSecurityDACRestoreSecurityFileLabel(dev->data.file.path); + ret = virSecurityDACGetRememberedLabel(priv, dev->data.file.path, + &user, &group); + if (ret < 0) + return -1; + else if (ret > 0) { + VIR_DEBUG("Skipping security label restore on %s " + "because the chardev device is still in use", + dev->data.file.path); + return 0; + } + + ret = virSecurityDACRestoreSecurityFileLabel(dev->data.file.path, + user, group); break; case VIR_DOMAIN_CHR_TYPE_PIPE: if ((virAsprintf(&out, "%s.out", dev->data.file.path) < 0) || (virAsprintf(&in, "%s.in", dev->data.file.path) < 0)) { virReportOOMError(); - goto done; + goto cleanup; } if (virFileExists(in) && virFileExists(out)) { - if ((virSecurityDACRestoreSecurityFileLabel(out) < 0) || - (virSecurityDACRestoreSecurityFileLabel(in) < 0)) { - goto done; + ret = virSecurityDACGetRememberedLabel(priv, in, &user, &group); + if (ret < 0) + goto cleanup; + else if (ret > 0) { + VIR_DEBUG("Skipping security label restore on %s " + "because the chardev device is still in use", in); + goto cleanup; + } + ret = virSecurityDACGetRememberedLabel(priv, out, &user, &group); + if (ret < 0) + goto cleanup; + else if (ret > 0) { + VIR_DEBUG("Skipping security label restore on %s " + "because the chardev device is still in use", out); + goto cleanup; + } + if ((virSecurityDACRestoreSecurityFileLabel(out, user, group) < 0) || + (virSecurityDACRestoreSecurityFileLabel(in, user, group) < 0)) { + ret = -1; + goto cleanup; + } + } else { + ret = virSecurityDACGetRememberedLabel(priv, dev->data.file.path, + &user, &group); + if (ret < 0) + goto cleanup; + else if (ret > 0) { + VIR_DEBUG("Skipping security label restore on %s " + "because the chardev device is still in use", + dev->data.file.path); + goto cleanup; + } + + if (virSecurityDACRestoreSecurityFileLabel(dev->data.file.path, + user, group) < 0) { + ret = -1; + goto cleanup; } - } else if (virSecurityDACRestoreSecurityFileLabel(dev->data.file.path) < 0) { - goto done; } ret = 0; break; @@ -697,7 +899,7 @@ virSecurityDACRestoreChardevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, break; } -done: +cleanup: VIR_FREE(in); VIR_FREE(out); return ret; @@ -723,6 +925,8 @@ virSecurityDACRestoreSecurityAllLabel(virSecurityManagerPtr mgr, virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); int i; int rc = 0; + uid_t user = 0; + gid_t group = 0; if (!priv->dynamicOwnership) return 0; @@ -752,13 +956,29 @@ virSecurityDACRestoreSecurityAllLabel(virSecurityManagerPtr mgr, mgr) < 0) rc = -1; - if (def->os.kernel && - virSecurityDACRestoreSecurityFileLabel(def->os.kernel) < 0) - rc = -1; + if (def->os.kernel) { + i = virSecurityDACGetRememberedLabel(priv, def->os.kernel, &user, &group); + if (i < 0) + rc = -1; + else if (i > 0) + VIR_DEBUG("Skipping security label restore on %s " + "because the kernel image is still in use", def->os.kernel); + else if (virSecurityDACRestoreSecurityFileLabel(def->os.kernel, + user, group) < 0) + rc = -1; + } - if (def->os.initrd && - virSecurityDACRestoreSecurityFileLabel(def->os.initrd) < 0) - rc = -1; + if (def->os.initrd) { + i = virSecurityDACGetRememberedLabel(priv, def->os.initrd, &user, &group); + if (i < 0) + rc = -1; + else if (i > 0) + VIR_DEBUG("Skipping security label restore on %s " + "because the initrd image is still in use", def->os.initrd); + else if (virSecurityDACRestoreSecurityFileLabel(def->os.initrd, + user, group) < 0) + rc = -1; + } return rc; } @@ -815,11 +1035,13 @@ virSecurityDACSetSecurityAllLabel(virSecurityManagerPtr mgr, return -1; if (def->os.kernel && - virSecurityDACSetOwnership(def->os.kernel, user, group) < 0) + ((virSecurityDACRememberLabel(priv, def->os.kernel) < 0) || + (virSecurityDACSetOwnership(def->os.kernel, user, group) < 0))) return -1; if (def->os.initrd && - virSecurityDACSetOwnership(def->os.initrd, user, group) < 0) + ((virSecurityDACRememberLabel(priv, def->os.initrd) < 0) || + (virSecurityDACSetOwnership(def->os.initrd, user, group) < 0))) return -1; return 0; @@ -835,7 +1057,8 @@ virSecurityDACSetSavedStateLabel(virSecurityManagerPtr mgr, gid_t group; virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); - if (virSecurityDACGetImageIds(def, priv, &user, &group)) + if (virSecurityDACGetImageIds(def, priv, &user, &group) || + (virSecurityDACRememberLabel(priv, savefile) < 0)) return -1; return virSecurityDACSetOwnership(savefile, user, group); @@ -848,11 +1071,23 @@ virSecurityDACRestoreSavedStateLabel(virSecurityManagerPtr mgr, const char *savefile) { virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + uid_t user = 0; + gid_t group = 0; + int ret; if (!priv->dynamicOwnership) return 0; - return virSecurityDACRestoreSecurityFileLabel(savefile); + ret = virSecurityDACGetRememberedLabel(priv, savefile, &user, &group); + if (ret < 0) + return -1; + else if (ret > 0) { + VIR_DEBUG("Skipping security label restore on %s " + "because the saved state file is still in use", savefile); + return 0; + } + + return virSecurityDACRestoreSecurityFileLabel(savefile, user, group); } diff --git a/src/security/security_driver.h b/src/security/security_driver.h index cc401e1..d54f754 100644 --- a/src/security/security_driver.h +++ b/src/security/security_driver.h @@ -40,6 +40,9 @@ typedef enum { typedef struct _virSecurityDriver virSecurityDriver; typedef virSecurityDriver *virSecurityDriverPtr; +typedef struct _virOldLabel virOldLabel; +typedef virOldLabel *virOldLabelPtr; + typedef virSecurityDriverStatus (*virSecurityDriverProbe) (const char *virtDriver); typedef int (*virSecurityDriverOpen) (virSecurityManagerPtr mgr); typedef int (*virSecurityDriverClose) (virSecurityManagerPtr mgr); -- 1.8.1.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list