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

Tetsuo Handa penguin-kernel at I-love.SAKURA.ne.jp
Fri Jun 9 14:02:59 UTC 2017


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 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