From b4c4a606bfa80c9c1f0712fe0c1be57b5833d801 Mon Sep 17 00:00:00 2001
From: imarkov <imarkov@tarantool.org>
Date: Mon, 29 Jan 2018 16:46:59 +0300
Subject: [PATCH] security: Prohibit to drop super role

* Create constant SUPER - id of super role
* Forward the constant to box.schema
* Add checks on drop super role

Closes #3084
---
 src/box/alter.cc         |  2 +-
 src/box/lua/schema.lua   |  2 +-
 src/box/lua/space.cc     |  8 ++++++++
 src/box/user_def.h       |  1 +
 test/box/access.result   | 44 ++++++++++++++++++++++++++++++++++++++++
 test/box/access.test.lua | 11 ++++++++++
 6 files changed, 66 insertions(+), 2 deletions(-)

diff --git a/src/box/alter.cc b/src/box/alter.cc
index c48e89f9e6..46c90c883a 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -2043,7 +2043,7 @@ on_replace_dd_user(struct trigger * /* trigger */, void *event)
 		access_check_ddl(old_user->def->name, old_user->def->owner,
 				 SC_USER, PRIV_D, true);
 		/* Can't drop guest or super user */
-		if (uid <= (uint32_t) BOX_SYSTEM_USER_ID_MAX) {
+		if (uid <= (uint32_t) BOX_SYSTEM_USER_ID_MAX || uid == SUPER) {
 			tnt_raise(ClientError, ER_DROP_USER,
 				  old_user->def->name,
 				  "the user or the role is a system");
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 89e27555c6..756134b91e 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -2143,7 +2143,7 @@ box.schema.role.drop = function(name, opts)
         return
     end
     if uid >= box.schema.SYSTEM_USER_ID_MIN and
-       uid <= box.schema.SYSTEM_USER_ID_MAX then
+       uid <= box.schema.SYSTEM_USER_ID_MAX or uid == box.schema.SUPER_ROLE_ID then
         -- gh-1205: box.schema.user.info fails
         box.error(box.error.DROP_USER, name, "the user or the role is a system")
     end
diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index e95c5285f7..29a9aca931 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -439,6 +439,14 @@ box_lua_space_init(struct lua_State *L)
 	lua_setfield(L, -2, "SYSTEM_USER_ID_MIN");
 	lua_pushnumber(L, BOX_SYSTEM_USER_ID_MAX);
 	lua_setfield(L, -2, "SYSTEM_USER_ID_MAX");
+	lua_pushnumber(L, ADMIN);
+	lua_setfield(L, -2, "ADMIN_ID");
+	lua_pushnumber(L, GUEST);
+	lua_setfield(L, -2, "GUEST_ID");
+	lua_pushnumber(L, PUBLIC);
+	lua_setfield(L, -2, "PUBLIC_ROLE_ID");
+	lua_pushnumber(L, SUPER);
+	lua_setfield(L, -2, "SUPER_ROLE_ID");
 	lua_pushnumber(L, BOX_INDEX_MAX);
 	lua_setfield(L, -2, "INDEX_MAX");
 	lua_pushnumber(L, BOX_SPACE_MAX);
diff --git a/src/box/user_def.h b/src/box/user_def.h
index 1104ec634a..8bf31c2fc4 100644
--- a/src/box/user_def.h
+++ b/src/box/user_def.h
@@ -170,6 +170,7 @@ enum {
 	GUEST = 0,
 	ADMIN =  1,
 	PUBLIC = 2, /* role */
+	SUPER = 31, /* role */
 	BOX_SYSTEM_USER_ID_MAX = PUBLIC
 };
 
diff --git a/test/box/access.result b/test/box/access.result
index 410f1d7762..0ee17ebff7 100644
--- a/test/box/access.result
+++ b/test/box/access.result
@@ -747,10 +747,18 @@ box.schema.user.drop('guest')
 ---
 - error: 'Failed to drop user or role ''guest'': the user or the role is a system'
 ...
+box.schema.role.drop('guest')
+---
+- error: Role 'guest' is not found
+...
 box.space._user.index.name:delete{'guest'}
 ---
 - error: 'Failed to drop user or role ''guest'': the user or the role is a system'
 ...
+box.space._user:delete{box.schema.GUEST_ID}
+---
+- error: 'Failed to drop user or role ''guest'': the user or the role is a system'
+...
 #box.schema.user.info('guest') > 0
 ---
 - true
@@ -759,14 +767,26 @@ box.schema.user.drop('admin')
 ---
 - error: 'Failed to drop user or role ''admin'': the user or the role is a system'
 ...
+box.schema.role.drop('admin')
+---
+- error: Role 'admin' is not found
+...
 box.space._user.index.name:delete{'admin'}
 ---
 - error: 'Failed to drop user or role ''admin'': the user or the role is a system'
 ...
+box.space._user:delete{box.schema.ADMIN_ID}
+---
+- error: 'Failed to drop user or role ''admin'': the user or the role is a system'
+...
 #box.schema.user.info('admin') > 0
 ---
 - true
 ...
+box.schema.user.drop('public')
+---
+- error: User 'public' is not found
+...
 box.schema.role.drop('public')
 ---
 - error: 'Failed to drop user or role ''public'': the user or the role is a system'
@@ -775,10 +795,34 @@ box.space._user.index.name:delete{'public'}
 ---
 - error: 'Failed to drop user or role ''public'': the user or the role is a system'
 ...
+box.space._user:delete{box.schema.PUBLIC_ROLE_ID}
+---
+- error: 'Failed to drop user or role ''public'': the user or the role is a system'
+...
 #box.schema.role.info('public') > 0
 ---
 - true
 ...
+box.schema.role.drop('super')
+---
+- error: 'Failed to drop user or role ''super'': the user or the role is a system'
+...
+box.schema.user.drop('super')
+---
+- error: User 'super' is not found
+...
+box.space._user.index.name:delete{'super'}
+---
+- error: 'Failed to drop user or role ''super'': the user or the role is a system'
+...
+box.space._user:delete{box.schema.SUPER_ROLE_ID}
+---
+- error: 'Failed to drop user or role ''super'': the user or the role is a system'
+...
+#box.schema.role.info('super') > 0
+---
+- true
+...
 -- gh-944 name is too long
 name = string.rep('a', box.schema.NAME_MAX - 1)
 ---
diff --git a/test/box/access.test.lua b/test/box/access.test.lua
index 02c6252e5e..241f254534 100644
--- a/test/box/access.test.lua
+++ b/test/box/access.test.lua
@@ -298,14 +298,25 @@ box.schema.func.drop('blah', 'blah')
 box.schema.user.passwd('guest', 'sesame')
 -- gh-1205 box.schema.user.info fails
 box.schema.user.drop('guest')
+box.schema.role.drop('guest')
 box.space._user.index.name:delete{'guest'}
+box.space._user:delete{box.schema.GUEST_ID}
 #box.schema.user.info('guest') > 0
 box.schema.user.drop('admin')
+box.schema.role.drop('admin')
 box.space._user.index.name:delete{'admin'}
+box.space._user:delete{box.schema.ADMIN_ID}
 #box.schema.user.info('admin') > 0
+box.schema.user.drop('public')
 box.schema.role.drop('public')
 box.space._user.index.name:delete{'public'}
+box.space._user:delete{box.schema.PUBLIC_ROLE_ID}
 #box.schema.role.info('public') > 0
+box.schema.role.drop('super')
+box.schema.user.drop('super')
+box.space._user.index.name:delete{'super'}
+box.space._user:delete{box.schema.SUPER_ROLE_ID}
+#box.schema.role.info('super') > 0
 
 -- gh-944 name is too long
 name = string.rep('a', box.schema.NAME_MAX - 1)
-- 
GitLab