Signed-off-by: Michal Privoznik <mprivozn@xxxxxxxxxx> --- src/security/security_dac.c | 193 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) diff --git a/src/security/security_dac.c b/src/security/security_dac.c index b6f75fe1d..0208c1751 100644 --- a/src/security/security_dac.c +++ b/src/security/security_dac.c @@ -68,6 +68,117 @@ struct _virSecurityDACCallbackData { virSecurityLabelDefPtr secdef; }; +typedef struct _virSecurityDACChownItem virSecurityDACChownItem; +typedef virSecurityDACChownItem *virSecurityDACChownItemPtr; +struct _virSecurityDACChownItem { + const char *path; + const virStorageSource *src; + uid_t uid; + gid_t gid; +}; + +typedef struct _virSecurityDACChownList virSecurityDACChownList; +typedef virSecurityDACChownList *virSecurityDACChownListPtr; +struct _virSecurityDACChownList { + virSecurityDACDataPtr priv; + virSecurityDACChownItemPtr *items; + size_t nItems; +}; + + +virThreadLocal chownList; + +static int +virSecurityDACChownListAppend(virSecurityDACChownListPtr list, + const char *path, + const virStorageSource *src, + uid_t uid, + gid_t gid) +{ + virSecurityDACChownItemPtr item; + + if (VIR_ALLOC(item) < 0) + return -1; + + item->path = path; + item->src = src; + item->uid = uid; + item->gid = gid; + + if (VIR_APPEND_ELEMENT(list->items, list->nItems, item) < 0) { + VIR_FREE(item); + return -1; + } + + return 0; +} + +static void +virSecurityDACChownListFree(void *opaque) +{ + virSecurityDACChownListPtr list = opaque; + size_t i; + + if (!list) + return; + + for (i = 0; i < list->nItems; i++) + VIR_FREE(list->items[i]); + VIR_FREE(list); +} + + +static int +virSecurityDACTransactionAppend(const char *path, + const virStorageSource *src, + uid_t uid, + gid_t gid) +{ + virSecurityDACChownListPtr list; + int ret = -1; + + list = virThreadLocalGet(&chownList); + if (!list) + return 0; + + if (virSecurityDACChownListAppend(list, path, src, uid, gid) < 0) + goto cleanup; + + ret = 0; + cleanup: + return ret; +} + + +static int virSecurityDACSetOwnershipInternal(const virSecurityDACData *priv, + const virStorageSource *src, + const char *path, + uid_t uid, + gid_t gid); + +static int +virSecurityDACTransactionRun(pid_t pid ATTRIBUTE_UNUSED, + void *opaque) +{ + virSecurityDACChownListPtr list = opaque; + size_t i; + + ignore_value(virThreadLocalSet(&chownList, NULL)); + for (i = 0; i < list->nItems; i++) { + virSecurityDACChownItemPtr item = list->items[i]; + + if (virSecurityDACSetOwnershipInternal(list->priv, + item->src, + item->path, + item->uid, + item->gid) < 0) + return -1; + } + + return 0; +} + + /* returns -1 on error, 0 on success */ int virSecurityDACSetUserAndGroup(virSecurityManagerPtr mgr, @@ -238,6 +349,13 @@ virSecurityDACProbe(const char *virtDriver ATTRIBUTE_UNUSED) static int virSecurityDACOpen(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) { + if (virThreadLocalInit(&chownList, + virSecurityDACChownListFree) < 0) { + virReportSystemError(errno, "%s", + _("Unable to initialize thread local variable")); + return -1; + } + return 0; } @@ -278,6 +396,69 @@ virSecurityDACPreFork(virSecurityManagerPtr mgr) return 0; } +static int +virSecurityDACTransactionStart(virSecurityManagerPtr mgr) +{ + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + virSecurityDACChownListPtr list; + + list = virThreadLocalGet(&chownList); + if (list) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Another relabel transaction is already started")); + return -1; + } + + if (VIR_ALLOC(list) < 0) + return -1; + + list->priv = priv; + + if (virThreadLocalSet(&chownList, list) < 0) { + virReportSystemError(errno, "%s", + _("Unable to set thread local variable")); + VIR_FREE(list); + return -1; + } + + return 0; +} + +static int +virSecurityDACTransactionCommit(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + pid_t pid) +{ + virSecurityDACChownListPtr list; + int ret = 0; + + list = virThreadLocalGet(&chownList); + if (!list) + return 0; + + if (virProcessRunInMountNamespace(pid, + virSecurityDACTransactionRun, + list) < 0) + ret = -1; + + ignore_value(virThreadLocalSet(&chownList, NULL)); + virSecurityDACChownListFree(list); + return ret; +} + +static void +virSecurityDACTransactionAbort(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + virSecurityDACChownListPtr list; + + list = virThreadLocalGet(&chownList); + if (!list) + return; + + ignore_value(virThreadLocalSet(&chownList, NULL)); + virSecurityDACChownListFree(list); +} + + static int virSecurityDACSetOwnershipInternal(const virSecurityDACData *priv, const virStorageSource *src, @@ -287,6 +468,14 @@ virSecurityDACSetOwnershipInternal(const virSecurityDACData *priv, { int rc; + /* Be aware that this function might run in a separate process. + * Therefore, any driver state changes would be thrown away. */ + + if ((rc = virSecurityDACTransactionAppend(path, src, uid, gid)) < 0) + return -1; + else if (rc > 0) + return 0; + VIR_INFO("Setting DAC user and group on '%s' to '%ld:%ld'", NULLSTR(src ? src->path : path), (long) uid, (long) gid); @@ -1626,6 +1815,10 @@ virSecurityDriver virSecurityDriverDAC = { .preFork = virSecurityDACPreFork, + .transactionStart = virSecurityDACTransactionStart, + .transactionCommit = virSecurityDACTransactionCommit, + .transactionAbort = virSecurityDACTransactionAbort, + .domainSecurityVerify = virSecurityDACVerify, .domainSetSecurityDiskLabel = virSecurityDACSetDiskLabel, -- 2.11.0 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list