diff mbox series

[1/2,security] Add new hook to compare new mount to an existing mount

Message ID 20210212211955.11239-1-olga.kornievskaia@gmail.com (mailing list archive)
State New
Headers show
Series [1/2,security] Add new hook to compare new mount to an existing mount | expand

Commit Message

Olga Kornievskaia Feb. 12, 2021, 9:19 p.m. UTC
From: Olga Kornievskaia <kolga@netapp.com>

Add a new hook that takes an existing super block and a new mount
with new options and determines if new options confict with an
existing mount or not.

Signed-off-by: Olga Kornievskaia <kolga@netapp.com>
---
 include/linux/lsm_hook_defs.h |  1 +
 include/linux/lsm_hooks.h     |  6 ++++
 include/linux/security.h      |  1 +
 security/security.c           |  7 +++++
 security/selinux/hooks.c      | 54 +++++++++++++++++++++++++++++++++++
 5 files changed, 69 insertions(+)

Comments

Casey Schaufler Feb. 12, 2021, 10:36 p.m. UTC | #1
On 2/12/2021 1:19 PM, Olga Kornievskaia wrote:
> From: Olga Kornievskaia <kolga@netapp.com>
>
> Add a new hook that takes an existing super block and a new mount
> with new options and determines if new options confict with an
> existing mount or not.
>
> Signed-off-by: Olga Kornievskaia <kolga@netapp.com>
> ---
>  include/linux/lsm_hook_defs.h |  1 +
>  include/linux/lsm_hooks.h     |  6 ++++
>  include/linux/security.h      |  1 +
>  security/security.c           |  7 +++++
>  security/selinux/hooks.c      | 54 +++++++++++++++++++++++++++++++++++
>  5 files changed, 69 insertions(+)
>
> diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
> index 7aaa753b8608..fbfc07d0b3d5 100644
> --- a/include/linux/lsm_hook_defs.h
> +++ b/include/linux/lsm_hook_defs.h
> @@ -62,6 +62,7 @@ LSM_HOOK(int, 0, sb_alloc_security, struct super_block *sb)
>  LSM_HOOK(void, LSM_RET_VOID, sb_free_security, struct super_block *sb)
>  LSM_HOOK(void, LSM_RET_VOID, sb_free_mnt_opts, void *mnt_opts)
>  LSM_HOOK(int, 0, sb_eat_lsm_opts, char *orig, void **mnt_opts)
> +LSM_HOOK(int, 0, sb_do_mnt_opts_match, struct super_block *sb, void *mnt_opts)

Don't you want this to be sb_mnt_opts_compatible ?
They may not have to match. They just need to be acceptable
to the security module. SELinux doesn't appear to require
that they "match" in all respects.


>  LSM_HOOK(int, 0, sb_remount, struct super_block *sb, void *mnt_opts)
>  LSM_HOOK(int, 0, sb_kern_mount, struct super_block *sb)
>  LSM_HOOK(int, 0, sb_show_options, struct seq_file *m, struct super_block *sb)
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index a19adef1f088..a11b062c1847 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -142,6 +142,12 @@
>   *	@orig the original mount data copied from userspace.
>   *	@copy copied data which will be passed to the security module.
>   *	Returns 0 if the copy was successful.
> + * @sb_do_mnt_opts_match:
> + *	Determine if the existing mount options are compatible with the new
> + *	mount options being used.
> + *	@sb superblock being compared
> + *	@mnt_opts new mount options
> + *	Return 1 if options are the same.

This is inconsistent with the convention for returns from LSM interfaces.
Return 0 on success and -ESOMETHINGOROTHER if the operation would be
denied. Your implementation of security_sb_do_mnt_opts_match() will
always return 0 if there's no module supplying the hook. I seriously
doubt that you want the mounts to fail 100% of the time if there isn't
an LSM.

Also, "options are the same" isn't the right description, even for
SELinux.

>   * @sb_remount:
>   *	Extracts security system specific mount options and verifies no changes
>   *	are being made to those options.
> diff --git a/include/linux/security.h b/include/linux/security.h
> index c35ea0ffccd9..07026db7304d 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -291,6 +291,7 @@ int security_sb_alloc(struct super_block *sb);
>  void security_sb_free(struct super_block *sb);
>  void security_free_mnt_opts(void **mnt_opts);
>  int security_sb_eat_lsm_opts(char *options, void **mnt_opts);
> +int security_sb_do_mnt_opts_match(struct super_block *sb, void *mnt_opts);
>  int security_sb_remount(struct super_block *sb, void *mnt_opts);
>  int security_sb_kern_mount(struct super_block *sb);
>  int security_sb_show_options(struct seq_file *m, struct super_block *sb);
> diff --git a/security/security.c b/security/security.c
> index 7b09cfbae94f..dae380916c6a 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -890,6 +890,13 @@ int security_sb_eat_lsm_opts(char *options, void **mnt_opts)
>  }
>  EXPORT_SYMBOL(security_sb_eat_lsm_opts);
>  
> +int security_sb_do_mnt_opts_match(struct super_block *sb,
> +				 void *mnt_opts)
> +{
> +	return call_int_hook(sb_do_mnt_opts_match, 0, sb, mnt_opts);
> +}
> +EXPORT_SYMBOL(security_sb_do_mnt_opts_match);
> +
>  int security_sb_remount(struct super_block *sb,
>  			void *mnt_opts)
>  {
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 644b17ec9e63..aaa3a725da94 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -2656,6 +2656,59 @@ static int selinux_sb_eat_lsm_opts(char *options, void **mnt_opts)
>  	return rc;
>  }
>  
> +static int selinux_sb_do_mnt_opts_match(struct super_block *sb, void *mnt_opts)
> +{
> +	struct selinux_mnt_opts *opts = mnt_opts;
> +	struct superblock_security_struct *sbsec = sb->s_security;
> +	u32 sid;
> +	int rc;
> +
> +	/* superblock not initialized (i.e. no options) - reject if any
> +	 * options specified, otherwise accept
> +	 */
> +	if (!(sbsec->flags & SE_SBINITIALIZED))
> +		return opts ? 0 : 1;
> +
> +	/* superblock initialized and no options specified - reject if
> +	 * superblock has any options set, otherwise accept
> +	 */
> +	if (!opts)
> +		return (sbsec->flags & SE_MNTMASK) ? 0 : 1;
> +
> +	if (opts->fscontext) {
> +		rc = parse_sid(sb, opts->fscontext, &sid);
> +		if (rc)
> +			return 0;
> +		if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid))
> +			return 0;
> +	}
> +	if (opts->context) {
> +		rc = parse_sid(sb, opts->context, &sid);
> +		if (rc)
> +			return 0;
> +		if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid))
> +			return 0;
> +	}
> +	if (opts->rootcontext) {
> +		struct inode_security_struct *root_isec;
> +
> +		root_isec = backing_inode_security(sb->s_root);
> +		rc = parse_sid(sb, opts->rootcontext, &sid);
> +		if (rc)
> +			return 0;
> +		if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
> +			return 0;
> +	}
> +	if (opts->defcontext) {
> +		rc = parse_sid(sb, opts->defcontext, &sid);
> +		if (rc)
> +			return 0;
> +		if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid))
> +			return 0;
> +	}
> +	return 1;
> +}
> +
>  static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)
>  {
>  	struct selinux_mnt_opts *opts = mnt_opts;
> @@ -6984,6 +7037,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
>  
>  	LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security),
>  	LSM_HOOK_INIT(sb_free_mnt_opts, selinux_free_mnt_opts),
> +	LSM_HOOK_INIT(sb_do_mnt_opts_match, selinux_sb_do_mnt_opts_match),
>  	LSM_HOOK_INIT(sb_remount, selinux_sb_remount),
>  	LSM_HOOK_INIT(sb_kern_mount, selinux_sb_kern_mount),
>  	LSM_HOOK_INIT(sb_show_options, selinux_sb_show_options),
Olga Kornievskaia Feb. 12, 2021, 10:50 p.m. UTC | #2
Thank you for comment Casey. Comments are in line.

On Fri, Feb 12, 2021 at 5:37 PM Casey Schaufler <casey@schaufler-ca.com> wrote:
>
> On 2/12/2021 1:19 PM, Olga Kornievskaia wrote:
> > From: Olga Kornievskaia <kolga@netapp.com>
> >
> > Add a new hook that takes an existing super block and a new mount
> > with new options and determines if new options confict with an
> > existing mount or not.
> >
> > Signed-off-by: Olga Kornievskaia <kolga@netapp.com>
> > ---
> >  include/linux/lsm_hook_defs.h |  1 +
> >  include/linux/lsm_hooks.h     |  6 ++++
> >  include/linux/security.h      |  1 +
> >  security/security.c           |  7 +++++
> >  security/selinux/hooks.c      | 54 +++++++++++++++++++++++++++++++++++
> >  5 files changed, 69 insertions(+)
> >
> > diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
> > index 7aaa753b8608..fbfc07d0b3d5 100644
> > --- a/include/linux/lsm_hook_defs.h
> > +++ b/include/linux/lsm_hook_defs.h
> > @@ -62,6 +62,7 @@ LSM_HOOK(int, 0, sb_alloc_security, struct super_block *sb)
> >  LSM_HOOK(void, LSM_RET_VOID, sb_free_security, struct super_block *sb)
> >  LSM_HOOK(void, LSM_RET_VOID, sb_free_mnt_opts, void *mnt_opts)
> >  LSM_HOOK(int, 0, sb_eat_lsm_opts, char *orig, void **mnt_opts)
> > +LSM_HOOK(int, 0, sb_do_mnt_opts_match, struct super_block *sb, void *mnt_opts)
>
> Don't you want this to be sb_mnt_opts_compatible ?
> They may not have to match. They just need to be acceptable
> to the security module. SELinux doesn't appear to require
> that they "match" in all respects.

You are right, it was a poor naming choice. Compatible is better. I
can change it.
>
>
> >  LSM_HOOK(int, 0, sb_remount, struct super_block *sb, void *mnt_opts)
> >  LSM_HOOK(int, 0, sb_kern_mount, struct super_block *sb)
> >  LSM_HOOK(int, 0, sb_show_options, struct seq_file *m, struct super_block *sb)
> > diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> > index a19adef1f088..a11b062c1847 100644
> > --- a/include/linux/lsm_hooks.h
> > +++ b/include/linux/lsm_hooks.h
> > @@ -142,6 +142,12 @@
> >   *   @orig the original mount data copied from userspace.
> >   *   @copy copied data which will be passed to the security module.
> >   *   Returns 0 if the copy was successful.
> > + * @sb_do_mnt_opts_match:
> > + *   Determine if the existing mount options are compatible with the new
> > + *   mount options being used.
> > + *   @sb superblock being compared
> > + *   @mnt_opts new mount options
> > + *   Return 1 if options are the same.
>
> This is inconsistent with the convention for returns from LSM interfaces.
> Return 0 on success and -ESOMETHINGOROTHER if the operation would be
> denied. Your implementation of security_sb_do_mnt_opts_match() will
> always return 0 if there's no module supplying the hook. I seriously
> doubt that you want the mounts to fail 100% of the time if there isn't
> an LSM.

The mounts are not failing. This allows a file system to decide
whether or not to share the superblocks or not. Ok a good point, I
haven't tested building a kernel where there is LSM but SElinux is not
compiled into the kernel (ie, the only user of that hook). I'm not
sure that's a possible or an interesting option.

Ok, I can flip the returns, I wasn't aware that SElinux is so
restrictive in its return function meaning. Returning 0 on a success
when you are looking for a match/capability seems backwards.

> Also, "options are the same" isn't the right description, even for
> SELinux.

Again compatible is better, I can change it.

> >   * @sb_remount:
> >   *   Extracts security system specific mount options and verifies no changes
> >   *   are being made to those options.
> > diff --git a/include/linux/security.h b/include/linux/security.h
> > index c35ea0ffccd9..07026db7304d 100644
> > --- a/include/linux/security.h
> > +++ b/include/linux/security.h
> > @@ -291,6 +291,7 @@ int security_sb_alloc(struct super_block *sb);
> >  void security_sb_free(struct super_block *sb);
> >  void security_free_mnt_opts(void **mnt_opts);
> >  int security_sb_eat_lsm_opts(char *options, void **mnt_opts);
> > +int security_sb_do_mnt_opts_match(struct super_block *sb, void *mnt_opts);
> >  int security_sb_remount(struct super_block *sb, void *mnt_opts);
> >  int security_sb_kern_mount(struct super_block *sb);
> >  int security_sb_show_options(struct seq_file *m, struct super_block *sb);
> > diff --git a/security/security.c b/security/security.c
> > index 7b09cfbae94f..dae380916c6a 100644
> > --- a/security/security.c
> > +++ b/security/security.c
> > @@ -890,6 +890,13 @@ int security_sb_eat_lsm_opts(char *options, void **mnt_opts)
> >  }
> >  EXPORT_SYMBOL(security_sb_eat_lsm_opts);
> >
> > +int security_sb_do_mnt_opts_match(struct super_block *sb,
> > +                              void *mnt_opts)
> > +{
> > +     return call_int_hook(sb_do_mnt_opts_match, 0, sb, mnt_opts);
> > +}
> > +EXPORT_SYMBOL(security_sb_do_mnt_opts_match);
> > +
> >  int security_sb_remount(struct super_block *sb,
> >                       void *mnt_opts)
> >  {
> > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > index 644b17ec9e63..aaa3a725da94 100644
> > --- a/security/selinux/hooks.c
> > +++ b/security/selinux/hooks.c
> > @@ -2656,6 +2656,59 @@ static int selinux_sb_eat_lsm_opts(char *options, void **mnt_opts)
> >       return rc;
> >  }
> >
> > +static int selinux_sb_do_mnt_opts_match(struct super_block *sb, void *mnt_opts)
> > +{
> > +     struct selinux_mnt_opts *opts = mnt_opts;
> > +     struct superblock_security_struct *sbsec = sb->s_security;
> > +     u32 sid;
> > +     int rc;
> > +
> > +     /* superblock not initialized (i.e. no options) - reject if any
> > +      * options specified, otherwise accept
> > +      */
> > +     if (!(sbsec->flags & SE_SBINITIALIZED))
> > +             return opts ? 0 : 1;
> > +
> > +     /* superblock initialized and no options specified - reject if
> > +      * superblock has any options set, otherwise accept
> > +      */
> > +     if (!opts)
> > +             return (sbsec->flags & SE_MNTMASK) ? 0 : 1;
> > +
> > +     if (opts->fscontext) {
> > +             rc = parse_sid(sb, opts->fscontext, &sid);
> > +             if (rc)
> > +                     return 0;
> > +             if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid))
> > +                     return 0;
> > +     }
> > +     if (opts->context) {
> > +             rc = parse_sid(sb, opts->context, &sid);
> > +             if (rc)
> > +                     return 0;
> > +             if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid))
> > +                     return 0;
> > +     }
> > +     if (opts->rootcontext) {
> > +             struct inode_security_struct *root_isec;
> > +
> > +             root_isec = backing_inode_security(sb->s_root);
> > +             rc = parse_sid(sb, opts->rootcontext, &sid);
> > +             if (rc)
> > +                     return 0;
> > +             if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
> > +                     return 0;
> > +     }
> > +     if (opts->defcontext) {
> > +             rc = parse_sid(sb, opts->defcontext, &sid);
> > +             if (rc)
> > +                     return 0;
> > +             if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid))
> > +                     return 0;
> > +     }
> > +     return 1;
> > +}
> > +
> >  static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)
> >  {
> >       struct selinux_mnt_opts *opts = mnt_opts;
> > @@ -6984,6 +7037,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
> >
> >       LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security),
> >       LSM_HOOK_INIT(sb_free_mnt_opts, selinux_free_mnt_opts),
> > +     LSM_HOOK_INIT(sb_do_mnt_opts_match, selinux_sb_do_mnt_opts_match),
> >       LSM_HOOK_INIT(sb_remount, selinux_sb_remount),
> >       LSM_HOOK_INIT(sb_kern_mount, selinux_sb_kern_mount),
> >       LSM_HOOK_INIT(sb_show_options, selinux_sb_show_options),
>
Casey Schaufler Feb. 13, 2021, 12:32 a.m. UTC | #3
On 2/12/2021 2:50 PM, Olga Kornievskaia wrote:
> Thank you for comment Casey. Comments are in line.
>
> On Fri, Feb 12, 2021 at 5:37 PM Casey Schaufler <casey@schaufler-ca.com> wrote:
>> On 2/12/2021 1:19 PM, Olga Kornievskaia wrote:
>>> From: Olga Kornievskaia <kolga@netapp.com>
>>>
>>> Add a new hook that takes an existing super block and a new mount
>>> with new options and determines if new options confict with an
>>> existing mount or not.
>>>
>>> Signed-off-by: Olga Kornievskaia <kolga@netapp.com>
>>> ---
>>>  include/linux/lsm_hook_defs.h |  1 +
>>>  include/linux/lsm_hooks.h     |  6 ++++
>>>  include/linux/security.h      |  1 +
>>>  security/security.c           |  7 +++++
>>>  security/selinux/hooks.c      | 54 +++++++++++++++++++++++++++++++++++
>>>  5 files changed, 69 insertions(+)
>>>
>>> diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
>>> index 7aaa753b8608..fbfc07d0b3d5 100644
>>> --- a/include/linux/lsm_hook_defs.h
>>> +++ b/include/linux/lsm_hook_defs.h
>>> @@ -62,6 +62,7 @@ LSM_HOOK(int, 0, sb_alloc_security, struct super_block *sb)
>>>  LSM_HOOK(void, LSM_RET_VOID, sb_free_security, struct super_block *sb)
>>>  LSM_HOOK(void, LSM_RET_VOID, sb_free_mnt_opts, void *mnt_opts)
>>>  LSM_HOOK(int, 0, sb_eat_lsm_opts, char *orig, void **mnt_opts)
>>> +LSM_HOOK(int, 0, sb_do_mnt_opts_match, struct super_block *sb, void *mnt_opts)
>> Don't you want this to be sb_mnt_opts_compatible ?
>> They may not have to match. They just need to be acceptable
>> to the security module. SELinux doesn't appear to require
>> that they "match" in all respects.
> You are right, it was a poor naming choice. Compatible is better. I
> can change it.
>>
>>>  LSM_HOOK(int, 0, sb_remount, struct super_block *sb, void *mnt_opts)
>>>  LSM_HOOK(int, 0, sb_kern_mount, struct super_block *sb)
>>>  LSM_HOOK(int, 0, sb_show_options, struct seq_file *m, struct super_block *sb)
>>> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
>>> index a19adef1f088..a11b062c1847 100644
>>> --- a/include/linux/lsm_hooks.h
>>> +++ b/include/linux/lsm_hooks.h
>>> @@ -142,6 +142,12 @@
>>>   *   @orig the original mount data copied from userspace.
>>>   *   @copy copied data which will be passed to the security module.
>>>   *   Returns 0 if the copy was successful.
>>> + * @sb_do_mnt_opts_match:
>>> + *   Determine if the existing mount options are compatible with the new
>>> + *   mount options being used.
>>> + *   @sb superblock being compared
>>> + *   @mnt_opts new mount options
>>> + *   Return 1 if options are the same.
>> This is inconsistent with the convention for returns from LSM interfaces.
>> Return 0 on success and -ESOMETHINGOROTHER if the operation would be
>> denied. Your implementation of security_sb_do_mnt_opts_match() will
>> always return 0 if there's no module supplying the hook. I seriously
>> doubt that you want the mounts to fail 100% of the time if there isn't
>> an LSM.
> The mounts are not failing. This allows a file system to decide
> whether or not to share the superblocks or not. 

OK. I hadn't gotten that far.

> Ok a good point, I
> haven't tested building a kernel where there is LSM but SElinux is not
> compiled into the kernel (ie, the only user of that hook).

That's a pretty important configuration.

>  I'm not
> sure that's a possible or an interesting option.

Yeah, it's both possible and interesting. Every Ubuntu, Tizen
and most Yocto Project based systems will be affected. There
are many use cases for which SELinux is not the best choice,
at least in this reviewer's experience.

> Ok, I can flip the returns, I wasn't aware that SElinux is so
> restrictive in its return function meaning.

You need to be aware that the LSM infrastructure, while
heavily influenced by SELinux, is not defined by SELinux.

>  Returning 0 on a success
> when you are looking for a match/capability seems backwards.

It is an artifact of the system internal convention of
returning 0 on success and an error code if something goes
wrong. There is one way to succeed, but many ways to fail.

>> Also, "options are the same" isn't the right description, even for
>> SELinux.
> Again compatible is better, I can change it.
>
>>>   * @sb_remount:
>>>   *   Extracts security system specific mount options and verifies no changes
>>>   *   are being made to those options.
>>> diff --git a/include/linux/security.h b/include/linux/security.h
>>> index c35ea0ffccd9..07026db7304d 100644
>>> --- a/include/linux/security.h
>>> +++ b/include/linux/security.h
>>> @@ -291,6 +291,7 @@ int security_sb_alloc(struct super_block *sb);
>>>  void security_sb_free(struct super_block *sb);
>>>  void security_free_mnt_opts(void **mnt_opts);
>>>  int security_sb_eat_lsm_opts(char *options, void **mnt_opts);
>>> +int security_sb_do_mnt_opts_match(struct super_block *sb, void *mnt_opts);
>>>  int security_sb_remount(struct super_block *sb, void *mnt_opts);
>>>  int security_sb_kern_mount(struct super_block *sb);
>>>  int security_sb_show_options(struct seq_file *m, struct super_block *sb);
>>> diff --git a/security/security.c b/security/security.c
>>> index 7b09cfbae94f..dae380916c6a 100644
>>> --- a/security/security.c
>>> +++ b/security/security.c
>>> @@ -890,6 +890,13 @@ int security_sb_eat_lsm_opts(char *options, void **mnt_opts)
>>>  }
>>>  EXPORT_SYMBOL(security_sb_eat_lsm_opts);
>>>
>>> +int security_sb_do_mnt_opts_match(struct super_block *sb,
>>> +                              void *mnt_opts)
>>> +{
>>> +     return call_int_hook(sb_do_mnt_opts_match, 0, sb, mnt_opts);
>>> +}
>>> +EXPORT_SYMBOL(security_sb_do_mnt_opts_match);
>>> +
>>>  int security_sb_remount(struct super_block *sb,
>>>                       void *mnt_opts)
>>>  {
>>> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
>>> index 644b17ec9e63..aaa3a725da94 100644
>>> --- a/security/selinux/hooks.c
>>> +++ b/security/selinux/hooks.c
>>> @@ -2656,6 +2656,59 @@ static int selinux_sb_eat_lsm_opts(char *options, void **mnt_opts)
>>>       return rc;
>>>  }
>>>
>>> +static int selinux_sb_do_mnt_opts_match(struct super_block *sb, void *mnt_opts)
>>> +{
>>> +     struct selinux_mnt_opts *opts = mnt_opts;
>>> +     struct superblock_security_struct *sbsec = sb->s_security;
>>> +     u32 sid;
>>> +     int rc;
>>> +
>>> +     /* superblock not initialized (i.e. no options) - reject if any
>>> +      * options specified, otherwise accept
>>> +      */
>>> +     if (!(sbsec->flags & SE_SBINITIALIZED))
>>> +             return opts ? 0 : 1;
>>> +
>>> +     /* superblock initialized and no options specified - reject if
>>> +      * superblock has any options set, otherwise accept
>>> +      */
>>> +     if (!opts)
>>> +             return (sbsec->flags & SE_MNTMASK) ? 0 : 1;
>>> +
>>> +     if (opts->fscontext) {
>>> +             rc = parse_sid(sb, opts->fscontext, &sid);
>>> +             if (rc)
>>> +                     return 0;
>>> +             if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid))
>>> +                     return 0;
>>> +     }
>>> +     if (opts->context) {
>>> +             rc = parse_sid(sb, opts->context, &sid);
>>> +             if (rc)
>>> +                     return 0;
>>> +             if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid))
>>> +                     return 0;
>>> +     }
>>> +     if (opts->rootcontext) {
>>> +             struct inode_security_struct *root_isec;
>>> +
>>> +             root_isec = backing_inode_security(sb->s_root);
>>> +             rc = parse_sid(sb, opts->rootcontext, &sid);
>>> +             if (rc)
>>> +                     return 0;
>>> +             if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
>>> +                     return 0;
>>> +     }
>>> +     if (opts->defcontext) {
>>> +             rc = parse_sid(sb, opts->defcontext, &sid);
>>> +             if (rc)
>>> +                     return 0;
>>> +             if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid))
>>> +                     return 0;
>>> +     }
>>> +     return 1;
>>> +}
>>> +
>>>  static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)
>>>  {
>>>       struct selinux_mnt_opts *opts = mnt_opts;
>>> @@ -6984,6 +7037,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
>>>
>>>       LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security),
>>>       LSM_HOOK_INIT(sb_free_mnt_opts, selinux_free_mnt_opts),
>>> +     LSM_HOOK_INIT(sb_do_mnt_opts_match, selinux_sb_do_mnt_opts_match),
>>>       LSM_HOOK_INIT(sb_remount, selinux_sb_remount),
>>>       LSM_HOOK_INIT(sb_kern_mount, selinux_sb_kern_mount),
>>>       LSM_HOOK_INIT(sb_show_options, selinux_sb_show_options),
diff mbox series

Patch

diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 7aaa753b8608..fbfc07d0b3d5 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -62,6 +62,7 @@  LSM_HOOK(int, 0, sb_alloc_security, struct super_block *sb)
 LSM_HOOK(void, LSM_RET_VOID, sb_free_security, struct super_block *sb)
 LSM_HOOK(void, LSM_RET_VOID, sb_free_mnt_opts, void *mnt_opts)
 LSM_HOOK(int, 0, sb_eat_lsm_opts, char *orig, void **mnt_opts)
+LSM_HOOK(int, 0, sb_do_mnt_opts_match, struct super_block *sb, void *mnt_opts)
 LSM_HOOK(int, 0, sb_remount, struct super_block *sb, void *mnt_opts)
 LSM_HOOK(int, 0, sb_kern_mount, struct super_block *sb)
 LSM_HOOK(int, 0, sb_show_options, struct seq_file *m, struct super_block *sb)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index a19adef1f088..a11b062c1847 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -142,6 +142,12 @@ 
  *	@orig the original mount data copied from userspace.
  *	@copy copied data which will be passed to the security module.
  *	Returns 0 if the copy was successful.
+ * @sb_do_mnt_opts_match:
+ *	Determine if the existing mount options are compatible with the new
+ *	mount options being used.
+ *	@sb superblock being compared
+ *	@mnt_opts new mount options
+ *	Return 1 if options are the same.
  * @sb_remount:
  *	Extracts security system specific mount options and verifies no changes
  *	are being made to those options.
diff --git a/include/linux/security.h b/include/linux/security.h
index c35ea0ffccd9..07026db7304d 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -291,6 +291,7 @@  int security_sb_alloc(struct super_block *sb);
 void security_sb_free(struct super_block *sb);
 void security_free_mnt_opts(void **mnt_opts);
 int security_sb_eat_lsm_opts(char *options, void **mnt_opts);
+int security_sb_do_mnt_opts_match(struct super_block *sb, void *mnt_opts);
 int security_sb_remount(struct super_block *sb, void *mnt_opts);
 int security_sb_kern_mount(struct super_block *sb);
 int security_sb_show_options(struct seq_file *m, struct super_block *sb);
diff --git a/security/security.c b/security/security.c
index 7b09cfbae94f..dae380916c6a 100644
--- a/security/security.c
+++ b/security/security.c
@@ -890,6 +890,13 @@  int security_sb_eat_lsm_opts(char *options, void **mnt_opts)
 }
 EXPORT_SYMBOL(security_sb_eat_lsm_opts);
 
+int security_sb_do_mnt_opts_match(struct super_block *sb,
+				 void *mnt_opts)
+{
+	return call_int_hook(sb_do_mnt_opts_match, 0, sb, mnt_opts);
+}
+EXPORT_SYMBOL(security_sb_do_mnt_opts_match);
+
 int security_sb_remount(struct super_block *sb,
 			void *mnt_opts)
 {
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 644b17ec9e63..aaa3a725da94 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2656,6 +2656,59 @@  static int selinux_sb_eat_lsm_opts(char *options, void **mnt_opts)
 	return rc;
 }
 
+static int selinux_sb_do_mnt_opts_match(struct super_block *sb, void *mnt_opts)
+{
+	struct selinux_mnt_opts *opts = mnt_opts;
+	struct superblock_security_struct *sbsec = sb->s_security;
+	u32 sid;
+	int rc;
+
+	/* superblock not initialized (i.e. no options) - reject if any
+	 * options specified, otherwise accept
+	 */
+	if (!(sbsec->flags & SE_SBINITIALIZED))
+		return opts ? 0 : 1;
+
+	/* superblock initialized and no options specified - reject if
+	 * superblock has any options set, otherwise accept
+	 */
+	if (!opts)
+		return (sbsec->flags & SE_MNTMASK) ? 0 : 1;
+
+	if (opts->fscontext) {
+		rc = parse_sid(sb, opts->fscontext, &sid);
+		if (rc)
+			return 0;
+		if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid))
+			return 0;
+	}
+	if (opts->context) {
+		rc = parse_sid(sb, opts->context, &sid);
+		if (rc)
+			return 0;
+		if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid))
+			return 0;
+	}
+	if (opts->rootcontext) {
+		struct inode_security_struct *root_isec;
+
+		root_isec = backing_inode_security(sb->s_root);
+		rc = parse_sid(sb, opts->rootcontext, &sid);
+		if (rc)
+			return 0;
+		if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
+			return 0;
+	}
+	if (opts->defcontext) {
+		rc = parse_sid(sb, opts->defcontext, &sid);
+		if (rc)
+			return 0;
+		if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid))
+			return 0;
+	}
+	return 1;
+}
+
 static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)
 {
 	struct selinux_mnt_opts *opts = mnt_opts;
@@ -6984,6 +7037,7 @@  static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
 
 	LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security),
 	LSM_HOOK_INIT(sb_free_mnt_opts, selinux_free_mnt_opts),
+	LSM_HOOK_INIT(sb_do_mnt_opts_match, selinux_sb_do_mnt_opts_match),
 	LSM_HOOK_INIT(sb_remount, selinux_sb_remount),
 	LSM_HOOK_INIT(sb_kern_mount, selinux_sb_kern_mount),
 	LSM_HOOK_INIT(sb_show_options, selinux_sb_show_options),