>From f1a5b437aa72920b352f23ccb33758da0925b05e 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  |   24 +++++++++++++++++++++---
 src/udev/udev-rules.c |   40 ++++++++++++++++++++++++++++++++++++++++
 src/udev/udev.h       |    6 +++++-
 4 files changed, 69 insertions(+), 5 deletions(-)

diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
index 3db2cb7..c8a4c0e 100644
--- a/src/udev/udev-event.c
+++ b/src/udev/udev-event.c
@@ -60,6 +60,8 @@ void udev_event_unref(struct udev_event *event)
         udev_list_cleanup(&event->run_list);
         free(event->program_result);
         free(event->name);
+        free(event->xattr_name);
+        free(event->xattr_label);
         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_name, event->xattr_label);
                 }
 
                 /* preserve old, or get new initialization timestamp */
diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c
index 1148a15..40a6385 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,7 +256,8 @@ 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 char *xattr_name, const char *xattr_label)
 {
         const char *devnode = udev_device_get_devnode(dev);
         dev_t devnum = udev_device_get_devnum(dev);
@@ -288,13 +293,26 @@ static int node_permissions_apply(struct udev_device *dev, bool apply, mode_t mo
                 label_fix(devnode, true, false);
         }
 
+        if ((xattr_name) && (xattr_label)) {
+#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 char *xattr_name, const char *xattr_label)
 {
         char filename[UTIL_PATH_SIZE];
         struct udev_list_entry *list_entry;
@@ -302,7 +320,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_name, xattr_label) < 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..f7319ee 100644
--- a/src/udev/udev-rules.c
+++ b/src/udev/udev-rules.c
@@ -161,6 +161,7 @@ enum token_type {
         TK_A_RUN_BUILTIN,               /* val, bool */
         TK_A_RUN_PROGRAM,               /* val, bool */
         TK_A_GOTO,                      /* size_t */
+        TK_A_XATTR,                     /* val, attr */
 
         TK_END,
 };
@@ -298,6 +299,7 @@ static const char *token_str(enum token_type type)
                 [TK_A_RUN_BUILTIN] =            "A RUN_BUILTIN",
                 [TK_A_RUN_PROGRAM] =            "A RUN_PROGRAM",
                 [TK_A_GOTO] =                   "A GOTO",
+                [TK_A_XATTR] =                  "A XATTR",
 
                 [TK_END] =                      "END",
         };
@@ -366,6 +368,9 @@ static void dump_token(struct udev_rules *rules, struct token *token)
                 log_debug("%s %s '%s' '%s'(%s)\n",
                           token_str(type), operation_str(op), attr, value, string_glob_str(glob));
                 break;
+        case TK_A_XATTR:
+                log_debug("%s %s '%s'\n", token_str(type), operation_str(op), attr, value);
+                break;
         case TK_M_TAG:
         case TK_A_TAG:
                 log_debug("%s %s '%s'\n", token_str(type), operation_str(op), value);
@@ -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");
@@ -2271,6 +2287,30 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
                                   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);
+                        char label[UTIL_PATH_SIZE];
+                        char name[UTIL_PATH_SIZE];
+
+                        if (event->xattr_final)
+                                break;
+                        if (cur->key.op == OP_ASSIGN_FINAL)
+                                event->xattr_final = true;
+                        udev_event_apply_format(event, xattr_label, label, sizeof(label));
+                        udev_event_apply_format(event, xattr_name, name, sizeof(name));
+                        free(event->xattr_name);
+                        free(event->xattr_label);
+                        event->xattr_name = strdup(name);
+                        event->xattr_label = strdup(label);
+
+                        log_debug("XATTR{%s} %s %s:%u\n",
+                                  event->xattr_name,
+                                  event->xattr_label,
+                                  rules_str(rules, rule->rule.filename_off),
+                                  rule->rule.filename_line);
+                        break;
+                }
                 case TK_A_OWNER_ID:
                         if (event->owner_final)
                                 break;
diff --git a/src/udev/udev.h b/src/udev/udev.h
index caec5f0..01faba7 100644
--- a/src/udev/udev.h
+++ b/src/udev/udev.h
@@ -39,6 +39,8 @@ struct udev_event {
         mode_t mode;
         uid_t uid;
         gid_t gid;
+        char *xattr_name;
+        char *xattr_label;
         struct udev_list run_list;
         int exec_delay;
         usec_t birth_usec;
@@ -53,6 +55,7 @@ struct udev_event {
         bool group_final;
         bool owner_set;
         bool owner_final;
+        bool xattr_final;
         bool mode_set;
         bool mode_final;
         bool name_final;
@@ -95,7 +98,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 char *xattr_name, const char *xattr_label);
 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

