>From c12617d69837e36f49d1dce6ebffb59c9ff8bcad Mon Sep 17 00:00:00 2001
From: Elena Reshetova <elena.reshetova@intel.com>
Date: Mon, 24 Jun 2013 18:19:42 +0300
Subject: [PATCH] Adding extended attribute support for udev nodes labelling

The patch is greatly based on original smack udev patch
by Brian McGillion.

The patch allows specifying an arbitary xattr value in udev
rules using the keyword "XATTR" together with the xattr name.
If a value is present in the rules file and xattrs are enabled,
then the corresponding xattr is set on the node upon node creation
to the specified value.

The above functionality can be used for labelling security xattrs
for different LSMs and for general setting of xattrs.
---
 src/udev/udev-event.c |    4 +++-
 src/udev/udev-node.c  |   27 ++++++++++++++++++++++++---
 src/udev/udev-rules.c |   33 +++++++++++++++++++++++++++++++++
 src/udev/udev.h       |    4 +++-
 4 files changed, 63 insertions(+), 5 deletions(-)

diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
index 3db2cb7..b280056 100644
--- a/src/udev/udev-event.c
+++ b/src/udev/udev-event.c
@@ -47,6 +47,7 @@ struct udev_event *udev_event_new(struct udev_device *dev)
         event->dev = dev;
         event->udev = udev;
         udev_list_init(udev, &event->run_list, false);
+        udev_list_init(udev, &event->xattr_list, false);
         event->fd_signal = -1;
         event->birth_usec = now(CLOCK_MONOTONIC);
         event->timeout_usec = 30 * 1000 * 1000;
@@ -60,6 +61,7 @@ void udev_event_unref(struct udev_event *event)
         udev_list_cleanup(&event->run_list);
         free(event->program_result);
         free(event->name);
+        udev_list_cleanup(&event->xattr_list);
         free(event);
 }
 
@@ -864,7 +866,7 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules,
                         }
 
                         apply = streq(udev_device_get_action(dev), "add") || event->owner_set || event->group_set || event->mode_set;
-                        udev_node_add(dev, apply, event->mode, event->uid, event->gid);
+                        udev_node_add(dev, apply, event->mode, event->uid, event->gid, &event->xattr_list);
                 }
 
                 /* preserve old, or get new initialization timestamp */
diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c
index 1148a15..93c2882 100644
--- a/src/udev/udev-node.c
+++ b/src/udev/udev-node.c
@@ -29,6 +29,10 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#ifdef HAVE_XATTR
+#include <attr/xattr.h>
+#endif
+
 #include "udev.h"
 
 static int node_symlink(struct udev_device *dev, const char *node, const char *slink)
@@ -252,11 +256,13 @@ void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev
         }
 }
 
-static int node_permissions_apply(struct udev_device *dev, bool apply, mode_t mode, uid_t uid, gid_t gid)
+static int node_permissions_apply(struct udev_device *dev, bool apply, mode_t mode, 
+                                  uid_t uid, gid_t gid, const struct udev_list *xattr_list)
 {
         const char *devnode = udev_device_get_devnode(dev);
         dev_t devnum = udev_device_get_devnum(dev);
         struct stat stats;
+        struct udev_list_entry *xattr_entry;
         int err = 0;
 
         if (streq(udev_device_get_subsystem(dev), "block"))
@@ -288,13 +294,28 @@ static int node_permissions_apply(struct udev_device *dev, bool apply, mode_t mo
                 label_fix(devnode, true, false);
         }
 
+        udev_list_entry_foreach(xattr_entry, udev_list_get_entry(xattr_list)) {
+                const char * xattr_name = udev_list_entry_get_name(xattr_entry);
+                const char * xattr_label = udev_list_entry_get_value(xattr_entry);
+#ifdef HAVE_XATTR
+                if (lsetxattr(devnode, xattr_name, xattr_label, strlen(xattr_label), 0) < 0) {
+                        log_error("setting xattr %s to value %s for node %s failed", xattr_name, xattr_label, devnode);
+                        err = -errno;
+                }
+#else
+                log_debug("not setting xattr %s to value %s for node %s due to lack of xattr support",
+                          xattr_name, xattr_label, devnode);
+#endif
+        }
+
         /* always update timestamp when we re-use the node, like on media change events */
         utimensat(AT_FDCWD, devnode, NULL, 0);
 out:
         return err;
 }
 
-void udev_node_add(struct udev_device *dev, bool apply, mode_t mode, uid_t uid, gid_t gid)
+void udev_node_add(struct udev_device *dev, bool apply, mode_t mode, 
+                   uid_t uid, gid_t gid, const struct udev_list *xattr_list)
 {
         char filename[UTIL_PATH_SIZE];
         struct udev_list_entry *list_entry;
@@ -302,7 +323,7 @@ void udev_node_add(struct udev_device *dev, bool apply, mode_t mode, uid_t uid,
         log_debug("handling device node '%s', devnum=%s, mode=%#o, uid=%d, gid=%d\n",
                   udev_device_get_devnode(dev), udev_device_get_id_filename(dev), mode, uid, gid);
 
-        if (node_permissions_apply(dev, apply, mode, uid, gid) < 0)
+        if (node_permissions_apply(dev, apply, mode, uid, gid, xattr_list) < 0)
                 return;
 
         /* always add /dev/{block,char}/$major:$minor */
diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
index 7a4fb70..53a412f 100644
--- a/src/udev/udev-rules.c
+++ b/src/udev/udev-rules.c
@@ -153,6 +153,7 @@ enum token_type {
         TK_A_GROUP_ID,                  /* gid_t */
         TK_A_MODE_ID,                   /* mode_t */
         TK_A_STATIC_NODE,               /* val */
+        TK_A_XATTR,                     /* val, attr */
         TK_A_ENV,                       /* val, attr */
         TK_A_TAG,                       /* val */
         TK_A_NAME,                      /* val */
@@ -290,6 +291,7 @@ static const char *token_str(enum token_type type)
                 [TK_A_GROUP_ID] =               "A GROUP_ID",
                 [TK_A_STATIC_NODE] =            "A STATIC_NODE",
                 [TK_A_MODE_ID] =                "A MODE_ID",
+                [TK_A_XATTR] =                  "A XATTR",
                 [TK_A_ENV] =                    "A ENV",
                 [TK_A_TAG] =                    "A ENV",
                 [TK_A_NAME] =                   "A NAME",
@@ -397,6 +399,9 @@ static void dump_token(struct udev_rules *rules, struct token *token)
         case TK_A_STATIC_NODE:
                 log_debug("%s '%s'\n", token_str(type), value);
                 break;
+        case TK_A_XATTR:
+                log_debug("%s %s '%s'\n", token_str(type), operation_str(op), attr, value);
+                break;
         case TK_M_EVENT_TIMEOUT:
                 log_debug("%s %u\n", token_str(type), token->key.event_timeout);
                 break;
@@ -909,6 +914,7 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
         case TK_M_ATTRS:
         case TK_A_ATTR:
         case TK_A_ENV:
+        case TK_A_XATTR:
                 attr = data;
                 token->key.value_off = rules_add_string(rule_tmp->rules, value);
                 token->key.attr_off = rules_add_string(rule_tmp->rules, attr);
@@ -1136,6 +1142,16 @@ static int add_rule(struct udev_rules *rules, char *line,
                         continue;
                 }
 
+                if (startswith(key, "XATTR{")) {
+                        attr = get_key_attribute(rules->udev, key + sizeof("XATTR")-1);
+                        if (attr == NULL) {
+                                log_error("error parsing XATTR attribute\n");
+                                goto invalid;
+                        }
+                        rule_add_key(&rule_tmp, TK_A_XATTR, op, value, attr);
+                        continue;
+                }
+
                 if (streq(key, "KERNELS")) {
                         if (op > OP_MATCH_MAX) {
                                 log_error("invalid KERNELS operation\n");
@@ -2307,6 +2323,23 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
                                   rules_str(rules, rule->rule.filename_off),
                                   rule->rule.filename_line);
                         break;
+               case TK_A_XATTR: {
+                        const char *xattr_label = rules_str(rules, cur->key.value_off);
+                        const char *xattr_name = rules_str(rules, cur->key.attr_off);
+
+                        if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL)
+                                udev_list_cleanup(&event->xattr_list);
+
+                        udev_list_entry_add(&event->xattr_list, xattr_name, xattr_label);
+
+                        log_debug("XATTR{%s} %s %s:%u\n",
+                                  xattr_name,
+                                  xattr_label,
+                                  rules_str(rules, rule->rule.filename_off),
+                                  rule->rule.filename_line);
+                        break;
+
+                }
                 case TK_A_ENV: {
                         const char *name = rules_str(rules, cur->key.attr_off);
                         char *value = rules_str(rules, cur->key.value_off);
diff --git a/src/udev/udev.h b/src/udev/udev.h
index caec5f0..dd326aa 100644
--- a/src/udev/udev.h
+++ b/src/udev/udev.h
@@ -39,6 +39,7 @@ struct udev_event {
         mode_t mode;
         uid_t uid;
         gid_t gid;
+        struct udev_list xattr_list;
         struct udev_list run_list;
         int exec_delay;
         usec_t birth_usec;
@@ -95,7 +96,8 @@ void udev_watch_end(struct udev *udev, struct udev_device *dev);
 struct udev_device *udev_watch_lookup(struct udev *udev, int wd);
 
 /* udev-node.c */
-void udev_node_add(struct udev_device *dev, bool apply, mode_t mode, uid_t uid, gid_t gid);
+void udev_node_add(struct udev_device *dev, bool apply, mode_t mode, uid_t uid, 
+                   gid_t gid, const struct udev_list *xattr_list);
 void udev_node_remove(struct udev_device *dev);
 void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old);
 
-- 
1.7.10.4

