OpenWrt web interface security enhancement

This article introduces an approach to enhance web interface security on OpenWrt. Though it acts clumsy somewhere, it is a useful reference for some guys seeking similar solutions, I think…

And, many thanks to the guys sharing their achievements which involved in this article!

This solution targets to provide 3 users, which namedroot, admin, user; and root owns the whole system access ability (of course),admin can execute several system commands and some modules’ check&change capability, anduser can only check the designated modules.

0 Introduction

 

Reference: http://www.cnblogs.com/gnuhpc/archive/2013/08/31/3293643.html

The following figure depicts how web interfaces works on OpenWrt. Three main modules are involved,httpd takes cares about packages transmission and parsing, LuCI in charge of web page presentation, and UCI executes the final configure actions.

 

When a http package arrives the system,uhttpd analysis it’s request first, and start a LuCI process to execute it, system command or UCI are involved based on its request content.

1 Add users

Edit /etc/password to add users:

root:x:0:0:root:/root:/bin/ash

daemon:*:1:1:daemon:/var:/bin/false

ftp:*:55:55:ftp:/home/ftp:/bin/false

network:*:101:101:network:/var:/bin/false

nobody:*:65534:65534:nobody:/var:/bin/false

admin:x:1000:1000::/:/bin/ash

user:x:1001:1001::/:/bin/ash

Edit /etc/group to add admin to root group.

root:x:0:root,admin

daemon:x:1:

adm:x:4:

mail:x:8:

audio:x:29:

www-data:x:33:

ftp:x:55:

users:x:100:

network:x:101:

nogroup:x:65534:

Edit /etc/shadow to impose password for each user.

The coresponding files in build environment are located in ‘package/base-files/files/etc/

2 Enable ‘su’ command

The default busybox excludes ‘su’, we need it to execute commands via different users.

Use ‘make menuconfig’ (base system ->busybox ->Login/Password Management Utility -> su),

or you can either edit ‘.config’ directly.

3 Reassign users’ authorities

The busybox is an integrated tool, and this solution discriminate users’ authorities with such a trick that make a copy of busybox and impose it different permission configurations:

-rwxr-xr-x. 1 smbtest smbtest 435712 Feb 9 15:38 busyboxl

lrwxrwxrwx. 1 smbtest smbtest     8 Feb  9 16:17 ash -> busyboxl

lrwxrwxrwx. 1 smbtest smbtest     8 Feb  9 16:17 sh -> busyboxl

-rwsr-xr--. 1 smbtest smbtest 435712 Feb 9 15:38 busybox

lrwxrwxrwx. 1 smbtest smbtest     7 Feb  9 16:17 cat -> busybox

lrwxrwxrwx. 1 smbtest smbtest     7 Feb  9 16:17 chgrp -> busybox

lrwxrwxrwx. 1 smbtest smbtest     7 Feb  9 16:17 chmod -> busybox

……

Note: 1 ash & sh are required by user to access web page and execute some basic scripts;

            2 attribute of busybox maybe changed during build, please make sure it when system finish start-up

To make it during building, the following lines can be copied to‘package/busybox/patches/copy_busybox.patch’:

diff -ru old/Makefile.custom new/Makefile.custom

--- a/Makefile.custom  2012-02-05 03:24:55.000000000 +0800

+++ b/Makefile.custom  2015-02-09 15:16:53.928314941 +0800

@@ -26,6 +26,18 @@

 install: $(srctree)/applets/install.sh busybox busybox.links

   $(Q)DO_INSTALL_LIBS="$(strip $(LIBBUSYBOX_SONAME) $(DO_INSTALL_LIBS))" \

       $(SHELL) $< $(CONFIG_PREFIX) $(INSTALL_OPTS)

+

+   #DUPLICATED BUSYBOX start: discriminate authority of admin and user

+  cp busybox $(srctree)/ipkg-install/bin/busyboxl

+  chmod 4750 $(srctree)/ipkg-install/bin/busybox

+  chmod 755 $(srctree)/ipkg-install/bin/busyboxl

+

+  rm $(srctree)/ipkg-install/bin/sh

+  rm $(srctree)/ipkg-install/bin/ash

+  ln -s busyboxl $(srctree)/ipkg-install/bin/sh

+  ln -s busyboxl $(srctree)/ipkg-install/bin/ash

+   # DUPLICATED BUSYBOX end.

+

 ifeq ($(strip $(CONFIG_FEATURE_SUID)),y)

   @echo

   @echo

4 Enable multi-user login via web interface

Reference: https://forum.openwrt.org/viewtopic.php?pid=163013#p163013

edit /usr/lib/lua/luci/controller/admin/index.lua and change line 28 to read
        page.sysauth = {"admin","user","root"}

edit /usr/lib/lua/luci/controller/admin/system.lua and change line 326 to read
       stat = luci.sys.user.setpasswd("admin", p1)

edit /usr/lib/lua/luci/controller/admin/servicectl.lua line 18 to read
      entry({"servicectl"}, alias("servicectl", "status")).sysauth = {"admin","user","root"}

The coresponding files in build environment are located in ‘luci/modules/admin-full/luasrc/controller/admin/…

5 Invoke LuCI by different users

Till now, different users can access web pages, but the reactions are same since theLuCI process is invoked by uhttpd, which owned by root.

The following lines can be copied to‘package/uhttpd/patches/multi-user.patch’, to start LuCI process according to the current login user.

--- a/uhttpd.c

+++ b/uhttpd.c

@@ -243,6 +243,56 @@

                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="));

+

+   if ( pwdhead != NULL ) {

+       usrhead += strlen("username=");

+       memcpy( user, usrhead, pwdhead-usrhead );

+   }

+   

+   mu_dbg(user);

+}

+

+static void mu_clear_luci_cache() {

+   system("rm -Rf /tmp/luci-*");

+}

+#endif

+

 static struct http_request * uh_http_header_parse(struct client *cl,

                                                                                                                                                                                                  char *buffer, int buflen)

 {

@@ -281,7 +331,23 @@

                                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 ( strlen(user)!=0 ) {/* if the POST is for log in*/

+               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,11 +486,31 @@

                                                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(pi->phys, pi->phys, NULL);

-

+           #endif

                                                /* in case it fails ... */

                                                printf("Status: 500 Internal Server Error\r\n\r\n"

                                                                   "Unable to launch the requested CGI program:\n"

7 Endow UCI configure authority to admin

Even admin possess root group’s authority, it cannot executeuci set or uci commit since such executions require more (I don’t known the exact answer though). Here ‘su’ is deployed again to solve this problem. Modify ‘usr/lib/lua/luci/cbi.lua’ like the followings:

@Map.parse

               if self.config == "designated_module" then

                   os.execute("echo root_password | su root -c \"uci commit\"")

               else

                   self.uci:commit(config)

@Map.set

               if self.config == " designated_module" then

                   cmd = "uci set "..self.config.."."..section.."."..option.."="..value

                   return os.execute("echo root_password | su root -c \"%s\"" % cmd)

               else

                   return self.uci:set(self.config, section, option, value)

               end

The corresponding file is located in‘luci/libs/web/luasrc’

And also, such as upgrade and reboot, you need to make some similiar changes to the file located in usr/lib/lua/luci/control/admin/system.lua ,the corresponding file is located in luci/modules/admin-full/luasrc/controller/admin/system.lua

8 Modify Web pages’ appearance

In fact, I did this before Section.5. Maybe there is more decent approach to get the logined user.

Copy the following lines after line 370 in‘usr/lib/lua/luci/dispatchers.lua’ to record the current user.

               if user then

                   -- if the "luci_user" is created by "user", "chmod" cannot take effect since limited authority.

                   -- so "admin" has to "chmod" again via "su".

                   os.execute("echo root_password | su root -c \"chmod 666 /var/luci_user\"")

                   os.execute("echo %s > /var/luci_user" % user)

                   os.execute("chmod 666 /var/luci_user")

               end

and then modify specific files about the appearance setences, such as

entry(…) in ‘usr/lib/lua/luci/controller/admin/xxx.lua’ is enable sub-tab on a certain page,which can be commented to cancel the sub-tab, ands:option(Value, …) in ‘usr/lib/lua/luci/model/cbi/xxx/xxx.lua’represents a EditBox, the parameter Value can be change toDummyValue to displace the EditBox with a TextView.

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值