Patches for 6.19

-----BEGIN PGP SIGNATURE-----
 
 iQJLBAABCAA1FiEEC+9tH1YyUwIQzUIeOKUVfIxDyBEFAmkp4M4XHGNhc2V5QHNj
 aGF1Zmxlci1jYS5jb20ACgkQOKUVfIxDyBFc+BAAq564RJ19Nj7WxV3juDGYLyWR
 b2P2w4euHo1PvWng058G3WnCzzS14xj6xShLJL711jbuue40lngBqllP+djHIdGM
 /Mq4I7BnVSJ4z2svvovebiQLoaCiSHN4Uo/b8cdYRz6scueyOQdJUXZvyLwVb4fY
 CxzfXbcXzsS8MEZikSqyVrjdEZ8yLKzL+uJEqm9FU/suaTUuMNeXudZaXHv5sa8H
 wxd5iFQhNe2NYCN0EV8Pz57q36ewvzC6UhZLIM+NlIXorfBFWEq54pYpa9OOnNNW
 4S+e4KWcNqcWtjxJbzh80Fyui3xv2/b1S73/0Akcf+yqp1mZjPLKZtmsFn83F9b3
 SxXGqHNUIZ79U6f656nDYSYYuXzAKkqzmGC94aj7AjABBXNRejPASXqsLLAtm+KY
 qwqcM2W6WwG1ILEixj5UpfC7qpsBVPYGf5cOi9iPlZD2+/l7OgPOTxNDAe7M5m+E
 M30eJCqObdl1Uw7QlHvMG5VQT9XkwJFL93OEOADqvE1GqTphIwMb4vJmGuYe/Sgg
 xqEWMLA6hKBP/B8eQvA7d02WLvzvegwB7C5RTWv1M+QGyzlNxTwqOv+vecm//IVS
 A3LzfxjadSYyRm9nNbZrYWqdVpvYhVcxHrLwzJ4jBsPjBSZM9QlbJHaCjs/T9Sg5
 pK0p5GsXIr3akV2BIvE=
 =n/1b
 -----END PGP SIGNATURE-----

Merge tag 'Smack-for-6.19' of https://github.com/cschaufler/smack-next

Pull smack updates from Casey Schaufler:

 - fix several cases where labels were treated inconsistently when
   imported from user space

 - clean up the assignment of extended attributes

 - documentation improvements

* tag 'Smack-for-6.19' of https://github.com/cschaufler/smack-next:
  Smack: function parameter 'gfp' not described
  smack: fix kernel-doc warnings for smk_import_valid_label()
  smack: fix bug: setting task label silently ignores input garbage
  smack: fix bug: unprivileged task can create labels
  smack: fix bug: invalid label of unix socket file
  smack: always "instantiate" inode in smack_inode_init_security()
  smack: deduplicate xattr setting in smack_inode_init_security()
  smack: fix bug: SMACK64TRANSMUTE set on non-directory
  smack: deduplicate "does access rule request transmutation"
This commit is contained in:
Linus Torvalds 2025-12-03 10:58:59 -08:00
commit 204a920f28
4 changed files with 274 additions and 118 deletions

View File

@ -601,10 +601,15 @@ specification.
Task Attribute
~~~~~~~~~~~~~~
The Smack label of a process can be read from /proc/<pid>/attr/current. A
process can read its own Smack label from /proc/self/attr/current. A
The Smack label of a process can be read from ``/proc/<pid>/attr/current``. A
process can read its own Smack label from ``/proc/self/attr/current``. A
privileged process can change its own Smack label by writing to
/proc/self/attr/current but not the label of another process.
``/proc/self/attr/current`` but not the label of another process.
Format of writing is : only the label or the label followed by one of the
3 trailers: ``\n`` (by common agreement for ``/proc/...`` interfaces),
``\0`` (because some applications incorrectly include it),
``\n\0`` (because we think some applications may incorrectly include it).
File Attribute
~~~~~~~~~~~~~~
@ -696,6 +701,11 @@ sockets.
A privileged program may set this to match the label of another
task with which it hopes to communicate.
UNIX domain socket (UDS) with a BSD address functions both as a file in a
filesystem and as a socket. As a file, it carries the SMACK64 attribute. This
attribute is not involved in Smack security enforcement and is immutably
assigned the label "*".
Smack Netlabel Exceptions
~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -300,9 +300,12 @@ int smk_tskacc(struct task_smack *, struct smack_known *,
int smk_curacc(struct smack_known *, u32, struct smk_audit_info *);
int smack_str_from_perm(char *string, int access);
struct smack_known *smack_from_secid(const u32);
int smk_parse_label_len(const char *string, int len);
char *smk_parse_smack(const char *string, int len);
int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int);
struct smack_known *smk_import_entry(const char *, int);
struct smack_known *smk_import_valid_label(const char *label, int label_len,
gfp_t gfp);
void smk_insert_entry(struct smack_known *skp);
struct smack_known *smk_find_entry(const char *);
bool smack_privileged(int cap);

View File

@ -443,19 +443,19 @@ struct smack_known *smk_find_entry(const char *string)
}
/**
* smk_parse_smack - parse smack label from a text string
* @string: a text string that might contain a Smack label
* @len: the maximum size, or zero if it is NULL terminated.
* smk_parse_label_len - calculate the length of the starting segment
* in the string that constitutes a valid smack label
* @string: a text string that might contain a Smack label at the beginning
* @len: the maximum size to look into, may be zero if string is null-terminated
*
* Returns a pointer to the clean label or an error code.
* Returns the length of the segment (0 < L < SMK_LONGLABEL) or an error code.
*/
char *smk_parse_smack(const char *string, int len)
int smk_parse_label_len(const char *string, int len)
{
char *smack;
int i;
if (len <= 0)
len = strlen(string) + 1;
if (len <= 0 || len > SMK_LONGLABEL)
len = SMK_LONGLABEL;
/*
* Reserve a leading '-' as an indicator that
@ -463,7 +463,7 @@ char *smk_parse_smack(const char *string, int len)
* including /smack/cipso and /smack/cipso2
*/
if (string[0] == '-')
return ERR_PTR(-EINVAL);
return -EINVAL;
for (i = 0; i < len; i++)
if (string[i] > '~' || string[i] <= ' ' || string[i] == '/' ||
@ -471,6 +471,25 @@ char *smk_parse_smack(const char *string, int len)
break;
if (i == 0 || i >= SMK_LONGLABEL)
return -EINVAL;
return i;
}
/**
* smk_parse_smack - copy the starting segment in the string
* that constitutes a valid smack label
* @string: a text string that might contain a Smack label at the beginning
* @len: the maximum size to look into, may be zero if string is null-terminated
*
* Returns a pointer to the copy of the label or an error code.
*/
char *smk_parse_smack(const char *string, int len)
{
char *smack;
int i = smk_parse_label_len(string, len);
if (i < 0)
return ERR_PTR(-EINVAL);
smack = kstrndup(string, i, GFP_NOFS);
@ -554,31 +573,26 @@ int smack_populate_secattr(struct smack_known *skp)
}
/**
* smk_import_entry - import a label, return the list entry
* @string: a text string that might be a Smack label
* @len: the maximum size, or zero if it is NULL terminated.
* smk_import_valid_allocated_label - import a label, return the list entry
* @smack: a text string that is a valid Smack label and may be kfree()ed.
* It is consumed: either becomes a part of the entry or kfree'ed.
* @gfp: Allocation type
*
* Returns a pointer to the entry in the label list that
* matches the passed string, adding it if necessary,
* or an error code.
* Returns: see description of smk_import_entry()
*/
struct smack_known *smk_import_entry(const char *string, int len)
static struct smack_known *
smk_import_allocated_label(char *smack, gfp_t gfp)
{
struct smack_known *skp;
char *smack;
int rc;
smack = smk_parse_smack(string, len);
if (IS_ERR(smack))
return ERR_CAST(smack);
mutex_lock(&smack_known_lock);
skp = smk_find_entry(smack);
if (skp != NULL)
goto freeout;
skp = kzalloc(sizeof(*skp), GFP_NOFS);
skp = kzalloc(sizeof(*skp), gfp);
if (skp == NULL) {
skp = ERR_PTR(-ENOMEM);
goto freeout;
@ -608,6 +622,44 @@ struct smack_known *smk_import_entry(const char *string, int len)
return skp;
}
/**
* smk_import_entry - import a label, return the list entry
* @string: a text string that might contain a Smack label at the beginning
* @len: the maximum size to look into, may be zero if string is null-terminated
*
* Returns a pointer to the entry in the label list that
* matches the passed string, adding it if necessary,
* or an error code.
*/
struct smack_known *smk_import_entry(const char *string, int len)
{
char *smack = smk_parse_smack(string, len);
if (IS_ERR(smack))
return ERR_CAST(smack);
return smk_import_allocated_label(smack, GFP_NOFS);
}
/**
* smk_import_valid_label - import a label, return the list entry
* @label: a text string that is a valid Smack label, not null-terminated
* @label_len: the length of the text string in the @label
* @gfp: the GFP mask used for allocating memory for the @label text string copy
*
* Return: see description of smk_import_entry()
*/
struct smack_known *
smk_import_valid_label(const char *label, int label_len, gfp_t gfp)
{
char *smack = kstrndup(label, label_len, gfp);
if (!smack)
return ERR_PTR(-ENOMEM);
return smk_import_allocated_label(smack, gfp);
}
/**
* smack_from_secid - find the Smack label associated with a secid
* @secid: an integer that might be associated with a Smack label

View File

@ -962,6 +962,42 @@ static int smack_inode_alloc_security(struct inode *inode)
return 0;
}
/**
* smk_rule_transmutes - does access rule for (subject,object) contain 't'?
* @subject: a pointer to the subject's Smack label entry
* @object: a pointer to the object's Smack label entry
*/
static bool
smk_rule_transmutes(struct smack_known *subject,
const struct smack_known *object)
{
int may;
rcu_read_lock();
may = smk_access_entry(subject->smk_known, object->smk_known,
&subject->smk_rules);
rcu_read_unlock();
return (may > 0) && (may & MAY_TRANSMUTE);
}
static int
xattr_dupval(struct xattr *xattrs, int *xattr_count,
const char *name, const void *value, unsigned int vallen)
{
struct xattr * const xattr = lsm_get_xattr_slot(xattrs, xattr_count);
if (!xattr)
return 0;
xattr->value = kmemdup(value, vallen, GFP_NOFS);
if (!xattr->value)
return -ENOMEM;
xattr->value_len = vallen;
xattr->name = name;
return 0;
}
/**
* smack_inode_init_security - copy out the smack from an inode
* @inode: the newly created inode
@ -977,23 +1013,30 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
struct xattr *xattrs, int *xattr_count)
{
struct task_smack *tsp = smack_cred(current_cred());
struct inode_smack *issp = smack_inode(inode);
struct smack_known *skp = smk_of_task(tsp);
struct smack_known *isp = smk_of_inode(inode);
struct inode_smack * const issp = smack_inode(inode);
struct smack_known *dsp = smk_of_inode(dir);
struct xattr *xattr = lsm_get_xattr_slot(xattrs, xattr_count);
int may;
int rc = 0;
int transflag = 0;
bool trans_cred;
bool trans_rule;
/*
* UNIX domain sockets use lower level socket data. Let
* UDS inode have fixed * label to keep smack_inode_permission() calm
* when called from unix_find_bsd()
*/
if (S_ISSOCK(inode->i_mode)) {
/* forced label, no need to save to xattrs */
issp->smk_inode = &smack_known_star;
goto instant_inode;
}
/*
* If equal, transmuting already occurred in
* smack_dentry_create_files_as(). No need to check again.
*/
if (tsp->smk_task != tsp->smk_transmuted) {
rcu_read_lock();
may = smk_access_entry(skp->smk_known, dsp->smk_known,
&skp->smk_rules);
rcu_read_unlock();
}
trans_cred = (tsp->smk_task == tsp->smk_transmuted);
if (!trans_cred)
trans_rule = smk_rule_transmutes(smk_of_task(tsp), dsp);
/*
* In addition to having smk_task equal to smk_transmuted,
@ -1001,47 +1044,38 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
* requests transmutation then by all means transmute.
* Mark the inode as changed.
*/
if ((tsp->smk_task == tsp->smk_transmuted) ||
(may > 0 && ((may & MAY_TRANSMUTE) != 0) &&
smk_inode_transmutable(dir))) {
struct xattr *xattr_transmute;
if (trans_cred || (trans_rule && smk_inode_transmutable(dir))) {
/*
* The caller of smack_dentry_create_files_as()
* should have overridden the current cred, so the
* inode label was already set correctly in
* smack_inode_alloc_security().
*/
if (tsp->smk_task != tsp->smk_transmuted)
isp = issp->smk_inode = dsp;
if (!trans_cred)
issp->smk_inode = dsp;
issp->smk_flags |= SMK_INODE_TRANSMUTE;
xattr_transmute = lsm_get_xattr_slot(xattrs,
xattr_count);
if (xattr_transmute) {
xattr_transmute->value = kmemdup(TRANS_TRUE,
TRANS_TRUE_SIZE,
GFP_NOFS);
if (!xattr_transmute->value)
return -ENOMEM;
if (S_ISDIR(inode->i_mode)) {
transflag = SMK_INODE_TRANSMUTE;
xattr_transmute->value_len = TRANS_TRUE_SIZE;
xattr_transmute->name = XATTR_SMACK_TRANSMUTE;
if (xattr_dupval(xattrs, xattr_count,
XATTR_SMACK_TRANSMUTE,
TRANS_TRUE,
TRANS_TRUE_SIZE
))
rc = -ENOMEM;
}
}
issp->smk_flags |= SMK_INODE_INSTANT;
if (xattr) {
xattr->value = kstrdup(isp->smk_known, GFP_NOFS);
if (!xattr->value)
return -ENOMEM;
xattr->value_len = strlen(isp->smk_known);
xattr->name = XATTR_SMACK_SUFFIX;
}
return 0;
if (rc == 0)
if (xattr_dupval(xattrs, xattr_count,
XATTR_SMACK_SUFFIX,
issp->smk_inode->smk_known,
strlen(issp->smk_inode->smk_known)
))
rc = -ENOMEM;
instant_inode:
issp->smk_flags |= (SMK_INODE_INSTANT | transflag);
return rc;
}
/**
@ -1315,12 +1349,22 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
int check_import = 0;
int check_star = 0;
int rc = 0;
umode_t const i_mode = d_backing_inode(dentry)->i_mode;
/*
* Check label validity here so import won't fail in post_setxattr
*/
if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
if (strcmp(name, XATTR_NAME_SMACK) == 0) {
/*
* UDS inode has fixed label
*/
if (S_ISSOCK(i_mode)) {
rc = -EINVAL;
} else {
check_priv = 1;
check_import = 1;
}
} else if (strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) {
check_priv = 1;
check_import = 1;
@ -1331,7 +1375,7 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
check_star = 1;
} else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
check_priv = 1;
if (!S_ISDIR(d_backing_inode(dentry)->i_mode) ||
if (!S_ISDIR(i_mode) ||
size != TRANS_TRUE_SIZE ||
strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0)
rc = -EINVAL;
@ -1462,12 +1506,15 @@ static int smack_inode_removexattr(struct mnt_idmap *idmap,
* Don't do anything special for these.
* XATTR_NAME_SMACKIPIN
* XATTR_NAME_SMACKIPOUT
* XATTR_NAME_SMACK if S_ISSOCK (UDS inode has fixed label)
*/
if (strcmp(name, XATTR_NAME_SMACK) == 0) {
if (!S_ISSOCK(d_backing_inode(dentry)->i_mode)) {
struct super_block *sbp = dentry->d_sb;
struct superblock_smack *sbsp = smack_superblock(sbp);
isp->smk_inode = sbsp->smk_default;
}
} else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0)
isp->smk_task = NULL;
else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0)
@ -3585,7 +3632,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
*/
/*
* UNIX domain sockets use lower level socket data.
* UDS inode has fixed label (*)
*/
if (S_ISSOCK(inode->i_mode)) {
final = &smack_known_star;
@ -3663,7 +3710,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
* @attr: which attribute to fetch
* @ctx: buffer to receive the result
* @size: available size in, actual size out
* @flags: unused
* @flags: reserved, currently zero
*
* Fill the passed user space @ctx with the details of the requested
* attribute.
@ -3724,47 +3771,55 @@ static int smack_getprocattr(struct task_struct *p, const char *name, char **val
* Sets the Smack value of the task. Only setting self
* is permitted and only with privilege
*
* Returns the length of the smack label or an error code
* Returns zero on success or an error code
*/
static int do_setattr(u64 attr, void *value, size_t size)
static int do_setattr(unsigned int attr, void *value, size_t size)
{
struct task_smack *tsp = smack_cred(current_cred());
struct cred *new;
struct smack_known *skp;
struct smack_known_list_elem *sklep;
int rc;
if (!smack_privileged(CAP_MAC_ADMIN) && list_empty(&tsp->smk_relabel))
return -EPERM;
int label_len;
/*
* let unprivileged user validate input, check permissions later
*/
if (value == NULL || size == 0 || size >= SMK_LONGLABEL)
return -EINVAL;
if (attr != LSM_ATTR_CURRENT)
return -EOPNOTSUPP;
skp = smk_import_entry(value, size);
if (IS_ERR(skp))
return PTR_ERR(skp);
label_len = smk_parse_label_len(value, size);
if (label_len < 0 || label_len != size)
return -EINVAL;
/*
* No process is ever allowed the web ("@") label
* and the star ("*") label.
*/
if (skp == &smack_known_web || skp == &smack_known_star)
return -EINVAL;
if (label_len == 1 /* '@', '*' */) {
const char c = *(const char *)value;
if (c == *smack_known_web.smk_known ||
c == *smack_known_star.smk_known)
return -EPERM;
}
if (!smack_privileged(CAP_MAC_ADMIN)) {
rc = -EPERM;
list_for_each_entry(sklep, &tsp->smk_relabel, list)
if (sklep->smk_label == skp) {
rc = 0;
break;
const struct smack_known_list_elem *sklep;
list_for_each_entry(sklep, &tsp->smk_relabel, list) {
const char *cp = sklep->smk_label->smk_known;
if (strlen(cp) == label_len &&
strncmp(cp, value, label_len) == 0)
goto in_relabel;
}
if (rc)
return rc;
return -EPERM;
in_relabel:
;
}
skp = smk_import_valid_label(value, label_len, GFP_KERNEL);
if (IS_ERR(skp))
return PTR_ERR(skp);
new = prepare_creds();
if (new == NULL)
return -ENOMEM;
@ -3777,7 +3832,7 @@ static int do_setattr(u64 attr, void *value, size_t size)
smk_destroy_label_list(&tsp->smk_relabel);
commit_creds(new);
return size;
return 0;
}
/**
@ -3785,7 +3840,7 @@ static int do_setattr(u64 attr, void *value, size_t size)
* @attr: which attribute to set
* @ctx: buffer containing the data
* @size: size of @ctx
* @flags: unused
* @flags: reserved, must be zero
*
* Fill the passed user space @ctx with the details of the requested
* attribute.
@ -3795,12 +3850,26 @@ static int do_setattr(u64 attr, void *value, size_t size)
static int smack_setselfattr(unsigned int attr, struct lsm_ctx *ctx,
u32 size, u32 flags)
{
int rc;
if (attr != LSM_ATTR_CURRENT)
return -EOPNOTSUPP;
rc = do_setattr(attr, ctx->ctx, ctx->ctx_len);
if (rc > 0)
return 0;
return rc;
if (ctx->flags)
return -EINVAL;
/*
* string must have \0 terminator, included in ctx->ctx
* (see description of struct lsm_ctx)
*/
if (ctx->ctx_len == 0)
return -EINVAL;
if (ctx->ctx[ctx->ctx_len - 1] != '\0')
return -EINVAL;
/*
* other do_setattr() caller, smack_setprocattr(),
* does not count \0 into size, so
* decreasing length by 1 to accommodate the divergence.
*/
return do_setattr(attr, ctx->ctx, ctx->ctx_len - 1);
}
/**
@ -3812,15 +3881,39 @@ static int smack_setselfattr(unsigned int attr, struct lsm_ctx *ctx,
* Sets the Smack value of the task. Only setting self
* is permitted and only with privilege
*
* Returns the length of the smack label or an error code
* Returns the size of the input value or an error code
*/
static int smack_setprocattr(const char *name, void *value, size_t size)
{
int attr = lsm_name_to_attr(name);
size_t realsize = size;
unsigned int attr = lsm_name_to_attr(name);
if (attr != LSM_ATTR_UNDEF)
return do_setattr(attr, value, size);
return -EINVAL;
switch (attr) {
case LSM_ATTR_UNDEF: return -EINVAL;
default: return -EOPNOTSUPP;
case LSM_ATTR_CURRENT:
;
}
/*
* The value for the "current" attribute is the label
* followed by one of the 4 trailers: none, \0, \n, \n\0
*
* I.e. following inputs are accepted as 3-characters long label "foo":
*
* "foo" (3 characters)
* "foo\0" (4 characters)
* "foo\n" (4 characters)
* "foo\n\0" (5 characters)
*/
if (realsize && (((const char *)value)[realsize - 1] == '\0'))
--realsize;
if (realsize && (((const char *)value)[realsize - 1] == '\n'))
--realsize;
return do_setattr(attr, value, realsize) ? : size;
}
/**
@ -4850,6 +4943,11 @@ static int smack_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
static int smack_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
{
/*
* UDS inode has fixed label. Ignore nfs label.
*/
if (S_ISSOCK(inode->i_mode))
return 0;
return smack_inode_setsecurity(inode, XATTR_SMACK_SUFFIX, ctx,
ctxlen, 0);
}
@ -4915,7 +5013,6 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
struct task_smack *otsp = smack_cred(old);
struct task_smack *ntsp = smack_cred(new);
struct inode_smack *isp;
int may;
/*
* Use the process credential unless all of
@ -4929,18 +5026,12 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
isp = smack_inode(d_inode(dentry->d_parent));
if (isp->smk_flags & SMK_INODE_TRANSMUTE) {
rcu_read_lock();
may = smk_access_entry(otsp->smk_task->smk_known,
isp->smk_inode->smk_known,
&otsp->smk_task->smk_rules);
rcu_read_unlock();
/*
* If the directory is transmuting and the rule
* providing access is transmuting use the containing
* directory label instead of the process label.
*/
if (may > 0 && (may & MAY_TRANSMUTE)) {
if (smk_rule_transmutes(otsp->smk_task, isp->smk_inode)) {
ntsp->smk_task = isp->smk_inode;
ntsp->smk_transmuted = ntsp->smk_task;
}