@@ -211,6 +211,8 @@ extern bool capable(int cap);
extern bool ns_capable(struct user_namespace *ns, int cap);
extern bool ns_capable_noaudit(struct user_namespace *ns, int cap);
extern bool ns_capable_setid(struct user_namespace *ns, int cap);
+extern bool ns_capable_inode(struct user_namespace *ns, int cap,
+ const struct inode *inode);
#else
static inline bool has_capability(struct task_struct *t, int cap)
{
@@ -246,6 +248,11 @@ static inline bool ns_capable_setid(struct user_namespace *ns, int cap)
{
return true;
}
+static bool ns_capable_inode(struct user_namespace *ns, int cap,
+ const struct inode *inode)
+{
+ return true;
+}
#endif /* CONFIG_MULTIUSER */
extern bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode);
extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
@@ -77,7 +77,7 @@ struct common_audit_data {
union {
struct path path;
struct dentry *dentry;
- struct inode *inode;
+ const struct inode *inode;
struct lsm_network_audit *net;
int cap;
int ipc_id;
@@ -1472,7 +1472,8 @@ union security_list_options {
int (*capable)(const struct cred *cred,
struct user_namespace *ns,
int cap,
- unsigned int opts);
+ unsigned int opts,
+ struct cap_aux_data *cad);
int (*quotactl)(int cmds, int type, int id, struct super_block *sb);
int (*quota_on)(struct dentry *dentry);
int (*syslog)(int type);
@@ -77,9 +77,18 @@ enum lsm_event {
LSM_POLICY_CHANGE,
};
+
+struct cap_aux_data {
+ char type;
+#define CAP_AUX_DATA_INODE 1
+ union {
+ const struct inode *inode;
+ } u;
+};
+
/* These functions are in security/commoncap.c */
extern int cap_capable(const struct cred *cred, struct user_namespace *ns,
- int cap, unsigned int opts);
+ int cap, unsigned int opts, struct cap_aux_data *cad);
extern int cap_settime(const struct timespec64 *ts, const struct timezone *tz);
extern int cap_ptrace_access_check(struct task_struct *child, unsigned int mode);
extern int cap_ptrace_traceme(struct task_struct *parent);
@@ -215,9 +224,10 @@ int security_capset(struct cred *new, const struct cred *old,
const kernel_cap_t *inheritable,
const kernel_cap_t *permitted);
int security_capable(const struct cred *cred,
- struct user_namespace *ns,
- int cap,
- unsigned int opts);
+ struct user_namespace *ns,
+ int cap,
+ unsigned int opts,
+ struct cap_aux_data *cad);
int security_quotactl(int cmds, int type, int id, struct super_block *sb);
int security_quota_on(struct dentry *dentry);
int security_syslog(int type);
@@ -478,9 +488,10 @@ static inline int security_capset(struct cred *new,
static inline int security_capable(const struct cred *cred,
struct user_namespace *ns,
int cap,
- unsigned int opts)
+ unsigned int opts,
+ struct cap_aux_data *cad)
{
- return cap_capable(cred, ns, cap, opts);
+ return cap_capable(cred, ns, cap, opts, NULL);
}
static inline int security_quotactl(int cmds, int type, int id,
@@ -297,7 +297,7 @@ bool has_ns_capability(struct task_struct *t,
int ret;
rcu_read_lock();
- ret = security_capable(__task_cred(t), ns, cap, CAP_OPT_NONE);
+ ret = security_capable(__task_cred(t), ns, cap, CAP_OPT_NONE, NULL);
rcu_read_unlock();
return (ret == 0);
@@ -338,7 +338,7 @@ bool has_ns_capability_noaudit(struct task_struct *t,
int ret;
rcu_read_lock();
- ret = security_capable(__task_cred(t), ns, cap, CAP_OPT_NOAUDIT);
+ ret = security_capable(__task_cred(t), ns, cap, CAP_OPT_NOAUDIT, NULL);
rcu_read_unlock();
return (ret == 0);
@@ -363,7 +363,8 @@ bool has_capability_noaudit(struct task_struct *t, int cap)
static bool ns_capable_common(struct user_namespace *ns,
int cap,
- unsigned int opts)
+ unsigned int opts,
+ struct cap_aux_data *cad)
{
int capable;
@@ -372,7 +373,7 @@ static bool ns_capable_common(struct user_namespace *ns,
BUG();
}
- capable = security_capable(current_cred(), ns, cap, opts);
+ capable = security_capable(current_cred(), ns, cap, opts, cad);
if (capable == 0) {
current->flags |= PF_SUPERPRIV;
return true;
@@ -393,7 +394,7 @@ static bool ns_capable_common(struct user_namespace *ns,
*/
bool ns_capable(struct user_namespace *ns, int cap)
{
- return ns_capable_common(ns, cap, CAP_OPT_NONE);
+ return ns_capable_common(ns, cap, CAP_OPT_NONE, NULL);
}
EXPORT_SYMBOL(ns_capable);
@@ -411,7 +412,7 @@ EXPORT_SYMBOL(ns_capable);
*/
bool ns_capable_noaudit(struct user_namespace *ns, int cap)
{
- return ns_capable_common(ns, cap, CAP_OPT_NOAUDIT);
+ return ns_capable_common(ns, cap, CAP_OPT_NOAUDIT, NULL);
}
EXPORT_SYMBOL(ns_capable_noaudit);
@@ -430,7 +431,7 @@ EXPORT_SYMBOL(ns_capable_noaudit);
*/
bool ns_capable_setid(struct user_namespace *ns, int cap)
{
- return ns_capable_common(ns, cap, CAP_OPT_INSETID);
+ return ns_capable_common(ns, cap, CAP_OPT_INSETID, NULL);
}
EXPORT_SYMBOL(ns_capable_setid);
@@ -470,7 +471,7 @@ bool file_ns_capable(const struct file *file, struct user_namespace *ns,
if (WARN_ON_ONCE(!cap_valid(cap)))
return false;
- if (security_capable(file->f_cred, ns, cap, CAP_OPT_NONE) == 0)
+ if (security_capable(file->f_cred, ns, cap, CAP_OPT_NONE, NULL) == 0)
return true;
return false;
@@ -503,7 +504,8 @@ bool capable_wrt_inode_uidgid(const struct inode *inode, int cap)
{
struct user_namespace *ns = current_user_ns();
- return ns_capable(ns, cap) && privileged_wrt_inode_uidgid(ns, inode);
+ return ns_capable_inode(ns, cap, inode) &&
+ privileged_wrt_inode_uidgid(ns, inode);
}
EXPORT_SYMBOL(capable_wrt_inode_uidgid);
@@ -524,7 +526,18 @@ bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns)
cred = rcu_dereference(tsk->ptracer_cred);
if (cred)
ret = security_capable(cred, ns, CAP_SYS_PTRACE,
- CAP_OPT_NOAUDIT);
+ CAP_OPT_NOAUDIT, NULL);
rcu_read_unlock();
return (ret == 0);
}
+
+bool ns_capable_inode(struct user_namespace *ns, int cap,
+ const struct inode *inode)
+{
+ struct cap_aux_data cad;
+
+ cad.type = CAP_AUX_DATA_INODE;
+ cad.u.inode = inode;
+ return ns_capable_common(ns, cap, CAP_OPT_NONE, &cad);
+}
+EXPORT_SYMBOL(ns_capable_inode);
@@ -446,7 +446,7 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog)
*/
if (!task_no_new_privs(current) &&
security_capable(current_cred(), current_user_ns(),
- CAP_SYS_ADMIN, CAP_OPT_NOAUDIT) != 0)
+ CAP_SYS_ADMIN, CAP_OPT_NOAUDIT, NULL) != 0)
return ERR_PTR(-EACCES);
/* Allocate a new seccomp_filter */
@@ -148,7 +148,8 @@ static int profile_capable(struct aa_profile *profile, int cap,
*
* Returns: 0 on success, or else an error code.
*/
-int aa_capable(struct aa_label *label, int cap, unsigned int opts)
+int aa_capable(struct aa_label *label, int cap, unsigned int opts,
+ struct cap_aux_data *cad)
{
struct aa_profile *profile;
int error = 0;
@@ -20,6 +20,7 @@
#include "apparmorfs.h"
struct aa_label;
+struct cap_aux_data;
/* aa_caps - confinement data for capabilities
* @allowed: capabilities mask
@@ -40,7 +41,8 @@ struct aa_caps {
extern struct aa_sfs_entry aa_sfs_entry_caps[];
-int aa_capable(struct aa_label *label, int cap, unsigned int opts);
+int aa_capable(struct aa_label *label, int cap, unsigned int opts,
+ struct cap_aux_data *cad);
static inline void aa_free_cap_rules(struct aa_caps *caps)
{
@@ -108,7 +108,7 @@ static int profile_tracer_perm(struct aa_profile *tracer,
aad(sa)->peer = tracee;
aad(sa)->request = 0;
aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE,
- CAP_OPT_NONE);
+ CAP_OPT_NONE, NULL);
return aa_audit(AUDIT_APPARMOR_AUTO, tracer, sa, audit_ptrace_cb);
}
@@ -172,14 +172,15 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
}
static int apparmor_capable(const struct cred *cred, struct user_namespace *ns,
- int cap, unsigned int opts)
+ int cap, unsigned int opts,
+ struct cap_aux_data *cad)
{
struct aa_label *label;
int error = 0;
label = aa_get_newest_cred_label(cred);
if (!unconfined(label))
- error = aa_capable(label, cap, opts);
+ error = aa_capable(label, cap, opts, cad);
aa_put_label(label);
return error;
@@ -124,7 +124,7 @@ int aa_task_setrlimit(struct aa_label *label, struct task_struct *task,
*/
if (label != peer &&
- aa_capable(label, CAP_SYS_RESOURCE, CAP_OPT_NOAUDIT) != 0)
+ aa_capable(label, CAP_SYS_RESOURCE, CAP_OPT_NOAUDIT, NULL) != 0)
error = fn_for_each(label, profile,
audit_resource(profile, resource,
new_rlim->rlim_max, peer,
@@ -68,7 +68,7 @@ static void warn_setuid_and_fcaps_mixed(const char *fname)
* kernel's capable() and has_capability() returns 1 for this case.
*/
int cap_capable(const struct cred *cred, struct user_namespace *targ_ns,
- int cap, unsigned int opts)
+ int cap, unsigned int opts, struct cap_aux_data *cad)
{
struct user_namespace *ns = targ_ns;
@@ -226,7 +226,7 @@ static inline int cap_inh_is_capped(void)
* capability
*/
if (cap_capable(current_cred(), current_cred()->user_ns,
- CAP_SETPCAP, CAP_OPT_NONE) == 0)
+ CAP_SETPCAP, CAP_OPT_NONE, NULL) == 0)
return 0;
return 1;
}
@@ -1211,7 +1211,8 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|| (cap_capable(current_cred(),
current_cred()->user_ns,
CAP_SETPCAP,
- CAP_OPT_NONE) != 0) /*[4]*/
+ CAP_OPT_NONE,
+ NULL) != 0) /*[4]*/
/*
* [1] no changing of bits that are locked
* [2] no unlocking of locks
@@ -1307,7 +1308,7 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages)
int cap_sys_admin = 0;
if (cap_capable(current_cred(), &init_user_ns,
- CAP_SYS_ADMIN, CAP_OPT_NOAUDIT) == 0)
+ CAP_SYS_ADMIN, CAP_OPT_NOAUDIT, NULL) == 0)
cap_sys_admin = 1;
return cap_sys_admin;
@@ -1328,7 +1329,7 @@ int cap_mmap_addr(unsigned long addr)
if (addr < dac_mmap_min_addr) {
ret = cap_capable(current_cred(), &init_user_ns, CAP_SYS_RAWIO,
- CAP_OPT_NONE);
+ CAP_OPT_NONE, NULL);
/* set PF_SUPERPRIV if it turns out we allow the low mmap */
if (ret == 0)
current->flags |= PF_SUPERPRIV;
@@ -288,17 +288,9 @@ static void dump_common_audit_data(struct audit_buffer *ab,
break;
}
case LSM_AUDIT_DATA_INODE: {
- struct dentry *dentry;
- struct inode *inode;
+ const struct inode *inode;
inode = a->u.inode;
- dentry = d_find_alias(inode);
- if (dentry) {
- audit_log_format(ab, " name=");
- audit_log_untrustedstring(ab,
- dentry->d_name.name);
- dput(dentry);
- }
audit_log_format(ab, " dev=");
audit_log_untrustedstring(ab, inode->i_sb->s_id);
audit_log_format(ab, " ino=%lu", inode->i_ino);
@@ -80,7 +80,8 @@ static bool check_setuid_policy_hashtable_key_value(kuid_t parent,
static int safesetid_security_capable(const struct cred *cred,
struct user_namespace *ns,
int cap,
- unsigned int opts)
+ unsigned int opts,
+ struct cap_aux_data *cad)
{
if (cap == CAP_SETUID &&
check_setuid_policy_hashtable_key(cred->uid)) {
@@ -691,9 +691,10 @@ int security_capset(struct cred *new, const struct cred *old,
int security_capable(const struct cred *cred,
struct user_namespace *ns,
int cap,
- unsigned int opts)
+ unsigned int opts,
+ struct cap_aux_data *cad)
{
- return call_int_hook(capable, 0, cred, ns, cap, opts);
+ return call_int_hook(capable, 0, cred, ns, cap, opts, cad);
}
int security_quotactl(int cmds, int type, int id, struct super_block *sb)
@@ -1620,7 +1620,10 @@ static inline u32 signal_to_av(int sig)
/* Check whether a task is allowed to use a capability. */
static int cred_has_capability(const struct cred *cred,
- int cap, unsigned int opts, bool initns)
+ int cap,
+ unsigned int opts,
+ bool initns,
+ struct cap_aux_data *cad)
{
struct common_audit_data ad;
struct av_decision avd;
@@ -1653,6 +1656,55 @@ static int cred_has_capability(const struct cred *cred,
if (rc2)
return rc2;
}
+
+ if (rc)
+ return rc;
+
+ if (cad && cad->type == CAP_AUX_DATA_INODE) {
+ const struct inode *inode = cad->u.inode;
+ struct inode_security_struct *isec = selinux_inode(inode);
+
+ switch (cap) {
+ case CAP_DAC_OVERRIDE:
+ av = INODE_CAP__DAC_OVERRIDE;
+ break;
+ case CAP_CHOWN:
+ av = INODE_CAP__CHOWN;
+ break;
+ case CAP_FSETID:
+ av = INODE_CAP__FSETID;
+ break;
+ case CAP_DAC_READ_SEARCH:
+ av = INODE_CAP__DAC_READ_SEARCH;
+ break;
+ case CAP_FOWNER:
+ av = INODE_CAP__FOWNER;
+ break;
+ case CAP_SETFCAP:
+ av = INODE_CAP__SETFCAP;
+ break;
+ default:
+ pr_err("SELinux: Unknown capability for inode %d\n",
+ cap);
+ return -EINVAL;
+ }
+
+ rc = avc_has_perm_noaudit(&selinux_state, sid, isec->sid,
+ SECCLASS_INODE_CAP, av, 0, &avd);
+ if (!(opts & CAP_OPT_NOAUDIT)) {
+ int rc2;
+
+ ad.type = LSM_AUDIT_DATA_INODE;
+ ad.u.inode = inode;
+ rc2 = avc_audit(&selinux_state, sid, isec->sid,
+ SECCLASS_INODE_CAP, av, &avd,
+ rc, &ad, 0);
+ if (rc2)
+ return rc2;
+ }
+ }
+
+
return rc;
}
@@ -2167,9 +2219,9 @@ static int selinux_capset(struct cred *new, const struct cred *old,
*/
static int selinux_capable(const struct cred *cred, struct user_namespace *ns,
- int cap, unsigned int opts)
+ int cap, unsigned int opts, struct cap_aux_data *cad)
{
- return cred_has_capability(cred, cap, opts, ns == &init_user_ns);
+ return cred_has_capability(cred, cap, opts, ns == &init_user_ns, cad);
}
static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
@@ -2243,7 +2295,7 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
int rc, cap_sys_admin = 0;
rc = cred_has_capability(current_cred(), CAP_SYS_ADMIN,
- CAP_OPT_NOAUDIT, true);
+ CAP_OPT_NOAUDIT, true, NULL);
if (rc == 0)
cap_sys_admin = 1;
@@ -3103,9 +3155,9 @@ static bool has_cap_mac_admin(bool audit)
const struct cred *cred = current_cred();
unsigned int opts = audit ? CAP_OPT_NONE : CAP_OPT_NOAUDIT;
- if (cap_capable(cred, &init_user_ns, CAP_MAC_ADMIN, opts))
+ if (cap_capable(cred, &init_user_ns, CAP_MAC_ADMIN, opts, NULL))
return false;
- if (cred_has_capability(cred, CAP_MAC_ADMIN, opts, true))
+ if (cred_has_capability(cred, CAP_MAC_ADMIN, opts, true, NULL))
return false;
return true;
}
@@ -3609,7 +3661,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
case KDSKBENT:
case KDSKBSENT:
error = cred_has_capability(cred, CAP_SYS_TTY_CONFIG,
- CAP_OPT_NONE, true);
+ CAP_OPT_NONE, true, NULL);
break;
/* default case assumes that the command will go
@@ -244,6 +244,9 @@ struct security_class_mapping secclass_map[] = {
{"map_create", "map_read", "map_write", "prog_load", "prog_run"} },
{ "xdp_socket",
{ COMMON_SOCK_PERMS, NULL } },
+ { "inode_cap",
+ { "dac_override", "chown", "fsetid", "fowner",
+ "dac_read_search", "setfcap", NULL } },
{ NULL }
};
@@ -640,7 +640,7 @@ bool smack_privileged_cred(int cap, const struct cred *cred)
struct smack_known_list_elem *sklep;
int rc;
- rc = cap_capable(cred, &init_user_ns, cap, CAP_OPT_NONE);
+ rc = cap_capable(cred, &init_user_ns, cap, CAP_OPT_NONE, NULL);
if (rc)
return false;