/*++ Copyright (c) Microsoft. All rights reserved. Module Name: Xattr.c Abstract: This file is a xattr test. --*/ #include "lxtcommon.h" #include "unittests.h" #include "lxtutil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lxtfs.h" #include #include #include "lxtmount.h" #if !defined(__amd64__) && !defined(__aarch64__) #include #else #include #include #define _LINUX_CAPABILITY_VERSION_3 0x20080522 #endif #define LXT_NAME "xattr" #define LXT_NAME_DRVFS "xattr_drvfs" #define LXT_XATTR_MODE 0777 #define LXT_XATTR_UID 1004 #define LXT_XATTR_GID 1004 #define LXT_XATTR_TEST_PARENT "/data/xattrtest" #define LXT_XATTR_ACCESS_FILE_PATH LXT_XATTR_TEST_PARENT "/xattrAccessFile" #define LXT_XATTR_FILE_PATH LXT_XATTR_TEST_PARENT "/xattrFile" #define LXT_XATTR_DIR_PATH LXT_XATTR_TEST_PARENT "/xattrDir" #define LXT_XATTR_LINK_PATH LXT_XATTR_TEST_PARENT "/xattrLink" #define LXT_XATTR_FIFO_PATH LXT_XATTR_TEST_PARENT "/xattrFifo" #define LXT_XATTR_SIZE_MAX (4040) #define LXT_XATTR_CASE_SENSITIVE_LENGTH (sizeof(LXT_XATTR_CASE_SENSITIVE) - 1) #define LXT_XATTR_TEST_VALUE "test" #define LXT_XATTR_TEST_LENGTH (sizeof(LXT_XATTR_TEST_VALUE) - 1) #define LXT_XATTR_PATH_COUNT (LXT_COUNT_OF(g_XattrPaths)) int XattrTestCreatePaths(int* Fds); void XattrTestDeletePaths(int* Fds); LXT_VARIATION_HANDLER XattrAccessTest; LXT_VARIATION_HANDLER XattrListTest; LXT_VARIATION_HANDLER XattrGetTest; LXT_VARIATION_HANDLER XattrRemoveTest; LXT_VARIATION_HANDLER XattrSetTest; // // Global constants // static const LXT_VARIATION g_LxtVariations[] = { {"xattr list", XattrListTest}, {"xattr get", XattrGetTest}, {"xattr set", XattrSetTest}, {"xattr remove", XattrRemoveTest}, {"xattr access", XattrAccessTest}}; static const char* g_XattrPaths[] = { LXT_XATTR_FILE_PATH, LXT_XATTR_DIR_PATH, LXT_XATTR_LINK_PATH, LXT_XATTR_FIFO_PATH, "/dev/null", "/proc/cpuinfo"}; ssize_t GetXattr(const char* Path, const char* Name, void* Value, size_t Size) { size_t Result = LxtGetxattr(Path, Name, Value, Size); if (Result < 0) { int SavedErrno = errno; ssize_t SavedResult = Result; Result = LxtGetxattr(Path, Name, NULL, 0); LxtLogInfo("getxattr(%s, %s, NULL, 0) = %Iu", Path, Name, Result); errno = SavedErrno; Result = SavedResult; } return Result; } int XattrTestEntry(int Argc, char* Argv[]) /*++ --*/ { LXT_ARGS Args; int Index; const char* Name; int Result; bool UseDrvFs; Name = LXT_NAME; UseDrvFs = false; for (Index = 1; Index < Argc; Index += 1) { if (strcmp(Argv[Index], "drvfs") == 0) { UseDrvFs = true; Name = LXT_NAME_DRVFS; break; } } LxtCheckResult(LxtInitialize(Argc, Argv, &Args, Name)); LxtCheckResult(LxtFsTestSetup(&Args, LXT_XATTR_TEST_PARENT, "/xattrtest", UseDrvFs)); LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations))); ErrorExit: LxtFsTestCleanup(LXT_XATTR_TEST_PARENT, "/xattrtest", UseDrvFs); LxtUninitialize(); return !LXT_SUCCESS(Result); } int XattrTestCreatePaths(int* Fds) /*++ --*/ { int Fd; int Index; int Result; memset(Fds, -1, sizeof(*Fds) * LXT_XATTR_PATH_COUNT); XattrTestDeletePaths(NULL); LxtCheckErrno(Fd = creat(LXT_XATTR_FILE_PATH, LXT_XATTR_MODE)); LxtClose(Fd); LxtCheckErrno(mkdir(LXT_XATTR_DIR_PATH, LXT_XATTR_MODE)); LxtCheckErrno(symlink(LXT_XATTR_FILE_PATH, LXT_XATTR_LINK_PATH)); LxtCheckErrno(mkfifo(LXT_XATTR_FIFO_PATH, LXT_XATTR_MODE)); for (Index = 0; Index < LXT_XATTR_PATH_COUNT; ++Index) { Fds[Index] = open(g_XattrPaths[Index], O_RDONLY | O_NONBLOCK); } ErrorExit: return Result; } void XattrTestDeletePaths(int* Fds) { int Index; if (Fds != NULL) { for (Index = 0; Index < LXT_XATTR_PATH_COUNT; ++Index) { if (Fds[Index] != -1) { LxtClose(Fds[Index]); Fds[Index] = -1; } } } unlink(LXT_XATTR_FILE_PATH); rmdir(LXT_XATTR_DIR_PATH); unlink(LXT_XATTR_LINK_PATH); unlink(LXT_XATTR_FIFO_PATH); return; } int XattrListTest(PLXT_ARGS Args) /*++ --*/ { char Buffer[1024]; ssize_t ExpectedSize; int Fds[LXT_XATTR_PATH_COUNT]; int Index; ssize_t Result; ssize_t Size; LxtCheckErrno(XattrTestCreatePaths(Fds)); for (Index = 0; Index < (LXT_XATTR_PATH_COUNT - 1); ++Index) { LxtLogInfo("%s", g_XattrPaths[Index]); // // Check that the xattr syscalls return 0 for all entries to indicate no // attributes are present. // // N.B. DrvFs (not in WslFs mode) will return the case sensitivity // attribute for directories only. // if ((Index == 1) && ((g_LxtFsInfo.FsType == LxtFsTypeDrvFs) || (g_LxtFsInfo.FsType == LxtFsTypeVirtioFs))) { ExpectedSize = LXT_XATTR_CASE_SENSITIVE_LENGTH + 1; } else { ExpectedSize = 0; } LxtCheckErrno(Size = LxtListxattr(g_XattrPaths[Index], Buffer, sizeof(Buffer))); LxtCheckEqual(Size, ExpectedSize, "%lld"); LxtCheckErrno(Size = LxtLlistxattr(g_XattrPaths[Index], Buffer, sizeof(Buffer))); LxtCheckEqual(Size, ExpectedSize, "%lld"); LxtCheckErrno(Size = LxtFlistxattr(Fds[Index], Buffer, sizeof(Buffer))); LxtCheckEqual(Size, ExpectedSize, "%lld"); // // Check that the buffer and size are not validated if there are no // attributes. // if (ExpectedSize == 0) { LxtCheckErrnoZeroSuccess(LxtListxattr(g_XattrPaths[Index], 0x1, sizeof(Buffer))); LxtCheckErrnoZeroSuccess(LxtLlistxattr(g_XattrPaths[Index], 0x1, sizeof(Buffer))); LxtCheckErrnoZeroSuccess(LxtFlistxattr(Fds[Index], 0x1, sizeof(Buffer))); LxtCheckErrnoZeroSuccess(LxtListxattr(g_XattrPaths[Index], Buffer, -1)); LxtCheckErrnoZeroSuccess(LxtLlistxattr(g_XattrPaths[Index], Buffer, -1)); LxtCheckErrnoZeroSuccess(LxtFlistxattr(Fds[Index], Buffer, -1)); } } // // Check for invalid parameters. // LxtCheckErrnoFailure(LxtListxattr(0x1, Buffer, sizeof(Buffer)), EFAULT); LxtCheckErrnoFailure(LxtLlistxattr(0x1, Buffer, sizeof(Buffer)), EFAULT); LxtCheckErrnoFailure(LxtFlistxattr(-1, Buffer, sizeof(Buffer)), EBADF); Result = LXT_RESULT_SUCCESS; ErrorExit: XattrTestDeletePaths(Fds); return (int)Result; } int XattrGetTest(PLXT_ARGS Args) /*++ --*/ { char Buffer[1024]; int Failures[] = {ENODATA, ENODATA, ENODATA, ENODATA, ENODATA, ENOTSUP, ENOTSUP}; int Fds[LXT_XATTR_PATH_COUNT]; int Index; char* Name = "security.capability"; ssize_t Result; LxtCheckErrno(XattrTestCreatePaths(Fds)); for (Index = 0; Index < LXT_XATTR_PATH_COUNT; ++Index) { // // Check that the xattr syscalls return 0 for all entries to indicate no // attributes are present. // LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[Index], Name, Buffer, sizeof(Buffer)), Failures[Index]); LxtCheckErrnoFailure(LxtLgetxattr(g_XattrPaths[Index], Name, Buffer, sizeof(Buffer)), Failures[Index]); LxtCheckErrnoFailure(LxtFgetxattr(Fds[Index], Name, Buffer, sizeof(Buffer)), Failures[Index]); // // Check that the buffer and size are not validated if there are no // attributes. // LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[Index], Name, 0x1, sizeof(Buffer)), Failures[Index]); LxtCheckErrnoFailure(LxtLgetxattr(g_XattrPaths[Index], Name, 0x1, sizeof(Buffer)), Failures[Index]); LxtCheckErrnoFailure(LxtFgetxattr(Fds[Index], Name, 0x1, sizeof(Buffer)), Failures[Index]); LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[Index], Name, Buffer, -1), Failures[Index]); LxtCheckErrnoFailure(LxtLgetxattr(g_XattrPaths[Index], Name, Buffer, -1), Failures[Index]); LxtCheckErrnoFailure(LxtFgetxattr(Fds[Index], Name, Buffer, -1), Failures[Index]); } // // Check for invalid parameters. // LxtCheckErrnoFailure(LxtGetxattr(0x1, Name, Buffer, sizeof(Buffer)), EFAULT); LxtCheckErrnoFailure(LxtLgetxattr(0x1, Name, Buffer, sizeof(Buffer)), EFAULT); LxtCheckErrnoFailure(LxtFgetxattr(-1, Name, Buffer, sizeof(Buffer)), EBADF); LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[0], 0x1, Buffer, sizeof(Buffer)), EFAULT); LxtCheckErrnoFailure(LxtLgetxattr(g_XattrPaths[0], 0x1, Buffer, sizeof(Buffer)), EFAULT); LxtCheckErrnoFailure(LxtFgetxattr(Fds[0], 0x1, Buffer, sizeof(Buffer)), EFAULT); if (g_LxtFsInfo.FsType != LxtFsTypeVirtioFs) { LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[0], "invalid.name", Buffer, sizeof(Buffer)), ENOTSUP); LxtCheckErrnoFailure(LxtLgetxattr(g_XattrPaths[0], "invalid.name", Buffer, sizeof(Buffer)), ENOTSUP); LxtCheckErrnoFailure(LxtFgetxattr(Fds[0], "invalid.name", Buffer, sizeof(Buffer)), ENOTSUP); } else { // The virtioFs implementation does not restrict the allowed attribute names, but these values will not be present. LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[0], "invalid.name", Buffer, sizeof(Buffer)), ENODATA); LxtCheckErrnoFailure(LxtLgetxattr(g_XattrPaths[0], "invalid.name", Buffer, sizeof(Buffer)), ENODATA); LxtCheckErrnoFailure(LxtFgetxattr(Fds[0], "invalid.name", Buffer, sizeof(Buffer)), ENODATA); } Result = LXT_RESULT_SUCCESS; ErrorExit: XattrTestDeletePaths(Fds); return (int)Result; } int XattrRemoveTest(PLXT_ARGS Args) /*++ --*/ { char Buffer[1024]; int Fds[LXT_XATTR_PATH_COUNT]; int Index; char* Name = "security.capability"; int Result; char TestData[] = "test"; LxtCheckErrno(XattrTestCreatePaths(Fds)); for (Index = 0; Index < LXT_XATTR_PATH_COUNT - 1; ++Index) { LxtLogInfo("%s %s", g_XattrPaths[Index], Name); // // Check that the xattr syscalls return the correct error code for all // entries to indicate no attributes are present. // LxtCheckErrnoFailure(LxtRemovexattr(g_XattrPaths[Index], Name), ENODATA); LxtCheckErrnoFailure(LxtLremovexattr(g_XattrPaths[Index], Name), ENODATA); LxtCheckErrnoFailure(LxtFremovexattr(Fds[Index], Name), ENODATA); } // // Create three ea's and delete them in various orders. // LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.1", TestData, sizeof(TestData), 0)); LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.2", TestData, sizeof(TestData), 0)); LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.3", TestData, sizeof(TestData), 0)); LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.1")); LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.2")); LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.3")); LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.1", TestData, sizeof(TestData), 0)); LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.2", TestData, sizeof(TestData), 0)); LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.3", TestData, sizeof(TestData), 0)); LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.1")); LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.3")); LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.2")); LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.1", TestData, sizeof(TestData), 0)); LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.2", TestData, sizeof(TestData), 0)); LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.3", TestData, sizeof(TestData), 0)); LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.2")); LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.1")); LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.3")); LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.1", TestData, sizeof(TestData), 0)); LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.2", TestData, sizeof(TestData), 0)); LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.3", TestData, sizeof(TestData), 0)); LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.2")); LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.3")); LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.1")); LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.1", TestData, sizeof(TestData), 0)); LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.2", TestData, sizeof(TestData), 0)); LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.3", TestData, sizeof(TestData), 0)); LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.3")); LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.1")); LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.2")); LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.1", TestData, sizeof(TestData), 0)); LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.2", TestData, sizeof(TestData), 0)); LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.3", TestData, sizeof(TestData), 0)); LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.3")); LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.2")); LxtCheckErrno(LxtRemovexattr(g_XattrPaths[0], "user.1")); // // Check for invalid parameters. // LxtCheckErrnoFailure(LxtRemovexattr(0x1, Name), EFAULT); LxtCheckErrnoFailure(LxtLremovexattr(0x1, Name), EFAULT); LxtCheckErrnoFailure(LxtFremovexattr(-1, Name), EBADF); LxtCheckErrnoFailure(LxtRemovexattr(g_XattrPaths[0], 0x1), EFAULT); LxtCheckErrnoFailure(LxtLremovexattr(g_XattrPaths[0], 0x1), EFAULT); LxtCheckErrnoFailure(LxtFremovexattr(Fds[0], 0x1), EFAULT); if (g_LxtFsInfo.FsType != LxtFsTypeVirtioFs) { LxtCheckErrnoFailure(LxtRemovexattr(g_XattrPaths[0], "invalid.name"), ENOTSUP); LxtCheckErrnoFailure(LxtLremovexattr(g_XattrPaths[0], "invalid.name"), ENOTSUP); LxtCheckErrnoFailure(LxtFremovexattr(Fds[0], "invalid.name"), ENOTSUP); } else { // The virtioFs implementation does not restrict the allowed attribute names, but these values will not be present. LxtCheckErrnoFailure(LxtRemovexattr(g_XattrPaths[0], "invalid.name"), ENODATA); LxtCheckErrnoFailure(LxtLremovexattr(g_XattrPaths[0], "invalid.name"), ENODATA); LxtCheckErrnoFailure(LxtFremovexattr(Fds[0], "invalid.name"), ENODATA); } Result = LXT_RESULT_SUCCESS; ErrorExit: XattrTestDeletePaths(Fds); return Result; } int XattrSetTest(PLXT_ARGS Args) /*++ --*/ { char* AttrName; char Buffer[1024]; int Count; char* DynamicBuffer; int DynamicBufferSize; int Fds[LXT_XATTR_PATH_COUNT]; int Index; char* Name = "security.foo"; int NameIndex; ssize_t Result; int Size; struct { char Name[256]; bool Found; } TestXattrs[103]; int TotalCount; int TotalSize; DynamicBuffer = NULL; LxtCheckErrno(XattrTestCreatePaths(Fds)); // // Check for invalid parameters. // LxtCheckErrnoFailure(LxtSetxattr(0x1, Name, Buffer, sizeof(Buffer), 0), EFAULT); LxtCheckErrnoFailure(LxtLsetxattr(0x1, Name, Buffer, sizeof(Buffer), 0), EFAULT); LxtCheckErrnoFailure(LxtFsetxattr(-1, Name, Buffer, sizeof(Buffer), 0), EBADF); LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], 0x1, Buffer, sizeof(Buffer), 0), EFAULT); LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], 0x1, Buffer, sizeof(Buffer), 0), EFAULT); LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], 0x1, Buffer, sizeof(Buffer), 0), EFAULT); if (g_LxtFsInfo.FsType != LxtFsTypeVirtioFs) { LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], "invalid.name", Buffer, sizeof(Buffer), 0), ENOTSUP); LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], "invalid.name", Buffer, sizeof(Buffer), 0), ENOTSUP); LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], "invalid.name", Buffer, sizeof(Buffer), 0), ENOTSUP); LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], "security.", Buffer, sizeof(Buffer), 0), EINVAL); LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], "security.", Buffer, sizeof(Buffer), 0), EINVAL); LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], "security.", Buffer, sizeof(Buffer), 0), EINVAL); LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], "trusted.", Buffer, sizeof(Buffer), 0), EINVAL); LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], "trusted.", Buffer, sizeof(Buffer), 0), EINVAL); LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], "trusted.", Buffer, sizeof(Buffer), 0), EINVAL); LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], "user.", Buffer, sizeof(Buffer), 0), EINVAL); LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], "user.", Buffer, sizeof(Buffer), 0), EINVAL); LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], "user.", Buffer, sizeof(Buffer), 0), EINVAL); } else { // The virtioFs implementation does not restrict the allowed attribute names. LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "invalid.name", Buffer, sizeof(Buffer), 0)); LxtCheckErrno(LxtLsetxattr(g_XattrPaths[0], "invalid.name", Buffer, sizeof(Buffer), 0)); LxtCheckErrno(LxtFsetxattr(Fds[0], "invalid.name", Buffer, sizeof(Buffer), 0)); } LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], "system.", Buffer, sizeof(Buffer), 0), ENOTSUP); LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], "system.", Buffer, sizeof(Buffer), 0), ENOTSUP); LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], "system.", Buffer, sizeof(Buffer), 0), ENOTSUP); AttrName = "user." "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" "ooooooooooo"; LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], AttrName, Buffer, sizeof(Buffer), 0), ERANGE); LxtCheckErrnoFailure(LxtLsetxattr(g_XattrPaths[0], AttrName, Buffer, sizeof(Buffer), 0), ERANGE); LxtCheckErrnoFailure(LxtFsetxattr(Fds[0], AttrName, Buffer, sizeof(Buffer), 0), ERANGE); LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], Name, Buffer, sizeof(Buffer), 10), EINVAL); // // Create an attribute and read it back, using various buffer sizes. // LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], "user.test", "1234", 4, 0)); LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], "user.test", NULL, 0)); LxtCheckEqual(Size, 4, "%d"); LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], "user.test", Buffer, sizeof(Buffer))); LxtCheckEqual(Size, 4, "%d"); Buffer[4] = '\0'; LxtCheckStringEqual(Buffer, "1234"); LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], "user.test", Buffer, 4)); LxtCheckEqual(Size, 4, "%d"); Buffer[4] = '\0'; LxtCheckStringEqual(Buffer, "1234"); LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[0], "user.test", Buffer, 3), ERANGE); LxtCheckErrnoZeroSuccess(LxtRemovexattr(g_XattrPaths[0], "user.test")); // // Create a max length attribute, and ensure that it exists afterwards. // // N.B. Max length is based on ext4 limits; LxFs and DrvFs allow bigger // attributes. // DynamicBuffer = malloc(LXT_XATTR_SIZE_MAX); for (Index = 0; Index < LXT_XATTR_SIZE_MAX; Index += 1) { DynamicBuffer[Index] = (char)Index; } LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], "user.0", DynamicBuffer, LXT_XATTR_SIZE_MAX, 0)); LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], "user.0", NULL, 0)); LxtCheckEqual(Size, LXT_XATTR_SIZE_MAX, "%d"); LxtCheckErrnoZeroSuccess(LxtRemovexattr(g_XattrPaths[0], "user.0")); Count = 0; memset(TestXattrs, 0, sizeof(TestXattrs)); // // Create a zero length attribute. // LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], "user.zero", NULL, 0, 0)); LxtCheckErrnoZeroSuccess(LxtGetxattr(g_XattrPaths[0], "user.zero", NULL, 0)); strncpy(TestXattrs[Count].Name, "user.zero", sizeof(TestXattrs[Count].Name) - 1); Count += 1; // // Create an attribute with the maximum name length. // if (g_LxtFsInfo.Flags.DrvFsBehavior != 0) { AttrName = "user." "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" "oooooooooooooo"; } else { AttrName = "user." "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" "oooooooooooooooooo"; } LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], AttrName, NULL, 0, 0)); LxtCheckErrnoZeroSuccess(LxtGetxattr(g_XattrPaths[0], AttrName, NULL, 0)); strncpy(TestXattrs[Count].Name, AttrName, sizeof(TestXattrs[Count].Name) - 1); Count += 1; // // A bunch of attributes to exercise listing. // for (int Index = 0; Index < 100; Index += 1) { snprintf(TestXattrs[Count].Name, sizeof(TestXattrs[Count].Name), "user.test%d", Index); LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], TestXattrs[Count].Name, DynamicBuffer, 10, 0)); Count += 1; } // // Check the behavior of XATTR_CREATE. // LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], "user.test0", NULL, 0)); LxtCheckEqual(Size, 10, "%d"); LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], "user.test0", DynamicBuffer, 15, XATTR_CREATE), EEXIST); LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], "user.test0", NULL, 0)); LxtCheckEqual(Size, 10, "%d"); LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], "user.new", DynamicBuffer, 15, XATTR_CREATE)); LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], "user.new", NULL, 0)); LxtCheckEqual(Size, 15, "%d"); strncpy(TestXattrs[Count].Name, "user.new", sizeof(TestXattrs[Count].Name) - 1); Count += 1; // // Check the behavior of XATTR_REPLACE. // LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], "user.test0", NULL, 0)); LxtCheckEqual(Size, 10, "%d"); LxtCheckErrnoFailure(LxtSetxattr(g_XattrPaths[0], "user.new2", DynamicBuffer, 15, XATTR_REPLACE), ENODATA); LxtCheckErrnoFailure(LxtGetxattr(g_XattrPaths[0], "user.new2", NULL, 0), ENODATA); LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], "user.new", DynamicBuffer, 10, XATTR_REPLACE)); LxtCheckErrno(Size = GetXattr(g_XattrPaths[0], "user.new", NULL, 0)); LxtCheckEqual(Size, 10, "%d"); // // Set a zero-length extended attribute with XATTR_REPLACE. // // N.B. Plan 9 does not support this, as it treats this operation like a // remove. // if (g_LxtFsInfo.FsType != LxtFsTypePlan9) { LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], "user.new", NULL, 0, XATTR_REPLACE)); LxtCheckErrnoZeroSuccess(LxtGetxattr(g_XattrPaths[0], "user.new", NULL, 0)); LxtCheckErrnoZeroSuccess(LxtSetxattr(g_XattrPaths[0], "user.new", "", 0, XATTR_REPLACE)); LxtCheckErrnoZeroSuccess(LxtGetxattr(g_XattrPaths[0], "user.new", NULL, 0)); } // // List the attributes. // TotalCount = Count; for (Index = 0; Index < 10; Index += 1) { LxtCheckErrno(DynamicBufferSize = LxtListxattr(g_XattrPaths[0], 0, NULL)); LxtLogInfo("listxattr returned %d", DynamicBufferSize); DynamicBuffer = realloc(DynamicBuffer, DynamicBufferSize); if (DynamicBuffer == NULL) { Result = LXT_RESULT_FAILURE; LxtLogError("realloc(%p %d) failed", DynamicBuffer, DynamicBufferSize); goto ErrorExit; } // // Ensure that the number of extended attributes returned matches the number created. // DynamicBufferSize = LxtListxattr(g_XattrPaths[0], DynamicBuffer, DynamicBufferSize); if (DynamicBufferSize > 0) { break; } else if (errno != ERANGE) { LxtLogError("listxattr returned %d", errno); goto ErrorExit; } // // Sleep before retrying. // sleep(1); } if (DynamicBufferSize < 0) { LxtLogError("listxattr returned %d", errno); Result = LXT_RESULT_FAILURE; goto ErrorExit; } for (Index = 0; Index < DynamicBufferSize; Index += strlen(&DynamicBuffer[Index]) + 1) { for (NameIndex = 0; NameIndex < TotalCount; NameIndex += 1) { if (strcmp(TestXattrs[NameIndex].Name, &DynamicBuffer[Index]) == 0) { if (TestXattrs[NameIndex].Found != false) { LxtLogError("Duplicate attribute: %s", &DynamicBuffer[Index]); Result = LXT_RESULT_FAILURE; goto ErrorExit; } TestXattrs[NameIndex].Found = true; break; } } if (NameIndex == TotalCount) { LxtLogError("Unknown attribute in listing: %s", &DynamicBuffer[Index]); Result = LXT_RESULT_FAILURE; goto ErrorExit; } LxtRemovexattr(g_XattrPaths[0], &DynamicBuffer[Index]); Count -= 1; } for (NameIndex = 0; NameIndex < TotalCount; NameIndex += 1) { if (TestXattrs[NameIndex].Found == false) { LxtLogError("Attribute missing from listing: %s", TestXattrs[NameIndex].Name); Result = LXT_RESULT_FAILURE; goto ErrorExit; } } LxtCheckEqual(Count, 0, "%d"); LxtCheckEqual(LxtListxattr(g_XattrPaths[0], DynamicBuffer, DynamicBufferSize), 0, "%d"); // // Ensure that two extended attributes with the same name but different // cases can be created. // // N.B. DrvFs, WslFs and Plan 9 do not support this. // if (g_LxtFsInfo.Flags.DrvFsBehavior == 0) { LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.foo", NULL, 0, 0)); LxtCheckErrno(LxtSetxattr(g_XattrPaths[0], "user.FOO", NULL, 0, 0)); LxtCheckErrno(Result = LxtListxattr(g_XattrPaths[0], Buffer, sizeof(Buffer))); Count = 0; for (Index = 0; Index < Result; Index += strlen(&Buffer[Index]) + 1) { LxtLogInfo("%s", &Buffer[Index]); Count += 1; } LxtCheckEqual(Count, 2, "%d"); } Result = LXT_RESULT_SUCCESS; ErrorExit: XattrTestDeletePaths(Fds); if (DynamicBuffer != NULL) { free(DynamicBuffer); } return (int)Result; } int XattrAccessTest(PLXT_ARGS Args) /*++ --*/ { // Example system.posix_acl_access structure created by "setfacl -m u:root:r" char AclAccess[] = {2, 0, 0, 0, 1, 0, 6, 0, 0xff, 0xff, 0xff, 0xff, 2, 0, 4, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0xff, 0xff, 0xff, 0xff, 16, 0, 4, 0, 0xff, 0xff, 0xff, 0xff, 32, 0, 4, 0, -1, -1, -1, -1}; // Example security.capability structure created by "setcap cap_net_raw+ep" char Capability[] = {1, 0, 0, 2, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; char Buffer[1024]; struct __user_cap_data_struct CapData[2]; struct __user_cap_header_struct CapHeader; int ChildPid; int Fd; ssize_t Result; ChildPid = -1; Fd = -1; LxtCheckErrno(Fd = creat(LXT_XATTR_ACCESS_FILE_PATH, 0777)); /* // // TODO: Enable this test variation once extended attributes in the system // namespace are supported. // // // Set the system.posix_acl_access EA and validate access to get, set, and list. // LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", AclAccess, sizeof(AclAccess), 0)); LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", Buffer, sizeof(Buffer))); LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0000)); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); LxtCheckErrno(setgid(LXT_XATTR_GID)); LxtCheckErrno(setuid(LXT_XATTR_UID)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_FOWNER)].permitted |= CAP_TO_MASK(CAP_FOWNER); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); LxtCheckEqual(Result, sizeof("system.posix_acl_access"), "%Iu"); LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", Buffer, sizeof(Buffer))); LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", AclAccess, sizeof(AclAccess), 0)); // // Drop the CAP_FOWNER capability and attempt again. // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); LxtCheckEqual(Result, sizeof("system.posix_acl_access"), "%Iu"); LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", Buffer, sizeof(Buffer))); LxtCheckErrnoFailure(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", AclAccess, sizeof(AclAccess), 0), EPERM); Result = LXT_RESULT_SUCCESS; goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0000)); // // Set the system.posix_acl_access EA and ensure it is able to be queried. // LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", AclAccess, sizeof(AclAccess), 0)); LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", Buffer, sizeof(Buffer))); LxtCheckErrno(chown(LXT_XATTR_ACCESS_FILE_PATH, LXT_XATTR_UID, LXT_XATTR_GID)); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); LxtCheckErrno(setgid(LXT_XATTR_GID)); LxtCheckErrno(setuid(LXT_XATTR_UID)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); LxtCheckEqual(Result, sizeof("system.posix_acl_access"), "%Iu"); LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", Buffer, sizeof(Buffer))); LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", AclAccess, sizeof(AclAccess), 0)); // // Drop the CAP_DAC_OVERRIDE capability and attempt again. // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); LxtCheckEqual(Result, sizeof("system.posix_acl_access"), "%Iu"); LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", Buffer, sizeof(Buffer))); LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access", AclAccess, sizeof(AclAccess), 0)); Result = LXT_RESULT_SUCCESS; goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(LxtRemovexattr(LXT_XATTR_ACCESS_FILE_PATH, "system.posix_acl_access")); LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0777)); */ // // Set the security.capability EA and validate access to get, set, and list. // LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Capability, sizeof(Capability), 0)); LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Buffer, sizeof(Buffer))); LxtCheckEqual(Result, sizeof(Capability), "%Iu"); LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0000)); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); LxtCheckErrno(setgid(LXT_XATTR_GID)); LxtCheckErrno(setuid(LXT_XATTR_UID)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SETFCAP)].permitted |= CAP_TO_MASK(CAP_SETFCAP); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); LxtCheckEqual(Result, sizeof("security.capability"), "%Iu"); LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Buffer, sizeof(Buffer))); LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Capability, sizeof(Capability), 0)); // // Drop the CAP_SETFCAP capability and attempt again. // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); LxtCheckEqual(Result, sizeof("security.capability"), "%Iu"); LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Buffer, sizeof(Buffer))); LxtCheckEqual(Result, sizeof(Capability), "%Iu"); LxtCheckErrnoFailure(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Capability, sizeof(Capability), 0), EPERM); Result = LXT_RESULT_SUCCESS; goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); // // Validate that the security.capability EA is removed when the file changes owners. // LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Capability, sizeof(Capability), 0)); LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Buffer, sizeof(Buffer))); LxtCheckErrno(chown(LXT_XATTR_ACCESS_FILE_PATH, 0, 0)); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); LxtCheckEqual(Result, 0, "%Iu"); LxtCheckErrnoFailure(LxtGetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Buffer, sizeof(Buffer)), ENODATA); LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Capability, sizeof(Capability), 0)); LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Buffer, sizeof(Buffer))); LxtCheckEqual(Result, sizeof(Capability), "%Iu"); LxtCheckErrno(LxtRemovexattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability")); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); LxtCheckEqual(Result, 0, "%Iu"); LxtCheckErrnoFailure(LxtGetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.capability", Buffer, sizeof(Buffer)), ENODATA); LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0777)); // // Set the security.foo EA and validate access to get, set, and list. // LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Capability, sizeof(Capability), 0)); LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Buffer, sizeof(Buffer))); LxtCheckEqual(Result, sizeof(Capability), "%Iu"); LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0000)); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); LxtCheckErrno(setgid(LXT_XATTR_GID)); LxtCheckErrno(setuid(LXT_XATTR_UID)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SYS_ADMIN)].permitted |= CAP_TO_MASK(CAP_SYS_ADMIN); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); LxtCheckEqual(Result, sizeof("security.foo"), "%Iu"); LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Buffer, sizeof(Buffer))); LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Capability, sizeof(Capability), 0)); // // Drop the CAP_SYS_ADMIN capability and attempt again. // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); LxtCheckEqual(Result, sizeof("security.foo"), "%Iu"); LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Buffer, sizeof(Buffer))); LxtCheckEqual(Result, sizeof(Capability), "%Iu"); LxtCheckErrnoFailure(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Capability, sizeof(Capability), 0), EPERM); Result = LXT_RESULT_SUCCESS; goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); // // Validate that the security.foo EA is not removed when the file changes owners. // LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Capability, sizeof(Capability), 0)); LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Buffer, sizeof(Buffer))); LxtCheckErrno(chown(LXT_XATTR_ACCESS_FILE_PATH, 0, 0)); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); LxtCheckEqual(Result, sizeof("security.foo"), "%Iu"); LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Buffer, sizeof(Buffer))); LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Capability, sizeof(Capability), 0)); LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Buffer, sizeof(Buffer))); LxtCheckEqual(Result, sizeof(Capability), "%Iu"); LxtCheckErrno(LxtRemovexattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo")); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); LxtCheckEqual(Result, 0, "%Iu"); LxtCheckErrnoFailure(LxtGetxattr(LXT_XATTR_ACCESS_FILE_PATH, "security.foo", Buffer, sizeof(Buffer)), ENODATA); LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0777)); // // Set the user.foo EA and ensure it is able to be queried. // LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0)); LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", NULL, 0)); LxtCheckErrno(chown(LXT_XATTR_ACCESS_FILE_PATH, LXT_XATTR_UID, LXT_XATTR_GID)); LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0700)); // // Fork and change the child to a UID that does not own the file. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(setuid(LXT_XATTR_UID)); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); LxtCheckEqual(Result, sizeof("user.foo"), "%Iu"); LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", NULL, 0)); LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0)); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0070)); // // Fork and change the child to a UID that does not own the file. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { // // First try with the CAP_DAC_OVERRIDE capability. // LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); LxtCheckErrno(setuid(LXT_XATTR_UID + 1)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); LxtCheckEqual(Result, sizeof("user.foo"), "%Iu"); LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", NULL, 0)); LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0)); // // Drop the CAP_DAC_OVERRIDE capability and attempt again. // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); LxtCheckEqual(Result, sizeof("user.foo"), "%Iu"); LxtCheckErrnoFailure(LxtGetxattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", NULL, 0), EACCES); LxtCheckErrnoFailure(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0), EACCES); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0007)); // // Fork and change the child to a UID that does not own the file with the // other bits set. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(setuid(LXT_XATTR_UID + 1)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); LxtCheckEqual(Result, sizeof("user.foo"), "%Iu"); LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", NULL, 0)); LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0)); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0002)); // // Fork and change the child to a UID that does not own the file with the // other bits set. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(setuid(LXT_XATTR_UID + 1)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); LxtCheckEqual(Result, sizeof("user.foo"), "%Iu"); LxtCheckErrnoFailure(LxtGetxattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", NULL, 0), EACCES); LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0)); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0004)); // // Fork and change the child to a UID that does not own the file with the // other bits set. // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(setuid(LXT_XATTR_UID + 1)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); LxtCheckEqual(Result, sizeof("user.foo"), "%Iu"); LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", NULL, 0)); LxtCheckErrnoFailure(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo", LXT_XATTR_TEST_VALUE, LXT_XATTR_TEST_LENGTH, 0), EACCES); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); LxtCheckErrno(LxtRemovexattr(LXT_XATTR_ACCESS_FILE_PATH, "user.foo")); LxtCheckErrno(chown(LXT_XATTR_ACCESS_FILE_PATH, 0, 0)); LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0777)); // // Test the trusted namespace, the caller requires the CAP_SYS_ADMIN // capability to read to write EA's in the trusted namespace. // Buffer[0] = 'y'; LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "trusted.overlay.opaque", Buffer, 1, 0)); LxtCheckErrno(Result = GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "trusted.overlay.opaque", Buffer, sizeof(Buffer))); LxtCheckErrno(chmod(LXT_XATTR_ACCESS_FILE_PATH, 0000)); LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(prctl(PR_SET_KEEPCAPS, 1)); memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; CapData[CAP_TO_INDEX(CAP_SYS_ADMIN)].permitted |= CAP_TO_MASK(CAP_SYS_ADMIN); CapData[0].effective = CapData[0].permitted; CapData[1].effective = CapData[1].permitted; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); LxtCheckEqual(Result, sizeof("trusted.overlay.opaque"), "%Iu"); LxtCheckErrno(GetXattr(LXT_XATTR_ACCESS_FILE_PATH, "trusted.overlay.opaque", NULL, 0)); LxtCheckErrno(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "trusted.overlay.opaque", Buffer, 1, 0)); // // Drop the CAP_SYS_ADMIN capability and attempt again. // // N.B. Unlike other namespaces, names in the trusted namespace will // not be returned if the caller does not have the correct permission. // This is file system specific, and Plan 9 does not do this. // memset(&CapData, 0, sizeof(CapData)); memset(&CapHeader, 0, sizeof(CapHeader)); CapHeader.version = _LINUX_CAPABILITY_VERSION_3; LxtCheckErrno(LxtCapSet(&CapHeader, CapData)); LxtCheckErrno(Result = LxtListxattr(LXT_XATTR_ACCESS_FILE_PATH, 0, NULL)); if ((g_LxtFsInfo.FsType == LxtFsTypePlan9) || (g_LxtFsInfo.FsType == LxtFsTypeVirtioFs)) { LxtCheckEqual(Result, sizeof("trusted.overlay.opaque"), "%Iu"); } else { LxtCheckEqual(Result, 0, "%Iu"); } LxtCheckErrnoFailure(LxtGetxattr(LXT_XATTR_ACCESS_FILE_PATH, "trusted.overlay.opaque", NULL, 0), ENODATA); LxtCheckErrnoFailure(LxtSetxattr(LXT_XATTR_ACCESS_FILE_PATH, "trusted.overlay.opaque", Buffer, 1, 0), EPERM); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckErrno(LxtWaitPidPoll(ChildPid, LXT_RESULT_SUCCESS)); Result = LXT_RESULT_SUCCESS; ErrorExit: if (Fd != 0) { LxtClose(Fd); } if (ChildPid == 0) { _exit(Result); } unlink(LXT_XATTR_ACCESS_FILE_PATH); return (int)Result; }