Merge branch 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar...
[sfrench/cifs-2.6.git] / security / selinux / hooks.c
index c1e37018c8eb05a27651045d2fec91d6f118c4aa..4bef86ed463b5c42b420dc105c6b7c4a3628aaae 100644 (file)
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  *  NSA Security-Enhanced Linux (SELinux) security module
  *
  *  Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
  *                    Yuichi Nakamura <ynakam@hitachisoft.jp>
  *  Copyright (C) 2016 Mellanox Technologies
- *
- *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License version 2,
- *     as published by the Free Software Foundation.
  */
 
 #include <linux/init.h>
@@ -1052,15 +1049,24 @@ static int selinux_add_mnt_opt(const char *option, const char *val, int len,
        if (token == Opt_error)
                return -EINVAL;
 
-       if (token != Opt_seclabel)
+       if (token != Opt_seclabel) {
                val = kmemdup_nul(val, len, GFP_KERNEL);
+               if (!val) {
+                       rc = -ENOMEM;
+                       goto free_opt;
+               }
+       }
        rc = selinux_add_opt(token, val, mnt_opts);
        if (unlikely(rc)) {
                kfree(val);
-               if (*mnt_opts) {
-                       selinux_free_mnt_opts(*mnt_opts);
-                       *mnt_opts = NULL;
-               }
+               goto free_opt;
+       }
+       return rc;
+
+free_opt:
+       if (*mnt_opts) {
+               selinux_free_mnt_opts(*mnt_opts);
+               *mnt_opts = NULL;
        }
        return rc;
 }
@@ -2616,10 +2622,11 @@ static int selinux_sb_eat_lsm_opts(char *options, void **mnt_opts)
        char *from = options;
        char *to = options;
        bool first = true;
+       int rc;
 
        while (1) {
                int len = opt_len(from);
-               int token, rc;
+               int token;
                char *arg = NULL;
 
                token = match_opt_prefix(from, len, &arg);
@@ -2635,15 +2642,15 @@ static int selinux_sb_eat_lsm_opts(char *options, void **mnt_opts)
                                                *q++ = c;
                                }
                                arg = kmemdup_nul(arg, q - arg, GFP_KERNEL);
+                               if (!arg) {
+                                       rc = -ENOMEM;
+                                       goto free_opt;
+                               }
                        }
                        rc = selinux_add_opt(token, arg, mnt_opts);
                        if (unlikely(rc)) {
                                kfree(arg);
-                               if (*mnt_opts) {
-                                       selinux_free_mnt_opts(*mnt_opts);
-                                       *mnt_opts = NULL;
-                               }
-                               return rc;
+                               goto free_opt;
                        }
                } else {
                        if (!first) {   // copy with preceding comma
@@ -2661,6 +2668,13 @@ static int selinux_sb_eat_lsm_opts(char *options, void **mnt_opts)
        }
        *to = '\0';
        return 0;
+
+free_opt:
+       if (*mnt_opts) {
+               selinux_free_mnt_opts(*mnt_opts);
+               *mnt_opts = NULL;
+       }
+       return rc;
 }
 
 static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)
@@ -4637,6 +4651,14 @@ static int selinux_socket_connect_helper(struct socket *sock,
        err = sock_has_perm(sk, SOCKET__CONNECT);
        if (err)
                return err;
+       if (addrlen < offsetofend(struct sockaddr, sa_family))
+               return -EINVAL;
+
+       /* connect(AF_UNSPEC) has special handling, as it is a documented
+        * way to disconnect the socket
+        */
+       if (address->sa_family == AF_UNSPEC)
+               return 0;
 
        /*
         * If a TCP, DCCP or SCTP socket, check name_connect permission
@@ -4657,8 +4679,6 @@ static int selinux_socket_connect_helper(struct socket *sock,
                 * need to check address->sa_family as it is possible to have
                 * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET.
                 */
-               if (addrlen < offsetofend(struct sockaddr, sa_family))
-                       return -EINVAL;
                switch (address->sa_family) {
                case AF_INET:
                        addr4 = (struct sockaddr_in *)address;
@@ -6331,11 +6351,12 @@ static int selinux_setprocattr(const char *name, void *value, size_t size)
        } else if (!strcmp(name, "fscreate")) {
                tsec->create_sid = sid;
        } else if (!strcmp(name, "keycreate")) {
-               error = avc_has_perm(&selinux_state,
-                                    mysid, sid, SECCLASS_KEY, KEY__CREATE,
-                                    NULL);
-               if (error)
-                       goto abort_change;
+               if (sid) {
+                       error = avc_has_perm(&selinux_state, mysid, sid,
+                                            SECCLASS_KEY, KEY__CREATE, NULL);
+                       if (error)
+                               goto abort_change;
+               }
                tsec->keycreate_sid = sid;
        } else if (!strcmp(name, "sockcreate")) {
                tsec->sockcreate_sid = sid;
@@ -6481,6 +6502,7 @@ static int selinux_key_permission(key_ref_t key_ref,
 {
        struct key *key;
        struct key_security_struct *ksec;
+       unsigned oldstyle_perm;
        u32 sid;
 
        /* if no specific permissions are requested, we skip the
@@ -6489,13 +6511,26 @@ static int selinux_key_permission(key_ref_t key_ref,
        if (perm == 0)
                return 0;
 
+       oldstyle_perm = perm & (KEY_NEED_VIEW | KEY_NEED_READ | KEY_NEED_WRITE |
+                               KEY_NEED_SEARCH | KEY_NEED_LINK);
+       if (perm & KEY_NEED_SETSEC)
+               oldstyle_perm |= OLD_KEY_NEED_SETATTR;
+       if (perm & KEY_NEED_INVAL)
+               oldstyle_perm |= KEY_NEED_SEARCH;
+       if (perm & KEY_NEED_REVOKE && !(perm & OLD_KEY_NEED_SETATTR))
+               oldstyle_perm |= KEY_NEED_WRITE;
+       if (perm & KEY_NEED_JOIN)
+               oldstyle_perm |= KEY_NEED_SEARCH;
+       if (perm & KEY_NEED_CLEAR)
+               oldstyle_perm |= KEY_NEED_WRITE;
+
        sid = cred_sid(cred);
 
        key = key_ref_to_ptr(key_ref);
        ksec = key->security;
 
        return avc_has_perm(&selinux_state,
-                           sid, ksec->sid, SECCLASS_KEY, perm, NULL);
+                           sid, ksec->sid, SECCLASS_KEY, oldstyle_perm, NULL);
 }
 
 static int selinux_key_getsecurity(struct key *key, char **_buffer)