摘要
OpenWrt系統安全改進<四>中介紹的只是在UI層面對用戶進行訪問控制,對於深層次非法操作並不能起到保護效果。本節介紹針對不同的用戶登錄請求,使用不同用戶啓動luci進程,從而實現不同用戶進行操作級別的訪問控制。
機制分析
web頁面操作涉及到uhttpd和luci兩個模塊,uhttpd處理http報文,將cgi請求轉給luci處理。從代碼實現就可以看出這兩個模塊目前只是針對單用戶:
1 uhttpd在初始化時,由root用戶啓動,在調用cgi請求時,使用execl調用luci
2 luci的cache內容只保留一份。
在本實踐中,創建admin(歸爲root用戶組)和user兩個用戶,當uhttpd接收到登陸請求包,判斷用戶是否發生變化,如果是就清除luci的cache(否則luci會執行出錯),使用su啓動luci進程。
準備工作
創建用戶和用戶組,相關的腳本(可執行文件)針對不同用戶組分配不同的權限。
(注意:busybox中的ash和sh需要能夠被所有用戶可以執行,我採用了兩份busybox,ash/sh link到所有用戶可執行的busybox,其他link到只有同組用戶可執行的busybox)
使能busybox的su
使能web ui的多用戶登陸
具體操作可以參見OpenWrt系統安全改進<一>
實現不同用戶身份執行luci
- --- a/uhttpd.c
- +++ b/uhttpd.c
- @@ -243,6 +243,64 @@
- return bound;
- }
- +#ifndef MULTI_USER
- +extern void MU_dbg(char *info);
- +void MU_dbg(char *info)
- +{
- + #if 0
- + int fd = open("/debug",O_RDWR|O_APPEND);
- + char buf[128] = {0};
- + int len = strlen(info);
- +
- + if (fd<0) {
- + fd = open("/debug",O_CREAT|O_RDWR);
- + }
- +
- + memcpy(buf,info,len);
- + buf[len] = '\n';
- +
- + write(fd,buf,len+1);
- + close(fd);
- + #endif
- +}
- +
- +static char CUR_USER[32] = {0};
- +
- +extern void get_cur_user(char *user);
- +void get_cur_user(char *user) {
- + if ( strlen(CUR_USER)==0 ) {
- + MU_dbg("CUR_USER empty");
- + strcpy(user,"admin");
- + } else {
- + memcpy(user,CUR_USER,strlen(CUR_USER));
- + }
- +}
- +
- +static void MU_get_user(const struct client *cl, char *user) {
- + char *usrhead = cl->httpbuf.ptr;
- + char *pwdhead = strfind(usrhead,strlen(usrhead),"&password=",strlen("&password="));
- +
- + usrhead += strlen("username=");
- + memcpy( user, usrhead, pwdhead-usrhead );
- +
- + MU_dbg(user);
- +}
- +
- +static void MU_clear_luci_cache() {
- + int ret = 0;
- +
- + ret = system("echo clear cache > /tmp/luci_action");
- + ret = system("rm -Rf /tmp/luci-*");
- + if ( ret == -1 ) {
- + system("echo fail >> /debug");
- + } else if ( ret==0 ) {
- + system("echo ignore >> /debug");
- + } else {
- + system("echo finish >> /debug");
- + }
- +}
- +#endif
- +
- static struct http_request * uh_http_header_parse(struct client *cl,
- char *buffer, int buflen)
- {
- @@ -281,7 +339,21 @@
- if (method && !strcmp(method, "GET"))
- req->method = UH_HTTP_MSG_GET;
- else if (method && !strcmp(method, "POST"))
- - req->method = UH_HTTP_MSG_POST;
- + #ifndef MULTI_USER
- + {
- + char user[32] = {0};
- +
- + MU_get_user(cl,user);
- + if( strncmp(user,CUR_USER,4)!=0 ) {
- + MU_clear_luci_cache();
- + }
- + memcpy(CUR_USER,user,32);
- +
- + req->method = UH_HTTP_MSG_POST;
- + }
- + #else
- + req->method = UH_HTTP_MSG_POST;
- + #endif
- else if (method && !strcmp(method, "HEAD"))
- req->method = UH_HTTP_MSG_HEAD;
- else
- --- a/uhttpd-cgi.c
- +++ b/uhttpd-cgi.c
- @@ -486,12 +486,32 @@
- if (chdir(pi->root))
- perror("chdir()");
- + #ifndef MULTI_USER
- + {
- + extern void get_cur_user(char *user);
- + extern void MU_dbg(char *info);
- +
- + char user[32] = {0};
- + char info[32] = {0};
- +
- + get_cur_user(user);
- +
- + sprintf(info,"exec via %s",user);
- + MU_dbg(info);
- +
- + if (ip!=NULL) {
- + execl("/bin/su","su",user,"-c",ip->path,pi->phys,NULL);
- + } else {
- + execl("/bin/su","su",user,"-c",pi->phys,NULL);
- + }
- + }
- + #else
- if (ip != NULL)
- - execl(ip->path, ip->path, pi->phys, NULL);
- - else
- + execl(ip->path, ip->path, pi->phys, NULL);
- + else
- execl(pi->phys, pi->phys, NULL);
- -
- - /* in case it fails ... */
- + #endif
- + /* in case it fails ... */
- printf("Status: 500 Internal Server Error\r\n\r\n"
- "Unable to launch the requested CGI program:\n"
- " %s: %s\n", ip ? ip->path : pi->phys, strerror(errno));
--- a/uhttpd.c
+++ b/uhttpd.c
@@ -243,6 +243,64 @@
return bound;
}
+#ifndef MULTI_USER
+extern void MU_dbg(char *info);
+void MU_dbg(char *info)
+{
+ #if 0
+ int fd = open("/debug",O_RDWR|O_APPEND);
+ char buf[128] = {0};
+ int len = strlen(info);
+
+ if (fd<0) {
+ fd = open("/debug",O_CREAT|O_RDWR);
+ }
+
+ memcpy(buf,info,len);
+ buf[len] = '\n';
+
+ write(fd,buf,len+1);
+ close(fd);
+ #endif
+}
+
+static char CUR_USER[32] = {0};
+
+extern void get_cur_user(char *user);
+void get_cur_user(char *user) {
+ if ( strlen(CUR_USER)==0 ) {
+ MU_dbg("CUR_USER empty");
+ strcpy(user,"admin");
+ } else {
+ memcpy(user,CUR_USER,strlen(CUR_USER));
+ }
+}
+
+static void MU_get_user(const struct client *cl, char *user) {
+ char *usrhead = cl->httpbuf.ptr;
+ char *pwdhead = strfind(usrhead,strlen(usrhead),"&password=",strlen("&password="));
+
+ usrhead += strlen("username=");
+ memcpy( user, usrhead, pwdhead-usrhead );
+
+ MU_dbg(user);
+}
+
+static void MU_clear_luci_cache() {
+ int ret = 0;
+
+ ret = system("echo clear cache > /tmp/luci_action");
+ ret = system("rm -Rf /tmp/luci-*");
+ if ( ret == -1 ) {
+ system("echo fail >> /debug");
+ } else if ( ret==0 ) {
+ system("echo ignore >> /debug");
+ } else {
+ system("echo finish >> /debug");
+ }
+}
+#endif
+
static struct http_request * uh_http_header_parse(struct client *cl,
char *buffer, int buflen)
{
@@ -281,7 +339,21 @@
if (method && !strcmp(method, "GET"))
req->method = UH_HTTP_MSG_GET;
else if (method && !strcmp(method, "POST"))
- req->method = UH_HTTP_MSG_POST;
+ #ifndef MULTI_USER
+ {
+ char user[32] = {0};
+
+ MU_get_user(cl,user);
+ if( strncmp(user,CUR_USER,4)!=0 ) {
+ MU_clear_luci_cache();
+ }
+ memcpy(CUR_USER,user,32);
+
+ req->method = UH_HTTP_MSG_POST;
+ }
+ #else
+ req->method = UH_HTTP_MSG_POST;
+ #endif
else if (method && !strcmp(method, "HEAD"))
req->method = UH_HTTP_MSG_HEAD;
else
--- a/uhttpd-cgi.c
+++ b/uhttpd-cgi.c
@@ -486,12 +486,32 @@
if (chdir(pi->root))
perror("chdir()");
+ #ifndef MULTI_USER
+ {
+ extern void get_cur_user(char *user);
+ extern void MU_dbg(char *info);
+
+ char user[32] = {0};
+ char info[32] = {0};
+
+ get_cur_user(user);
+
+ sprintf(info,"exec via %s",user);
+ MU_dbg(info);
+
+ if (ip!=NULL) {
+ execl("/bin/su","su",user,"-c",ip->path,pi->phys,NULL);
+ } else {
+ execl("/bin/su","su",user,"-c",pi->phys,NULL);
+ }
+ }
+ #else
if (ip != NULL)
- execl(ip->path, ip->path, pi->phys, NULL);
- else
+ execl(ip->path, ip->path, pi->phys, NULL);
+ else
execl(pi->phys, pi->phys, NULL);
-
- /* in case it fails ... */
+ #endif
+ /* in case it fails ... */
printf("Status: 500 Internal Server Error\r\n\r\n"
"Unable to launch the requested CGI program:\n"
" %s: %s\n", ip ? ip->path : pi->phys, strerror(errno));
實現admin執行UCI修改
完成上步操作後,使用不同用戶登錄可以根據用戶執行LuCI,但是admin和user都只能查詢UCI相關的一些配置,而我期望的admin能夠修改配置。
經過一些嘗試,發現僅僅爲admin賦予uci的執行權限和相關配置文件的寫權限扔不能執行UCI修改操作(uci set/commit)。
我希望修改的配置是通過UCI Map來進行修改的,修改usr/lib/lua/luci/cbi.lua文件的Map.set和Map.parse,使用su權限來執行uci set 和 commit。
後續工作
目前實現雖然支持了多用戶的操作權限控制,但是對於用戶切換時訪問效率低,後續可以修改luci,針對每個用戶創建一份cache。