我所用Ubuntu版本为ubuntu 16.04 64bit
一、安装
apt install apache2 libapache2-mod-fcgid libfcgi-dev
二、配置
检查 /etc/apache2/mods-enabled目录下是否已经有了fcgid相关软连接,如果没有可以自己添加软连接,我的如下:
fcgid.conf -> ../mods-available/fcgid.conf
fcgid.load -> ../mods-available/fcgid.load
修改 /etc/apache2/sites-enabled/000-default.conf添加如下内容:
SetHandler fcgid-script
Options +ExecCGI
# Customize the next two directives for your requirements.
Order allow,deny
Allow from all
最后重启apache2:
/etc/init.d/apache2 restart
三、开发fastcgi程序
示例代码如下(tiny-fcgi.c):
#include
#include
#include
#include
#include
#include
int main()
{
extern char **environ;
int nlen = 0;
int i;
char *psz_content = NULL;
char **pp_env;
setuid(geteuid()); //设置实际用户id为有效用户id
while(FCGI_Accept() >= 0)
{
printf("Content-type: text/html\n\n");
for ( pp_env = environ; *pp_env; pp_env++ )
printf("%s
", *pp_env);
if ( strcmp("GET", getenv("REQUEST_METHOD")) == 0 )
{
char buf[128];
printf("%s
", getenv("QUERY_STRING"));
sprintf(buf, "mkdir -p /var/www/cgi-bin/%s", getenv("QUERY_STRING"));
printf("%s\n", buf);
system(buf);
}
else
{
nlen = atoi(getenv("CONTENT_LENGTH"));
psz_content = ( char * )malloc( nlen + 1 );
memset( psz_content, 0, nlen + 1 );
printf("char value:
");
for (i = 0; i < nlen; i++ )
{
psz_content[i] = fgetc(stdin);
printf( "%c", psz_content[i] );
}
printf("
string value: %s
", psz_content);
}
}
return 0;
}
下面操作都是在root用户下进行:
编译:gcc tiny-fcgi.c -o tiny-fcgi -lfcgi
设置程序suid:chmod a+s tiny-fcgi
把编译完成的tiny-fcgi拷贝到/var/www/cgi-bin/目录下(在步骤二里面配置的)
打开浏览器,输入http://192.168.99.1/cgi-bin/tiny-fcgi?dir (这里假设你主机ip地址为192.168.99.1),会发现fcgi程序运行成功,打印了一些环境变量,同时在/var/www/cgi-bin目录下创建了dir目录(该目录下创建文件夹需要root权限),说明该程序具有root权限
四、实际使用的总结
fastcgi root运行经验:CGI程序如果通过C代码执行存取,设置该程序的拥有者为root(chown),再设置suid(chmod +s fastcgi)即可具有root存取权限; 如果在该C代码内通过system()函数执行外部脚本,则需要在system()之前通过setuid()函数设置实际用户ID为有效用户id(setuid(geteuid());)。
五、自己的理解
首先,我们要知道一点:进程在运行的时候,有一些属性,其中包括 实际用户ID,实际组ID,有效用户ID,有效组ID等。 实际用户ID和实际组ID标识我们是谁,谁在运行这个程序。 而有效用户ID和有效组ID则决定了进程在运行时的权限。内核在决定进程是否有文件存取权限时,是采用了进程的有效用户ID来进行判断的。
设置用户ID位:用于对外的权限的开发,它的作用是我们如何去修改有效用户ID,在后面的例子中在展开。借用别人的一张图片:
自己的理解如下:
当设置用户ID位打开时,非root用户在执行该程序时,有效用户ID被设置为该文件的用户ID。试想如果该文件为root所有,同时设置了设置用户ID位,则其他用户在执行该程序时就有root的存取权限了;实际上这时候因为有了root权限,程序代码中调用setuid(geteuid())也是成功的,即实际用户ID和有效用户都为root。
Apache2程序默认是以www-data用户和组运行的,默认情况下不允许以root用户运行(除非自己编译,改变编译选项,但是这样运行不安全)。我们的fastcgi程序由Apache2启动,继承了Apache2的用户和组,该用户没有root权限。
如果我们设置自己的fastcgi程序的有效用户ID(chmod +s fastcgi),则我们的程序具有root权限了(内核在决定进程是否有文件存取权限时,是采用了进程的有效用户ID来进行判断的);但是如果我们在程序中运行外部脚本,该脚本继承的是实际用户ID,不具有root权限;如果我们在运行脚本之前,设置自己的UID为0(即root),则可以使脚本继承该UID,以root用户执行
六、参考
http://bigbang.waterlin.org/bang/using-apache-with-cpp-to-develop-fastcgi-web-server/
Apache下root权限运行CGI:https://blog.csdn.net/a_nervous_free_man/article/details/49914491
无死角理解保存设置用户ID,设置用户ID位,有效用户ID,实际用户ID: https://blog.csdn.net/demiaowu/article/details/39370355