| #pragma strict_types, no_warn_deprecated |
| |
| #include "/secure/master.h" |
| |
| static mapping projects=([]); |
| static string *insecure,*deputy_files; |
| |
| int ReloadInsecureFile() |
| { |
| insecure = efun::explode( read_file("/secure/INSECURE") || "", "\n" ); |
| insecure -= ({""}); |
| insecure = map( insecure, #'lower_case/*'*/ ); |
| return(1); |
| } |
| |
| void LoadDeputyFileList() |
| { |
| // Leseberechtigungen fuer /log/ARCH/* setzen |
| deputy_files = efun::explode( read_file("/log/ARCH/DEPUTY_FILELIST") || "", |
| "\n" ) - ({""}); |
| return; |
| } |
| |
| #define PATH_ARRAY(x) (explode(x, "/")-({"."})) |
| string *full_path_array(string path, string user) { |
| string *strs; |
| string cwd; |
| |
| if(!path) |
| return ({"",""}); // additional "" to yield "/" later. |
| |
| // remove multiple '/' |
| path = regreplace(path, "/+", "/", 1); |
| |
| switch(path[0]) { |
| case '/': |
| if(!path[1]) return ({"",""}); //additional "" to yield "/" later |
| strs=PATH_ARRAY(path); |
| if (!sizeof(strs)) |
| strs = ({"",""}); |
| break; |
| case '+': |
| if(!path[1]) return ({"","d"}); |
| strs=({"","d"})+PATH_ARRAY(path[1..<1]); |
| break; |
| case '~': |
| if(user && !path[1]) |
| return ({"","players",user}); |
| if(user && path[1]=='/') |
| strs=({"","players",user})+PATH_ARRAY(path[2..<1]); |
| else |
| strs=({"","players"})+PATH_ARRAY(path[1..<1]); |
| break; |
| default: |
| if(user && TP && getuid(TP) == user |
| && (cwd=(string)TP->QueryProp(P_CURRENTDIR))) |
| strs=PATH_ARRAY(cwd + "/" + path); |
| else |
| strs=PATH_ARRAY(path); |
| } |
| // /../ behandeln. Einschraenkungen der RegExp: |
| // /a/b/../../d wuerde nur durch Wiederholung aufgeloest. |
| // /../d wird nicht richtig aufgeloest. |
| //regreplace("/d/inseln/toeter/room/hoehle/../wald/bla.c","(.*)\/[^/]+/\.\. |
| // /(.*)", "\\1\/\\2", RE_GLOBAL) |
| |
| // dies sieht schlimmer aus als es ist (member ist O(n)), solange das Array |
| // nicht gross wird. |
| int p; |
| while((p=member(strs, "..")) != -1) |
| strs = strs[0..p-2]+strs[p+1..]; |
| |
| return strs; |
| } |
| #undef PATH_ARRAY |
| |
| string _get_path(string path, string user) { |
| string *p_arr = full_path_array(path, user); |
| // make path absolute |
| if (p_arr[0] != "") |
| p_arr = ({""}) + p_arr; |
| |
| return implode(p_arr,"/"); |
| } |
| |
| static int project_access(string user, string project) |
| { |
| mixed *lines; |
| string s; |
| mapping tmp; |
| |
| if (!member(projects,project)) |
| { |
| s=read_file(PROJECTDIR+"/"+project+"/ACCESS_RIGHTS"); |
| if(!s||s=="") |
| return 0; |
| tmp=([]); |
| for (lines=explode(s,"\n")-({""});sizeof(lines);lines=lines[1..]) |
| { |
| if (lines[0][0]=='*') |
| tmp[lines[0][1..]]=2; |
| else |
| tmp[lines[0]]=1; |
| } |
| projects[project]=({tmp,time()}); |
| return tmp[user]; |
| } |
| projects[project][1]=time(); |
| return projects[project][0][user]; |
| } |
| |
| void OutdateProjectCache(string project) |
| { |
| m_delete(projects,project); |
| } |
| |
| static void _cleanup_projects() { |
| int i; |
| mixed *users; |
| |
| for (users=m_indices(projects),i=sizeof(users)-1;i>=0;i--) |
| if((time()-projects[users[i]][1])>1800) |
| m_delete(projects,users[i]); |
| } |
| |
| int access_rights(string *p_arr, string euid) |
| { |
| int i; |
| |
| i = sizeof(p_arr) - 2; |
| |
| while ( i >= 0 && |
| file_size( implode(p_arr[0..i], "/") + "/access_rights.c" ) < 0 ) |
| i--; |
| |
| if ( i < 0 ) |
| return 0; |
| |
| if ( !catch(i = (int)call_other( |
| implode(p_arr[0..i], "/") + "/access_rights", |
| "access_rights", euid, |
| implode(p_arr[i+1..], "/") ); publish) ) |
| return i; |
| |
| return 0; |
| } |
| |
| string make_path_absolute(string str) { |
| return _get_path(str, getuid(TP)); |
| } |
| |
| |
| mixed valid_write(string path, string euid, string fun, object obj) |
| { |
| int s,lvl; |
| string *strs; |
| int *date; |
| |
| if (member(path,' ')!=-1) return 0; |
| |
| // Unter LIBDATADIR (/data) sollen komplett identische Schreibrechte |
| // vergeben werden. |
| if (sizeof(path) > 6 |
| && path[0..5] == "/"LIBDATADIR"/") |
| return valid_write(path[5..], euid, fun, obj) != 0; |
| |
| switch(fun) { |
| case "log_file": |
| strs=full_path_array("/"+path, 0); |
| path = implode(strs, "/"); |
| strs -= ({""}); // remove trailing and leading "/". |
| if (sizeof(strs)>1 && strs[0]=="log") return path; |
| return 0; |
| case "save_object": |
| if (!path) return 0; |
| strs=full_path_array("/"+path, 0); |
| break; |
| case "ed_start": |
| if (path && path[0]) |
| strs=full_path_array(path, euid); |
| else strs=({"players",euid,".err"}); |
| break; |
| default: |
| strs=full_path_array(path, euid); |
| } |
| |
| if (!euid || euid=="NOBODY" || euid=="ftp" || euid=="anonymous") return 0; |
| |
| // Pfad soll ab jetzt auf jeden Fall absolut sein. |
| if (strs[0] != "") |
| path = "/" + implode(strs, "/"); |
| else |
| path=implode(strs, "/"); |
| |
| // fuer die Rechtebestimmung "/" an Anfang und Ende entfernen |
| strs -= ({""}); |
| |
| //Root-Objekte und Master duerfen immer. |
| if (euid == ROOTID || obj==TO) return path; |
| |
| lvl=query_wiz_level(euid); |
| |
| //Toplevel: nur EM+ |
| if ((s=sizeof(strs))<=1) return lvl>=ARCH_LVL; |
| |
| switch(strs[0]) { |
| case TMPDIR: |
| return 1; |
| |
| case STDDIR: |
| case SYSDIR: |
| case LIBOBJDIR: |
| case ETCDIR: |
| case LIBROOMDIR: |
| return lvl>=ARCH_LVL; |
| |
| case LIBITEMDIR: |
| return lvl>=ELDER_LVL; |
| |
| case SPELLBOOKDIR: |
| // wenn unterhalb eines unterverzeichnisses des Stils "gilde", dann ists |
| // einfach |
| if (sizeof(strs) > 2 |
| && guild_master(euid, strs[1])) |
| return 1; |
| |
| // sonst nur EMs bzw. access_rights.c fragen. |
| return ((lvl>=ARCH_LVL) || access_rights(strs,euid)); |
| |
| case GUILDDIR: |
| // wenn unterhalb eines unterverzeichnisses des Stils "filde.gilde", |
| // dann ists einfach |
| if (sizeof(strs) > 2) { |
| string *tmp = explode(strs[1],"files."); |
| if (sizeof(tmp) == 2) { |
| if (guild_master(euid, tmp[1])) |
| return 1; |
| // Objekte dort sollen auch schreiben duerfen. |
| if (euid == ("GUILD."+tmp[1])) |
| return 1; |
| } |
| } |
| // sonst nur EMs bzw. access_rights.c fragen. |
| return ((lvl>=ARCH_LVL) || access_rights(strs,euid)); |
| |
| case DOCDIR: |
| if ( s > 1 && (strs[1] == "balance") ) |
| return ((lvl>=ARCH_LVL) || access_rights(strs,euid)); |
| else |
| return ((lvl>=SPECIAL_LVL) || access_rights(strs,euid)); |
| |
| case FTPDIR: |
| if ( s > 1 && (strs[1] == "incoming" || strs[1] == "tmp" || |
| s == 3 && strs[1] == "News" && strs[2] == euid+".news") ) |
| return 1; |
| |
| if (lvl>=ELDER_LVL) |
| return 1; |
| |
| return 0; |
| |
| case MAILDIR: |
| if (euid==MAILID || strs[1]=="spool") break; |
| return 0; |
| |
| case LIBSAVEDIR: |
| if (lvl>=ARCH_LVL) return 1; |
| if (s==3 && strs[1] == euid[0..0] && |
| (strs[2]==euid+".o" || strs[2]==euid)) break; |
| return 0; |
| |
| case LIBLOGDIR: |
| /* auf /log/ARCH/ haben wirklich nur EMs Zugriff */ |
| if (strs[1]=="ARCH" && lvl<ARCH_LVL) return 0; |
| |
| /* alles andere duerfen auch Weise... */ |
| if (lvl>=ELDER_LVL) return 1; |
| |
| /* Allgemeine logfiles in /log duerfen wirklich nur geschrieben werden */ |
| if (s==2 && strs[1][0]>='A' && strs[1][0]<='Z' && fun != "write_file") |
| return 0; |
| |
| /* Unterhalb von /log/syslog darf nur geschrieben werden */ |
| if (s>1 && strs[1]=="syslog" && fun != "write_file") |
| return 0; |
| |
| /* loggen ins eigene repfile immer erlauben */ |
| if (s==3 && strs[1]=="report" && |
| strs[2]==explode(euid, ".")[<1]+".rep") break; |
| |
| /* in fremden Verzeichnissen hat niemand was zu loggen */ |
| if (get_wiz_level(strs[1]) >= WIZARD_LVL && |
| strs[1] != efun::explode(euid, ".")[<1]) |
| return 0; |
| break; |
| |
| case WIZARDDIR: |
| /* kein Zugriff auf Objekte mit hoeheren Rechten */ |
| if (query_wiz_level(strs[1]) > lvl) return 0; |
| |
| /* Magier selbst oder Weise duerfen hier schreiben */ |
| if ((IS_WIZARD(euid) && euid==strs[1])||(lvl>=ELDER_LVL)) break; |
| |
| /* Es steht jedoch frei, auch anderen Schreibrechte zu geben... */ |
| return access_rights(strs,euid); |
| |
| case DOMAINDIR: |
| /* neue Regionen duerfen nur Erzmagier anlegen */ |
| if (s<2 && lvl<ARCH_LVL) return 0; |
| |
| /* kein Zugriff auf Objekte mit hoeheren Rechten */ |
| if (s>2 && query_wiz_level(creator_file(path)) > lvl) |
| return 0; |
| |
| /* Auf Regionfiles von erzmagier haben nur EMs Zugriff */ |
| if (creator_file(path)=="erzmagier" && lvl<ARCH_LVL) return 0; |
| |
| /* innerhalb der Region haben RMs und Weise volle Schreibrechte */ |
| if (lvl>=ELDER_LVL || domain_master(euid,strs[1])) break; |
| |
| /* neue Verzeichnisse in der Region kann nur RM+ anlegen */ |
| if (s<=3 && (fun=="mkdir" || fun=="rmdir")) return 0; |
| |
| /* Magier auf ihre eigenen Files... */ |
| if (s>2 && strs[2]==euid) break; |
| |
| /* Files der Magier in der Region in ihre eigenen Verzeichnisse... */ |
| if (s>2 && euid==sprintf("d.%s.%s", strs[1], strs[2])) break; |
| |
| /* Files mit Regionsuid */ |
| if (euid==strs[1]) break; |
| |
| /* Es ist moeglich anderen Magiern Rechte bei sich einzuraeumen. */ |
| if (access_rights(strs,euid)>0) break; |
| |
| /* Auf das Verzeichniss 'alle' haben alle Regionsmitglieder Zugriff. |
| Ausser, wenn sie ueber access_rights.c explizit ausgeschlossen |
| werden (Returncode < 0). */ |
| if (s>2 && strs[2]=="alle" && domain_member(euid, strs[1]) && |
| access_rights(strs,euid)>=0) break; |
| return 0; |
| |
| case PROJECTDIR: |
| /* Nur Erzmagier duerfen neue Projektverzeichnisse anlegen... */ |
| if (s<3 && lvl<ARCH_LVL) return 0; |
| |
| /* alles weitere duerfen auch Weise tun */ |
| if (lvl>=ELDER_LVL) break; |
| |
| /* in den Serviceverzeichnissen muss lediglich der Name stimmen */ |
| if (s>3 && strs[1]=="service" && euid==strs[2]) break; |
| |
| /* Objekte eines Projektes haben Schreibzugriffe auf ihr Projekt */ |
| if (s>3 && (implode(strs[0..1], ".") == euid |
| || implode(strs[0..2], ".") == euid) ) return 1; |
| |
| /* Schreibrechte koennen natuerlich auch vergeben werden... */ |
| if (project_access(euid,strs[1])) break; |
| // Alternativ besteht neben dem ACCESS_RIGHTS auch noch die |
| // Moeglichkeit, per access_rights.c Rechte einzuraeumen. |
| if (access_rights(strs,euid)>0) break; |
| |
| return 0; |
| |
| default: return 0; |
| } |
| return path; |
| } |
| |
| mixed valid_read(string path, string euid, string fun, object obj) |
| { |
| int s, lev; |
| string *strs, suf; |
| |
| if (member(path,' ')!=-1) return 0; |
| |
| // Unter LIBDATADIR (/data) sollen komplett identische Leserechte |
| // vergeben werden. |
| if (sizeof(path) > 6 |
| && path[0..5] == "/"LIBDATADIR"/") |
| return valid_read(path[5..], euid, fun, obj) != 0; |
| |
| if (!euid) euid="-"; |
| |
| strs=full_path_array(path, euid); |
| // Pfad soll ab jetzt auf jeden Fall absolut sein. |
| if (strs[0] != "") |
| path = "/" + implode(strs, "/"); |
| else |
| path=implode(strs, "/"); |
| |
| // fuer die Rechtebestimmung "/" an Anfang und Ende entfernen |
| strs -= ({""}); |
| |
| if (!sizeof(strs) || !sizeof(path) || euid == ROOTID || obj==TO) return path; |
| |
| if ((s=sizeof(strs)) <= 1) return path; |
| |
| lev=query_wiz_level(euid); |
| |
| switch(strs[0]) { |
| case "core": |
| case "wahl": |
| return 0; |
| |
| case NEWSDIR: |
| if (strs[1] == "archiv.pub") // oeffentliches archiv |
| return path; |
| else if (strs[1] == "archiv.magier" // Magier-Archiv |
| && lev >= WIZARD_LVL) |
| return path; |
| |
| return 0; // kein Zugriff auf /news/ oder alles andere. |
| |
| case "maps": |
| if (lev<=WIZARD_LVL) return 0; |
| |
| case "": |
| case ETCDIR: |
| case STDDIR: |
| case SYSDIR: |
| case DOCDIR: |
| case LIBOBJDIR: |
| case LIBROOMDIR: |
| case LIBITEMDIR: |
| case FTPDIR: |
| return path; |
| |
| case MAILDIR: |
| return (euid==MAILID); |
| |
| case SECUREDIR: |
| if (strs[1]=="save") return 0; |
| if (path[<2..]==".o" || path[<5..]==".dump") return 0; |
| if (strs[1]!="ARCH" || lev>=ARCH_LVL) return path; |
| |
| case LIBLOGDIR: |
| if ( strs[1] == "ARCH" && !IS_DEPUTY(euid) ) |
| return 0; |
| |
| if ( strs[1] == "ARCH" && lev < ARCH_LVL && s == 3 && |
| strs[2] != "*" && strs[2][0..2] != "bb." && |
| member( deputy_files, strs[2] ) < 0 ) |
| return 0; |
| |
| if ( lev > WIZARD_LVL ) |
| return path; |
| |
| if ( s == 2 && (strs[1] == "report" || strs[1] == "error") ) |
| return path; // fuer 'cd' |
| |
| // /log sollte jeder auflisten koennen |
| if ( s == 2 && strs[1]=="*" && fun=="get_dir") |
| return path; |
| |
| // unter /log/report/, /log/error und /log/warning/ duerfen alle lesen |
| if ( s==3 && |
| (strs[1] == "report" || strs[1] == "error" |
| || strs[1] =="warning")) |
| return path; |
| |
| // /log/<Magiername> darf von <Magiername> natuerlich auch |
| // gelesen werden |
| if ( s >= 2 && strs[1] == euid ) |
| return path; |
| |
| return 0; |
| |
| case "backup": |
| case LIBSAVEDIR: |
| if (lev>WIZARD_LVL) return path; |
| |
| /* Objekte in /p/* haben bisher leider wizlevel 0 */ |
| //if (lev==0) return path; |
| |
| if (fun=="file_size") return path; |
| |
| // das eigene Savefile darf man natuerlich immer. ;-) |
| if (s==3 && (strs[2]==euid+".o" || strs[2]==euid)) |
| return path; |
| return 0; |
| |
| case PROJECTDIR: |
| /* Pruefen ob ein File existiert darf jeder... */ |
| if (fun=="file_size") return path; |
| |
| /* Die Service-Verzeichnisse darf jeder lesen */ |
| if (s>3 && strs[1]=="service") return path; |
| |
| //Weise duerfen in /p/ schreiben, also auch lesen. (Zesstra, 04.11.06) |
| if (lev>=ELDER_LVL) return path; |
| |
| /* wer hier schreiben darf, darf natuerlich auf jeden Fall lesen */ |
| //Anmerkung v. Zesstra: das prueft nur, ob jemand in ACCESS_RIGHTS |
| //steht, nicht ob jemand (ggf. aus anderen Gruenden schreiben darf) |
| if (project_access(euid,strs[1])) return path; |
| //Alternativ kann man explizite Schreibrechte auch via access_rights.c |
| //vergeben. (und damit natuerlich auch Leserechte) |
| if (access_rights(strs,euid)>0) return path; |
| |
| /* Objekte eines Projektes haben Lesezugriff auf ihr Projekt */ |
| if (s>3 && (implode(strs[0..1], ".") == euid |
| || implode(strs[0..2], ".") == euid) ) return path; |
| |
| /* Fall-Through */ |
| |
| case GUILDDIR: |
| /* "secure"-Verzeichnisse darf nur lesen, wer da auch schreiben darf */ |
| if ( s > 3 && strs[<2] == "secure" && member( insecure, strs[1] ) < 0 |
| && lev < ARCH_LVL && !access_rights(strs, euid) ) |
| return 0; |
| |
| /* Pruefen ob ein File existiert darf jeder... */ |
| if (fun=="file_size") return path; |
| |
| /* Fall-Through */ |
| |
| case SPELLBOOKDIR: |
| // Gildenpbjekte koennen hier lesen |
| if (lev==0 && euid[0..4]=="GUILD") return path; |
| |
| // Mit Level <= 20 darf man hier nichts lesen |
| if ( lev<=WIZARD_LVL && sizeof(regexp( ({strs[<1]}), "\\.[och]" )) ) |
| return 0; |
| |
| return path; |
| |
| case WIZARDDIR: |
| if (s==2) return path; |
| /* das eigene Verzeichniss darf man natuerlich immer lesen... */ |
| if (strs[1]==euid && lev>=WIZARD_LVL) return path; |
| |
| /* Pruefen ob ein File existiert darf jeder... */ |
| if (fun=="file_size") return path; |
| |
| /* fremde Verzeichnisse mit <= Level 20 noch nicht */ |
| if (lev<=WIZARD_LVL) return 0; |
| |
| /* wo man schreiben darf, darf man natuerlich auch lesen... */ |
| if (lev>=ELDER_LVL) return path; |
| |
| // kein Zugriff auf archivierten Code (wo z.B. secure/ drin sein |
| // koennen) |
| if (member(({"bz2","gz","zip"}), explode(strs[<1],".")[<1]) > -1) |
| return 0; |
| |
| /* Files ohne Code sind nicht weiter interessant... */ |
| if ( !sizeof(regexp( ({strs[<1]}), "\\.[och]" )) ) |
| return path; |
| |
| /* folgende Funktionen sind natuerlich voellig uninteressant */ |
| if (member(({"get_dir", "restore_object"}), fun)!=-1) |
| return path; |
| |
| /* Zugriff erlaubt, aber mitloggen */ |
| write_file( READ_FILE, sprintf("%O %s %s: %s\n", fun, ctime()[4..], |
| euid, path) ); |
| return path; |
| |
| case DOMAINDIR: |
| /* Mit Level 15 darf man hier _nichts_ lesen */ |
| if ( fun!="file_size" && lev<WIZARD_LVL && |
| sizeof(regexp( ({strs[<1]}), "\\.[och]" )) ) return 0; |
| |
| /* den Verzeichnisbaum von /d/ darf man natuerlich sonst lesen */ |
| if (s<=2) return path; |
| |
| /* eigenen Code darf man natuerlich auch lesen */ |
| if (euid==strs[2] || euid==sprintf("d.%s.%s", strs[1], strs[2])) |
| return path; |
| |
| /* Deputies haben ein gemeinsames Verzeichnis unter /d/erzmagier */ |
| if (strs[1]=="erzmagier" && strs[2]=="polizei" && IS_DEPUTY(euid)) |
| return path; |
| |
| /* d/erzmagier darf man nur als Erzmagier lesen */ |
| if (strs[1]=="erzmagier" && lev<ARCH_LVL) return 0; |
| |
| /* einige Regionen haben std-verzeichnisse, die darf man natuerlich |
| auch mit Level 20 bereits komplett lesen! */ |
| if (strs[2]=="std") return path; |
| |
| /* "secure"-Verzeichnisse darf nur lesen, wer da auch schreiben darf */ |
| if ( s > 4 && strs[<2] == "secure" && member( insecure, strs[2] ) < 0 ){ |
| if ( lev < ELDER_LVL && !domain_master(euid, strs[1]) |
| && !access_rights(strs, euid) ) |
| return 0; |
| else |
| return path; |
| } |
| |
| /* Dokus, Infos und .readme darf man immer lesen... */ |
| if ( fun=="file_size" || !sizeof(regexp( ({strs[<1]}), "\\.[och]" )) ) |
| return path; |
| |
| /* Mit Level 20 darf man noch keinen fremden Code lesen! */ |
| if (lev<=WIZARD_LVL) return 0; |
| |
| /* Weise duerfen natuerlich alles weitere lesen */ |
| if (lev>=ELDER_LVL) return path; |
| |
| /* Regionsmagier in ihren Regionen natuerlich auch */ |
| if (lev>=LORD_LVL && domain_master(euid, strs[1])) return path; |
| |
| /* folgende Funktionen sind natuerlich voellig uninteressant */ |
| if (member(({"get_dir", "restore_object"}), fun)!=-1) |
| return path; |
| |
| /* Zugriff erlaubt, aber mitloggen */ |
| write_file( READ_FILE, sprintf("%O %s %s: %s\n", fun, ctime()[4..], |
| euid, path) ); |
| return path; |
| } |
| if (lev<ARCH_LVL) return 0; |
| return path; |
| } |
| |