Tarantool development patches archive
 help / color / mirror / Atom feed
* [Tarantool-patches] [PATCH v1 1/1] box: remove unnecessary rights from peristent functions
@ 2020-12-21 11:38 imeevma
  2020-12-21 19:14 ` Sergey Ostanevich
  0 siblings, 1 reply; 15+ messages in thread
From: imeevma @ 2020-12-21 11:38 UTC (permalink / raw)
  To: s.ostanevich; +Cc: tarantool-patches

After this patch, the persistent functions "box.schema.user.info" and
"LUA" will have the same rights as the user who executed them.

The problem was that setuid was unnecessarily set. Because of this,
these functions had the same rights as the user who created them.
However, they must have the same rights as the user who used them.

Part of tarantool/security#1
---
https://github.com/tarantool/security/issues/1
https://github.com/tarantool/tarantool/tree/imeevma/gh-security-1-lua-function-access-2_6


 src/box/bootstrap.snap       | Bin 5976 -> 5997 bytes
 src/box/lua/upgrade.lua      |  30 +++++++++++++++++++++++++++++
 test/box-py/bootstrap.result |   4 ++--
 test/box/access.result       |  36 +++++++++++++++++++++++++++++++++++
 test/box/access.test.lua     |  15 +++++++++++++++
 5 files changed, 83 insertions(+), 2 deletions(-)

diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap
index 8bd4f7ce24216a8bcced6aa97c20c83eb7a02c77..b9d84d7abd9dba0dd5ca72438c69cc6a5c5f0470 100644

diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
index add791cd7..be1a0d47d 100644
--- a/src/box/lua/upgrade.lua
+++ b/src/box/lua/upgrade.lua
@@ -971,6 +971,35 @@ local function upgrade_to_2_3_1()
     create_session_settings_space()
 end
 
+--------------------------------------------------------------------------------
+-- Tarantool 2.6.2
+--------------------------------------------------------------------------------
+local function backport_upgrade_2_7_1_function_access()
+    local _func = box.space._func
+    local _priv = box.space._priv
+    local datetime = os.date("%Y-%m-%d %H:%M:%S")
+    local funcs_to_change = {'LUA', 'box.schema.user.info'}
+    for _, name in pairs(funcs_to_change) do
+        local func = _func.index['name']:get(name)
+        if func ~= nil and func.setuid ~= 0 then
+            local id = func.id
+            log.info('remove old function "'..name..'"')
+            _priv:delete({2, 'function', id})
+            _func:delete({id})
+            log.info('create function "'..name..'" with unset setuid')
+            local new_func = func:update({{'=', 4, 0}, {'=', 18, datetime},
+                                          {'=', 19, datetime}})
+            _func:replace(new_func)
+            log.info('grant execute on function "'..name..'" to public')
+            _priv:replace{ADMIN, PUBLIC, 'function', id, box.priv.X}
+        end
+    end
+end
+
+local function upgrade_to_2_6_2()
+    backport_upgrade_2_7_1_function_access()
+end
+
 --------------------------------------------------------------------------------
 
 local function get_version()
@@ -1007,6 +1036,7 @@ local function upgrade(options)
         {version = mkversion(2, 2, 1), func = upgrade_to_2_2_1, auto = true},
         {version = mkversion(2, 3, 0), func = upgrade_to_2_3_0, auto = true},
         {version = mkversion(2, 3, 1), func = upgrade_to_2_3_1, auto = true},
+        {version = mkversion(2, 6, 2), func = upgrade_to_2_6_2, auto = true},
     }
 
     for _, handler in ipairs(handlers) do
diff --git a/test/box-py/bootstrap.result b/test/box-py/bootstrap.result
index 0876e77a6..a371bb369 100644
--- a/test/box-py/bootstrap.result
+++ b/test/box-py/bootstrap.result
@@ -4,7 +4,7 @@ box.internal.bootstrap()
 box.space._schema:select{}
 ---
 - - ['max_id', 511]
-  - ['version', 2, 3, 1]
+  - ['version', 2, 6, 2]
 ...
 box.space._cluster:select{}
 ---
@@ -167,7 +167,7 @@ box.space._user:select{}
 ...
 for _, v in box.space._func:pairs{} do r = {} table.insert(r, v:update({{"=", 18, ""}, {"=", 19, ""}})) return r end
 ---
-- - [1, 1, 'box.schema.user.info', 1, 'LUA', '', 'function', [], 'any', 'none', 'none',
+- - [1, 1, 'box.schema.user.info', 0, 'LUA', '', 'function', [], 'any', 'none', 'none',
     false, false, true, ['LUA'], {}, '', '', '']
 ...
 box.space._priv:select{}
diff --git a/test/box/access.result b/test/box/access.result
index 20b1b8b35..27e636122 100644
--- a/test/box/access.result
+++ b/test/box/access.result
@@ -2141,3 +2141,39 @@ box.schema.user.revoke('guest', 'read,write,execute', 'space', 'not_universe')
 sp:drop()
 ---
 ...
+--
+-- Make sure that the functions "LUA" and "box.schema.user.info" do not have
+-- excess rights.
+--
+_ = box.schema.func.call("LUA", "return 1")
+---
+...
+_ = box.schema.func.call("LUA", "return box.space._space:count()")
+---
+...
+_ = box.schema.func.call("box.schema.user.info", 0)
+---
+...
+_ = box.schema.func.call("box.schema.user.info", 1)
+---
+...
+session.su('guest')
+---
+...
+_ = box.schema.func.call("LUA", "return 1")
+---
+...
+_ = box.schema.func.call("LUA", "return box.space._space:count()")
+---
+- error: Read access to space '_space' is denied for user 'guest'
+...
+_ = box.schema.func.call("box.schema.user.info", 0)
+---
+...
+_ = box.schema.func.call("box.schema.user.info", 1)
+---
+- error: User '1' is not found
+...
+session.su('admin')
+---
+...
diff --git a/test/box/access.test.lua b/test/box/access.test.lua
index 3e083a383..a62f87ad8 100644
--- a/test/box/access.test.lua
+++ b/test/box/access.test.lua
@@ -824,3 +824,18 @@ box.schema.user.grant('guest', 'read,write,execute', 'space', 'not_universe')
 box.schema.user.revoke('guest', 'read,write,execute', 'universe')
 box.schema.user.revoke('guest', 'read,write,execute', 'space', 'not_universe')
 sp:drop()
+
+--
+-- Make sure that the functions "LUA" and "box.schema.user.info" do not have
+-- excess rights.
+--
+_ = box.schema.func.call("LUA", "return 1")
+_ = box.schema.func.call("LUA", "return box.space._space:count()")
+_ = box.schema.func.call("box.schema.user.info", 0)
+_ = box.schema.func.call("box.schema.user.info", 1)
+session.su('guest')
+_ = box.schema.func.call("LUA", "return 1")
+_ = box.schema.func.call("LUA", "return box.space._space:count()")
+_ = box.schema.func.call("box.schema.user.info", 0)
+_ = box.schema.func.call("box.schema.user.info", 1)
+session.su('admin')
-- 
2.25.1

^ permalink raw reply	[flat|nested] 15+ messages in thread
* [Tarantool-patches] [PATCH v1 1/1] box: remove unnecessary rights from peristent functions
@ 2020-12-22  7:56 imeevma
  2020-12-22  8:13 ` Sergey Ostanevich
  0 siblings, 1 reply; 15+ messages in thread
From: imeevma @ 2020-12-22  7:56 UTC (permalink / raw)
  To: s.ostanevich; +Cc: tarantool-patches

After this patch, the persistent functions "box.schema.user.info" and
"LUA" will have the same rights as the user who executed them.

The problem was that setuid was unnecessarily set. Because of this,
these functions had the same rights as the user who created them.
However, they must have the same rights as the user who used them.

Part of tarantool/security#1
---
https://github.com/tarantool/security/issues/1
https://github.com/tarantool/tarantool/tree/imeevma/gh-security-1-lua-function-access-2_5

 src/box/bootstrap.snap       | Bin 5976 -> 5975 bytes
 src/box/lua/upgrade.lua      |  30 +++++++++++++++++++++++++++++
 test/box-py/bootstrap.result |   4 ++--
 test/box/access.result       |  36 +++++++++++++++++++++++++++++++++++
 test/box/access.test.lua     |  15 +++++++++++++++
 5 files changed, 83 insertions(+), 2 deletions(-)

diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap
index 8bd4f7ce24216a8bcced6aa97c20c83eb7a02c77..49203ad568ee549ce63ea059775cb74369f407f6 100644

diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
index add791cd7..e5ddd513f 100644
--- a/src/box/lua/upgrade.lua
+++ b/src/box/lua/upgrade.lua
@@ -971,6 +971,35 @@ local function upgrade_to_2_3_1()
     create_session_settings_space()
 end
 
+--------------------------------------------------------------------------------
+-- Tarantool 2.5.3
+--------------------------------------------------------------------------------
+local function backport_upgrade_2_7_1_function_access()
+    local _func = box.space._func
+    local _priv = box.space._priv
+    local datetime = os.date("%Y-%m-%d %H:%M:%S")
+    local funcs_to_change = {'LUA', 'box.schema.user.info'}
+    for _, name in pairs(funcs_to_change) do
+        local func = _func.index['name']:get(name)
+        if func ~= nil and func.setuid ~= 0 then
+            local id = func.id
+            log.info('remove old function "'..name..'"')
+            _priv:delete({2, 'function', id})
+            _func:delete({id})
+            log.info('create function "'..name..'" with unset setuid')
+            local new_func = func:update({{'=', 4, 0}, {'=', 18, datetime},
+                                          {'=', 19, datetime}})
+            _func:replace(new_func)
+            log.info('grant execute on function "'..name..'" to public')
+            _priv:replace{ADMIN, PUBLIC, 'function', id, box.priv.X}
+        end
+    end
+end
+
+local function upgrade_to_2_5_3()
+    backport_upgrade_2_7_1_function_access()
+end
+
 --------------------------------------------------------------------------------
 
 local function get_version()
@@ -1007,6 +1036,7 @@ local function upgrade(options)
         {version = mkversion(2, 2, 1), func = upgrade_to_2_2_1, auto = true},
         {version = mkversion(2, 3, 0), func = upgrade_to_2_3_0, auto = true},
         {version = mkversion(2, 3, 1), func = upgrade_to_2_3_1, auto = true},
+        {version = mkversion(2, 5, 3), func = upgrade_to_2_5_3, auto = true},
     }
 
     for _, handler in ipairs(handlers) do
diff --git a/test/box-py/bootstrap.result b/test/box-py/bootstrap.result
index 0876e77a6..9ebccc56d 100644
--- a/test/box-py/bootstrap.result
+++ b/test/box-py/bootstrap.result
@@ -4,7 +4,7 @@ box.internal.bootstrap()
 box.space._schema:select{}
 ---
 - - ['max_id', 511]
-  - ['version', 2, 3, 1]
+  - ['version', 2, 5, 3]
 ...
 box.space._cluster:select{}
 ---
@@ -167,7 +167,7 @@ box.space._user:select{}
 ...
 for _, v in box.space._func:pairs{} do r = {} table.insert(r, v:update({{"=", 18, ""}, {"=", 19, ""}})) return r end
 ---
-- - [1, 1, 'box.schema.user.info', 1, 'LUA', '', 'function', [], 'any', 'none', 'none',
+- - [1, 1, 'box.schema.user.info', 0, 'LUA', '', 'function', [], 'any', 'none', 'none',
     false, false, true, ['LUA'], {}, '', '', '']
 ...
 box.space._priv:select{}
diff --git a/test/box/access.result b/test/box/access.result
index 20b1b8b35..27e636122 100644
--- a/test/box/access.result
+++ b/test/box/access.result
@@ -2141,3 +2141,39 @@ box.schema.user.revoke('guest', 'read,write,execute', 'space', 'not_universe')
 sp:drop()
 ---
 ...
+--
+-- Make sure that the functions "LUA" and "box.schema.user.info" do not have
+-- excess rights.
+--
+_ = box.schema.func.call("LUA", "return 1")
+---
+...
+_ = box.schema.func.call("LUA", "return box.space._space:count()")
+---
+...
+_ = box.schema.func.call("box.schema.user.info", 0)
+---
+...
+_ = box.schema.func.call("box.schema.user.info", 1)
+---
+...
+session.su('guest')
+---
+...
+_ = box.schema.func.call("LUA", "return 1")
+---
+...
+_ = box.schema.func.call("LUA", "return box.space._space:count()")
+---
+- error: Read access to space '_space' is denied for user 'guest'
+...
+_ = box.schema.func.call("box.schema.user.info", 0)
+---
+...
+_ = box.schema.func.call("box.schema.user.info", 1)
+---
+- error: User '1' is not found
+...
+session.su('admin')
+---
+...
diff --git a/test/box/access.test.lua b/test/box/access.test.lua
index 3e083a383..a62f87ad8 100644
--- a/test/box/access.test.lua
+++ b/test/box/access.test.lua
@@ -824,3 +824,18 @@ box.schema.user.grant('guest', 'read,write,execute', 'space', 'not_universe')
 box.schema.user.revoke('guest', 'read,write,execute', 'universe')
 box.schema.user.revoke('guest', 'read,write,execute', 'space', 'not_universe')
 sp:drop()
+
+--
+-- Make sure that the functions "LUA" and "box.schema.user.info" do not have
+-- excess rights.
+--
+_ = box.schema.func.call("LUA", "return 1")
+_ = box.schema.func.call("LUA", "return box.space._space:count()")
+_ = box.schema.func.call("box.schema.user.info", 0)
+_ = box.schema.func.call("box.schema.user.info", 1)
+session.su('guest')
+_ = box.schema.func.call("LUA", "return 1")
+_ = box.schema.func.call("LUA", "return box.space._space:count()")
+_ = box.schema.func.call("box.schema.user.info", 0)
+_ = box.schema.func.call("box.schema.user.info", 1)
+session.su('admin')
-- 
2.25.1

^ permalink raw reply	[flat|nested] 15+ messages in thread
* [Tarantool-patches] [PATCH v1 1/1] box: remove unnecessary rights from peristent functions
@ 2020-12-21 10:51 imeevma
  2020-12-21 19:23 ` Sergey Ostanevich
  2020-12-23 12:58 ` Kirill Yukhin
  0 siblings, 2 replies; 15+ messages in thread
From: imeevma @ 2020-12-21 10:51 UTC (permalink / raw)
  To: s.ostanevich; +Cc: tarantool-patches

After this patch, the persistent functions "box.schema.user.info" and
"LUA" will have the same rights as the user who executed them.

The problem was that setuid was unnecessarily set. Because of this,
these functions had the same rights as the user who created them.
However, they must have the same rights as the user who used them.

Fixes tarantool/security#1
---
https://github.com/tarantool/security/issues/1
https://github.com/tarantool/tarantool/tree/imeevma/gh-security-1-lua-function-access

@ChangeLog
 - The functions "LUA" and "box.schema.user.info" now have the
   same rights as the user who executes them. (gh-security-1).


 src/box/bootstrap.snap       | Bin 5976 -> 5991 bytes
 src/box/lua/upgrade.lua      |  30 +++++++++++++++++++++++++++++
 test/box-py/bootstrap.result |   4 ++--
 test/box/access.result       |  36 +++++++++++++++++++++++++++++++++++
 test/box/access.test.lua     |  15 +++++++++++++++
 5 files changed, 83 insertions(+), 2 deletions(-)

diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap
index 8bd4f7ce24216a8bcced6aa97c20c83eb7a02c77..9e7a4e3291accfc387b315f85be9b01f443048fd 100644

diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
index a86a0d410..09cd015f0 100644
--- a/src/box/lua/upgrade.lua
+++ b/src/box/lua/upgrade.lua
@@ -971,6 +971,35 @@ local function upgrade_to_2_3_1()
     create_session_settings_space()
 end
 
+--------------------------------------------------------------------------------
+-- Tarantool 2.7.1
+--------------------------------------------------------------------------------
+local function backport_upgrade_2_7_1_function_access()
+    local _func = box.space._func
+    local _priv = box.space._priv
+    local datetime = os.date("%Y-%m-%d %H:%M:%S")
+    local funcs_to_change = {'LUA', 'box.schema.user.info'}
+    for _, name in pairs(funcs_to_change) do
+        local func = _func.index['name']:get(name)
+        if func ~= nil and func.setuid ~= 0 then
+            local id = func.id
+            log.info('remove old function "'..name..'"')
+            _priv:delete({2, 'function', id})
+            _func:delete({id})
+            log.info('create function "'..name..'" with unset setuid')
+            local new_func = func:update({{'=', 4, 0}, {'=', 18, datetime},
+                                          {'=', 19, datetime}})
+            _func:replace(new_func)
+            log.info('grant execute on function "'..name..'" to public')
+            _priv:replace{ADMIN, PUBLIC, 'function', id, box.priv.X}
+        end
+    end
+end
+
+local function upgrade_to_2_7_1()
+    backport_upgrade_2_7_1_function_access()
+end
+
 --------------------------------------------------------------------------------
 
 local handlers = {
@@ -985,6 +1014,7 @@ local handlers = {
     {version = mkversion(2, 2, 1), func = upgrade_to_2_2_1, auto = true},
     {version = mkversion(2, 3, 0), func = upgrade_to_2_3_0, auto = true},
     {version = mkversion(2, 3, 1), func = upgrade_to_2_3_1, auto = true},
+    {version = mkversion(2, 7, 1), func = upgrade_to_2_7_1, auto = true},
 }
 
 -- Schema version of the snapshot.
diff --git a/test/box-py/bootstrap.result b/test/box-py/bootstrap.result
index 0876e77a6..ed7accea3 100644
--- a/test/box-py/bootstrap.result
+++ b/test/box-py/bootstrap.result
@@ -4,7 +4,7 @@ box.internal.bootstrap()
 box.space._schema:select{}
 ---
 - - ['max_id', 511]
-  - ['version', 2, 3, 1]
+  - ['version', 2, 7, 1]
 ...
 box.space._cluster:select{}
 ---
@@ -167,7 +167,7 @@ box.space._user:select{}
 ...
 for _, v in box.space._func:pairs{} do r = {} table.insert(r, v:update({{"=", 18, ""}, {"=", 19, ""}})) return r end
 ---
-- - [1, 1, 'box.schema.user.info', 1, 'LUA', '', 'function', [], 'any', 'none', 'none',
+- - [1, 1, 'box.schema.user.info', 0, 'LUA', '', 'function', [], 'any', 'none', 'none',
     false, false, true, ['LUA'], {}, '', '', '']
 ...
 box.space._priv:select{}
diff --git a/test/box/access.result b/test/box/access.result
index 20b1b8b35..27e636122 100644
--- a/test/box/access.result
+++ b/test/box/access.result
@@ -2141,3 +2141,39 @@ box.schema.user.revoke('guest', 'read,write,execute', 'space', 'not_universe')
 sp:drop()
 ---
 ...
+--
+-- Make sure that the functions "LUA" and "box.schema.user.info" do not have
+-- excess rights.
+--
+_ = box.schema.func.call("LUA", "return 1")
+---
+...
+_ = box.schema.func.call("LUA", "return box.space._space:count()")
+---
+...
+_ = box.schema.func.call("box.schema.user.info", 0)
+---
+...
+_ = box.schema.func.call("box.schema.user.info", 1)
+---
+...
+session.su('guest')
+---
+...
+_ = box.schema.func.call("LUA", "return 1")
+---
+...
+_ = box.schema.func.call("LUA", "return box.space._space:count()")
+---
+- error: Read access to space '_space' is denied for user 'guest'
+...
+_ = box.schema.func.call("box.schema.user.info", 0)
+---
+...
+_ = box.schema.func.call("box.schema.user.info", 1)
+---
+- error: User '1' is not found
+...
+session.su('admin')
+---
+...
diff --git a/test/box/access.test.lua b/test/box/access.test.lua
index 3e083a383..a62f87ad8 100644
--- a/test/box/access.test.lua
+++ b/test/box/access.test.lua
@@ -824,3 +824,18 @@ box.schema.user.grant('guest', 'read,write,execute', 'space', 'not_universe')
 box.schema.user.revoke('guest', 'read,write,execute', 'universe')
 box.schema.user.revoke('guest', 'read,write,execute', 'space', 'not_universe')
 sp:drop()
+
+--
+-- Make sure that the functions "LUA" and "box.schema.user.info" do not have
+-- excess rights.
+--
+_ = box.schema.func.call("LUA", "return 1")
+_ = box.schema.func.call("LUA", "return box.space._space:count()")
+_ = box.schema.func.call("box.schema.user.info", 0)
+_ = box.schema.func.call("box.schema.user.info", 1)
+session.su('guest')
+_ = box.schema.func.call("LUA", "return 1")
+_ = box.schema.func.call("LUA", "return box.space._space:count()")
+_ = box.schema.func.call("box.schema.user.info", 0)
+_ = box.schema.func.call("box.schema.user.info", 1)
+session.su('admin')
-- 
2.25.1

^ permalink raw reply	[flat|nested] 15+ messages in thread
* [Tarantool-patches] [PATCH v1 1/1] box: remove unnecessary rights from peristent functions
@ 2020-11-03  0:03 imeevma
  2020-11-11 21:48 ` Vladislav Shpilevoy
  0 siblings, 1 reply; 15+ messages in thread
From: imeevma @ 2020-11-03  0:03 UTC (permalink / raw)
  To: v.shpilevoy; +Cc: tarantool-patches

After this patch, the persistent functions "box.schema.user.info" and
"LUA" will have the same rights as the user who executed them.

Fixes tarantool/security#1
---
https://github.com/tarantool/security/issues/1
https://github.com/tarantool/tarantool/tree/imeevma/gh-security-1-lua-function-access

 src/box/bootstrap.snap       | Bin 5976 -> 5990 bytes
 src/box/lua/upgrade.lua      |  35 ++++++++++++++++++++++++++++++++++
 test/box-py/bootstrap.result |   4 ++--
 test/box/access.result       |  36 +++++++++++++++++++++++++++++++++++
 test/box/access.test.lua     |  15 +++++++++++++++
 5 files changed, 88 insertions(+), 2 deletions(-)

diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap
index 8bd4f7ce24216a8bcced6aa97c20c83eb7a02c77..cfab8dc811e91c924108a003e325b22f53ef0ab4 100644
GIT binary patch
delta 5887
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
VoX5$|9>OH$w>10)&<E8Jt?kl7LO%ci

delta 5855
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

diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
index add791cd7..671e441ca 100644
--- a/src/box/lua/upgrade.lua
+++ b/src/box/lua/upgrade.lua
@@ -971,6 +971,40 @@ local function upgrade_to_2_3_1()
     create_session_settings_space()
 end
 
+--------------------------------------------------------------------------------
+-- Tarantool 2.7.1
+--------------------------------------------------------------------------------
+
+local function upgrade_to_2_7_1()
+    local _func = box.space[box.schema.FUNC_ID]
+    local _priv = box.space[box.schema.PRIV_ID]
+
+    local datetime = os.date("%Y-%m-%d %H:%M:%S")
+
+    -- Re-create "box.schema.user.info" function.
+    log.info('remove old function "box.schema.user.info"')
+    _priv:delete({2, 'function', 1})
+    _func:delete({1})
+    log.info('create function "box.schema.user.info" with setuid')
+    _func:replace({1, ADMIN, 'box.schema.user.info', 0, 'LUA', '', 'function',
+                  {}, 'any', 'none', 'none', false, false, true, {'LUA'},
+                  setmap({}), '', datetime, datetime})
+    log.info('grant execute on function "box.schema.user.info" to public')
+    _priv:replace{ADMIN, PUBLIC, 'function', 1, box.priv.X}
+
+    -- Re-create "LUA" function.
+    log.info('remove old function "LUA"')
+    _priv:delete({2, 'function', 65})
+    _func:delete({65})
+    log.info('create function "LUA"')
+    _func:replace({65, ADMIN, 'LUA', 0, 'LUA',
+                   'function(code) return assert(loadstring(code))() end',
+                   'function', {'string'}, 'any', 'none', 'none', false, false,
+                   true, {'LUA', 'SQL'}, setmap({}), '', datetime, datetime})
+    log.info('grant execute on function "LUA" to public')
+    _priv:replace{ADMIN, PUBLIC, 'function', 65, box.priv.X}
+end
+
 --------------------------------------------------------------------------------
 
 local function get_version()
@@ -1007,6 +1041,7 @@ local function upgrade(options)
         {version = mkversion(2, 2, 1), func = upgrade_to_2_2_1, auto = true},
         {version = mkversion(2, 3, 0), func = upgrade_to_2_3_0, auto = true},
         {version = mkversion(2, 3, 1), func = upgrade_to_2_3_1, auto = true},
+        {version = mkversion(2, 7, 1), func = upgrade_to_2_7_1, auto = true},
     }
 
     for _, handler in ipairs(handlers) do
diff --git a/test/box-py/bootstrap.result b/test/box-py/bootstrap.result
index 0876e77a6..ed7accea3 100644
--- a/test/box-py/bootstrap.result
+++ b/test/box-py/bootstrap.result
@@ -4,7 +4,7 @@ box.internal.bootstrap()
 box.space._schema:select{}
 ---
 - - ['max_id', 511]
-  - ['version', 2, 3, 1]
+  - ['version', 2, 7, 1]
 ...
 box.space._cluster:select{}
 ---
@@ -167,7 +167,7 @@ box.space._user:select{}
 ...
 for _, v in box.space._func:pairs{} do r = {} table.insert(r, v:update({{"=", 18, ""}, {"=", 19, ""}})) return r end
 ---
-- - [1, 1, 'box.schema.user.info', 1, 'LUA', '', 'function', [], 'any', 'none', 'none',
+- - [1, 1, 'box.schema.user.info', 0, 'LUA', '', 'function', [], 'any', 'none', 'none',
     false, false, true, ['LUA'], {}, '', '', '']
 ...
 box.space._priv:select{}
diff --git a/test/box/access.result b/test/box/access.result
index 20b1b8b35..92d6453d7 100644
--- a/test/box/access.result
+++ b/test/box/access.result
@@ -2141,3 +2141,39 @@ box.schema.user.revoke('guest', 'read,write,execute', 'space', 'not_universe')
 sp:drop()
 ---
 ...
+--
+-- Make sure that the functions "LUA" and "box.schema.user.info" do not have
+-- excess rights.
+--
+_ = box.schema.func.call("LUA", "return 1")
+---
+...
+_ = box.schema.func.call("LUA", "return box.space._space:get(257)")
+---
+...
+_ = box.schema.func.call("box.schema.user.info", 0)
+---
+...
+_ = box.schema.func.call("box.schema.user.info", 1)
+---
+...
+session.su('guest')
+---
+...
+_ = box.schema.func.call("LUA", "return 1")
+---
+...
+_ = box.schema.func.call("LUA", "return box.space._space:get(257)")
+---
+- error: Read access to space '_space' is denied for user 'guest'
+...
+_ = box.schema.func.call("box.schema.user.info", 0)
+---
+...
+_ = box.schema.func.call("box.schema.user.info", 1)
+---
+- error: User '1' is not found
+...
+session.su('admin')
+---
+...
diff --git a/test/box/access.test.lua b/test/box/access.test.lua
index 3e083a383..89fb34fe6 100644
--- a/test/box/access.test.lua
+++ b/test/box/access.test.lua
@@ -824,3 +824,18 @@ box.schema.user.grant('guest', 'read,write,execute', 'space', 'not_universe')
 box.schema.user.revoke('guest', 'read,write,execute', 'universe')
 box.schema.user.revoke('guest', 'read,write,execute', 'space', 'not_universe')
 sp:drop()
+
+--
+-- Make sure that the functions "LUA" and "box.schema.user.info" do not have
+-- excess rights.
+--
+_ = box.schema.func.call("LUA", "return 1")
+_ = box.schema.func.call("LUA", "return box.space._space:get(257)")
+_ = box.schema.func.call("box.schema.user.info", 0)
+_ = box.schema.func.call("box.schema.user.info", 1)
+session.su('guest')
+_ = box.schema.func.call("LUA", "return 1")
+_ = box.schema.func.call("LUA", "return box.space._space:get(257)")
+_ = box.schema.func.call("box.schema.user.info", 0)
+_ = box.schema.func.call("box.schema.user.info", 1)
+session.su('admin')
-- 
2.25.1

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2020-12-23 12:58 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-21 11:38 [Tarantool-patches] [PATCH v1 1/1] box: remove unnecessary rights from peristent functions imeevma
2020-12-21 19:14 ` Sergey Ostanevich
  -- strict thread matches above, loose matches on Subject: below --
2020-12-22  7:56 imeevma
2020-12-22  8:13 ` Sergey Ostanevich
2020-12-21 10:51 imeevma
2020-12-21 19:23 ` Sergey Ostanevich
2020-12-22 17:44   ` Mergen Imeev
2020-12-23  9:44     ` Sergey Ostanevich
2020-12-23 12:58 ` Kirill Yukhin
2020-11-03  0:03 imeevma
2020-11-11 21:48 ` Vladislav Shpilevoy
2020-12-03  9:54   ` Mergen Imeev
2020-12-04 23:10     ` Vladislav Shpilevoy
2020-12-16 20:23       ` Mergen Imeev
2020-12-20 14:05         ` Vladislav Shpilevoy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox