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 named root, admin, user; and root owns the whole system access ability (of course), admin can execute several system commands and some modules’ check&change capability, and user 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 the LuCI 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 execute uci 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’
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, and s:option(Value, …) in ‘usr/lib/lua/luci/model/cbi/xxx/xxx.lua’ represents a EditBox, the parameter Value can be change to DummyValue to displace the EditBox with a TextView.
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。