// vim:foldmethod=marker:syntax=lpc:noexpandtab // $Id: sandbox.c,v 1.16 2008/02/06 12:16:16 lynx Exp $ #include #include #if __EFUN_DEFINED__(shutdown) MASK(shutdown, "SHUTDOWN") #endif #if __EFUN_DEFINED__(add_action) MASK(add_action, "ADD_ACTION") #endif #if __EFUN_DEFINED__(break_point) MASK(break_point, "BREAK_POINT") #endif #if __EFUN_DEFINED__(call_direct) MASK(call_direct, "CALL_DIRECT") #endif #if __EFUN_DEFINED__(call_direct_resolved) MASK(call_direct_resolved, "CALL_DIRECT_RESOLVED") #endif #if __EFUN_DEFINED__(call_resolved) MASK(call_resolved, "CALL_RESOLVED") #endif #if __EFUN_DEFINED__(swap) MASK(swap, "SWAP") #endif #if __EFUN_DEFINED__(debug_info) MASK(debug_info, "DEBUG_INFO") #endif #if __EFUN_DEFINED__(command) MASK(command, "COMMAND") #endif #if __EFUN_DEFINED__(command_stack) MASK(command_stack, "COMMAND_STACK") #endif #if __EFUN_DEFINED__(command_stack_depth) MASK(command_stack_depth, "COMMAND_STACK_DEPTH") #endif #if __EFUN_DEFINED__(convert_charset) MASK(convert_charset, "CONVERT_CHARSET") #endif #if __EFUN_DEFINED__(debug_message) && DEBUG < 1 MASK(debug_message, "DEBUG_MESSAGE") #endif #if __EFUN_DEFINED__(disable_commands) MASK(disable_commands, "DISABLE_COMMANDS") #endif #if __EFUN_DEFINED__(enable_commands) MASK(enable_commands, "ENABLE_COMMANDS") #endif #if __EFUN_DEFINED__(function_exists) MASK(function_exists, "FUNCTION_EXISTS") #endif #if __EFUN_DEFINED__(functionlist) MASK(functionlist, "FUNCTIONLIST") #endif #if __EFUN_DEFINED__(garbage_collection) MASK(garbage_collection, "GARBAGECOLLECTION") #endif #if __EFUN_DEFINED__(get_dir) // caught by valid_read? MASK(get_dir, "GETDIR") #endif #if __EFUN_DEFINED__(include_list) MASK(include_list, "INCLUDE_LIST") #endif #if __EFUN_DEFINED__(inherit_list) MASK(inherit_list, "INHERIT_LIST") #endif #if __EFUN_DEFINED__(input_to_info) MASK(input_to_info, "INPUT_TO_INFO") #endif #if __EFUN_DEFINED__(last_instructions) MASK(last_instructions, "LAST_INSTRUCTIONS") #endif #if __EFUN_DEFINED__(load_object) MASK(load_object, "LOAD_OBJECT") #endif #if __EFUN_DEFINED__(mkdir) // caught by valid_write? MASK(mkdir, "MKDIR") #endif #if __EFUN_DEFINED__(move_object) MASK(move_object, "MOVE_OBJECT") #endif #if __EFUN_DEFINED__(object_info) MASK(object_info, "OBJECT_INFO") #endif #if __EFUN_DEFINED__(process_string) MASK(process_string, "PROCESS_STRING") #endif #if __EFUN_DEFINED__(query_ip_name) MASK(query_ip_name, "QUERY_IP_NAME") #endif #if __EFUN_DEFINED__(query_ip_number) MASK(query_ip_number, "QUERY_IP_NUMBER") #endif #if __EFUN_DEFINED__(query_load_average) MASK(query_load_average, "QUERY_LOAD_AVERAGE") #endif #if __EFUN_DEFINED__(query_shadowing) MASK(query_shadowing, "QUERY_SHADOWING") #endif #if __EFUN_DEFINED__(remove_action) MASK(remove_action, "REMOVE_ACTION") #endif #if __EFUN_DEFINED__(remove_input_to) MASK(remove_input_to, "REMOVE_INPUT_TO") #endif #if __EFUN_DEFINED__(remove_interactive) MASK(remove_interactive, "REMOVE_INTERACTIVE") #endif #if __EFUN_DEFINED__(rename) // seems to be caught by valid_(read|write), but // i have to check that MASK(rename, "RENAME") #endif #if __EFUN_DEFINED__(rm) // caught by valid_write? MASK(rm, "RM") #endif #if __EFUN_DEFINED__(rmdir) // caught by valid_write? MASK(rmdir, "RMDIR") #endif #if __EFUN_DEFINED__(rusage) MASK(rusage, "RUSAGE") #endif #if __EFUN_DEFINED__(say) MASK(say, "SAY") #endif #if __EFUN_DEFINED__(set_environment) MASK(set_environment, "SET_ENVIRONMENT") #endif #if __EFUN_DEFINED__(set_is_wizard) MASK(set_is_wizard, "SET_IS_WIZARD") #endif #if __EFUN_DEFINED__(set_modify_command) MASK(set_modify_command, "SET_MODIFY_COMMAND") #endif #if __EFUN_DEFINED__(set_prompt) MASK(set_prompt, "SET_PROMPT") #endif #if __EFUN_DEFINED__(swap) MASK(swap, "SWAP") #endif #if __EFUN_DEFINED__(tell_object) MASK(tell_object, "TELL_OBJECT") #endif #if __EFUN_DEFINED__(tell_room) MASK(tell_room, "TELL_ROOM") #endif #if __EFUN_DEFINED__(tls_deinit_connection) MASK(tls_deinit_connection, "TLS_DEINIT_CONNECTION") #endif #if __EFUN_DEFINED__(tls_init_connection) MASK(tls_init_connection, "TLS_INIT_CONNECTION") #endif #if __EFUN_DEFINED__(tls_query_connection_info) MASK(tls_query_connection_info, "TLS_QUERY_CONNECTION_INFO") #endif #if __EFUN_DEFINED__(tls_query_connection_state) MASK(tls_query_connection_state, "TLS_QUERY_CONNECTION_STATE") #endif #if __EFUN_DEFINED__(transfer) //MASK(transfer, "TRANSFER") // avoid deprecation warning, and as nobody uses it: nomask void transfer() {} #endif #if __EFUN_DEFINED__(unshadow) MASK(unshadow, "UNSHADOW") #endif #if __EFUN_DEFINED__(users) MASK(users, "USERS") #endif #if __EFUN_DEFINED__(variable_list) MASK(variable_list, "VARIABLE_LIST") #endif #if __EFUN_DEFINED__(wizlist_info) MASK(wizlist_info, "WIZLIST_INFO") #endif nomask mixed call_other(mixed obj, string func, varargs mixed * b) { P4(("call_other(%O, %O, %O)\n", obj, func, b)); if (extern_call()) { mixed euid = geteuid(previous_object()); if (stringp(euid) && euid[0] != '/') { string s; object o; s = stringp(obj) ? obj : object_name(obj); o = objectp(obj) ? obj : find_object(obj); #ifndef __COMPAT_MODE__ if (s[0] == '/') s = s[1..]; #endif if (euid[0] == '@') { // yes, this check is overdone, but only for // now. i think we can use exactly this // structure for allowed call_others into // daemons. says tobij. // yes, checking the identity of daemons is much // easier. some of them we already have stored in library // vars, others have static object names, so they can be put // into a switch or mapping. it's only doing so for "any" // user object, which gets too messy. it is architecturally // much cleaner to let sendmsg() decide which msg is legal // (which it has to do anyway) so we don't need yet another // security scheme in here with this wild guessing for user // objects. in fact two redundant security approaches also // unnecessarily raise the chances of being flawed. so let's // allow as few -> operations as possible for sandbox mode. // // great idea. library_object()->sendmsg() _is_ // a -> operation, in case you forgot we're doing that (in // entity.c). // // if we rename the library-sendmsg to something reachable // without a call_other, anyways, sendmsg() will be an extra // function call if it just redirects the msg to // target->msg(). // in fact, it might be same complexity as calling target->msg // directly, as call_other is overloaded and an lfun doing // ... virtually the same thing for the discussed type of // call. // // the if here isn't even more complex than in sendmsg, it'll // be of equal or less complexity, plus, normal objects // (and, if we want to allow ->qName for everybody anywhere, // normal call_others (well, qName) // don't run deep into this check. // // so, after all, the only argument is "evil! code! // 'duplication'!", which i prefer over (for users who do // their own rooms etc) incomprehensible // #ifndef SADNBOX // target->msg // #else // sendmsg(target, ...) // #endif // mess in files. // // additionally, my sandbox-philosophy is to keep things // maximum equal in usage with and without SANDBOX, // so i'll do it this way. unless (o == previous_object() || o == ME) { int i; if ((i = index(s, '#')) == -1) i = strlen(s) - 1; unless (func == "qName") switch (s[..i]) { case "net/person#": case "net/user#": //if (func == "qName" || func == "msg") break; if (func == "msg") break; default: if (sscanf(s[..i], "net/%~s/user#") && func == "msg") break; raise_error(sprintf("INVALID %O " "call_other(%O, %O, %O)\n", euid, obj, func, b)); } } } else { raise_error(sprintf("INVALID %O call_other(%O, %O, %O)\n", euid, obj, func, b)); } } else unless (euid) { raise_error(sprintf("INVALID euid-0 (%O) call_other(%O, %O, %O)\n", previous_object(), obj, func, b)); } set_this_object(previous_object()); } return efun::call_other(obj, func, b...); }