smack: limit privilege by label
There have been a number of requests to make the Smack LSM enforce MAC even in the face of privilege, either capability based or superuser based. This is not universally desired, however, so it seems desirable to make it optional. Further, at least one legacy OS implemented a scheme whereby only processes running with one particular label could be exempt from MAC. This patch supports these three cases. If /smack/onlycap is empty (unset or null-string) privilege is enforced in the normal way. If /smack/onlycap contains a label only processes running with that label may be MAC exempt. If the label in /smack/onlycap is the star label ("*") the semantics of the star label combine with the privilege restrictions to prevent any violations of MAC, even in the presence of privilege. Again, this will be independent of the privilege scheme. Signed-off-by: Casey Schaufler <casey@schaufler-ca.com> Reviewed-by: James Morris <jmorris@namei.org>
This commit is contained in:
parent
cf9481e289
commit
1544623536
|
@ -178,6 +178,7 @@ u32 smack_to_secid(const char *);
|
||||||
extern int smack_cipso_direct;
|
extern int smack_cipso_direct;
|
||||||
extern int smack_net_nltype;
|
extern int smack_net_nltype;
|
||||||
extern char *smack_net_ambient;
|
extern char *smack_net_ambient;
|
||||||
|
extern char *smack_onlycap;
|
||||||
|
|
||||||
extern struct smack_known *smack_known;
|
extern struct smack_known *smack_known;
|
||||||
extern struct smack_known smack_known_floor;
|
extern struct smack_known smack_known_floor;
|
||||||
|
|
|
@ -157,7 +157,7 @@ int smk_access(char *subject_label, char *object_label, int request)
|
||||||
*
|
*
|
||||||
* This function checks the current subject label/object label pair
|
* This function checks the current subject label/object label pair
|
||||||
* in the access rule list and returns 0 if the access is permitted,
|
* in the access rule list and returns 0 if the access is permitted,
|
||||||
* non zero otherwise. It allows that current my have the capability
|
* non zero otherwise. It allows that current may have the capability
|
||||||
* to override the rules.
|
* to override the rules.
|
||||||
*/
|
*/
|
||||||
int smk_curacc(char *obj_label, u32 mode)
|
int smk_curacc(char *obj_label, u32 mode)
|
||||||
|
@ -168,6 +168,14 @@ int smk_curacc(char *obj_label, u32 mode)
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return if a specific label has been designated as the
|
||||||
|
* only one that gets privilege and current does not
|
||||||
|
* have that label.
|
||||||
|
*/
|
||||||
|
if (smack_onlycap != NULL && smack_onlycap != current->security)
|
||||||
|
return rc;
|
||||||
|
|
||||||
if (capable(CAP_MAC_OVERRIDE))
|
if (capable(CAP_MAC_OVERRIDE))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ enum smk_inos {
|
||||||
SMK_DIRECT = 6, /* CIPSO level indicating direct label */
|
SMK_DIRECT = 6, /* CIPSO level indicating direct label */
|
||||||
SMK_AMBIENT = 7, /* internet ambient label */
|
SMK_AMBIENT = 7, /* internet ambient label */
|
||||||
SMK_NLTYPE = 8, /* label scheme to use by default */
|
SMK_NLTYPE = 8, /* label scheme to use by default */
|
||||||
|
SMK_ONLYCAP = 9, /* the only "capable" label */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -68,6 +69,16 @@ int smack_net_nltype = NETLBL_NLTYPE_CIPSOV4;
|
||||||
*/
|
*/
|
||||||
int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT;
|
int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unless a process is running with this label even
|
||||||
|
* having CAP_MAC_OVERRIDE isn't enough to grant
|
||||||
|
* privilege to violate MAC policy. If no label is
|
||||||
|
* designated (the NULL case) capabilities apply to
|
||||||
|
* everyone. It is expected that the hat (^) label
|
||||||
|
* will be used if any label is used.
|
||||||
|
*/
|
||||||
|
char *smack_onlycap;
|
||||||
|
|
||||||
static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
|
static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
|
||||||
struct smk_list_entry *smack_list;
|
struct smk_list_entry *smack_list;
|
||||||
|
|
||||||
|
@ -787,6 +798,85 @@ static const struct file_operations smk_ambient_ops = {
|
||||||
.write = smk_write_ambient,
|
.write = smk_write_ambient,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* smk_read_onlycap - read() for /smack/onlycap
|
||||||
|
* @filp: file pointer, not actually used
|
||||||
|
* @buf: where to put the result
|
||||||
|
* @cn: maximum to send along
|
||||||
|
* @ppos: where to start
|
||||||
|
*
|
||||||
|
* Returns number of bytes read or error code, as appropriate
|
||||||
|
*/
|
||||||
|
static ssize_t smk_read_onlycap(struct file *filp, char __user *buf,
|
||||||
|
size_t cn, loff_t *ppos)
|
||||||
|
{
|
||||||
|
char *smack = "";
|
||||||
|
ssize_t rc = -EINVAL;
|
||||||
|
int asize;
|
||||||
|
|
||||||
|
if (*ppos != 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (smack_onlycap != NULL)
|
||||||
|
smack = smack_onlycap;
|
||||||
|
|
||||||
|
asize = strlen(smack) + 1;
|
||||||
|
|
||||||
|
if (cn >= asize)
|
||||||
|
rc = simple_read_from_buffer(buf, cn, ppos, smack, asize);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* smk_write_onlycap - write() for /smack/onlycap
|
||||||
|
* @filp: file pointer, not actually used
|
||||||
|
* @buf: where to get the data from
|
||||||
|
* @count: bytes sent
|
||||||
|
* @ppos: where to start
|
||||||
|
*
|
||||||
|
* Returns number of bytes written or error code, as appropriate
|
||||||
|
*/
|
||||||
|
static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
|
||||||
|
size_t count, loff_t *ppos)
|
||||||
|
{
|
||||||
|
char in[SMK_LABELLEN];
|
||||||
|
char *sp = current->security;
|
||||||
|
|
||||||
|
if (!capable(CAP_MAC_ADMIN))
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This can be done using smk_access() but is done
|
||||||
|
* explicitly for clarity. The smk_access() implementation
|
||||||
|
* would use smk_access(smack_onlycap, MAY_WRITE)
|
||||||
|
*/
|
||||||
|
if (smack_onlycap != NULL && smack_onlycap != sp)
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
|
if (count >= SMK_LABELLEN)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (copy_from_user(in, buf, count) != 0)
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Should the null string be passed in unset the onlycap value.
|
||||||
|
* This seems like something to be careful with as usually
|
||||||
|
* smk_import only expects to return NULL for errors. It
|
||||||
|
* is usually the case that a nullstring or "\n" would be
|
||||||
|
* bad to pass to smk_import but in fact this is useful here.
|
||||||
|
*/
|
||||||
|
smack_onlycap = smk_import(in, count);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations smk_onlycap_ops = {
|
||||||
|
.read = smk_read_onlycap,
|
||||||
|
.write = smk_write_onlycap,
|
||||||
|
};
|
||||||
|
|
||||||
struct option_names {
|
struct option_names {
|
||||||
int o_number;
|
int o_number;
|
||||||
char *o_name;
|
char *o_name;
|
||||||
|
@ -919,6 +1009,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
|
||||||
{"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR},
|
{"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR},
|
||||||
[SMK_NLTYPE] =
|
[SMK_NLTYPE] =
|
||||||
{"nltype", &smk_nltype_ops, S_IRUGO|S_IWUSR},
|
{"nltype", &smk_nltype_ops, S_IRUGO|S_IWUSR},
|
||||||
|
[SMK_ONLYCAP] =
|
||||||
|
{"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR},
|
||||||
/* last one */ {""}
|
/* last one */ {""}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue