• Linux 安全 - 扩展属性xattr


    前言

    一、简介

    xattr - Extended attributes
    
    • 1

    扩展属性是与文件和目录永久关联的名称-值对,类似于与进程关联的环境变量。属性可以被定义或未定义。如果属性被定义,其值可以为空或非空。
    扩展属性提供了一种方式,可以在文件和目录上关联额外的元数据或信息,超出了存储在索引节点(inode)中的标准属性。它们可以用于增强文件系统的功能,实现访问控制、标记、版本控制等功能。

    扩展属性是对系统中所有索引节点(即 stat(2) 数据)关联的普通属性的扩展。它们通常用于为文件系统提供附加功能,例如可以使用扩展属性来实现额外的安全功能,比如访问控制列表(ACL)。
    ACL 允许更细粒度地控制文件和目录的权限,为不同的用户或组指定访问权限,超出传统的所有者-组-其他权限模型。

    具有对文件或目录的搜索访问权限的用户可以使用 listxattr(2) 系统调用检索为该文件或目录定义的属性名称列表。使用 getxattr(2) 系统调用读取特定属性的值,而使用 setxattr(2) 系统调用设置或修改属性的值。
    读取(getxattr(2))会检索属性的整个值并将其存储在缓冲区中。写入(setxattr(2))会用新值替换任何先前的值。

    扩展属性被视为原子对象进行访问。扩展属性被视为原子对象进行访问,这意味着读取或写入属性是一个原子操作。在读取时,会检索属性的整个值并存储在缓冲区中。在写入时,新值将替换与该属性关联的任何先前值。

    扩展属性可以组织到命名空间中,提供一种方式来对属性进行分类和管理。命名空间有助于避免不同应用程序或系统之间可能使用的扩展属性之间的冲突。

    扩展属性占用的空间可能计入文件所有者和文件组的磁盘配额。
    扩展属性消耗的空间可能计入文件所有者和文件组的磁盘配额。这意味着扩展属性的大小可能会计入用户或组的配额限制。

    其中数字2表示系统调用。

    二、扩展属性命名空间

    2.1 简介

    属性名称:属性名称是以空字符(null-terminated)结尾的字符串。属性名称总是以完全限定的"命名空间.属性"形式指定,例如"user.mime_type"、“trusted.md5sum”、“system.posix_acl_access"或"security.selinux”。

    命名空间机制:命名空间机制用于定义不同类别的扩展属性。存在不同类别的原因有多种,例如在操作某个命名空间的扩展属性时所需的权限和能力可能与其他命名空间不同。

    扩展属性命名空间用于对扩展属性进行分类和归类。每个命名空间代表一个独立的属性类别或类,具有特定的目的和要求。

    命名空间中的属性名称采用完全限定的"命名空间.属性"格式。这种格式确保属性名称的唯一性和明确性,使得属性的识别和组织更加一致。

    当前定义的命名空间类别:Linux内核定义了几个扩展属性类别或命名空间,包括security(安全)、system(系统)、trusted(可信)和user(用户)。这些命名空间具有不同的目的,对于在命名空间内操作属性可能有不同的权限和能力要求。

    security(安全):用于存储与安全相关的扩展属性,例如安全上下文(security context)。
    system(系统):用于存储与系统操作和管理相关的扩展属性,例如文件的访问控制列表(ACL)。
    trusted(可信):用于存储需要特定信任级别的扩展属性,例如数字签名或校验和。
    user(用户):用于存储用户自定义的扩展属性,例如用户自定义元数据。

    未来可能添加的类别:在未来可能添加其他类别的扩展属性,以适应不同的需求和用例。

    通过使用扩展属性命名空间,可以将不同类别的扩展属性进行分类和管理,并根据需要定义适当的权限和操作要求。这提供了一种灵活的方式来处理不同类型的扩展属性,以满足系统和应用程序的特定需求。

    2.2 security扩展属性

    扩展安全属性(Extended Security Attributes)是使用安全属性命名空间的一种特殊类型的属性。它主要由内核安全模块(例如Security Enhanced Linux,SELinux)使用,用于在文件和进程上强制执行额外的安全策略和控制。这些属性通过提供细粒度的访问控制和安全标记来增强系统的整体安全性。

    扩展安全属性还用于实现文件能力,(参见capabilities)。文件能力允许为可执行文件授予特定的权限或特权,使其能够执行通常需要高权限的特定操作。这些能力以与文件关联的扩展安全属性的形式存储。

    capabilities请参考:Linux 安全 - Capabilities机制

    对扩展安全属性的读取和写入权限由安全模块为每个属性实施的策略决定。安全模块定义了访问和修改这些属性的规则和限制。当未加载任何安全模块时,所有进程都具有对扩展安全属性的读取访问权限,而写入访问权限仅限于具有CAP_SYS_ADMIN特权的进程。CAP_SYS_ADMIN是一种授予管理特权的强大能力。这确保了系统的基本安全性,并防止未经授权的更改或篡改安全属性。

    扩展安全属性通过允许实施强制访问控制(MAC)策略、安全上下文、访问控制规则和其他安全相关功能,提供了额外的安全层级。这些属性提供了一种强制访问限制的方式,确保只有经过授权的进程或用户可以与敏感文件或资源进行交互。

    在具体的实现中,扩展安全属性用于增强系统安全性,并允许对文件和进程应用额外的安全策略和权限控制。这些属性可以包括安全上下文、访问控制规则、安全标签等。安全模块负责管理和保护这些属性的访问,确保只有经过授权的进程才能读取和修改安全属性。

    需要注意的是,具体的访问权限和策略可能因不同的系统配置和安全模块而有所变化。因此,在实际应用中,需要根据特定的系统设置和需求来配置和管理扩展安全属性以确保系统的安全性。

    2.3 System扩展属性

    系统扩展属性(System Extended Attributes)是内核用于存储系统对象的属性,例如访问控制列表(Access Control Lists)。对系统属性的读取和写入权限取决于内核中文件系统为每个系统属性实施的策略。

    系统扩展属性主要用于存储与系统操作和管理相关的数据和信息。其中包括但不限于访问控制列表(ACLs)、扩展文件属性(Extended File Attributes)、索引节点(Inodes)等。这些属性允许存储额外的元数据和相关信息,以实现更丰富的文件和对象管理。

    对于系统扩展属性的访问权限,取决于内核中文件系统对每个系统属性实施的策略。不同的文件系统可能会有不同的策略,以确定哪些进程或用户可以读取和修改系统属性。这些策略可能涉及访问控制规则、权限设置以及与安全模块的集成。

    需要注意的是,系统扩展属性的读取和写入权限通常受到更严格的限制,以确保对系统对象的操作和管理受到适当的控制。具体的权限和策略将根据所使用的文件系统和内核配置而有所不同。

    系统扩展属性在提供更丰富的系统管理功能和元数据存储方面起着重要作用。它们允许操作系统和文件系统在内核级别存储和管理与系统对象相关的信息,并实施适当的访问控制以保护系统的完整性和安全性。

    2.4 Trusted扩展属性

    可信扩展属性(Trusted Extended Attributes)仅对具有CAP_SYS_ADMIN能力的进程可见和可访问。此类属性用于在用户空间(即内核之外)实现机制,以保持扩展属性中的信息,而普通进程不应该访问这些属性。

    可信扩展属性是一种特殊类型的扩展属性,其设计目的是提供一种安全机制,用于存储敏感信息或限制对特定数据的访问。这些属性的访问权限被限制在具有CAP_SYS_ADMIN能力的进程中,通常是系统管理员或具有特权的进程。

    通过将信息存储在可信扩展属性中,可以确保普通进程无法访问或修改这些属性中的敏感信息。这种机制可以用于实现安全策略、访问控制限制、数字签名验证或其他需要保护数据完整性和机密性的功能。

    需要注意的是,可信扩展属性主要用于用户空间的机制和应用程序,而不是内核本身。这意味着保护和限制对可信扩展属性的访问通常是通过用户空间的工具、库或应用程序来实现。内核通过授予CAP_SYS_ADMIN能力来管理对这些属性的访问权限。

    总结而言,可信扩展属性提供了一种在用户空间中实现机制的方式,以存储敏感信息并限制对其的访问。这种属性仅对具有CAP_SYS_ADMIN能力的进程可见和可访问,从而确保普通进程无法访问或修改这些属性中的信息。这提供了一种安全机制,用于保护敏感数据和实施访问控制。

    2.5 User扩展属性

    用户扩展属性(User Extended Attributes)是一种灵活的机制,用于为文件和目录存储额外的元数据或信息,超出传统文件属性的范围。它允许用户和应用程序将自定义数据附加到文件上,例如标签、描述、评级或任何其他对文件组织和管理有用的任意信息。

    用户扩展属性以键值对的形式与各个文件或目录关联存储。每个属性都有一个名称(键)和相应的值。可以独立于文件的内容、权限或其他属性来访问和修改这些属性。

    访问权限方面,用户扩展属性的访问受文件的权限位控制。要检索属性的值,需要对文件具有读取权限。要更改属性的值,需要对文件具有写入权限。这提供了一定程度的控制,以决定谁可以访问和修改与文件或目录关联的扩展属性。

    对于特殊文件(如设备文件)和符号链接,用户扩展属性通常仅限于常规文件和目录。这是为了防止在特殊文件或目录上允许用户扩展属性时可能出现的滥用或资源消耗问题。

    用户扩展属性有多种实际用途。例如,它们可以用于存储与媒体文件相关的元数据,如艺术家名称、专辑标题或持续时间。它们还可以用于存储应用程序特定的信息,如配置设置或偏好设置。此外,某些桌面环境或文件管理器使用用户扩展属性来存储自定义信息,以增强文件组织或用户体验。

    总而言之,用户扩展属性提供了一种灵活的机制,可以为文件和目录附加自定义的元数据或附加信息。它们扩展了文件系统的功能,并使用户和应用程序能够存储和检索与其特定需求相关的自定义数据。

    三、用户空间使用

    3.1 setfattr/getfattr

    setfattr/getfattr这两个命令属于用户空间的二进制程序。
    (1)

    NAME
           setfattr - set extended attributes of filesystem objects
    
    • 1
    • 2
    DESCRIPTION
           The setfattr command associates a new value with an extended attribute name for each specified file.
    
    • 1
    • 2
    OPTIONS
           -n name, --name=name
               Specifies the name of the extended attribute to set.
               
           -v value, --value=value
               Specifies  the  new  value  of the extended attribute.
    
           -x name, --remove=name
               Remove the named extended attribute entirely.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    setfattr命令用于设置文件的扩展属性(命名属性)。

    使用语法:

    setfattr [-n <属性名称>] [-v <属性值>] <文件路径>
    
    • 1

    参数说明:

    -n <属性名称>:指定要设置的属性名称。
    -v <属性值>:指定要设置的属性值。
    <文件路径>:要设置属性的文件路径。
    
    • 1
    • 2
    • 3

    (2)

    NAME
           getfattr - get extended attributes of filesystem objects
    
    • 1
    • 2
    DESCRIPTION
           For each file, getfattr displays the file name, and the set of extended attribute names (and optionally values) which are associated with that file.
    
           The output format of getfattr -d is as follows:
                   1:  # file: somedir/
                   2:  user.name0="value0"
                   3:  user.name1="value1"
                   4:  user.name2="value2"
                   5:  ...
    
           Line  1  identifies the file name for which the following lines are being reported.  The remaining lines (lines 2 to 4 above) show the name and value pairs associated with
           the specified file.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    OPTIONS
           -n name, --name=name
               Dump the value of the named extended attribute extended attribute.
    
           -d, --dump
               Dump the values of all extended attributes associated with pathname.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    getfattr命令用于获取文件的扩展属性。它可以用于检索文件的命名属性及其对应的值。

    使用语法:

    getfattr [-d] [-n <属性名称>] <文件路径>
    
    • 1

    参数说明:

    -d:以人类可读的格式显示属性值。
    -n <属性名称>:指定要获取的属性名称。
    <文件路径>:要获取属性的文件路径。
    
    • 1
    • 2
    • 3

    (3)

    # getfattr -d 1.txt
    # setfattr -n user.test -v "Hello World" 1.txt
    # getfattr -d 1.txt
    # file: 1.txt
    user.test="Hello World"
    
    # setfattr -n user.example -v "Hello World" 1.txt
    # getfattr -d 1.txt
    # file: 1.txt
    user.example="Hello World"
    user.test="Hello World"
    
    # setfattr -x user.test 1.txt
    # getfattr -d 1.txt
    # file: 1.txt
    user.example="Hello World"
    
    # setfattr -x user.example 1.txt
    # getfattr -d 1.txt
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    # getfattr -n user.test 1.txt
    # file: 1.txt
    user.test="Hello World"
    
    # getfattr 1.txt
    # file: 1.txt
    user.test
    
    # getfattr -n user.test 1.txt
    # file: 1.txt
    user.test="Hello World"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3.2 setxattr/getxattr/listxattr

    setfattr,getfattr是用户空间二进制程序,而setxattr,getxattr,listxattr是对应的系统调用。setfattr,getfattr在内部调用了setxattr,getxattr,listxattr对应的系统调用。

    # strace -e trace=file setfattr -n user.example -v "hello world" 1.txt
    ......
    setxattr("1.txt", "user.example", "hello world", 11, 0) = 0
    
    • 1
    • 2
    • 3
    # strace -e trace=file getfattr -n user.example 1.txt
    ......
    getxattr("1.txt", "user.example", NULL, 0) = 12
    getxattr("1.txt", "user.example", "Hello World", 256) = 12
    
    • 1
    • 2
    • 3
    • 4
    # strace -e trace=file getfattr 1.txt
    ......
    listxattr("1.txt", NULL, 0)             = 30
    listxattr("1.txt", "security.selinux\0user.example\0", 256) = 30
    
    • 1
    • 2
    • 3
    • 4

    (1)

    setxattr, lsetxattr, fsetxattr - set an extended attribute value
    
    • 1
     #include 
    
     int setxattr(const char *path, const char *name,
                   const void value[.size], size_t size, int flags);
     int lsetxattr(const char *path, const char *name,
                   const void value[.size], size_t size, int flags);
     int fsetxattr(int fd, const char *name,
                   const void value[.size], size_t size, int flags);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    setxattr()函数用于在文件系统中设置与给定路径关联的名称标识的扩展属性的值。size参数指定value的大小(以字节为单位),允许使用零长度的value。

    简而言之,setxattr()函数通过给定的路径和名称标识,在文件系统中设置扩展属性的值。扩展属性允许您为文件添加自定义的属性,这些属性不同于文件的常规属性(例如文件大小、权限等)。

    lsetxattr()函数与setxattr()函数相同,只是在符号链接的情况下,扩展属性设置在符号链接本身上,而不是它所指向的文件上。

    fsetxattr()函数与setxattr()函数相同,只是将扩展属性设置在由fd引用的打开文件上(由open(2)返回的文件描述符),而不是通过路径来指定。

    简而言之,lsetxattr()函数用于在符号链接上设置扩展属性,而不是链接所指向的文件。fsetxattr()函数用于在由文件描述符引用的打开文件上设置扩展属性。

    这些函数提供了在不同情况下设置扩展属性的选项,以适应不同的使用场景。

    一个简单的c语言程序:

    #include 
    #include 
    #include 
    
    int main() {
        const char *path = "1.txt";
        const char *name = "user.example";
        const char *value = "Hello World";
        size_t size = strlen(value) + 1; // 包括 NULL 终止符
    
        int result = setxattr(path, name, value, size, 0);
        if (result == -1) {
            perror("setxattr");
            return 1;
        }
    
        printf("属性设置成功!\n");
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    (2)

    getxattr, lgetxattr, fgetxattr - retrieve an extended attribute value
    
    • 1
     #include 
    
     ssize_t getxattr(const char *path, const char *name,
                      void value[.size], size_t size);
     ssize_t lgetxattr(const char *path, const char *name,
                      void value[.size], size_t size);
     ssize_t fgetxattr(int fd, const char *name,
                      void value[.size], size_t size);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    getxattr()函数用于检索与文件系统中给定路径关联的名称标识的扩展属性的值。属性值将存放在由value指向的缓冲区中,size指定了该缓冲区的大小。调用的返回值是放入value中的字节数。

    简而言之,getxattr()函数通过给定的路径和名称标识,在文件系统中检索扩展属性的值。您可以提供一个缓冲区来存储属性值,并指定缓冲区的大小。函数将返回实际放入缓冲区中的字节数,以便您知道属性值的大小。

    lgetxattr()函数与getxattr()函数相似,只是在符号链接的情况下,会直接获取符号链接本身的扩展属性值,而不是链接所指向的文件的属性值。

    fgetxattr()函数与getxattr()函数也相似,只是它会直接获取由文件描述符fd(通过open(2)返回的文件描述符)引用的打开文件的扩展属性值,而不是通过路径指定文件。

    这两个函数提供了在不同情况下获取扩展属性值的选项。lgetxattr()用于获取符号链接本身的扩展属性值,而不是链接所指向的文件的属性值。fgetxattr()用于获取由文件描述符引用的打开文件的扩展属性值。

    一个简单的c语言程序:

    #include 
    #include 
    #include 
    
    int main() {
        const char *path = "1.txt";
        const char *attrName = "user.example";
        char buffer[256];
    
        ssize_t bytesRead = getxattr(path, attrName, buffer, sizeof(buffer));
        if (bytesRead == -1) {
            perror("getxattr");
            return 1;
        }
    
        printf("Attribute value: %.*s\n", (int)bytesRead, buffer);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    (3)

     listxattr, llistxattr, flistxattr - list extended attribute names
    
    • 1
     #include 
    
     ssize_t listxattr(const char *path, char *_Nullable list, size_t size);
     ssize_t llistxattr(const char *path, char *_Nullable list, size_t size);
     ssize_t flistxattr(int fd, char *_Nullable list, size_t size);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    listxattr()函数用于检索与给定路径在文件系统中关联的扩展属性名称列表。检索到的列表将放置在list参数所指向的由调用者分配的缓冲区中,缓冲区的大小以字节为单位由size参数指定。列表中的名称是以空字符(‘\0’)作为分隔的,依次排列。调用进程无权访问的扩展属性名称可能会被省略在列表之外。函数返回属性名称列表的长度。

    listxattr()系统调用用于获取文件或目录的所有扩展属性的名称列表。它返回一个以空字符(‘\0’)分隔的属性名称字符串列表。

    注意listxattr()系统调用只是获取文件的属性名称列表,没有获取属性名称的值,要获取属性名称的值,需要调用getxattr()系统用调用。

    参数说明:

    path:要获取扩展属性列表的文件或目录的路径名。
    list:存储属性名称列表的缓冲区。
    size:缓冲区的大小。
    
    • 1
    • 2
    • 3

    返回值:

    如果成功获取到属性名称列表,返回值为列表中所有属性名称的总字节数(包括空字符分隔符)。
    如果指定的文件或目录不存在扩展属性或给定的缓冲区大小不足以容纳属性名称列表,返回值为-1,并设置errno为相应的错误代码。
    
    • 1
    • 2

    llistxattr()函数用于获取与符号链接本身关联的扩展属性名称列表,而不是链接所指向的文件的属性。

    flistxattr()函数用于在打开的文件描述符fd上进行查询,而不是通过路径名进行查询。

    一个简单的c语言程序:

    #include 
    #include 
    
    #include 
    #include 
    
    int main() {
        const char *path = "1.txt";
        char buffer[1024];
    
        ssize_t attrListLength = listxattr(path, buffer, sizeof(buffer));
        if (attrListLength == -1) {
            perror("listxattr");
            return 1;
        }
    
        // Iterate over the attribute names in the buffer
        char *attrName = buffer;
        while (attrListLength > 0) {
            printf("Attribute name: %s\n", attrName);
            size_t attrNameLength = strlen(attrName) + 1;  // Include the null terminator
            attrName += attrNameLength;
            attrListLength -= attrNameLength;
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    增加获取扩展属性名称的值:

    #include 
    #include 
    #include 
    
    #include 
    #include 
    
    int main() {
        const char *path = "1.txt";
        char buffer[1024];
    
        ssize_t attrListLength = listxattr(path, buffer, sizeof(buffer));
        if (attrListLength == -1) {
            perror("listxattr");
            return 1;
        }
    
        // Iterate over the attribute names in the buffer
        char *attrName = buffer;
        while (attrListLength > 0) {
            char attrValue[1024];
            ssize_t attrValueLength = getxattr(path, attrName, attrValue, sizeof(attrValue));
            if (attrValueLength == -1) {
                perror("getxattr");
                // Continue to the next attribute
            } else {
                printf("Attribute name: %s\n", attrName);
                printf("Attribute value: %.*s\n", (int)attrValueLength, attrValue);
            }
    
            size_t attrNameLength = strlen(attrName) + 1;  // Include the null terminator
            attrName += attrNameLength;
            attrListLength -= attrNameLength;
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    参考资料

    https://man7.org/linux/man-pages/man7/xattr.7.html

  • 相关阅读:
    【C++】三大特性之继承
    javaSE学习笔记(五)集合框架-Collection,List,Set,Map,HashMap,Hashtable,ConcurrentHashMap
    ARM---CAN2.0B读取 汽车BMS报文
    3D 生成重建004-DreamFusion and SJC :TEXT-TO-3D USING 2D DIFFUSION
    Mybatis03-ResultMap及分页
    前端工程化面试题及答案【集合】
    微信小程序前端开发
    自动化测试生命周期的六个阶段
    【威联通】共享文件夹设置
    解析 RocketMQ 业务消息 - “顺序消息”
  • 原文地址:https://blog.csdn.net/weixin_45030965/article/details/134446963