Linux中让特殊权限(SUID)对Python脚本生效
通常情况下,我们需要用Python写一些脚本对项目文件做管理,而这样的文件管理是必然附带特殊权限的。通常的处理方法是RPC。但是首先RPC写起来还是挺麻烦的,其次对于存储池来说,RPC服务器在处理完指定数据后客户端往往需要一段不短的时间来等待数据在存储节点间的同步。由此,我希望能使用SUID来直接在客户端执行脚本。但很显然,SUID通常只能对C/C++等编译出来的程序生效,对Python脚本是无效的,除非你打算给/usr/bin/python设置SUID——那就相当于完全开放权限了。为此,我在网上找了很久,终于找到了一个可行的方案。
suid-python
我在StackOverflow上找到了这个问题:Semantics of SUID (Set-User-ID),该问题下方有人贴上了一个C的源代码:suid-python.c,我惊喜的发现它可以完全满足我的需求。我先把源代码贴上来。
/* suid-python version 0.95
* by Steven Elliott <selliott4@austin.rr.com>
*
* A program to securely run Python scripts with the setuid bits set on the
* script applied to the resulting Python process as if the Python script was
* an executable. This utility could probably be easily adapted to other
* scripting languages.
*
* Install this program by building it with
* gcc -o suid-python suid-python.c
* then copy it to a secure directory
* cp suid-python /usr/local/bin
* and then set the ownership to root setuid bit
* chown root.root suid-python
* chmod 755 suid-python
* chmod u+s suid-python
*
* Try it by creating a secure Python script that starts with
* #!/usr/bin/env suid-python
* that is to run as a user other than the invoking user. Set that user and
* corresponding setuid bits, and then run it.
*
* This program is subject to the same open source license as Python:
* http://www.python.org/2.4.2/license.html
*/
#include <errno.h>
#include <libgen.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
/* Defines */
/* Flags. There are to be used with "#if", not "#ifdef". These are all off
* since there are security problems with each one.
*/
#define ISEC_ALLOWED 0 /* Isecure directories. */
#define STICKY_ALLOWED 0 /* Consider sticky bits, such as /tmp */
#define SWITCHES_ALLOWED 0 /* Python switches, such as -i */
#define NOBODY "nobody"
#define PYTHON "python"
#define SAFE_PATH "PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:" \
"/usr/sbin:/usr/bin:/usr/X11R6/bin"
/* Globals */
/* Harmful environment variables on Linux. Other OSs may have other
* variables.
*/
static char *bad_prefixes[] = {
"IFS", "LD_", "PATH", "PYTHON", NULL };
/* Directories that are likely to be trusted (directories that are likely to
* be local and that don't have the "nosuid" mount option set).
*/
#if ! ISEC_ALLOWED
static char *trusted_dirs[] = {
"/bin/", "/boot/", "/etc/", "/lib/", "/opt/",
"/root/", "/sbin/", "/usr/", NULL
};
#endif
static gid_t script_gid;
static int script_sgid_set;
static int script_suid_set;
static uid_t script_uid;
struct stat script_stat;
void
usage(char *name)
{
fprintf(stderr, "Usage: %s suid-script [arg1 [arg2 ..]]\n",
basename(name));
}
/* Wrapper functions that abort when they fail. */
char *
getcwd_abort(char *buf, size_t size)
{
char *cwd;
cwd = getcwd(buf, size);
if (!cwd)
{
fprintf(stderr, "Could not get the CWD of %s. errno=%d\n",
buf, errno);
exit(1);
}
return cwd;
}
int
lstat_abort(const char *file_name, struct stat *buf)
{
if (lstat(file_name, buf) == -1)
{
fprintf(stderr, "Could not lstat %s. errno=%d\n", file_name, errno);
exit(1);
}
return 0;
}
void *
malloc_abort(size_t size)
{
void *buf;
buf = malloc(size);
if (!buf)
{