// // /bin/adm/master.c -- Vincent's Hollow // This place is like a lot of little servers/daemons in one! :) // // need to add: // request_privileges() // compile_object() #include <wizlev.h> #include <daemons.h> #include <login.h> #include "/bin/adm/parse_com.c" #define KERBEROS #ifndef KERBEROS #undef LOGIN #define LOGIN "/bin/adm/login" #endif // locally used daemons. #define VIRTUAL "/bin/adm/virtual" // some misc files for master.c's use. #define LOAD "/bin/adm/preload" // other "magic" values. #define PROFILE_TIME 900 // profile to log file every x seconds. inherit EFUN_OB; string *netdead; int startup_time, login_count, debug; // // MISC QUERY // string get_bb_uid() { return "backbone"; } string get_root_uid() { return "root"; } int query_login_count() { return login_count; } mixed query_netdead() { if(netdead) return netdead; } int query_startup_time() { return startup_time; } // // SYSTEM STARTUP // // optional flag passed to driver when first starting up // driver "-fcall obj func arg" "-fshutdown" void flag(string str) { string file,arg; if(sscanf(str, "for %d", arg) == 1) { int i; for(i = 0 ; i < arg ; i++) { // empty loop for speed test } return; } if(str == "shutdown") { shutdown(); return; } if(sscanf(str, "echo %s", arg) == 1) { write(arg + "\n"); return; } if(sscanf(str, "call %s %s", file, arg) == 2) { arg = (string)call_other(file, arg); write("Got " + arg + " back.\n"); return; } write("master.c: Unknown flag " + str + "\n"); } // set time for the current world's start up time void set_startup_time() { startup_time = time(); log_file("SYSLOG","VH started at " + ctime(time()) + " on " + query_host_name() + "\n"); } // preload stuff -- called from epilog(); void preload(string file) { int t1; string err; if (file_size(file + ".c") == -1) return; t1 = time(); write("Preloading: " + file + "."); err = catch(call_other(file, "??")); if (err != 0) { write("\nGot error " + err + " when loading " + file + ".\n"); } else { t1 = time() - t1; write("(" + t1/60 + "." + t1 % 60 + ")\n"); } } // profile for the game static void system_profile() { string profile; profile = ctime(time()) + " (" + ctime(startup_time) + ") " + query_host_name() + "\n " + login_count + " login " + sizeof(users()) + " active " + sizeof(netdead) + " netdead " + memusage() + " bytes\n"; log_file("SYSPROFILE",profile); call_out("system_profile", PROFILE_TIME); } // loading various master data string *epilog(int load_empty) { string *preload_things, str; int i; if(load_empty) { write("Not preloading.\n"); return 0; } str = read_file(LOAD); if(str == 0) return 0; preload_things = explode(str, "\n"); for(i=0; i < sizeof(preload_things); i++) { if(preload_things[i][0] == '#') preload_things[i] = 0; preload(preload_things[i]); } set_startup_time(); system_profile(); return preload_things; } // // CONNECT SERVER // // calls everytime someone tries to connect object connect() { object login_ob; mixed err; err = catch(login_ob = clone_object(LOGIN)); #ifdef DEBUG write("master: connect: Cloned LOGIN\n"); #endif if (err) { write("********************************************\n"); write("** You sense a strange disturbance in VH! **\n"); write("** The Pantheonic Guild's fixing it ASAP. **\n"); write("** Bear with us and try again later. **\n"); write("********************************************\n"); destruct(this_object()); } return login_ob; } // // VARIOUS VALID CHECK SERVERS // int valid_exec(object ob) { string name; name = file_name(ob); if (debug) write("Debug: valid_exec: name = "+name+"\n"); if (name[0..8] == "/bin/adm/") return 1; return 0; } nomask int valid_shadow(object ob) { if (debug) write("Debug: valid_shadow: ob = "+file_name(ob) +", previous_object = "+file_name(previous_object())+"\n"); if(getuid(ob) == get_root_uid()) return 0; if(function_exists("query_wiz", previous_object())) return 0; if(function_exists("query_name",previous_object())) return 0; if(function_exists("query_real_name",previous_object())) return 0; return !ob->query_prevent_shadow(previous_object()); } // do we need passing eff_user to this darn func? [michael 4/23/92] valid_shutdown() { if (debug) write("Debug: valid_shutdown: previous_object = " + file_name(previous_object()) +"\n"); if(geteuid(previous_object()) == get_root_uid()) return 1; return 0; } // // Added by Dracos - anything can destruct anything for now // valid_destruct(object destructor, object destructee) { return 1; } // // Added by Dracos - nothing can hide right now // valid_hide(object ob) { return 0; } // // Added by Dracos - allow all socket functions for now // // info stucture: info[0] = int fd // info[1] = object owner // info[2] = string ip_address // info[3] = int port // valid_socket(object calling_ob, string func, mixed *info) { return 1; } valid_snoop(object snooper, object snoopee) { int snooper_level, snoopee_level, last_snooper_level; object cursnoop; if (debug) write("Debug: valid_snoop: snooper = "+file_name(snooper) +", snoopee = "+file_name(snoopee) +"\n"); if(!snoopee && snooper) return 1; snooper_level = (int)snooper->query_wiz(); snoopee_level = (int)snoopee->query_wiz(); /* Can't snoop someone when someone of higher level is already. */ cursnoop=query_snoop(snoopee); if (cursnoop) { last_snooper_level = (int)cursnoop->query_wiz(); if (last_snooper_level>snooper_level) {return 0;} } if(snooper_level >= snoopee_level || snooper_level >= GOD) { if(snoopee_level >= GOD || snooper_level == snoopee_level) tell_object(snoopee,"You feel like someone is watching you.\n"); write("snoop: snooping " + snoopee->query_name() + "\n"); return 1; } return 0; } // checks who is snooping whom [michael 4/29/92] void check_snoop() { object *ob; int i, wiz; wiz = (int)this_player(1)->query_wiz(); ob = users(); for(i = 0; i < sizeof(ob); i++) { object snooper; snooper = query_snoop(ob[i]); // if the snooper is of higher level than checker, then no-no check :) if(!snooper || snooper->query_wiz() > wiz) continue; write(snooper->query_real_name() + " -> " + ob[i]->query_name() + "\n"); } } #ifdef KERBEROS mapping dirs; mapping groups; #define BB_GROUPS ({ "wiz" , "senior" , "elder" , "demi" }) void query_kerberos() { dirs=(mapping)KERBEROS_D->query_dir_map(); groups=(mapping)KERBEROS_D->query_groups_map(); groups[get_bb_uid()]=BB_GROUPS; } // fstat returns an array: // ({ string owner , string group , int default_read, int set_uid }) mixed *fstat(string args) { string *path; int i; string str; if(args=="/") return ({ "root" , "god" , 1 , 0 }); path=explode(args,"/"); for(i=sizeof(path)-1;i>0;i--) { str=implode(path[0..i],"/"); if(!undefinedp(dirs[str])) return ({ dirs[str][0] , dirs[str][1], dirs[str][2] , dirs[str][3] }); } if(!undefinedp(dirs[path[0]])) return ({ dirs[path[0]][0] , dirs[path[0]][1], dirs[path[0]][2] , dirs[path[0]][3] }); return ({ "root" , "god" , 1 , 0 }); } valid_seteuid(object ob, string str) { mixed *dir_entry; if (debug) write("Debug: valid_seteuid: ob = "+file_name(ob)+", str = "+str+", uid = "+getuid(ob)+"\n"); if(getuid(ob) == get_root_uid()) return 1; if(str == "TEMP") return 1; if(getuid(ob) == str) return 1; dir_entry=fstat(file_name(ob)); if(dir_entry[3] && dir_entry[0] == str) return 1; return 0; } valid_read(string path, mixed who, string func) { string euid; mixed *dir_info; object caller; if(objectp(who)) caller=who; else caller=find_living(who); if(caller) euid=geteuid(caller); else return 0; if(euid==get_root_uid()) return 1; dir_info=fstat(path); if(dir_info[1]=="everyone") return 1; if(euid==dir_info[0]) return 1; if(member_array(dir_info[1],groups[euid])>=0) return 1; return dir_info[2]; } valid_write(string path, mixed who, string func) { string euid; mixed *dir_info; object caller; // these files are not writable from within the game if(path[0..7]=="/bin/adm") return 0; if(objectp(who)) caller=who; else caller=find_living(who); if(caller) euid=geteuid(caller); else return 0; if(euid==get_root_uid()) return 1; dir_info=fstat(path); if(dir_info[1]=="everyone") return 1; if(euid==dir_info[0]) return 1; if(member_array(dir_info[1],groups[euid])>=0) return 1; return 0; } #else valid_seteuid(object ob, string str) { if (debug) write("Debug: valid_seteuid: ob = "+file_name(ob)+", str = "+str+", uid = "+getuid(ob)+"\n"); if(getuid(ob) == get_root_uid()) return 1; if(str == "TEMP") return 1; if(getuid(ob) == str) return 1; if(creator_file(file_name(ob)) == str) return 1; return 0; } // soon to add a kerberos.c server to /bin/daemon and take over this task // same with valid_read(); valid_write(string file, mixed user, string func) { string *path, eff_user, junk, junk2; int f_WIZ, f_SEN, f_ELD, f_DEM; string *special; int i, ender; if (debug && objectp(user)) write("Debug: valid_write: file = "+file+", effuser = " +geteuid(user) +"\n"); else if (debug) write("Debug: valid_write: file = "+file+", effuser = "+ user +"\n"); if (!objectp(user)) user = find_living(user); eff_user = geteuid(user); if (!eff_user) return 0; file = resolv_path("/", file); path = explode(file, "/"); if (!path || sizeof(path) == 0) return 0; /* These files can never be writable from within */ if ((path[0] == "bin") && (sizeof(path) > 1) && (path[1] == "adm")) return 0; /* Root can always do as they please. */ if (eff_user == get_root_uid()) return 1; /* Backbone users have some special privs */ if (eff_user == get_bb_uid()) { /* can write to /data/usr */ if (sscanf(file, "/data/usr/%s", junk)) return 1; } /* If there is forcing going on -- refuse permission. */ if (this_player() != this_player(1)) return 0; /* Let's let the gods have most fun! */ if ((int)this_player()->query_wiz() >= GOD) return 1; /* special log write access */ if (path[0] == "log") if (func == "log_file") return 1; /* Here we check for special access privs. */ special = (string *)this_player()->list_access_list(); if (file_size(file) == -2) ender = 0; else ender = 1; if (special) { string makepath; makepath = ""; for (i = 0; i < (sizeof(path) - ender); i++) makepath = makepath + "/" + path[i]; if (debug) write("Debug valid_write: makepath = "+makepath+"\n"); /* FILE SECURITY RISK! Disable write immediately */ if (member_array("FSR", special) > -1) return 0; /* grant permission if permission is in access list */ if ((member_array(makepath, special) > -1) || (member_array(makepath+"/", special) > -1)) return 1; /* Granting permission for special wiz levels */ if (member_array("WIZ", special) > -1) f_WIZ = 1; if (member_array("SEN", special) > -1) f_SEN = 1; if (member_array("ELD", special) > -1) f_ELD = 1; if (member_array("DEM", special) > -1) f_DEM = 1; } while (sizeof(path) < 5) path += ({ "" }); /* as long as the user is not FSR, he/she can write to these */ if (path[0] == "tmp" || path[0] == "open") return 1; /* Here we check for access to some other directories */ if (f_WIZ || f_SEN || f_ELD || f_DEM) if (path[0] == "w") if (path[1] == eff_user) return 1; if (f_SEN || f_ELD || f_DEM) if (path[0] == "w") return 1; if (f_ELD || f_DEM) if (path[0] == "doc") return 1; if (f_DEM) { if (path[0] == "room" || path[0] == "obj") return 1; if (path[0] == "std" ) return 1; if (path[0] == "log" && path[1] == "history") return 1; } /* No write permission. (DEFAULT) */ return 0; } valid_read(string file, mixed user, string func) { string *path, eff_user; if (debug && objectp(user)) write("Debug: valid_read: file = "+file+", effuser = " +geteuid(user) +"\n"); else if (debug) write("Debug: valid_read: file = "+file+", effuser = "+ user +"\n"); if(!objectp(user)) user = find_living(user); eff_user = geteuid(user); if(!eff_user) return 0; file = resolv_path("/", file); /* if you can write to it, you can read it */ if(valid_write(file, user, func)) return 1; path = explode(file, "/"); if(!path) path = allocate(5); else while(sizeof(path) < 5) path += ({ "" }); /* root reads all */ if (eff_user == "root") return 1; /* Here we check for directories of no read */ if(path[0] == "w" && path[2] == "closed") return 0; if(path[0] == "data" && path[1] == "usr") return 0; if(path[0] == "data" && path[1] == "adm") return 0; /* I, dirk, am sick and tired of people reading history logs!!! */ if(path[0] == "log" && path[1] == "history") return 0; /* read (default) */ return 1; } #endif // // SAVE/RESTORE_OBJECT SERVERS // save_player() { object pobj; int res; pobj = previous_object(); export_uid(pobj); res = (int)pobj->save_player(pobj->query_real_name()); seteuid((string)pobj->query_real_name()); export_uid(pobj); seteuid(get_root_uid()); return res; } load_player_from_file(string name, object player) { int res; export_uid(player); res = (int)player->actually_restore_player(name); seteuid(name); export_uid(player); seteuid(get_root_uid()); return res; } void save_player_to_file(object player) { string name; name = (string)player->query_real_name(); if(!name) return; if(!seteuid(get_root_uid())) write("Seteuid failed!\n"); export_uid(player); player->actually_save_player(name); seteuid(name); export_uid(player); seteuid(get_root_uid()); } // // MISC SYSTEM SERVERS // // when the system goes down, this will try to make it less "deadly" static void crash(string str, object cmd_giver, object curob) { log_file("SYSLOG","VH crashed at " + ctime(time()) + " on " + query_host_name() + "\n"); log_file("SYSLOG", "\tReason: " + str + "\n"); // log_file("SYSLOG", "\tCommand Giver: "+cmd_giver->query_name()+"\n"); // log_file("SYSLOG", "\tCurrent Object: "+file_name(curob)+"\n"); } // when room is destroyed, the contents in it are "saved" void destruct_environment_of(object ob) { string place, res; if(!interactive(ob)) return; place = file_name(environment(ob)); tell_object(ob, "A fiery burst of light erupts out of nowhere as the area around\n"+ "you disappears from view. You sense that a mighty wizard has\n"+ "transformed the place back into pure magic. Then, everything\n"+ "slowly comes back together again....\n"); res = catch(ob->move(place)); if(res) ob->move("/room/void"); } // keep track of number of logins void add_login_count() { login_count++; } // keep track of netdead players/wiz void add_netdead(string str) { if(this_player(1) != this_player()) return; if(netdead) if(member_array(str, netdead) != -1) return; if(netdead && sizeof(netdead) > 0) netdead += ({ str }); else netdead = ({ str }); log_file("NETDEAD", str + " (" + query_ip_name() + ") " + ctime(time()) + "\n"); } void remove_netdead(string str) { /* have to check whether netdead==0 or nobody can log in - Quag */ if(netdead && member_array(str, netdead) != -1) netdead -= ({ str }); } // Simple enough, most folks don't have read in /data/save // do we still need this? [michael 4/23/92] player_exists(string name) { if(file_size("/data/adm/save/"+ extract(name,0,0) + "/" +name+ ".o") < 0 ) return 0; return 1; } // create error log files string get_wiz_name(string file) { string name, rest; int tmp; if(sscanf(file, "w/%s/%s", name, rest) == 2) return "/w/" + name; if(sscanf(file, "bin/%s", rest) == 1) return "bin"; if(sscanf(file, "obj/%s", rest) == 1) return "obj"; if(sscanf(file, "open/%s", rest) == 1) return "open"; if(sscanf(file, "room/%s", rest) == 1) return "room"; if(sscanf(file, "std/%s", rest) == 1) return "std"; return "log"; } void log_error(string file, string message) { string name, log, tmp; name = get_wiz_name(file); /* New version of error logs by sunnywiz */ log = sprintf("%s %s\n> %s",query_time("date"),query_time("time"), message); // log = extract(ctime(time()),11,18) + "\n" + message + "\n"; if(sscanf(name, "/w/%s", tmp) == 1) { write_file(name + "/.error", log); } else log_file(name, log); } // For wizard who wants to save ed setup. It is saved in the file // /w/wiz_name/.edrc. save_ed_setup(object who, int code) { string file; if (!intp(code)) return 0; file = "/w/" + (string)who->query_real_name() + "/.edrc"; rm(file); return write_file(file, code + ""); } retrieve_ed_setup(object who) { string file; int code; file = "/w/" + (string)who->query_real_name() + "/.edrc"; if (file_size(file) <= 0) return 0; sscanf(read_file(file), "%d", code); return code; } // This is called by the game driver to resolve path names in ed. string make_path_absolute(string file) { file = resolv_path((string)this_player()->query_path(), file); return file; } // virtual object server. mixed compile_object(string file) { return (mixed) VIRTUAL->compile_object(file); } // This lets us turn on debugging info when we want. // This gets noisy. Currently only checks valid_* functions. void set_debug_info() { debug = !debug; } // EOF