>From 3f023e42dfac31f6454644e21c58d84863fd0844 Mon Sep 17 00:00:00 2001
From: Elena Reshetova <elena.reshetova@intel.com>
Date: Tue, 7 May 2013 14:22:09 +0300
Subject: [PATCH] Adding Smack support for udev nodes labelling

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

The patch allows specifying an arbitary Smack label in udev
rules using the keyword "SMACK". If a label is present
in the rules file and xattrs are enabled, then the label is
set on the node upon node creation.

The above functionality is needed on Smack-enabled distributions
in order to setup a proper security context for device nodes.
---
 src/udev/udev-event.c |    3 ++-
 src/udev/udev-node.c  |   21 ++++++++++++++++++---
 src/udev/udev-rules.c |   34 ++++++++++++++++++++++++++++++++++
 src/udev/udev.h       |    4 +++-
 4 files changed, 57 insertions(+), 5 deletions(-)

diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
index 3db2cb7..559b17e 100644
--- a/src/udev/udev-event.c
+++ b/src/udev/udev-event.c
@@ -60,6 +60,7 @@ void udev_event_unref(struct udev_event *event)
         udev_list_cleanup(&event->run_list);
         free(event->program_result);
         free(event->name);
+        free(event->smack_label);
         free(event);
 }
 
@@ -864,7 +865,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->smack_label);
                 }
 
                 /* preserve old, or get new initialization timestamp */
diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c
index 1148a15..36542ba 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,7 @@ 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 *smack_label)
 {
         const char *devnode = udev_device_get_devnode(dev);
         dev_t devnum = udev_device_get_devnum(dev);
@@ -288,13 +292,24 @@ static int node_permissions_apply(struct udev_device *dev, bool apply, mode_t mo
                 label_fix(devnode, true, false);
         }
 
+        if (smack_label) {
+#ifdef HAVE_XATTR
+                if (lsetxattr(devnode, "security.SMACK64", smack_label, strlen(smack_label), 0) < 0) {
+                        log_error("setting smack label %s for node %s failed", smack_label, devnode);
+                        err = -errno;
+                }
+#else
+                log_debug("not setting smack label %s for node %s due to lack of xattr support", smack_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 *smack_label)
 {
         char filename[UTIL_PATH_SIZE];
         struct udev_list_entry *list_entry;
@@ -302,7 +317,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, smack_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..6653b51 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_SMACK,                     /* val */
 
         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_SMACK] =                  "A SMACK",
 
                 [TK_END] =                      "END",
         };
@@ -368,6 +370,7 @@ static void dump_token(struct udev_rules *rules, struct token *token)
                 break;
         case TK_M_TAG:
         case TK_A_TAG:
+        case TK_A_SMACK:
                 log_debug("%s %s '%s'\n", token_str(type), operation_str(op), value);
                 break;
         case TK_A_STRING_ESCAPE_NONE:
@@ -898,6 +901,7 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
         case TK_A_GOTO:
         case TK_M_TAG:
         case TK_A_TAG:
+        case TK_A_SMACK:
                 token->key.value_off = rules_add_string(rule_tmp->rules, value);
                 break;
         case TK_M_IMPORT_BUILTIN:
@@ -1422,6 +1426,17 @@ static int add_rule(struct udev_rules *rules, char *line,
                         continue;
                 }
 
+                if (streq(key, "SMACK")) {
+                        if (value[0] == '\0') {
+                                log_error("SMACK=\"\" is ignored, invalid label \"\" "
+                                "please remove it from %s:%u\n", filename, lineno);
+                                continue;
+                        }
+                        rule_add_key(&rule_tmp, TK_A_SMACK, op, value, NULL);
+                        rule_tmp.rule.rule.can_set_name = true;
+                        continue;
+                }
+
                 if (streq(key, "OPTIONS")) {
                         const char *pos;
 
@@ -2271,6 +2286,25 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
                                   rule->rule.filename_line);
                         break;
                 }
+                case TK_A_SMACK: {
+                        const char *smack_label = rules_str(rules, cur->key.value_off);
+                        char smack_str[UTIL_PATH_SIZE];
+
+                        if (event->smack_final)
+                                break;
+                        if (cur->key.op == OP_ASSIGN_FINAL)
+                                event->smack_final = true;
+
+                        udev_event_apply_format(event, smack_label, smack_str, sizeof(smack_str));
+                        free(event->smack_label);
+                        event->smack_label = strdup(smack_str);
+
+                        log_debug("SMACK %s %s:%u\n",
+                                  event->smack_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..9427109 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;
+        char *smack_label;
         struct udev_list run_list;
         int exec_delay;
         usec_t birth_usec;
@@ -53,6 +54,7 @@ struct udev_event {
         bool group_final;
         bool owner_set;
         bool owner_final;
+        bool smack_final;
         bool mode_set;
         bool mode_final;
         bool name_final;
@@ -95,7 +97,7 @@ 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 *smack_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.9.5

