[systemd-devel] [PATCH v2, ping?] tmpfiles, man: Add xattr support to tmpfiles

Maciej Wereski m.wereski at partner.samsung.com
Mon Jul 15 06:22:40 PDT 2013


This patch makes it possible to set extended attributes on files created
by tmpfiles. This can be especially used to set SMACK security labels on
volatile files and directories.

It is done by adding new line of type "t". Such line should contain
attributes in Argument field, using following format:

name=value

All other fields are ignored.

If value contains spaces, then it must be surrounded by quotation marks.
User can also put quotation mark in value by escaping it with backslash.

Example:
D /var/run/cups - - - -
t /var/run/cups - - - - security.SMACK64=printing
---
I've used "t" because IMHO "a" will be better for acl. To sum up: when
"t" is met and it's not in hashmap, then it will be added. Then if other
line for the same file appears, then it replaces SET_XATTR item in
hashmap and has extended attributes added. If item earler existed in
hashmap, then extended attributes are merged to existing entry. This
means that there can be more than one "t" lines for one file. There is
also posibility to have standalone "t" line. I hope that this is desired
behaviour.

regards,
Maciej
---
 man/tmpfiles.d.xml      |  26 ++++-
 src/tmpfiles/tmpfiles.c | 274 ++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 285 insertions(+), 15 deletions(-)

diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml
index 519f9bc..92157b5 100644
--- a/man/tmpfiles.d.xml
+++ b/man/tmpfiles.d.xml
@@ -229,6 +229,21 @@ L    /tmp/foobar -    -    -    -   /dev/null</programlisting>
                                         place of normal path
                                         names.</para></listitem>
                                 </varlistentry>
+
+                                <varlistentry>
+                                        <term><varname>t</varname></term>
+                                        <listitem><para>Set extended
+                                        attributes on item. It should be
+                                        used with conjunction with other
+                                        types (only d, D, f, F, L, p, c, b, z
+                                        makes sense). If used as a standalone
+                                        line, then <command>systemd-tmpfiles
+                                        </command> will try to set extended
+                                        attributes on specified path.
+                                        This can be especially used to set
+                                        SMACK labels.
+                                        </para></listitem>
+                                </varlistentry>
                         </variablelist>
                 </refsect2>
 
@@ -242,7 +257,7 @@ L    /tmp/foobar -    -    -    -   /dev/null</programlisting>
                         objects. For z, Z lines if omitted or when set
                         to - the file access mode will not be
                         modified. This parameter is ignored for x, r,
-                        R, L lines.</para>
+                        R, L, t lines.</para>
                 </refsect2>
 
                 <refsect2>
@@ -254,7 +269,7 @@ L    /tmp/foobar -    -    -    -   /dev/null</programlisting>
                         omitted or when set to - the default 0 (root)
                         is used. For z, Z lines when omitted or when set to -
                         the file ownership will not be modified.
-                        These parameters are ignored for x, r, R, L lines.</para>
+                        These parameters are ignored for x, r, R, L, t lines.</para>
                 </refsect2>
 
                 <refsect2>
@@ -307,8 +322,10 @@ L    /tmp/foobar -    -    -    -   /dev/null</programlisting>
                         minor formatted as integers, separated by :,
                         e.g. "1:3". For f, F, w may be used to specify
                         a short string that is written to the file,
-                        suffixed by a newline. Ignored for all other
+                        suffixed by a newline. Fot t determines extended
+                        attributes to be set. Ignored for all other
                         lines.</para>
+
                 </refsect2>
 
         </refsect1>
@@ -320,7 +337,8 @@ L    /tmp/foobar -    -    -    -   /dev/null</programlisting>
                         <para><command>screen</command> needs two directories created at boot with specific modes and ownership.</para>
 
                         <programlisting>d /var/run/screens  1777 root root 10d
-d /var/run/uscreens 0755 root root 10d12h</programlisting>
+d /var/run/uscreens 0755 root root 10d12h
+t /var/run/screen - - - - user.name="John Koval" security.SMACK64=screen</programlisting>
                 </example>
         </refsect1>
 
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 555347a..098413f 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -39,6 +39,9 @@
 #include <glob.h>
 #include <fnmatch.h>
 #include <sys/capability.h>
+#ifdef HAVE_XATTR
+#include <attr/xattr.h>
+#endif
 
 #include "log.h"
 #include "util.h"
@@ -75,7 +78,10 @@ typedef enum ItemType {
         REMOVE_PATH = 'r',
         RECURSIVE_REMOVE_PATH = 'R',
         RELABEL_PATH = 'z',
-        RECURSIVE_RELABEL_PATH = 'Z'
+        RECURSIVE_RELABEL_PATH = 'Z',
+
+        /* These ones are options/additional operations */
+        SET_XATTR = 't'
 } ItemType;
 
 typedef struct Item {
@@ -83,6 +89,7 @@ typedef struct Item {
 
         char *path;
         char *argument;
+        char **xattrs;
         uid_t uid;
         gid_t gid;
         mode_t mode;
@@ -447,6 +454,52 @@ static int item_set_perms(Item *i, const char *path) {
         return label_fix(path, false, false);
 }
 
+static int item_set_xattrs(Item *i, const char *path) {
+#ifdef HAVE_XATTR
+        char *name, *value;
+        char **x;
+        int n;
+        if (!i->xattrs)
+                return 0;
+        STRV_FOREACH(x, i->xattrs) {
+                value = *x;
+                name = strsep(&value, "=");
+                if (name == NULL || value == NULL) {
+                        log_warning("%s: %s is not valid xattr, ignoring.", path, *x);
+                        continue;
+                }
+                n = strlen(value);
+                if (value[n-1]  == '\"')
+                        value[n-1] = '\0';
+                if (value[0] == '\"')
+                        memmove(value, value+1, n);
+                value = strreplace(value, "\\\"", "\"");
+                if (!value)
+                        return log_oom();
+                n = strlen(value);
+                if (i->type == CREATE_SYMLINK) {
+                        if (lsetxattr(path, name, value, n+1, 0) < 0) {
+                                log_error("Setting extended attribute %s=%s on symlink %s failed: %m", name, value, path);
+                                free(value);
+                                return -errno;
+                        }
+                }
+                else if (setxattr(path, name, value, n+1, 0) < 0) {
+                        log_error("Setting extended attribute %s=%s on %s failed: %m", name, value, path);
+                        free(value);
+                        return -errno;
+                }
+                free(value);
+        }
+        return 0;
+#else
+        (void)i;
+        (void)path;
+        log_error("Setting extended attributes requested, but tmpfiles was compiled without XATTR support!");
+        return -ENOTSUP;
+#endif
+}
+
 static int write_one_file(Item *i, const char *path) {
         int r, e, fd, flags;
         struct stat st;
@@ -507,6 +560,12 @@ static int write_one_file(Item *i, const char *path) {
         if (r < 0)
                 return r;
 
+        if (i->xattrs) {
+                r = item_set_xattrs(i, i->path);
+                if (r < 0)
+                        return r;
+        }
+
         return 0;
 }
 
@@ -595,6 +654,72 @@ static int recursive_relabel(Item *i, const char *path) {
         return r;
 }
 
+#ifdef HAVE_XATTR
+static int get_xattrs_from_arg(Item *i){
+        _cleanup_free_ char *xattr = NULL;
+        _cleanup_strv_free_ char **tmp = NULL;
+        char *p;
+        char **f;
+        unsigned n, len;
+
+        assert(i);
+        assert(i->type == SET_XATTR);
+
+        if (!i->argument) {
+                log_error("%s: Argument can't be empty!", i->path);
+                return -EBADMSG;
+        }
+
+        xattr = new0(char, strlen(i->argument)+1);
+        if (!xattr)
+                return log_oom();
+
+        tmp = strv_split(i->argument, WHITESPACE);
+        if (!tmp)
+                return log_oom();
+
+        for (n = 0; n < strv_length(tmp); ++n) {
+                len = strlen(tmp[n]);
+                strncpy(xattr, tmp[n], len+1);
+                p = strchr(xattr, '=');
+                if (!p) {
+                        log_error("%s: Attribute has incorrect format.", i->path);
+                        return -EBADMSG;
+                }
+                if (p[1] == '\"') {
+                        while (true) {
+                                if (!p)
+                                        p = tmp[n];
+                                else
+                                        p += 2;
+                                p = strchr(p, '\"');
+                                if (p && xattr[p-xattr-1] != '\\')
+                                        break;
+                                p = NULL;
+                                ++n;
+                                if (n == strv_length(tmp))
+                                        break;
+                                len += strlen(tmp[n]) + 1;
+                                strncat(xattr, " ", 1);
+                                strncat(xattr, tmp[n], len);
+                        }
+                }
+                strstrip(xattr);
+                f = i->xattrs;
+                i->xattrs = strv_append(i->xattrs, xattr);
+                if (!i->xattrs){
+                        strv_free(f);
+                        return log_oom();
+                }
+        }
+
+        free(i->argument);
+        i->argument = NULL;
+
+        return 0;
+}
+#endif
+
 static int glob_item(Item *i, int (*action)(Item *, const char *)) {
         int r = 0, k;
         _cleanup_globfree_ glob_t g = {};
@@ -674,6 +799,12 @@ static int create_item(Item *i) {
                 if (r < 0)
                         return r;
 
+                if (i->xattrs) {
+                        r = item_set_xattrs(i, i->path);
+                        if (r < 0)
+                                return r;
+                }
+
                 break;
 
         case CREATE_FIFO:
@@ -701,6 +832,12 @@ static int create_item(Item *i) {
                 if (r < 0)
                         return r;
 
+                if (i->xattrs) {
+                        r = item_set_xattrs(i, i->path);
+                        if (r < 0)
+                                return r;
+                }
+
                 break;
 
         case CREATE_SYMLINK: {
@@ -730,6 +867,13 @@ static int create_item(Item *i) {
                 }
 
                 free(x);
+
+                if (i->xattrs) {
+                        r = item_set_xattrs(i, i->path);
+                        if (r < 0)
+                                return r;
+                }
+
                 break;
         }
 
@@ -776,6 +920,12 @@ static int create_item(Item *i) {
                 if (r < 0)
                         return r;
 
+                if (i->xattrs) {
+                        r = item_set_xattrs(i, i->path);
+                        if (r < 0)
+                                return r;
+                }
+
                 break;
         }
 
@@ -784,6 +934,12 @@ static int create_item(Item *i) {
                 r = glob_item(i, item_set_perms);
                 if (r < 0)
                         return r;
+
+                if (i->xattrs) {
+                        r = glob_item(i, item_set_xattrs);
+                        if (r < 0)
+                                return r;
+                }
                 break;
 
         case RECURSIVE_RELABEL_PATH:
@@ -791,6 +947,15 @@ static int create_item(Item *i) {
                 r = glob_item(i, recursive_relabel);
                 if (r < 0)
                         return r;
+                break;
+
+        case SET_XATTR:
+                r = get_xattrs_from_arg(i);
+                if (r < 0)
+                        return r;
+                r = item_set_xattrs(i, i->path);
+                if (r < 0)
+                        return r;
         }
 
         log_debug("%s created successfully.", i->path);
@@ -817,6 +982,7 @@ static int remove_item_instance(Item *i, const char *instance) {
         case RELABEL_PATH:
         case RECURSIVE_RELABEL_PATH:
         case WRITE_FILE:
+        case SET_XATTR:
                 break;
 
         case REMOVE_PATH:
@@ -862,6 +1028,7 @@ static int remove_item(Item *i) {
         case RELABEL_PATH:
         case RECURSIVE_RELABEL_PATH:
         case WRITE_FILE:
+        case SET_XATTR:
                 break;
 
         case REMOVE_PATH:
@@ -924,6 +1091,53 @@ static int clean_item_instance(Item *i, const char* instance) {
         return r;
 }
 
+static int copy_item_contents(Item *dest, const Item *src) {
+        assert(src);
+        assert(dest);
+
+        if (dest == src)
+                return 0;
+
+        dest->type =  src->type;
+
+        free(dest->path);
+        dest->path = strdup(src->path);
+        if (!dest->path)
+                return -ENOMEM;
+
+        if (dest->argument) {
+                free(dest->argument);
+                dest->argument = NULL;
+        }
+        if (src->argument)
+                dest->argument = strdup(src->argument);
+        if (!dest->argument && src->argument)
+                return -ENOMEM;
+
+        if (dest->xattrs) {
+                strv_free(dest->xattrs);
+                dest->xattrs = NULL;
+        }
+        if (src->xattrs)
+                dest->xattrs = strv_copy(src->xattrs);
+        if (!dest->xattrs && src->xattrs)
+                return -ENOMEM;
+
+        dest->uid = src->uid;
+        dest->gid = src->gid;
+        dest->mode = src->mode;
+        dest->age = src->age;
+
+        dest->uid_set = src->uid_set;
+        dest->gid_set = src->gid_set;
+        dest->mode_set = src->mode_set;
+        dest->age_set = src->age_set;
+
+        dest->keep_first_level = src->keep_first_level;
+
+        return 0;
+}
+
 static int clean_item(Item *i) {
         int r = 0;
 
@@ -968,6 +1182,7 @@ static void item_free(Item *i) {
 
         free(i->path);
         free(i->argument);
+        strv_free(i->xattrs);
         free(i);
 }
 
@@ -1099,6 +1314,13 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                 break;
         }
 
+        case SET_XATTR:
+                if (!i->argument) {
+                        log_error("[%s:%u] Set extended attribute requires argument.", fname, line);
+                        return -EBADMSG;
+                }
+                break;
+
         default:
                 log_error("[%s:%u] Unknown file type '%c'.", fname, line, type);
                 return -EBADMSG;
@@ -1176,17 +1398,47 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
         existing = hashmap_get(h, i->path);
         if (existing) {
 
-                /* Two identical items are fine */
-                if (!item_equal(existing, i))
-                        log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path);
-
+                char **tmp;
+
+                if (i->type == SET_XATTR) {
+                        r = get_xattrs_from_arg(i);
+                        if (r < 0)
+                                return r;
+                        tmp = existing->xattrs;
+                        existing->xattrs = strv_merge(existing->xattrs, i->xattrs);
+                        if (!existing->xattrs) {
+                                strv_free(tmp);
+                                strv_free(i->xattrs);
+                                return log_oom();
+                        }
+                } else if (existing->type == SET_XATTR) {
+                        r = get_xattrs_from_arg(existing);
+                        if (r < 0)
+                                return r;
+                        tmp = existing->xattrs;
+                        existing->xattrs = NULL;
+                        r = copy_item_contents(existing, i);
+                        if (r < 0)
+                                return r;
+                        existing->xattrs = strv_merge(existing->xattrs, tmp);
+                        if (!existing->xattrs){
+                                strv_free(tmp);
+                                return log_oom();
+                        }
+                } else {
+                        /* Two identical items are fine */
+                        if (!item_equal(existing, i))
+                                log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path);
+                        return 0;
+                }
+                strv_free(i->xattrs);
                 return 0;
-        }
-
-        r = hashmap_put(h, i->path, i);
-        if (r < 0) {
-                log_error("Failed to insert item %s: %s", i->path, strerror(-r));
-                return r;
+        } else {
+                r = hashmap_put(h, i->path, i);
+                if (r < 0) {
+                        log_error("Failed to insert item %s: %s", i->path, strerror(-r));
+                        return r;
+                }
         }
 
         i = NULL; /* avoid cleanup */
-- 
1.8.3.1



More information about the systemd-devel mailing list