[PATCH v1] shebang: restrict python interactive prompt/interpreter

Mimi Zohar zohar at linux.vnet.ibm.com
Fri Jun 9 15:06:06 UTC 2017


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 at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html



More information about the Linux-security-module-archive mailing list