Merge tag 'notifications-20200601' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.git] / security / smack / smack_lsm.c
index cd44b79bf1f5778f0e971c56589b2c37d3b8905f..8ffbf951b7ed78b482b5f0131297011634f2cd79 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/parser.h>
 #include <linux/fs_context.h>
 #include <linux/fs_parser.h>
+#include <linux/watch_queue.h>
 #include "smack.h"
 
 #define TRANS_TRUE     "TRUE"
@@ -4213,13 +4214,14 @@ static void smack_key_free(struct key *key)
  * smack_key_permission - Smack access on a key
  * @key_ref: gets to the object
  * @cred: the credentials to use
- * @perm: requested key permissions
+ * @need_perm: requested key permission
  *
  * Return 0 if the task has read and write to the object,
  * an error code otherwise
  */
 static int smack_key_permission(key_ref_t key_ref,
-                               const struct cred *cred, unsigned perm)
+                               const struct cred *cred,
+                               enum key_need_perm need_perm)
 {
        struct key *keyp;
        struct smk_audit_info ad;
@@ -4230,8 +4232,26 @@ static int smack_key_permission(key_ref_t key_ref,
        /*
         * Validate requested permissions
         */
-       if (perm & ~KEY_NEED_ALL)
+       switch (need_perm) {
+       case KEY_NEED_READ:
+       case KEY_NEED_SEARCH:
+       case KEY_NEED_VIEW:
+               request |= MAY_READ;
+               break;
+       case KEY_NEED_WRITE:
+       case KEY_NEED_LINK:
+       case KEY_NEED_SETATTR:
+               request |= MAY_WRITE;
+               break;
+       case KEY_NEED_UNSPECIFIED:
+       case KEY_NEED_UNLINK:
+       case KEY_SYSADMIN_OVERRIDE:
+       case KEY_AUTHTOKEN_OVERRIDE:
+       case KEY_DEFER_PERM_CHECK:
+               return 0;
+       default:
                return -EINVAL;
+       }
 
        keyp = key_ref_to_ptr(key_ref);
        if (keyp == NULL)
@@ -4248,7 +4268,7 @@ static int smack_key_permission(key_ref_t key_ref,
        if (tkp == NULL)
                return -EACCES;
 
-       if (smack_privileged_cred(CAP_MAC_OVERRIDE, cred))
+       if (smack_privileged(CAP_MAC_OVERRIDE))
                return 0;
 
 #ifdef CONFIG_AUDIT
@@ -4256,10 +4276,6 @@ static int smack_key_permission(key_ref_t key_ref,
        ad.a.u.key_struct.key = keyp->serial;
        ad.a.u.key_struct.key_desc = keyp->description;
 #endif
-       if (perm & (KEY_NEED_READ | KEY_NEED_SEARCH | KEY_NEED_VIEW))
-               request |= MAY_READ;
-       if (perm & (KEY_NEED_WRITE | KEY_NEED_LINK | KEY_NEED_SETATTR))
-               request |= MAY_WRITE;
        rc = smk_access(tkp, keyp->security, request, &ad);
        rc = smk_bu_note("key access", tkp, keyp->security, request, rc);
        return rc;
@@ -4294,8 +4310,81 @@ static int smack_key_getsecurity(struct key *key, char **_buffer)
        return length;
 }
 
+
+#ifdef CONFIG_KEY_NOTIFICATIONS
+/**
+ * smack_watch_key - Smack access to watch a key for notifications.
+ * @key: The key to be watched
+ *
+ * Return 0 if the @watch->cred has permission to read from the key object and
+ * an error otherwise.
+ */
+static int smack_watch_key(struct key *key)
+{
+       struct smk_audit_info ad;
+       struct smack_known *tkp = smk_of_current();
+       int rc;
+
+       if (key == NULL)
+               return -EINVAL;
+       /*
+        * If the key hasn't been initialized give it access so that
+        * it may do so.
+        */
+       if (key->security == NULL)
+               return 0;
+       /*
+        * This should not occur
+        */
+       if (tkp == NULL)
+               return -EACCES;
+
+       if (smack_privileged_cred(CAP_MAC_OVERRIDE, current_cred()))
+               return 0;
+
+#ifdef CONFIG_AUDIT
+       smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_KEY);
+       ad.a.u.key_struct.key = key->serial;
+       ad.a.u.key_struct.key_desc = key->description;
+#endif
+       rc = smk_access(tkp, key->security, MAY_READ, &ad);
+       rc = smk_bu_note("key watch", tkp, key->security, MAY_READ, rc);
+       return rc;
+}
+#endif /* CONFIG_KEY_NOTIFICATIONS */
 #endif /* CONFIG_KEYS */
 
+#ifdef CONFIG_WATCH_QUEUE
+/**
+ * smack_post_notification - Smack access to post a notification to a queue
+ * @w_cred: The credentials of the watcher.
+ * @cred: The credentials of the event source (may be NULL).
+ * @n: The notification message to be posted.
+ */
+static int smack_post_notification(const struct cred *w_cred,
+                                  const struct cred *cred,
+                                  struct watch_notification *n)
+{
+       struct smk_audit_info ad;
+       struct smack_known *subj, *obj;
+       int rc;
+
+       /* Always let maintenance notifications through. */
+       if (n->type == WATCH_TYPE_META)
+               return 0;
+
+       if (!cred)
+               return 0;
+       subj = smk_of_task(smack_cred(cred));
+       obj = smk_of_task(smack_cred(w_cred));
+
+       smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NOTIFICATION);
+       rc = smk_access(subj, obj, MAY_WRITE, &ad);
+       rc = smk_bu_note("notification", subj, obj, MAY_WRITE, rc);
+       return rc;
+}
+#endif /* CONFIG_WATCH_QUEUE */
+
 /*
  * Smack Audit hooks
  *
@@ -4684,8 +4773,15 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
        LSM_HOOK_INIT(key_free, smack_key_free),
        LSM_HOOK_INIT(key_permission, smack_key_permission),
        LSM_HOOK_INIT(key_getsecurity, smack_key_getsecurity),
+#ifdef CONFIG_KEY_NOTIFICATIONS
+       LSM_HOOK_INIT(watch_key, smack_watch_key),
+#endif
 #endif /* CONFIG_KEYS */
 
+#ifdef CONFIG_WATCH_QUEUE
+       LSM_HOOK_INIT(post_notification, smack_post_notification),
+#endif
+
  /* Audit hooks */
 #ifdef CONFIG_AUDIT
        LSM_HOOK_INIT(audit_rule_init, smack_audit_rule_init),