diff --git a/CMakeLists.txt b/CMakeLists.txt index fd8dcac63c3157995fff317ab21ac1cfd1679a50..357518f3c27a990026b9b55132e9a4b021125115 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,8 @@ if (NOT READLINE_FOUND) message(FATAL_ERROR "readline library not found.") endif() +option(ENABLE_VALGRIND "Enable integration with valgrind, a memory analyzing tool" OFF) + check_symbol_exists(MAP_ANON sys/mman.h HAVE_MAP_ANON) check_symbol_exists(MAP_ANONYMOUS sys/mman.h HAVE_MAP_ANONYMOUS) check_include_file(sys/time.h HAVE_SYS_TIME_H) @@ -376,6 +378,7 @@ message (STATUS "ENABLE_SSE2: ${ENABLE_SSE2}") message (STATUS "ENABLE_AVX: ${ENABLE_AVX}") message (STATUS "ENABLE_GCOV: ${ENABLE_GCOV}") message (STATUS "ENABLE_GPROF: ${ENABLE_GPROF}") +message (STATUS "ENABLE_VALGRIND: ${ENABLE_VALGRIND}") message (STATUS "ENABLE_TRACE: ${ENABLE_TRACE}") message (STATUS "ENABLE_BACKTRACE: ${ENABLE_BACKTRACE} (symbol resolve: ${HAVE_BFD})") message (STATUS "ENABLE_BUNDLED_LUAJIT: ${ENABLE_BUNDLED_LUAJIT}") diff --git a/cmake/luajit.cmake b/cmake/luajit.cmake index 9a19ec76b6dc3199e4c9b20c3b6ae21685392a10..af55e0a9a11deb3b02672b1d1dfddf6e28fa8df2 100644 --- a/cmake/luajit.cmake +++ b/cmake/luajit.cmake @@ -131,14 +131,19 @@ message (STATUS "Use LuaJIT library: ${LUAJIT_LIB}") macro(luajit_build) set (luajit_buildoptions BUILDMODE=static) set (luajit_copt "") + set (luajit_xcflags "") if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") set (luajit_buildoptions ${luajit_buildoptions} CCDEBUG=${CC_DEBUG_OPT}) set (luajit_copt ${luajit_copt} -O1) - set (luajit_buildoptions ${luajit_buildoptions} XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT') + set (luajit_xcflags ${luajit_xcflags} + -DLUA_USE_APICHECK -DLUA_USE_ASSERT) else () set (luajit_copt ${luajit_copt} -O2) endif() - set (luajit_copt ${luajit_copt} -I${PROJECT_SOURCE_DIR}/libobjc) + if (ENABLE_VALGRIND) + set (luajit_xcflags ${luajit_xcflags} + -DLUAJIT_USE_VALGRIND -DLUAJIT_USE_SYSMALLOC) + endif() set (luajit_target_cc "${CMAKE_C_COMPILER} ${CMAKE_C_FLAGS}") # Use external unwind on all platforms. set (luajit_target_cc "${luajit_target_cc} -DLUAJIT_UNWIND_EXTERNAL=1") @@ -164,6 +169,7 @@ macro(luajit_build) set (luajit_buildoptions ${luajit_buildoptions} CFLAGS="" CXXFLAGS="" + XCFLAGS="${luajit_xcflags}" HOST_CC="${luajit_host_cc}" TARGET_CC="${luajit_target_cc}" CCOPT="${luajit_copt}") diff --git a/cmake/profile.cmake b/cmake/profile.cmake index 94de41788032ed753dca12938923e20a738f11b9..c86a89d3d88f56ca219c2251eb03b33f10cc8a29 100644 --- a/cmake/profile.cmake +++ b/cmake/profile.cmake @@ -32,3 +32,12 @@ option(ENABLE_GPROF "Enable integration with gprof, a performance analyzing tool if (ENABLE_GPROF) add_compile_flags("C;CXX" "-pg") endif() + +option(ENABLE_VALGRIND "Enable integration with valgrind, a memory analyzing tool" OFF) +if (ENABLE_VALGRIND) + check_include_file(valgrind/valgrind.h HAVE_VALGRIND_VALGRIND_H) + if (NOT HAVE_VALGRIND_VALGRIND_H) + message (FATAL_ERROR + "ENABLE_VALGRIND option is set but valgrind/valgrind.h is not found") + endif() +endif() diff --git a/src/box/access.h b/src/box/access.h index 94c5b1d4851f4bd1717f50c5439e64d62261892e..b0cbf366c4cafdc94261813ff62523d5fc363e50 100644 --- a/src/box/access.h +++ b/src/box/access.h @@ -41,6 +41,8 @@ enum { PRIV_W = 2, /* CALL */ PRIV_X = 4, + /** Everything. */ + PRIV_ALL = PRIV_R + PRIV_W + PRIV_X }; /* Privilege name for error messages */ diff --git a/src/box/alter.cc b/src/box/alter.cc index 9201c9c32d337e09f181154433c742b502bc776e..858d14806e18434a95e91f206055f9888f3e5516 100644 --- a/src/box/alter.cc +++ b/src/box/alter.cc @@ -62,6 +62,9 @@ #define PRIV_OBJECT_ID 3 #define PRIV_ACCESS 4 +/** _func columns */ +#define FUNC_SETUID 3 + /* {{{ Auxiliary functions and methods. */ void @@ -1256,7 +1259,7 @@ static struct trigger drop_user_trigger = { rlist_nil, user_cache_remove_user, NULL, NULL }; static void -user_cache_replace_user(struct trigger * /* trigger */, void *event) +user_cache_alter_user(struct trigger * /* trigger */, void *event) { struct txn *txn = (struct txn *) event; struct txn_stmt *stmt = txn_stmt(txn); @@ -1266,7 +1269,7 @@ user_cache_replace_user(struct trigger * /* trigger */, void *event) } static struct trigger modify_user_trigger = - { rlist_nil, user_cache_replace_user, NULL, NULL }; + { rlist_nil, user_cache_alter_user, NULL, NULL }; /** * A trigger invoked on replace in the user table. @@ -1324,6 +1327,8 @@ func_def_create_from_tuple(struct func_def *func, struct tuple *tuple) { func->fid = tuple_field_u32(tuple, ID); func->uid = tuple_field_u32(tuple, UID); + func->auth_token = user_cache_find(func->uid)->auth_token; + func->setuid = false; const char *name = tuple_field_cstr(tuple, NAME); uint32_t len = strlen(name); if (len >= sizeof(func->name)) { @@ -1333,6 +1338,8 @@ func_def_create_from_tuple(struct func_def *func, struct tuple *tuple) snprintf(func->name, sizeof(func->name), "%s", name); /** Nobody has access to the function but the owner. */ memset(func->access, 0, sizeof(func->access)); + if (tuple_field_count(tuple) > FUNC_SETUID) + func->setuid = tuple_field_u32(tuple, FUNC_SETUID); } /** Remove a function from function cache */ diff --git a/src/box/key_def.h b/src/box/key_def.h index 152e4a08b079cd4643b58c9ae83f175e837cbe21..63e7251357d554a31174d1ddac1f06980d7d7769 100644 --- a/src/box/key_def.h +++ b/src/box/key_def.h @@ -301,6 +301,16 @@ struct func_def { uint32_t fid; /** Owner of the function. */ uint32_t uid; + /** + * True if the function requires change of user id before + * invocaction. + */ + bool setuid; + /** + * Authentication id of the owner of the function, + * used for set-user-id functions. + */ + uint8_t auth_token; /** Function name. */ char name[BOX_NAME_MAX + 1]; /** diff --git a/src/box/lua/call.cc b/src/box/lua/call.cc index 4a394417a3afd9734ca1b8db70a208784a520f18..8fc9b24539fdc0487bfd979532084ab8ec3d06e8 100644 --- a/src/box/lua/call.cc +++ b/src/box/lua/call.cc @@ -459,26 +459,74 @@ lbox_call_loadproc(struct lua_State *L) return box_lua_find(L, name, name + name_len); } -static inline void -access_check_func(const char *name, uint32_t name_len, - struct user *user, uint8_t access) +/** + * Check access to a function and change the current + * user id if the function is a set-definer-user-id one. + * The original user is restored in the destructor. + */ +struct SetuidGuard { - access &= ~user->universal_access.effective; - /* - * No special check for ADMIN user is necessary - * since ADMIN has universal access. - */ - if (access == 0) + /** True if the function was set-user-id one. */ + bool setuid; + /** Original authentication token, only set if setuid = true. */ + uint8_t orig_auth_token; + /** Original user id, only set if setuid = true. */ + uint32_t orig_uid; + + inline SetuidGuard(const char *name, uint32_t name_len, + struct user *user, uint8_t access); + inline ~SetuidGuard(); +}; + +SetuidGuard::SetuidGuard(const char *name, uint32_t name_len, + struct user *user, uint8_t access) + :setuid(false) +{ + /* + * If the user has universal access, don't bother with setuid. + * No special check for ADMIN user is necessary + * since ADMIN has universal access. + */ + if (user->universal_access.effective & PRIV_ALL) return; + access &= ~user->universal_access.effective; + /* + * We need to look up the function by name even if + * the user has access to it, since it could require + * a set of other user id. + */ struct func_def *func = func_by_name(name, name_len); + if (func == NULL && access == 0) { + /** + * Well, the function is not explicitly defined, + * so it's obviously not a setuid one. Wasted + * cycles on look up while the user had universal + * access :( + */ + return; + } if (func == NULL || (func->uid != user->uid && - access & ~func->access[user->auth_token].effective)) { + access & ~func->access[user->auth_token].effective)) { + /* Access violation, report error. */ char name_buf[BOX_NAME_MAX + 1]; snprintf(name_buf, sizeof(name_buf), "%.*s", name_len, name); tnt_raise(ClientError, ER_FUNCTION_ACCESS_DENIED, priv_name(access), user->name, name_buf); } + if (func->setuid) { + /** Remember and change the current user id. */ + setuid = func->setuid; + orig_auth_token = user->auth_token; + orig_uid = user->uid; + session_set_user(session(), func->auth_token, func->uid); + } +} + +SetuidGuard::~SetuidGuard() +{ + if (setuid) + session_set_user(session(), orig_auth_token, orig_uid); } /** @@ -497,12 +545,13 @@ box_lua_call(struct request *request, struct port *port) /* Try to find a function by name */ int oc = box_lua_find(L, name, name + name_len); /** - * Check access to the function. Sic: the order + * Check access to the function and optionally change + * execution time user id (set user id). Sic: the order * is important, as is described in * https://github.com/tarantool/tarantool/issues/300 * - if a function does not exist, say it first. */ - access_check_func(name, name_len, user, PRIV_X); + SetuidGuard setuid(name, name_len, user, PRIV_X); /* Push the rest of args (a tuple). */ const char *args = request->tuple; uint32_t arg_count = mp_decode_array(&args); diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index c163e470ad0101e6c8d35411c4d3e0d7dc5991ed..bb1e9a46f2f4a0d167c9a0b39baad5a224a7494b 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -911,13 +911,16 @@ local function object_name(object_type, object_id) end box.schema.func = {} -box.schema.func.create = function(name) +box.schema.func.create = function(name, opts) local _func = box.space[box.schema.FUNC_ID] local func = _func.index.name:get{name} if func then box.error(box.error.FUNCTION_EXISTS, name) end - _func:auto_increment{session.uid(), name} + check_param_table(opts, { setuid = 'boolean' }) + opts = update_param_table(opts, { setuid = false }) + opts.setuid = opts.setuid and 1 or 0 + _func:auto_increment{session.uid(), name, opts.setuid} end box.schema.func.drop = function(name) @@ -926,8 +929,8 @@ box.schema.func.drop = function(name) local fid = object_resolve('function', name) local privs = _priv:select{} for k, tuple in pairs(privs) do - if tuple[2] == 'function' and tuple[3] == function_id then - box.schema.user.revoke(tuple[1], tuple[4], tuple[2], tuple[3]) + if tuple[3] == 'function' and tuple[4] == fid then + box.schema.user.revoke(tuple[2], tuple[5], tuple[3], tuple[4]) end end _func:delete{fid} @@ -1082,6 +1085,10 @@ box.schema.role.drop = function(name) end return box.schema.user.drop(name) end -box.schema.role.grant = box.schema.user.grant -box.schema.role.revoke = box.schema.user.revoke -box.schema.role .info = box.schema.user.info +box.schema.role.grant = function(user_name, role_name, grantor) + return box.schema.user.grant(user_name, 'execute', 'role', role_name, grantor) +end +box.schema.role.revoke = function(user_name, role_name) + return box.schema.user.revoke(user_name, 'execute', 'role', role_name) +end +box.schema.role.info = box.schema.user.info diff --git a/test/box/access.result b/test/box/access.result index faee3a129f2324c892f37b44d95cf085d2e76ca1..b3310bbb6045b1582819abd0d10c082c1afde2f8 100644 --- a/test/box/access.result +++ b/test/box/access.result @@ -389,9 +389,6 @@ box.space._priv:select{id} --- - [] ... -session = nil ---- -... -- ----------------------------------------------------------- -- Be a bit more rigorous in what is accepted in space _user -- ----------------------------------------------------------- @@ -413,3 +410,6 @@ box.space._user:insert{10, 1, 'name', 'role', 'password'} - error: 'Failed to create role ''name'': authentication data can not be set for a role' ... +session = nil +--- +... diff --git a/test/box/access.test.lua b/test/box/access.test.lua index 347d166c0a3c88166b6755fcc480c7f054662121..5358c7d37b2e5a6272a0b86352ce452d6b5d10c7 100644 --- a/test/box/access.test.lua +++ b/test/box/access.test.lua @@ -167,7 +167,6 @@ box.schema.user.grant('user', 'read', 'universe') box.space._priv:select{id} box.schema.user.drop('user') box.space._priv:select{id} -session = nil -- ----------------------------------------------------------- -- Be a bit more rigorous in what is accepted in space _user -- ----------------------------------------------------------- @@ -175,3 +174,4 @@ box.space._user:insert{10, 1, 'name'} box.space._user:insert{10, 1, 'name', 'strange-object-type'} box.space._user:insert{10, 1, 'name', 'user', 'password'} box.space._user:insert{10, 1, 'name', 'role', 'password'} +session = nil diff --git a/test/box/access_bin.result b/test/box/access_bin.result index 1e08ae289fd80d9d12e4f1b2de48722ecfb92b7c..23aa5b2d6f54fc0b81d192f24bec291cd079f5c2 100644 --- a/test/box/access_bin.result +++ b/test/box/access_bin.result @@ -28,3 +28,70 @@ c:close() box.schema.user.revoke('guest', 'read,write,execute', 'universe') --- ... +-- gh-488 suid functions +-- +setuid_space = box.schema.space.create('setuid_space') +--- +... +setuid_space:create_index('primary') +--- +... +setuid_func = function() return box.space.setuid_space:auto_increment{} end +--- +... +box.schema.func.create('setuid_func') +--- +... +box.schema.user.grant('guest', 'execute', 'function', 'setuid_func') +--- +... +c = net.box:new(0, box.cfg.listen) +--- +... +c:call("setuid_func") +--- +- error: Read access denied for user 'guest' to space 'setuid_space' +... +session.su('guest') +--- +... +setuid_func() +--- +- error: Read access denied for user 'guest' to space 'setuid_space' +... +session.su('admin') +--- +... +box.schema.func.drop('setuid_func') +--- +... +box.schema.func.create('setuid_func', { setuid = true }) +--- +... +box.schema.user.grant('guest', 'execute', 'function', 'setuid_func') +--- +... +c:call("setuid_func") +--- +- - [1] +... +session.su('guest') +--- +... +setuid_func() +--- +- error: Read access denied for user 'guest' to space 'setuid_space' +... +session.su('admin') +--- +... +c:close() +--- +... +box.schema.func.drop('setuid_func') +--- +... +setuid_space:drop() +--- +... +-- diff --git a/test/box/access_bin.test.lua b/test/box/access_bin.test.lua index 692f60e332f787afcdaca4445aa05d82e9c98d03..f9841d7ea50220baa1ddb83ccf11d29607c37b0c 100644 --- a/test/box/access_bin.test.lua +++ b/test/box/access_bin.test.lua @@ -10,3 +10,27 @@ c:call("dostring", "session.su('admin')") c:call("dostring", "return session.user()") c:close() box.schema.user.revoke('guest', 'read,write,execute', 'universe') + +-- gh-488 suid functions +-- +setuid_space = box.schema.space.create('setuid_space') +setuid_space:create_index('primary') +setuid_func = function() return box.space.setuid_space:auto_increment{} end +box.schema.func.create('setuid_func') +box.schema.user.grant('guest', 'execute', 'function', 'setuid_func') +c = net.box:new(0, box.cfg.listen) +c:call("setuid_func") +session.su('guest') +setuid_func() +session.su('admin') +box.schema.func.drop('setuid_func') +box.schema.func.create('setuid_func', { setuid = true }) +box.schema.user.grant('guest', 'execute', 'function', 'setuid_func') +c:call("setuid_func") +session.su('guest') +setuid_func() +session.su('admin') +c:close() +box.schema.func.drop('setuid_func') +setuid_space:drop() +-- diff --git a/test/box/role.result b/test/box/role.result index 4ca69d3f8b6ecbebcdf2e4587eaa262c4aaef612..aae49703b603f01a4af9bd9c401979bfd03a52be 100644 --- a/test/box/role.result +++ b/test/box/role.result @@ -23,15 +23,15 @@ box.session.su('iddqd') -- test granting privilege to a role box.schema.role.grant('iddqd', 'execute', 'universe') --- +- error: Role 'execute' is not found ... box.schema.role.info('iddqd') --- -- - - execute - - universe - - +- [] ... box.schema.role.revoke('iddqd', 'execute', 'universe') --- +- error: Role 'execute' is not found ... box.schema.role.info('iddqd') --- @@ -54,3 +54,19 @@ box.schema.user.info('tester') - role - iddqd ... +-- test granting user to a user +box.schema.user.grant('tester', 'execute', 'role', 'tester') +--- +- error: Role 'tester' is not found +... +-- test granting a non-execute grant on a role - error +box.schema.user.grant('tester', 'write', 'role', 'iddqd') +--- +... +box.schema.user.grant('tester', 'read', 'role', 'iddqd') +--- +... +-- test granting role to a role +box.schema.user.grant('iddqd', 'execute', 'role', 'iddqd') +--- +... diff --git a/test/box/role.test.lua b/test/box/role.test.lua index d60b7a6e9a85b2fde4e19737e371e179e0a19f58..9d63d60331337d8dd78be4c1a1d5f84f38023356 100644 --- a/test/box/role.test.lua +++ b/test/box/role.test.lua @@ -15,3 +15,10 @@ box.schema.user.create('tester') box.schema.user.info('tester') box.schema.user.grant('tester', 'execute', 'role', 'iddqd') box.schema.user.info('tester') +-- test granting user to a user +box.schema.user.grant('tester', 'execute', 'role', 'tester') +-- test granting a non-execute grant on a role - error +box.schema.user.grant('tester', 'write', 'role', 'iddqd') +box.schema.user.grant('tester', 'read', 'role', 'iddqd') +-- test granting role to a role +box.schema.user.grant('iddqd', 'execute', 'role', 'iddqd') diff --git a/third_party/luajit b/third_party/luajit index 880ca300e8fb7b432b9d25ed377db2102e4cb63d..41156fe1cdd6b60a5e8d9855c57699e89ccfbf97 160000 --- a/third_party/luajit +++ b/third_party/luajit @@ -1 +1 @@ -Subproject commit 880ca300e8fb7b432b9d25ed377db2102e4cb63d +Subproject commit 41156fe1cdd6b60a5e8d9855c57699e89ccfbf97